[
  {
    "path": ".gitattributes",
    "content": ".gitattributes export-ignore\n.gitignore export-ignore\n\n*.jpg binary\n*.gif binary\n*.png binary\n*.ttf binary\n*.zip binary\n*.rar binary\n*.gz binary\n\n*.min.js diff=nodiff\n*.min.css diff=nodiff\n"
  },
  {
    "path": ".gitignore",
    "content": "# Ignore config.php in any directory\nconfig.php\n*config.php\n\nbadqrys.txt\n\n# Ignore Yandex and Google placeholders\n/yandex_*.txt\n/google*.txt\n/google*.html\n\n# Ignore override CSS files\n/desing/css/*override*\n\n# Ignore uploaded files\n/files\n\n# Ignore local folder, test folder and test files\n/test/\n/.local/\nt.*\n\n# Ignore update flag\n/includes/update.last\n\n# Ignore cache folder\n/cache/\n/design/cache/\n\n# Ignore avatar folder\n/images/avatar/\n\n# Ignore instanced servers\n/servers/\n\n# Ignore all dotfiles...\n.*\n# ...except for .gitignore & .gitattributes\n!.gitignore\n!.gitattributes\n!.htaccess\n\n# Ignoring all files started with \"__\" - it's a local or global test files\n__*\n\n# Ignore backup folder\n/admin/sxd/cfg.php\n/admin/sxd/error.log\n/admin/sxd/ses.php\n/admin/sxd/backup/*.job.php\n\n# Ignore backups\n/admin/backup/*.gz\n/admin/backup/*.bz2\n/admin/backup/*.sql\n/admin/sxd/backup/*.gz\n/admin/sxd/backup/*.bz2\n/admin/sxd/backup/*.sql\n\n# Ignore skin xNova\n/skins/xnova/\n\n# Ignore forums\n/phpBB3/\n/forum/\n\n# Ignore DirInfo\ndirinfo\n\n# Ignore CMD-files\n/*.cmd\n\n# Ignore xcache interface\n/xcache/\n/xcache-admin/\n/xcache-admin2/\n\n"
  },
  {
    "path": ".htaccess",
    "content": "AddDefaultCharset UTF-8\n\n# Disable index list\nOptions -Indexes\n\n# Disable access to config.php\n<Files config.php>\n  order allow,deny\n  deny from all\n</Files>\n\n<Files ~ \"\\.html$\">\n  order allow,deny\n  deny from all\n</Files>\n\n# Deny access to all dotted fields and folders\nRedirectMatch 404 /\\..*$\n\n\n#<Files \"\\.(tar|gif|png|...|xml)$\">\n#    order allow,deny\n#    deny from all\n#</Files>\n\n"
  },
  {
    "path": "README.MD",
    "content": "# We Want YOU!\nSuperNova need YOU! We need maintainers  for  localizations.  Any  language  is\nfine! Currently we need maintainer for English locale to check and  fix  it  as\nbase for other non-Russian speaker users!\n\n# Project \"SuperNova.WS\"\n\"SuperNova.WS\" (shortly SN) is  a  massive  multiplayer  online  browser  space\nstrategy game (like oGame). SN is a very heavily modified XNova RageRepack v226\n\nSN written on PHP. It require MySQL-server for work\n\nSN developed under GNU GENERAL PUBLIC LICENSE Version 2,  June  1991.  You  can\nread it in /docs/license.txt (English). Also there is  additional  restrictions:\n1. You can not remove or change any copyright notices from any part of  SN.  If\nyou wish to use any part of code in your development you should  also  add  all\ncopyright notices\n2. You absolutly can not sell SN-based product without my  special  permission.\nIf you want to sell such product - contact me\n\nCurrently SN supports Russian and English languages\n\n\n## Disclaimer\nWARNING! Project status is \"Alpha\"! It means  that  any  update  can  seriously\nchange any aspect of gameplay!  Also  it  means  that  there  may  be  lack  of\ndocumentation, no user-friendly installer. Also it means  that  any  user  that\nwant to use this code should have some knowledge about  configuring  web-server\nwith PHP  and  MySQL  and  have  knowledge  about  installing  and  configuring\nPHP-scripts. However code is pretty stable and has \"beta\"-quality.\n\nWARNING! SN PROVIDED \"AS-IS\" WITH NO EXPLICIT OR IMPLIED WARRANTY AT ALL!\n\nWARNING! MANY ASPECTS OF GAMEPLAY AND ALGORYTHMS DIFFERS  FROM  OGAME  OFFICIAL\nSERVERS! IT'S NOT A BUG - IT WAS MADE  INTENTIONALLY!  DO  NOT  COMPLY  IF  YOU\nENCOUNTER SUCH DIFFERENCE! Some notes about SN future development can  be  read\nin /docs/todo.txt (Russian)\n\n\n## Web-resources\n\n### Main Hub For Your SuperNova Experience\nRussian Version http://supernova.ws/index.htm\n\nEnglish Version http://supernova.ws/index-en.htm\n\n### GitHub\nYou encouraged to use GitHub as main source for SN releases and builds\n\nSN on GitHub: http://github.com/supernova-ws/SuperNova\n\nGIT-repository: git://github.com/supernova-ws/SuperNova.git\n\nGIT-repository through HTTP: https://github.com/supernova-ws/SuperNova#\n\nLatest stable release: https://github.com/supernova-ws/SuperNova/zipball/master\n\nLatest night build (stability is not guaranted!): https://github.com/supernova-ws/SuperNova/zipball/trunk\n\nSVN-interface (SVN-interface on GitHub works very unstable): http://svn.github.com/supernova-ws/SuperNova.git\n\nThere are 3 branches on GitHub repository\n- \"master\" contains last stable version.\n- \"trunk\" contains most recent and feature-rich code. Here can be some minor bugs\n- \"test\"  contains  developer  snapshot.   It   can   be   partially   or   fully\nnon-functional.  Hovewer  here  you  can  see  currently  developing  features.\n\n### SourceForge\nGIT-repository on SourceForge:\n  git://supernova-ws.git.sourceforge.net/gitroot/supernova-ws/supernova-ws\n\nRelease archives:\n  http://sourceforge.net/projects/supernova-ws/files/releases/\n\nDIFFs\n  http://sourceforge.net/projects/supernova-ws/files/diffs/\n\nPatches (archive of differences between indicated versions)\n  http://sourceforge.net/projects/supernova-ws/files/patches/\n\n### Support\nForum (Russian and English):\n  http://forum.supernova.ws/viewforum.php?f=73\n\n### Live Demo Universes\nSuperNova (x1):\n  http://supernova.ws\n\noGame (x2):\n  http://ogame.supernova.ws\n\nBeta (x50):\n  http://beta.supernova.ws\n\n## Documentation\nAll documentation located in folder `/docs`.\n\nLicense (English):\n  `/docs/license.txt`\n\nProject documentation (Russian):\n  `/docs/readme.txt`\n\nUser-level changelog (Russian):\n  `/docs/changelog.txt`\n\nInstallation instructions (Russian):\n  `/docs/install.md`\n  \nInstallation instructions (English, google-translated):\n  `/docs/install-en.md`\n\n\nDocumentation for developers & administrators (Russian):\n  `/docs/html/developer.html`\n\nChangelog for developers & administrators (Russian):\n  `/docs/changelog_dev.txt`\n\nPlans for further development (Russian):\n  `/docs/todo.txt`\n\nCoding Gudelines for developers (English):\n  `/docs/html/coding-guidelines.html`\n\nBellitrized game background (Russian):\n  `/docs/SuperNova - All you didn't want to know but forced to.docx`\n\n\n# Copyright notice\nСуперНова copyright (c) 2009-2020 Gorlum для http://supernova.ws\n\nProject \"SuperNova.WS\" copyright (c) 2009-2020 by Gorlum for http://supernova.ws\n"
  },
  {
    "path": "_error-404.php",
    "content": "/* <?php\n$redirectFrom = !empty($_SERVER[\"REDIRECT_URL\"]) ? $_SERVER[\"REDIRECT_URL\"] : '';\n// $serverProtocol = !empty($_SERVER[\"SERVER_PROTOCOL\"]) ? $_SERVER[\"SERVER_PROTOCOL\"] : 'HTTP/1.0';\n\n// header($_SERVER[\"SERVER_PROTOCOL\"].\" 404 Not Found\");\n// @header(\"{$serverProtocol} 200 OK\");\necho \"File {$redirectFrom} not found on server. Contact administration if you think that this is error\";\ndie();\n?> */\n"
  },
  {
    "path": "admin/add_moon.php",
    "content": "<?php\n\n/**\n * admin/add_moon.php\n *\n * @version 2\n * @copyright 2014 Gorlum for http://supernova.ws\n */\n\nuse DBAL\\db_mysql;\nuse Planet\\DBStaticPlanet;\nuse Universe\\Universe;\n\ndefine('INSIDE', true);\ndefine('INSTALL', false);\ndefine('IN_ADMIN', true);\n\nrequire('../common.' . substr(strrchr(__FILE__, '.'), 1));\n\nglobal $lang, $user;\n\nSnTemplate::messageBoxAdminAccessDenied(AUTH_LEVEL_ADMINISTRATOR);\n\n$template = SnTemplate::gettemplate(\"admin/add_moon\", true);\n\nif(sys_get_param_str('mode') == 'addit')\n{\n  $PlanetID = sys_get_param_id('user');\n  $MoonName = sys_get_param_str('name');\n\n  db_mysql::db_transaction_start();\n  $PlanetSelected = DBStaticPlanet::db_planet_by_id($PlanetID, true);\n  uni_create_moon($PlanetSelected['galaxy'], $PlanetSelected['system'], $PlanetSelected['planet'], $PlanetSelected['id_owner'], Universe::moonSizeRandom(), false, ['name' => $MoonName]);\n  db_mysql::db_transaction_commit();\n\n  SnTemplate::messageBoxAdmin($lang['addm_done'], $lang['addm_title']);\n}\n\nSnTemplate::display($template, $lang['addm_title']);\n"
  },
  {
    "path": "admin/adm_flying_fleets.php",
    "content": "<?php\n\n/**\n * adm_flying_fleets.php\n *\n * @copyright 2014 by Gorlum for http://supernova.ws/\n */\n\nuse Fleet\\DbFleetStatic;\n\ndefine('INSIDE', true);\ndefine('INSTALL', false);\ndefine('IN_ADMIN', true);\nrequire('../common.' . substr(strrchr(__FILE__, '.'), 1));\n\nglobal $lang, $user;\n\nSnTemplate::messageBoxAdminAccessDenied(AUTH_LEVEL_ADMINISTRATOR);\n\n$template = SnTemplate::gettemplate('admin/adm_flying_fleets', true);\n\n//$FlyingFleets = db_fleet_list_all();\n//while($CurrentFleet = db_fetch($FlyingFleets))\n\n$all_flying_fleets = DbFleetStatic::db_fleet_list('', DB_SELECT_PLAIN);\nforeach($all_flying_fleets as $fleet_id => $CurrentFleet) {\n  $FleetOwner = db_user_by_id($CurrentFleet['fleet_owner']);\n  $TargetOwner = db_user_by_id($CurrentFleet['fleet_target_owner']);\n\n  $fleet_data = tpl_parse_fleet_db($CurrentFleet, ++$i, $FleetOwner);\n  $fleet_data['fleet']['OWNER_NAME'] = htmlentities($FleetOwner['username'], ENT_COMPAT, 'UTF-8');\n  $fleet_data['fleet']['TARGET_OWNER_NAME'] = htmlentities($TargetOwner['username'], ENT_COMPAT, 'UTF-8');\n\n  $fleet_data['fleet']['STAY_TIME_INT'] = $CurrentFleet['fleet_end_stay'];\n\n  $template->assign_block_vars('fleets', $fleet_data['fleet']);\n  foreach($fleet_data['ships'] as $ship_data) {\n    $template->assign_block_vars('fleets.ships', $ship_data);\n  }\n}\n\nSnTemplate::display($template, $lang['flt_title']);\n"
  },
  {
    "path": "admin/adm_log_main.php",
    "content": "<?php\n\n/**\n * admin/adm_log_main.php\n *\n * @version 2.0 - full rewrote\n * @copyright 2014 by Gorlum for http://supernova.ws\n *\n */\n\ndefine('INSIDE', true);\ndefine('INSTALL', false);\ndefine('IN_ADMIN', true);\n\nrequire('../common.' . substr(strrchr(__FILE__, '.'), 1));\n\nglobal $lang, $user;\n\nSnTemplate::messageBoxAdminAccessDenied(AUTH_LEVEL_ADMINISTRATOR);\n\nif($delete = sys_get_param_id('delete'))\n{\n  doquery(\"DELETE FROM `{{logs}}` WHERE `log_id` = {$delete} LIMIT 1;\");\n}\nelseif(sys_get_param_str('delete_update_info'))\n{\n  doquery(\"DELETE FROM `{{logs}}` WHERE `log_code` in (103, 180, 191);\");\n}\nelseif(sys_get_param_str('deleteall') == 'yes')\n{\n//  doquery(\"TRUNCATE TABLE `{{logs}}`\");\n}\n\n/**\n * @param $value\n *\n * @return string|null\n */\nfunction admLogRender($value) {\n  if (is_array($value)) {\n    $result = '<table class=\"no_border_image var_in\">';\n    foreach ($value as $key => $val) {\n      $result .= '<tr><td>' . $key . '</td><td>' . var_export($val, true) . '</td></tr>';\n    }\n    $result .= '</table>';\n  } else {\n    $result = var_export($value, true);\n  }\n\n  return $result;\n}\n\nif($detail = sys_get_param_id('detail'))\n{\n  $template = SnTemplate::gettemplate('admin/adm_log_main_detail', true);\n\n  $errorInfo = doquery(\"SELECT * FROM `{{logs}}` WHERE `log_id` = {$detail} LIMIT 1;\", true);\n  $error_dump = json_decode($errorInfo['log_dump'], true);\n  if(is_array($error_dump))\n  {\n    foreach ($error_dump as $key => $value)\n    {\n      // Parsing user options to readable state\n      if($key == 'user' ) {\n        if(!empty($value['options'])) {\n          $value['options'] = explode(USER_OPTIONS_SPLIT, $value['options']);\n        }\n      }\n\n      $val = $key == 'query_log' ? $value : admLogRender($value);\n\n      $template->assign_block_vars('vars', [\n        'VAR_NAME' => $key,\n        'VAR_VALUE' => $val,\n      ]);\n    }\n  }\n\n  // Replacing LF with HTML <br /> tag for better readability\n  $errorInfo['log_text'] = str_replace(\"\\n\", '<br />', $errorInfo['log_text']);\n\n  $template->assign_vars($errorInfo);\n}\nelse\n{\n  $template = SnTemplate::gettemplate('admin/adm_log_main', true);\n\n  $i = 0;\n  $query = doquery(\"SELECT * FROM `{{logs}}` ORDER BY log_id DESC LIMIT 100;\");\n  while($u = db_fetch($query))\n  {\n    $i++;\n    $v = [];\n    $u['log_text'] = str_replace(\"\\n\", \"<br />\", $u['log_text']);\n    foreach($u as $key => $value)\n    {\n      $v[strtoupper($key)] = $value;\n    }\n    $template->assign_block_vars('error', $v);\n  }\n  $query = doquery(\"SELECT COUNT(*) AS LOG_MESSAGES_TOTAL, {$i} AS LOG_MESSAGES_VISIBLE FROM `{{logs}}`;\", true);\n\n  $template->assign_vars($query);\n}\n\nSnTemplate::display($template, $lang['adm_er_ttle']);\n"
  },
  {
    "path": "admin/adm_message_list.php",
    "content": "<?php\n\n/**\n * admin/adm_message_list.php\n *\n * @version 2\n * @copyright 2014 by Gorlum for http://supernova.ws/\n *\n */\n\ndefine('INSIDE'  , true);\ndefine('INSTALL' , false);\ndefine('IN_ADMIN', true);\n\nrequire('../common.' . substr(strrchr(__FILE__, '.'), 1));\n\nglobal $lang, $user;\n\nSnTemplate::messageBoxAdminAccessDenied(AUTH_LEVEL_ADMINISTRATOR);\n\n$template = SnTemplate::gettemplate('admin/adm_messagelist', true);\n\n$int_type_selected = sys_get_param_int('int_type_selected', -1);\n$allowed_types = array(\n  -1 => array(\n    'VALUE' => -1,\n    'TEXT' => $lang['adm_pay_filter_all']\n  ),\n);\n$template->assign_block_vars('int_type_selected', $allowed_types[-1]);\nforeach($sn_message_class_list as $key => $value)\n{\n  if($key == MSG_TYPE_NEW || $key == MSG_TYPE_OUTBOX)\n  {\n    continue;\n  }\n\n  $template->assign_block_vars('int_type_selected', $allowed_types[$key] = array(\n    'VALUE' => $key,\n    'TEXT' => $lang['msg_class'][$key],\n  ));\n}\n\n\n$message_delete = sys_get_param_id('msg_del');\nif(sys_get_param('str_delete_selected') && is_array($message_delete = sys_get_param('selected')) && !empty($message_delete))\n{\n  $message_delete = implode(', ', $message_delete);\n}\n\nif($message_delete)\n{\n  doquery(\"DELETE FROM {{messages}} WHERE `message_id` in ({$message_delete});\");\n  $template->assign_block_vars('result', array('MESSAGE' => sprintf($lang['mlst_messages_deleted'], $message_delete)));\n}\n\n\nif(sys_get_param('str_delete_date') && checkdate($month = sys_get_param_id('delete_month'), $day = sys_get_param_id('delete_day'), $year = sys_get_param_id('delete_year')))\n{\n  $delete_date = \"{$year}-{$month}-{$day}\";\n  doquery(\"DELETE FROM {{messages}} WHERE message_time <= UNIX_TIMESTAMP('{$delete_date}')\" . ($int_type_selected >= 0 ? \" AND `message_type` = {$int_type_selected}\" : ''));\n  $template->assign_block_vars('result', array('MESSAGE' => sprintf($lang['mlst_messages_deleted_date'], $allowed_types[$int_type_selected]['TEXT'], $delete_date)));\n}\n\n\n$page_max = doquery('SELECT COUNT(*) AS `max` FROM `{{messages}}`' . ($int_type_selected >= 0 ? \" WHERE `message_type` = {$int_type_selected};\" : ''), true);\n$page_max = ceil($page_max['max'] / 25);\n\n$int_page_current = min(sys_get_param_id('int_page_current', 1), $page_max);\n\nif(sys_get_param('page_prev') && $int_page_current > 1)\n{\n  $int_page_current--;\n}\nelseif(sys_get_param('page_next') && $int_page_current < $page_max)\n{\n  $int_page_current++;\n}\n\nfor($i = 1; $i <= $page_max; $i++)\n{\n  $template->assign_block_vars('page', array('NUMBER' => $i));\n}\n\n$StartRec = ($int_page_current - 1) * 25;\n\n$Messages = db_message_list_admin_by_type($int_type_selected, $StartRec);\nwhile($row = db_fetch($Messages))\n{\n  $row['FROM'] = htmlentities($row['FROM'], ENT_COMPAT, 'UTF-8');\n  $row['OWNER_NAME'] = htmlentities($row['OWNER_NAME'], ENT_COMPAT, 'UTF-8');\n  $row['TEXT'] = nl2br($row['TEXT']);\n  $template->assign_block_vars('message', $row);\n}\n\n$template->assign_vars(array(\n  'PAGE_MAX' => $page_max,\n  'PAGE_CURRENT' => $int_page_current,\n  'TYPE_SELECTED' => $int_type_selected,\n));\n\nSnTemplate::display($template, $lang['mlst_title']);\n"
  },
  {
    "path": "admin/adm_metamatter.php",
    "content": "<?php\n\n/**\n * adm_meta_matter.php\n *\n * Adjust Meta Matter quantity\n *\n * @version 2.0 (c) copyright 2013-2017 by Gorlum for http://supernova.ws\n *\n */\n\nuse Common\\Exceptions\\ExceptionSnLocalized;\n\ndefine('INSIDE', true);\ndefine('INSTALL', false);\ndefine('IN_ADMIN', true);\n\nrequire('../common.' . substr(strrchr(__FILE__, '.'), 1));\n\nif(!SN::$gc->modules->countModulesInGroup('payment')) {\n  sys_redirect(SN_ROOT_VIRTUAL . 'admin/overview.php');\n}\n\nSnTemplate::messageBoxAdminAccessDenied(AUTH_LEVEL_ADMINISTRATOR);\n\n/**\n * @param classLocale $lang\n * @param array       $user\n * @param string      $accountIdOrName_unsafe\n * @param string      $playerIdOrName_unsafe\n * @param float       $points\n * @param string      $reason_unsafe\n * @param bool        $confirmed\n *\n * @throws ExceptionSnLocalized\n */\nfunction admin_meta_matter_model($lang, $user, $accountIdOrName_unsafe, $playerIdOrName_unsafe, $points, $reason_unsafe, $confirmed) {\n  // If no points and no username - nothing to do\n  if (!$points && !$playerIdOrName_unsafe && !$accountIdOrName_unsafe) {\n    return;\n  }\n\n  if (!$points) {\n    throw new ExceptionSnLocalized('adm_mm_err_points_empty', ERR_ERROR);\n  }\n\n  $account = new Account(SN::$auth->account->db);\n\n  if (!empty($accountIdOrName_unsafe)) {\n    if (\n      !$account->db_get_by_id($accountIdOrName_unsafe)\n      &&\n      !$account->db_get_by_name($accountIdOrName_unsafe)\n      &&\n      !$account->db_get_by_email($accountIdOrName_unsafe)\n    ) {\n      throw new ExceptionSnLocalized('adm_mm_err_account_not_found', ERR_ERROR);\n    }\n  } elseif (!empty($playerIdOrName_unsafe)) {\n    $row = dbPlayerByIdOrName($playerIdOrName_unsafe);\n    if (empty($row['id'])) {\n      throw new ExceptionSnLocalized('adm_mm_err_player_not_found', ERR_ERROR, null, array($playerIdOrName_unsafe));\n    }\n\n    if (!$account->dbGetByPlayerId($row['id'])) {\n      throw new ExceptionSnLocalized('adm_mm_err_player_no_account', ERR_ERROR, null, array($playerIdOrName_unsafe));\n    }\n  } else {\n    throw new ExceptionSnLocalized('adm_mm_err_account_and_player_empty', ERR_ERROR);\n  }\n\n  $sprintfPayload = array(\n    $account->account_name,\n    $account->account_id,\n    HelperString::numberFloorAndFormat($points),\n    !empty($row['id']) ? $row['id'] : 0,\n    !empty($row['username']) ? $row['username'] : ''\n  );\n\n  if ($confirmed) {\n    if (empty($account->metamatter_change(\n      RPG_ADMIN,\n      $points,\n      sprintf(\n        $lang['adm_mm_msg_change_mm_log_record'],\n        $account->account_id,\n        $account->account_name,\n        $user['id'],\n        $user['username'],\n        $reason_unsafe,\n        core_auth::$main_provider->account->account_id,\n        core_auth::$main_provider->account->account_name,\n        !empty($row['id']) ? $row['id'] : 0,\n        !empty($row['username']) ? $row['username'] : ''\n      )\n    ))) {\n      throw new ExceptionSnLocalized($lang['adm_mm_err_mm_change_failed'], ERR_ERROR);\n    }\n\n    throw new ExceptionSnLocalized('adm_mm_msg_mm_changed', ERR_NONE, null, $sprintfPayload);\n  } else {\n    throw new ExceptionSnLocalized('adm_mm_msg_confirm_mm_change', ERR_WARNING, null, $sprintfPayload);\n  }\n\n}\n\n\n/**\n * @param array       $user\n * @param classLocale $lang\n */\nfunction admin_meta_matter_view($user, $lang) {\n  $accountIdOrName_unsafe = sys_get_param_str_unsafe('accountId');\n  $playerIdOrName_unsafe = sys_get_param_str_unsafe('playerId');\n  $points = sys_get_param_float('points');\n  $reason_unsafe = sys_get_param_str_unsafe('reason');\n  $confirmed = sys_get_param('confirm_mm_change');\n  $confirmed = !empty($confirmed); // can't use empty() or isset() with function result in PHP 5.3\n\n  $template = SnTemplate::gettemplate(\"admin/adm_metamatter\", true);\n\n  try {\n    admin_meta_matter_model($lang, $user, $accountIdOrName_unsafe, $playerIdOrName_unsafe, $points, $reason_unsafe, $confirmed);\n  } catch (ExceptionSnLocalized $e) {\n    $template->assign_block_vars('result', array(\n      'MESSAGE' => $e->getMessageLocalized(),\n      'STATUS'  => $e->getCode() ? $e->getCode() : ERR_NONE,\n    ));\n\n    if ($e->getCode() != ERR_NONE) {\n      $template->assign_vars(array(\n        'ACCOUNT_ID' => sys_safe_output($accountIdOrName_unsafe),\n        'PLAYER_ID'  => sys_safe_output($playerIdOrName_unsafe),\n        'POINTS'     => $points,\n        'REASON'     => sys_safe_output($reason_unsafe),\n      ));\n    };\n\n    if ($e->getCode() == ERR_WARNING) {\n      $template->assign_vars(array(\n        'NEED_CONFIRMATION' => 1,\n      ));\n    }\n  }\n\n  SnTemplate::display($template, $lang['adm_dm_title']);\n}\n\nglobal $user, $lang;\n\nadmin_meta_matter_view($user, $lang);\n"
  },
  {
    "path": "admin/adm_planet_list.php",
    "content": "<?php\n\ndefine('INSIDE'  , true);\ndefine('INSTALL' , false);\ndefine('IN_ADMIN', true);\n\nrequire('../common.' . substr(strrchr(__FILE__, '.'), 1));\n\nglobal $config, $lang, $user;\n\nSnTemplate::messageBoxAdminAccessDenied(AUTH_LEVEL_ADMINISTRATOR);\n\n$planet_active = sys_get_param_int('planet_active');\nif(!$planet_active) {\n  $planet_type = sys_get_param_int('planet_type', 1);\n  $planet_type = $planet_type == 3 ? 3 : 1;\n} else {\n  $active_time = SN_TIME_NOW - SN::$config->game_users_online_timeout;\n}\n$table_parent_columns = $planet_type == 3 || $planet_active;\n\n$template = SnTemplate::gettemplate('admin/adm_planet_list', true);\n\n$query = db_planet_list_admin_list($table_parent_columns, $planet_active, $active_time, $planet_type);\nwhile ($planet_row = db_fetch($query)) {\n  $template->assign_block_vars('planet', array(\n    'ID'          => $planet_row['id'],\n    'NAME'        => js_safe_string($planet_row['name']),\n    'GALAXY'      => $planet_row['galaxy'],\n    'SYSTEM'      => $planet_row['system'],\n    'PLANET'      => $planet_row['planet'],\n    'PLANET_TYPE' => $planet_row['planet_type'],\n    'PLANET_TYPE_PRINT' => $lang['sys_planet_type_sh'][$planet_row['planet_type']],\n    'PARENT_ID'   => js_safe_string($planet_row['parent_planet']),\n    'PARENT_NAME' => js_safe_string($planet_row['parent_name']),\n    'OWNER'       => js_safe_string($planet_row['username']),\n    'OWNER_ID'    => $planet_row['id_owner'],\n  ));\n}\n\n$page_title =\n  $lang['adm_planet_list_title'] . ': ' .\n  ($planet_active ? $lang['adm_planet_active'] :\n    ($planet_type ? ($planet_type == 3 ? $lang['sys_moons'] : $lang['sys_planets']) : '')\n  );\n$template->assign_vars(array(\n  'PAGE_TITLE' => $page_title,\n\n  'PLANET_COUNT'  => SN::$db->db_num_rows($query),\n  'PARENT_COLUMN' => $table_parent_columns,\n));\n\nSnTemplate::display($template, $page_title);\n"
  },
  {
    "path": "admin/adm_quest.php",
    "content": "<?php\n\n/**\n * quest.php\n *\n * @v1 (c) copyright 2011 by Gorlum for http://supernova.ws\n *\n */\n\ndefine('INSIDE'  , true);\ndefine('INSTALL' , false);\ndefine('IN_ADMIN'  , true);\n\nrequire('../common.' . substr(strrchr(__FILE__, '.'), 1));\n\nSnTemplate::messageBoxAdminAccessDenied(AUTH_LEVEL_OPERATOR);\n\nroughQuestRenderWrapper();\n"
  },
  {
    "path": "admin/adm_user_analyze.php",
    "content": "<?php\n\n/**\n * adm_payment.php\n *\n * @version 1.0\n * @copyright 2013 by Gorlum for http://supernova.ws\n*/\ndefine('INSIDE', true);\ndefine('INSTALL', false);\ndefine('IN_ADMIN', true);\n\nrequire('../common.' . substr(strrchr(__FILE__, '.'), 1));\n\nglobal $lang, $user;\n\nSnTemplate::messageBoxAdminAccessDenied(AUTH_LEVEL_ADMINISTRATOR);\n\n// define('SESSION_INTERRUPT', 15*60); // Можно увеличить до 4 часов - никито не может сидеть 2 суток с перерывом менее 4 часов\n// define('SUSPICIOUS_LONG', 2 * 60*60); // Тогда это увеличиваем до, скажем суток - и там смотрим\n\ndefine('SESSION_INTERRUPT', 1 * 60*60); // Можно увеличить до 4 часов - никито не может сидеть 2 суток с перерывом менее 4 часов\ndefine('SUSPICIOUS_LONG', 16 * 60*60); // Тогда это увеличиваем до, скажем суток - и там смотрим\n\n\nfunction check_suspicious(&$session, &$session_list_last_id, &$row) {\n  $session[2] = $session[1] - $session[0];\n  if($session[2] > SUSPICIOUS_LONG)\n  {\n    $session[2] = pretty_time($session[2]);\n    $session[0] = date(FMT_DATE_TIME_SQL, $session[0]);\n    $session[1] = date(FMT_DATE_TIME_SQL, $session[1]);\n    $session_list_last_id[] = $session;\n  }\n  //$row ?\n  $session = array(\n//    0 => $row['time'], // start\n//    1 => $row['time'], // end\n    0 => $row['visit_time'], // start\n    1 => $row['visit_time'], // end\n  )\n   //: false\n   ;\n}\n\n$session_list = array();\n$query = doquery(\"SELECT `visit_time`, user_id FROM {{counter}} where user_id <> 0 and visit_time > UNIX_TIMESTAMP(DATE_SUB(NOW(), INTERVAL 7 DAY)) order by user_id, visit_time;\");\n$session = array();\nif($row = db_fetch($query)) {\n  $session = array(\n    0 => strtotime($row['visit_time']), // start\n    1 => strtotime($row['visit_time']), // end\n  );\n  $last_id = $row['user_id'];\n}\nwhile($row = db_fetch($query)) {\n  $row['visit_time'] = strtotime($row['visit_time']);\n  if($last_id == $row['user_id']) {\n    // Тот же юзер\n    if($row['visit_time'] - $session[1] <= SESSION_INTERRUPT) { // Та же сессия\n      $session[1] = $row['visit_time'];\n    } else {\n      // Новая сессия\n//      check_suspicious($session, $session_list[$last_id], $row);\n      $session[2] = $session[1] - $session[0];\n      if($session[2] > SUSPICIOUS_LONG)\n      {\n        $session[2] = pretty_time($session[2]);\n        $session[0] = date(FMT_DATE_TIME_SQL, $session[0]);\n        $session[1] = date(FMT_DATE_TIME_SQL, $session[1]);\n        $session_list[$last_id][] = $session;\n      }\n      $session = array(\n        0 => $row['visit_time'], // start\n        1 => $row['visit_time'], // end\n      );\n    }\n  } else {\n//    check_suspicious($session, $session_list[$last_id], $row);\n    $session[2] = $session[1] - $session[0];\n      if($session[2] > SUSPICIOUS_LONG)\n      {\n        $session[2] = pretty_time($session[2]);\n        $session[0] = date(FMT_DATE_TIME_SQL, $session[0]);\n        $session[1] = date(FMT_DATE_TIME_SQL, $session[1]);\n        $session_list[$last_id][] = $session;\n      }\n    $session = array(\n      0 => $row['visit_time'], // start\n      1 => $row['visit_time'], // end\n    );\n    $last_id = $row['user_id'];\n  }\n}\n\nif($last_id) {\n  // check_suspicious($session, $session_list[$last_id], $row = array('time' => 0));\n  $session[2] = $session[1] - $session[0];\n\n  if($session[2] > SUSPICIOUS_LONG)\n  {\n    $session[2] = pretty_time($session[2]);\n    $session[0] = date(FMT_DATE_TIME_SQL, $session[0]);\n    $session[1] = date(FMT_DATE_TIME_SQL, $session[1]);\n    $session_list[$last_id][] = $session;\n  }\n}\n\nprint(\"<table border='1'>\");\nprint(\"<tr>\");\nprint(\"<td>ID</td><td>Username</td><td>Start</td><td>End</td><td>Length</td>\");\nprint(\"<td>Last online</td>\");\nprint(\"</tr>\");\nforeach($session_list as $user_id => $value) {\n  $user_record = doquery(\"SELECT `username`, onlinetime FROM {{users}} WHERE id = {$user_id};\", true);\n  foreach($value as $interval_data) {\n    print(\"<tr>\");\n    print(\"<td>{$user_id}</td><td>{$user_record['username']}</td><td>{$interval_data[0]}</td><td>{$interval_data[1]}</td><td>{$interval_data[2]}</td>\");\n    print(\"<td>\" . date(FMT_DATE_TIME_SQL, $user_record['onlinetime']) . \"</td>\");\n    print(\"</tr>\");\n  }\n}\nprint(\"</table>\");\n"
  },
  {
    "path": "admin/adm_user_stat.php",
    "content": "<?php\n\n/**\n * adm_payment.php\n *\n * @version 1.0\n * @copyright 2013 by Gorlum for http://supernova.ws\n*/\ndefine('INSIDE', true);\ndefine('INSTALL', false);\ndefine('IN_ADMIN', true);\n\nrequire('../common.' . substr(strrchr(__FILE__, '.'), 1));\n\n\nfunction admin_date_sort($a, $b)\n{\n  return $a['DATE'] == $b['DATE'] ? 0 : ($a['DATE'] > $b['DATE'] ? 1 : -1);\n}\n\nif($user['authlevel'] < 3)\n{\n  AdminMessage($lang['adm_err_denied']);\n}\n\n$template = SnTemplate::gettemplate('admin/adm_user_stat', true);\n\n$dt_from = sys_get_param_date_sql('dt_from', '2000-01-01');\nif(strlen($dt_from) == 4)\n{\n  $dt_from .= '-01';\n}\n$dt_to = sys_get_param_date_sql('dt_to', date('Y-m-d', SN_TIME_NOW + PERIOD_DAY));\nif(strlen($dt_to) == 4)\n{\n  $dt_to .= '-01';\n}\n$sql_date = \n  ($dt_from ? \" AND register_time >= UNIX_TIMESTAMP(STR_TO_DATE('{$dt_from}', '%Y-%m-%d %H:%i:%s')) \" : '') .\n  ($dt_to ? \" AND register_time < UNIX_TIMESTAMP(STR_TO_DATE('{$dt_to}', '%Y-%m-%d %H:%i:%s')) \" : '');\n\n$min_max_dates = doquery($q = \"SELECT min(register_time) AS min_register, max(register_time) AS max_register, avg(onlinetime - register_time) AS avg_play_time,  STR_TO_DATE('{$dt_to}', '%Y-%m-%d %H:%i:%s') FROM {{users}} WHERE 1 \" . $sql_date, true);\n\n$interval = $min_max_dates['max_register'] - $min_max_dates['min_register'];\n\nswitch(true)\n{\n  case $interval >= PERIOD_YEAR * 4:\n    $sql_group_format = \"%Y\";\n    $sql_date_add = 'year';\n  break;\n\n  case $interval > PERIOD_DAY * 32:\n    $sql_group_format = \"%Y-%m\";\n    $sql_date_add = 'month';\n  break;\n\n  case $interval > PERIOD_DAY:\n    $sql_group_format = \"%Y-%m-%d\";\n    $sql_date_add = 'day';\n  break;\n\n  default:\n    $sql_group_format = \"%Y-%m-%d %H:00:00\";\n    $sql_date_add = 'hour';\n    $stop_next = true;\n  break;\n}\n\n$stat_date = array();\n$max_registered = 0;\n$max_accounts = 0;\n\n$sql_group_by2 = \"DATE_FORMAT(account_register_time, '{$sql_group_format}')\";\n$sql_date2 =\n  ($dt_from ? \" AND account_register_time >= (STR_TO_DATE('{$dt_from}', '%Y-%m-%d %H:%i:%s')) \" : '') .\n  ($dt_to ? \" AND account_register_time < (STR_TO_DATE('{$dt_to}', '%Y-%m-%d %H:%i:%s')) \" : '');\n$query = doquery(\n  \"SELECT \n    count(*) AS the_count, \n    DATE_FORMAT(account_register_time, '{$sql_group_format}') AS the_date, \n    DATE_FORMAT(account_register_time, '%a') AS DoW, \n    DATE_FORMAT(DATE_ADD(account_register_time, INTERVAL 1 {$sql_date_add}), '{$sql_group_format}') AS date_next\nFROM {{account}} WHERE 1 \" . $sql_date2 . \" GROUP BY {$sql_group_by2}\");\nwhile($row = db_fetch($query))\n{\n  $stat_date[$row['the_date']] = array(\n    'DATE' => $row['the_date'],\n    'DOW' => $row['DoW'],\n    'DATE_URL' => urlencode($row['the_date']),\n    'DATE_NEXT_URL' => urlencode($row['date_next']),\n    'ACCOUNTS' => $row['the_count'],\n  );\n\n  $max_accounts = max($max_accounts, $row['the_count']);\n}\n\n$sql_group_by = \"DATE_FORMAT(FROM_UNIXTIME(register_time), '{$sql_group_format}')\";\n$query = doquery(\"SELECT count(*) AS the_count, {$sql_group_by} AS the_date, DATE_FORMAT(DATE_ADD(FROM_UNIXTIME(register_time), INTERVAL 1 {$sql_date_add}), '{$sql_group_format}') AS date_next\nFROM {{users}} WHERE user_as_ally IS NULL \" . $sql_date . \" GROUP BY {$sql_group_by}\");\nwhile($row = db_fetch($query))\n{\n  $stat_date[$row['the_date']]['REGISTERED'] = $row['the_count'];\n\n  $max_registered = max($max_registered, $row['the_count']);\n}\n\n$query = doquery(\"SELECT count(*) AS the_count, {$sql_group_by} AS the_date FROM {{users}} WHERE user_as_ally IS NULL \" .\n\" AND onlinetime <= register_time + \" . PERIOD_DAY .\n' AND UNIX_TIMESTAMP(NOW()) >= register_time  + ' . PERIOD_DAY .\n$sql_date . \" GROUP BY {$sql_group_by}\");\nwhile($row = db_fetch($query))\n{\n  $stat_date[$row['the_date']]['REJECTED'] = $row['the_count'];\n}\n\n$query = doquery(\"SELECT count(*) AS the_count, {$sql_group_by} AS the_date FROM {{users}} WHERE user_as_ally IS NULL \" .\n\" AND onlinetime > register_time + \" . PERIOD_DAY .\n' AND onlinetime <= register_time + ' . PERIOD_WEEK .\n' AND UNIX_TIMESTAMP(NOW()) >= register_time + ' . PERIOD_WEEK .\n$sql_date . \" GROUP BY {$sql_group_by}\");\nwhile($row = db_fetch($query))\n{\n  $stat_date[$row['the_date']]['LEAVED'] = $row['the_count'];\n}\n\n$query = doquery(\"SELECT count(*) AS the_count, {$sql_group_by} AS the_date FROM {{users}} WHERE user_as_ally IS NULL \" .\n' AND UNIX_TIMESTAMP(NOW()) - onlinetime <= ' . PERIOD_DAY .\n' AND UNIX_TIMESTAMP(NOW()) - register_time >= ' . PERIOD_DAY .\n$sql_date . \" GROUP BY {$sql_group_by}\");\nwhile($row = db_fetch($query))\n{\n  $stat_date[$row['the_date']]['ACTIVE'] = $row['the_count'];\n}\n\n$query = doquery(\"SELECT count(*) AS the_count, {$sql_group_by} AS the_date FROM {{users}} WHERE user_as_ally IS NULL \" .\n' AND UNIX_TIMESTAMP(NOW()) - onlinetime > ' . PERIOD_DAY .\n' AND UNIX_TIMESTAMP(NOW()) - onlinetime <= ' . PERIOD_WEEK .\n' AND UNIX_TIMESTAMP(NOW()) - register_time >= ' . PERIOD_WEEK .\n$sql_date . \" GROUP BY {$sql_group_by}\");\nwhile($row = db_fetch($query))\n{\n  $stat_date[$row['the_date']]['DORMANT'] = $row['the_count'];\n}\n\nuasort($stat_date, 'admin_date_sort');\n\n$total = array();\nforeach($stat_date as $key => &$value)\n{\n  $value['TOTAL'] = $value['REJECTED'] + $value['LEAVED'];\n  $value['LEAVED_PERCENT'] = $value['REGISTERED'] ? round($value['TOTAL'] / $value['REGISTERED'] * 100) : 0;\n  $value['ACTIVE_PERCENT'] = $value['REGISTERED'] ? round($value['ACTIVE'] / $value['REGISTERED'] * 100) : 0;\n  $value['DORMANT_PERCENT'] = $value['REGISTERED'] ? round($value['DORMANT'] / $value['REGISTERED'] * 100) : 0;\n  foreach($value as $key2 => $value2)\n  {\n    $total[$key2] += $value2;\n  }\n  $value['REGISTERED_PERCENT'] = ceil($max_registered ? $value['REGISTERED'] * 100 / $max_registered : 0);\n  $template->assign_block_vars('stats', $value);\n}\n$total['DATE'] = 'Всего';\n$total['TH'] = 1;\n$total['LEAVED_PERCENT'] = count($stat_date) ? round($total['TOTAL'] / $total['REGISTERED'] * 100) : 0;\n$total['ACTIVE_PERCENT'] = count($stat_date) ? round($total['ACTIVE'] / $total['REGISTERED'] * 100) : 0;\n$total['DORMANT_PERCENT'] = count($stat_date) ? round($total['DORMANT'] / $total['REGISTERED'] * 100) : 0;\n$template->assign_block_vars('stats', $total);\n\n$template->assign_vars(array(\n  'AVG_PLAY_TIME' => round($min_max_dates['avg_play_time'] / PERIOD_DAY, 2),\n  'STOP_NEXT' => intval($stop_next),\n  'INTERVAL' => $sql_date_add,\n));\n\nSnTemplate::display($template, $lang['adm_user_stat'], false, '', true);\n"
  },
  {
    "path": "admin/admin_analyze_matter.php",
    "content": "<?php\n/**\n * Created by Gorlum 18.02.2017 19:37\n */\n\ndefine('INSIDE', true);\ndefine('INSTALL', false);\ndefine('IN_ADMIN', true);\n\nrequire('../common.' . substr(strrchr(__FILE__, '.'), 1));\n\nglobal $lang, $user;\n\nSnTemplate::messageBoxAdminAccessDenied(AUTH_LEVEL_ADMINISTRATOR);\n\n$constants = get_defined_constants(true);\n$rpgConstants = array();\nforeach($constants['user'] as $constantName => $constantValue) {\n  if(substr($constantName, 0, 4) == 'RPG_') {\n    $rpgConstants[$constantValue] = $constantName;\n  }\n}\n\n$spent = array();\n\n$result = SN::$db->doquery(\n  \"SELECT\n    CONCAT(log_dark_matter_reason, '_', IF(sign(sum(log_dark_matter_amount)) > 0, 1, -1)) as `BALANCE`,\n    log_dark_matter_reason as `REASON`,\n    sum(log_dark_matter_amount) as `DM_AMOUNT`,\n    count(log_dark_matter_amount) as `DM_COUNT`,\n    sign(sum(log_dark_matter_amount)) as `SIGN`\n  FROM `{{log_dark_matter}}`\n  GROUP BY log_dark_matter_reason, IF(sign((log_dark_matter_amount)) > 0, 1, -1) ORDER BY sum(log_dark_matter_amount) DESC;\"\n);\n\nwhile($row = SN::$db->db_fetch($result)) {\n  $row['CONSTANT'] = $rpgConstants[$row['REASON']];\n\n  $row['DM_AMOUNT_TEXT'] = HelperString::numberFloorAndFormat($row['DM_AMOUNT']);\n\n//  $row['TOTAL_AMOUNT'] = $row['DM_AMOUNT'];\n//  $row['TOTAL_COUNT'] = $row['DM_COUNT'];\n//  $row['TOTAL_AMOUNT_TEXT'] = pretty_number($row['TOTAL_AMOUNT']);\n\n  $spent[$row['BALANCE']] = $row;\n}\n\n$result = SN::$db->doquery(\n  \"SELECT\n    CONCAT(reason, '_', IF(sign(sum(amount)) > 0, 1, -1)) as `BALANCE`,\n    reason as `REASON`,\n    sum(amount) as `MM_AMOUNT`,\n    count(amount) as `MM_COUNT`,\n    sign(sum(amount)) as `SIGN`\n  FROM `{{log_metamatter}}`\n  GROUP BY reason, if(sign((amount)) > 0, 1, -1) ORDER BY sum(amount) DESC;\"\n);\n\nwhile($row = SN::$db->db_fetch($result)) {\n  if(empty($spent[$row['BALANCE']])) {\n    $spent[$row['BALANCE']] = array();\n  }\n\n  $row['CONSTANT'] = $rpgConstants[$row['REASON']];\n  $row['MM_AMOUNT_TEXT'] = HelperString::numberFloorAndFormat($row['MM_AMOUNT']);\n\n  $spent[$row['BALANCE']] = array_merge_recursive_numeric($spent[$row['BALANCE']], $row);\n}\n\nforeach($spent as &$row) {\n  @$row['TOTAL_COUNT'] = $row['MM_COUNT'] + $row['DM_COUNT'];\n  @$row['TOTAL_AMOUNT'] = $row['MM_AMOUNT'] + $row['DM_AMOUNT'];\n  @$row['TOTAL_AMOUNT_TEXT'] = HelperString::numberFloorAndFormat($row['TOTAL_AMOUNT']);\n  @$row['TOTAL_COUNT_TEXT'] = HelperString::numberFloorAndFormat($row['TOTAL_COUNT']);\n}\n\nusort($spent, function ($a, $b) {\n  return $a['TOTAL_AMOUNT'] < $b['TOTAL_AMOUNT'] ? -1 :\n    ($a['TOTAL_AMOUNT'] > $b['TOTAL_AMOUNT'] ? 1 : 0);\n});\n\n\n$template = SnTemplate::gettemplate(\"admin/admin_analyze_matter\", true);\nforeach ($spent as $row) {\n  $template->assign_block_vars('spent', $row);\n}\n$fromDate = SN::$db->doQueryAndFetch(\"SELECT min(log_dark_matter_timestamp) FROM `{{log_dark_matter}}`;\");\n$template->assign_var(\"MIN_DATE\", reset($fromDate));\n\n\nSnTemplate::display($template, '{Анализ расхода и прихода материи}');\n"
  },
  {
    "path": "admin/admin_chat.php",
    "content": "<?php\n\n/**\n * Project \"SuperNova.WS\" copyright (c) 2009-2017 Gorlum\n * @version #45d0#\n **/\n\ndefine('INSIDE', true);\ndefine('INSTALL', false);\ndefine('IN_ADMIN', true);\n\nrequire('../common.' . substr(strrchr(__FILE__, '.'), 1));\n\nSnTemplate::messageBoxAdminAccessDenied(AUTH_LEVEL_ADMINISTRATOR);\n\nglobal $user, $lang;\n\n$delete = sys_get_param_str('delete');\n$deleteall = sys_get_param_str('deleteall');\n\nif ($delete) {\n  doquery(\"DELETE FROM `{{chat}}` WHERE `messageid` = {$delete};\");\n} elseif ($deleteall == 'yes') {\n  doquery(\"DELETE FROM `{{chat}}`;\");\n}\n\n$template = SnTemplate::gettemplate('admin/admin_chat', true);\n\n$query = doquery(\"SELECT * FROM `{{chat}}` ORDER BY `messageid` DESC LIMIT 25;\");\nwhile ($e = db_fetch($query)) {\n  $template->assign_block_vars('message', array(\n    'ID'          => $e['messageid'],\n    'TIMESTAMP'   => str_replace(' ', '&nbsp;', date(FMT_DATE_TIME, $e['timestamp'])),\n    'PLAYER_NAME' => $e['user'],\n    'MESSAGE'     => nl2br($e['message']),\n  ));\n}\n\n$template->assign_vars(array(\n  'PAGE_HEADER' => $lang['adm_ch_ttle'],\n  'msg_num'     => SN::$gc->db->db_num_rows($query),\n));\n\nSnTemplate::display($template);\n"
  },
  {
    "path": "admin/admin_darkmatter.php",
    "content": "<?php\n\n/**\n * admin_darkmatter.php\n *\n * Adjust Dark Matter quantity\n *\n * @version 2.0 (c) copyright 2010-2017 by Gorlum for http://supernova.ws/\n *\n */\n\nuse Common\\Exceptions\\ExceptionSnLocalized;\n\ndefine('INSIDE', true);\ndefine('INSTALL', false);\ndefine('IN_ADMIN', true);\n\nrequire('../common.' . substr(strrchr(__FILE__, '.'), 1));\n\nSnTemplate::messageBoxAdminAccessDenied(AUTH_LEVEL_ADMINISTRATOR);\n\n/**\n * @param $lang\n * @param $user\n *\n * @throws ExceptionSnLocalized\n *\n */\nfunction admin_dark_matter_model($lang, $user) {\n  $points = sys_get_param_float('points');\n  $reason_unsafe = sys_get_param_str_unsafe('reason');\n  $playerIdOrName_unsafe = sys_get_param_str_unsafe('playerId');\n\n  // If no points and no username - nothing to do\n  if (!$points && !$playerIdOrName_unsafe) {\n    return;\n  }\n\n  if (!$points) {\n    throw new ExceptionSnLocalized('adm_dm_no_quant', ERR_ERROR);\n  }\n  if (empty($playerIdOrName_unsafe)) {\n    throw new ExceptionSnLocalized('adm_dm_no_dest', ERR_ERROR);\n  }\n\n  $row = dbPlayerByIdOrName($playerIdOrName_unsafe);\n  if (empty($row['id'])) {\n    throw new ExceptionSnLocalized('adm_dm_user_none', ERR_ERROR, null, array($playerIdOrName_unsafe));\n  }\n\n  // Does anything post to DB?\n  if (!rpg_points_change(\n    $row['id'],\n    RPG_ADMIN,\n    $points,\n    sprintf($lang['adm_matter_change_log_record'], $row['id'], $row['username'], $user['id'], $user['username'], $reason_unsafe)\n  )\n  ) {\n    // No? We will say it to user...\n    throw new ExceptionSnLocalized('adm_dm_add_err', ERR_ERROR);\n  }\n\n  throw new ExceptionSnLocalized(\n    'adm_dm_user_added',\n    ERR_NONE,\n    null,\n    array($row['username'], $row['id'], HelperString::numberFloorAndFormat($points))\n  );\n}\n\n\n/**\n * @param $template |null $template\n */\nfunction admin_dark_matter_view($template = null) {\n  global $user, $lang;\n\n  $playerIdOrName_unsafe = sys_get_param_str_unsafe('playerId');\n  $points = sys_get_param_float('points');\n  $reason_unsafe = sys_get_param_str_unsafe('reason');\n\n  $template = SnTemplate::gettemplate(\"admin/admin_darkmatter\", true);\n\n  try {\n    admin_dark_matter_model($lang, $user);\n  } catch (ExceptionSnLocalized $e) {\n    $template->assign_block_vars('result', array(\n      'MESSAGE' => $e->getMessageLocalized(),\n      'STATUS'  => $e->getCode() ? $e->getCode() : ERR_NONE,\n    ));\n\n    if ($e->getCode() != ERR_NONE) {\n      $template->assign_vars(array(\n        'PLAYER_ID' => sys_safe_output($playerIdOrName_unsafe),\n        'POINTS'    => $points,\n        'REASON'    => sys_safe_output($reason_unsafe),\n      ));\n    };\n\n  }\n\n  SnTemplate::display($template, $lang['adm_dm_title']);\n\n  return $template;\n}\n\nadmin_dark_matter_view();\n"
  },
  {
    "path": "admin/admin_locale.php",
    "content": "<?php\n\n/**\n * adm_locale.php\n *\n * @v1 (c) copyright 2011 by Gorlum for http://supernova.ws\n *\n */\n\ndefine('INSIDE'  , true);\ndefine('INSTALL' , false);\ndefine('IN_ADMIN'  , true);\n\nrequire('../common.' . substr(strrchr(__FILE__, '.'), 1));\n\nglobal $lang, $user;\n\nSnTemplate::messageBoxAdminAccessDenied(AUTH_LEVEL_ADMINISTRATOR);\n\nfunction adm_lng_assign_string($lang_id, $locale_string_name, $value) {\n  global $locale_string_template, $languages_info, $languages, $domain;\n\n  if(is_array($value)) {\n    foreach($value as $sub_key => $sub_value) {\n      adm_lng_assign_string($lang_id, \"{$locale_string_name}[{$sub_key}]\", $sub_value);\n    }\n  } elseif($value) {\n    if(!isset($locale_string_template[$locale_string_name])) {\n      $locale_string_template[$locale_string_name] = array();\n    }\n    $locale_string_template[$locale_string_name] = array_merge($locale_string_template[$locale_string_name], array(\"[{$lang_id}]\" => htmlentities($value, ENT_COMPAT, 'utf-8')));\n  }\n}\n\nfunction adm_lng_load($full_filename) {\n//  $lang_old = $lang;\n//  $lang = array();\n  require($full_filename);\n//  $lang_new = $lang;\n//  $lang = $lang_old;\n//  return $lang_new;\n  return $a_lang_array;\n}\n\nfunction adm_lng_parse_string($string_name, $string_value, $ident = '  ') {\n  global $domain, $lang_id;\n\n  $return = \"{$ident}'{$string_name}' => \";\n  if(isset($string_value[$lang_id]) && !is_array($string_value[$lang_id])) {\n    $return .= \"'\" . str_replace(array(\"\\\\\", \"'\"), array('\\\\\\\\', \"\\\\'\"), $string_value[$lang_id]) . \"',\";\n  } else {\n    $return .= \"array(\\r\\n\";\n    foreach($string_value as $arr_name => $arr_data) {\n      $return .= adm_lng_parse_string($arr_name, $arr_data, $ident . '  ');\n    }\n    $return .= \"{$ident}),\\r\\n\";\n  }\n\n  return $return . \"\\r\\n\";\n}\n\n$honor_constants = array(\n  'admin' => array(\n    '[adm_opt_ver_response]' => 'SNC_VER_',\n    '[adm_opt_ver_response_short]' => 'SNC_VER_',\n  ),\n\n  'alliance' => array(\n    '[ali_dip_relations]' => 'ALLY_DIPLOMACY_',\n  ),\n\n  'artifacts' => array(\n    '[art_moon_create]' => 'ART_',\n  ),\n\n  'fleet' => array(\n    '[fl_attack_error]' => 'ATTACK_',\n    '[fl_shrtcup]' => 'PT_',\n    '[fl_planettype]' => 'PT_',\n  ),\n\n  'infos' => array(\n    '[info]' => array('TECH_', 'MRC_', 'SHIP_', 'RES_', 'ART_', 'STRUC_'),\n  ),\n\n  'market' => array(\n    '[eco_mrk_errors]' => 'MARKET_', \n  ),\n\n  'quest' => array(\n    '[qst_status_list]' => 'QUEST_STATUS_', \n  ),\n\n  'tech' => array(\n    '[type_mission]' => 'MT_', \n    '[tech]' => array('TECH_', 'MRC_', 'SHIP_', 'RES_', 'ART_', 'STRUC_'),\n  ),\n\n);\n\nfunction adm_lng_write_string($string_name, $string_value, $ident = '  ', $string_name_prefix = '') {\n  global $lang_id, $file_handler, $constants, $honor_constants, $domain;\n\n  $string_name_new = false;\n\n  if(isset($honor_constants[$domain][$string_name_prefix])) {\n    $found_constants = array_keys($constants, $string_name);\n    foreach($found_constants as $constant_name) {\n      $honor_prefix_list = is_array($honor_constants[$domain][$string_name_prefix]) ? $honor_constants[$domain][$string_name_prefix] : array($honor_constants[$domain][$string_name_prefix]);\n      foreach($honor_prefix_list as $honor_prefix) {\n        if(strpos($constant_name, $honor_prefix) === 0) {\n          $string_name_new = $constant_name;\n          break;\n        }\n      }\n    }\n  }\n\n  $string_name_new = $string_name_new ? $string_name_new : \"'{$string_name}'\";\n  fwrite($file_handler, \"{$ident}{$string_name_new} => \");\n  if(isset($string_value[$lang_id]) && !is_array($string_value[$lang_id])) {\n    fwrite($file_handler, \"'\" . str_replace(array(\"\\\\\", \"'\"), array('\\\\\\\\', \"\\\\'\"), $string_value[$lang_id]) . \"',\");\n//    fwrite($file_handler, \"'\" . addslashes($string_value[$lang_id]) . \"',\");\n  } else {\n    $string_name_prefix = $string_name_prefix . \"[{$string_name}]\";\n    fwrite($file_handler, \"array(\\r\\n\");\n    foreach($string_value as $arr_name => $arr_data) {\n      adm_lng_write_string($arr_name, $arr_data, $ident . '  ', $string_name_prefix);\n    }\n    fwrite($file_handler, \"{$ident}),\\r\\n\");\n  }\n\n  fwrite($file_handler, \"\\r\\n\");\n}\n\n$template = SnTemplate::gettemplate('admin/admin_locale', true);\n\nlng_include('system');\nlng_include('tech');\nlng_include('admin');\n\n$languages = array();\n$language_domains = array();\n$languages_info = lng_get_list();\n$domain = sys_get_param_str('domain');\n\nif($domain) {\n  $lang_new = sys_get_param('lang_new');\n  if(!empty($lang_new) && is_array($lang_new)) {\n    $constants = get_defined_constants(true);\n    $constants = $constants['user'];\n    ksort($constants);\n    foreach($languages_info as $lang_id => $land_data) {\n      $file_handler = fopen(SN_ROOT_PHYSICAL . \"language/{$lang_id}/{$domain}.mo.php.new\", 'w');\n      fwrite($file_handler, \"<?php\\r\\n\\r\\n/*\\r\\n#############################################################################\n#  Filename: {$domain}.mo.php\n#  Project: SuperNova.WS\n#  Website: http://www.supernova.ws\n#  Description: Massive Multiplayer Online Browser Space Strategy Game\\r\\n#\\r\\n\");\n\n      foreach($land_data['LANG_COPYRIGHT'] as $lang_copyright) {\n        $lang_copyright = str_replace(array('&copy;', '&quot;', '&lt;', '&gt;'), array('©', '\"', '<', '>'), $lang_copyright);\n        fwrite($file_handler, \"#  {$lang_copyright}\\r\\n\");\n      }\n      fwrite($file_handler, \"#############################################################################\\r\\n*/\\r\\n\n/**\\r\\n*\\r\\n* @package language\\r\\n* @system [{$land_data['LANG_NAME_ENGLISH']}]\\r\\n* @version \" . SN_VERSION . \"\\r\\n*\\r\\n*/\\r\\n\n/**\\r\\n* DO NOT CHANGE\\r\\n*/\\r\\n\\r\\nif (!defined('INSIDE')) die();\\r\\n\n\\$a_lang_array = array(\\r\\n\");\n      foreach($lang_new as $string_name => $string_value) {\n        adm_lng_write_string($string_name, $string_value);\n      }\n      fwrite($file_handler, \");\\r\\n\");\n      fclose($file_handler);\n    }\n\n    sys_redirect(\"admin_locale.php?domain={$domain}\");\n  }\n\n  foreach($languages_info as $lang_id => $lang_data) {\n    $template->assign_block_vars('language', $lang_data);\n    $full_filename = SN_ROOT_PHYSICAL . \"language/{$lang_id}/{$domain}.mo.php\";\n    $languages[$lang_id] = adm_lng_load($full_filename . (file_exists($full_filename . '.new') ? '.new' : ''));\n    foreach($languages[$lang_id] as $locale_string_name => $cork) {\n      adm_lng_assign_string($lang_id, \"[{$locale_string_name}]\", $languages[$lang_id][$locale_string_name]);\n    }\n  }\n\n  foreach($locale_string_template as $locale_string_name => $locale_string_list) {\n    $template->assign_block_vars('string', array(\n      'NAME' => $locale_string_name,\n    ));\n\n    foreach($languages_info as $lang_id => $cork2) {\n      $template->assign_block_vars('string.locale', array(\n        'LANG' => $lang_id,\n        'VALUE' => $locale_string_list[\"[{$lang_id}]\"],\n      ));\n    }\n  }\n\n  $template->assign_vars(array(\n    'DOMAIN' => $domain,\n  ));\n} else {\n  $path = SN_ROOT_PHYSICAL . \"language/\";\n  $dir = dir($path);\n  while (false !== ($lang_id = $dir->read())) {\n    $full_path = $path . $lang_id;\n    if($lang_id[0] != \".\" && is_dir($full_path)) {\n      $lang_file_list = dir($full_path);\n      while (false !== ($filename = $lang_file_list->read())) {\n        $lang_domain = strtolower(substr($filename, 0, strpos($filename, '.')));\n        if(!$lang_domain) {\n          continue;\n        }\n\n        $file_ext = strtolower(substr($filename, strpos($filename, '.')));\n        if($lang_domain != 'language') {\n          if($file_ext == '.mo.php.new' || ($file_ext == '.mo.php' && empty($languages[$lang_id][$lang_domain]))) {\n            $language_domains[$lang_domain] = $lang_domain;\n            $languages[$lang_id][$lang_domain] = $lang_domain;\n          }\n        }\n      }\n    }\n  }\n  $dir->close();\n\n  foreach($language_domains as $lang_domain) {\n    $template->assign_block_vars('domain', array(\n      'NAME' => $lang_domain,\n    ));\n  }\n}\n\nSnTemplate::display($template, $lang['adm_lng_title']);\n"
  },
  {
    "path": "admin/admin_user_activity.php",
    "content": "<?php\n/** @noinspection SqlResolve */\n\nini_set('memory_limit', '512M');\n\ndefine('INSIDE', true);\ndefine('INSTALL', false);\ndefine('IN_ADMIN', true);\n\nrequire('../common.' . substr(strrchr(__FILE__, '.'), 1));\n\nglobal $lang, $user;\n\n//messageBoxAdminAccessDenied(AUTH_LEVEL_ADMINISTRATOR);\n\n\n$userId = sys_get_param_id('id');\n\n$template = SnTemplate::gettemplate('admin/admin_user_activity');\nvisualize($userId);\n$template->assign_recursive($template_result);\nSnTemplate::display($template, \"Активность игрока [{$userId}] {$template_result['USER_NAME']}\");\n\nfunction visualize($userId) {\n  global $template_result;\n\n  $activityPeriod = PERIOD_HOUR * 1;\n//  $activityPeriod = PERIOD_MINUTE_10;\n\n  $userIdSafe = round(floatval($userId));\n\n  $iter = SN::$gc->db->selectIterator(\n    \"SELECT c.visit_time, c.visit_length, c.counter_id\n    FROM `{{counter}}` as c\n    where \n          user_id={$userIdSafe}\n          AND visit_time > '2018-01-01'\n    order by visit_time desc;\"\n  );\n\n  $user = SN::$db->doQueryAndFetch(\"SELECT `username` FROM `{{users}}` WHERE `id` = {$userIdSafe}\");\n\n  $template_result += [\n    'RECORDS'   => count($iter),\n    'USER_ID'   => $userId,\n    'USER_NAME' => $user['username'],\n  ];\n\n  if (!count($iter)) {\n    return;\n  }\n\n  $from = null;\n  $to   = null;\n\n  $perHour = [];\n  foreach ($iter as $record) {\n    empty($to) ? $to = $record['visit_time'] : false;\n\n    $from = $record['visit_time'];\n\n    $time      = strtotime($record['visit_time']);\n    $hourStart = floor($time / $activityPeriod) * $activityPeriod;\n\n    $length = $record['visit_length'];\n    $length == 0 ? $length = 1 : false;\n\n    do {\n      $leftOfThisHour = $hourStart + $activityPeriod - $time;\n\n      if ($length < $leftOfThisHour) {\n        $spendOnThisHour = $length;\n        $length          = 0;\n      } else {\n        $spendOnThisHour = $leftOfThisHour;\n        $length          -= $leftOfThisHour;\n      }\n      $perHour[$hourStart] += $spendOnThisHour;\n\n      $hourStart += $activityPeriod;\n    } while ($length > 0);\n\n  }\n\n  $template_result += [\n    'PERIOD' => $activityPeriod,\n\n    'DATE_FROM' => $from,\n    'DATE_TO'   => $to,\n  ];\n\n  ksort($perHour);\n\n  end($perHour);\n  $lastHour = key($perHour);\n\n  reset($perHour);\n  $firstHour = key($perHour);\n  $thisHour  = $firstHour;\n\n  do {\n    if (empty($perHour[$thisHour])) {\n      $perHour[$thisHour] = 0;\n    }\n    $thisHour += $activityPeriod;\n  } while ($thisHour < $lastHour);\n\n  krsort($perHour);\n\n  end($perHour);\n  $lastHour = key($perHour);\n\n  $dayOpened  = null;\n  $toTemplate = [];\n  foreach ($perHour as $hour => $length) {\n    $openDay  = false;\n    $closeDay = false;\n\n    if (!$dayOpened) {\n      $openDay   = true;\n      $dayOpened = 1;\n    }\n\n    if ($dayOpened && (date('H', $hour) == 0 || $hour == $lastHour)) {\n      $closeDay  = true;\n      $dayOpened = 0;\n    }\n\n    $lengthPercent = $length / $activityPeriod * 100;\n    $toTemplate[]  = [\n      'TIME'           => date(FMT_TIME, $hour),\n      'LENGTH'         => $length,\n      'LENGTH_PERCENT' => $lengthPercent > 100 ? 100 : $lengthPercent,\n      'MINUTES'        => round($length / PERIOD_MINUTE, 1), // unused?\n      'TIME_CLASS'     => $length ? 'present' : 'none',\n\n      'OPEN_DAY'  => $openDay,\n      'CLOSE_DAY' => $closeDay,\n      'DATE'      => date(FMT_DATE, $hour),\n      'DAY_CLASS' => in_array(date('w', $hour), [0, 6]) ? 'weekend' : '',\n    ];\n\n    $template_result['.']['hourly'] = $toTemplate;\n  }\n\n}\n"
  },
  {
    "path": "admin/ajax_maintenance.php",
    "content": "<?php /** @noinspection SqlResolve */\n\nuse DBAL\\db_mysql;\n\ndefine('IN_ADMIN', true);\n\nrequire('../includes/init.' . substr(strrchr(__FILE__, '.'), 1));\n\nglobal $user, $lang, $result;\n/**\n * @var array $user\n */\n\nif(\n  ($user['authlevel'] < 3)\n  &&\n  (empty($config->admin_http_key) || empty($_GET['admin_http_key']) || $_GET['admin_http_key'] != $config->admin_http_key)\n)\n{\n  SnTemplate::messageBox($lang['sys_noalloaw'], $lang['sys_noaccess']);\n  die();\n}\n\n// If we have  already passed here through admin_http_key - then we can safely assume that it's admin\nif(empty($user['authlevel'])) {\n  $user['authlevel'] = 3;\n}\n\ndefine('IN_AJAX', true);\n\nlng_include('admin');\n\n$totaltime = microtime(true);\n$pack_until = date(\"Y-m-01 00:00:00\", SN_TIME_NOW - PERIOD_MONTH * 3);\n\n// [#] info_best_battles 1b0\n$best_reports = array();\nif(defined('MODULE_INFO_BEST_BATTLES_QUERY')) {\n  $query = doquery(MODULE_INFO_BEST_BATTLES_QUERY);\n  while($row = db_fetch($query)) {\n    $best_reports[] = $row['ube_report_id'];\n  }\n}\n$best_reports = !empty($best_reports) ? ' AND ube_report_id NOT IN (' . implode(',', $best_reports) . ')' : '';\n\n\n$ques = array(\n//  'DELETE {{users}}.* FROM {{users}} WHERE `user_as_ally` IS NULL and `onlinetime` < unix_timestamp(now()) - ( 60 * 60 * 24 * 45) and metamatter_total <= 0;',\n\n  // Выводим из отпуска игроков, которые находятся там более 4 недель\n//  'UPDATE {{users}}\n//  SET vacation = 0, vacation_next = 0\n//  WHERE\n//    authlevel = 0 AND user_as_ally IS NULL AND user_bot = ' . USER_BOT_PLAYER . ' /* Не админы, Не Альянсы, Не боты */\n//    AND vacation > 0 AND banaday = 0 /* В отпуске и не в бане */\n//    AND vacation < UNIX_TIMESTAMP(DATE_SUB(NOW(), INTERVAL 4 WEEK)) /* Находящиеся в отпуске более 4 недель */;',\n\n//  // Игроки удаляются по Регламенту\n//  'DELETE FROM `{{users}}` WHERE\n//    authlevel = 0 AND user_as_ally IS NULL AND user_bot = ' . USER_BOT_PLAYER . ' AND metamatter_total = 0 AND /* Не админы, Не Альянсы, Не боты, Не Бессмертные*/\n//    metamatter = 0 AND /* Нету ММ */\n//    vacation = 0 AND banaday = 0 AND /* Не в отпуске, Не в бане */\n//    (\n//      (onlinetime - register_time < 5 * 60 AND UNIX_TIMESTAMP() - onlinetime > 2*7 *86400)\n//      OR (onlinetime - register_time < 30 * 60 AND UNIX_TIMESTAMP() - onlinetime > 4*7 *86400)\n//      OR (onlinetime - register_time < 10 * 60*60 AND UNIX_TIMESTAMP() - onlinetime > 6*7 *86400)\n//      OR (UNIX_TIMESTAMP() - onlinetime > 8*7 *86400)\n//    );',\n\n  // Игроки, которые не были активны более 4 недель становятся I-шками. Для них\n  // Отключаем получение писем\n//  'UPDATE {{users}}\n//  SET OPTIONS = \"\"\n//  WHERE\n//    authlevel = 0 AND user_as_ally IS NULL AND user_bot = ' . USER_BOT_PLAYER . ' AND vacation = 0 /* Не админы, Не Альянсы, Не боты, Не в отпуске */\n//    AND onlinetime < UNIX_TIMESTAMP(DATE_SUB(NOW(), INTERVAL 4 WEEK)) /* Не выходившие в онлайн более 4 недель */;',\n//  // Отключаем производство на планетах\n//  'UPDATE {{users}} AS u\n//    JOIN {{planets}} AS p ON p.id_owner = u.id\n//  SET\n//    metal_perhour = 0,\n//    crystal_perhour  = 0,\n//    deuterium_perhour  = 0,\n//    metal_mine_porcent = 0,\n//    crystal_mine_porcent = 0,\n//    deuterium_sintetizer_porcent = 0,\n//    solar_plant_porcent = 0,\n//    fusion_plant_porcent = 0,\n//    solar_satelit_porcent = 0,\n//    ship_sattelite_sloth_porcent = 0\n//  WHERE\n//\t\tauthlevel = 0 AND user_as_ally IS NULL AND user_bot = ' . USER_BOT_PLAYER . ' AND vacation = 0 /* Не админы, Не Альянсы, Не боты, Не в отпуске */\n//\t\tAND onlinetime < UNIX_TIMESTAMP(DATE_SUB(NOW(), INTERVAL 4 WEEK)) /* Не выходившие в онлайн более 4 недель */;',\n  // Удаляем все здания из очереди\n//  'DELETE q FROM {{users}} AS u JOIN {{que}} AS q ON q.que_player_id = u.id\n//  WHERE\n//\t\tauthlevel = 0 AND user_as_ally IS NULL AND user_bot = ' . USER_BOT_PLAYER . ' AND vacation = 0 /* Не админы, Не Альянсы, Не боты, Не в отпуске */\n//\t\tAND onlinetime < UNIX_TIMESTAMP(DATE_SUB(NOW(), INTERVAL 4 WEEK)) /* Не выходившие в онлайн более 4 недель */;',\n  // Возвращаем все флоты ???\n  // Пока не будем делать запрос - за 4 недели всяко все флоты должны вернутся...\n  // TODO I-шки - неделя на разграбление - или сколько там стата хранится...\n\n  // Удаляем планеты без пользователей\n  'DELETE FROM `{{planets}}` WHERE `id_owner` not in (select id from `{{users}}`) AND id_owner <> 0;', // TODO NO FK Переписать на джоине\n  // Удаляем юниты без планет\n  'DELETE un FROM `{{unit}}` AS un\n    LEFT JOIN `{{planets}}` AS pl ON pl.id = un.unit_location_id\n  WHERE unit_location_type = ' . LOC_PLANET . ' AND pl.id IS NULL;',\n  // Удаляем пустые юниты с 0 уровнем (кроме Капитана) - TODO - перенести в модуль, если нужно!\n//  'DELETE FROM {{unit}} WHERE unit_location_type = ' . LOC_PLANET . ' AND unit_level = 0 AND unit_type <> ' . UNIT_CAPTAIN,\n  // Удаляем очереди на ничьих планетах\n  'DELETE q FROM `{{que}}` AS q\n    LEFT JOIN `{{planets}}` AS p ON p.id = q.que_planet_id\n  WHERE\n    que_type IN (' . QUE_STRUCTURES . ', ' . QUE_HANGAR . ', ' . SUBQUE_FLEET . ', ' . SUBQUE_DEFENSE . ')\n    AND\n    (p.id_owner = 0 OR p.id_owner IS NULL);',\n\n  // Удаляем пустые САБы\n  'DELETE FROM `{{aks}}` WHERE `id` NOT IN (SELECT DISTINCT `fleet_group` FROM `{{fleets}}`);', // TODO Переписать на джоине\n\n  // UBE reports\n  \"DELETE FROM `{{ube_report}}` WHERE `ube_report_time_combat` < DATE_SUB(NOW(), INTERVAL 60 DAY) {$best_reports};\", // TODO Настройка\n\n  // Чистка сообщений - ВРЕМЕННО ОТКЛЮЧЕНО\n//  'DELETE FROM `{{messages}}`  WHERE `message_owner`  not in (select id from {{users}});', // TODO NO FK\n  // Удаляются сообщения, старше  4 недель, кроме личных и Альянсовских\n  'DELETE FROM `{{messages}}` WHERE\n    UNIX_TIMESTAMP() - message_time > 4*7 * 24 * 60 * 60 AND\n    message_type NOT IN (' . MSG_TYPE_PLAYER . ', ' . MSG_TYPE_ALLIANCE . ', ' . MSG_TYPE_ADMIN . ');',\n  // Удаляются сообщения у пользователей, которые неактивны больше 4 недель - кроме личных и Альянсовских\n  'DELETE m FROM `{{users}}` AS u\n  JOIN `{{messages}}` AS m ON m.message_owner = u.id\n  WHERE\n    message_type NOT IN (' . MSG_TYPE_PLAYER . ', ' . MSG_TYPE_ALLIANCE . ') AND\n    authlevel = 0 AND  user_as_ally IS NULL AND /* Не админы, Не Альянсы */\n    UNIX_TIMESTAMP() - onlinetime > 4*7 *86400;',\n\n  'DELETE FROM `{{chat}}` WHERE timestamp < unix_timestamp(now()) - (60 * 60 * 24 * 14);',\n\n  // Recalculate Alliance members\n  \"UPDATE `{{alliance}}` as a LEFT JOIN (SELECT ally_id, count(*) as ally_memeber_count FROM `{{users}}` WHERE ally_id IS NOT NULL GROUP BY ally_id) as u ON u.ally_id = a.id\n    SET a.`ally_members` = u.ally_memeber_count;\",\n  // Deleting empty Alliances - ВРЕМЕННО ОТКЛЮЧЕНО\n//  'DELETE FROM {{alliance}} WHERE id not in (select ally_id from {{users}} WHERE `user_as_ally` IS NOT NULL group by ally_id);',\n//  'DELETE FROM {{alliance}} WHERE ally_members <= 0;',\n  \"UPDATE `{{users}}` SET ally_id = null, ally_name = null, ally_tag = null, ally_register_time = 0, ally_rank_id = 0 WHERE ally_id not in (select id from `{{alliance}}`);\",\n\n  // Пакуем данные по логу ТМ\n  array(\n    \"INSERT INTO {{log_dark_matter}}\n      (log_dark_matter_timestamp, log_dark_matter_username, log_dark_matter_reason, log_dark_matter_amount,\n      log_dark_matter_comment, log_dark_matter_page, log_dark_matter_sender)\n    SELECT\n      '{$pack_until}', IF(u.username IS NULL, ldm.log_dark_matter_username, u.username), \" . RPG_CUMULATIVE . \", sum(ldm.log_dark_matter_amount),\n      'Баланс на {$pack_until}', 'admin/ajax_maintenance.php', ldm.log_dark_matter_sender\n    FROM\n      {{log_dark_matter}} AS ldm\n      LEFT JOIN {{users}} AS u ON u.id = ldm.log_dark_matter_sender\n    WHERE\n      ldm.log_dark_matter_timestamp < '{$pack_until}'\n    GROUP BY\n      log_dark_matter_sender;\",\n\n    \"DELETE FROM `{{log_dark_matter}}` WHERE log_dark_matter_timestamp < '{$pack_until}';\",\n  ),\n\n  // Пакуем статистические данные по онлайну пользователей\n  array(\n    \"REPLACE INTO `{{log_users_online}}`\n      (online_timestamp, online_count, online_aggregated)\n    SELECT\n      FROM_UNIXTIME((UNIX_TIMESTAMP(online_timestamp) DIV \" . PERIOD_MINUTE_10 . \") * (\" . PERIOD_MINUTE_10 . \")), ceil(avg(online_count)), \" . LOG_ONLIINE_AGGREGATE_PERIOD_MINUTE_10 . \"\n    FROM\n      `{{log_users_online}}`\n    WHERE\n      online_timestamp < '{$pack_until}' AND online_aggregated = \" . LOG_ONLIINE_AGGREGATE_NONE . \"\n    GROUP BY\n      (UNIX_TIMESTAMP(online_timestamp) DIV \" . PERIOD_MINUTE_10 . \") * (\" . PERIOD_MINUTE_10 . \");\",\n\n    \"DELETE FROM `{{log_users_online}}` WHERE online_timestamp < '{$pack_until}' AND online_aggregated = \" . LOG_ONLIINE_AGGREGATE_NONE,\n  ),\n\n  // Удаляем старые записи из логов\n  \"DELETE FROM `{{logs}}` WHERE log_timestamp < '{$pack_until}';\",\n  // Удаляем записи о маинтенансе, апдейте и пересчете статистики более чем недельной давности - они нам уже не нужны\n  'DELETE FROM `{{logs}}` WHERE\n    `log_code` IN (' . LOG_INFO_DB_CHANGE . ', ' . LOG_INFO_MAINTENANCE . ', ' . LOG_INFO_STAT_START . ', ' . LOG_INFO_STAT_PROCESS . ', ' . LOG_INFO_STAT_FINISH . ')\n    AND `log_timestamp` < DATE_SUB(NOW(),INTERVAL 7 DAY);',\n\n\n  // Удаляем вхождения игроков, на которые никто не ссылается\n  \"DELETE spe FROM `{{security_player_entry}}` AS spe\n    LEFT JOIN `{{counter}}` AS c ON c.player_entry_id = spe.id\n  WHERE c.counter_id IS NULL;\",\n  //  Удаляем устройства, на которые никто не ссылается\n  \"DELETE sd FROM `{{security_device}}` AS sd\n    LEFT JOIN `{{security_player_entry}}` AS spe ON spe.device_id = sd.device_id\n  WHERE spe.id IS NULL;\",\n  // Удаляем браузеры, на которые никто не ссылается\n  \"DELETE sb FROM `{{security_browser}}` AS sb\n    LEFT JOIN `{{security_player_entry}}` AS spe ON spe.browser_id = sb.browser_id\n  WHERE spe.id IS NULL;\",\n  // Удаляем строки запросов, на которые никто не ссылается\n  \"DELETE sqs FROM `{{security_query_strings}}` AS sqs\n    LEFT JOIN `{{counter}}` AS c ON c.query_string_id = sqs.id\n  WHERE c.counter_id IS NULL;\",\n  // Удаляем УРЛы, на которые никто не ссылается\n  \"DELETE su FROM `{{security_url}}` AS su\n    LEFT JOIN `{{counter}}` AS c ON c.page_url_id = su.url_id\n  WHERE c.counter_id IS NULL;\",\n\n//  \"INSERT INTO {{counter}} SET\n//        `page_url_id` = {$this->page_address_id},\n\n\n  // Удаляем записи визитов без пользователей\n//  'DELETE FROM `{{counter}}` WHERE `user_id` NOT IN (SELECT `id` FROM `{{users}}`);',\n);\n\nfunction sn_maintenance_pack_user_list($user_list) {\n  $user_list = explode(',', $user_list);\n  foreach($user_list as $key => $user_id) {\n    if(!is_numeric($user_id)) {\n      unset($user_list[$key]);\n    }\n  }\n\n  $result = array();\n  if(!empty($user_list)) {\n    $query = doquery(\"SELECT `id` FROM `{{users}}` WHERE `id` in (\" . implode(',', $user_list) . \")\");\n    while($row = db_fetch($query)) {\n      $result[] = $row['id'];\n    }\n  }\n\n  return implode(',', $result);\n}\n\nglobal $config, $debug, $lang;\n\ndb_mysql::db_transaction_start();\n$old_server_status = SN::$config->pass()->game_disable;\n$old_server_status == GAME_DISABLE_NONE ? SN::$config->pass()->game_disable = GAME_DISABLE_MAINTENANCE : false;\ndb_mysql::db_transaction_commit();\n\nforeach($ques as $que_transaction) {\n  db_mysql::db_transaction_start();\n\n  !is_array($que_transaction) ? $que_transaction = array($que_transaction) : false;\n  foreach($que_transaction as $que) {\n    set_time_limit(120);\n    $QryResult = doquery($que);\n    //$msg .= '<hr>' . $que . '<hr>';\n    $que = str_replace(array('{{', '}}'), '', $que);\n    //$que = str_replace('{{', '', $que);\n    //$que = str_replace('}}', '', $que);\n\n    $msg .=\n      '<li>' . htmlspecialchars($que) .\n        ' --- <span style=\"' . ($QryResult ? 'ok\">OK' : 'error\">FAILED!') . '</span> ' .\n      SN::$db->db_affected_rows() . ' ' . $lang['adm_records'] .\n      \"</li>\";\n\n    $debug->warning($que . ' --- ' . ($QryResult ? 'OK' : 'FAILED!') . ' ' . SN::$db->db_affected_rows() . ' ' . $lang['adm_records'], 'System maintenance', LOG_INFO_MAINTENANCE);\n  }\n\n  db_mysql::db_transaction_commit();\n}\n\ndb_mysql::db_transaction_start();\nSN::$config->pass()->stats_hide_player_list = sn_maintenance_pack_user_list(SN::$config->pass()->stats_hide_player_list);\n$debug->warning('Упакован stats_hide_player_list', 'System maintenance', LOG_INFO_MAINTENANCE);\ndb_mysql::db_transaction_commit();\n\ndb_mysql::db_transaction_start();\nSN::$config->db_saveItem('game_watchlist', sn_maintenance_pack_user_list(SN::$config->pass()->game_watchlist));\n$debug->warning('Упакован game_watchlist', 'System maintenance', LOG_INFO_MAINTENANCE);\ndb_mysql::db_transaction_commit();\n\nSN::$config->db_saveItem('users_amount', db_user_count());\nSN::$config->db_saveItem('game_disable', $old_server_status);\n\n$_GET['admin_update'] = 1;\n\ninclude_once('../scheduler.php');\n$totaltime = microtime(true) - $totaltime;\n\n$result = $result ? \"<li>{$lang['adm_stat_title']} - {$result}</li>\" : '';\n$result = '<div align=\"left\"><ul>' . $msg . $result . '</ul></div>';\necho json_encode($result . ' ' . $totaltime);\n"
  },
  {
    "path": "admin/banned.php",
    "content": "<?php\n\n/**\n * @copyright Copyright (c) 2009 by Gorlum for http://supernova.ws\n */\ndefine('INSIDE', true);\ndefine('INSTALL', false);\ndefine('IN_ADMIN', true);\n\nrequire('../common.' . substr(strrchr(__FILE__, '.'), 1));\n\nSnTemplate::messageBoxAdminAccessDenied(AUTH_LEVEL_MODERATOR);\n\nglobal $lang, $user;\n\n$mode = sys_get_param_str('mode', 'banit');\n$name_unsafe = sys_get_param_str_unsafe('name');\n$name_output = sys_safe_output($name_unsafe);\n$action = sys_get_param_str('action');\n\n$player_banned_row = db_user_by_username($name_unsafe);\nif ($mode == 'banit' && $action) {\n  if ($player_banned_row) {\n    $reas = $_POST['why'];\n    $days = $_POST['days'];\n    $hour = $_POST['hour'];\n    $mins = $_POST['mins'];\n    $secs = $_POST['secs'];\n\n    $BanTime = $days * 86400;\n    $BanTime += $hour * 3600;\n    $BanTime += $mins * 60;\n    $BanTime += $secs;\n\n    sys_admin_player_ban($user, $player_banned_row, $BanTime, $is_vacation = sys_get_param_int('isVacation'), sys_get_param_str('why'));\n\n    $DoneMessage = \"{$lang['adm_bn_thpl']} {$name_output} {$lang['adm_bn_isbn']}\";\n\n    if ($is_vacation) {\n      $DoneMessage .= $lang['adm_bn_vctn'];\n    }\n\n    $DoneMessage .= $lang['adm_bn_plnt'];\n  } else {\n    $DoneMessage = sprintf($lang['adm_bn_errr'], $name_output);\n  }\n\n  SnTemplate::messageBoxAdmin($DoneMessage, $lang['adm_ban_title']);\n} elseif ($mode == 'unbanit' && $action) {\n  sys_admin_player_ban_unset($user, $player_banned_row, ($reason = sys_get_param_str('why')) ? $reason : $lang['sys_unbanned']);\n\n  $DoneMessage = $lang['adm_unbn_thpl'] . \" \" . $name_output . \" \" . $lang['adm_unbn_isbn'];\n  SnTemplate::messageBoxAdmin($DoneMessage, $lang['adm_unbn_ttle']);\n};\n\n$parsetemplate = SnTemplate::gettemplate(\"admin/admin_ban\", true);\n\n$parsetemplate->assign_vars(array(\n  'name' => $name_output,\n  'mode' => $mode,\n));\n\nSnTemplate::display($parsetemplate, $lang['adm_ban_title']);\n"
  },
  {
    "path": "admin/includes/admin_planet_edit.inc.php",
    "content": "<?php\n\nfunction admin_planet_edit_mode(&$template, &$admin_planet_edit_mode_list){return sn_function_call('admin_planet_edit_mode', array(&$template, &$admin_planet_edit_mode_list));}\nfunction sn_admin_planet_edit_mode(&$template, &$admin_planet_edit_mode_list)\n{\n  global $lang;\n\n  $admin_planet_edit_mode_list = array_merge(isset($admin_planet_edit_mode_list) ? $admin_planet_edit_mode_list : array(), array(\n    'structures' => $lang['tech'][UNIT_STRUCTURES],\n    'fleet' => $lang['tech'][UNIT_SHIPS],\n    'defense' => $lang['tech'][UNIT_DEFENCE],\n    'resources_loot' => $lang['tech'][UNIT_RESOURCES],\n  ));\n\n  $mode = sys_get_param_str('mode');\n  $admin_planet_edit_mode_list_keys = array_keys($admin_planet_edit_mode_list);\n  $mode = in_array($mode, $admin_planet_edit_mode_list_keys) ? $mode : $admin_planet_edit_mode_list_keys[0];\n\n  return $mode;\n}\n\nfunction admin_planet_edit_template(&$template, $edit_planet_row, $mode){return sn_function_call('admin_planet_edit_template', array(&$template, $edit_planet_row, $mode));}\n/**\n * @param template $template\n * @param $edit_planet_row\n * @param $mode\n */\nfunction sn_admin_planet_edit_template(&$template, $edit_planet_row, $mode)\n{\n  global $lang;\n\n  $unit_list = sn_get_groups($mode);\n  if(empty($unit_list))\n  {\n    return;\n  }\n  $name_list = $lang['tech'];\n\n  foreach($unit_list as $unit_id)\n  {\n    $template->assign_block_vars('unit', array(\n      'ID'    => $unit_id,\n      'NAME'  => $name_list[$unit_id],\n      'TEXT'  => HelperString::numberFloorAndFormat(mrc_get_level($user, $edit_planet_row, $unit_id, false, true)),\n      'VALUE' => '',\n    ));\n  }\n}\n\nfunction admin_planet_edit_query_string($unit_id, $unit_amount, $mode){return sn_function_call('admin_planet_edit_query_string', array($unit_id, $unit_amount, $mode));}\nfunction sn_admin_planet_edit_query_string($unit_id, $unit_amount, $mode)\n{\n  if($unit_amount && in_array($unit_id, sn_get_groups($mode)))\n  {\n    $unit_amount = round($unit_amount);\n    $unit_name = get_unit_param($unit_id, P_NAME);\n    $result = \"{$unit_name} = GREATEST(0, {$unit_name} + ({$unit_amount}))\";\n  }\n  else\n  {\n    $result = '';\n  }\n\n  return $result;\n}\n"
  },
  {
    "path": "admin/index.html",
    "content": ""
  },
  {
    "path": "admin/maintenance.php",
    "content": "<?php\n\n/**\n * maintenance.php\n *\n * @version 1.0\n * @copyright 2009 by Gorlum for http://oGame.Triolan.COM.UA\n */\ndefine('INSIDE', true);\ndefine('INSTALL', false);\ndefine('IN_ADMIN', true);\n\nrequire('../common.' . substr(strrchr(__FILE__, '.'), 1));\n\nglobal $lang, $user;\n\nSnTemplate::messageBoxAdminAccessDenied(AUTH_LEVEL_ADMINISTRATOR);\n\n$script = '\n<script type=\"text/javascript\">\n$(document).ready(function() {\n  $.post(\"admin/ajax_maintenance.php\", function(result) {\n    $(\"#admin_message\").html(result);\n  }, \"json\" );\n});\n</script>';\n\nSnTemplate::messageBoxAdmin($script . '<img src=design/images/progressbar.gif><br>' . $lang['sys_wait'], $lang['adm_maintenance_title']);\n"
  },
  {
    "path": "admin/overview.php",
    "content": "<?php\n\n/**\n *\n * admin/overview.php\n *\n * @version 2.0 copyright (c) 2014 Gorlum for http://supernova.ws\n *\n */\n\ndefine('ADMIN_USER_OVERVIEW', true);\n\nrequire_once('userlist.php');\n"
  },
  {
    "path": "admin/planet_compensate.php",
    "content": "<?php\n\nuse DBAL\\db_mysql;\nuse Planet\\DBStaticPlanet;\nuse Unit\\DBStaticUnit;\n\ndefine('INSIDE', true);\ndefine('INSTALL', false);\ndefine('IN_ADMIN', true);\n\nrequire('../common.' . substr(strrchr(__FILE__, '.'), 1));\n\nglobal $lang, $user;\n\nSnTemplate::messageBoxAdminAccessDenied(AUTH_LEVEL_ADMINISTRATOR);\n\n$template = SnTemplate::gettemplate('admin/planet_compensate', true);\n\n$galaxy_src = sys_get_param_int('galaxy_src');\n$system_src = sys_get_param_int('system_src');\n$planet_src = sys_get_param_int('planet_src');\n\n$galaxy_dst = sys_get_param_int('galaxy_dst');\n$system_dst = sys_get_param_int('system_dst');\n$planet_dst = sys_get_param_int('planet_dst');\n\n$bonus = sys_get_param_float('bonus', 1);\n\n$username_unsafe = sys_get_param_str_unsafe('username');\n$username = sys_get_param_escaped('username');\n\nif ($galaxy_src) {\n  $errors = array();\n\n  $owner = db_user_by_username($username_unsafe, true);\n  $planet = DBStaticPlanet::db_planet_by_gspt($galaxy_src, $system_src, $planet_src, PT_PLANET);\n  if (empty($planet)) {\n    $errors[] = $lang['adm_pl_comp_err_0'];\n  }\n  if ($planet['destruyed']) {\n    $errors[] = $lang['adm_pl_comp_err_1'];\n  }\n  if (empty($username) || empty($owner) || $planet['id_owner'] != $owner['id']) {\n    $errors[] = $lang['adm_pl_comp_err_4'];\n  }\n\n  $destination = DBStaticPlanet::db_planet_by_gspt($galaxy_dst, $system_dst, $planet_dst, PT_PLANET);\n  if (empty($destination)) {\n    $errors[] = $lang['adm_pl_comp_err_2'];\n  }\n  if ($planet['id'] == $destination['id']) {\n    $errors[] = $lang['adm_pl_comp_err_5'];\n  }\n  if ($planet['id_owner'] != $destination['id_owner']) {\n    $errors[] = $lang['adm_pl_comp_err_3'];\n  }\n\n  $moon = DBStaticPlanet::db_planet_by_gspt($galaxy_src, $system_src, $planet_src, PT_MOON);\n  if (!empty($errors)) {\n    foreach ($errors as $error) {\n      $template->assign_block_vars('error', array(\n        'TEXT' => $error,\n      ));\n    }\n  } else {\n  db_mysql::db_transaction_start();\n  SN::$gc->db->lockRecords([\n    'users'   => [$owner['id'],],\n    'planets' => [$planet['id'], $destination['id'], !empty($moon['id']) ? $moon['id'] : 0],\n  ]);\n\n  $planet = sys_o_get_updated($owner['id'], $planet['id'], SN_TIME_NOW);\n  $que = $planet['que'];\n  $planet = $planet['planet'];\n\n  $destination = sys_o_get_updated($owner['id'], $destination['id'], SN_TIME_NOW);\n  $destination = $destination['planet'];\n\n    $template->assign_var('CHECK', 1);\n\n    $final_cost = killer_add_planet($planet);\n\n    if (!empty($moon)) {\n      $moon = sys_o_get_updated($owner['id'], $moon['id'], SN_TIME_NOW);\n      $moon = $moon['planet'];\n      $final_cost = killer_add_planet($moon, $final_cost);\n    }\n\n    foreach (sn_get_groups('resources_loot') as $resource_id) {\n      $resource_name = pname_resource_name($resource_id);\n      $template->assign_var(\"{$resource_name}_cost\", $final_cost[$resource_id]);\n      $final_cost[$resource_id] = floor($final_cost[$resource_id] * $bonus);\n      $template->assign_var(\"{$resource_name}_bonus\", $final_cost[$resource_id]);\n    }\n\n    if ($_GET['btn_confirm']) {\n      $time = SN_TIME_NOW + PERIOD_DAY;\n\n      DBStaticUnit::db_unit_list_delete($planet['id_owner'], LOC_PLANET, $planet['id']);\n      DBStaticPlanet::db_planet_set_by_id($planet['id'], \"id_owner = 0, destruyed = {$time}\");\n      if (!empty($moon)) {\n        DBStaticUnit::db_unit_list_delete($planet['id_owner'], LOC_PLANET, $moon['id']);\n        DBStaticPlanet::db_planet_set_by_id($moon['id'], \"id_owner = 0, destruyed = {$time}\");\n      }\n\n      DBStaticPlanet::db_planet_set_by_id($destination['id'], \"metal = metal + '{$final_cost[RES_METAL]}', crystal = crystal + '{$final_cost[RES_CRYSTAL]}', deuterium = deuterium + '{$final_cost[RES_DEUTERIUM]}'\");\n      $template->assign_var('CHECK', 2);\n    }\n    db_mysql::db_transaction_commit();\n  }\n}\n\n$template->assign_vars(array(\n  'galaxy_src' => $galaxy_src,\n  'system_src' => $system_src,\n  'planet_src' => $planet_src,\n\n  'galaxy_dst' => $galaxy_dst,\n  'system_dst' => $system_dst,\n  'planet_dst' => $planet_dst,\n\n  'bonus' => $bonus,\n\n  'username' => $username,\n));\n\nSnTemplate::display($template, $lang['adm_pl_comp_title']);\n\n/**\n * @param array $planet\n * @param array $final_cost\n *\n * @return array|mixed\n */\nfunction killer_add_planet($planet, $final_cost = []) {\n  $sn_group_resources_loot = sn_get_groups('resources_loot');\n\n  // Adding structures cost\n  foreach (sn_get_groups('structures') as $unit_id) {\n    $build_level = mrc_get_level($user, $planet, $unit_id, true, true);\n    if ($build_level > 0) {\n      $unit_cost = get_unit_param($unit_id, 'cost');\n      $build_factor = $unit_cost['factor'] != 1 ? (1 - pow($unit_cost['factor'], $build_level)) / (1 - $unit_cost['factor']) : $unit_cost['factor'];\n      foreach ($sn_group_resources_loot as $resource_id) {\n        $final_cost[$resource_id] += isset($unit_cost[$resource_id]) && $unit_cost[$resource_id] > 0 ? floor($unit_cost[$resource_id] * $build_factor) : 0;\n      }\n    }\n  }\n  // Adding fleet and defense cost\n  foreach (sn_get_groups(array('defense', 'fleet')) as $unit_id) {\n    $unit_count = mrc_get_level($user, $planet, $unit_id, true, true);\n    if ($unit_count > 0) {\n      $unit_cost = get_unit_param($unit_id, 'cost');\n      foreach ($sn_group_resources_loot as $resource_id) {\n        $final_cost[$resource_id] += isset($unit_cost[$resource_id]) && $unit_cost[$resource_id] > 0 ? floor($unit_cost[$resource_id] * $unit_count) : 0;\n      }\n    }\n  }\n  // Adding plain resources\n  foreach ($sn_group_resources_loot as $resource_id) {\n    $final_cost[$resource_id] += floor(mrc_get_level($user, $planet, $resource_id, true, true));\n  }\n\n  return $final_cost;\n}\n"
  },
  {
    "path": "admin/planet_edit.php",
    "content": "<?php\n\nuse Planet\\DBStaticPlanet;\nuse Player\\RecordPlayer;\nuse Unit\\DBStaticUnit;\n\ndefine('INSIDE', true);\ndefine('INSTALL', false);\ndefine('IN_ADMIN', true);\n\nrequire('../common.' . substr(strrchr(__FILE__, '.'), 1));\n\nglobal $lang, $user;\n\nSnTemplate::messageBoxAdminAccessDenied(AUTH_LEVEL_ADMINISTRATOR);\n\n//messageBoxAdmin('Временно не работает');\n\nrequire(\"includes/admin_planet_edit.inc\" . DOT_PHP_EX);\n\n$template = SnTemplate::gettemplate('admin/admin_planet_edit', true);\n\n$mode      = admin_planet_edit_mode($template, $admin_planet_edit_mode_list);\n$planet_id = sys_get_param_id('planet_id');\n\n$unit_list = sys_get_param('unit_list');\nif (sys_get_param('change_data') && !empty($unit_list)) {\n  planet_edit_model($planet_id, $unit_list, $mode);\n}\n\nif ($planet_id) {\n  $edit_planet_row = DBStaticPlanet::db_planet_by_id($planet_id);\n  admin_planet_edit_template($template, $edit_planet_row, $mode);\n}\n\nforeach ($admin_planet_edit_mode_list as $page_mode => $mode_locale) {\n  $template->assign_block_vars('page_menu', array(\n    'ID'   => $page_mode,\n    'TEXT' => $mode_locale,\n  ));\n}\n\n$template->assign_vars(array(\n  'MODE'        => $mode,\n  'PLANET_ID'   => $planet_id,\n  'PLANET_NAME' => empty($edit_planet_row) ? '' : $lang['sys_planet_type'][$edit_planet_row['planet_type']] . ' ' . uni_render_planet($edit_planet_row),\n  'PAGE_HINT'   => $lang['adm_planet_edit_hint'],\n));\n\nSnTemplate::display($template, $lang['adm_am_ttle']);\n\n\n/**\n * @param       $planet_id\n * @param array $unit_list\n * @param       $mode\n */\nfunction planet_edit_model($planet_id, array $unit_list, $mode) {\n  $thePlanet = DBStaticPlanet::db_planet_by_id($planet_id);\n  $theUserId = $thePlanet['id_owner'];\n  $thePlayer = RecordPlayer::findRecordById($theUserId);\n\n  $query_string = [];\n  foreach ($unit_list as $unit_id => $unit_amount) {\n    if ($mode === 'resources_loot') {\n      if (!floatval($unit_amount)) {\n        continue;\n      }\n\n      if ($unit_query_string = admin_planet_edit_query_string($unit_id, $unit_amount, $mode)) {\n        $query_string[] = $unit_query_string;\n      }\n    } elseif (in_array($mode, [UNIT_SHIPS_STR, UNIT_STRUCTURES_STR, UNIT_DEFENCE_STR,]) ) {\n      if (!floatval($unit_amount)) {\n        continue;\n      }\n\n      $currentAmount = mrc_get_level($thePlayer, $thePlanet, $unit_id);\n\n      $newAmount = $currentAmount + $unit_amount;\n\n      if ($newAmount <= 0) {\n        DBStaticUnit::db_unit_list_delete($theUserId, LOC_PLANET, $planet_id, $unit_id);\n      } else {\n        DBStaticUnit::dbChangeUnit($theUserId, $planet_id, $unit_id, $unit_amount);\n\n        DBStaticUnit::cache_clear();\n      }\n    }\n\n  }\n\n  if (!empty($query_string)) {\n    DBStaticPlanet::db_planet_set_by_id($planet_id, implode(', ', $query_string));\n  }\n\n}\n"
  },
  {
    "path": "admin/settings.php",
    "content": "<?php\n\n/** @noinspection PhpDefineCanBeReplacedWithConstInspection */\n/** @noinspection PhpRedundantOptionalArgumentInspection */\n\n/**\n * settings.php\n *\n * @version 2.0 Full rewrite copyright (c) Gorlum 2009-2010 for http://supernova.ws\n * @version 1.0\n * @copyright 2008 by ??????? for XNova\n */\n\nuse Unit\\DBStaticUnit;\n\ndefine('INSIDE'  , true);\ndefine('INSTALL' , false);\ndefine('IN_ADMIN', true);\nrequire('../common.' . substr(strrchr(__FILE__, '.'), 1));\n\nglobal $lang, $user, $sn_version_check_class;\n\nSnTemplate::messageBoxAdminAccessDenied(AUTH_LEVEL_ADMINISTRATOR);\n\n$template = SnTemplate::gettemplate('admin/settings', true);\n\nif(sys_get_param('save')) {\n  SN::$config->game_name               = sys_get_param_str_unsafe('game_name');\n  SN::$config->game_mode               = sys_get_param_int('game_mode');\n  SN::$config->game_speed              = sys_get_param_float('game_speed', 1);\n  SN::$config->fleet_speed             = sys_get_param_float('fleet_speed', 1);\n  SN::$config->resource_multiplier     = sys_get_param_float('resource_multiplier', 1);\n  SN::$config->user_vacation_disable   = sys_get_param_int('user_vacation_disable', 0);\n  SN::$config->url_faq                 = sys_get_param_str_unsafe('url_faq');\n  SN::$config->url_forum               = sys_get_param_str_unsafe('url_forum');\n  SN::$config->url_rules               = sys_get_param_str_unsafe('url_rules');\n  SN::$config->url_purchase_metamatter         = sys_get_param_str_unsafe('url_purchase_metamatter');\n  SN::$config->game_disable            = sys_get_param_int('game_disable');\n  SN::$config->game_disable_reason     = sys_get_param_str_unsafe('game_disable_reason');\n  SN::$config->server_updater_check_auto = sys_get_param_int('server_updater_check_auto');\n\n  SN::$config->game_user_changename      = sys_get_param_int('game_user_changename', SN::$config->game_user_changename);\n  SN::$config->game_user_changename_cost = sys_get_param_int('game_user_changename_cost', SN::$config->game_user_changename_cost);\n\n  SN::$config->eco_scale_storage       = sys_get_param_int('eco_scale_storage');\n\n  SN::$config->game_default_language   = sys_get_param_str_unsafe('game_default_language', DEFAULT_LANG);\n  SN::$config->game_default_skin       = sys_get_param_str_unsafe('game_default_skin', DEFAULT_SKINPATH);\n  SN::$config->game_default_template   = sys_get_param_str_unsafe('game_default_template', SnTemplate::getServerDefaultTemplateName());\n\n  SN::$config->game_maxGalaxy          = sys_get_param_int('game_maxGalaxy', 5);\n  SN::$config->game_maxSystem          = sys_get_param_int('game_maxSystem', 199);\n  SN::$config->game_maxPlanet          = sys_get_param_int('game_maxPlanet', 16);\n\n  SN::$config->player_max_colonies     = sys_get_param_int('player_max_colonies', -1);\n\n  SN::$config->fleet_bashing_attacks   = sys_get_param_int('fleet_bashing_attacks', 3);\n  SN::$config->fleet_bashing_interval  = sys_get_param_int('fleet_bashing_interval', 30 * 60);\n  SN::$config->fleet_bashing_scope     = sys_get_param_int('fleet_bashing_scope', 24 * 60 * 60);\n  SN::$config->fleet_bashing_war_delay = sys_get_param_int('fleet_bashing_war_delay', 12 * 60 * 60);\n  SN::$config->fleet_bashing_waves     = sys_get_param_int('fleet_bashing_waves', 3);\n\n  SN::$config->allow_buffing           = sys_get_param_int('allow_buffing');\n  SN::$config->ally_help_weak          = sys_get_param_int('ally_help_weak');\n  SN::$config->game_email_pm           = sys_get_param_int('game_email_pm');\n\n  SN::$config->rpg_exchange_metal      = sys_get_param_int('rpg_exchange_metal', 1);\n  SN::$config->rpg_exchange_crystal    = sys_get_param_int('rpg_exchange_crystal', 2);\n  SN::$config->rpg_exchange_deuterium  = sys_get_param_int('rpg_exchange_deuterium', 4);\n  SN::$config->rpg_exchange_darkMatter = sys_get_param_int('rpg_exchange_darkMatter', 400);\n\n  SN::$config->tpl_minifier            = sys_get_param_int('tpl_minifier', 0);\n\n  SN::$config->initial_fields          = sys_get_param_int('initial_fields', 200);\n  SN::$config->eco_planet_starting_metal = sys_get_param_float('eco_planet_starting_metal', 500);\n  SN::$config->eco_planet_starting_crystal = sys_get_param_float('eco_planet_starting_crystal', 500);\n  SN::$config->eco_planet_starting_deuterium = sys_get_param_float('eco_planet_starting_deuterium', 0);\n  SN::$config->metal_basic_income      = sys_get_param_float('metal_basic_income', 40);\n  SN::$config->crystal_basic_income    = sys_get_param_float('crystal_basic_income', 20);\n  SN::$config->deuterium_basic_income  = sys_get_param_float('deuterium_basic_income', 10);\n  SN::$config->energy_basic_income     = sys_get_param_float('energy_basic_income', 0);\n  SN::$config->eco_planet_storage_metal = sys_get_param_float('eco_planet_storage_metal', BASE_STORAGE_SIZE);\n  SN::$config->eco_planet_storage_crystal = sys_get_param_float('eco_planet_storage_crystal', BASE_STORAGE_SIZE);\n  SN::$config->eco_planet_storage_deuterium = sys_get_param_float('eco_planet_storage_deuterium', BASE_STORAGE_SIZE);\n\n  SN::$config->chat_timeout            = sys_get_param_int('chat_timeout', 5);\n\n  SN::$config->game_news_overview      = sys_get_param_int('game_news_overview', 5);\n  SN::$config->advGoogleLeftMenuIsOn   = sys_get_param_int('advGoogleLeftMenuIsOn');\n  SN::$config->advGoogleLeftMenuCode   = sys_get_param('advGoogleLeftMenuCode');\n  SN::$config->debug                   = sys_get_param_int('debug');\n  SN::$config->game_counter            = sys_get_param_int('game_counter');\n  SN::$config->geoip_whois_url         = sys_get_param_str('geoip_whois_url');\n\n  SN::$config->uni_price_galaxy        = sys_get_param_float('uni_price_galaxy');\n  SN::$config->uni_price_system        = sys_get_param_float('uni_price_system');\n\n  SN::$config->user_birthday_gift      = sys_get_param_float('user_birthday_gift');\n  SN::$config->user_birthday_range     = sys_get_param_int('user_birthday_range');\n\n  SN::$config->stats_hide_admins       = sys_get_param_int('stats_hide_admins');\n  SN::$config->stats_hide_player_list  = sys_get_param_str('stats_hide_player_list');\n  SN::$config->stats_hide_pm_link      = sys_get_param_int('stats_hide_pm_link');\n  SN::$config->stats_schedule          = sys_get_param_str('stats_schedule');\n\n  SN::$config->empire_mercenary_base_period = sys_get_param_int('empire_mercenary_base_period');\n  if(SN::$config->empire_mercenary_temporary != sys_get_param_int('empire_mercenary_temporary')) {\n    if(SN::$config->empire_mercenary_temporary) {\n      DBStaticUnit::db_unit_list_admin_delete_mercenaries_finished();\n    } else {\n      DBStaticUnit::db_unit_list_admin_set_mercenaries_expire_time(SN::$config->empire_mercenary_base_period);\n    }\n\n    SN::$config->empire_mercenary_temporary = sys_get_param_int('empire_mercenary_temporary');\n  }\n\n  SN::$config->db_saveAll();\n\n  $template->assign_var('MESSAGE', $lang['adm_opt_saved']);\n}\n\n$template->assign_vars([\n  'ALLOW_BUFFING' => SN::$config->allow_buffing,\n  'ALLY_HELP_WEAK' => SN::$config->ally_help_weak,\n  'GAME_EMAIL_PM' => SN::$config->game_email_pm,\n  'game_mode' => SN::$config->game_mode,\n  'game_language' => SN::$config->game_default_language,\n  'ECO_SCALE_STORAGE' => SN::$config->eco_scale_storage,\n  'USER_VACATION_DISABLE' => SN::$config->user_vacation_disable,\n  'ADV_LEFT_MENU' => SN::$config->advGoogleLeftMenuIsOn,\n  'GAME_DISABLE' => SN::$config->game_disable,\n  'GAME_DEBUG' => SN::$config->debug,\n  'GAME_COUNTER' => SN::$config->game_counter,\n  'TPL_MINIFIER' => SN::$config->tpl_minifier,\n  'EMPIRE_MERCENARY_TEMPORARY' => SN::$config->empire_mercenary_temporary,\n\n  'SERVER_UPDATE_CHECK_AUTO' => SN::$config->server_updater_check_auto,\n  'CHECK_DATE' => SN::$config->server_updater_check_last ? date(FMT_DATE_TIME, SN::$config->server_updater_check_last) : 0,\n  'CHECK_RESULT' => isset($lang['adm_opt_ver_response'][SN::$config->server_updater_check_result]) ? $lang['adm_opt_ver_response'][SN::$config->server_updater_check_result] : $lang['adm_opt_ver_response'][SNC_VER_UNKNOWN_RESPONSE],\n  'CHECK_CLASS' => isset($sn_version_check_class[SN::$config->server_updater_check_result]) ? $sn_version_check_class[SN::$config->server_updater_check_result] : $sn_version_check_class[SNC_VER_UNKNOWN_RESPONSE],\n\n  'SERVER_UPDATE_ID' => SN::$config->server_updater_id,\n  'SERVER_UPDATE_KEY' => SN::$config->server_updater_key,\n\n  'STATS_HIDE_ADMINS' => SN::$config->stats_hide_admins,\n  'STATS_HIDE_PM_LINK' => SN::$config->stats_hide_pm_link,\n\n  'GAME_CHANGE_NAME'      => SN::$config->game_user_changename,\n  'GAME_CHANGE_NAME_COST' => SN::$config->game_user_changename_cost,\n]);\n\nSnTemplate::tpl_assign_select($template, 'change_name_options', SN::$lang['adm_opt_player_change_name_options']);\nSnTemplate::tpl_assign_select($template, 'sys_game_disable_reason', SN::$lang['sys_game_disable_reason'], 'ID', 'NAME');\nSnTemplate::tpl_assign_select($template, 'game_modes', SN::$lang['sys_game_mode'], 'ID', 'NAME');\nSnTemplate::tpl_assign_select($template, 'ver_response', SN::$lang['adm_opt_ver_response'], 'ID', 'NAME');\n\n$lang_list = lng_get_list();\nforeach($lang_list as $lang_id => $lang_data) {\n  $template->assign_block_vars('game_languages', array(\n    'ID'   => $lang_id,\n    'NAME' => \"{$lang_data['LANG_NAME_NATIVE']} ({$lang_data['LANG_NAME_ENGLISH']})\",\n  ));\n}\n\nSnTemplate::display($template, $lang['adm_opt_title']);\n"
  },
  {
    "path": "admin/statbuilder.php",
    "content": "<?php\n\n/**\n * StatBuilder.php\n *\n * @version 1.1 (c) copyright 2010 by Gorlum for http://supernova.ws\n *   [*] All calculations moved to StatFunctions.php - thus we can utilize them in automatized stats calculations\n * @version 1\n * @copyright 2008 by Chlorel for XNova\n */\n\ndefine('INSIDE'  , true);\ndefine('INSTALL' , false);\ndefine('IN_ADMIN', true);\nrequire_once('../common.' . substr(strrchr(__FILE__, '.'), 1));\n\nglobal $lang, $user;\n\nSnTemplate::messageBoxAdminAccessDenied(AUTH_LEVEL_ADMINISTRATOR);\n\nif(SN_TIME_NOW >= SN::$config->pass()->var_stat_update_admin_forced && SN_TIME_NOW >= SN::$config->pass()->var_stat_update_end)\n{\n  SN::$config->pass()->var_stat_update_admin_forced = SN_TIME_NOW + 120;\n\n  $script = '<script type=\"text/javascript\">\n  $(document).ready(function() {\n    // send requests\n    $.post(\"scheduler.php?admin_update=1&' . SN_TIME_NOW . '\", function(result) {\n      // format result\n      // alert(xml);\n      // var result = [ $(\"message\", xml).text() ];\n      // output result\n      // $(\"#admin_message\").html(result.join(\"\"));\n      $(\"#admin_message\").html(result);\n    }, \"json\" );\n  });\n  </script>';\n\n  SnTemplate::messageBoxAdmin(\"{$script}<img src=\\\"design/images/progressbar.gif\\\"><br>{$lang['sys_wait']}\", $lang['adm_stat_title'], '', 0);\n}\nelse\n{\n  SnTemplate::messageBoxAdmin($lang['adm_stat_already_started'], $lang['adm_stat_title'], 'admin/overview.php');\n}\n\n// require_once('../scheduler.php');\n"
  },
  {
    "path": "admin/sxd/.htaccess",
    "content": "<Files cfg.php>\norder allow,deny\ndeny from all\n</Files>\n\n<Files ses.php>\norder allow,deny\ndeny from all\n</Files>\n\n<Files tmpl.php>\norder allow,deny\ndeny from all\n</Files>\n\n<Files sxd.lic.php>\norder allow,deny\ndeny from all\n</Files>"
  },
  {
    "path": "admin/sxd/backup/.htaccess",
    "content": "AddType application/octetstream .gz .bz2 .sql\n\n<Files ~ \"\\.(php|log|rtl|stp)$\">\norder allow,deny\ndeny from all\n</Files>\n"
  },
  {
    "path": "admin/sxd/backup/index.htm",
    "content": ""
  },
  {
    "path": "admin/sxd/bsd_license.txt",
    "content": "Copyright (c) 2003-2009, zapimir\nportions copyright (c) 2005-2009 Binovator\nAll rights reserved.\n\nRedistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:\n\n    * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.\n    * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.\n    * Neither the name of Sypex nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n"
  },
  {
    "path": "admin/sxd/img/index.htm",
    "content": ""
  },
  {
    "path": "admin/sxd/index.php",
    "content": "<?php\n/***************************************************************************\\\n| Sypex Dumper               version 2.0.11                                 |\n| (c) 2003-2011 zapimir      zapimir@zapimir.net       http://sypex.net/    |\n| (c) 2005-2011 BINOVATOR    info@sypex.net                                 |\n|---------------------------------------------------------------------------|\n|     created: 2003.09.02 19:07              modified: 2013.08.27 06:27     |\n|---------------------------------------------------------------------------|\n| Sypex Dumper is released under the terms of the BSD license               |\n|   http://sypex.net/bsd_license.txt                                        |\n\\***************************************************************************/\nheader(\"Expires: Wed, 19 Nov 2008 19:19:19 GMT\");\nheader(\"Cache-Control: no-store, no-cache, must-revalidate\");\nheader(\"Content-Type: text/html; charset=utf-8\");\n//error_reporting(E_ALL);\nerror_reporting(0);\nif (!ini_get('zlib.output_compression') && function_exists('ob_gzhandler')) ob_start('ob_gzhandler');\nset_error_handler('sxd_error_handler');\nregister_shutdown_function('sxd_shutdown');\n$SXD = new Sypex_Dumper();\nchdir(dirname(__FILE__));\n$SXD->init(!empty($argc) && $argc > 1 ? $argv : false);\n \nclass Sypex_Dumper {\n\tfunction Sypex_Dumper() {\n\t\tdefine('C_DEFAULT', 1);\n\t\tdefine('C_RESULT', 2);\n\t\tdefine('C_ERROR', 3);\n\t\tdefine('C_WARNING', 4);\n\t\tdefine('SXD_DEBUG', false);\n\t\tdefine('TIMER', array_sum(explode(' ', microtime()))); \n\t\tdefine('V_SXD', 20011);\n\t\tdefine('V_PHP', sxd_ver2int(phpversion()));\n\t\t$this->name = 'Sypex Dumper 2.0.11';\n\t}\n\tfunction loadLang($lng_name = 'auto'){\n\t\tif($lng_name == 'auto'){\n\t\t\tinclude('lang/list.php');\n\t\t\t$this->langs = &$langs;\n\t\t\t$lng = 'en';\n\t\t\tif(preg_match_all('/[a-z]{2}(-[a-z]{2})?/', $_SERVER['HTTP_ACCEPT_LANGUAGE'], $m)) {\n\t\t\t\tforeach($m[0] AS $l){\n\t\t\t\t\tif(isset($langs[$l])){\n\t\t\t\t\t\t$lng_name = $l;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif(file_exists(\"lang/lng_{$lng_name}.php\")) include(\"lang/lng_{$lng_name}.php\");\n\t\telse include(\"lang/lng_en.php\");\n\t\t$this->LNG = &$LNG;\n\t\t$this->LNG['name'] = $lng_name;\n\t\treturn true;\n\t}\n\tfunction init($args = false){\n\t\tif (get_magic_quotes_gpc()) {\n\t\t\t$_POST = sxd_antimagic($_POST);\n\t\t}\n\t\tinclude('cfg.php');\n\t\t$this->loadLang($CFG['lang']);\n\t\tif (!ini_get('safe_mode') && function_exists('set_time_limit') && strpos(ini_get('disable_functions'), 'set_time_limit') === false) @set_time_limit($CFG['time_web']);\n\t\telseif (ini_get('max_execution_time') < $CFG['time_web']) $CFG['time_web'] = ini_get('max_execution_time');\n\t\t$this->CFG = &$CFG;\n\t\t$this->try = false;\n\t\t$this->virtualize = false;\n\t\t$this->cron_mode = false;\n\t\t// Проверяем авторизацию и делаем коннект к базе\n\t\tif(empty($this->CFG['my_user'])){\n\t\t\t$this->CFG['my_host'] = 'localhost';\n\t\t\t$this->CFG['my_port'] = 3306;\n\t\t\t$this->CFG['my_user'] = 'root';\n\t\t\t$this->CFG['my_pass'] = '';\n\t\t\t$this->CFG['my_comp'] = 0;\n\t\t\t$this->CFG['my_db'] = '';\n\t\t}\n\t\tif ($args) { // консольный режим\n\t\t    foreach($args AS $key => $arg){\n\t\t        if (preg_match(\"/^-([hupoj])=(.*?)$/\", $arg, $m)){\n\t\t            switch ($m[1]) {\n\t\t                case 'h': $this->CFG['my_host'] = $m[2]; break; // хост\n\t\t                case 'o': $this->CFG['my_port'] = $m[2]; break; // порт\n\t\t                case 'u': $this->CFG['my_user'] = $m[2]; break; // логин\n\t\t                case 'p': $this->CFG['my_pass'] = $m[2]; break; // пароль\n\t\t                case 'j': $this->CFG['sjob'] = $m[2]; break; // job-файл\n\t\t            }\n\t\t        }\n\t\t    }\n\t\t    $this->cron_mode = true;\n\t\t    set_time_limit($CFG['time_cron']);\n\t\t    // Загружаем конфиг файл, если нужно\n\t\t    $auth = $this->connect();\n\t\t    if($auth && !empty($this->CFG['sjob'])){\n\t\t\t\t$this->ajax($this->loadJob($this->CFG['sjob']));\n\t\t\t\techo file_get_contents($this->JOB['file_log']);\n\t\t\t\tif(file_exists($this->JOB['file_log'])) unlink($this->JOB['file_log']);\n\t\t\t\tif(file_exists($this->JOB['file_rtl'])) unlink($this->JOB['file_rtl']);\n\t\t    }\n\t\t    else echo 'Auth error';\n\t\t    exit;\n\t\t}\n\t\telseif(!empty($this->CFG['auth'])){ // Авторизация\n\t\t\t$auth = false;\n\t\t\t$sfile = 'ses.php';\n\t\t\t\n\t\t\tif(!empty($_COOKIE['sxd']) && preg_match('/^[\\da-f]{32}$/', $_COOKIE['sxd'])){\n\t\t\t\tinclude($sfile);\n\t\t\t\tif(isset($SES[$_COOKIE['sxd']])) {\n\t\t\t\t\t$auth = true;\n\t\t\t\t\t$this->CFG = $SES[$_COOKIE['sxd']]['cfg'];\n\t\t\t\t\t$this->SES = &$SES;\n\t\t\t\t\t$this->loadLang($this->CFG['lang']);\n\t\t\t\t}\n\t\t\t}\n\t\t\tif(!$auth) {\n\t\t\t\t$user = !empty($_POST['user']) ? $_POST['user'] : '';\n\t\t\t\t$pass = !empty($_POST['pass']) ? $_POST['pass'] : '';\n\t\t\t\t$host = !empty($_POST['host']) ? $_POST['host'] : (!empty($this->CFG['my_host']) ? $this->CFG['my_host'] : 'localhost');\n\t\t\t\t$port = !empty($_POST['port']) && is_numeric($_POST['port']) ? $_POST['port'] : 3306;\n\t\t\t\t$temp = preg_split('/\\s+/', $this->CFG['auth']);\n\t\t\t\tif(!empty($_REQUEST['lang']) && preg_match('/^[a-z]{2}(-[a-z]{2})?$/', $_REQUEST['lang'])) {$this->loadLang($_REQUEST['lang']);}\n\t\t\t\tforeach($temp AS $a){\n\t\t\t\t\tswitch($a) {\n\t\t\t\t\t\tcase 'cfg': \tif(empty($user)) {continue;}\n\t\t\t\t\t\t\t\t\t\t$auth = !empty($CFG['user']) && isset($CFG['pass']) && $CFG['user']== $user && $CFG['pass'] == $pass;\n\t\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase 'mysql':\tif(empty($user)) {continue;}\n\t\t\t\t\t\t\t\t\t\tif($host != 'localhost' && !empty($this->CFG['my_host']) && $this->CFG['my_host'] != $host) {continue;}\n\t\t\t\t\t\t\t\t\t\t$auth = $this->connect($host, $port, $user, $pass);\n\t\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tdefault:\t\t$file = 'auth_' . $a . '.php';\n\t\t\t\t\t\t\t\t\t\tif(!file_exists($file)) continue;\n\t\t\t\t\t\t\t\t\t\tinclude\t$file;\n\t\t\t\t\t}\n\t\t\t\t\tif($auth) break;\n\t\t\t\t}\n\t\t\t\tif($auth){\n\t\t\t\t\t$key = md5(rand(1,100000) . $user . microtime());\n\t\t\t\t\t$CFG['lang'] = $this->LNG['name'];\n\t\t\t\t\t$_COOKIE['sxd'] = $key;\n\t\t\t\t\t$this->saveCFG();\n\t\t\t\t\tif(V_PHP > 50200) setcookie('sxd', $key, !empty($_POST['save']) ? time() + 31536000 : 0, '', '', false, true);\n\t\t\t\t\telse setcookie('sxd', $key, !empty($_POST['save']) ? time() + 31536000 : 0, '', '', false);\n\t\t\t\t\theader(\"Location: ./\");\n\t\t\t\t\texit;\n\t\t\t\t}\n\t\t\t\tforeach(array('user', 'pass', 'host', 'port') AS $key){\n\t\t\t\t\t$_POST[$key] = !empty($_POST[$key]) ? htmlspecialchars($_POST[$key], ENT_NOQUOTES) : '';\n\t\t\t\t}\n\t\t\t\t$_POST['save'] = !empty($_POST['save']) ? ' CHECKED' : '';\n\t\t\t}\n\t\t\tif (!$auth) {\n\t\t\t\tif(!empty($_POST['ajax'])){\n\t\t\t\t\techo \"sxd.hideLoading();alert('Session not found');\";\n\t\t\t\t\texit;\n\t\t\t\t}\n\t\t\t\t$this->lng_list = '<option value=\"auto\">- auto -</opinion>';\n\t\t\t\tif(!isset($this->langs)) {include('lang/list.php');$this->langs = &$langs;}\n\t\t\t\tforeach($this->langs AS $k => $v){\n\t\t\t\t\t$this->lng_list .= \"<option value=\\\"{$k}\\\"\" . ($k == (!empty($_REQUEST['lang']) ? $this->LNG['name'] : $this->CFG['lang']) ? ' SELECTED' : '') . \">{$v}</opinion>\";\n\t\t\t\t}\n\t\t\t\tinclude('tmpl.php');\n\t\t\t\techo sxd_tpl_auth();\n\t\t\t\texit;\n\t\t\t}\n\t\t}\n\t\tif(empty($_POST['ajax']['act']) || $_POST['ajax']['act'] != 'save_connect') $this->connect();\n\t\tif(isset($_POST['ajax'])) $this->ajax($_POST['ajax']);\n\t\telse $this->main();exit;\n\t}\n\tfunction saveToFile($name, $content){\n\t\t$fp = fopen($name, \"w\");\n\t\tfwrite($fp, $content);\n\t\tfclose($fp);\n\t}\n\tfunction connect($host = null, $port = null, $user = null, $pass = null){\n\t\t$this->error = '';\n\t\t$this->try = true;\n\t\tif(!empty($user) && isset($pass)) {\n\t\t\t$this->CFG['my_host'] = $host;\n\t\t\t$this->CFG['my_port'] = $port;\n\t\t\t$this->CFG['my_user'] = $user;\n\t\t\t$this->CFG['my_pass'] = $pass;\n\t\t}\n\t\tif(mysql_connect($this->CFG['my_host'] . ($this->CFG['my_host']{0} != ':' ? \":{$this->CFG['my_port']}\" : ''),  $this->CFG['my_user'], $this->CFG['my_pass'])) {\n\t\t\tif(V_PHP > 50202) mysql_set_charset('utf8') or sxd_my_error();\n\t\t\telse mysql_query('SET NAMES utf8') or sxd_my_error();\n\t\t\tdefine('V_MYSQL', sxd_ver2int(mysql_get_server_info()));\n\t\t}\n\t\telse {\n\t\t\tdefine('V_MYSQL', 0); \n\t\t\t$this->error = \"sxd.actions.tab_connects();alert(\" . sxd_esc(mysql_error()) . \");\";\n\t\t}\t\n\t\t$this->try = false;\n\t\treturn V_MYSQL ? true: false;\n\t}\n\tfunction main(){\n   \t\t// Тулбар\n\t\t$this->VAR['toolbar'] = sxd_php2json(\n\t\t\tarray(\n\t\t\t\tarray('backup', $this->LNG['tbar_backup'], 1, 3),\n\t\t\t\tarray('restore', $this->LNG['tbar_restore'], 2, 3),\n\t\t\t\tarray('|'),\n\t\t\t\tarray('files', $this->LNG['tbar_files'], 3, 1), \n\t\t\t\tarray('services', $this->LNG['tbar_services'], 5, 1),\n\t\t\t\tarray('|'),\n\t\t\t\tarray('createdb', $this->LNG['tbar_createdb'], 7, 0),\n\t\t\t\tarray('connects', $this->LNG['tbar_connects'], 6, 0),\n\t\t\t\tarray('|'),\n\t\t\t\tarray('options', $this->LNG['tbar_options'], 4, 1),\n\t\t\t\tarray('|'),\n\t\t\t\tarray('exit', $this->LNG['tbar_exit'], 8, 1),\n\t\t\t)\n\t\t);\n  \t\t$this->db = 'temp';\n\t\t$zip = array($this->LNG['zip_none']);\n\t\tif (function_exists(\"gzopen\")) {\n\t\t\tfor($i = 1; $i <10; $i++){\n\t\t\t\t$zip[] = \"GZip: {$i}\";\n\t\t\t}\n\t\t\t$zip[1] .= \" ({$this->LNG['zip_min']})\";\n\t\t\t$zip[7] .= \" ({$this->LNG['default']})\";\n\t\t}\n\t\tif (function_exists(\"bzopen\")) {\n\t\t    $zip[10] = \"BZip\";\n\t\t}\n\t\tend($zip);\n\t\t$zip[key($zip)] .=  \" ({$this->LNG['zip_max']})\";\n\t\t$this->VAR['combos'] =\n\t\t\t$this->addCombo('backup_db', $this->db, 11, 'db', array()/*$this->getDBList()*/) . \n\t\t\t$this->addCombo('backup_charset', 0, 9, 'charset', $this->getCharsetList()) .\n\t\t\t$this->addCombo('backup_zip', 7, 10, 'zip', $zip) .\n\t\t\t$this->addCombo('restore_db', $this->db, 11, 'db') . \n\t\t\t$this->addCombo('restore_charset', 0, 9, 'charset') . \n\t\t\t$this->addCombo('restore_file', 0, 12, 'files', $this->getFileList()) . \n\t\t\t$this->addCombo('restore_type', 0, 13, 'types', array(\"CREATE + INSERT ({$this->LNG['default']})\", 'TRUNCATE + INSERT', 'REPLACE', 'INSERT IGNORE')) .\n\t\t\t$this->addCombo('services_db', $this->db, 11, 'db') .\n\t\t\t$this->addCombo('services_check', 0, 5, 'check', array(\"- {$this->LNG['default']} -\", 'QUICK', 'FAST', 'CHANGED', 'MEDIUM', 'EXTENDED')) .\n\t\t\t$this->addCombo('services_repair', 0, 5, 'repair', array(\"- {$this->LNG['default']} -\", 'QUICK', 'EXTENDED')) .\n\t\t\t$this->addCombo('db_charset', 0, 9, 'collation', $this->getCollationList()) .\n\t\t\t$this->addCombo('db_charset_col', 0, 15, 'collation:db_charset')\n\t\t;\n\t\tif (!V_MYSQL) $this->VAR['combos'] .= $this->error;\n\t\t$this->VAR['combos']   .= $this->getSavedJobs() . \"sxd.confirms = {$this->CFG['confirm']};sxd.actions.dblist();\";\n\t\t$this->LNG['del_date']  = sprintf($this->LNG['del_date'], '<input type=\"text\" id=\"del_time\" class=txt style=\"width:24px;\" maxlength=\"3\">');\n\t\t$this->LNG['del_count'] = sprintf($this->LNG['del_count'], '<input id=\"del_count\" type=\"text\" class=txt style=\"width:18px;\" maxlength=\"2\">');\n\t\t\n\t\tinclude('tmpl.php');\n\t\techo sxd_tpl_page();\n\t}\n\tfunction addCombo($name, $sel, $ico, $opt_name, $opts = ''){\n\t\t$opts = !empty($opts) ? \"{{$opt_name}:\" . sxd_php2json($opts) . '}' : \"'{$opt_name}'\";\n\t\treturn \"sxd.addCombo('{$name}', '{$sel}', {$ico}, {$opts});\\n\";\n\t}\n\tfunction ajax($req){\n\t\t$res = '';\n\t\t$act = $req['act'];\n\t\tif($req['act'] == 'run_savedjob'){\n\t\t\t$req = $this->loadJob($req);\n\t\t}\n\t\tswitch($req['act']){\n\t\t\tcase 'load_db': \n\t\t\t\t$res = $this->getObjects(str_replace('_db', '', $req['name']), $req['value']);\n\t\t\t\tbreak;\n\t\t\tcase 'load_files': \n\t\t\t\t$res = $this->getFileObjects('restore', $req['value']);\n\t\t\t\tbreak;\n\t\t\tcase 'filelist': \n\t\t\t\t$res = \"sxd.clearOpt('files');sxd.addOpt(\" . sxd_php2json(array('files' => $this->getFileList())) . \");\";\n\t\t\t\tbreak;\n\t\t\tcase 'dblist':\n\t\t\t\t$res = \"sxd.clearOpt('db');sxd.addOpt(\" . sxd_php2json(array('db' => $this->getDBList())) . \");sxd.combos.restore_db.select(0,'-');sxd.combos.services_db.select(0,'-');sxd.combos.backup_db.select(0,'-');\";\n\t\t\t\tbreak;\n\t\t\tcase 'load_connect':\n\t\t\t\t$CFG = $this->cfg2js($this->CFG); \n\t\t\t\t$res = \"z('con_host').value = '{$CFG['my_host']}', z('con_port').value = '{$CFG['my_port']}', z('con_user').value = '{$CFG['my_user']}',\n\t\t\tz('con_pass').value = '', z('con_comp').checked = {$CFG['my_comp']}, z('con_db').value = '{$CFG['my_db']}', z('con_pass').changed = false;\" ;\n\t\t\t\tbreak;\n\t\t\tcase 'save_connect': \n\t\t\t\t$res = $this->saveConnect($req);\n\t\t\t\tbreak;\n\t\t\tcase 'save_job': \n\t\t\t\tunset($req['act']);\n\t\t\t\t$this->saveJob('sj_' . $req['job'] , $req);\n\t\t\t\t$res = $this->getSavedJobs();\n\t\t\t\tbreak;\n\t\t\tcase 'add_db': \n\t\t\t\t$res = $this->addDb($req);\n\t\t\t\tbreak;\n\t\t\tcase 'load_options':\n\t\t\t\t$CFG = $this->cfg2js($this->CFG);\n\t\t\t\t$res = \"z('time_web').value = '{$CFG['time_web']}', z('time_cron').value = '{$CFG['time_cron']}', z('backup_path').value = '{$CFG['backup_path']}',\n\t\t\tz('backup_url').value = '{$CFG['backup_url']}', z('globstat').checked = {$CFG['globstat']}, z('charsets').value = '{$CFG['charsets']}', z('only_create').value = '{$CFG['only_create']}', z('auth').value = '{$CFG['auth']}', z('conf_import').checked = {$CFG['confirm']} & 1, z('conf_file').checked = {$CFG['confirm']} & 2, z('conf_db').checked = {$CFG['confirm']} & 4;sxd.confirms = {$this->CFG['confirm']};\";\n\t\t\t\tbreak;\n\t\t\tcase 'save_options': \n\t\t\t\t$res = $this->saveOptions($req);\n\t\t\t\tbreak;\n\t\t\tcase 'delete_file':\n\t\t\t\tif(preg_match('/^[^\\/]+?\\.sql(\\.(gz|bz2))?$/', $req['name'])) {\n\t\t\t\t\t$file = $this->CFG['backup_path'] . $req['name'];\n\t\t\t\t\tif(file_exists($file)) unlink($file);\n\t\t\t\t}\n\t\t\t\t$res = $this->getFileListExtended();\n\t\t\t\tbreak;\n\t\t\tcase 'delete_db':\n\t\t\t\t$res = $this->deleteDB($req['name']);\n\t\t\t\tbreak;\n\t\t\tcase 'load_files_ext': \n\t\t\t\t$res .= $this->getFileListExtended();\n\t\t\t\tbreak;\n\t\t\tcase 'services': \n\t\t\t\t$this->runServices($req);\n\t\t\t\tbreak;\t\n\t\t\tcase 'backup': \n\t\t\t\t$this->addBackupJob($req);\n\t\t\t\tbreak;\n\t\t\tcase 'restore': \n\t\t\t\t$this->addRestoreJob($req);\n\t\t\t\tbreak;\n\t\t\tcase 'resume': \n\t\t\t\t$this->resumeJob($req);\n\t\t\t\tbreak;\n\t\t\tcase 'exit': \n\t\t\t\tsetcookie('sxd', '', 0);\n\t\t\t\t$res = \"top.location.href = \" . sxd_esc($this->CFG['exitURL']) . \";\";\n\t\t\t\tbreak;\n\t\t}\t\n\t\techo $res;\n\t}\n\tfunction loadJob($job){\n\t\t$file = $this->CFG['backup_path'] . 'sj_' . (is_array($job) ? $job['job'] : $job) . '.job.php';\n\t\tif(!file_exists($file)) return;\n\t\tinclude($file);\n\t\t$JOB['act'] = $JOB['type'];\n\t\t$JOB['type'] = 'run';\n\t\treturn $JOB;\n\t}\n\tfunction deleteDB($name){\n\t\t$r = mysql_query('DROP DATABASE `' . sxd_esc($name, false) . '`') or sxd_my_error();\n\t\tif($r){\n        \techo \"sxd.clearOpt('db');sxd.addOpt(\" . sxd_php2json(array('db' => $this->getDBList())) . \");sxd.combos.services_db.select(0,'-');\";\n\t\t}\n        else\n        \techo \"alert(\" . sxd_esc(mysql_error()) . \");\";\n\t}\n\tfunction cfg2js($cfg){\n\t\tforeach($cfg AS $k => $v){\n\t\t\t$cfg[$k] = sxd_esc($v, false);\n\t\t}\n\t\treturn $cfg;\n\t}\n\tfunction addDb($req){\n        $r = mysql_query('CREATE DATABASE `' . sxd_esc($req['name'], false) . '`' . (V_MYSQL > 40100 ? \"CHARACTER SET {$req['charset']} COLLATE {$req['collate']}\" : ''));\n        if($r)\n        \techo \"sxd.addOpt(\" . sxd_php2json(array('db' => array($req['name'] => \"{$req['name']} (0)\"))) . \");\";\n        else\n        \t sxd_my_error();\n\t}\n\tfunction saveConnect($req){\n\t\t$this->CFG['my_host'] = $req['host'];\n\t\t$this->CFG['my_port'] = (int)$req['port'];\n\t\t$this->CFG['my_user'] = $req['user'];\n\t\tif(isset($req['pass'])) $this->CFG['my_pass'] = $req['pass'];\n\t\t$this->CFG['my_comp'] = $req['comp'] ? 1 : 0;\n\t\t$this->CFG['my_db']   = $req['db'];\n\t\t$this->saveCFG();\n        $this->connect();\n        if (V_MYSQL) {\n\t        $tmp = array(\n        \t\t'db' => $this->getDBList(),\n        \t\t'charset' => $this->getCharsetList(),\n        \t\t'collation' => $this->getCollationList()\n\t\t\t);\n\t        echo \"sxd.clearOpt('db');sxd.clearOpt('charset');sxd.clearOpt('collation');sxd.addOpt(\" . sxd_php2json($tmp) . \");sxd.combos.backup_db.select(0,'-');sxd.combos.restore_db.select(0,'-');sxd.combos.services_db.select(0,'-');sxd.combos.backup_charset.select(0,'-');sxd.combos.services_db.select(0,'-');sxd.combos.db_charset.select(0,'-');\";\n\t\t}\n\t\telse {\n\t\t\t echo $this->error;\n\t\t}\n\t}\n\tfunction saveOptions($req){\n\t\t$this->CFG['time_web']    = $req['time_web'];\n\t\t$this->CFG['time_cron']   = $req['time_cron'];\n\t\t$this->CFG['backup_path'] = $req['backup_path'];\n\t\t$this->CFG['backup_url']  = $req['backup_url'];\n\t\t$this->CFG['globstat']    = $req['globstat'] ? 1 : 0;\n\t\t$this->CFG['charsets']    = $req['charsets'];\n\t\t$this->CFG['only_create'] = $req['only_create'];\n\t\t$this->CFG['auth']        = $req['auth'];\n\t\t$this->CFG['confirm']     = $req['confirm'];\n\t\t$this->saveCFG();\n\t}\n\tfunction saveCFG(){\n\t\tif (isset($_COOKIE['sxd'])) {\n\t\t\t$this->SES[$_COOKIE['sxd']] = array('cfg' => $this->CFG, 'time' => time(), 'lng' => $this->LNG['name']);\n\t\t\t$this->saveToFile('ses.php', \"<?php\\n\\$SES = \" . var_export($this->SES, true) . \";\\n\" . \"?>\");\n\t\t}\n        if (!$this->virtualize){\n        \t$this->saveToFile('cfg.php', \"<?php\\n\\$CFG = \" . var_export($this->CFG, true) . \";\\n\" . \"?>\");\n        }\n\t}\n\tfunction runServices($job) {\n\t\t$serv = array('optimize' => 'OPTIMIZE', 'analyze' => 'ANALYZE', 'check' => 'CHECK', 'repair' => 'REPAIR');\n\t\t$add = array('check'  => array('', 'QUICK', 'FAST', 'CHANGED', 'MEDIUM', 'EXTENDED'), 'repair' => array('', 'QUICK', 'EXTENDED'));\n\t\tif(isset($serv[$job['type']])) {\n\t\t\tmysql_select_db($job['db']);\n\t\t\t$filter = $object = array();\n\t\t\t$this->createFilters($job['obj'], $filter, $object);\n\t\t\t$r = mysql_query('SHOW TABLE STATUS') or sxd_my_error();\n\t\t\tif (!$r) return;\n\t\t\t$tables = array();\n\t\t\twhile($item = mysql_fetch_assoc($r)){\n\t\t\t\tif(V_MYSQL > 40101 && is_null($item['Engine']) && preg_match('/^VIEW/i', $item['Comment'])) continue;\n\t\t\t\tif(sxd_check($item['Name'], $object['TA'], $filter['TA'])) $tables[] = \"`{$item['Name']}`\";\n\t\t\t}\n\t\t\t$sql = $serv[$job['type']] . ' TABLE ' . implode(',', $tables);\n\t\t\t\n\t\t\tif ($job['type'] == 'check' || $job['type'] == 'repair') {\n\t\t\t\t$sql .= isset($add[$job['type']][$job[$job['type']]]) ? ' ' . $add[$job['type']][$job[$job['type']]] : '';\n\t\t\t}\n\t\t\t\n\t\t\t$r = mysql_query($sql) or sxd_my_error();\n\t\t\tif (!$r) return;\n\t\t\t$res = array();\n\t\t\twhile($item = mysql_fetch_row($r)){\n\t\t\t\t$res[] = $item;\n\t\t\t}\n\t\t\techo 'sxd.result.add(' . sxd_php2json($res). ');';\n\t\t}\n\t}\n\tfunction createFilters(&$obj, &$filter, &$object){\n\t\t$types = array('TA', 'TC', 'VI', 'PR', 'FU', 'TR', 'EV');\n\t\tforeach($types AS $type){\n\t\t\t$filter[$type] = array();\n\t\t\t$object[$type] = array();\n\t\t\tif(!empty($obj[$type])){\n\t\t\t\tforeach($obj[$type] AS $v){\n\t\t\t\t\tif(strpos($v, '*') !== false) {\n\t\t\t\t\t\t$filter[$type][] = str_replace('*', '.*?', $v); \n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\t$object[$type][$v] = true;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t$filter[$type] = count($filter[$type]) > 0 ? '/^(' . implode('|', $filter[$type]) . ')$/i' : '';\n\t\t\t}\n\t\t}\n\t}\n\tfunction closeConnect(){\n\t\t//return;\n\t\t@ignore_user_abort(1); \n\t\theader(\"SXD: {$this->name}\");\n\t\t$size = ob_get_length();\n//\t\t@fastcgi_finish_request();\n\t\theader(\"Content-Length: {$size}\");\n\t\theader(\"Connection: close\"); \n\t\t@ob_end_flush();\n\t\t@flush();\n\t}\n\tfunction resumeJob($job){\n\t\t$this->closeConnect();\n\t\tinclude($this->CFG['backup_path'] . $job['job'] . '.job.php');\n\t\t$this->JOB = &$JOB;\n\t\tif(file_exists($this->JOB['file_stp'])) unlink($this->JOB['file_stp']);\n\t\t$this->fh_rtl = fopen($this->JOB['file_rtl'], 'r+b');\n\t\t$this->fh_log = fopen($this->JOB['file_log'], 'ab');\n\t\t$t = fgets($this->fh_rtl);\n\t\tif(!empty($t)){\n\t\t\t$this->rtl = explode(\"\\t\", $t);\t\n\t\t}\n\t\telse {\n\t\t\t$this->addLog($this->LNG['not_found_rtl']);\n\t\t\texit;\n\t\t}\n\t\t// TODO: проверить удаление кодировки\n\t\t//$this->rtl[6] = '';\n\t\tfseek($this->fh_rtl, 0);\n\t\t$this->rtl[1] = time();\n\t\t$this->rtl[9] = 0;\n\t\tfwrite($this->fh_rtl, implode(\"\\t\", $this->rtl));\n\t\tif ($this->JOB['act'] == 'backup') $this->runBackupJob(true);\n\t\telseif ($this->JOB['act'] == 'restore') $this->runRestoreJob(true);\n\t}\n\tfunction addRestoreJob($job) {\n\t\t$this->closeConnect();\n\t\t$this->JOB = $job;\n\t\t// Создаем список объектов и фильтр\n\t\t$filter = $object = array();\n   \t\t$this->createFilters($this->JOB['obj'], $filter, $object);\n\t\t\n\t\t$objects = $this->getFileObjects('restore', $this->JOB['file'], false);\n\t\t$todo = array();\n\t\t$rows = 0;\n\t\t$this->tab_rows = array();\n\t\t$todo = array();\n\t\tforeach($objects AS $t => $list){\n\t\t\tif($t == 'TA' && (!empty($object['TC']) || !empty($filter['TC']))) {}\n\t\t\telseif(empty($object[$t]) && empty($filter[$t])) {continue;}\n\t\t\tif (empty($list)) continue;\n\t\t\t\n\t\t\tforeach($list AS $item){\n\t\t\t\tswitch($t){\n\t\t\t\t\tcase 'TA':\n\t\t\t\t\t\t$type = '';\n\t\t\t\t\t\tif(sxd_check($item[0], $object['TA'], $filter['TA'])){\n\t\t\t\t\t\t\t$type = empty($item[1]) ? 'TC' : 'TA';\n\t\t\t\t\t\t}\n\t\t\t\t\t\telseif(sxd_check($item[0], $object['TC'], $filter['TC'])) {\n\t\t\t\t\t\t\t$type = 'TC';\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse continue;\n\t\t\t\t\t\t$todo['TA'][]   = array($type, $item[0], $item[1], $item[2]);\n\t\t\t\t\t\t$rows += $type == 'TA' ? $item[1] : 0;\n\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tif(sxd_check($item, $object[$t], $filter[$t])) {\n\t\t\t\t\t\t\t$todo[$t][] = array($t, $item);\n\t\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t$this->JOB['file_tmp'] = $this->JOB['file_name'] = $this->CFG['backup_path'] . $this->JOB['file'];\n\t\t$this->JOB['file_rtl'] = $this->CFG['backup_path'] . $this->JOB['job'] . '.rtl';\n\t\t$this->JOB['file_log'] = $this->CFG['backup_path'] . $this->JOB['job'] . '.log';\n\t\t$this->JOB['file_stp'] = $this->CFG['backup_path'] . $this->JOB['job'] . '.stp';\n\t\tif(file_exists($this->JOB['file_stp'])) unlink($this->JOB['file_stp']);\n\t\t\n\t\t$this->fh_tmp = $this->openFile($this->JOB['file_tmp'], 'r');\n\t\t// Для чужих дампов определяем разделители строк\n\t\tif(is_null($this->JOB['obj'])) {\n\t\t\t$s = fread($this->fh_tmp, 2048);\n\t\t\tif(strpos($s, \"\\r\\n\")) $this->JOB['eol'] = \"\\r\\n\";\n\t\t\telseif(strpos($s, \"\\n\")) $this->JOB['eol'] = \"\\n\";\n\t\t\telse $this->JOB['eol'] = \"\\r\";\n\t\t\t$bom = strncmp($s, \"\\xEF\\xBB\\xBF\", 3) == 0 ? 3 : ((strncmp($s, \"\\xFE\\xFF\", 2) == 0 || strncmp($s, \"\\xFF\\xFE\", 2) == 0) ? 2 : 0);\n\t\t\tfseek($this->fh_tmp, $bom);\n\t\t}\n\t\t$this->JOB['todo'] = $todo;\n\t\t$this->saveJob($this->JOB['job'], $this->JOB);\n\t\t$this->fh_rtl = fopen($this->JOB['file_rtl'], 'wb');\n\t\t$this->fh_log = fopen($this->JOB['file_log'], 'wb');\n\t\t$this->rtl = array(time(), time(), $rows, 0, '', '', '', 0, 0, 0, 0, TIMER, \"\\n\");\n\t\t$this->addLog(sprintf($this->LNG['restore_begin'], $this->JOB['db']));\n\t\t$this->addLog(\"{$this->LNG['combo_file']} {$this->JOB['file']}\");\n\t\t$this->runRestoreJob();\n\t}\n\tfunction runRestoreJob($continue = false){\n\t\t$ei = false;\n\t\tif($continue){\n\t\t\t$this->fh_tmp = $this->openFile($this->JOB['file_tmp'], 'r');\n\t\t\tfseek($this->fh_tmp, $this->rtl[3]);\n\t\t\tif(!empty($this->rtl[6])) $this->setNames($this->JOB['correct'] == 1 && !empty($this->JOB['charset']) ? $this->JOB['charset'] : $this->rtl[6]);\n\t\t\tif($this->rtl[7] < $this->rtl[10]) $ei = true; \n\t\t}\n\t\tmysql_select_db($this->JOB['db']);\n\t\tif(is_null($this->JOB['obj'])) $this->runRestoreJobForeign($continue);\n\t\t//mysql_query(\"SET NAMES 'UTF8'\");\n\t\t$types = array('VI' => 'View', 'PR' => 'Procedure', 'FU' => 'Function', 'TR' => 'Trigger', 'EV' => 'Event');\n\t\t$fcache = '';\n\t\t$writes = 0;\n\t\t$old_charset = '';\n\t\t$tab = '';\n\t\t$seek = 0;\n\t\t$this->rtl[3] = ftell($this->fh_tmp);\n\t\tfseek($this->fh_rtl, 0);\n\t\t$this->rtl[1] = time();\n\t\tfwrite($this->fh_rtl, implode(\"\\t\", $this->rtl));\n\t\t$c = 0;\n\t\tswitch($this->JOB['strategy']){\n\t\t\tcase 1: $tc = 'TRUNCATE'; $td = 'INSERT'; break;\n\t\t\tcase 2: $tc = ''; $td = 'REPLACE'; break;\n\t\t\tcase 3: $tc = ''; $td = 'INSERT IGNORE'; break;\n\t\t\tdefault: $tc = 'DROP TABLE IF EXISTS'; $td = 'INSERT';\n\t\t}\n\t\t$tab_exists = array();\n\t\tif($this->JOB['strategy'] > 0){\n\t\t\t$r = mysql_query(\"SHOW TABLES\") or sxd_my_error();\n\t\t\twhile($item = mysql_fetch_row($r)){\n\t\t\t\t$tab_exists[$item[0]] = true;\n\t\t\t}\n\t\t}\n\t\t$insert = $continue && $this->rtl[7] < $this->rtl[10] ? \"{$td} INTO `{$this->rtl[5]}` VALUES \" : '';\n\t\t//$enable_index = array();\n\t\tif(V_MYSQL > 40014) {\n\t\t\tmysql_query(\"SET UNIQUE_CHECKS=0\");\n\t\t\tmysql_query(\"SET FOREIGN_KEY_CHECKS=0\");\n\t\t\tif(V_MYSQL > 40101) mysql_query(\"SET SQL_MODE='NO_AUTO_VALUE_ON_ZERO'\");\n\t\t\tif(V_MYSQL > 40111) mysql_query(\"SET SQL_NOTES=0\");\n\t\t}\n\t\t$log_sql = false;\n\t\t$fields = '';\n\t\t$time_old = time();\n\t\t$exit_time = $time_old + $this->CFG['time_web'] - 1;\n\t\twhile($q = sxd_read_sql($this->fh_tmp, $seek, $ei)){\t\n\t\t\tif($time_old < time()) {\n\t\t\t\tif(file_exists($this->JOB['file_stp'])){\n\t\t\t\t\t$type = file_get_contents($this->JOB['file_stp']);\n\t\t\t\t\t$this->rtl[9] = !empty($type) ? $type : 2;\n\t\t\t\t\tfseek($this->fh_rtl, 0);\n\t\t\t\t\t$this->rtl[1] = time();\n\t\t\t\t\tfwrite($this->fh_rtl, implode(\"\\t\", $this->rtl));\n\t\t\t\t\t/*if($type == 1) {\n\t\t\t\t\t\t\n\t\t\t\t\t}*/\n\t\t\t\t\tunset($this->rtl);\n\t\t\t\t\texit;\n\t\t\t\t}\n\t\t\t\t$time_old = time();\n\t\t\t\tif($time_old >= $exit_time){\n\t\t\t\t\t$this->rtl[9] = 3;\n\t\t\t\t\tfseek($this->fh_rtl, 0);\n\t\t\t\t\t$this->rtl[1] = time();\n\t\t\t\t\tfwrite($this->fh_rtl, implode(\"\\t\", $this->rtl));\n\t\t\t\t\tunset($this->rtl);\n\t\t\t\t\texit;\n\t\t\t\t}\n\t\t\t\tclearstatcache(); \n\t\t\t}\n\t\t\tswitch($q{0}){\n\t\t\t\tcase '(':\n\t\t\t\t\tif($continue) {\n\t\t\t\t\t\t$this->addLog(sprintf(\"{$this->LNG['restore_TC']} {$this->LNG['continue_from']}\", $this->rtl[5], $this->rtl[3]));\n\t\t\t\t\t\t$continue = false;\n\t\t\t\t\t}\n\t\t\t\t\t$q = $insert . $q;\n\t\t\t\t\t$ex = 1;\n\t\t\t\t\t$c = 1;\n\t\t\t\t\tbreak;\t\n\t\t\t\tcase 'I':\n\t\t\t\t\tif (preg_match('/^INSERT( INTO `(.+?)`) VALUES/', $q, $m)) {\n\t\t\t\t\t\t$insert = $td . $m[1] . $fields . \" VALUES \\n\";\n\t\t\t\t\t\t$tab = $m[2];\n\t\t\t\t\t\t$this->rtl[7] = 0;\n\t\t\t\t\t\t$this->rtl[8] = 0;\n\t\t\t\t\t\tforeach($this->JOB['todo']['TA'] AS $t){\n\t\t\t\t\t\t\tif($t[1] == $tab) {\n\t\t\t\t\t\t\t\t$this->rtl[8] = $t[2];\n\t\t\t\t\t\t\t}\t\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif($this->JOB['strategy']) {\n\t\t\t\t\t\t\t$q = substr_replace($q, $insert, 0, strlen($m[0]));\n\t\t\t\t\t\t}\n\t\t\t\t\t\t//mysql_query(\"LOCK TABLES `{$tab}` WRITE\") or die (mysql_error());\n\t\t\t\t\t\tmysql_query(\"ALTER TABLE `{$tab}` DISABLE KEYS\") or sxd_my_error();\n\t\t\t\t\t\t//if(!empty($this->JOB['autoinc'])) mysql_query(\"ALTER TABLE `{$tab}` AUTO_INCREMENT = 1\") or sxd_my_error();\n\t\t\t\t\t\t$ex = 1;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\tcase 'C':\n\t\t\t\t\t$ex = 1;\n\t\t\t\t\tif (preg_match('/^CREATE TABLE `/', $q)) {\n\t\t\t\t\t\tif($this->JOB['strategy'] != 0 && isset($tab_exists[$this->rtl[5]])) $ex = 0;\n\t\t\t\t\t\telse {\n\t\t\t\t\t\t\t$ex = 1;\n\t\t\t\t\t\t\tif((!empty($this->JOB['correct']) && !empty($this->JOB['charset']))){\n\t\t\t\t\t\t\t\t$q = preg_replace('/(DEFAULT)?\\s*(CHARSET|CHARACTER SET|COLLATE)[=\\s]+\\w+/i', '', $q) . (V_MYSQL < 40100 ? '' : ' DEFAULT CHARSET=' . $this->JOB['charset']);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif(!empty($this->JOB['autoinc'])) $q = preg_replace(\"/AUTO_INCREMENT=\\d+/\", \"AUTO_INCREMENT=1\", $q);\n\t\t\t\t\t\t}\n\t\t\t\t\t\t// Достаем имена полей таблицы\n\t\t\t\t\t\t$fields = $this->JOB['strategy'] > 0 && preg_match_all('/^\\s+(`.+?`) /m', $q, $f, PREG_PATTERN_ORDER) ? '(' . implode(',', $f[1]) . ')' : '';\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\tcase '#': // Команды для дампера\n\t\t\t\t\tif (preg_match(\"/\\#\\t(TC|TD|VI|PR|FU|TR|EV)`(.+?)`(([^_]+?)_.+?)?$/\", $q, $m)) {\n\t\t\t\t\t\t//if(!empty($tab)) $enable_index[] = $tab;\n//\t\t\t\t\t\t$this->setNames($this->JOB['correct'] == 1 && !empty($this->JOB['charset']) ? $this->JOB['charset'] : empty($m[3]) ? '' : $m[3]);\n\t\t\t\t\t\t$this->setNames('binary');\n\t\t\t\t\t\tif($m[1] == 'TC') {\n\t\t\t\t\t\t\t$this->addLog(sprintf($this->LNG['restore_TC'], $m[2]));\n\t\t\t\t\t\t\t$insert = '';\n\t\t\t\t\t\t\t$tab = '';\n\t\t\t\t\t\t\t$this->rtl[4] = 'TD';\n\t\t\t\t\t\t\t$this->rtl[5] = $m[2];\n\t\t\t\t\t\t\t$ei = 0;\n\t\t\t\t\t\t\tif($tc && ($this->JOB['strategy'] == 0 || isset($tab_exists[$m[2]]))) {\n\t\t\t\t\t\t\t\tmysql_query(\"{$tc} `{$m[2]}`\") or sxd_my_error();\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\telseif($m[1] == 'TD'){\n\t\t\t\t\t\t\t$ei = 1;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse {\n\t\t\t\t\t\t\t$this->rtl[4] = $m[1];\n\t\t\t\t\t\t\t$this->rtl[5] = $m[2];\n\t\t\t\t\t\t\t$this->rtl[7] = 0;\n\t\t\t\t\t\t\t$this->rtl[8] = 0;\n\t\t\t\t\t\t\tmysql_query(\"DROP {$types[$m[1]]} IF EXISTS `{$m[2]}`\") or sxd_my_error();\n\t\t\t\t\t\t\t$this->addLog(sprintf($this->LNG[\"restore_{$m[1]}\"], $m[2]));\n\t\t\t\t\t\t\t$ei = 0;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\t$ex = 0;\n\t\t\t\t\tbreak;\n\t\t\t\tdefault: \n\t\t\t\t\t$insert = '';\n\t\t\t\t\t$ex = 1;\n\t\t\t}\n\t\t\tif($ex) {\n\t\t\t\t$this->rtl[3] = ftell($this->fh_tmp) - $seek;\n\t\t\t\tfseek($this->fh_rtl, 0);\n\t\t\t\t$this->rtl[1] = time();\n\t\t\t\tfwrite($this->fh_rtl, implode(\"\\t\", $this->rtl));\n\t\t\t\tif(mysql_query($q)) {\n\t\t\t\t\tif($insert) {\n\t\t\t\t\t\t$c = 1;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\terror_log(date('r') . \"\\n----------\\n{$q}\\n\", 3, \"backup/sql_error.log\");\n\t\t\t\t\tsxd_my_error();\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tif($c){\n\t\t\t\t\t$i = mysql_affected_rows();\n\t\t\t\t\t$this->rtl[3] = ftell($this->fh_tmp) - $seek;\n\t\t\t\t\t$this->rtl[7] += $i;\n\t\t\t\t\t$this->rtl[10] += $i;\n\t\t\t\t\tfseek($this->fh_rtl, 0);\n\t\t\t\t\t$this->rtl[1] = time();\n\t\t\t\t\tfwrite($this->fh_rtl, implode(\"\\t\", $this->rtl));\n\t\t\t\t\t$c = 1;\n\t\t\t\t}\n\t\t\t}\n\t\t\t\n\t\t}\n\t\t// Включаем ключи\n\t\t$this->addLog($this->LNG['restore_keys']);\n\t\t$this->rtl[4] = 'EK';\n\t\t$this->rtl[5] = '';\n\t\t$this->rtl[6] = '';\n\t\t$this->rtl[7] = 0;\n\t\t$this->rtl[8] = 0;\n\t\tforeach($this->JOB['todo']['TA'] AS $tab){\n\t\t\tif ($tab[0] == 'TC') continue;\n\t\t\tmysql_query(\"ALTER TABLE `{$tab[1]}` ENABLE KEYS\") or sxd_my_error();\n\t\t\t$this->rtl[1] = time();\n\t\t\t$this->rtl[5] = $tab[1];\n\t\t\tfseek($this->fh_rtl, 0);\n\t\t\tfwrite($this->fh_rtl, implode(\"\\t\", $this->rtl));\n\t\t}\n\t\t$this->rtl[4] = 'EOJ';\n\t\t$this->rtl[5] = round(array_sum(explode(' ', microtime())) - $this->rtl[11], 4);\n\t\t\n\t\tfseek($this->fh_rtl, 0);\n\t\tfwrite($this->fh_rtl, implode(\"\\t\", $this->rtl));\n\t\t$this->addLog(sprintf($this->LNG['restore_end'], $this->JOB['db']));\n\t\tfclose($this->fh_log);\n\t\tfclose($this->fh_rtl);\n\t}\n\tfunction runRestoreJobForeign($continue = false){\n\t\t$ei = false;\n\t\t\n\t\t$fcache = '';\n\t\t$writes = 0;\n\t\t$old_charset = '';\n\t\t$tab = '';\n\t\t$seek = 0;\n\t\t$this->rtl[3] = ftell($this->fh_tmp);\n\t\tfseek($this->fh_rtl, 0);\n\t\t$this->rtl[1] = time();\n\t\tfwrite($this->fh_rtl, implode(\"\\t\", $this->rtl));\n\t\t$c = 0;\n\t\t\n\t\t$log_sql = false;\n\t\t$fields = '';\n\t\t$insert = '';\n\t\t$last_tab = '';\n\t\t$time_old = time();\n\t\t$exit_time = $time_old + $this->CFG['time_web'] - 1;\n\t\t$delimiter = \";\";\n\t\twhile($q = sxd_read_sql($this->fh_tmp, $seek, $ei, $delimiter, $this->JOB['eol'])){\n\t\t\t$q = ltrim($q);\n\t\t\tif(empty($q)) break;\n\t\t\tif($time_old < time()) {\n\t\t\t\tif(file_exists($this->JOB['file_stp'])){\n\t\t\t\t\t$type = file_get_contents($this->JOB['file_stp']);\n\t\t\t\t\t$this->rtl[9] = !empty($type) ? $type : 2;\n\t\t\t\t\tfseek($this->fh_rtl, 0);\n\t\t\t\t\t$this->rtl[1] = time();\n\t\t\t\t\tfwrite($this->fh_rtl, implode(\"\\t\", $this->rtl));\n\t\t\t\t\t/*if($type == 1) {\n\t\t\t\t\t\t\n\t\t\t\t\t}*/\n\t\t\t\t\tunset($this->rtl);\n\t\t\t\t\texit;\n\t\t\t\t}\n\t\t\t\t$time_old = time();\n\t\t\t\tif($time_old >= $exit_time){\n\t\t\t\t\t$this->rtl[9] = 3;\n\t\t\t\t\tfseek($this->fh_rtl, 0);\n\t\t\t\t\t$this->rtl[1] = time();\n\t\t\t\t\tfwrite($this->fh_rtl, implode(\"\\t\", $this->rtl));\n\t\t\t\t\tunset($this->rtl);\n\t\t\t\t\texit;\n\t\t\t\t}\n\t\t\t\tclearstatcache(); \n\t\t\t}\n\t\t\tdo {\n\t\t\t\t$repeat = false;\n\t\t\t\t//error_log(\"-----------------\\n[{$q}]\\n\", 3, \"q.log\");\n\t\t\t\t//if(empty($q)) {continue 2;}\n\t\t\t\tswitch($q{0}){\n\t\t\t\t\tcase '(':\n\t\t\t\t\t\tif($continue) {\n\t\t\t\t\t\t\t$this->addLog(sprintf(\"{$this->LNG['restore_TC']} {$this->LNG['continue_from']}\", $this->rtl[5], $this->rtl[3]));\n\t\t\t\t\t\t\t$continue = false;\n\t\t\t\t\t\t}\n\t\t\t\t\t\t$q = $insert . $q;\n\t\t\t\t\t\t$ex = 1;\n\t\t\t\t\t\t$c = 1;\n\t\t\t\t\t\tbreak;\t\n\t\t\t\t\tcase 'I':\n\t\t\t\t\t\n\t\t\t\t\t\tif (preg_match('/^(INSERT( INTO `?(.+?)`?).+?\\sVALUES)/s', $q, $m)) {\n\t\t\t\t\t\t\t$insert = trim($m[1]) . ' ';\n\t\t\t\t\t\t\t$tab = $m[3];\n\t\t\t\t\t\t\t$this->rtl[7] = 0;\n\t\t\t\t\t\t\t$this->rtl[8] = 0;\n\t\t\t\t\t\t\t$ex = 1;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 'C':\n\t\t\t\t\t\t$ex = 1;\n\t\t\t\t\t\t$ei = 1;\n\t\t\t\t\t\tif (preg_match('/^CREATE TABLE.+?`(.+?)`/', $q, $m)) {\n\t\t\t\t\t\t\t$ex = 1;\n\t\t\t\t\t\t\t$tab = $m[1];\n\t\t\t\t\t\t\t$this->addLog(sprintf($this->LNG['restore_TC'], $tab));\n\t\t\t\t\t\t\t//mysql_query(\"DROP TABLE IF EXISTS `{$tab}`\");\n\t\t\t\t\t\t\tif((!empty($this->JOB['correct']) && !empty($this->JOB['charset']))){\n\t\t\t\t\t\t\t\t$q = preg_replace('/(DEFAULT)?\\s*(CHARSET|CHARACTER SET|COLLATE)[=\\s]+\\w+/i', '', $q) . (V_MYSQL < 40100 ? '' : ' DEFAULT CHARSET=' . $this->JOB['charset']);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\telseif(empty($this->JOB['charset'])){\n\t\t\t\t\t\t\t\tif(preg_match(\"/(CHARACTER SET|CHARSET)[=\\s]+(\\w+)/i\", $q, $charset)){\n\t\t\t\t\t\t\t\t\t$this->setNames($charset[2]);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase '-' && $q{1} == '-':\n\t\t\t\t\tcase '#':\n\t\t\t\t\t\t$repeat = true;\n\t\t\t\t\t\t$q = ltrim(substr($q, strpos($q, $this->JOB['eol'])));\n\t\t\t\t\t\t$ex = 0;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase '/':\n\t\t\t\t\tcase 'S':\n\t\t\t\t\t\tif (preg_match('/SET NAMES (\\w+)/', $q, $m)) {\n\t\t\t\t\t\t\t$this->JOB['charset'] = $m[1];\n\t\t\t\t\t\t\t$this->setNames($this->JOB['charset']);\n\t\t\t\t\t\t\t$ex = 0;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse $ex = 1;\n\t\t\t\t\tbreak;\n\t\t\t\t\tdefault: \n\t\t\t\t\t\t$insert = '';\n\t\t\t\t\t\t$ex = 1;\n\t\t\t\t\t\t$ei = 0;\n\t\t\t\t}\n\t\t\t}  while ($repeat);\n\t\t\tif($ex) {\n\t\t\t\t$this->rtl[3] = ftell($this->fh_tmp) - $seek;\n\t\t\t\tfseek($this->fh_rtl, 0);\n\t\t\t\t$this->rtl[1] = time();\n\t\t\t\tfwrite($this->fh_rtl, implode(\"\\t\", $this->rtl));\n\t\t\t\terror_log(\"-----------------\\n{$q}\\n\", 3, \"sql.log\");\n\t\t\t\tif(mysql_query($q)) {\n\t\t\t\t\tif($insert) {\n\t\t\t\t\t\t$c = 1;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\terror_log(\"-----------------\\n{$q}\\n\", 3, \"error.log\");\n\t\t\t\t\tsxd_my_error();\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tif($c){\n\t\t\t\t\t$i = mysql_affected_rows();\n\t\t\t\t\t$this->rtl[3] = ftell($this->fh_tmp) - $seek;\n\t\t\t\t\t$this->rtl[7] += $i;\n\t\t\t\t\t$this->rtl[10] += $i;\n\t\t\t\t\tfseek($this->fh_rtl, 0);\n\t\t\t\t\t$this->rtl[1] = time();\n\t\t\t\t\tfwrite($this->fh_rtl, implode(\"\\t\", $this->rtl));\n\t\t\t\t\t$c = 1;\n\t\t\t\t}\n\t\t\t}\n\t\t\t\n\t\t}\n\t\t\n\t\t$this->rtl[4] = 'EOJ';\n\t\t$this->rtl[5] = round(array_sum(explode(' ', microtime())) - $this->rtl[11], 4);\n\t\t$this->rtl[7] = 0;\n\t\t$this->rtl[8] = 0;\n\t\t\n\t\tfseek($this->fh_rtl, 0);\n\t\tfwrite($this->fh_rtl, implode(\"\\t\", $this->rtl));\n\t\t$this->addLog(sprintf($this->LNG['restore_end'], $this->JOB['db']));\n\t\tfclose($this->fh_log);\n\t\tfclose($this->fh_rtl);\n\t}\n\tfunction addBackupJob($job) {\n\t\t$this->closeConnect();\n\t\t// Создаем новое задание\n\t\t$this->JOB = $job;\n\t\tmysql_select_db($this->JOB['db']);\n\t\t// Создаем список объектов и фильтр\n\t\t$filter = $object = array();\n\t\t$this->createFilters($this->JOB['obj'], $filter, $object);\n\t\t$queries = array(\n\t\t\tarray('TABLE STATUS', 'Name', 'TA')\n\t\t);\n\t\tif (V_MYSQL > 50014) {\n\t\t\t$queries[] = array(\"PROCEDURE STATUS WHERE db='{$this->JOB['db']}'\", 'Name', 'PR');\n\t\t\t$queries[] = array(\"FUNCTION STATUS WHERE db='{$this->JOB['db']}'\", 'Name', 'FU');\n\t\t\t$queries[] = array('TRIGGERS', 'Trigger', 'TR');\n\t\t\tif(V_MYSQL > 50100) $queries[] = array('EVENTS', 'Name', 'EV');\n\t\t}\n\t\t$todo = $header = array();\n\t\t$tabs = $rows = 0;\n\t\t$only_create = explode(' ', $this->CFG['only_create']);\n\t\tforeach($queries AS $query){\n\t\t\t$t = $query[2];\n\t\t\tif($t == 'TA' && (!empty($object['TC']) || !empty($filter['TC']))) {}\n\t\t\telseif(empty($object[$t]) && empty($filter[$t])) continue;\n\t\t\t$r = mysql_query('SHOW ' . $query[0]) or sxd_my_error();\n\t\t\tif (!$r) continue;\n\t\t\t$todo[$t] = array();\n\t\t\t$header[$t] = array();\n\t\t\t\n\t\t\twhile($item = mysql_fetch_assoc($r)){\n\t\t\t\t$n = $item[$query[1]];\n\t\t\t\tswitch($t){\n\t\t\t\t\tcase 'TA':\n\t\t\t\t\tcase 'TC':\n\t\t\t\t\t\tif(V_MYSQL > 40101 && is_null($item['Engine']) && preg_match('/^VIEW/i', $item['Comment'])) {\n\t\t\t\t\t\t\tif(sxd_check($n, $object['VI'], $filter['VI'])){\n\t\t\t\t\t\t\t\t$todo['VI'] = array();\n\t\t\t\t\t\t\t\t$header['VI']= array();\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telseif(sxd_check($n, $object['TA'], $filter['TA'])){\n\t\t\t\t\t\t\t$engine = V_MYSQL > 40101 ? $item['Engine'] : $item['Type'];\n\t\t\t\t\t\t\t$t = in_array($engine, $only_create) ? 'TC' : 'TA';\n\t\t\t\t\t\t}\n\t\t\t\t\t\telseif(sxd_check($n, $object['TC'], $filter['TC'])) {\n\t\t\t\t\t\t\t$t = 'TC';\n\t\t\t\t\t\t\t$item['Rows'] = $item['Data_length'] = '';\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse continue;\n\t\t\t\t\t\t$todo['TA'][]   = array($t, $n, !empty($item['Collation']) ? $item['Collation'] : '', $item['Auto_increment'], $item['Rows'], $item['Data_length']);\n\t\t\t\t\t\t$header['TA'][] = \"{$n}`{$item['Rows']}`{$item['Data_length']}\";\n\t\t\t\t\t\t$tabs++;\n\t\t\t\t\t\t$rows += $item['Rows'];\n\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tif(sxd_check($n, $object[$t], $filter[$t])) {\n\t\t\t\t\t\t\t$todo[$t][] = array($t, $n, !empty($item['collation_connection']) ? $item['collation_connection'] : '');\n\t\t\t\t\t\t\t$header[$t][] = $n;\n\t\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\t\n\t\t}\n\t\tif (V_MYSQL > 50014 && (!empty($object['VI']) || !empty($filter['VI']))) {\n\t\t\t// Бэкап обзоров, нужно отсортировать зависимые\n\t\t\t$r = mysql_query(\"SELECT table_name, view_definition /*!50121 , collation_connection */ FROM INFORMATION_SCHEMA.VIEWS WHERE TABLE_SCHEMA = '{$this->JOB['db']}'\") or sxd_my_error();\n\t\t\t$views = $dumped = $views_collation = array();\n\t\t\t$re = \"/`{$this->JOB['db']}`.`(.+?)`/\";\n\t\t\twhile($item = mysql_fetch_assoc($r)){\n\t\t\t\tpreg_match_all($re, preg_replace(\"/^select.+? from/i\", '', $item['view_definition']), $m);\n\t\t\t\t$used = $m[1];\t\n\t\t\t\t$views_collation[$item['table_name']] = !empty($item['collation_connection']) ? $item['collation_connection'] : '';\n\t\t\t\t$views[$item['table_name']] = $used;\n\t\t\t}\n\t\t\t\n\t\t\twhile (count($views) > 0) {\n\t\t\t\tforeach($views AS $n => $view) {\n\t\t\t\t\t$can_dumped = true;\n\t\t\t\t\tforeach($view AS $k) {\n\t\t\t\t\t\tif (isset($views[$k]) && !isset($dumped[$k])) $can_dumped = false;\t\n\t\t\t\t\t}\n\t\t\t\t\tif ($can_dumped) {\n\t\t\t\t\t\tif(sxd_check($n, $object['VI'], $filter['VI'])){\n\t\t\t\t\t\t\t$todo['VI'][] = array('VI', $n, $views_collation[$n]);\n\t\t\t\t\t\t\t$header['VI'][] = $n;\n\t\t\t\t\t\t}\n\t\t\t\t\t\t$dumped[$n] = 1;\n\t\t\t\t\t\tunset($views[$n]);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tunset($dumped);\n\t\t\tunset($views);\n\t\t\tunset($views_collation);\n\t\t}\n\t\t$this->JOB['file_tmp'] = $this->CFG['backup_path'] . $this->JOB['job'] . '.tmp';\n\t\t$this->JOB['file_rtl'] = $this->CFG['backup_path'] . $this->JOB['job'] . '.rtl';\n\t\t$this->JOB['file_log'] = $this->CFG['backup_path'] . $this->JOB['job'] . '.log';\n\t\t$this->JOB['file_stp'] = $this->CFG['backup_path'] . $this->JOB['job'] . '.stp';\n\t\tif(file_exists($this->JOB['file_stp'])) unlink($this->JOB['file_stp']);\n\t\t$this->fh_tmp = $this->openFile($this->JOB['file_tmp'], 'w');\n\t\t$this->JOB['file'] = sprintf('%s_%s.%s', (isset($this->JOB['title']) ? $this->JOB['job'] : $this->JOB['db']), date('Y-m-d_H-i-s'), $this->JOB['file_ext']);\n\t\t$this->JOB['file_name'] = $this->CFG['backup_path'] . $this->JOB['file'];\n\t\t$this->JOB['todo'] = $todo;\n\t\t$this->saveJob($this->JOB['job'], $this->JOB);\n\t\t$fcache = implode('|', array('#SXD20', V_SXD, V_MYSQL, V_PHP, date('Y.m.d H:i:s'), $this->JOB['db'], $this->JOB['charset'], $tabs, $rows, sxd_esc($this->JOB['comment'], false))) . \"\\n\";\n\t\tforeach($header AS $t => $o){\n\t\t\tif (!empty($o)) $fcache .= \"#{$t} \" . implode('|', $o) . \"\\n\";\t\n\t\t}\n\t\t$this->fh_rtl = fopen($this->JOB['file_rtl'], 'wb');\n\t\t$this->fh_log = fopen($this->JOB['file_log'], 'wb');\n\t\t$this->rtl = array(time(), time(), $rows, 0, '', '', '', 0, 0, 0, 0, TIMER, \"\\n\");\n\t\t$fcache .= \"#EOH\\n\\n\";\n\t\t$this->write($fcache);\n\t\t$this->addLog(sprintf($this->LNG['backup_begin'], $this->JOB['db']));\n\t\t$this->runBackupJob();\n\t}\n\tfunction runBackupJob($continue = false){\n\t\tif($continue){\n\t\t\t$this->fh_tmp = $this->openFile($this->JOB['file_tmp'], 'a');\n\t\t\tmysql_select_db($this->JOB['db']);\n\t\t}\n\t\tmysql_query(\"SET SQL_QUOTE_SHOW_CREATE = 1\");\n\t\t$types = array('VI' => 'View', 'PR' => 'Procedure', 'FU' => 'Function', 'TR' => 'Trigger', 'EV' => 'Event');\n\t\t$fcache = '';\n\t\t$writes = 0;\n\t\t\n\t\tif(V_MYSQL > 40101) mysql_query(\"SET SESSION character_set_results = '\" . ($this->JOB['charset'] ? $this->JOB['charset'] : 'binary') .\"'\") or sxd_my_error();\n\t\t$time_old = time();\n\t\t$exit_time = $time_old + $this->CFG['time_web'] - 1;\n\t\t$no_cache = V_MYSQL < 40101 ? 'SQL_NO_CACHE ' : '';\n\t\tforeach($this->JOB['todo'] AS $t => $o){\n\t\t\tif (empty($this->rtl[4])) $this->rtl[4] = $t;\n\t\t\telseif ($this->rtl[4] != $t) continue;\n\t\t\tforeach($o AS $n){ \n\t\t\t\tif (empty($this->rtl[5])) {\n\t\t\t\t\t$this->rtl[5] = $n[1];\n\t\t\t\t\t$this->rtl[7] = 0;\n\t\t\t\t\t$this->rtl[8] = !empty($n[4]) ? $n[4] : 0;\n\t\t\t\t}\n\t\t\t\telseif ($this->rtl[5] != $n[1]) continue;\n\t\t\t\t// Делаем бэкап\n\t\t\t\tswitch($n[0]){\n\t\t\t\t\tcase 'TC':\n\t\t\t\t\tcase 'TD': \t\t\t\t\n\t\t\t\t\tcase 'TA':\n\t\t\t\t\t\t$from = '';\n\t\t\t\t\t\tif ($n[0] == 'TC' || $this->rtl[7] == 0){\n\t\t\t\t\t\t\t// Бэкап структуры таблицы\n\t\t\t\t\t\t\t$r = mysql_query(\"SHOW CREATE TABLE `{$n[1]}`\") or sxd_my_error();\n\t\t\t\t\t\t\t$item = mysql_fetch_assoc($r);\n\t\t\t\t\t\t    $fcache .= \"#\\tTC`{$n[1]}`{$n[2]}\\t;\\n{$item['Create Table']}\\t;\\n\";\n\t\t\t\t\t\t    $this->addLog(sprintf($this->LNG['backup_TC'], $n[1]));\n\t\t\t            \t$this->rtl[7] = 0; \n\t\t\t\t\t\t    if($n[0] == 'TC' || !$n[4]) break;\n\t\t\t\t\t\t    // Бэкапим данные таблицы\n\t\t\t\t\t\t\t$fcache .= \"#\\tTD`{$n[1]}`{$n[2]}\\t;\\nINSERT INTO `{$n[1]}` VALUES \\n\";\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse {\n\t\t\t\t\t\t\t$from = \" LIMIT {$this->rtl[7]}, {$this->rtl[8]}\";\n\t\t\t\t\t\t\t$this->addLog(sprintf(\"{$this->LNG['backup_TC']} {$this->LNG['continue_from']}\", $n[1], $this->rtl[7]));\n\t\t\t\t\t\t}\n\t\t\t\t\t\t// Определяем типы полей\n\t\t\t\t\t\t$notNum = array();\n\t\t\t\t\t\t$r = mysql_query(\"SHOW COLUMNS FROM `{$n[1]}`\") or sxd_my_error();\n\t\t\t            $fields = 0;\n\t\t\t            while($col = mysql_fetch_array($r)) {\n\t\t\t            \t// TODO: проверить типы SET, ENUM и BIT\n            \t\t\t\t$notNum[$fields] = preg_match(\"/^(tinyint|smallint|mediumint|bigint|int|float|double|real|decimal|numeric|year)/\", $col['Type']) ? 0 : 1; \n            \t\t\t\t$fields++;\n\t\t\t            }\n\t\t\t            $time_old = time();\n\t\t\t            $z = 0;\n\t\t\t            // Достаем данные\n\t\t\t            $r = mysql_unbuffered_query(\"SELECT {$no_cache}* FROM `{$n[1]}`{$from}\");\n\t\t\t            while($row = mysql_fetch_row($r)) {\n\t\t\t            \t if (strlen($fcache) >= 61440) {\n\t\t\t            \t \t$z = 0;\n\t\t\t\t\t\t\t\tif($time_old < time()) {\n\t\t\t\t\t\t\t\t\tif(file_exists($this->JOB['file_stp'])){\n\t\t\t\t\t\t\t\t\t\t$type = file_get_contents($this->JOB['file_stp']);\n\t\t\t\t\t\t\t\t\t\t$this->rtl[9] = !empty($type) ? $type : 2;\n\t\t\t\t\t\t\t\t\t\t$this->write($fcache);\n\t\t\t\t\t\t\t\t\t\tif($type == 1) {\n\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\tunset($this->rtl);\n\t\t\t\t\t\t\t\t\t\texit;\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t$time_old = time();\n\t\t\t\t\t\t\t\t\tif($time_old >= $exit_time){\n\t\t\t\t\t\t\t\t\t\t$this->rtl[9] = 3;\n\t\t\t\t\t\t\t\t\t\t$this->write($fcache);\n\t\t\t\t\t\t\t\t\t\tunset($this->rtl);\n\t\t\t\t\t\t\t\t\t\texit;\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\tclearstatcache(); \n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t$this->write($fcache); \n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tfor($k = 0; $k < $fields; $k++){\n\t\t\t\t\t\t\t\tif(!isset($row[$k])) {$row[$k] = '\\N';}\n\t\t\t\t\t\t\t\telseif($notNum[$k]) {$row[$k] =  '\\'' . mysql_real_escape_string($row[$k]) . '\\'';} // TODO: Потестить скорость эскэйпинга строк\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t$fcache .= '(' . implode(',', $row) . \"),\\n\";\n\t\t\t\t\t\t\t$this->rtl[7]++;  \n\t\t\t\t\t\t\t$this->rtl[10]++;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tunset($row);\n\t\t\t\t\t\tmysql_free_result($r);\n\t\t\t\t\t\t$fcache = substr_replace($fcache, \"\\t;\\n\",  -2, 2);\n\t\t\t\t\tbreak;\n\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tif(V_MYSQL < 50121 && $n[0] == 'TR'){\n\t\t\t\t\t\t\t// SHOW CREATE TRIGGER отсутствует до MySQL 5.1.21\n\t\t\t\t\t\t\t$r = mysql_query(\"SELECT * FROM `INFORMATION_SCHEMA`.`TRIGGERS` WHERE `TRIGGER_SCHEMA` = '{$this->JOB['db']}' AND `TRIGGER_NAME` = '{$n[1]}'\") or sxd_my_error();\n\t\t\t\t\t\t\t$item = mysql_fetch_assoc($r);\n\t\t\t\t\t\t\t$fcache .= \"#\\tTR`{$n[1]}`{$n[2]}\\t;\\nCREATE TRIGGER `{$item['TRIGGER_NAME']}` {$item['ACTION_TIMING']} {$item['EVENT_MANIPULATION']} ON `{$item['EVENT_OBJECT_TABLE']}` FOR EACH ROW {$item['ACTION_STATEMENT']}\\t;\\n\";\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse {\n\t\t\t\t\t\t\t$this->addLog(sprintf($this->LNG['backup_' . $n[0]], $n[1]));\n\t\t\t\t\t\t\t$r = mysql_query(\"SHOW CREATE {$types[$n[0]]} `{$n[1]}`\") or sxd_my_error();\n\t\t\t\t\t\t\t$item = mysql_fetch_assoc($r);\n\t\t\t\t\t\t\t$fcache .= \"#\\t{$n[0]}`{$n[1]}`{$n[2]}\\t;\\n\" . preg_replace(\"/DEFINER=`.+?`@`.+?` /\", '', ($n[0] == 'TR' ? $item['SQL Original Statement'] : $item['Create ' . $types[$n[0]]])) . \"\\t;\\n\";\n\t\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\t$this->rtl[5] = '';\n\t\t\t}\n\t\t\t$this->rtl[4] = '';\n\t\t}\n\t\t$this->rtl[4] = 'EOJ';\n\t\t$this->rtl[5] = round(array_sum(explode(' ', microtime())) - $this->rtl[11], 4);\n\t\t$this->rtl[6] = '';\n\t\t$this->rtl[7] = 0;\n\t\t$this->rtl[8] = 0;\n\t\t$this->write($fcache);\n\t\tfclose($this->fh_tmp);\n\t\trename($this->JOB['file_tmp'], $this->JOB['file_name']);\n\t\t$this->addLog(sprintf($this->LNG['backup_end'], $this->JOB['db']));\n\t\tif(file_exists('sxd2ftp.php')) include('sxd2ftp.php');\n\t\tif ($this->JOB['del_time'] || $this->JOB['del_count']) {\n            $this->addLog($this->LNG['autodelete']);\n            $deldate = '';\n            if (!empty($this->JOB['del_time'])){ // Удаление по дням\n                $deldate = date(\"Y-m-d_H-i-s\", time() - intval($this->JOB['del_time']) * 86400);\n            }\n            $deleted = false;\n            if ($dh = opendir($this->CFG['backup_path'])) {\n                $files = array();\n                $name = isset($this->JOB['title']) ? $this->JOB['job'] : $this->JOB['db'];\n                while (false !== ($file = readdir($dh))) { \n                    if (preg_match(\"/^{$name}_(\\d{4}-\\d{2}-\\d{2}_\\d{2}-\\d{2}-\\d{2})\\.sql/\", $file, $m)) { \n                        if ($deldate && $m[1] < $deldate) {\n                            if(unlink($this->CFG['backup_path'] . $file)) $this->addLog(sprintf($this->LNG['del_by_date'], $file));\n                            else  $this->addLog(sprintf($this->LNG['del_fail'], $file));\n                            $deleted = true;\n                        }\n                        else {$files[$m[1]] = $file;}\n                    }\n                }\n                closedir($dh);\n                // Сортируем файлы по дате и удаляем самые старые\n                if (!empty($this->JOB['del_count'])){\n                    ksort($files);\n                    $file_to_delete = count($files) - $this->JOB['del_count'];\n                    foreach ($files AS $file){\n                        if ($file_to_delete-- > 0){ \n                        \tif(unlink($this->CFG['backup_path'] . $file)) $this->addLog(sprintf($this->LNG['del_by_count'], $file));\n                            else  $this->addLog(sprintf($this->LNG['del_fail'], $file));\n                            $deleted = true;\n                        }\n                    }\n                }\n            }\n            if(!$deleted) $this->addLog($this->LNG['del_nothing']);\n\t\t}\n\t\tfclose($this->fh_log);\n\t\tfclose($this->fh_rtl);\n\t}\n\tfunction setNames($collation){\n\t\tif(empty($collation)) return;\n\t\tif($this->rtl[6] != $collation) {\n\t\t\tmysql_query('SET NAMES \\'' . preg_replace('/^(\\w+?)_/', '\\\\1\\' COLLATE \\'\\\\1_', $collation) . '\\'') or sxd_my_error();\n\t\t\t/*if(!$this->rtl[7])*/ $this->addLog(sprintf($this->LNG['set_names'], $collation));\n\t\t\t$this->rtl[6] = $collation;\t\n\t\t}\n\t}\n\tfunction write(&$str){\n\t\tfseek($this->fh_rtl, 0);\n\t\t$this->rtl[1] = time();\n\t\t$this->rtl[3] += fwrite($this->fh_tmp, $str);\n\t\tfwrite($this->fh_rtl, implode(\"\\t\", $this->rtl));\n\t\t$str = '';\n\t}\n\tfunction addLog($str, $type = 1){\n\t\tfwrite($this->fh_log, date('Y.m.d H:i:s') . \"\\t{$type}\\t{$str}\\n\");\n\t}\n\tfunction getDBList(){\n\t\t$dbs = $items = array();\n        if (!V_MYSQL) return $dbs; \n        $qq = (V_MYSQL < 50000) ? '' : '\\'';\n\t\tif ($this->CFG['my_db']) {\n\t\t\t$tmp = explode(',', $this->CFG['my_db']);\n\t\t\tforeach($tmp AS $d){\n\t\t\t\t$d = trim($d);\n\t\t\t\t$items[] = $qq . sxd_esc($d, false) . $qq;\n\t\t\t\t$dbs[$d] = \"{$d} (0)\";\n\t\t\t}\n\t\t}\n\t\telse{\n\t\t\t$result = mysql_query(\"SHOW DATABASES\") or sxd_my_error();\n    \t\twhile($item = mysql_fetch_row($result)){\n    \t\t\tif($item[0] == 'information_schema' || $item[0] == 'mysql' || $item[0] == 'performance_schema') continue;\n    \t\t\t$items[] = $qq . sxd_esc($item[0], false) . $qq;\n    \t\t\t$dbs[$item[0]] = \"{$item[0]} (0)\";\n    \t\t}\t\n\t\t}\n\t\tif(V_MYSQL < 50000){\n\t\t\tforeach($items AS $item){\n    \t\t\t$tables = mysql_query(\"SHOW TABLES FROM `{$item}`\") or sxd_my_error();\n    \t\t\tif ($tables) {\n    \t  \t\t\t$tabs = mysql_num_rows($tables);\n    \t  \t\t\t$dbs[$item] = \"{$item} ({$tabs})\";\n    \t  \t\t}\n\t\t\t}\n\t\t}\n\t\telse {\n\t\t\t$where = (count($items) > 0) ? 'WHERE `table_schema` IN (' . implode(',', $items) . ')' : '';\n\t\t\t$result = mysql_query(\"SELECT `table_schema`, COUNT(*) FROM `information_schema`.`tables` {$where} GROUP BY `table_schema`\") or sxd_my_error();\n\t\t\twhile($item = mysql_fetch_row($result)){\n    \t\t\tif($item[0] == 'information_schema' || $item[0] == 'mysql' || $item[0] == 'performance_schema') continue;\n    \t\t\t$dbs[$item[0]] = \"{$item[0]} ({$item[1]})\";\n    \t\t}\n\t\t}\n\t    return $dbs;\n\t}\n\tfunction getCharsetList(){\n\t\t$tmp = array(0 => '- auto -');\n\t\tif (!V_MYSQL) return $tmp; \n\t\tif(V_MYSQL > 40101) {\n\t\t\t$def_charsets = '';\n\t\t\tif(!empty($this->CFG['charsets'])){\n\t\t\t\t$def_charsets = preg_match_all(\"/([\\w*?]+)\\s*/\", $this->CFG['charsets'], $m, PREG_PATTERN_ORDER) ? '/^(' . str_replace(array('?','*'), array('.','\\w+?'), implode('|', $m[1])) . ')$/i' : '';\n\t\t\t}\n    \t\t$r = mysql_query(\"SHOW CHARACTER SET\") or sxd_my_error(); \n    \t\tif ($r) {\n    \t\t\twhile($item = mysql_fetch_assoc($r)){\n    \t  \t\t\tif (empty($def_charsets) || preg_match($def_charsets, $item['Charset'])) $tmp[$item['Charset']] = \"{$item['Charset']}\"; // ({$item['Description']})\n    \t\t\t}\n\t\t\t}\n\t\t}\n\t    return $tmp;\n\t}\n\tfunction getCollationList(){\n\t\t$tmp = array(); \n\t\tif (!V_MYSQL) return $tmp; \n\t\tif(V_MYSQL > 40101) {\n\t\t\t$def_charsets = '';\n\t\t\tif(!empty($this->CFG['charsets'])){\n\t\t\t\t$def_charsets = preg_match_all(\"/([\\w*?]+)\\s*/\", $this->CFG['charsets'], $m, PREG_PATTERN_ORDER) ? '/^(' . str_replace(array('?','*'), array('.','\\w+?'), implode('|', $m[1])) . ')$/i' : '';\n\t\t\t}\n    \t\t$r = mysql_query(\"SHOW COLLATION\") or sxd_my_error(); \n    \t\tif ($r) {\n    \t\t\twhile($item = mysql_fetch_assoc($r)){\n    \t  \t\t\tif (empty($def_charsets) || preg_match($def_charsets, $item['Charset'])) $tmp[$item['Charset']][$item['Collation']] = $item['Default'] == 'Yes' ? 1 : 0; \n    \t\t\t}\n\t\t\t}\n\t\t}\n\t    return $tmp;\n\t}\n\tfunction getObjects($tree, $db_name){\n\t\tmysql_select_db($db_name);\n\t\t// Достаем таблицы\n\t\t$r = mysql_query('SHOW TABLE STATUS');\n\t\t$tab_prefix_last = $tab_prefix = '*';\n\t\t$objects = array('TA' => array(), 'VI' => array(), 'PR' => array(), 'FU' => array(), 'TR' => array(), 'EV' => array());\n\t\tif($r){\n\t\t\twhile($item = mysql_fetch_assoc($r)){\n\t\t\t\tif(V_MYSQL > 40101 && is_null($item['Engine']) && preg_match('/^VIEW/i', $item['Comment'])) {\n\t\t\t\t\t$objects['VI'][]= $item['Name'];\n\t\t\t\t}\n\t\t\t\telse{\n\t\t\t\t\t$objects['TA'][] = array($item['Name'], $item['Rows'], $item['Data_length']);\n\t\t\t\t} \n\t\t\t}\n\t\t\t\n\t\t\tif (V_MYSQL > 50014 && $tree != 'services') {\n\t\t\t\t$shows = array(\n\t\t\t\t\t\"PROCEDURE STATUS WHERE db='{$db_name}'\", \n\t\t\t\t\t\"FUNCTION STATUS WHERE db='{$db_name}'\",\n\t\t\t\t\t'TRIGGERS'\n\t\t\t\t);\n\t\t\t\tif(V_MYSQL > 50100) $shows[] = \"EVENTS WHERE db='{$db_name}'\";\n\t\t\t\t// TODO: Поправить проверку событий и триггеров\n\t\t\t\tfor($i = 0, $l = count($shows); $i < $l; $i++){\n\t\t\t\t\t$r = mysql_query('SHOW ' . $shows[$i]);\n\t\t\t\t\tif($r && mysql_num_rows($r) > 0) {\n\t\t\t\t\t\t$col_name = $shows[$i] == 'TRIGGERS' ? 'Trigger' : 'Name';\n\t\t\t\t\t\t$type = substr($shows[$i], 0, 2);\n\t\t\t\t\t\twhile($item = mysql_fetch_assoc($r)){\n\t\t\t\t\t\t\t$objects[$type][] = $item[$col_name];\t\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\telse {\n\t\t\t\t$objects['VI'] = array();\n\t\t\t}\n\t\t}\n\t\treturn $this->formatTree($tree, $objects);\n\t}\n\tfunction getFileObjects($tree, $name, $formatTree = true){\n\t\t// Достаем таблицы\n\t\t$objects = array('TA' => array(), 'VI' => array(), 'PR' => array(), 'FU' => array(), 'TR' => array(), 'EV' => array());\n\t\tif(!preg_match('/\\.sql(\\.(gz|bz2))?$/i', $name, $m)) return '';\n\t\t$name = $this->CFG['backup_path'] . $name;\n\t\tif(!is_readable($name)) {return \"sxd.tree.{$tree}.error(sxd.lng('err_fopen'))\";}\n\t\t$fp   = $this->openFile($name, 'r');\n\t\t$temp = fread($fp, 60000);\n\t\t// Формат файла Sypex Dumper 2 - SXD20\n\t\t//if(!preg_match('/^(#SXD20\\|.+?)\\n#EOH\\n/s', $temp, $m)) return \"sxd.tree.{$tree}.error(sxd.lng('err_sxd2'));z('restore_savejob').disabled = z('restore_runjob').disabled = true;\";\n\t\tif(preg_match('/^(#SXD20\\|.+?)\\n#EOH\\n/s', $temp, $m)){\n\t\t\t$head = explode(\"\\n\", $m[1]);\n\t\t\t$h = explode('|', $head[0]);\n\t\t\tfor($i = 1, $c = count($head); $i < $c; $i++){\n\t\t\t\t$objects[substr($head[$i], 1,2)] = explode('|', substr($head[$i], 4));\n\t\t\t}\n\t\t\tfor($i = 0, $l = count($objects['TA']); $i < $l; $i++){\n\t\t\t\t$objects['TA'][$i] = explode('`', $objects['TA'][$i]);\n\t\t\t}\n\t\t}\n\t\telse {\n\t\t\t$h[9] = '';\n\t\t}\n\t\treturn $formatTree ? $this->formatTree($tree, $objects) .  \"sxd.comment.restore.value = '{$h[9]}';z('restore_savejob').disabled = z('restore_runjob').disabled = false;\" : $objects;\n\t}\n\tfunction formatTree($tree, &$objects){\n\t\t$obj = '';\n\t\t$pid = $row = 1;\n\t\t$info = array(\n\t\t\t'TA' => array($this->LNG['obj_tables'], 1),\n\t\t\t'VI' => array($this->LNG['obj_views'], 3), \n\t\t\t'PR' => array($this->LNG['obj_procs'], 5), \n\t\t\t'FU' => array($this->LNG['obj_funcs'], 7), \n\t\t\t'TR' => array($this->LNG['obj_trigs'], 9), \n\t\t\t'EV' => array($this->LNG['obj_events'], 11)\n\t\t);\n\t\t// Находим таблицы с префиксами\n\t\t$tab_prefix_last = $tab_prefix = '*';\n\t\tfor($i = 0, $l = count($objects['TA']); $i < $l; $i++){\n\t\t\t$t = $objects['TA'][$i];\n\t\t\t$tab_prefix = preg_match(\"/^([a-z0-9]+_)/\", $t[0], $m) ? $m[1] : '*';\n\t\t\tif ($tab_prefix != $tab_prefix_last) {\n\t\t\t\tif ($tab_prefix != '*') $objects['TA']['*'][] = $tab_prefix;\n\t\t\t\t$tab_prefix_last = $tab_prefix;\n\t\t\t}\n\t\t\t$objects['TA'][$tab_prefix][] = $t;\n\t\t\tunset($objects['TA'][$i]);\n\t\t}\n\t\tforeach($objects AS $type => $o){\n\t\t\tif(!count($o)) continue;\n\t\t\tif($type == 'TA') {\n\t\t\t\t$open_childs = count($o['*']) > 1 ? 0 : 1;\n\t\t\t\t$obj .= \"[{$row},0,\" . sxd_esc($info[$type][0]) . \",1,1,1],\";\n\t\t\t\t$row++;\n\t\t\t\tforeach($o['*'] AS $value){\n\t\t\t\t\tif(is_string($value)){\n\t\t\t\t\t\tif(count($o[$value]) > 1)\t{\n\t\t\t\t\t\t\t$obj .= \"[{$row},1,'{$value}*',1,1,{$open_childs}],\";\t\n\t\t\t\t\t\t\t$pid = $row++; \n\t\t\t\t\t\t\tfor($i = 0, $l = count($o[$value]); $i < $l; $i++){\n\t\t\t\t\t\t\t\t$checked = ($o[$value][$i][1] == '' && $o[$value][$i][2] == '') ? 2 : 1;\n\t\t\t\t\t\t\t\t$obj .= \"[{$row},{$pid},\" . sxd_esc($o[$value][$i][0]) . \",2,{$checked},{$o[$value][$i][2]}],\";\n\t\t\t\t\t\t\t\t$row++;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse {\n\t\t\t\t\t\t\t$value = $o[$value][0];\t\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\t//$pid = 1;\n\t\t\t\t\tif (is_array($value)){\n\t\t\t\t\t\t$checked = ($value[1] == '' && $value[2] == '') ? 2 : 1;\n\t\t\t\t\t\t$obj .= \"[{$row},1,'{$value[0]}',2,{$checked},{$value[2]}],\";\n\t\t\t\t\t\t$row++;\t\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\telse {\n\t\t\t\t$obj .= \"[{$row},0,\" . sxd_esc($info[$type][0]) . \",{$info[$type][1]},1,1],\";\n\t\t\t\t$pid = $row++;\n\t\t\t\t$info[$type][1]++;\n\t\t\t\tfor($i = 0, $l = count($o); $i < $l; $i++){\n\t\t\t\t\t$o[$i] = sxd_esc($o[$i], false);\n\t\t\t\t\t$obj .= \"[{$row},{$pid},'{$o[$i]}',{$info[$type][1]},1,0],\";\n\t\t\t\t\t$row++;\t\n\t\t\t\t}\n\t\t\t} \n\t\t}\n\t\t$add = '';\n\t\tif($tree == 'restore') $add = \"z('autoinc').disabled = z('restore_type').disabled = \" . ($obj ? 'false' : 'true') . \";\";\n\t\treturn ($obj ? 'sxd.tree.' . $tree . '.drawTree([' . substr_replace($obj, ']',  -1) . \");\" : \"sxd.tree.{$tree}.error(sxd.lng('err_sxd2'));\") . $add;\t\n\t}\n\tfunction getFileList(){\n\t\t$files = array();\n\t\tif (is_dir($this->CFG['backup_path']) && false !== ($handle = opendir($this->CFG['backup_path']))) {\n            while (false !== ($file = readdir($handle))) {\n                if (preg_match(\"/^.+?\\.sql(\\.(gz|bz2))?$/\", $file)) {\n                    $files[$file] = $file;\n                }\n            }\n            closedir($handle);\n        }\n        ksort($files);\n\t\treturn $files;\n\t}\n\tfunction getSavedJobs(){\n\t\t$sj = array('sj_backup' => array(), 'sj_restore' => array(),);\n\t\tif (is_dir($this->CFG['backup_path']) && false !== ($handle = opendir($this->CFG['backup_path']))) {\n            while (false !== ($file = readdir($handle))) {\n                if (preg_match(\"/^sj_(.+?)\\.job.php$/\", $file)) {\n                \tinclude($this->CFG['backup_path'] . $file);\n                    $sj['sj_' . $JOB['type']][$JOB['job']] = \"<b>{$JOB['job']}</b><br><i>{$JOB['title']}&nbsp;</i>\";\n                }\n            }\n            closedir($handle);\n        }\n        if(count($sj['sj_backup']) > 0){\n        \tksort($sj['sj_backup']);\t\n\t\t}\n        else {\n        \t$sj['sj_backup'] = array(0 => '<b>No Saved Jobs</b><br>' . $this->LNG['no_saved']);\t\n\t\t}\n\t\tif(count($sj['sj_restore']) > 0){\n        \tksort($sj['sj_restore']);\t\n\t\t}\n        else {\n        \t$sj['sj_restore'] = array(0 => '<b>No Saved Jobs</b><br>' . $this->LNG['no_saved']);\t\n\t\t}\n\t\treturn \"sxd.clearOpt('sj_backup');sxd.clearOpt('sj_restore');sxd.addOpt(\" . sxd_php2json($sj) . \");\";\n\t}\n\tfunction getFileListExtended(){\n\t\t$files = array();\n\t\tif (is_dir($this->CFG['backup_path']) && false !== ($handle = opendir($this->CFG['backup_path']))) {\n            while (false !== ($file = readdir($handle))) {\n                if (preg_match(\"/^.+?\\.sql(\\.(gz|bz2))?$/\", $file, $m)) {\n                    $fp   = $this->openFile($this->CFG['backup_path'] . $file, 'r');\n                    $ext  = !empty($m[2]) ? $m[2] : 'sql';\n                    $temp = fgets($fp);\n                    if(preg_match('/^(#SXD20\\|.+?)\\n/s', $temp, $m)){\n                    \t$h = explode('|', $m[1]);\n                    \t$files[] = array($h[5], substr($h[4], 0, -3), $ext, $h[7], number_format($h[8], 0, '', ' '), filesize($this->CFG['backup_path'] . $file), $h[9], $file);\t\n\t\t\t\t\t}\n\t\t\t\t\telseif(preg_match('/^(#SKD101\\|.+?)\\n/s', $temp, $m)){\n                    \t$h = explode('|', $m[1]);\n                    \t$files[] = array($h[1], substr($h[3], 0, -3), $ext, $h[2], number_format($h[4], 0, '', ' '), filesize($this->CFG['backup_path'] . $file), 'SXD 1.0.x', $file);\t\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\t$files[] = array($file, '-', $ext, '-', '-', filesize($this->CFG['backup_path'] . $file), '', $file);\n\t\t\t\t\t}\n                }\n            }\n            closedir($handle);\n        }\n        function s($a, $b){\n\t\t    return strcmp($b[1], $a[1]);\n\t\t}\n        usort($files, 's');\n\t\treturn 'sxd.files.clear();sxd.files.add(' . sxd_php2json($files) . ');';\n\t}\n\tfunction saveJob($job, $config){\n\t\t$this->saveToFile($this->CFG['backup_path'] . $job . '.job.php', \"<?php\\n\\$JOB = \" . var_export($config, true) . \";\\n\" . \"?>\");\n\t}\n\tfunction openFile($name, $mode){\n\t\tif($mode == 'r') {\n\t\t\tif(preg_match('/\\.(sql|sql\\.bz2|sql\\.gz)$/i', $name, $m)) $this->JOB['file_ext'] = strtolower($m[1]);\n\t\t}\n\t\telse{\n\t\t\tswitch($this->JOB['zip']) {\n\t\t\t\tcase 0 : $this->JOB['file_ext'] = 'sql'; break;\n\t\t\t\tcase 10: $this->JOB['file_ext'] = 'sql.bz2'; break;\n\t\t\t\tdefault: $this->JOB['file_ext'] = 'sql.gz'; break; \n\t\t\t}\n\t\t}\n\t\tswitch ($this->JOB['file_ext']){\n\t\t\tcase 'sql':\n\t\t\t\treturn fopen($name, \"{$mode}b\");\n\t\t\t\tbreak;\n\t\t\tcase 'sql.bz2':\n\t\t\t\treturn bzopen($name, $mode);\n\t\t\t\tbreak;\n\t\t\tcase 'sql.gz':\n\t\t\t\treturn gzopen($name, $mode . ($mode == 'w' ? $this->JOB['zip'] : ''));\n\t\t\t\tbreak;\n\t\t\tdefault: return false;\n\t\t}\n\t}\n}\nfunction sxd_read_sql($f, &$seek, $ei, $delimiter = \"\\t;\", $eol = \"\\n\"){\n\tstatic $l = '';\n\tstatic $r = 0;\n\t$fs = ftell($f);\n\t$delim_len = strlen($delimiter . $eol);\n\twhile($r || $s = fread($f, 61440)){\n\t\tif(!$r) $l .= $s;\n\t\t$pos = strpos($l, $delimiter . $eol);\n\t\tif ($pos !== false) {\n\t\t\t// Есть окончание запроса\n\t\t\t$q = substr($l, 0, $pos);\n\t\t\t$l = substr($l, $pos+$delim_len);\n\t\t\t$r = 1;\n\t\t\t$seek = strlen($l);\n\t\t\treturn $q;\n\t\t}\n\t\tif($ei) {\n\t\t\t$pos = strrpos($l, $eol);\n\t\t\tif($pos > 0 && $l{$pos-1} === ',') {\n\t\t\t\t// Окончание не найдено\n\t\t\t\t$q = substr($l, 0, $pos-1);\n\t\t\t\t$l = substr($l, $pos+ strlen($eol));\n\t\t\t\t$seek = strlen($l);\n\t\t\t\t$r = 0;\n\t\t\t\treturn $q;\n\t\t\t}\n\t\t}\n\t\t$r = 0;\t\n\t}\n\tif (!empty($l)) {\n\t\treturn $l;\n\t}\n\treturn false;\n}\nfunction sxd_check($n, $obj, $filt){\n\treturn isset($obj[$n]) || ($filt && preg_match($filt, $n));\n}\nfunction sxd_php2json($obj){\n\tif(count($obj) == 0) return '[]';\n\t$is_obj = isset($obj[0]) && isset($obj[count($obj) - 1]) ? false : true;\n\t$str = $is_obj ? '{' : '[';\n    foreach ($obj AS $key  => $value) {\n    \t$str .= $is_obj ? \"'\" . addcslashes($key, \"\\n\\r\\t'\\\\/\") . \"'\" . ':' : ''; \n        if     (is_array($value))   $str .= sxd_php2json($value);\n        elseif (is_null($value))    $str .= 'null';\n        elseif (is_bool($value))    $str .= $value ? 'true' : 'false';\n\t\telseif (is_numeric($value)) $str .= $value;\n\t\telse                        $str .= \"'\" . addcslashes($value, \"\\n\\r\\t'\\\\/\") . \"'\";\n\t\t$str .= ',';\n    }\n\treturn  substr_replace($str, $is_obj ? '}' : ']', -1);\n}\nfunction sxd_ver2int($ver){\n\treturn preg_match(\"/^(\\d+)\\.(\\d+)\\.(\\d+)/\", $ver, $m) ? sprintf(\"%d%02d%02d\", $m[1], $m[2], $m[3]) : 0;\n}\nfunction sxd_error_handler($errno, $errmsg, $filename, $linenum, $vars){\n    global $SXD;\n    if($SXD->try) return;\n\tif($errno == 8192) return;\n    if(strpos($errmsg, 'timezone settings')) return;\n    $errortype = array(1 => 'Error', 2 => 'Warning', 4 => 'Parsing Error', 8 => 'Notice', 16 => 'Core Error', 32 => 'Core Warning', 64 => 'Compile Error',\n\t\t\t\t\t   128 => 'Compile Warning', 256 => 'MySQL Error', 512 => 'Warning', 1024 => 'Notice',\n\t\t\t\t\t\t2048 => 'Strict', 8192 => 'Deprecated', 16384 => 'Deprecated');\n\t$str = sxd_esc(\"{$errortype[$errno]}: {$errmsg} ({$filename}:{$linenum})\", false);\n\tif(SXD_DEBUG) error_log(\"[index.php]\\n{$str}\\n\", 3, \"backup/error.log\");\n\t\n    if($errno == 8 || $errno == 1024) {\n    \tif (!$SXD->fh_log && !$SXD->fh_rtl) echo isset($_POST['ajax']) ? \"alert('\" . ($str) . \"');\" : $str;\n    \telse {\n    \t\tfwrite($SXD->fh_log, date('Y.m.d H:i:s') . \"\\t3\\t{$str}\\n\");\n\t\t}\n\t}\n    elseif($errno < 1024) {\n    \t$SXD->error = true;\n    \tif (!$SXD->fh_log && !$SXD->fh_rtl) echo isset($_POST['ajax']) ? \"alert('\" . ($str) . \"');\" : $str;\n    \telse {\n    \t\t$SXD->rtl[1] = time();\n    \t\t$SXD->rtl[9] = 5;\n    \t\tfseek($SXD->fh_rtl, 0);\n\t\t\tfwrite($SXD->fh_rtl, implode(\"\\t\", $SXD->rtl));\n    \t\tfwrite($SXD->fh_log, date('Y.m.d H:i:s') . \"\\t4\\t{$str}\\n\");\n    \t\tunset($SXD->rtl);\n\t\t}\n\t\t\n    \tdie;\n\t}\n}\nfunction sxd_esc($str, $quoted = true){\n\treturn $quoted ? \"'\" . addcslashes($str, \"\\\\\\0\\n\\r\\t\\'\") . \"'\" : addcslashes($str, \"\\\\\\0\\n\\r\\t\\'\");\n}\nfunction sxd_my_error(){\n\ttrigger_error(mysql_error(), E_USER_ERROR);\t\n}\nfunction sxd_shutdown(){\n\tglobal $SXD;\n\tif(isset($SXD->fh_rtl) && is_resource($SXD->fh_rtl) && !empty($SXD->rtl) && empty($SXD->error)) {\n\t\t$SXD->rtl[1] = time();\n\t\tif(!empty($SXD->JOB['file_stp']) && file_exists(dirname(__FILE__) . '/' . $SXD->JOB['file_stp'])){\n\t\t\t$type = file_get_contents(dirname(__FILE__) . '/' . $SXD->JOB['file_stp']);\n\t\t\t$SXD->rtl[9] = !empty($type) ? $type : 2;\n\t\t}\n\t\telse $SXD->rtl[9] = 5;\n\t\tfseek($SXD->fh_rtl, 0);\n\t\tfwrite($SXD->fh_rtl, implode(\"\\t\", $SXD->rtl));\n\t}\n}\nfunction sxd_antimagic($arr){\n\treturn is_array($arr) ? array_map('sxd_antimagic', $arr) : stripslashes($arr);\n}\n"
  },
  {
    "path": "admin/sxd/info.php",
    "content": "<?php\n// Sypex Dumper 2\nheader(\"Content-Type: text/html; charset=utf-8\");\nerror_reporting(0);\nset_error_handler(\"sxd_error\");\nif(!empty($_POST['ajax']['job']) && preg_match(\"/^[\\w-]+$/\", $_POST['ajax']['job'])){\n\t$d = date(\"'Y.m.d H:i:s'\");\n\tif(!empty($_COOKIE['sxd'])) {\n\t\tinclude('ses.php');\n\t\tif(!empty($SES[$_COOKIE['sxd']])) {\n\t\t\t$CFG = &$SES[$_COOKIE['sxd']]['cfg'];\n\t\t\tinclude(load_lang($SES[$_COOKIE['sxd']]['lng']));\n\t\t}\n\t}\n\tif(empty($LNG)) {\n\t\tinclude('cfg.php');\n\t\tinclude(load_lang($CFG['lang']));\n\t\tif(!empty($CFG['auth'])) {echo \"sxd.log.add({$d},[\" . esc($LNG['stop_5']) . \" (1)]);sxd.hideLoading();\"; exit;}\n\t} \n\t$job_name = $_POST['ajax']['job'];\n\t$log_seek = !empty($_POST['ajax']['lseek']) ? (int) $_POST['ajax']['lseek'] : 0;\n\t$job_file = \"{$CFG['backup_path']}{$job_name}.job.php\";\n\tif (!file_exists($job_file)) exit;\n\tinclude($job_file);\n\tswitch($_POST['ajax']['act']) {\n\t\tcase 'info':\n\t\t\tif (!file_exists($JOB['file_rtl'])) exit;\n\t\t\t$fh = fopen($JOB['file_rtl'], 'r+b');\n\t\t\t$time = time();\n\t\t\t$f = explode(\"\\t\", fgets($fh));\n\t\t\tif(empty($f[0])) $f[0] = $time;\n\t\t\t$pt = !empty($f[2]) ? round(100 * $f[10] / $f[2], 2) : 100;\n\t\t\t$pc = !empty($f[8]) ? round(100 * $f[7] / $f[8], 2) : 100;\n\t\t\t$lh = fopen($JOB['file_log'], 'rb');\n\t\t\tfseek($lh, $log_seek);\n\t\t\t$rawlog = fread($lh, 8192);\n\t\t\t$log = '';\n\t\t\t$old_time = '';\n\t\t\t$logs = array();\n\t\t\tif(!empty($rawlog)){\n\t\t\t\t$temp = explode(\"\\n\", $rawlog);\n\t\t\t\tforeach($temp AS $l){\n\t\t\t\t\tif(empty($l)) continue;\n\t\t\t\t\t$t = explode(\"\\t\", $l);\n\t\t\t\t\tif(count($t) < 3) continue;\n\t\t\t\t\tif($t[0] != $old_time){\n\t\t\t\t\t\tif(!empty($logs)) $log .= \"sxd.log.add('{$old_time}',[\" . implode(',', $logs) . \"]);\";\n\t\t\t\t\t\t$old_time = $t[0];\n\t\t\t\t\t\t$logs = array();\n\t\t\t\t\t}\n\t\t\t\t\t$logs[] = esc($t[2]);\t\n\t\t\t\t}\n\t\t\t\tif(!empty($logs)) $log .= \"sxd.log.add('{$old_time}',[\" . implode(',', $logs) . \"]);\";\n\t\t\t}\n\t\t\t$log_seek = ftell($lh);\n\t\t\techo $log . \"sxd.job.log_seek = {$log_seek};\";\n\t\t\t// Читаем лог\n\t\t\tif($f[4] == 'EOJ') {\n\t\t\t\t$pt = $pc = 100; \n\t\t\t\tfclose($lh);\n\t\t\t\tfclose($fh);\n\t\t\t\tif (function_exists('usleep')) usleep(400000);\n\t\t\t\telse sleep(1);\n\t\t\t\tif($JOB['act'] == 'backup') $f[3] = filesize(file_exists($JOB['file_name']) ? $JOB['file_name'] : $JOB['file_tmp']);\n\t\t\t\t// Обновляем список файлов\n\t\t\t\tif($JOB['act'] == 'backup') print \"sxd.actions.filelist(); z('btn_down').file = '{$JOB['file']}'; z('btn_down').style.display = '';\";\n\t\t\t\techo \"sxd.timer.set({$f[0]},{$f[1]},{$pt});sxd.progress.current.set({$pc}, 0, {$f[8]}, {$f[8]});sxd.progress.total.set({$pt},{$f[3]});\";\n\t\t\t\techo \"sxd.log.add({$d},['{$LNG['job_done']}', '{$LNG['js']['records']}: {$f[10]}', '{$LNG['file_size']}: ' + sxd.formatSize({$f[3]},2), '{$LNG['job_time']}: {$f[5]} {$LNG['seconds']}']);sxd.hideLoading();\";\n\t\t\t\tunlink($JOB['file_log']);\n\t\t\t\tunlink($JOB['file_rtl']);\n\t\t\t\tunlink($job_file);\n\t\t\t}\n\t\t\telse if($f[9] > 0){\n\t\t\t\techo \"sxd.log.add({$d},[\" . esc($LNG['stop_' . $f[9]]) . \"]);\" . (($f[9] == 3 || $f[9] == 4) ? 'sxd.resumeJob();' : 'sxd.hideLoading();');\n\t\t\t}\n\t\t\telse{\n\t\t\t\tif($JOB['act'] == 'backup') $f[3] = filesize(file_exists($JOB['file_name']) ? $JOB['file_name'] : $JOB['file_tmp']);\n\t\t\t\tif($f[4] != 'EK' && time() > $f[1] + 45) {\n\t\t\t\t\tfopen($JOB['file_stp'],'w');\n\t\t\t\t\t$f[9] = 0;\n\t\t\t\t\t$f[1] = $time;\n\t\t\t\t\tfwrite($fh, implode(\"\\t\", $f) . \"\\n\");\n\t\t\t\t\techo \"sxd.log.add({$d},[\" . esc($LNG['job_freeze']) . \"]);sxd.hideLoading();z('btn_resume').style.display = '';z('btn_pause').style.display = 'none';\";\n\t\t\t\t}\n\t\t\t\techo \"sxd.timer.set({$f[0]},{$time},{$pt});sxd.progress.current.set({$pc},0,{$f[7]}, {$f[8]});sxd.progress.total.set({$pt},{$f[3]});\";\t\n\t\t\t}\n\t\tbreak;\n\t\tcase 'stop': \n\t\t\t$fs = fopen($JOB['file_stp'],'w');\n\t\t\tfwrite($fs, 1);\n\t\t\techo \"sxd.log.add({$d},[\" . esc($LNG['stop_job']) . \"]);\";\n\t\tbreak;\n\t\tcase 'pause': \n\t\t\tfopen($JOB['file_stp'],'w');\n\t\t\techo \"sxd.log.add({$d},[\" . esc($LNG['stop_job']) . \"]);\";\n\t\tbreak;\n\t}\n}\nelse echo \"sxd.hideLoading();\";\t\n\nfunction load_lang($lng_name = 'auto'){\n\tif($lng_name == 'auto'){\n\t\tinclude('lang/list.php');\n\t\t$lng = 'en';\n\t\tif(preg_match_all('/[a-z]{2}(-[a-z]{2})?/', $_SERVER['HTTP_ACCEPT_LANGUAGE'], $m)) {\n\t\t\tforeach($m[0] AS $l){\n\t\t\t\tif(isset($langs[$l])){\n\t\t\t\t\t$lng_name = $l;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\tif(file_exists(\"lang/lng_{$lng_name}.php\")) return \"lang/lng_{$lng_name}.php\";\n\telse return \"lang/lng_en.php\";\n}\nfunction esc($str){\n\treturn \"'\" . addcslashes($str, \"\\\\\\0\\n\\r\\t\\'\") . \"'\";\n}\n\nfunction sxd_error($errno, $errmsg, $filename, $linenum, $vars){\n\tglobal $JOB;\n\tif($errno == 8192) return;\n\tif(strpos($errmsg, 'timezone settings')) return;\n\tif(!empty($JOB['file_stp'])) fopen($JOB['file_stp'],'w');\n\t$errortype = array(1 => 'Error', 2 => 'Warning', 4 => 'Parsing Error', 8 => 'Notice', 16 => 'Core Error', 32 => 'Core Warning', 64 => 'Compile Error', \n\t\t\t\t\t   128 => 'Compile Warning', 256 => 'User Error', 512 => 'User Warning', 1024 => 'User Notice');\n\t$str = \"{$errortype[$errno]}: {$errmsg} ({$filename}:{$linenum})\";\n\t//error_log(\"[info.php]\\n{$str}\\n\", 3, \"error.log\"); \n    if($errno == 8 || $errno == 1024) {\n    \techo \"sxd.log.add('\" . date(\"Y.m.d H:i:s\") . \"',[\" . esc($str) . \"], 4);sxd.hideLoading();\";\n\t}\n    elseif($errno < 1024) {\n    \techo \"sxd.log.add('\" . date(\"Y.m.d H:i:s\") . \"',[\" . esc($str) . \"], 4);sxd.hideLoading();\";\n    \tdie;\n\t}\n}"
  },
  {
    "path": "admin/sxd/lang/index.htm",
    "content": ""
  },
  {
    "path": "admin/sxd/lang/list.php",
    "content": "<?php\n$langs = array (\n  'en' => 'English',\n  'ru' => 'Русский',\n  'uk' => 'Українська',\n);\n?>"
  },
  {
    "path": "admin/sxd/lang/lng_en.php",
    "content": "<?php\n// Language File for Sypex Dumper 2\n$LNG = array(\n\n// Information about the language file\n'ver'\t\t\t\t=> 20005, // Dumper version\n'translated'\t\t=> 'zapimir (http://sypex.net/)', // Contacts\n'name'\t\t\t\t=> 'English', // Lang name\n\n// Toolbar\n'tbar_backup'\t\t=> 'Export',\n'tbar_restore'\t\t=> 'Import', \n'tbar_files'\t\t=> 'Files',\n'tbar_services'\t\t=> 'Services',\n'tbar_options'\t\t=> 'Options',\n'tbar_createdb'\t\t=> 'Create DB',\n'tbar_connects'\t\t=> 'Connection',\n'tbar_exit'\t\t\t=> 'Exit',\n\n// Names of objects in the tree\n'obj_tables'\t\t=> 'Tables',\n'obj_views'\t\t\t=> 'Views',\n'obj_procs'\t\t\t=> 'Procedures',\n'obj_funcs'\t\t\t=> 'Functions',\n'obj_trigs'\t\t\t=> 'Triggers',\n'obj_events'\t\t=> 'Events',\n\n// Export\n'zip_max'\t\t\t=> 'max',\n'zip_min'\t\t\t=> 'min',\n'zip_none'\t\t\t=> 'Uncompressed',\n'default'\t\t\t=> 'default',\n'combo_db'\t\t\t=> 'Database (Schema):', \n'combo_charset'\t\t=> 'Charset:', \n'combo_zip'\t\t\t=> 'Compression:', \n'combo_comments'\t=> 'Comment:',\n'del_legend'\t\t=> 'Autodelete if:',\n'del_date'\t\t\t=> 'age of files more than %s days',\n'del_count'\t\t\t=> 'number of files more than %s',\n'tree'\t\t\t\t=> 'Select objects:',\n'no_saved'\t\t\t=> 'No saved jobs',\n'btn_save'\t\t\t=> 'Save',\n'btn_exec'\t\t\t=> 'Execute',\n\n// Import\t\n'combo_file'\t\t=> 'File:',\n'combo_strategy'\t=> 'Restore strategy:',\n'ext_legend'\t\t=> 'Extended options:',\n'correct'\t\t\t=> 'Charset correction',\n'autoinc'\t\t\t=> 'Reset AUTO_INCREMENT',\n\n// Log\n'status_current'\t=> 'Current status:',\n'status_total'\t\t=> 'Total status:',\n'time_elapsed'\t\t=> 'Elapsed:',\n'time_left'\t\t\t=> 'Left:',\n'btn_stop'\t\t\t=> 'Abort',\n'btn_pause'\t\t\t=> 'Pause',\n'btn_resume'\t\t=> 'Resume',\n'btn_again'\t\t\t=> 'Repeat',\n'btn_clear'\t\t\t=> 'Clear log',\n\n// Files\n'btn_delete'\t\t=> 'Delete',\n'btn_download'\t\t=> 'Download',\n'btn_open'\t\t\t=> 'Open',\n\n// Services\n'opt_check'\t\t\t=> 'Options for Check:',\n'opt_repair'\t\t=> 'Options for Repair:',\n'btn_delete_db'\t\t=> 'Delete DB',\n'btn_check'\t\t\t=> 'Check',\n'btn_repair'\t\t=> 'Repair',\n'btn_analyze'\t\t=> 'Analyze',\n'btn_optimize'\t\t=> 'Optimize',\n\n// Options\n'cfg_legend'\t\t=> 'Basic settings:',\n'cfg_time_web'\t\t=> 'Timelimit web (seconds):',\n'cfg_time_cron'\t\t=> 'Timelimit cron (seconds):',\n'cfg_backup_path'\t=> 'Path to backup directory:',\n'cfg_backup_url'\t=> 'URL to backup directory:',\n'cfg_globstat'\t\t=> 'Global statistics:',\n'cfg_extended'\t\t=> 'Extended settings:',\n'cfg_charsets'\t\t=> 'Charset filter:',\n'cfg_only_create'\t=> 'Only create types:',\n'cfg_auth'\t\t\t=> 'Authorization chain:',\n'cfg_confirm'\t\t=> 'Ask confirmation for:',\n'cfg_conf_import'\t=> 'import',\n'cfg_conf_file'\t\t=> 'file delete',\n'cfg_conf_db'\t\t=> 'database delete',\n\n// Connection\n'con_header'\t\t=> 'Connection Settings',\n'connect'\t\t\t=> 'Connection',\n'my_host'\t\t\t=> 'Host:',\n'my_port'\t\t\t=> 'Port:',\n'my_user'\t\t\t=> 'User:',\n'my_pass'\t\t\t=> 'Password:',\n'my_pass_hidden'\t=> 'Password is not shown',\n'my_comp'\t\t\t=> 'Compression protocol',\n'my_db'\t\t\t\t=> 'Databases:',\n'btn_cancel'\t\t=> 'Cancel',\n\n// Save Job\n'sj_header'\t\t\t=> 'Save job',\n'sj_job'\t\t\t=> 'Job',\n'sj_name'\t\t\t=> 'Name (eng.):',\n'sj_title'\t\t\t=> 'Description:',\n\n// Create DB\n'cdb_header'\t\t=> 'Create new database',\n'cdb_detail'\t\t=> 'Details',\n'cdb_name'\t\t\t=> 'Name:',\n'combo_collate'\t\t=> 'Collation:',\n'btn_create'\t\t=> 'Create',\n\n// Authorization\n'js_required'\t\t=> 'JavaScript must be enabled',\n'auth'\t\t\t\t=> 'Authorization',\n'auth_user'\t\t\t=> 'User:',\n'auth_remember'\t\t=> 'remember',\n'btn_enter'\t\t\t=> 'Enter',\n'btn_details'\t\t=> 'Details',\n\n// Log messages\n'not_found_rtl'\t\t=> 'RTL-file not exists',\n'backup_begin'\t\t=> 'Start export DB `%s`',\n'backup_TC'\t\t\t=> 'Export table `%s`',\n'backup_VI'\t\t\t=> 'Export view `%s`',\n'backup_PR'\t\t\t=> 'Export procedure `%s`',\n'backup_FU'\t\t\t=> 'Export function `%s`',\n'backup_EV'\t\t\t=> 'Export event `%s`',\n'backup_TR'\t\t\t=> 'Export trigger `%s`',\n'continue_from'\t\t=> 'from positions %s',\n'backup_end'\t\t=> 'Export database `%s` finished.',\n'autodelete'\t\t=> 'Autodelete of old files:',\n'del_by_date'\t\t=> '- `%s` - deleted (by date)',\n'del_by_count'\t\t=> '- `%s` - deleted (by count)',\n'del_fail'\t\t\t=> '- `%s` - delete fail',\n'del_nothing'\t\t=> '- no files to delete',\n'set_names'\t\t\t=> 'Set connection encoding: `%s`',\n'restore_begin'\t\t=> 'Start import DB `%s`',\n'restore_TC'\t\t=> 'Import table `%s`',\n'restore_VI'\t\t=> 'Import view `%s`',\n'restore_PR'\t\t=> 'Import procedure `%s`',\n'restore_FU'\t\t=> 'Import function `%s`',\n'restore_EV'\t\t=> 'Import event `%s`',\n'restore_TR'\t\t=> 'Import trigger `%s`',\n'restore_keys'\t\t=> 'Enable indexes',\n'restore_end'\t\t=> 'DB `%s` restored from a backup.',\n'stop_1'\t\t\t=> 'Execution aborted by user', \n'stop_2'\t\t\t=> 'Execution stopped by user',\n'stop_3'\t\t\t=> 'Execution stopped by timer',\n'stop_4'\t\t\t=> 'Execution stopped by timeout',\n'stop_5'\t\t\t=> 'Execution aborted because of an error',\n'job_done'\t\t\t=> 'Job successful',\n'file_size'\t\t\t=> 'File size',\n'job_time'\t\t\t=> 'Time spent',\n'seconds'\t\t\t=> 'seconds',\n'job_freeze'\t\t=> 'The process has not been updated for more than 30 seconds. Click Resume',\n'stop_job'\t\t\t=> 'Stop request',\n\n// For JS\n'js' => array(\n\t\n\t// Tabs names\n\t'backup'\t\t=> 'Export database (schema)',\n\t'restore'\t\t=> 'Import database (schema)',\n\t'log'\t\t\t=> 'Log',\n\t'result'\t\t=> 'Results',\n\t'files'\t\t\t=> 'Files',\n\t'services'\t\t=> 'Services',\n\t'options'\t\t=> 'Options',\n\n\t// Tables header\n\t'dt'\t\t\t=> 'Date/time',\n\t'action'\t\t=> 'Action',\n\t'db'\t\t\t=> 'Database',\n\t'type'\t\t\t=> 'Type',\n\t'tab'\t\t\t=> 'Tabs',\n\t'records'\t\t=> 'Records',\n\t'size'\t\t\t=> 'Size',\n\t'comment'\t\t=> 'Comments',\n\n\t// AJAX Status\n\t'load'\t\t\t=> 'Loading',\n\t'run'\t\t\t=> 'Running...',\n\t'sdb'\t\t\t=> 'Create new database',\n\t'sc'\t\t\t=> 'Save connection',\n\t'sj'\t\t\t=> 'Save job',\n\t'so'\t\t\t=> 'Save options',\n\n\t// Messages\n\t'pro'\t\t\t=> 'Option available only in Pro-version',\n\t'err_fopen'\t\t=> 'Unable to open file',\n\t'err_sxd2'\t\t=> 'View file contents available only for files created by Sypex Dumper 2',\n\t'err_empty_db'\t=> 'Database is empty',\n\t'fdc'\t\t\t=> 'Do you really want to delete file?',\n\t'ddc'\t\t\t=> 'Do you really want to delete database?',\n\t'fic'\t\t\t=> 'Do you really want to import file?',\n\n\t// Sizes\n\t'sizes'\t\t\t=> array('B', 'KB', 'MB', 'GB'),\n)\n);\n?>\n"
  },
  {
    "path": "admin/sxd/lang/lng_ru.php",
    "content": "<?php\n// Language File for Sypex Dumper 2\n$LNG = array(\n\n// Информация о файле локализации\n'ver'\t\t\t\t=> 20005, // Для какой версии предназначен файл\n'translated'\t\t=> 'zapimir (http://sypex.net/)', // Контакты переводчика\n'name'\t\t\t\t=> 'Русский', // Название языка\n\n// Панель инструментов\n'tbar_backup'\t\t=> 'Экспорт',\n'tbar_restore'\t\t=> 'Импорт', \n'tbar_files'\t\t=> 'Файлы',\n'tbar_services'\t\t=> 'Сервисы',\n'tbar_options'\t\t=> 'Опции',\n'tbar_createdb'\t\t=> 'Создать БД',\n'tbar_connects'\t\t=> 'Соединение',\n'tbar_exit'\t\t\t=> 'Выход',\n\n// Названия объектов в дереве\n'obj_tables'\t\t=> 'Таблицы',\n'obj_views'\t\t\t=> 'Представления',\n'obj_procs'\t\t\t=> 'Процедуры',\n'obj_funcs'\t\t\t=> 'Функции',\n'obj_trigs'\t\t\t=> 'Триггеры',\n'obj_events'\t\t=> 'События',\n\n// Экспорт\n'zip_max'\t\t\t=> 'максимум',\n'zip_min'\t\t\t=> 'минимум',\n'zip_none'\t\t\t=> 'Без сжатия',\n'default'\t\t\t=> 'по умолчанию',\n'combo_db'\t\t\t=> 'База данных:', \n'combo_charset'\t\t=> 'Кодировка:', \n'combo_zip'\t\t\t=> 'Сжатие:', \n'combo_comments'\t=> 'Комментарий:',\n'del_legend'\t\t=> 'Автоудаление, если:',\n'del_date'\t\t\t=> 'файлам больше %s дней',\n'del_count'\t\t\t=> 'количество файлов более %s',\n'tree'\t\t\t\t=> 'Выберите объекты:',\n'no_saved'\t\t\t=> '*Нет сохраненных задач',\n'btn_save'\t\t\t=> 'Сохранить',\n'btn_exec'\t\t\t=> 'Выполнить',\n\n// Импорт\t\n'combo_file'\t\t=> 'Файл:',\n'combo_strategy'\t=> 'Стратегия восстановления:',\n'ext_legend'\t\t=> 'Дополнительные опции:',\n'correct'\t\t\t=> 'Коррекция кодировки',\n'autoinc'\t\t\t=> 'Обнулить AUTO_INCREMENT',\n\n// Лог\n'status_current'\t=> 'Текущий статус:',\n'status_total'\t\t=> 'Общий статус:',\n'time_elapsed'\t\t=> 'Прошло:',\n'time_left'\t\t\t=> 'Осталось:',\n'btn_stop'\t\t\t=> 'Прервать',\n'btn_pause'\t\t\t=> 'Пауза',\n'btn_resume'\t\t=> 'Продолжить',\n'btn_again'\t\t\t=> 'Повторить',\n'btn_clear'\t\t\t=> 'Очистить лог',\n\n// Файлы\n'btn_delete'\t\t=> 'Удалить',\n'btn_download'\t\t=> 'Скачать',\n'btn_open'\t\t\t=> 'Открыть',\n\n// Сервисы\n'opt_check'\t\t\t=> 'Опции для Проверить:',\n'opt_repair'\t\t=> 'Опции для Починить:',\n'btn_delete_db'\t\t=> 'Удалить БД',\n'btn_check'\t\t\t=> 'Проверить',\n'btn_repair'\t\t=> 'Починить',\n'btn_analyze'\t\t=> 'Анализировать',\n'btn_optimize'\t\t=> 'Оптимизировать',\n\n// Опции\n'cfg_legend'\t\t=> 'Основные настройки:',\n'cfg_time_web'\t\t=> 'Время выполнения в web (сек.):',\n'cfg_time_cron'\t\t=> 'Время выполнения в cron (сек.):',\n'cfg_backup_path'\t=> 'Путь к каталогу backup:',\n'cfg_backup_url'\t=> 'URL к каталогу backup:',\n'cfg_globstat'\t\t=> 'Глобальная статистика:',\n'cfg_extended'\t\t=> 'Расширенные настройки:',\n'cfg_charsets'\t\t=> 'Фильтр для кодировок:',\n'cfg_only_create'\t=> 'Бэкап только структуры:',\n'cfg_auth'\t\t\t=> 'Цепочка авторизации:',\n'cfg_confirm'\t\t=> 'Спрашивать подтверждение для:',\n'cfg_conf_import'\t=> 'импорта БД',\n'cfg_conf_file'\t\t=> 'удаления файла',\n'cfg_conf_db'\t\t=> 'удаления БД',\n\n// Соединение\n'con_header'\t\t=> 'Параметры соединения',\n'connect'\t\t\t=> 'Соединение',\n'my_host'\t\t\t=> 'Хост:',\n'my_port'\t\t\t=> 'Порт:',\n'my_user'\t\t\t=> 'Пользователь:',\n'my_pass'\t\t\t=> 'Пароль:',\n'my_pass_hidden'\t=> 'Пароль не показан',\n'my_comp'\t\t\t=> 'Протокол со сжатием',\n'my_db'\t\t\t\t=> 'Базы данных:',\n'btn_cancel'\t\t=> 'Отмена',\n\n// Сохранение задания\n'sj_header'\t\t\t=> 'Сохранение задания',\n'sj_job'\t\t\t=> 'Задание',\n'sj_name'\t\t\t=> 'Имя (англ.):',\n'sj_title'\t\t\t=> 'Описание:',\n\n// Создание БД\n'cdb_header'\t\t=> 'Создание базы данных',\n'cdb_detail'\t\t=> 'Детали',\n'cdb_name'\t\t\t=> 'Название:',\n'combo_collate'\t\t=> 'Сравнение',\n'btn_create'\t\t=> 'Создать',\n\n// Авторизация\n'js_required'\t\t=> 'JavaScript должен быть включен',\n'auth'\t\t\t\t=> 'Авторизация',\n'auth_user'\t\t\t=> 'Пользователь:',\n'auth_remember'\t\t=> 'Запомнить',\n'btn_enter'\t\t\t=> 'Войти',\n'btn_details'\t\t=> 'Детали',\n\n// Сообщения в логе\n'not_found_rtl'\t\t=> 'Отсутствует RTL-файл',\n'backup_begin'\t\t=> 'Начало экспорта БД `%s`',\n'backup_TC'\t\t\t=> 'Экспорт таблицы `%s`',\n'backup_VI'\t\t\t=> 'Экспорт представления `%s`',\n'backup_PR'\t\t\t=> 'Экспорт процедуры `%s`',\n'backup_FU'\t\t\t=> 'Экспорт функции `%s`',\n'backup_EV'\t\t\t=> 'Экспорт события `%s`',\n'backup_TR'\t\t\t=> 'Экспорт триггера `%s`',\n'continue_from'\t\t=> 'с позиции %s',\n'backup_end'\t\t=> 'Резервная копия БД `%s` создана.',\n'autodelete'\t\t=> 'Автоудаление старых файлов:',\n'del_by_date'\t\t=> '- `%s` - удален (по дате)',\n'del_by_count'\t\t=> '- `%s` - удален (по дате)',\n'del_fail'\t\t\t=> '- `%s` - удалить не удалось',\n'del_nothing'\t\t=> '- нет файлов для удаления',\n'set_names'\t\t\t=> 'Установлена кодировка соединения: `%s`',\n'restore_begin'\t\t=> 'Начало импорта БД `%s`',\n'restore_TC'\t\t=> 'Импорт таблицы `%s`',\n'restore_VI'\t\t=> 'Импорт представления `%s`',\n'restore_PR'\t\t=> 'Импорт процедуры `%s`',\n'restore_FU'\t\t=> 'Импорт функции `%s`',\n'restore_EV'\t\t=> 'Импорт события `%s`',\n'restore_TR'\t\t=> 'Импорт триггера `%s`',\n'restore_keys'\t\t=> 'Включение индексов',\n'restore_end'\t\t=> 'БД `%s` восстановлена из резервной копии.',\n'stop_1'\t\t\t=> 'Выполнение прервано пользователем', \n'stop_2'\t\t\t=> 'Выполнение остановлено пользователем',\n'stop_3'\t\t\t=> 'Выполнение остановлено по таймеру',\n'stop_4'\t\t\t=> 'Выполнение остановлено по таймауту',\n'stop_5'\t\t\t=> 'Выполнение прервано из-за ошибки',\n'job_done'\t\t\t=> 'Задание успешно выполнено',\n'file_size'\t\t\t=> 'Размер файла',\n'job_time'\t\t\t=> 'Времени затрачено',\n'seconds'\t\t\t=> 'сек.',\n'job_freeze'\t\t=> 'Процесс не обновлялся более 30 секунд. Нажмите Продолжить',\n'stop_job'\t\t\t=> 'Запрос на остановку',\n\n// Надписи в JS\n'js' => array(\n\n\t// Названия вкладок\n\t'backup'\t\t=> 'Экспорт базы данных',\n\t'restore'\t\t=> 'Импорт базы данных',\n\t'log'\t\t\t=> 'Лог действий',\n\t'result'\t\t=> 'Результат выполнения',\n\t'files'\t\t\t=> 'Файлы резервных копий',\n\t'services'\t\t=> 'Сервисы',\n\t'options'\t\t=> 'Опции',\n\n\t// Заголовки таблиц\n\t'dt'\t\t\t=> 'Дата и время',\n\t'action'\t\t=> 'Действие',\n\t'db'\t\t\t=> 'База данных',\n\t'type'\t\t\t=> 'Тип',\n\t'tab'\t\t\t=> 'Табл.',\n\t'records'\t\t=> 'Записей',\n\t'size'\t\t\t=> 'Размер',\n\t'comment'\t\t=> 'Комментарий',\n\t\n\t// Статусы\n\t'load'\t\t\t=> 'Загрузка',\n\t'run'\t\t\t=> 'Выполнение...',\n\t'sdb'\t\t\t=> 'Создание базы данных',\n\t'sc'\t\t\t=> 'Сохранение соединения',\n\t'sj'\t\t\t=> 'Сохранение задания',\n\t'so'\t\t\t=> 'Сохранение опций',\n\n\t// Сообщения\n\t'pro'\t\t\t=> 'Опция доступна только в Pro-версии',\n\t'err_fopen'\t\t=> 'Не удается открыть файл',\n\t'err_sxd2'\t\t=> 'Просмотр содержимого файла доступен только для файлов созданных Sypex Dumper 2',\n\t'err_empty_db'\t=> 'База данных пустая',\n\t'fdc'\t\t\t=> 'Вы действительно хотите удалить файл?',\n\t'ddc'\t\t\t=> 'Вы действительно хотите удалить базу данных?',\n\t'fic'\t\t\t=> 'Вы действительно хотите импортировать файл?',\n\n\t// Сокращения размеров файла\n\t'sizes'\t\t\t=> array('Б', 'КБ', 'МБ', 'ГБ'),\n)\n);\n?>\n"
  },
  {
    "path": "admin/sxd/lang/lng_uk.php",
    "content": "<?php\n// Language File for Sypex Dumper 2\n$LNG = array(\n\n// Информация о файле локализации\n'ver'\t\t\t\t=> 20005, // Для какой версии дампера предназначен файл\n'translated'\t\t=> 'dmytro (http://sypex.net/)', // Контакты переводчика\n'name'\t\t\t\t=> 'Українська', // Название языка\n\n// Панель инструментов\n'tbar_backup'\t\t=> 'Експорт',\n'tbar_restore'\t\t=> 'Імпорт',\n'tbar_files'\t\t=> 'Файли',\n'tbar_services'\t\t=> 'Сервіси',\n'tbar_options'\t\t=> 'Опції',\n'tbar_createdb'\t\t=> 'Створити БД',\n'tbar_connects'\t\t=> 'З’єднання',\n'tbar_exit'\t\t\t=> 'Вихід',\n\n// Названия объектов в дереве\n'obj_tables'\t\t=> 'Таблиці',\n'obj_views'\t\t\t=> 'Представлення',\n'obj_procs'\t\t\t=> 'Процедури',\n'obj_funcs'\t\t\t=> 'Функції',\n'obj_trigs'\t\t\t=> 'Тригери',\n'obj_events'\t\t=> 'Події',\n\n// Экспорт\n'zip_max'\t\t\t=> 'максимум',\n'zip_min'\t\t\t=> 'мінімум',\n'zip_none'\t\t\t=> 'Без стиснення',\n'default'\t\t\t=> 'стандартно',\n'combo_db'\t\t\t=> 'База даних:', \n'combo_charset'\t\t=> 'Кодування:',\n'combo_zip'\t\t\t=> 'Стиснення:',\n'combo_comments'\t=> 'Коментар:',\n'del_legend'\t\t=> 'Автовидалення, якщо:',\n'del_date'\t\t\t=> 'файлам більше %s днів',\n'del_count'\t\t\t=> 'кількість файлів понад %s',\n'tree'\t\t\t\t=> 'Виберіть об’єкти:',\n'no_saved'\t\t\t=> 'Немає збережених завдань',\n'btn_save'\t\t\t=> 'Зберегти',\n'btn_exec'\t\t\t=> 'Виконати',\n\n// Импорт\t\n'combo_file'\t\t=> 'Файл:',\n'combo_strategy'\t=> 'Стратегія відновлення:',\n'ext_legend'\t\t=> 'Додаткові опції:',\n'correct'\t\t\t=> 'Корекція кодування',\n'autoinc'\t\t\t=> 'Обнулити AUTO_INCREMENT',\n\n// Лог\n'status_current'\t=> 'Поточний статус:',\n'status_total'\t\t=> 'Загальний статус:',\n'time_elapsed'\t\t=> 'Пройшло:',\n'time_left'\t\t\t=> 'Залишилося:',\n'btn_stop'\t\t\t=> 'Перервати',\n'btn_pause'\t\t\t=> 'Пауза',\n'btn_resume'\t\t=> 'Продовжити',\n'btn_again'\t\t\t=> 'Повторити',\n'btn_clear'\t\t\t=> 'Очистити лог',\n\n// Файлы\n'btn_delete'\t\t=> 'Видалити',\n'btn_download'\t\t=> 'Завантажити',\n'btn_open'\t\t\t=> 'Відкрити',\n\n// Сервисы\n'opt_check'\t\t\t=> 'Опції для Перевірити:',\n'opt_repair'\t\t=> 'Опції для Полагодити:',\n'btn_delete_db'\t\t=> 'Видалити БД',\n'btn_check'\t\t\t=> 'Перевірити',\n'btn_repair'\t\t=> 'Полагодити',\n'btn_analyze'\t\t=> 'Аналізувати',\n'btn_optimize'\t\t=> 'Оптимізувати',\n\n// Опции\n'cfg_legend'\t\t=> 'Основні налаштування:',\n'cfg_time_web'\t\t=> 'Час виконання у web (сек.):',\n'cfg_time_cron'\t\t=> 'Час виконання у cron (сек.):',\n'cfg_backup_path'\t=> 'Шлях до каталогу backup:',\n'cfg_backup_url'\t=> 'URL до каталогу backup:',\n'cfg_globstat'\t\t=> 'Глобальна статистика:',\n'cfg_extended'\t\t=> 'Розширені налаштування:',\n'cfg_charsets'\t\t=> 'Фільтр для кодувань:',\n'cfg_only_create'\t=> 'Бекап лише структури:',\n'cfg_auth'\t\t\t=> 'Ланцюжок авторизації:',\n'cfg_confirm'\t\t=> 'Запитувати підтвердження для:',\n'cfg_conf_import'\t=> 'імпорту БД',\n'cfg_conf_file'\t\t=> 'видалення файлу',\n'cfg_conf_db'\t\t=> 'видалення БД',\n\n// Соединение\n'con_header'\t\t=> 'Параметри з’єднання',\n'connect'\t\t\t=> 'З’єднання',\n'my_host'\t\t\t=> 'Хост:',\n'my_port'\t\t\t=> 'Порт:',\n'my_user'\t\t\t=> 'Користувач:',\n'my_pass'\t\t\t=> 'Пароль:',\n'my_pass_hidden'\t=> 'Пароль не відображається',\n'my_comp'\t\t\t=> 'Протокол із стисненням',\n'my_db'\t\t\t\t=> 'Бази даних:',\n'btn_cancel'\t\t=> 'Відміна',\n\n// Сохранение задания\n'sj_header'\t\t\t=> 'Збереження завдання',\n'sj_job'\t\t\t=> 'Завдання',\n'sj_name'\t\t\t=> 'Ім’я (англ.):',\n'sj_title'\t\t\t=> 'Опис:',\n\n// Создание БД\n'cdb_header'\t\t=> 'Створення бази даних',\n'cdb_detail'\t\t=> 'Деталі',\n'cdb_name'\t\t\t=> 'Назва:',\n'combo_collate'\t\t=> 'Порівняння',\n'btn_create'\t\t=> 'Створити',\n\n// Авторизация\n'js_required'\t\t=> 'JavaScript має бути ввімкнено',\n'auth'\t\t\t\t=> 'Авторизація',\n'auth_user'\t\t\t=> 'Користувач:',\n'auth_remember'\t\t=> 'Запам’ятати',\n'btn_enter'\t\t\t=> 'Увійти',\n'btn_details'\t\t=> 'Деталі',\n\n// Сообщения в логе\n'not_found_rtl'\t\t=> 'Відсутній RTL-файл',\n'backup_begin'\t\t=> 'Початок експорту БД `%s`',\n'backup_TC'\t\t\t=> 'Експорт таблиці `%s`',\n'backup_VI'\t\t\t=> 'Експорт представлення `%s`',\n'backup_PR'\t\t\t=> 'Експорт процедури `%s`',\n'backup_FU'\t\t\t=> 'Експорт функції `%s`',\n'backup_EV'\t\t\t=> 'Експорт події `%s`',\n'backup_TR'\t\t\t=> 'Експорт тригера `%s`',\n'continue_from'\t\t=> 'з позиції %s',\n'backup_end'\t\t=> 'Резервна копія БД `%s` створена.',\n'autodelete'\t\t=> 'Автовидалення старих файлів:',\n'del_by_date'\t\t=> '- `%s` - видалений (по даті)',\n'del_by_count'\t\t=> '- `%s` - видалений (по даті)',\n'del_fail'\t\t\t=> '- `%s` - видалити не вдалося',\n'del_nothing'\t\t=> '- немає файлів для видалення',\n'set_names'\t\t\t=> 'Встановлено кодування з’єднання: `%s`',\n'restore_begin'\t\t=> 'Початок імпорту БД `%s`',\n'restore_TC'\t\t=> 'Імпорт таблиці `%s`',\n'restore_VI'\t\t=> 'Імпорт представлення `%s`',\n'restore_PR'\t\t=> 'Імпорт процедури `%s`',\n'restore_FU'\t\t=> 'Імпорт функції `%s`',\n'restore_EV'\t\t=> 'Імпорт події `%s`',\n'restore_TR'\t\t=> 'Імпорт тригера `%s`',\n'restore_keys'\t\t=> 'Включення індексів',\n'restore_end'\t\t=> 'БД `%s` відновлена з резервної копії.',\n'stop_1'\t\t\t=> 'Виконання перервано користувачем', \n'stop_2'\t\t\t=> 'Виконання зупинено користувачем',\n'stop_3'\t\t\t=> 'Виконання зупинено по таймеру',\n'stop_4'\t\t\t=> 'Виконання зупинено по таймауту',\n'stop_5'\t\t\t=> 'Виконання перервано із-за помилки',\n'job_done'\t\t\t=> 'Завдання успішно виконано',\n'file_size'\t\t\t=> 'Розмір файлу',\n'job_time'\t\t\t=> 'Часу витрачено',\n'seconds'\t\t\t=> 'сек.',\n'job_freeze'\t\t=> 'Процес не оновлювався більше 30 секунд. Натисніть Продовжити',\n'stop_job'\t\t\t=> 'Запит на зупинку',\n\n// Надписи в JS\n'js' => array(\n\n\t// Названия вкладок\n\t'backup'\t\t=> 'Експорт бази даних',\n\t'restore'\t\t=> 'Імпорт бази даних',\n\t'log'\t\t\t=> 'Лог дій',\n\t'result'\t\t=> 'Результат виконання',\n\t'files'\t\t\t=> 'Файли резервних копій',\n\t'services'\t\t=> 'Сервіси',\n\t'options'\t\t=> 'Опції',\n\n\t// Заголовки таблиц\n\t'dt'\t\t\t=> 'Дата і час',\n\t'action'\t\t=> 'Дія',\n\t'db'\t\t\t=> 'База даних',\n\t'type'\t\t\t=> 'Тип',\n\t'tab'\t\t\t=> 'Табл.',\n\t'records'\t\t=> 'Записів',\n\t'size'\t\t\t=> 'Розмір',\n\t'comment'\t\t=> 'Коментар',\n\n\t// Статусы\n\t'load'\t\t\t=> 'Завантаження',\n\t'run'\t\t\t=> 'Виконання...',\n\t'sdb'\t\t\t=> 'Створення бази даних',\n\t'sc'\t\t\t=> 'Збереження з’єднання',\n\t'sj'\t\t\t=> 'Збереження завдання',\n\t'so'\t\t\t=> 'Збереження опцій',\n\n\t// Сообщения\n\t'pro'\t\t\t=> 'Опція доступна лише у версії Pro',\n\t'err_fopen'\t\t=> 'Не вдається відкрити файл',\n\t'err_sxd2'\t\t=> 'Перегляд вмісту файлу доступний лише для файлів створених Sypex Dumper 2',\n\t'err_empty_db'\t=> 'База даних порожня',\n\t'fdc'\t\t\t=> 'Ви дійсно бажаєте видалити файл?',\n\t'ddc'\t\t\t=> 'Ви дійсно бажаєте видалити базу даних?',\n\t'fic'\t\t\t=> 'Ви дійсно бажаєте імпортувати файл?',\n\n\t// Сокращения размеров файла\n\t'sizes'\t\t\t=> array('Б', 'КБ', 'МБ', 'ГБ'),\n)\n);\n?>\n"
  },
  {
    "path": "admin/sxd/lang/update.php",
    "content": "<?php\n// Sypex Dumper 2 langfile updater\n$path = './';\n$langlist = array();\nif (false !== ($handle = opendir($path))) {\n    while (false !== ($file = readdir($handle))) {\n        if (preg_match(\"/^lng_([a-z]{2}(-[a-z]{2})?)\\.php$/\", $file, $m)) {\n            include($path . $file);\n            $langlist[$m[1]] = $LNG['name'];\n        }\n    }\n    closedir($handle);\n    if(count($langlist) > 0){\n    \t$fp = fopen('list.php', 'w');\n    \tfwrite($fp, \"<?php\\n\\$langs = \" . var_export($langlist, 1) . \";\\n?>\");\n\t}\n}\necho 'Langlist updated';\n?>\n"
  },
  {
    "path": "admin/sxd/load.php",
    "content": "<?php\nerror_reporting(0);\nif(!empty($_SERVER['QUERY_STRING']) && preg_match(\"/^(\\w+)(\\.v\\d+)?(pro)?\\.(lng\\.js|js|css|gif|png|ico)$/\", $_SERVER['QUERY_STRING'], $m)){\n\t$compress = true;\n\t$skin = '';\n\t$file = $skin;\n\theader('Expires: ' . gmdate('D, d M Y H:i:s', time() + 1209600) . ' GMT');\n\theader('Cache-Control: max-age=1209600, public');\n\tswitch($m[4]) {\n\t\tcase 'css': $type = 'text/css; charset=UTF-8'; break;\n\t\tcase 'js':  $type = 'application/x-javascript; charset=UTF-8'; break;\n\t\tcase 'lng.js': \n\t\t\theader('Content-Type: application/x-javascript; charset=UTF-8');\n\t\t\t//if(!ini_get('zlib.output_compression') && function_exists('ob_gzhandler')) ob_start('ob_gzhandler');\n\t\t\tinclude(\"lang/lng_{$m[1]}.php\");\n\t\t\techo 'sxdlng = ' . sxd_php2json($LNG['js']) . ';';\n\t\t\texit;\n\t\tcase 'png': $file = 'img/'; $type = 'image/png';$compress = false; break;\n\t\tcase 'gif': $file = 'img/'; $type = 'image/gif';$compress = false; break;\n\t\tcase 'ico': $file = ''; $type = 'image/x-icon';$compress = false; break;\n\t}\t\n\t$file .= $m[1] . '.' . $m[4];\n\tif(is_file($file)){\n\t\t//if($compress) if(!ini_get('zlib.output_compression') && function_exists('ob_gzhandler')) ob_start('ob_gzhandler');\n\t\theader('Content-Type: ' . $type);\n\t\treadfile($file);exit;\n\t}\n}\nfunction sxd_php2json($obj){\n\tif(count($obj) == 0) return '[]';\n\t$is_obj = isset($obj[count($obj) - 1]) ? false : true;\n\t$str = $is_obj ? '{' : '[';\n    foreach ($obj AS $key  => $value) {\n    \t$str .= $is_obj ? \"'\" . addcslashes($key, \"\\n\\r\\t'\\\\/\") . \"'\" . ':' : ''; \n        if     (is_array($value))   $str .= sxd_php2json($value);\n        elseif (is_null($value))    $str .= 'null';\n        elseif (is_bool($value))    $str .= $value ? 'true' : 'false';\n\t\telseif (is_numeric($value)) $str .= $value;\n\t\telse                        $str .= \"'\" . addcslashes($value, \"\\n\\r\\t'\\\\/\") . \"'\";\n\t\t$str .= ',';\n    }\n\treturn  substr_replace($str, $is_obj ? '}' : ']', -1);\n}\n"
  },
  {
    "path": "admin/sxd/readme_en.txt",
    "content": "Installing\n\n1.Unzip it and upload the contents of the directory to the server.\n2.Set chmod 777 for backup directory.\n3.Set chmod 666 for files cfg.php and ses.php.\n\nUsing\n\n1.Open an URL http://domain.com/sxd/ in browser.\n2.Enter the username and password for your database.\nPlease report problems and bugs to the forum, indicating your versions of browser, php and mysql.\n"
  },
  {
    "path": "admin/sxd/readme_ru.txt",
    "content": " Sypex Dumper 2:\n\n      .\n chmod 777   backup\n chmod 666  777   cfg.php  ses.php\n\n   url  http://mysite.net/sxd/,      mysql.\n\n       http://sypex.net/forum/    , php  mysql"
  },
  {
    "path": "admin/sxd/sxd.css",
    "content": "html{height:100%;overflow:auto;margin:0;}body{min-height:100%;position:relative;overflow:hidden;background-color:#f0f0f0;margin:0;}* html body{height:100%;}td{border:0;padding:0;}td,input,select,textarea,div{font:11px tahoma,verdana;color:#000;}fieldset{padding:3px;}form{display:inline;margin:0;}#overlay{display:none;position:absolute;top:0;left:0;background-color:#000;z-index:100;width:100%;height:100%;opacity:.2;filter:progid:DXImageTransform.Microsoft.Alpha(opacity=20);}legend{color:#215a91;padding:0 4px;}#main_div{position:absolute;left:50%;top:50%;width:580px;border:1px solid #919B9C;text-align:left;cursor:default;margin:-231px 0 0 -292px;padding:1px;}#header{font:13px verdana,arial;color:#fff;height:100%;font-weight:700;margin-bottom:1px;background-color:#306191;padding:3px 5px;}#name{font-size:12px;font-weight:700;background-color:#fff;border-bottom:1px solid #ccc9b8;padding:6px 5px;}#loading{float:right;padding-right:20px;height:16px;line-height:16px;font-size:10px;color:#777;background:url(load.php?load.gif) no-repeat right;visibility:hidden;}.content{height:100%;background-color:#fcfbfe;filter:progid:DXImageTransform.Microsoft.Gradient(gradientType=0,startColorStr=#FCFBFE,endColorStr=#F4F3EE);padding-top:2px;}.content td{padding:0 6px 6px;}input.text{border:1px solid #7f9db9;width:200px;}input.txt{width:100%;height:14px;border:1px solid;border-color:#abadb3 #dbdfe6 #e3e9ef #e2e3ea;margin:0;}input.txt2{height:20px;width:100%;border-width:0;padding:0;}textarea{border:0;width:100%;height:42px;overflow:auto;margin:0;}.border{border:1px solid;overflow:hidden;background-color:#fff;border-color:#abadb3 #dbdfe6 #e3e9ef #e2e3ea;}.dialog{display:none;z-index:1000;background-color:#f0f0f0;border:1px solid #919B9C;width:321px;position:absolute;top:50%;left:50%;margin:-83px 0 0 -160px;padding:1px;}.dialog .header{font:12px verdana,arial;color:#fff;height:100%;font-weight:700;margin-bottom:1px;background-color:#306191;padding:3px 5px;}#dia_auth{display:block;}.header a{text-decoration:none;color:#fff;}.dialog td{padding:0;}.i202{width:192px;}.zTree{overflow:auto;border:1px solid #828790;background-color:#fff;-moz-user-select:none;-khtml-user-select:none;user-select:none;cursor:default;width:312px;height:320px;}.zTree ul{margin:3px;padding:0;}.zTree .close ul{display:none;}.zTree li > ul{list-style-type:none;background:url(data:image/gif;base64,R0lGODlhAgACAIAAAB4dGf///yH5BAEAAAEALAAAAAACAAIAAAICRF4AOw==) 5px 0 repeat-y;padding-left:16px;margin:0;}.zTree li.dl > ul{background:none;}.zTree li{font:11px Tahoma;line-height:18px;margin:0;padding:0;}.zTree li > div{background:url(load.php?tree.png) no-repeat 0 18px;height:18px;white-space:nowrap;}.zTree a{text-decoration:none;color:#000;padding-right:3px;white-space:nowrap;cursor:default;min-height:16px;}.zTree a:hover{background-color:#eff8fd;}.zTree span{font:10px Tahoma;color:#bbb;margin-left:4px;}.zTree a:hover span{color:#555;}.zTree i{display:inline-block;vertical-align:middle;background:url(load.php?tree.png) no-repeat 0 18px;width:18px;height:18px;}.zTree a i{display:inline-block;vertical-align:middle;background:url(load.php?icons.png) no-repeat 16px 0;width:16px;height:16px;}.zTree .error{background-color:#f7f7f7;height:100%;}.zTree .error div{color:#777;text-align:center;padding-top:50%;display:block;}.zTBar{height:28px;padding-left:4px;cursor:default;-moz-user-select:none;-khtml-user-select:none;user-select:none;background:url(load.php?tb_bg.png) repeat-x;overflow:hidden;}.zTBar div{font:11px Tahoma;float:left;height:22px;background:url(load.php?tb_bg.png) repeat-x 0 26px;}.zTBar .lb,.zTBar .rb,.zTBar .mb{width:3px;}.zTBar .txt{height:16px;background:url(load.php?icons.png) no-repeat 0 0;white-space:nowrap;margin:4px 1px;padding:0 2px 0 22px;}.zTBar .spl{background-position:0 -50px;width:3px;margin:3px 2px;}.zTBar .more{width:7px;background-position:0 -160px;margin:0 2px;}.zTBar .ico{height:16px;width:16px;background:url(load.php?icons.png) no-repeat 0 -16px;margin:3px 1px;}.zSel{height:20px;background:url(load.php?sel_bg.png) repeat-x 0 0;-moz-user-select:none;-khtml-user-select:none;user-select:none;overflow:hidden;}.zSel div{cursor:default;float:left;height:20px;width:2px;background:url(load.php?sel_bg.png) repeat-x 0 0;}.zSel .arr{float:right;width:15px;}.zSel .txt{height:16px;line-height:14px;background:url(load.php?icons.png) no-repeat 0 -16px;white-space:nowrap;margin:2px 1px;padding:0 2px 0 22px;}.zSel .more{width:7px;background-position:0 -140px;margin:0 2px;}.zSelMenu{border:1px solid;background-color:red;border-color:#83868d #c6c7d2 #d2dbe5;}.zSelMenu div div{line-height:14px;padding:0 3px;}.zSelMenu i{font-size:11px;font-style:normal;color:#555;}.zSelMenu .ovr{background-color:#39f;color:#fff;}#sxdMenu{position:absolute;top:0;left:0;width:240px;height:100px;border:1px solid #828790;background-color:#fff;overflow:auto;cursor:default;display:none;}.zGrid table{table-layout:fixed;}.zGrid{border:1px solid #828790;overflow:hidden;margin-top:4px;background-color:#fff;cursor:default;padding:1px;}.zGrid th,.zGrid td{font-weight:400;text-align:left;text-overflow:ellipsis;white-space:nowrap;vertical-align:top;overflow:hidden;padding:2px 3px;}.zGrid td.wrap{white-space:normal;}.zGrid .header{overflow:hidden;background:url(load.php?log_bg.png) repeat-x;}.zGrid th{border:1px solid;border-color:#fff #e3e4e6 #d5d5d5 #fcfdfd;padding:3px 3px 4px;}.zGrid td{border:1px solid transparent;}.zGrid tr:hover td{background-color:#eff8fd;border-color:#eff8fd;}.zGrid tr.sel td{background-color:#d7effc;border-color:#d7effc;}.zProc{border:1px solid;float:left;height:14px;overflow:hidden;width:300px;background-color:#fff;}.zProc.green{border-color:#55994d;}.zProc.blue{border-color:#5170ab;}.zProc div{height:14px;}.zProc .top{position:inherit;margin-top:-14px;top:-14px;z-index:100;width:0;display:block;overflow:hidden;background:url(load.php?log_bg.png) repeat-x 0 22px;}.zProc.green .top{background-position:0 -22px;border-right:1px solid #55994d;}.zProc.blue .top{background-position:0 -36px;border-right:1px solid #5170ab;}.zProc .txt{width:300px;text-align:center;font-weight:700;}.zProc .bot .txt{color:#333;}.zProc .top .txt{color:#fff;}.progress td{padding:1px;}table,.i100{width:100%;}.caption,.zTBar .btn{margin:3px 0;}"
  },
  {
    "path": "admin/sxd/sxd.js",
    "content": "function z(el){return document.getElementById(el);}\nvar sxd={version:{num:20007,type:'lite',full:''},init:function(){var icons={' .lb':'0 -80px',' .rb':'-2px -80px',' .mb':'-4px -80px','.ovr':'0 -20px','.ovr .lb':'0 -100px','.ovr .rb':'-2px -100px','.ovr .mb':'-4px -100px','.ovr .arr':'0 -40px','.dwn':'0 -20px','.dwn .lb':'0 -100px','.dwn .rb':'-2px -120px','.dwn .mb':'-4px -120px','.dwn .arr':'0 -60px'};for(var ico in icons){this.css.add('.zSel'+ico+' {background-position:'+icons[ico]+';}');}\nthis.version.full=z('header').innerHTML;this.main=z('main_div');this.menu.el=z('sxdMenu');this.name=z('name').lastChild;this.loading=z('loading');this.overlay=z('overlay');this.progress.current=zProgressObject();this.progress.current.init('sxdProc1',1);this.progress.total=zProgressObject();this.progress.total.init('sxdProc2',2);this.tbar=zToolbarObject();this.tree.backup=zTreeObject();this.tree.backup.init('backup_tree',1);this.tree.restore=zTreeObject();this.tree.restore.init('restore_tree',2);this.tree.services=zTreeObject();this.tree.services.init('services_tree',3);this.comment.backup=z('backup_comment');this.comment.restore=z('restore_comment');this.timer.t1=z('sxdTime1');this.timer.t2=z('sxdTime2');this.timeout=null;this.log=zGridObject();this.log.init('sxdGrid1',{header:[[this.lng('dt'),120],[this.lng('action')]],width:565,height:286});this.files=zGridObject();this.files.init('sxdGrid2',{header:[[this.lng('db'),90],[this.lng('dt'),90],[this.lng('type'),30],[this.lng('tab'),30,1,this.lng('table')],[this.lng('records'),60,1],[this.lng('size'),45,1],[this.lng('comment')]],width:565,height:334});this.result=zGridObject();this.result.init('sxdGrid3',{header:[['Table',160],['Op',70],['Msg_type',50],['Msg_text']],width:565,height:334});this.addDialogs(['connect','createdb','savejob','charsets']);this.addTabs(['backup','restore','log','result','files','services','options']);},job:{name:'abcdefgh',stop:0,log_seek:0,type:''},menu:{el:null,type:'zSelMenu'},tabs:{},openTab:function(name){if(this.opened)this.tabs[this.opened].el.style.display='none';this.opened=name;this.name.innerHTML=this.tabs[name].name;this.tabs[name].el.style.display='';document.title=sxd.version.full;},addTabs:function(o){for(var i=0,l=o.length;i<l;i++){this.tabs[o[i]]={name:this.lng(o[i]),el:z('tab_'+o[i])};}\nthis.openTab(o[0]);},dialogs:{},addDialogs:function(o){for(var i=0,l=o.length;i<l;i++){this.dialogs[o[i]]=z('dia_'+o[i]);}},tbar:{},tree:{},options:{},progress:{},combos:{},comment:{},lng:function(s){return sxdlng[s]?sxdlng[s]:'[LNG: '+s+']';},timer:{set:function(time1,time2,proc){function lead0(i){return i<10?'0'+i:i;}\nfunction sec2time(sec){return lead0(Math.floor(sec/60))+':'+lead0(sec%60);}\nif(!time1&&!time2)this.t1.innerHTML='00:00',this.t2.innerHTML='00:00';else{var t=time2-time1;this.t1.innerHTML=sec2time(t);this.t2.innerHTML=sec2time(t>0&&proc>0?Math.round(t/(proc/100)-t):0);}}},actions:{db:function(){if(!this.value)return;if(this.name=='backup_db'||this.name=='services_db')sxd.ajax('index',sxd.lng('load'),{act:'load_db',name:this.name,value:this.value});},dblist:function(){sxd.ajax('index',sxd.lng('load'),{act:'dblist'});},filelist:function(){sxd.ajax('index',sxd.lng('load'),{act:'filelist'});},files:function(){if(!this.value)return;sxd.ajax('index',sxd.lng('load'),{act:'load_files',name:this.name,value:this.value});},tab_connects:function(){sxd.ajax('index',sxd.lng('load'),{act:'load_connect'});sxd.showDialog('connect');},tab_createdb:function(){sxd.showDialog('createdb');},tab_backup:function(){sxd.openTab('backup');if(sxd.tree.backup.load)sxd.combos.backup_db.action();},tab_restore:function(){sxd.openTab('restore');if(sxd.tree.restore.load)sxd.combos.restore_file.action();},tab_files:function(){sxd.ajax('index',sxd.lng('load'),{act:'load_files_ext'});sxd.openTab('files');},tab_services:function(){sxd.openTab('services');if(sxd.tree.services.load)sxd.combos.services_db.action();},tab_options:function(){sxd.ajax('index',sxd.lng('load'),{act:'load_options'});sxd.openTab('options');},tab_exit:function(){sxd.ajax('index',sxd.lng('load'),{act:'exit'});}},informer:function(){if(sxd.job.timer)clearTimeout(sxd.job.timer);if(sxd.job.stop)return;sxd.job.timer=setTimeout(function(){sxd.ajax('info',sxd.lng('run'),{job:sxd.job.name,act:'info',lseek:sxd.job.log_seek},1,function(){sxd.informer();});},250);},addDb:function(){sxd.ajax('index',this.lng('sdb'),{act:'add_db',name:z('db_name').value,charset:this.combos.db_charset.value,collate:this.combos.db_charset_col.value});sxd.hideDialog('createdb');},saveConnect:function(){sxd.ajax('index',this.lng('sc'),{act:'save_connect',host:z('con_host').value,port:z('con_port').value,user:z('con_user').value,pass:z('con_pass').changed?z('con_pass').value:null,comp:z('con_comp').checked?'1':'0',db:z('con_db').value});sxd.hideDialog('connect');z('con_pass').value='';},job2php:function(){return sxd.opened=='restore'||sxd.job.type=='restore'?{act:'restore',type:'restore',db:this.combos.restore_db.value,charset:this.combos.restore_charset.value,file:this.combos.restore_file.value,strategy:this.combos.restore_type.value,correct:z('correct').checked?1:0,autoinc:z('autoinc').checked?1:0,obj:this.tree.restore.save()}:{act:'backup',type:'backup',db:this.combos.backup_db.value,charset:this.combos.backup_charset.value,zip:this.combos.backup_zip.value,comment:this.comment.backup.value,del_time:z('del_time').value,del_count:z('del_count').value,obj:this.tree.backup.save()};},saveJob:function(){var t=this.job2php();t.job=z('sj_name').value,t.act='save_job',t.title=z('sj_title').value;sxd.ajax('index',this.lng('sj'),t);sxd.hideDialog('savejob');},runSavedJob:function(type,job){if(job==0)return;this.clearLogTab();sxd.job.name=job;sxd.job.type=type;sxd.job.act='run_savedjob';sxd.ajax('index',this.lng('run'),{act:'run_savedjob',type:type,job:job},1);this.openTab('log');z('btn_resume').style.display='none';z('btn_pause').style.display='';z('btn_stop').disabled=false;z('btn_pause').disabled=false;z('btn_again').disabled=true;sxd.job.stop=0;sxd.informer();},saveOptions:function(){sxd.confirms=z('conf_import').checked*1+z('conf_file').checked*2+z('conf_db').checked*4;sxd.ajax('index',this.lng('so'),{act:'save_options',time_web:z('time_web').value*1,time_cron:z('time_cron').value*1,backup_path:z('backup_path').value,backup_url:z('backup_url').value,globstat:z('globstat').checked?'1':'0',charsets:z('charsets').value,only_create:z('only_create').value,auth:z('auth').value,confirm:sxd.confirms});sxd.hideDialog('connect');z('con_pass').value='';},clearLogTab:function(){this.log.clear();this.timer.set(0);this.job.log_seek=0;this.progress.current.set(0);this.progress.total.set(0);},runBackup:function(){this.clearLogTab();z('btn_resume').style.display='none';z('btn_pause').style.display='';z('btn_down').style.display='none';z('btn_stop').disabled=false;z('btn_pause').disabled=false;z('btn_again').disabled=true;sxd.job.name=this.newJob();sxd.job.type='backup';var t=this.job2php();this.openTab('log');t.type='run',t.job=sxd.job.name;this.ajax('index',this.lng('run'),t,1);sxd.job.stop=0;sxd.informer();},stopJob:function(){z('btn_again').disabled=false;z('btn_stop').disabled=true;z('btn_pause').disabled=true;sxd.ajax('info',this.lng('run'),{job:sxd.job.name,act:'stop'},1);},pauseJob:function(){z('btn_again').disabled=false;z('btn_stop').disabled=true;z('btn_pause').disabled=true;sxd.ajax('info',this.lng('run'),{job:sxd.job.name,act:'pause'},1);z('btn_resume').style.display='';z('btn_pause').style.display='none';},resumeJob:function(){z('btn_stop').disabled=false;z('btn_pause').disabled=false;z('btn_again').disabled=true;sxd.ajax('index',this.lng('run'),{job:sxd.job.name,act:'resume'},1);z('btn_resume').style.display='none';z('btn_pause').style.display='';sxd.job.stop=0;sxd.informer();},runAgain:function(){if(sxd.job.act=='run_savedjob'){sxd.runSavedJob(sxd.job.type,sxd.job.name);}\nelse if(sxd.job.type=='backup'){sxd.runBackup();}\nelse if(sxd.job.type=='restore'){sxd.runRestore();}},runRestore:function(){if(sxd.confirms&1&&!confirm(sxd.lng('fic')))return;this.clearLogTab();z('btn_resume').style.display='none';z('btn_pause').style.display='';z('btn_down').style.display='none';z('btn_stop').disabled=false;z('btn_pause').disabled=false;z('btn_again').disabled=true;sxd.job.name=this.newJob();sxd.job.type='restore';var t=this.job2php();this.openTab('log');t.type='run',t.job=sxd.job.name;this.ajax('index',this.lng('run'),t,1);sxd.job.stop=0;sxd.informer();},runServices:function(type){switch(type){case'delete':if(sxd.confirms&4&&!confirm(sxd.lng('ddc')))return;sxd.ajax('index',sxd.lng('run'),{act:'delete_db',name:this.combos.services_db.value});return;}\nthis.result.clear();this.openTab('result');this.ajax('index',this.lng('run'),{act:'services',type:type,db:this.combos.services_db.value,check:this.combos.services_check.value,repair:this.combos.services_repair.value,obj:this.tree.services.save()});},runFiles:function(type,file){file=file||this.files.selected.file;switch(type){case'open':sxd.combos.restore_file.select(file);sxd.openTab('restore');break;case'download':location.href=sxd.backupUrl+file;break;case'delete':if(!file)return;if(sxd.confirms&2&&!confirm(sxd.lng('fdc')))return;sxd.ajax('index',sxd.lng('run'),{act:'delete_file',name:file});sxd.actions.filelist();break;}},ajax:function(url,txt,req,nothide,onload){function obj2php(obj,depth){var s='';depth=depth||'';for(var o in obj){s+=typeof obj[o]=='object'?obj2php(obj[o],depth+'['+o+']'):'ajax'+depth+'['+o+']='+encodeURIComponent(obj[o])+'&';}\nreturn s;}\nthis.showLoading(txt);url+='.php';var params=obj2php(req);var xhr=window.XMLHttpRequest?new XMLHttpRequest():(window.ActiveXObject?new ActiveXObject('Microsoft.XMLHTTP'):null);xhr.open('POST',url,true);xhr.setRequestHeader('Content-Type','application/x-www-form-urlencoded');var t=this;function oncomplete(){if(xhr&&xhr.readyState==4){if(xhr.status==200){eval(xhr.responseText);}\nelse{alert(xhr.statusText);}\nif(onload)onload();xhr=null;clearInterval(iv);iv=null;if(!nothide)t.hideLoading();}}\nvar iv=setInterval(oncomplete,33);xhr.send(params);},css:{add:function(rule,index){var s=document.styleSheets[0];if(!index)index=s.cssRules?s.cssRules.length:s.rules.length;if(s.addRule){if(/^([^{]+)\\{(.*)\\}\\s*$/.test(rule)){s.addRule(RegExp.$1,RegExp.$2,index);}}\nelse{s.insertRule(rule,index);}},swap:function(elem,classes,sel){var tmp=elem.className.split(/\\s+/);for(var t in tmp){for(var c in classes){if(tmp[t]==classes[c]){tmp[t]=classes[sel];break;}}}\nelem.className=tmp.join(' ');}},showDialog:function(name){this.overlay.style.display='block';var d=this.dialogs[name];d.style.display='block';d.style.marginTop='-'+Math.round(d.clientHeight/2-2)+'px';d.getElementsByTagName('input')[0].focus();},hideDialog:function(name){this.dialogs[name].style.display='none';this.overlay.style.display='none';},addCombo:function(name,value,ico,opts){this.combos[name]={ico:ico||0,el:z(name)};var c=this.combos[name];var t;if(typeof opts=='object'){opts=this.addOpt(opts);}\nelse if(t=/^(\\w+):(\\w+)$/.exec(opts)){var tmp={};if(this.combos[t[2]].options.length>0)tmp[opts]=this.combos[t[2]].options[this.combos[t[2]].sel].opts;else tmp[opts]=[];value=this.addOpt(tmp,1);this.combos[t[2]].child=c;}\nelse{var t={};t[opts]={};opts=this.addOpt(t);}\nc.options=this.options[opts];c.optName=opts;c.name=name;c.parent=this;c.el.className='zSel';c.el.innerHTML='<div class=\"lb\"></div><div class=\"txt\" style=\"background-position:-16px '+((c.ico-1)*-16)+'px;\"></div><div class=\"arr\"><div class=\"mb\"></div><div class=\"more\"></div><div class=\"rb\"></div></div>';c.action=function(){};c.select=function(value,wheel){this.sel*=1;if(this.options==undefined)return false;if(value=='+'){this.sel+=this.sel<this.options.length-1?1:0;}\nelse if(value=='-'){this.sel-=this.sel>0?1:0;}\nelse{this.sel=0;for(var i in this.options)if(this.options[i].value==value){this.sel=i;break;}}\nif(wheel){this.parent.menu.el.style.display='none';document.body.onclick=null;}\nthis.value=this.options[this.sel]?this.options[this.sel].value:0;this.text=this.options[this.sel]?this.options[this.sel].text:'';this.el.childNodes[1].innerHTML=this.text;if(this.child){this.parent.clearOpt(this.child.optName);var tmp={};tmp[this.child.optName]=this.options[this.sel].opts;this.child.select(this.parent.addOpt(tmp,1),0);}\nvar _this=this;if(wheel){clearTimeout(sxd.timeout);sxd.timeout=setTimeout(function(){_this.action();},333);}\nelse this.action();};c.select(value,0);c.action=this.actions[opts]||function(){};c.show=function(){if(!c.el.disabled)_this.showMenu(c,c.options,{});};var _this=this;c.el.onmouseover=function(e){e=e||window.event;if(!c.el.disabled)c.el.className='zSel ovr';};c.el.onmousewheel=function(e){e=e||window.event;var s=e.wheelDelta>0?'-':'+';if(!c.el.disabled)c.select(s,1);};if(c.el.addEventListener)c.el.addEventListener('DOMMouseScroll',function(e){e.wheelDelta=-e.detail;c.el.onmousewheel(e);},false);c.el.onmouseout=function(e){c.el.className='zSel';};c.el.onmousedown=function(e){e=e||window.event;if(!c.el.disabled){if(_this.menu.el.style.display!='none'&&_this.menu.el.obj.name==c.name){_this.menu.el.style.display='none';c.el.className='zSel ovr';}\nelse{c.el.className='zSel dwn';c.show();}}\nif(e.stopPropagation)e.stopPropagation();e.cancelBubble=true;};},addOpt:function(options,txt){txt=txt||0;var def='';for(var o in options){if(!this.options[o]){this.options[o]=[];}\nfor(var i in options[o]){if(typeof options[o][i]=='object')\nthis.options[o].push({text:i,value:i*1==i?i*1:i,opts:options[o][i]});else{if(txt&&options[o][i])def=i;this.options[o].push({text:txt?i:options[o][i],value:i*1==i?i*1:i});}}\nthis.options[o]=this.options[o].sort(this.keyNatSort);}\nreturn txt?def:o;},clearOpt:function(name){if(name){if(this.options[name])this.options[name].length=0;}\nelse for(var o in this.options)this.options[o].length=0;},showMenu:function(obj,opts,cfg){var s='',m=this.menu.el;m.className='zSelMenu';m.innerHTML='<div style=\"width:100%;\"></div>';for(var o in opts){s=document.createElement('DIV');s.innerHTML=opts[o].text;s.title=s.firstChild.nodeValue||'';if(o==obj.sel){s.className='ovr';m.over=s;}\ns.value=opts[o].value;s.onmouseover=function(){if(m.over)m.over.className='';this.className='ovr';m.over=this;};s.onmousedown=function(){if(cfg.runjob)sxd.runSavedJob(m.obj.com,this.value);else if(cfg.btn)sxd.runServices(this.value);else m.obj.select(this.value,0);m.style.display='none';};m.firstChild.appendChild(s);}\nm.style.display='block';var pos=this.offset(obj.el);m.style.width=(cfg.width?cfg.width:obj.el.offsetWidth-4)+'px';var height=(m.firstChild.offsetHeight<260?m.firstChild.offsetHeight:260);m.style.height=height+'px';m.style.top=pos.top-(cfg.btn?height+obj.el.offsetHeight+2:0)+'px';m.style.left=pos.left+'px';m.scrollTop=m.over?m.over.offsetTop:0;m.obj=obj;m.onmousedown=function(e){e=e||window.event;if(e.stopPropagation)e.stopPropagation();e.cancelBubble=true;};document.body.onmousedown=function(){m.style.display='none';document.body.onclick=null;};},offset:function(el){var top=el.offsetTop+el.offsetHeight+1,left=el.offsetLeft+2;while((el=el.offsetParent))top+=el.offsetTop-el.scrollTop,left+=el.offsetLeft-el.scrollLeft;return{top:top,left:left};},keyNatSort:function(a,b){var aa,bb;a=a.value,b=b.value;while((aa=/^(\\D+|(\\d+))(.*?)$/.exec(a))&&(bb=/^(\\D+|(\\d+))(.*?)$/.exec(b))){if(aa[2]&&bb[2]&&aa[2]!=bb[2])return aa[2]*1>bb[2]*1?1:-1;else if(aa[1]&&bb[1]&&aa[1]!=bb[1])return aa[1]>bb[1]?1:-1;else if(aa[3]&&bb[3]&&aa[3]!=bb[3])a=aa[3],b=bb[3];else return 0;}},formatSize:function(size,add){add=add||0;if(size>=0){var i=0;while(size>999&&(size/=1024))i++;size=(i>0?size.toPrecision(3):size)+' '+sxdlng.sizes[i];if(add==2)return size;else if(add==1)return' <span>[ '+size+' ]</span>';return'[ '+size+' ]';}\nreturn'';},showLoading:function(txt){this.loading.innerHTML=txt;this.loading.style.visibility='visible';},hideLoading:function(){this.loading.style.visibility='hidden';sxd.job.stop=1;z('btn_stop').disabled=true;z('btn_pause').disabled=true;z('btn_again').disabled=false;},newJob:function(){var key='qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM1234567890';var name='';for(var i=0;i<8;i++)name+=key.charAt(Math.round(Math.random()*61));return name;}};function zTreeObject(){return{init:function(elem,num){this.el=z(elem);this.name=elem;this.prefix='zti_'+num+'_';this.id='#'+elem;this.index=[];this.data=[];this.load=true;this.err=false;var _this=this;this.el.className='zTree';this.el.onselectstart=function(){return false;};this.el.onclick=function(e){e=e||window.event;var t=e.target||e.srcElement;switch(t.tagName.toUpperCase()){case'I':if(t.className=='pm'){_this.openBranch(_this.getId(t));}\nelse if(t.className.match(/^cb[0124]$/)){_this.clickBranch(_this.getId(t));}\nbreak;case'A':_this.clickBranch(_this.getId(t));if(e.stopPropagation)e.stopPropagation();e.cancelBubble=true;break;}\nif(t.parentNode.tagName.toUpperCase()=='A'){_this.clickBranch(_this.getId(t));}\nreturn false;};this.el.onmousedown=function(e){e=e||window.event;var t=e.target||e.srcElement;if(t.tagName.toUpperCase()=='A'||t.parentNode.tagName.toUpperCase()=='A'){return false;}};var treeIcons=['li.df > div','li.dt > div','li.dl > div','.close .pm','.pm','.cb0','.cb1','.cb2','.cb3','.cb4'];for(var ico in treeIcons){sxd.css.add('.zTree '+treeIcons[ico]+' {background-position: 0 -'+(ico*18)+'px;}');}\nvar Icons=['.cTA','.cVI','.cPR','.cFU','.cTR','.cEV','.iTA','.iVI','.iPR','.iFU','.iTR','.iEV'];for(ico in Icons){sxd.css.add('.zTree '+Icons[ico]+' {background-position: 0 -'+(ico*16)+'px;}');}},getId:function(o){while(!o.parentNode.id&&(o=o.parentNode)){}\nreturn o.parentNode.id.replace(this.prefix,'')*1;},openBranch:function(id){var o=this.index[id];o.open=(o.open+1)%2;sxd.css.swap(o.el,['close','open'],o.open);},clickBranch:function(id){if(sxd.version.type=='lite'&&this.name=='restore_tree'){alert(sxd.lng('pro'));return false;}\nvar o=this.index[id];if(o.checked==3){return false;}\nthis.setCheckBox(id,o.checked==4?1:(o.checked+1)%(o.type.match(/^.TA$/)?3:2));this.changeChilds(o);this.checkParents(o);},setCheckBox:function(id,check){check=(this.index[id].typen==2&&check==1&&this.index[id].size==undefined)?2:check;this.index[id].checked=check;this.index[id].el.firstChild.childNodes[1].className='cb'+check;},changeChilds:function(o){if(o.childs){for(var c in o.childs){this.setCheckBox(o.childs[c].id,o.checked);this.changeChilds(o.childs[c]);}}},checkParents:function(o){var check;while(o.pid>0){o=this.index[o.pid];check=-1;for(var c in o.childs){if(check>=0&&check!=o.childs[c].checked){check=4;break;}\ncheck=o.childs[c].checked;}\nthis.setCheckBox(o.id,check);}\nif(o.id==1){this.recountSizes(o);}},drawTree:function(d){this.load=this.err=false;if(typeof(d)!='object')return false;var index=[''],data=[];var types=['','cTA','iTA','cVI','iVI','cPR','iPR','cFU','iFU','cTR','iTR','cEV','iEV'];for(var k=0,l=d.length;k<l;k++){o={};o.id=d[k][0];o.pid=d[k][1];o.parent=index[o.pid];o.name=d[k][2];o.type=types[d[k][3]];o.typen=d[k][3];o.checked=d[k][4]||0;if(d[k][3]<3)o.size=d[k][3]==2?d[k][5]:0;if(d[k][3]&1)o.childs=[],o.open=d[k][5]||0;index[o.id]=o;if(o.pid>0)index[o.pid].childs.push(o);else data.push(o);}\nthis.lastId=0;this.el.innerHTML=this.drawChilds(data,0);for(k=1,l=index.length;k<l;k++){if(!index[k])continue;o=z(this.prefix+index[k].id);index[k].el=o;}\nthis.data=data;this.index=index;},recountSizes:function(o){if(!o.childs){return 0;}\nvar size=0;for(var k in o.childs){size+=this.recountSizes(o.childs[k]);if(o.childs[k].type=='iTA'&&o.childs[k].checked==1){size+=o.childs[k].size||0;}}\no.el.getElementsByTagName('span')[0].innerHTML=sxd.formatSize(size,0);return size;},drawChilds:function(aChilds,pid){if(aChilds.length==0)return'';var sTree='<ul>';var cLi,cPlus,cCheck,cIcon,sChilds,o,nChild=0,lastChild=aChilds.length,pCheck=-1,chSize=0;for(var k=0;k<lastChild;k++){o=aChilds[k];if(o.childs&&o.childs.length>0){cPlus='pm';sChilds=this.drawChilds(o.childs,o.id);}\nelse{if(pid==0){o.checked=3;}\ncPlus=sChilds='';}\npCheck=(pCheck>=0&&pCheck!=o.checked)?4:o.checked;++nChild;if(lastChild==nChild){cLi='dl';}\nelse{cLi='dt';}\ncCheck='cb'+o.checked;cIcon=o.type||'iTA';if(o.size&&(o.checked==1||o.checked==4)){chSize+=o.size;}\nsTree+='<li class=\"'+cLi+(o.open?' open':' close')+'\" id=\"'+this.prefix+o.id+'\"><div><i class=\"'+cPlus\n+'\"></i><i class=\"'+cCheck+'\"></i><a href=\"#\"><i class='+cIcon+'></i> '+o.name\n+sxd.formatSize(o.size,1)+' </a></div>'+sChilds+'</li>';}\nif(o.pid>0){o.parent.checked=pCheck;if(chSize>0){o.parent.size+=chSize;}}\nreturn sTree+'</ul>';},save:function(){if(this.err)return null;var data=this.data;var saved={TA:[],TC:[],VI:[],PR:[],FU:[],TR:[],EV:[]};var o,c,tc;for(var i1=0,l1=data.length;i1<l1;i1++){o=data[i1];if(o.checked==4){for(var i2=0,l2=o.childs.length;i2<l2;i2++){c=o.childs[i2];if(c.checked==4){for(var i3=0,l3=c.childs.length;i3<l3;i3++){tc=c.childs[i3];if(tc.checked==1)saved['TA'].push(tc.name);else if(tc.checked==2)saved['TC'].push(tc.name);}}\nelse if(c.checked==1)saved[c.type.substr(1,2)].push(c.name);else if(c.checked==2)saved['TC'].push(c.name);}}\nelse if(o.checked==1)saved[o.type.substr(1,2)].push('*');else if(o.checked==2)saved['TC'].push('*');}\nreturn saved;},error:function(str){str=str||'Empty';this.err=true;this.el.innerHTML='<div class=error><div>'+str+'</div></div>';}};}\nfunction zToolbarObject(){return{init:function(elem,buttons){this.el=z(elem);this.name=elem;this.id='#'+elem;this.el.className='zTBar';this.buttons=buttons;this.curBtn={id:0,more:0,pop:0,el:null,com:null};var _this=this;this.el.onmouseover=function(e){if(_this.curBtn.pop)return;if(_this.getId(e||window.event)){_this.curBtn.el.className='btn ovr';}};this.el.onmouseout=function(e){if(_this.curBtn.pop)return;if(_this.getId(e||window.event)){_this.curBtn.el.className='btn';}};this.el.onmousedown=function(e){e=e||window.event;if(_this.curBtn.pop)return;if(_this.getId(e||window.event)){_this.curBtn.el.className=_this.curBtn.more?'btn ovr':'btn dwn';if(_this.curBtn.more){_this.curBtn.el.lastChild.className='arr dwn';_this.curBtn.pop=1;sxd.showMenu(_this.curBtn,sxd.options['sj_'+_this.curBtn.com],{width:220,runjob:true});document.body.onclick=function(){_this.curBtn.el.className='btn';_this.curBtn.el.lastChild.className='arr';_this.curBtn.pop=0;document.body.onclick=null;};if(e.stopPropagation)e.stopPropagation();e.cancelBubble=true;}}};this.el.onmouseup=function(e){if(_this.curBtn.pop)return;if(_this.getId(e||window.event)){_this.curBtn.el.className='btn ovr';if(_this.curBtn.more){_this.curBtn.el.lastChild.className='arr';}\nsxd.actions['tab_'+_this.curBtn.com](_this.curBtn.more);}};var icons={'.btn.ovr':'0 -28px','.ovr .lb':'0 -72px','.ovr .rb':'-4px -72px','.ovr .mb':'0 -138px','.btn.dwn':'0 -94px','.dwn .lb':'0 -116px','.dwn .rb':'-4px -116px','.dwn .mb':'-4px -138px','.dwn.arr':'0 -94px','.dwn.arr .mb':'-4px -50px'};for(var ico in icons){sxd.css.add('.zTBar '+ico+' {background-position:'+icons[ico]+';}');}\nvar btn,sTBar='';for(var b in buttons){btn=buttons[b];if(btn[0]=='|'){sTBar+='<div class=\"spl\"></div>';continue;}\nico=' style=\"background-position:-16px '+((btn[2]-1)*-16)+'px;\"';sTBar+='<div class=\"btn\" id=\"ztbi_'+b+'\"><div class=\"lb\"></div>';sTBar+=btn[3]&1?'<div class=\"txt\"'+ico+'>'+btn[1]+'</div>':'<div class=\"ico\" title=\"'+btn[1]+'\"'+ico+'></div>';sTBar+=btn[3]&2?'<div class=\"arr\"><div class=\"mb\"></div><div class=\"more\"></div><div class=\"rb\"></div></div>':'<div class=\"rb\"></div>';sTBar+='</div>';}\nthis.el.innerHTML=sTBar;if(this.el.lastChild.offsetTop>30){btn=this.el.lastChild.childNodes[1];btn.className='ico';btn.title=btn.innerHTML;btn.innerHTML='';}},getId:function(e){var o=e.target||e.srcElement;var r={id:0,more:0,pop:0,el:null,com:null};while(!o.id){if(o.className.match(/arr/)){r.more=1;}\no=o.parentNode;}\nif(o.id&&o.className!='zTBar'){r.id=o.id.replace('ztbi_','')*1;r.el=o;r.com=this.buttons[r.id][0];this.curBtn=r;return true;}\nreturn false;}};}\nfunction zProgressObject(){return{init:function(elem,type){this.el=z(elem);this.name=elem;this.type=type;this.el.className='zProc '+(type==2?'blue':'green');this.el.innerHTML='<div class=bot><div class=txt>0%</div></div><div class=top><div class=txt>0%</div></div>';this.txt1=this.el.firstChild.firstChild;this.txt2=this.el.lastChild.firstChild;this.top=this.el.lastChild;},set:function(proc,size,cur,tot){proc=proc>0?(proc>100?100:proc):0;size=size||0;cur=cur||0;tot=tot||0;this.txt1.innerHTML=proc+'%'+(size?' '+sxd.formatSize(size):'')+(cur&&tot?' ['+cur+'/'+tot+']':'');this.txt2.innerHTML=this.txt1.innerHTML;if(this.type==2)document.title=proc+'% - '+sxd.version.full;this.top.style.width=Math.round(proc*3)+'px';}};}\nfunction zGridObject(){return{init:function(elem,cfg){this.el=z(elem);this.name=elem;this.cfg=cfg;this.el.className='zGrid';var header='';this.cols=cfg.header.length;for(var i=0;i<this.cols;i++){header+='<th'+(cfg.header[i][1]?' width='+cfg.header[i][1]:'')+'>'+cfg.header[i][0]+'</th>';}\nthis.el.innerHTML='<div class=header><div><table cellspacing=0><tr>'+header+'</tr></table></div></div><div style=\"overflow-x:hidden;overflow-y:auto;\"><table cellspacing=\"0\"></table></div>';this.data=this.el.lastChild.firstChild;this.datadiv=this.el.lastChild;this.data.style.width=cfg.width+'px';this.el.firstChild.style.height='22px';this.el.firstChild.style.width=(cfg.width-1)+'px';this.datadiv.style.height=(cfg.height-23)+'px';this.datadiv.style.width=(cfg.width-1)+'px';if(this.name=='sxdGrid1'){this.oldtime='';this.add=this.addLog;}\nelse{var _this=this;this.selected=null;this.add=function(rows){if(typeof rows[0]=='object'){for(var row in rows)this.addRow(rows[row]);}\nelse this.addRow(rows);};this.datadiv.onclick=function(){_this.selected.className='';_this.selected=null;};}},addLog:function(time,txt,color){var newrow,td1,td2;for(var i=0;i<txt.length;i++){newrow=this.data.insertRow(-1);td1=newrow.insertCell(-1);td2=newrow.insertCell(-1);td1.width=120;if(!i){td1.innerHTML=this.oldtime!=time?time:'&nbsp;';this.oldtime=time;}\ntd2.className='wrap';td2.innerHTML=txt[i]||'&nbsp;';if(color)td2.style.color=color;}\nthis.datadiv.scrollTop=this.datadiv.scrollHeight;},addRow:function(row){var newrow=this.data.insertRow(-1);var td;if(this.name=='sxdGrid2')row[5]=sxd.formatSize(row[5],2);for(var i=0;i<this.cols;i++){td=newrow.insertCell(-1);if(this.cfg.header[i][1])td.width=this.cfg.header[i][1];if(this.cfg.header[i][2]==1)td.style.textAlign='right';td.innerHTML=row[i]||'&nbsp;';td.title=row[i]||'';if(i==0&&this.name=='sxdGrid2')td.title+=\"\\n\"+row[7];}\nnewrow.file=row[this.cols];var _this=this;newrow.onclick=function(e){e=e||window.event;if(_this.selected)_this.selected.className='';this.className='sel';_this.selected=this;if(e.stopPropagation)e.stopPropagation();e.cancelBubble=true;};newrow.ondblclick=function(e){e=e||window.event;if(_this.selected)_this.selected.className='';this.className='sel';_this.selected=this;sxd.runFiles('open');if(e.stopPropagation)e.stopPropagation();e.cancelBubble=true;};},clear:function(){for(var i=0,l=this.data.rows.length;i<l;i++){this.data.deleteRow(0);}\nthis.oldtime='';}};}"
  },
  {
    "path": "admin/sxd/tmpl.php",
    "content": "<?php\n// Templates\nfunction sxd_tpl_page(){\nglobal $SXD;\nreturn <<<HTML\n<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\" \"http://www.w3.org/TR/html4/strict.dtd\">\n<html>\n<head>\n<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\">\n<title>{$SXD->name}</title>\n<link rel=\"stylesheet\" type=\"text/css\" href=\"load.php?sxd.v208.css\">\n<script type=\"text/javascript\" src=\"load.php?sxd.v208.js\"></script>\n<script type=\"text/javascript\" src=\"load.php?{$SXD->LNG['name']}.lng.js\"></script>\n<link rel=\"shortcut icon\" href=\"load.php?favicon.v208.ico\">\n</head>\n\n<body>\n<div id=\"main_div\">\n\t<div id=\"header\">{$SXD->name}</div>\n\t<div id=\"sxdToolbar\"></div>\n\t<div id=\"name\"><div id=\"loading\"></div><b></b></div>\n\t<div id=\"content\" class=\"content\">\n\t\t<table cellspacing=\"0\" id=\"tab_backup\">\n\t\t\t<tr>\n\t\t\t\t<td width=\"242\" valign=\"top\">\n\t\t\t\t\t<div class=\"caption\">{$SXD->LNG['combo_db']}</div><div id=\"backup_db\"></div>\n\t\t\t\t\t<div class=\"caption\">{$SXD->LNG['combo_charset']}</div><div id=\"backup_charset\"></div>\n\t\t\t\t\t<div class=\"caption\">{$SXD->LNG['combo_zip']}</div><div id=\"backup_zip\"></div>\n\t\t\t\t\t<div class=\"caption\">{$SXD->LNG['combo_comments']}</div>\n\t\t\t\t\t<div class=\"border\"><textarea cols=\"10\" rows=\"3\" id=\"backup_comment\"></textarea></div>\n\t\t\t\t\t<div class=\"caption\" style=\"margin-top:12px;\">\n\t\t\t\t\t\t<fieldset><legend>{$SXD->LNG['del_legend']}</legend>\n\t\t\t\t\t\t<div class=\"caption\">&nbsp;– {$SXD->LNG['del_date']}</div>\n\t\t\t\t\t\t<div class=\"caption\">&nbsp;– {$SXD->LNG['del_count']}</div>\n\t\t\t\t\t\t</fieldset>\n\t\t\t\t\t</div>\n\t\t\t\t</td>\n\t\t\t\t<td width=\"308\" valign=\"top\">\n\t\t\t\t\t<div class=\"caption\">{$SXD->LNG['tree']}</div><div id=backup_tree class=\"zTree\"></div>\n\t\t\t\t</td>\n\t\t\t</tr>\n\t\t\t<tr><td></td><td align=\"right\"><input type=\"button\" value=\"{$SXD->LNG['btn_save']}\" onclick=\"sxd.showDialog('savejob');z('sj_name').value = sxd.combos.backup_db.value;\"> <input type=\"button\" value=\"{$SXD->LNG['btn_exec']}\" onclick=\"sxd.runBackup();\"></td></tr>\n\t\t</table>\n\t\t\n\t\t<table cellspacing=\"0\" id=\"tab_restore\" style=\"display:none;\">\n\t\t\t<tr>\n\t\t\t\t<td width=\"242\" valign=\"top\">\n\t\t\t\t\t<div class=\"caption\">{$SXD->LNG['combo_db']}</div><div id=\"restore_db\"></div>\n\t\t\t\t\t<div class=\"caption\">{$SXD->LNG['combo_charset']}</div><div id=\"restore_charset\"></div>\n\t\t\t\t\t<div class=\"caption\">{$SXD->LNG['combo_file']}</div><div id=\"restore_file\"></div>\n\t\t\t\t\t<div class=\"caption\">{$SXD->LNG['combo_comments']}</div>\n\t\t\t\t\t<div class=\"border\"><textarea cols=\"10\" rows=\"3\" id=\"restore_comment\" readonly></textarea></div>\n\t\t\t\t\t<div class=\"caption\">{$SXD->LNG['combo_strategy']}</div><div id=\"restore_type\"></div>\n\t\t\t\t\t<div class=\"caption\" style=\"margin-top:17px;\">\n\t\t\t\t\t\t<fieldset><legend>{$SXD->LNG['ext_legend']}</legend>\n\t\t\t\t\t\t<div class=\"caption\"><label><input type=\"checkbox\" id=\"correct\"> {$SXD->LNG['correct']}</label></div>\n\t\t\t\t\t\t<div class=\"caption\"><label><input type=\"checkbox\" id=\"autoinc\"> {$SXD->LNG['autoinc']}</label></div>\n\t\t\t\t\t\t</fieldset>\n\t\t\t\t\t</div>\n\t\t\t\t</td>\n\t\t\t\t<td width=\"308\" valign=\"top\">\n\t\t\t\t\t<div class=\"caption\">{$SXD->LNG['tree']}</div><div id=restore_tree class=\"zTree\"></div>\n\t\t\t\t</td>\n\t\t\t</tr>\n\t\t\t<tr><td></td><td align=\"right\"><input type=\"button\" value=\"{$SXD->LNG['btn_save']}\" onclick=\"sxd.showDialog('savejob');z('sj_name').value = sxd.combos.restore_db.value;\" id=restore_savejob> <input type=\"button\" value=\"{$SXD->LNG['btn_exec']}\" onclick=\"sxd.runRestore();\" id=restore_runjob></td></tr>\n\t\t</table>\n\t\t\n\t\t<table cellspacing=\"0\" id=\"tab_log\" style=\"display:none;\">\n\t\t\t<tr>\n\t\t\t\t<td valign=\"top\" colspan=2>\n\t\t\t\t\t<div id=sxdGrid1></div> \n\t\t\t\t</td>\n\t\t\t</tr>\n\t\t\t<tr><td colspan=2>\n\t\t\t<table class=progress>\n\t\t\t\t<tr><td>{$SXD->LNG['status_current']}</td><td><div id=\"sxdProc1\"></div></td><td width=60>{$SXD->LNG['time_elapsed']}</td><td width=40 align=right id=\"sxdTime1\">00:00</td></tr>\n\t\t\t\t<tr><td>{$SXD->LNG['status_total']}</td><td><div id=\"sxdProc2\"></div></td><td>{$SXD->LNG['time_left']}</td><td align=right id=\"sxdTime2\">00:00</td></tr>\n\t\t\t</table>\n\t\t\t</td></tr>\n\t\t\t<tr><td width=\"152\"><input type=\"button\" value=\"{$SXD->LNG['btn_clear']}\" onclick=\"sxd.log.clear();\"></td><td width=\"380\" align=\"right\">\n\t\t\t<input type=\"button\" value=\"{$SXD->LNG['btn_download']}\" id=\"btn_down\" onclick=\"sxd.runFiles('download', this.file);\" style=\"display:none;\">\n\t\t\t<input type=\"button\" value=\"{$SXD->LNG['btn_again']}\" id=\"btn_again\" onclick=\"sxd.runAgain();\" disabled>\n\t\t\t<input type=\"button\" value=\"{$SXD->LNG['btn_stop']}\" id=\"btn_stop\" onclick=\"sxd.stopJob();\" disabled>\n\t\t\t<input type=\"button\" value=\"{$SXD->LNG['btn_pause']}\" id=\"btn_pause\" onclick=\"sxd.pauseJob();\" disabled>\n\t\t\t<input type=\"button\" value=\"{$SXD->LNG['btn_resume']}\" id=\"btn_resume\" onclick=\"sxd.resumeJob();\" style=\"display:none;\">\n\t\t\t</td></tr>\n\t\t</table>\n\t\t<table cellspacing=\"0\" id=\"tab_result\" style=\"display:none;\">\n\t\t\t<tr>\n\t\t\t\t<td valign=\"top\">\n\t\t\t\t\t<div id=sxdGrid3></div> \n\t\t\t\t</td>\n\t\t\t</tr>\n\t\t\t<tr><td>\n\t\t\t<input type=\"button\" value=\"{$SXD->LNG['btn_clear']}\" onclick=\"sxd.result.clear();\">\n\t\t\t</td></tr>\n\t\t</table>\n\t\t\n\t\t<table cellspacing=\"0\" id=\"tab_files\" style=\"display:none;\">\n\t\t\t<tr>\n\t\t\t\t<td valign=\"top\" colspan=2>\n\t\t\t\t\t<div id=sxdGrid2></div> \n\t\t\t\t</td>\n\t\t\t</tr>\n\t\t\t<tr><td width=\"242\"><form id=\"save_file\" method=\"GET\" style=\"visibility:hidden;display:inline;\" target=save></form><input type=\"button\" value=\"{$SXD->LNG['btn_delete']}\" onclick=\"sxd.runFiles('delete')\"></td><td width=\"290\" align=\"right\">\n\t\t\t<input type=\"button\" value=\"{$SXD->LNG['btn_download']}\" onclick=\"sxd.runFiles('download')\">\n\t\t\t<input type=\"button\" value=\"{$SXD->LNG['btn_open']}\" onclick=\"sxd.runFiles('open')\">\n\t\t\t</td></tr>\n\t\t</table>\n\t\t\n\t\t\n\t\t<table cellspacing=\"0\" id=\"tab_services\" style=\"display:none;\">\n\t\t\t<tr>\n\t\t\t\t<td width=\"242\" valign=\"top\">\n\t\t\t\t\t<div class=\"caption\">{$SXD->LNG['combo_db']}</div><div id=\"services_db\"></div>\n\t\t\t\t\t<br>\n\t\t\t\t\t<div class=\"caption\">{$SXD->LNG['opt_check']}</div><div id=\"services_check\"></div>\n\t\t\t\t\t<div class=\"caption\">{$SXD->LNG['opt_repair']}</div><div id=\"services_repair\"></div>\n\t\t\t\t</td>\n\t\t\t\t<td width=\"308\" valign=\"top\">\n\t\t\t\t\t<div class=\"caption\">{$SXD->LNG['tree']}</div><div id=services_tree class=\"zTree\"></div>\n\t\t\t\t</td>\n\t\t\t</tr>\n\t\t\t<tr><td align=\"right\" colspan=2><input type=\"button\" value=\"{$SXD->LNG['btn_delete_db']}\" onclick=\"sxd.runServices('delete')\" style=\"float:left;\"> <input type=\"button\" value=\"{$SXD->LNG['btn_check']}\" onclick=\"sxd.runServices('check')\"> <input type=\"button\" value=\"{$SXD->LNG['btn_repair']}\" onclick=\"sxd.runServices('repair')\"> <input type=\"button\" value=\"{$SXD->LNG['btn_analyze']}\" onclick=\"sxd.runServices('analyze')\">  <input type=\"button\" value=\"{$SXD->LNG['btn_optimize']}\" onclick=\"sxd.runServices('optimize')\"></td></tr>\n\t\t</table>\n\t\t<table cellspacing=\"0\" id=\"tab_options\" style=\"display:none;\">\n\t\t\t<tr>\n\t\t\t\t<td valign=\"top\" colspan=2>\n\t\t\t\t\t<div style=\"height: 341px;\">\n\t\t\t\t\t<fieldset>\n\t\t\t\t\t<legend>{$SXD->LNG['cfg_legend']}</legend>\n\t\t\t\t\t\t<table cellspacing=\"0\">\n\t\t\t\t\t\t\t<tr>\n\t\t\t\t\t\t\t\t<td width=190>{$SXD->LNG['cfg_time_web']}</td>\n\t\t\t\t\t\t\t\t<td width=45><input type=\"text\" id=\"time_web\" style=\"width:40px;\"></td>\n\t\t\t\t\t\t\t\t<td align=\"right\">{$SXD->LNG['cfg_time_cron']}</td>\n\t\t\t\t\t\t\t\t<td width=40 align=\"right\"><input type=\"text\" id=\"time_cron\" style=\"width:40px;\"></td>\n\t\t\t\t\t\t\t</tr>\n\t\t\t\t\t\t\t<tr>\n\t\t\t\t\t\t\t\t<td>{$SXD->LNG['cfg_backup_path']}</td>\n\t\t\t\t\t\t\t\t<td colspan=3><input type=\"text\" id=\"backup_path\" style=\"width:351px;\"></td>\n\t\t\t\t\t\t\t</tr>\n\t\t\t\t\t\t\t<tr>\n\t\t\t\t\t\t\t\t<td>{$SXD->LNG['cfg_backup_url']}</td>\n\t\t\t\t\t\t\t\t<td colspan=3><input type=\"text\" id=\"backup_url\" style=\"width:351px;\"></td>\n\t\t\t\t\t\t\t</tr>\n\t\t\t\t\t\t\t<tr>\n\t\t\t\t\t\t\t\t<td>{$SXD->LNG['cfg_globstat']}</td>\n\t\t\t\t\t\t\t\t<td colspan=3><input type=\"checkbox\" id=\"globstat\" value=\"1\"></td>\n\t\t\t\t\t\t\t</tr>\n\t\t\t\t\t\t</table>\n\t\t\t\t\t</fieldset>\n\t\t\t\t\t<fieldset>\n\t\t\t\t\t<legend>{$SXD->LNG['cfg_confirm']}</legend>\n\t\t\t\t\t\t<table cellspacing=\"0\">\n\t\t\t\t\t\t\t<tr>\n\t\t\t\t\t\t\t\t<td width=\"33%\"><label><input type=\"checkbox\" id=\"conf_import\" value=\"1\"> {$SXD->LNG['cfg_conf_import']}</label></td>\n\t\t\t\t\t\t\t\t<td width=\"34%\"><label><input type=\"checkbox\" id=\"conf_file\" value=\"1\"> {$SXD->LNG['cfg_conf_file']}</label></td>\n\t\t\t\t\t\t\t\t<td width=\"33%\"><label><input type=\"checkbox\" id=\"conf_db\" value=\"1\"> {$SXD->LNG['cfg_conf_db']}</label></td>\n\t\t\t\t\t\t\t</tr>\n\t\t\t\t\t\t</table>\n\t\t\t\t\t</fieldset>\n\t\t\t\t\t<fieldset>\n\t\t\t\t\t<legend>{$SXD->LNG['cfg_extended']}</legend>\n\t\t\t\t\t\t<table cellspacing=\"0\">\n\t\t\t\t\t\t\t<tr>\n\t\t\t\t\t\t\t\t<td width=190>{$SXD->LNG['cfg_charsets']}</td>\n\t\t\t\t\t\t\t\t<td><input type=\"text\" id=\"charsets\" value=\"\" style=\"width:351px;\"></td>\n\t\t\t\t\t\t\t</tr>\n\t\t\t\t\t\t\t<tr>\n\t\t\t\t\t\t\t\t<td>{$SXD->LNG['cfg_only_create']}</td>\n\t\t\t\t\t\t\t\t<td><input type=\"text\" id=\"only_create\" value=\"\" style=\"width:351px;\"></td>\n\t\t\t\t\t\t\t</tr>\n\t\t\t\t\t\t\t<tr>\n\t\t\t\t\t\t\t\t<td>{$SXD->LNG['cfg_auth']}</td>\n\t\t\t\t\t\t\t\t<td><input type=\"text\" id=\"auth\" value=\"\" style=\"width:351px;\"></td>\n\t\t\t\t\t\t\t</tr>\n\t\t\t\t\t\t</table>\n\t\t\t\t\t</fieldset>\n\t\t\t\t\t</div>\n\t\t\t\t</td>\n\t\t\t</tr>\n\t\t\t<tr><td align=\"right\" colspan=2><input type=\"button\" value=\"{$SXD->LNG['btn_save']}\" onclick=\"sxd.saveOptions();\"></td></tr>\n\t\t</table>\n\t</div>\n</div>\n\n<div id=\"overlay\"></div>\n<div class=\"dialog\" id =\"dia_connect\">\n\t<div class=\"header\">{$SXD->LNG['con_header']}</div>\n\t<div class=\"content\">\n\t\t<table cellspacing=\"5\">\n\t\t\t<tr>\n\t\t\t\t<td valign=\"top\">\n\t\t\t\t<fieldset>\n\t\t\t\t<legend>{$SXD->LNG['connect']}</legend>\n\t\t\t\t\t<table cellspacing=\"3\">\n\t\t\t\t\t\t<tr>\n\t\t\t\t\t\t\t<td width=\"80\">{$SXD->LNG['my_host']}</td>\n\t\t\t\t\t\t\t<td width=\"126\"><input type=\"text\" id=\"con_host\" style=\"width:120px;\"></td>\n\t\t\t\t\t\t\t<td width=\"40\" align=\"right\">{$SXD->LNG['my_port']}</td>\n\t\t\t\t\t\t\t<td width=\"36\"><input type=\"text\" id=\"con_port\" maxlength=\"5\" style=\"width:30px;\"></td>\n\t\t\t\t\t\t</tr>\n\t\t\t\t\t\t<tr>\n\t\t\t\t\t\t\t<td>{$SXD->LNG['my_user']}</td>\n\t\t\t\t\t\t\t<td colspan=\"3\"><input type=\"text\" id=\"con_user\" name=\"user\" style=\"width:202px;\"></td>\n\t\t\t\t\t\t</tr>\n\t\t\t\t\t\t<tr>\n\t\t\t\t\t\t\t<td>{$SXD->LNG['my_pass']}</td>\n\t\t\t\t\t\t\t<td colspan=\"3\"><input type=\"password\" id=\"con_pass\" name=\"pass\" title=\"{$SXD->LNG['my_pass_hidden']}\" style=\"width:202px;\" onchange=\"this.changed = true;\"></td>\n\t\t\t\t\t\t</tr>\n\t\t\t\t\t\t<tr>\n\t\t\t\t\t\t\t<td></td>\n\t\t\t\t\t\t\t<td colspan=\"3\"><label><input type=\"checkbox\" id=\"con_comp\" value=\"1\"> {$SXD->LNG['my_comp']}</label></td>\n\t\t\t\t\t\t</tr>\n\t\t\t\t\t\t<tr>\n\t\t\t\t\t\t\t<td>{$SXD->LNG['my_db']}</td>\n\t\t\t\t\t\t\t<td colspan=\"3\"><input type=\"text\" id=\"con_db\" style=\"width:202px;\"></td>\n\t\t\t\t\t\t</tr>\n\t\t\t\t\t</table></fieldset>\n\t\t\t\t</td>\n\t\t\t</tr>\n\t\t\t<tr class=\"buttons\"><td align=\"right\"><input type=\"button\" value=\"{$SXD->LNG['btn_save']}\" onclick=\"sxd.saveConnect();\"><input type=\"button\" value=\"{$SXD->LNG['btn_cancel']}\" onclick=\"sxd.hideDialog('connect');\"></td></tr>\n\t\t</table>\n\t</div>\n</div>\n<div class=\"dialog\" id =\"dia_savejob\">\n\t<div class=\"header\">{$SXD->LNG['sj_header']}</div>\n\t<div class=\"content\">\n\t\t<table cellspacing=\"5\">\n\t\t\t<tr>\n\t\t\t\t<td valign=\"top\">\n\t\t\t\t<fieldset>\n\t\t\t\t<legend>{$SXD->LNG['sj_job']}</legend>\n\t\t\t\t\t<table cellspacing=\"3\">\n\t\t\t\t\t\t<tr>\n\t\t\t\t\t\t\t<td width=\"80\">{$SXD->LNG['sj_name']}</td>\n\t\t\t\t\t\t\t<td><input type=\"text\" id=\"sj_name\" style=\"width:202px;\" maxlength=\"12\" value=\"\"></td>\n\t\t\t\t\t\t</tr>\n\t\t\t\t\t\t<tr>\n\t\t\t\t\t\t\t<td>{$SXD->LNG['sj_title']}</td>\n\t\t\t\t\t\t\t<td><input type=\"text\" id=\"sj_title\" maxlength=\"64\" style=\"width:202px;\"></td>\n\t\t\t\t\t\t</tr>\n\t\t\t\t\t</table></fieldset>\n\t\t\t\t</td>\n\t\t\t</tr>\n\t\t\t<tr class=\"buttons\"><td align=\"right\"><input type=\"button\" value=\"{$SXD->LNG['btn_save']}\" onclick=\"sxd.saveJob();\"><input type=\"button\" value=\"{$SXD->LNG['btn_cancel']}\" onclick=\"sxd.hideDialog('savejob');\"></td></tr>\n\t\t</table>\n\t</div>\n</div>\n<div class=dialog id=\"dia_createdb\">\n\t<div class=\"header\">{$SXD->LNG['cdb_header']}</div>\n\t<div class=\"content\">\n\t\t<table cellspacing=\"5\">\n\t\t\t<tr>\n\t\t\t\t<td valign=\"top\">\n\t\t\t\t<fieldset>\n\t\t\t\t<legend>{$SXD->LNG['cdb_detail']}</legend>\n\t\t\t\t\t<table cellspacing=\"3\">\n\t\t\t\t\t\t<tr>\n\t\t\t\t\t\t\t<td width=\"80\">{$SXD->LNG['cdb_name']}</td>\n\t\t\t\t\t\t\t<td width=\"202\"><input type=\"text\" id=\"db_name\" value=\"my_db_1\" style=\"width:202px;\"></td>\n\t\t\t\t\t\t</tr>\n\t\t\t\t\t\t<tr>\n\t\t\t\t\t\t\t<td>{$SXD->LNG['combo_charset']}</td>\n\t\t\t\t\t\t\t<td><div id=\"db_charset\"></div></td>\n\t\t\t\t\t\t</tr>\n\t\t\t\t\t\t<tr>\n\t\t\t\t\t\t\t<td>{$SXD->LNG['combo_collate']}</td>\n\t\t\t\t\t\t\t<td><div id=\"db_charset_col\"></div></td>\n\t\t\t\t\t\t</tr>\n\t\t\t\t\t</table>\n\t\t\t\t</fieldset>\n\t\t\t\t</td>\n\t\t\t</tr>\n\t\t\t<tr class=\"buttons\"><td align=\"right\"><input type=\"button\" value=\"{$SXD->LNG['btn_create']}\" onclick=\"sxd.addDb();\"><input type=\"button\" value=\"{$SXD->LNG['btn_cancel']}\" onclick=\"sxd.hideDialog('createdb');\"></td></tr>\n\t\t</table>\n\t</div>\n</div>\n\n<div id=sxdMenu style=\"display:none;z-index:9999;\"></div>\n<script type=\"text/javascript\">\nsxd.init();\nsxd.backupUrl = '{$SXD->CFG['backup_url']}';\nsxd.tbar.init('sxdToolbar', {$SXD->VAR['toolbar']}); \n{$SXD->VAR['combos']}\nsxd.actions.tab_backup();\n</script>\n</body>\n</html>\nHTML;\n}\n\nfunction sxd_tpl_auth($error = ''){\nglobal $SXD;\nreturn <<<HTML\n<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\" \"http://www.w3.org/TR/html4/strict.dtd\">  \n<html>\n<head>\n<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\">\n<title>{$SXD->name}</title>\n<link rel=\"shortcut icon\" href=\"load.php?favicon.v208.ico\">\n<link rel=\"stylesheet\" type=\"text/css\" href=\"load.php?sxd.v208.css\">\n</head>\n<body>\n<div class=\"dialog\" id=\"dia_auth\">\n\t<div class=\"header\"><a href=\"http://sypex.net/\">{$SXD->name}</a></div>\n\t<div class=\"content\" id=\"div_1\" style=\"line-height:50px;text-align:center;\">{$SXD->LNG['js_required']}</div>\n\t<div class=\"content\" id=\"div_2\" style=\"display:none;\">\n\t\t<form method=\"post\">\n\t\t<table cellspacing=\"5\">\n\t\t\t<tr>\n\t\t\t\t<td valign=\"top\" colspan=\"3\">\n\t\t\t\t<fieldset>\n\t\t\t\t<legend>{$SXD->LNG['auth']}</legend>\n\t\t\t\t\t<table cellspacing=\"3\">\n\t\t\t\t\t\t<tr>\n\t\t\t\t\t\t\t<td width=\"90\">{$SXD->LNG['auth_user']}</td>\n\t\t\t\t\t\t\t<td width=\"192\"><input type=\"text\" name=\"user\" value=\"{$_POST['user']}\" class=\"i202\"></td>\n\t\t\t\t\t\t</tr>\n\t\t\t\t\t\t<tr>\n\t\t\t\t\t\t\t<td>{$SXD->LNG['my_pass']}</td>\n\t\t\t\t\t\t\t<td><input type=\"password\" name=\"pass\" value=\"{$_POST['pass']}\" class=\"i202\"></td>\n\t\t\t\t\t\t</tr>\n\t\t\t\t\t\t<tr>\n\t\t\t\t\t\t\t<td></td>\n\t\t\t\t\t\t\t<td><label><input type=\"checkbox\" name=\"save\" value=\"1\"{$_POST['save']}> {$SXD->LNG['auth_remember']}</label></td>\n\t\t\t\t\t\t</tr>\n\t\t\t\t\t\t<tr>\n\t\t\t\t\t\t\t<td>Language:</td>\n\t\t\t\t\t\t\t<td><select type=\"text\" name=\"lang\" style=\"width:198px;\" onChange=\"this.form.submit();\">{$SXD->lng_list}</select></td>\n\t\t\t\t\t\t</tr>\n\t\t\t\t\t</table>\n\t\t\t\t\t<table cellspacing=\"3\" id=\"hst\" style=\"display:none;\">\n\t\t\t\t\t\t<tr>\n\t\t\t\t\t\t\t<td width=\"90\">{$SXD->LNG['my_host']}</td>\n\t\t\t\t\t\t\t<td width=\"116\"><input type=\"text\" name=\"host\" style=\"width:110px;\" value=\"{$_POST['host']}\"></td>\n\t\t\t\t\t\t\t<td width=\"40\" align=\"right\">{$SXD->LNG['my_port']}</td>\n\t\t\t\t\t\t\t<td width=\"36\"><input type=\"text\" name=\"port\" maxlength=\"5\" style=\"width:30px;\" value=\"{$_POST['port']}\"></td>\n\t\t\t\t\t\t</tr>\n\t\t\t\t\t</table>\n\t\t\t\t</fieldset>\n\t\t\t\t</td>\n\t\t\t</tr>\n\t\t\t<tr class=\"buttons\"><td align=\"left\"><input type=\"button\" value=\"{$SXD->LNG['btn_details']}\" onclick=\"var s = document.getElementById('hst').style; s.display = (s.display == 'block') ? 'none' : 'block';\"></td><td align=\"right\"><input type=\"submit\" value=\"{$SXD->LNG['btn_enter']}\"></td></tr>\n\t\t</table>\n\t\t</form>\n\t</div>\n\t<script type=\"text/javascript\">document.getElementById('div_1').style.display = 'none';document.getElementById('div_2').style.display = 'block';</script>\n</div>\n</body>\n</html>\nHTML;\n}"
  },
  {
    "path": "admin/tools.php",
    "content": "<?php\n/**\n * erreurs.php\n *\n * @version 1.0\n * @copyright 2009 by Gorlum for http://oGame.Triolan.COM.UA\n */\n\ndefine('INSIDE', true);\ndefine('INSTALL', false);\ndefine('IN_ADMIN', true);\nrequire('../common.' . substr(strrchr(__FILE__, '.'), 1));\n\nglobal $lang, $user, $template_result;\n\nSnTemplate::messageBoxAdminAccessDenied(AUTH_LEVEL_ADMINISTRATOR);\n\n$mode = sys_get_param_int('mode');\n\nswitch ($mode) {\n  case ADM_COUNTER_RECALC:\n    $template_result['.']['result'][] = [\n      'STATUS'  => ERR_NONE,\n      'MESSAGE' => number_format(memory_get_usage()) . ' - memory Before',\n    ];\n    $t = new \\General\\LogCounterShrinker(SN::$gc);\n    $template_result['.']['result'][] = $t->process();\n    unset($t);\n    $template_result['.']['result'][] = [\n      'STATUS'  => ERR_NONE,\n      'MESSAGE' => number_format(memory_get_usage()) . ' - memory After',\n    ];\n  break;\n\n  case ADM_TOOL_CONFIG_RELOAD:\n    SN::$config->db_loadAll();\n    SN::$db->schema()->clear();\n\n    SN::$config->pass()->game_watchlist;\n    if (SN::$config->game_watchlist) {\n      SN::$config->game_watchlist_array = explode(';', SN::$config->game_watchlist);\n    } else {\n      unset(SN::$config->game_watchlist_array);\n    }\n  break;\n\n  case ADM_TOOL_MD5:\n    $template = SnTemplate::gettemplate(\"admin/md5enc\", true);\n    $password_seed = sys_get_param_str_unsafe('seed', SN_SYS_SEC_CHARS_ALLOWED);\n    $password_length = sys_get_param_int('length', 16);\n    $string = ($string = sys_get_param_str_unsafe('string')) ? $string : sys_random_string($password_length, $password_seed);\n\n    $template->assign_vars(array(\n      'SEED'   => $password_seed,\n      'LENGTH' => $password_length,\n      'STRING' => htmlentities($string),\n      'MD5'    => md5($string),\n    ));\n    SnTemplate::display($template, $lang['adm_tools_md5_header']);\n  break;\n\n  case ADM_TOOL_FORCE_ALL:\n    SN::$config->db_saveItem('db_version', 40);\n    require_once('../includes/update.php');\n  break;\n\n  case ADM_TOOL_FORCE_LAST:\n    SN::$config->db_saveItem('db_version', floor(SN::$config->db_version - 1));\n    require_once('../includes/update.php');\n  break;\n\n  case ADM_TOOL_INFO_PHP:\n    phpinfo();\n  break;\n\n  case ADM_TOOL_INFO_SQL:\n    $template = SnTemplate::gettemplate(\"simple_table\", true);\n\n    $template->assign_block_vars('table', $lang['adm_tool_sql_table']['server']);\n    $status = array(\n      $lang['adm_tool_sql_server_version'] => SN::$db->getServerInfo(),\n      $lang['adm_tool_sql_client_version'] => SN::$db->getClientInfo(),\n      $lang['adm_tool_sql_host_info']      => SN::$db->getHostInfo(),\n    );\n    foreach ($status as $key => $value) {\n      $template->assign_block_vars('table.row', array(\n        'VALUE_1' => $key,\n        'VALUE_2' => $value,\n      ));\n    }\n\n    $template->assign_block_vars('table', $lang['adm_tool_sql_table']['status']);\n    $status = explode('  ', SN::$db->getServerStat());\n    foreach ($status as $value) {\n      $row = explode(': ', $value);\n      $template->assign_block_vars('table.row', array(\n        'VALUE_1' => $row[0],\n        'VALUE_2' => $row[1],\n      ));\n    }\n\n\n    $template->assign_block_vars('table', $lang['adm_tool_sql_table']['params']);\n    $result = doquery('SHOW STATUS;');\n    while ($row = db_fetch($result)) {\n      $template->assign_block_vars('table.row', array(\n        'VALUE_1' => $row['Variable_name'],\n        'VALUE_2' => $row['Value'],\n      ));\n    }\n\n    $template->assign_vars(array(\n      'PAGE_HEADER'   => $lang['adm_tool_sql_page_header'],\n      'COLUMN_NAME_1' => $lang['adm_tool_sql_param_name'],\n      'COLUMN_NAME_2' => $lang['adm_tool_sql_param_value'],\n      'TABLE_FOOTER'  => 'test',\n    ));\n\n    SnTemplate::display($template);\n  break;\n\n  case ADM_PTL_TEST:\n    $template = SnTemplate::gettemplate(\"admin/admin_ptl_test\", true);\n\n    $template->assign_vars(array(\n      'PAGE_TITLE' => $lang['adm_ptl_test'],\n\n      'VAR'                => 'VALUE',\n      'RENDER_VAR'         => '{VAR}',\n      'RENDER_DEFINED_VAR' => '{$VAR}',\n\n\n      'VAR_VALUE' => 'VAR_VALUE',\n\n      'RENDER_VAR_VALUE'       => '{VAR_VALUE}',\n      'RENDER_NAVBAR_RESEARCH' => '{I_navbar_research|html}',\n    ));\n\n    $template->assign_block_vars('render_test_block', array(\n      'BLOCK_VAR' => '{VAR}',\n    ));\n\n\n    $tests = array(\n      array('HEADER' => '{VAR} and {$VAR} Variables'),\n      array(\n        'SAMPLE'      => '{VAR}',\n        'EXPECTED'    => 'VALUE',\n        'DESCRIPTION' => 'Root variable - existing',\n      ),\n      array(\n        'SAMPLE'      => '{VAR_NOT_EXISTS}',\n        'EXPECTED'    => '',\n        'DESCRIPTION' => 'Root variable - non-existing',\n      ),\n      array(\n        'SAMPLE'      => '{АБВГД}',\n        'EXPECTED'    => '{АБВГД}',\n        'DESCRIPTION' => 'Root variable - wrong name',\n      ),\n      array(\n        'SAMPLE'      => '{$VAR}',\n        'EXPECTED'    => '$VALUE',\n        'DESCRIPTION' => 'DEFINE-d variable - existing',\n      ),\n      array(\n        'SAMPLE'      => '{$VAR_NOT_EXISTS}',\n        'EXPECTED'    => '',\n        'DESCRIPTION' => 'DEFINE-d variable - non-existing',\n      ),\n      array(\n        'SAMPLE'      => '{$АБВГД}',\n        'EXPECTED'    => '{$АБВГД}',\n        'DESCRIPTION' => 'DEFINE-d variable - wrong name',\n      ),\n\n      array('HEADER' => '{C_xxx} - Config'),\n      array(\n        'SAMPLE'      => '{C___ptl_test}',\n        'EXPECTED'    => 'config_ptl_test',\n        'DESCRIPTION' => 'Config variable',\n      ),\n      array(\n        'SAMPLE'      => '{C___ptl_test_array[value]}',\n        'EXPECTED'    => 'config_ptl_test_array',\n        'DESCRIPTION' => 'Config array variable',\n      ),\n\n      array('HEADER' => '{L_xxx} and {LA_xxx} - Language'),\n      array(\n        'SAMPLE'      => '{L_admin_ptl_test_la_}',\n        'EXPECTED'    => 'Single\\'Double\"ZeroEnd',\n        'DESCRIPTION' => 'Language string',\n      ),\n      array(\n        'SAMPLE'      => '{LA_admin_ptl_test_la_}',\n        'EXPECTED'    => 'Single\\\\\\'Double\\\"Zero\\0End',\n        'DESCRIPTION' => 'JavaScript-safe language string',\n      ),\n      array(\n        'SAMPLE'      => '{L_surely_not_exists_string_test}',\n        'EXPECTED'    => '{ L_surely_not_exists_string_test }',\n        'DESCRIPTION' => 'Language string - non-existing',\n      ),\n      array(\n        'SAMPLE'      => '{LA_surely_not_exists_string_test}',\n        'EXPECTED'    => '{ LA_surely_not_exists_string_test }',\n        'DESCRIPTION' => 'JS-safe language string - non-existing',\n      ),\n\n      array('HEADER' => '{I_xxx} - Image rendering'),\n      array(\n        'SAMPLE'      => \"{\" . ($tag = \"I_NO_IMAGE|height=\\\"20%\\\"|width=\\\"20%\\\"\") . \"}<br />{{$tag}|html}\",\n        'EXPECTED'    => ($imgPath = SN_ROOT_VIRTUAL . 'design/images/_no_image.png') . \"<br /><img src=\\\"{$imgPath}\\\" height=\\\"20%\\\" width=\\\"20%\\\" />\",\n        'DESCRIPTION' => 'Image - not existing',\n      ),\n\n      array(\n        'SAMPLE'      => \"{\" . ($tag = \"I_/design/images/icon_note_pinned_64x64.png\") . \"}<br />{{$tag}|html}\",\n        'EXPECTED'    => ($imgPath = SN_ROOT_VIRTUAL . 'design/images/icon_note_pinned_64x64.png') . \"<br /><img src=\\\"{$imgPath}\\\" />\",\n        'DESCRIPTION' => 'Direct image access by absolute path',\n      ),\n      array(\n        'SAMPLE'      => \"{\" . ($tag = \"I_images/border_small.png\") . \"}<br />{{$tag}|html}\",\n        'EXPECTED'    => ($imgPath = SN_ROOT_VIRTUAL . 'skins/EpicBlue/images/border_small.png') . \"<br /><img src=\\\"{$imgPath}\\\" />\",\n        'DESCRIPTION' => 'Access image in skin by relative path',\n      ),\n      array(\n        'SAMPLE'      => \"{\" . ($tag = \"I_navbar_research\") . \"}<br />{{$tag}|html}\",\n        'EXPECTED'    => ($imgPath = SN_ROOT_VIRTUAL . 'design/images/navbar_research_64x64.png') . \"<br /><img src=\\\"{$imgPath}\\\" />\",\n        'DESCRIPTION' => 'Image direct access by ID in skin.ini',\n      ),\n      array(\n        'SAMPLE'      => \"{\" . ($tag = \"I_navbar_research|skin=supernova-ivash\") . \"}<br />{{$tag}|html}\",\n        'EXPECTED'    => ($imgPath = SN_ROOT_VIRTUAL . 'skins/supernova-ivash/navbar/navbar_research_64x64.png') . \"<br /><img src=\\\"{$imgPath}\\\" />\",\n        'DESCRIPTION' => 'Param \\'skin\\' - get image by Image ID from other skin',\n      ),\n      array(\n        'SAMPLE'      => \"{\" . ($tag = \"I_navbar_research|height=\\\"20%\\\"|width=\\\"20%\\\"\") . \"}<br />{{$tag}|html}\",\n        'EXPECTED'    => ($imgPath = SN_ROOT_VIRTUAL . 'design/images/navbar_research_64x64.png') . \"<br /><img src=\\\"{$imgPath}\\\" height=\\\"20%\\\" width=\\\"20%\\\" />\",\n        'DESCRIPTION' => 'Image attributes - height 20%, width 20%',\n      ),\n      array(\n        'SAMPLE'      => \"{\" . ($tag = \"I_navbar_research|skin=supernova-ivash|height=\\\"40px\\\"\") . \"}<br />{{$tag}|html}\",\n        'EXPECTED'    => ($imgPath = SN_ROOT_VIRTUAL . 'skins/supernova-ivash/navbar/navbar_research_64x64.png') . \"<br /><img src=\\\"{$imgPath}\\\" height=\\\"40px\\\" />\",\n        'DESCRIPTION' => 'Param \\'skin\\' with other params',\n      ),\n\n      array(\n        'SAMPLE'      => \"{\" . ($tag = \"I_[\\$BLACK]\") . \"}<br />{{$tag}|html}\",\n        'EXPECTED'    => ($imgPath = SN_ROOT_VIRTUAL . 'skins/EpicBlue/planeten/black_moon.jpg') . \"<br /><img src=\\\"{$imgPath}\\\" />\",\n        'DESCRIPTION' => 'DEFINE-d test',\n      ),\n\n      array(\n        'SAMPLE'      => \"{\" . ($tag = \"I_s_[\\$BLACK]\") . \"}<br />{{$tag}|html}\",\n        'EXPECTED'    => ($imgPath = SN_ROOT_VIRTUAL . 'skins/EpicBlue/planeten/small/s_black_moon.jpg') . \"<br /><img src=\\\"{$imgPath}\\\" />\",\n        'DESCRIPTION' => 'DEFINE-d and prefix test',\n      ),\n\n      array(\n        'SAMPLE'      => '{R_[RENDER_NAVBAR_RESEARCH]}',\n        'EXPECTED'    => '<img src=\"' . SN_ROOT_VIRTUAL . 'design/images/navbar_research_64x64.png\"/>',\n        'DESCRIPTION' => 'Re-rendering image',\n      ),\n\n      array('HEADER' => 'Blocks'),\n    );\n\n//    $tests = array(\n//      array('HEADER' => '!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!'),\n//      array(\n//        'SAMPLE'      => '{L_admin_ptl_test_la_}',\n//        'EXPECTED'    => 'Single\\'Double\"ZeroEnd',\n//        'DESCRIPTION' => 'Language string',\n//      ),\n//    );\n\n    SN::$config->__ptl_test = 'config_ptl_test';\n    SN::$config->__ptl_test_array = array('value' => 'config_ptl_test_array');\n\n    foreach ($tests as $test) {\n      $test['CONSTRUCTION'] = str_replace(array('{', '}'), array('&#123;', '&#125;'), $test['SAMPLE']);\n      $template->assign_block_vars('test', $test);\n    }\n\n    SnTemplate::display($template);\n  break;\n}\n\n$template = SnTemplate::gettemplate(\"admin/admin_tools\", true);\n$template->assign_vars(array(\n  'PAGE_HEADER' => $lang['adm_tools'],\n));\n\n$template->assign_recursive($template_result);\n\nSnTemplate::display($template);\n"
  },
  {
    "path": "admin/userlist.php",
    "content": "<?php\n/** @noinspection SqlResolve */\n/** @noinspection PhpUnnecessaryCurlyVarSyntaxInspection */\n/** @noinspection PhpDeprecationInspection */\n\n/**\n * Project \"SuperNova.WS\" copyright (c) 2009-2025 Gorlum\n * @version #46d0#\n **/\n\nuse Player\\PlayerStatic;\n\nconst INSIDE   = true;\nconst INSTALL  = false;\nconst IN_ADMIN = true;\n\nrequire('../common.' . substr(strrchr(__FILE__, '.'), 1));\n\nSnTemplate::messageBoxAdminAccessDenied(3);\n\nglobal $config, $lang, $user;\n\nif ($user['authlevel'] < 3) {\n  sys_redirect(SN_ROOT_VIRTUAL . 'admin/banned.php');\n}\n\nini_set('memory_limit', SN::$config->stats_php_memory ?: '256M');\n\nlng_include('admin');\n\n$is_players_online_page = defined('ADMIN_USER_OVERVIEW') && ADMIN_USER_OVERVIEW === true;\n\n$sort_fields = array(\n  SORT_ID              => 'id',\n  SORT_NAME            => 'username',\n  SORT_EMAIL           => 'email',\n  SORT_IP              => 'user_lastip',\n  SORT_TIME_REGISTERED => 'register_time',\n  SORT_TIME_LAST_VISIT => 'onlinetime',\n  SORT_TIME_BAN_UNTIL  => 'banaday',\n  SORT_REFERRAL_COUNT  => 'referral_count',\n  SORT_REFERRAL_DM     => 'referral_dm',\n  SORT_VACATION        => 'vacation',\n);\n\nif (empty(sys_get_param('sort'))) {\n  $sort = SORT_TIME_LAST_VISIT;\n  $desc = true;\n} else {\n  $sort = sys_get_param_int('sort', SORT_ID);\n  $sort = ($sort_fields[$sort] ?? null) ? $sort : SORT_ID;\n\n  $desc = (bool)(sys_get_param_int('desc') ? 1 : 0);\n}\n\nif (($action = sys_get_param_int('action')) && ($user_id = sys_get_param_id('uid')) && ($user_selected = db_user_by_id($user_id, false))) {\n  if ($user_selected['authlevel'] < $user['authlevel'] && $user['authlevel'] >= 3) {\n    switch ($action) {\n      case ACTION_DELETE:\n        PlayerStatic::DeleteSelectedUser($user_id);\n        sys_redirect(\"{$_SERVER['SCRIPT_NAME']}?sort={$sort}&desc=\" . (int)$desc);\n      break;\n\n      case ACTION_USE:\n        // Impersonate\n        SN::$auth->impersonate($user_selected);\n      break;\n    }\n  } else {\n    // Restricted try to delete user higher or equal level\n    SnTemplate::messageBoxAdmin($lang['adm_err_denied']);\n  }\n}\n\n/** @noinspection SpellCheckingInspection */\n$template = SnTemplate::gettemplate('admin/userlist', true);\n\n$multi_ip = array();\n$ip_query = db_user_list_admin_multi_accounts();\nwhile ($ip = db_fetch($ip_query)) {\n  $multi_ip[$ip['user_lastip']] = $ip['ip_count'];\n}\n\n$geoIp = geoip_status();\n\n$query = db_user_list_admin_sorted($sort_fields[$sort], $is_players_online_page, $desc);\nwhile ($user_row = db_fetch($query)) {\n  if ($user_row['banaday']) {\n    $ban_details = doquery(\"SELECT * FROM {{banned}} WHERE `ban_user_id` = {$user_row['id']} ORDER BY ban_id DESC LIMIT 1\", true);\n  } else {\n    $ban_details = [\n      'ban_time'        => 0,\n      'ban_issuer_name' => '',\n      'ban_reason'      => '',\n    ];\n  }\n\n  $geoIpInfo = $geoIp ? geoip_ip_info(ip2longu($user_row['user_lastip'])) : array();\n  foreach ($geoIpInfo as $key => $value) {\n    $geoIpInfo[strtoupper($key)] = $value;\n    unset($geoIpInfo[$key]);\n  }\n\n  $template->assign_block_vars('user', array(\n      'ID'              => $user_row['id'],\n      'NAME'            => $renderedNick = player_nick_render_to_html($user_row, ['player_rank' => true, 'vacancy' => true, 'birthday' => true, 'award' => true, NICK_RANK_NO_TEXT => true,]),\n      'NAME_HTML'       => htmlentities($user_row['username'], ENT_QUOTES, 'UTF-8'),\n      'IP'              => $user_row['user_lastip'],\n      'IP_MULTI'        => intval($multi_ip[$user_row['user_lastip']]),\n      'TIME_REGISTERED' => date(FMT_DATE_TIME_SQL, $user_row['register_time']),\n      'TIME_PLAYED'     => date(FMT_DATE_TIME_SQL, $user_row['onlinetime']),\n      'ACTIVITY'        => pretty_time(SN_TIME_NOW - $user_row['onlinetime']),\n      'REFERRAL_COUNT'  => $user_row['referral_count'],\n      'REFERRAL_DM'     => HelperString::numberFloorAndFormat($user_row['referral_dm']),\n      'BANNED'          => $user_row['banaday'] ? date(FMT_DATE_TIME_SQL, $user_row['banaday']) : 0,\n      'BAN_DATE'        => date(FMT_DATE_TIME_SQL, $ban_details['ban_time']),\n      'BAN_ISSUER'      => $ban_details['ban_issuer_name'],\n      'BAN_REASON'      => $ban_details['ban_reason'],\n      'METAMATTER'      => HelperString::numberFloorAndFormat($user_row['metamatter_total']),\n      'ACTION'          => $user_row['authlevel'] < $user['authlevel'],\n      'RESTRICTED'      => $user['authlevel'] < 3,\n      'EMAIL'           => $user_row['email_2'],\n      'VACATION'        => $user_row['vacation'] ? date(FMT_DATE_TIME_SQL, $user_row['vacation']) : '-',\n    ) + $geoIpInfo);\n}\n\n/** @noinspection SpellCheckingInspection */\n$isMetaMatter = !empty(SN::$gc->modules->getModule('unit_res_metamatter'));\n$template->assign_vars([\n  'USER_COUNT'      => SN::$db->db_num_rows($query),\n  'SORT'            => $sort,\n  'DESC'            => (int)$desc,\n  'GEOIP'           => $geoIp,\n  'METAMATTER'      => $isMetaMatter,\n  'GEOIP_WHOIS_URL' => SN::$config->geoip_whois_url,\n\n  'PAGE_URL'    => $_SERVER['SCRIPT_NAME'],\n  'PAGE_HEADER' => $is_players_online_page ? $lang['adm_ul_title_online'] : $lang['adm_ul_title'],\n]);\n\n/** @formatter:off */\n$template->assign_recursive([\n  '.'    => [\n    'header' => [\n      ['.' => ['header_row' => [\n        ['TITLE' => $lang['sys_id'],                 'ROWSPAN' => 2, 'COLSPAN' => 1, 'SORT' => SORT_ID,],\n        ['TITLE' => $lang['sys_user_name'],          'ROWSPAN' => 2, 'COLSPAN' => 1, 'SORT' => SORT_NAME,],\n        $isMetaMatter ? ['TITLE' => $lang['sys_metamatter_sh'],      'ROWSPAN' => 2, 'COLSPAN' => 1, 'SORT' => SORT_NONE,] : [],\n        ['TITLE' => $lang['adm_ul_referral'],        'ROWSPAN' => 1, 'COLSPAN' => 2, 'SORT' => SORT_NONE,],\n        ['TITLE' => $lang['adm_ul_time_registered'], 'ROWSPAN' => 2, 'COLSPAN' => 1, 'SORT' => SORT_TIME_REGISTERED,],\n        ['TITLE' => $lang['adm_ul_time_played'],     'ROWSPAN' => 2, 'COLSPAN' => 1, 'SORT' => SORT_TIME_LAST_VISIT,],\n        ['TITLE' => $lang['adm_ov_activ'],           'ROWSPAN' => 2, 'COLSPAN' => 1, 'SORT' => SORT_NONE,],\n        ['TITLE' => $lang['adm_ul_time_banned'],     'ROWSPAN' => 2, 'COLSPAN' => 1, 'SORT' => SORT_TIME_BAN_UNTIL,],\n        ['TITLE' => $lang['sys_vacation_in'],        'ROWSPAN' => 2, 'COLSPAN' => 1, 'SORT' => SORT_VACATION,],\n        ['TITLE' => $lang['adm_ul_mail'],            'ROWSPAN' => 2, 'COLSPAN' => 1, 'SORT' => SORT_EMAIL,],\n        ['TITLE' => $lang['sys_ip'],                 'ROWSPAN' => 2, 'COLSPAN' => 1, 'SORT' => SORT_IP,],\n        $geoIp ? ['TITLE' => $lang['geoip'],                  'ROWSPAN' => 2, 'COLSPAN' => 1, 'SORT' => SORT_NONE,] : [],\n        ['TITLE' => $lang['adm_sys_actions'],        'ROWSPAN' => 2, 'COLSPAN' => 1, 'SORT' => SORT_NONE,],\n      ]]],\n      ['.' => ['header_row' => [\n        ['TITLE' => $lang['adm_ul_players'], 'SORT' => SORT_REFERRAL_COUNT,],\n        ['TITLE' => $lang['adm_ul_dms'], 'SORT' => SORT_REFERRAL_DM,],\n      ]]],\n    ],\n  ],\n]);\n/** @formatter:on */\n\nSnTemplate::display($template);\n"
  },
  {
    "path": "affilates.php",
    "content": "<?php\n\n/**\n * affilates.php\n *\n * v2 (c) copyright 2010 by Gorlum for http://supernova.ws\n *  [~] Complies with PCG1\n * v1 (c) copyright 2010 by Gorlum for http://supernova.ws\n */\n\ninclude('common.' . substr(strrchr(__FILE__, '.'), 1));\n\nlng_include('affilates');\n\n$template = SnTemplate::gettemplate('affilates', true);\n\n$rpg_bonus_minimum = SN::$config->rpg_bonus_minimum;\n$rpg_bonus_divisor = SN::$config->rpg_bonus_divisor ? SN::$config->rpg_bonus_divisor : 10;\n\n$affilates = db_referrals_list_by_id($user['id']);\nwhile ($affilate = db_fetch($affilates))\n{\n  $affilate_gain = $affilate['dark_matter'] >= $rpg_bonus_minimum ? floor($affilate['dark_matter'] / $rpg_bonus_divisor) : 0;\n\n  $template->assign_block_vars('affilates', array(\n    'REGISTERED'  => date(FMT_DATE_TIME, $affilate['register_time']),\n    'USERNAME'    => $affilate['username'],\n    'DARK_MATTER' => $affilate['dark_matter'],\n    'GAINED'      => $affilate_gain,\n  ));\n\n  $gained += $affilate_gain;\n}\n\n$bannerURL  = SN_ROOT_VIRTUAL_PARENT . SN::$config->int_banner_URL;\n$bannerURL .= strpos($bannerURL, '?') ? '&' : '?';\n$bannerURL .= \"id={$user['id']}\";\n\n$userbarURL  = SN_ROOT_VIRTUAL_PARENT . SN::$config->int_userbar_URL;\n$userbarURL .= strpos($userbarURL, '?') ? '&' : '?';\n$userbarURL .= \"id={$user['id']}\";\n\n$template->assign_vars(array(\n  'GAINED'     => $gained,\n  'user_id'    => $user['id'],\n  'bannerURL'  => $bannerURL,\n  'userbarURL' => $userbarURL,\n));\n\nSnTemplate::display($template, $lang['aff_title']);\n"
  },
  {
    "path": "ajax_version_check.php",
    "content": "<?php\n\nrequire_once('includes/init.' . substr(strrchr(__FILE__, '.'), 1));\n\n\\Common\\Tools\\VersionCheckerDeprecated::handleCall();\n"
  },
  {
    "path": "alliance.php",
    "content": "<?php\n\nuse Alliance\\Alliance;\nuse Pages\\Helpers\\PageHelperAlly;\n\ninclude('common.' . substr(strrchr(__FILE__, '.'), 1));\n\nif (SN::$config->game_mode == GAME_BLITZ) {\n  SnTemplate::messageBox($lang['sys_blitz_page_disabled'], $lang['sys_error'], 'overview.php', 10);\n  die();\n}\n\ndefine('SN_IN_ALLY', true);\n\n// Main admin page save themes\nlng_include('alliance');\n\n$mode = sys_get_param_str('mode');\n\nif ($mode == 'ainfo') {\n  include('includes/alliance/ali_info.inc');\n}\n\nif (!$user['ally_id']) {\n  $user_request = doquery(\"SELECT * FROM {{alliance_requests}} WHERE `id_user` ='{$user['id']}' LIMIT 1;\", '', true);\n  if ($user_request['id_user']) {\n    require('includes/alliance/ali_external_request.inc');\n  } else {\n    switch ($mode) {\n      case 'search':\n        PageHelperAlly::pageExternalSearch($mode, $user, $lang);\n      break;\n\n      case 'apply':\n        require('includes/alliance/ali_external_request.inc');\n      break;\n\n      case 'make':\n        require('includes/alliance/ali_external_create_ally.inc');\n      break;\n\n      default:\n        $parsetemplate = SnTemplate::gettemplate('ali_external', true);\n        PageHelperAlly::externalSearchRecommend($user, $parsetemplate);\n        SnTemplate::display($parsetemplate, $lang['alliance']);\n      break;\n    }\n  }\n}\n\nAlliance::sn_ali_fill_user_ally($user);\n//$ally = doquery(\"SELECT * FROM {{alliance}} WHERE `id` ='{$user['ally_id']}'\", '', true);\nif (!isset($user['ally'])) {\n  db_user_set_by_id($user['id'], \"`ally_id` = null, `ally_name` = null, `ally_register_time` = 0, `ally_rank_id` = 0\");\n  SnTemplate::messageBox($lang['ali_sys_notFound'], $lang['your_alliance'], 'alliance.php');\n}\n$ally = &$user['ally'];\n/*\n$ally_rights = array(\n  0 => 'name',\n  1 => 'mail',\n  2 => 'online',\n  3 => 'invite',\n  4 => 'kick',\n  5 => 'admin',\n  6 => 'forum',\n  7 => 'diplomacy'\n);\n*/\n$rights_old = array(\n  0 => 'name',\n  1 => 'mails',\n  2 => 'onlinestatus',\n  3 => 'bewerbungenbearbeiten',\n  4 => 'kick',\n  5 => 'rechtehand'\n);\n\n// This piece converting old ally data to new one\n//  unset($ally['ranklist']);\nif (!$ally['ranklist'] && $ally['ally_ranks']) {\n  $ally_ranks = unserialize($ally['ally_ranks']);\n  $i = 0;\n  foreach ($ally_ranks as $rank_id => $rank) {\n    foreach ($ally_rights as $key => $value) {\n      $ranks[$i][$value] = $rank[$rights_old[$key]];\n    }\n    db_user_list_set_ally_deprecated_convert_ranks($user['ally_id'], $i, $rank_id);\n    $i++;\n  }\n\n  if (!empty($ranks)) {\n    ali_rank_list_save($ranks);\n  }\n}\n\n$ranks = Alliance::ally_get_ranks($ally);\n\n$isAllyOwner = $ally['ally_owner'] == $user['id'];\n$user_can_send_mails = $ranks[$user['ally_rank_id']]['mail'] || $isAllyOwner;\n$userCanPostForum = $ranks[$user['ally_rank_id']]['forum'] || $isAllyOwner;\n$user_onlinestatus = $ranks[$user['ally_rank_id']]['online'] || $isAllyOwner;\n$user_admin_applications = $ranks[$user['ally_rank_id']]['invite'] || $isAllyOwner;\n$user_can_kick = $ranks[$user['ally_rank_id']]['kick'] || $isAllyOwner;\n$user_can_negotiate = $ranks[$user['ally_rank_id']]['diplomacy'] || $isAllyOwner;\n$user_can_edit_rights = $user_admin = $ranks[$user['ally_rank_id']]['admin'] || $isAllyOwner;\n\n$edit = sys_get_param_str('edit');\nSN::$gc->pimp->allyInternalMainModel();\nswitch ($mode) {\n  case 'admin':\n    if (!array_key_exists($edit, $sn_ali_admin_internal)) {\n      $edit = 'default';\n    }\n    if ($sn_ali_admin_internal[$edit]['include']) {\n      require(\"includes/{$sn_ali_admin_internal[$edit]['include']}\");\n    }\n    if (isset($sn_ali_admin_internal[$edit]['function']) && is_callable($sn_ali_admin_internal[$edit]['function'])) {\n      call_user_func($sn_ali_admin_internal[$edit]['function']);\n    }\n  break;\n\n  case 'memberslist':\n    require('includes/alliance/ali_internal_members.inc');\n  break;\n  case 'circular':\n    require('includes/alliance/ali_internal_admin_mail.inc');\n  break;\n  default:\n    require('includes/alliance/ali_info.inc');\n  break;\n}\n\nfunction ally_pre_call() {\n  $func_args = func_get_args();\n\n  return sn_function_call('ally_pre_call', $func_args);\n}\n"
  },
  {
    "path": "annonce.php",
    "content": "<?php\n\n/**\n * annonce.php\n *\n * Announces for trading between players\n *\n * @version 1.0s - Security checked for SQL-injection by Gorlum for http://supernova.ws\n * @version 1.0\n * @copyright 2008 by ??????? for XNova\n */\n\ninclude('common.' . substr(strrchr(__FILE__, '.'), 1));\n\n$users = db_user_by_id($user['id'], false);\n$action = sys_get_param_int('action');\n$GET_id = sys_get_param_int('id');\n\nlng_include('announce');\n\nswitch ($action) {\n  case 1://on veut poster une annonce\n    $page .= '<HTML>\n    <center>\n    <br>\n    <table width=\"600\">\n    <td class=\"c\" colspan=\"10\" align=\"center\"><b><font color=\"white\">' . $lang['Classifieds'] . '</font></b></td></tr>\n    <form action=\"annonce.php?action=2\" method=\"post\">\n    <td class=\"c\" colspan=\"10\" align=\"center\"><b>' . $lang['Resources_to_be_sold'] . '</font></b></td>\n    <tr><th colspan=\"5\">' . $lang['metal'] . '</th><th colspan=\"5\"><input type=\"texte\" value=\"0\" name=\"metalvendre\" /></th></tr>\n    <tr><th colspan=\"5\">' . $lang['crystal'] . '</th><th colspan=\"5\"><input type=\"texte\" value=\"0\" name=\"cristalvendre\" /></th></tr>\n    <tr><th colspan=\"5\">' . $lang['deuterium'] . '</th><th colspan=\"5\"><input type=\"texte\" value=\"0\" name=\"deutvendre\" /></th></tr>\n\n    <td class=\"c\" colspan=\"10\" align=\"center\"><b>' . $lang['Desired_resources'] . '</font></b></td></tr>\n    <tr><th colspan=\"5\">' . $lang['metal'] . '</th><th colspan=\"5\"><input type=\"texte\" value=\"0\" name=\"metalsouhait\" /></th></tr>\n    <tr><th colspan=\"5\">' . $lang['crystal'] . '</th><th colspan=\"5\"><input type=\"texte\" value=\"0\" name=\"cristalsouhait\" /></th></tr>\n    <tr><th colspan=\"5\">' . $lang['deuterium'] . '</th><th colspan=\"5\"><input type=\"texte\" value=\"0\" name=\"deutsouhait\" /></th></tr>\n    <tr><th colspan=\"10\"><input type=\"submit\" value=\"' . $lang['send'] . '\" /></th></tr>\n\n    <form>\n    </table>\n    </HTML>';\n\n    SnTemplate::display($page);\n  break;\n\n  case 2:// On vient d'envoyer une annonce, on l'enregistre et on affiche un message comme quoi on l'a bien fait\n    $metalvendre = sys_get_param_float('metalvendre');\n    $metalsouhait = sys_get_param_float('metalsouhait');\n    $cristalvendre = sys_get_param_float('cristalvendre');\n    $cristalsouhait = sys_get_param_float('cristalsouhait');\n    $deutvendre = sys_get_param_float('deutvendre');\n    $deutsouhait = sys_get_param_float('deutsouhait');\n\n    if (($metalvendre != 0 && $metalsouhait == 0) || ($cristalvendre != 0 && $cristalsouhait == 0) || ($deutvendre != 0 && $deutsouhait == 0)) {\n      doquery(\"INSERT INTO {{annonce}} SET `user` ='{$users['username']}', `galaxie` ='{$users['galaxy']}', `systeme` ='{$users['system']}', `metala` ='{$metalvendre}', `cristala` ='{$cristalvendre}', `deuta` ='{$deutvendre}', `metals` ='{$metalsouhait}', `cristals` ='{$cristalsouhait}', `deuts` ='{$deutsouhait}'\");\n      SnTemplate::messageBox($lang['Your_announce_was_recorded'], $lang['announce_status'], \"annonce.php\");\n    } else {\n      SnTemplate::messageBox($lang['Your_announce_not_recorded'], $lang['announce_status'], \"annonce.php?action=1\");\n    }\n  break;\n\n  case 3://Suppression d'annonce\n    doquery(\"DELETE FROM {{annonce}} WHERE `id` = {$GET_id}\");\n    SnTemplate::messageBox($lang['Your_announce_was_deleted'], $lang['announce_status'], \"annonce.php\");\n  break;\n\n  default://Sinon on affiche la liste des annonces\n    $annonce = doquery(\"SELECT * FROM {{annonce}} ORDER BY `id` DESC \");\n\n    $page2 = \"<HTML><center><br>\n    <table width=\\\"600\\\">\n    <td class=\\\"c\\\" colspan=\\\"10\\\"><font color=\\\"#FFFFFF\\\">{$lang['Classifieds']}</font></td></tr>\n    <tr><th colspan=\\\"3\\\">{$lang['Infos_of_delivery']}</th><th colspan=\\\"3\\\">{$lang['Resources_to_be_sold']}</th><th colspan=\\\"3\\\">{$lang['Desired_resources']}</th><th>{$lang['Action']}</th></tr>\n    <tr><th>{$lang['Salesman']}</th><th>{$lang['Galaxy']}</th><th>{$lang['Solar_system']}</th><th>{$lang['metal']}</th><th>{$lang['crystal']}</th><th>{$lang['deuterium']}</th><th>{$lang['metal']}</th><th>{$lang['crystal']}</th><th>{$lang['deuterium']}</th><th>{$lang['Delete']}</th></tr>\";\n\n    while ($b = db_fetch($annonce)) {\n      $page2 .= '<tr><th>';\n      foreach ($b as $name => $value) {\n        if ($name != 'id') {\n          $page2 .= $value;\n          $page2 .= '</th><th>';\n        }\n      }\n      $page2 .= ($b['user'] == $users['username']) ? \"<a href=\\\"annonce.php?action=3&id={$b[id]}\\\">X</a></th></tr>\" : \"</th></tr>\";\n    }\n\n    $page2 .= \"<tr><th colspan=\\\"10\\\" align=\\\"center\\\"><a href=\\\"annonce.php?action=1\\\">{$lang['add_announce']}</a></th></tr></td></table></HTML>\";\n\n    SnTemplate::display($page2);\n  break;\n}\n\n// Créé par Tom1991 Copyright 2008\n// Modifié par BenjaminV\n"
  },
  {
    "path": "announce.php",
    "content": "<?php\n/** @noinspection PhpUnnecessaryCurlyVarSyntaxInspection */\n\n/**\n * announce.php\n *\n * @copyright (c) 2010-2016 Gorlum for http://supernova.ws\n */\n\n$allow_anonymous = true;\ninclude('common.' . substr(strrchr(__FILE__, '.'), 1));\n\nglobal $config, $lang;\n\nnws_mark_read($user);\n$template = SnTemplate::gettemplate('announce', true);\n\n$announce_id = sys_get_param_id('id');\n$text = sys_get_param_str('text');\n$announce_time = sys_get_param_str('dtDateTime');\n$detail_url = sys_get_param_str('detail_url');\n$mode = sys_get_param_str('mode');\n\n$survey_answers = sys_get_param('survey_answers');\n$annQuery = '';\n\n$announce = array();\nif ($user['authlevel'] >= 3) {\n  if (!empty($text)) {\n    $announce_time = strtotime($announce_time, SN_TIME_NOW);\n    $announce_time = $announce_time ?: SN_TIME_NOW;\n\n    if ($mode == 'edit') {\n      /** @noinspection SqlResolve */\n      doquery(\"UPDATE `{{announce}}` SET `tsTimeStamp` = FROM_UNIXTIME({$announce_time}), `strAnnounce`='{$text}', detail_url = '{$detail_url}' WHERE `idAnnounce`={$announce_id};\");\n      /** @noinspection SqlResolve */\n      doquery(\"DELETE FROM `{{survey}}` WHERE `survey_announce_id` = {$announce_id};\");\n    } else {\n      /** @noinspection SqlResolve */\n      doquery(\"INSERT INTO `{{announce}}`\n        SET `tsTimeStamp` = FROM_UNIXTIME({$announce_time}), `strAnnounce`='{$text}', detail_url = '{$detail_url}',\n        `user_id` = {$user['id']}, `user_name` = '\" . SN::$db->db_escape($user['username']) . \"'\");\n      $announce_id = SN::$db->db_insert_id();\n    }\n    if (($survey_question = sys_get_param_str('survey_question')) && $survey_answers) {\n      $survey_until = strtotime($survey_until = sys_get_param_str('survey_until'), SN_TIME_NOW);\n      /** @noinspection PhpIdempotentOperationInspection */\n      $survey_until = date(FMT_DATE_TIME_SQL, $survey_until ?: SN_TIME_NOW + PERIOD_DAY * 1);\n      /** @noinspection SqlResolve */\n      doquery(\"INSERT INTO `{{survey}}` SET `survey_announce_id` = {$announce_id}, `survey_question` = '{$survey_question}', `survey_until` = '{$survey_until}'\");\n      $survey_id = SN::$db->db_insert_id();\n\n      // To remove difference between Linux/Windows/OsX/etc. browsers\n      $survey_answers = nl2br($survey_answers);\n      $survey_answers = explode('<br />', $survey_answers);\n      foreach ($survey_answers as $survey_answer) {\n        $survey_answer = SN::$db->db_escape(trim($survey_answer));\n        if ($survey_answer) {\n          /** @noinspection SqlResolve */\n          doquery(\"INSERT INTO `{{survey_answers}}` SET `survey_parent_id` = {$survey_id}, `survey_answer_text` = '{$survey_answer}'\");\n        }\n      }\n    }\n\n    if ($announce_time <= SN_TIME_NOW) {\n      if ($announce_time > SN::$config->var_news_last && $announce_time == SN_TIME_NOW) {\n        SN::$config->db_saveItem('var_news_last', $announce_time);\n      }\n\n      if (sys_get_param_int('news_mass_mail')) {\n        $text = sys_get_param('text') . ($detail_url ? \" <a href=\\\"{$detail_url}\\\"><span class=\\\"positive\\\">{$lang['news_more']}</span></a>\" : '');\n        msg_send_simple_message('*', 0, 0, MSG_TYPE_ADMIN, $lang['sys_administration'], $lang['news_title'], $text);\n      }\n    }\n\n    $mode = '';\n    $announce_id = 0;\n  }\n\n  $survey_answers = '';\n  switch ($mode) {\n    case 'del':\n      /** @noinspection SqlResolve */\n      doquery(\"DELETE FROM `{{announce}}` WHERE `idAnnounce` = {$announce_id} LIMIT 1;\");\n      $mode = '';\n    break;\n\n    /** @noinspection PhpMissingBreakStatementInspection */\n    case 'edit':\n      $template->assign_var('ID', $announce_id);\n    case 'copy':\n      /** @noinspection SqlResolve */\n      $announce = doquery(\n        \"SELECT a.*, s.survey_id, s.survey_question, s.survey_until\n        FROM `{{announce}}` AS a\n        LEFT JOIN `{{survey}}` AS s ON s.survey_announce_id = a.idAnnounce\n        WHERE `idAnnounce` = {$announce_id} LIMIT 1;\", true);\n      if ($announce['survey_id']) {\n        /** @noinspection SqlResolve */\n        $query = doquery(\"SELECT survey_answer_text FROM `{{survey_answers}}` WHERE survey_parent_id = {$announce['survey_id']};\");\n        $survey_answers_array = [];\n        while ($row = db_fetch($query)) {\n          $survey_answers_array[] = $row['survey_answer_text'];\n        }\n        $survey_answers = implode(\"\\n\", $survey_answers_array);\n      }\n    break;\n\n    default:\n      if ($announce_id) {\n        $annQuery = \"AND `idAnnounce` = {$announce_id} \";\n      }\n    break;\n  }\n} else {\n  $annQuery = 'AND UNIX_TIMESTAMP(`tsTimeStamp`) <= ' . SN_TIME_NOW . ' ';\n\n  if ($announce_id) {\n    $annQuery .= \"AND `idAnnounce` = {$announce_id} \";\n  }\n}\n\n/** @noinspection PhpRedundantOptionalArgumentInspection */\nnws_render($user, $template, $annQuery, 20);\n\n$template->assign_vars([\n  'PAGE_HEADER'     => $lang['news_title'],\n  'AUTHLEVEL'       => $user['authlevel'],\n  'MODE'            => $mode,\n  'ANNOUNCE_ID'     => $announce_id,\n  'tsTimeStamp'     => $announce['tsTimeStamp'],\n  'strAnnounce'     => $announce['strAnnounce'], // Obsolete ?\n  'strAnnounceJS'   => json_encode($announce['strAnnounce']),\n  'DETAIL_URL'      => $announce['detail_url'],\n  'SURVEY_QUESTION' => $announce['survey_question'],\n  'SURVEY_UNTIL'    => $announce['survey_until'],\n  'SURVEY_ANSWERS'  => $survey_answers,\n]);\n\nSnTemplate::display($template, $lang['news_title']);\n"
  },
  {
    "path": "artifacts.php",
    "content": "<?php\n\n/**\n * artifacts.php\n * Artifact actions\n *\n * @package roleplay\n * @version 1.0\n *\n * Revision History\n * ================\n * 1.0 copyright (c) 2011 by Gorlum for http://supernova.ws\n *\n */\n\n\nuse DBAL\\db_mysql;\nuse Unit\\DBStaticUnit;\n\nglobal $lang, $user, $planetrow;\n\ninclude('common.' . substr(strrchr(__FILE__, '.'), 1));\n\nlng_include('infos');\nlng_include('artifacts');\n\ninclude('includes/includes/art_artifact.php');\n\n$sn_group_artifacts = sn_get_groups('artifacts');\n\n/**\n * @param $user\n * @param $unit_id\n * @param $planetrow\n * @param $lang\n *\n * @return string\n */\nfunction art_buy($user, $unit_id, $planetrow, $lang) {\n  $Message = '';\n  db_mysql::db_transaction_start();\n\n  $user           = db_user_by_id($user['id'], true);\n  $artifact_level = mrc_get_level($user, array(), $unit_id, true);\n\n  $build_data     = eco_get_build_data($user, $planetrow, $unit_id, $artifact_level, true);\n  $darkmater_cost = $build_data[BUILD_CREATE][RES_DARK_MATTER];\n\n  // TODO: more correct check - with \"FOR UPDATE\"\n  if (mrc_get_level($user, null, RES_DARK_MATTER) >= $darkmater_cost) {\n    $unit_max_stack = get_unit_param($unit_id, P_MAX_STACK);\n    if (!isset($unit_max_stack) || $unit_max_stack > mrc_get_level($user, $planetrow, $unit_id)) {\n      if (!DBStaticUnit::dbUserAdd($user['id'], $unit_id, 1)) {\n        $Message = '{Ошибка записи в БД}';\n      } else {\n        rpg_points_change($user['id'], RPG_ARTIFACT, -($darkmater_cost), \"Spent for artifact {$lang['tech'][$unit_id]} ID {$unit_id}\");\n        db_mysql::db_transaction_commit();\n        sys_redirect(\"artifacts.php#{$unit_id}\");\n      }\n    } else {\n      $Message = $lang['off_maxed_out'];\n    }\n  } else {\n    $Message = $lang['sys_no_points'];\n  }\n  db_mysql::db_transaction_rollback();\n\n  return $Message;\n}\n\nif (($action = sys_get_param_int('action')) && in_array($unit_id = sys_get_param_int('unit_id'), $sn_group_artifacts)) {\n  $Message = '';\n  switch ($action) {\n    case ACTION_BUY:\n      $Message = art_buy($user, $unit_id, $planetrow, $lang);\n    break;\n\n    case ACTION_USE:\n      art_use($user, $planetrow, $unit_id);\n      sys_redirect(\"artifacts.php#{$unit_id}\");\n    break;\n  }\n  SnTemplate::messageBox($Message, $lang['tech'][UNIT_ARTIFACTS], 'artifacts.' . PHP_EX, 5);\n}\n\n$user = db_user_by_id($user['id'], true);\n\n$template = SnTemplate::gettemplate('artifacts', true);\n\nforeach ($sn_group_artifacts as $artifact_id) {\n  $artifact_level      = mrc_get_level($user, [], $artifact_id, true);\n  $build_data          = eco_get_build_data($user, $planetrow, $artifact_id, $artifact_level);\n  $artifact_data       = get_unit_param($artifact_id);\n  $artifact_data_bonus = SnTemplate::tpl_render_unit_bonus_data($artifact_data);\n\n  $template->assign_block_vars('artifact', array(\n    'ID'          => $artifact_id,\n    'NAME'        => $lang['tech'][$artifact_id],\n    'DESCRIPTION' => $lang['info'][$artifact_id]['description'],\n    'EFFECT'      => $lang['info'][$artifact_id]['effect'],\n    'COST'        => $build_data[BUILD_CREATE][RES_DARK_MATTER],\n    'LEVEL'       => intval($artifact_level),\n    'LEVEL_MAX'   => intval($artifact_data['max']),\n    'BONUS'       => $artifact_data_bonus,\n    'BONUS_TYPE'  => $artifact_data[P_BONUS_TYPE],\n    'CAN_BUY'     => $build_data['CAN'][BUILD_CREATE],\n  ));\n}\n\n$template->assign_vars(array(\n  'PAGE_HEADER' => $lang['tech'][UNIT_ARTIFACTS],\n  'PAGE_HINT'   => $lang['art_page_hint'],\n));\n\nSnTemplate::display($template, $lang['tech'][UNIT_ARTIFACTS]);\n"
  },
  {
    "path": "banned.php",
    "content": "<?php\n\n/**\n * banned.php\n *\n * List of all issued bans\n *\n * 2.0 (c) copyright 2010-2011 by Gorlum for http://supernova.ws\n *  [!] Full rewrite\n *  [~] Complies with PCG1\n *  [~] Utilize PTE\n * @version 1.0 Created by e-Zobar (XNova Team). All rights reversed (C) 2008\n *\n */\n\n$allow_anonymous = true;\ninclude('common.' . substr(strrchr(__FILE__, '.'), 1));\n\n$template = SnTemplate::gettemplate('banned_body', true);\n\n$query = doquery(\"SELECT * FROM {{banned}} ORDER BY `ban_id` DESC;\");\n$i=0;\nwhile($ban_row = db_fetch($query))\n{\n  $template->assign_block_vars('banlist', array(\n    'USER_NAME'   => $ban_row['ban_user_name'],\n    'REASON'      => $ban_row['ban_reason'],\n    'FROM'        => $ban_row['ban_time'] ? date(FMT_DATE_TIME, $ban_row['ban_time']) : '--',\n    'UNTIL'       => date(FMT_DATE_TIME, $ban_row['ban_until']),\n    'ISSUER_NAME' => $ban_row['ban_issuer_name']\n  ));\n  $i++;\n}\n\n$template->assign_var('BANNED_COUNT', $i);\nSnTemplate::display($template);\n"
  },
  {
    "path": "banner.php",
    "content": "<?php\n/**\n* banner.php\n* @version 1.0s - Security checks by Gorlum for http://supernova.ws\n* @version 1.0\n*\n* Simple wrapper for INT_createBanner.php\n* Create banner or userbar\n* banner.php?id=<userid>&type=<banner|userbar>&format=<png>\n*\n* @copyright 2010 by Gorlum for http://supernova.ws\n*\n*/\n\n$allow_anonymous = true;\ninclude('includes/init.' . substr(strrchr(__FILE__, '.'), 1));\n\nlng_include('overview');\n\n$id = sys_get_param_id('id');\n$type = sys_get_param_str('type', 'userbar');\n$format = sys_get_param_str('format', 'png');\n\nint_banner_create($id, $type, $format);\n"
  },
  {
    "path": "blitz_register.php",
    "content": "<?php\n\nuse DBAL\\db_mysql;\nuse Universe\\Universe;\n\ninclude('common.' . substr(strrchr(__FILE__, '.'), 1));\n\nif ($user['authlevel'] < AUTH_LEVEL_DEVELOPER) {\n  $error_message = SN::$config->game_mode == GAME_BLITZ ? 'sys_blitz_page_disabled' : (\n  !SN::$config->game_blitz_register ? 'sys_blitz_registration_disabled' : ''\n  );\n\n  if ($error_message) {\n    SnTemplate::messageBox($lang[$error_message], $lang['sys_error'], 'overview.php', 10);\n    die();\n  }\n\n}\n\n$current_round = intval(SN::$config->db_loadItem('game_blitz_register_round'));\n$current_price = intval(SN::$config->db_loadItem('game_blitz_register_price'));\n\nif (SN::$config->db_loadItem('game_blitz_register') == BLITZ_REGISTER_OPEN && (sys_get_param_str('register_me') || sys_get_param_str('register_me_not'))) {\n  db_mysql::db_transaction_start();\n  $user          = db_user_by_id($user['id'], true);\n  $is_registered = doquery(\"SELECT `id` FROM {{blitz_registrations}} WHERE `user_id` = {$user['id']} AND `round_number` = {$current_round} FOR UPDATE;\", true);\n  if (sys_get_param_str('register_me')) {\n    if (empty($is_registered) && mrc_get_level($user, null, RES_METAMATTER) >= $current_price) {\n      doquery(\"INSERT IGNORE INTO {{blitz_registrations}} SET `user_id` = {$user['id']}, `round_number` = {$current_round};\");\n      //mm_points_change($user['id'], RPG_BLITZ_REGISTRATION, -$current_price, \"Регистрация в раунде {$current_round} Блица\");\n      SN::$auth->account->metamatter_change(RPG_BLITZ_REGISTRATION, -$current_price, \"Регистрация в раунде {$current_round} Блица\");\n    }\n  } elseif (sys_get_param_str('register_me_not') && !empty($is_registered)) {\n    doquery(\"DELETE FROM {{blitz_registrations}} WHERE `user_id` = {$user['id']} AND `round_number` = {$current_round};\");\n    // mm_points_change($user['id'], RPG_BLITZ_REGISTRATION_CANCEL, $current_price, \"Отмена регистрации в раунде {$current_round} Блица\");\n    SN::$auth->account->metamatter_change(RPG_BLITZ_REGISTRATION_CANCEL, $current_price, \"Отмена регистрации в раунде {$current_round} Блица\");\n  }\n  $registered_count = doquery(\"SELECT count(`id`) AS `count` FROM {{blitz_registrations}} WHERE `round_number` = {$current_round};\", true);\n  SN::$config->db_saveItem('game_blitz_register_users', $registered_count['count']);\n  db_mysql::db_transaction_commit();\n}\n\n$blitz_generated            = array();\n$blitz_result               = array();\n$blitz_prize_players_active = 0;\n$blitz_players              = 0;\n$blitz_prize_dark_matter    = 0;\n$blitz_prize_places         = 0;\nif ($user['authlevel'] >= AUTH_LEVEL_DEVELOPER) {\n  if (sys_get_param_str('generate')) {\n    $next_id = 0;\n    $query   = doquery(\"SELECT `id` FROM {{blitz_registrations}} WHERE `round_number` = {$current_round} ORDER BY RAND();\");\n    while ($row = db_fetch($query)) {\n      $next_id++;\n      $blitz_name     = 'Игрок' . $next_id;\n      $blitz_password = sys_random_string(8);\n      doquery(\"UPDATE {{blitz_registrations}} SET blitz_name = '{$blitz_name}', blitz_password = '{$blitz_password}' WHERE `id` = {$row['id']} AND `round_number` = {$current_round};\");\n    }\n  } elseif (sys_get_param_str('import_generated')) {\n    // ЭТО НА БЛИЦЕ!!!\n    doquery(\"DELETE FROM {{users}} WHERE username like 'Игрок%';\");\n    doquery(\"DELETE FROM {{planets}} WHERE id_owner not in (SELECT `id` FROM {{users}});\");\n\n    $imported_string = explode(';', sys_get_param_str('generated_string'));\n    shuffle($imported_string);\n\n    $new_players  = count($imported_string);\n    $system_count = ceil($new_players / SN::$config->game_maxGalaxy);\n    $system_step  = floor(SN::$config->game_maxSystem / $system_count);\n\n    pdump($system_count, '$system_count');\n    pdump($system_step, '$system_step');\n\n    $skin     = DEFAULT_SKINPATH;\n    $language = DEFAULT_LANG;\n\n    $galaxy = 1;\n    $system = $system_step;\n    $planet = round(SN::$config->game_maxPlanet / 2);\n\n    foreach ($imported_string as &$string_data) {\n      $string_data   = explode(',', $string_data);\n      $username_safe = $string_data[0];\n\n      $user_new = player_create($username_safe, sys_random_string(), array(\n        'password_encoded_unsafe' => core_auth::password_encode($string_data[1], ''),\n\n        'galaxy' => $galaxy,\n        'system' => $system,\n        'planet' => $planet,\n      ));\n\n      $moon_row = uni_create_moon($galaxy, $system, $planet, $user_new['id'], Universe::MOON_MAX_SIZE, false);\n\n      if (($system += $system_step) >= SN::$config->game_maxSystem) {\n        $galaxy++;\n        $system = $system_step;\n      }\n    }\n    doquery('UPDATE {{users}} SET dark_matter = 50000, dark_matter_total = 50000;');\n\n    SN::$config->db_saveItem('users_amount', SN::$config->users_amount + $new_players);\n    // generated_string\n  } elseif (sys_get_param_str('import_result') && ($blitz_result_string = sys_get_param_str('blitz_result_string'))) {\n    $blitz_result      = explode(';', $blitz_result_string);\n    $blitz_last_update = $blitz_result[0]; // Пока не используется\n    unset($blitz_result[0]);\n    foreach ($blitz_result as $blitz_result_data) {\n      $blitz_result_data = explode(',', $blitz_result_data);\n      if (count($blitz_result_data) == 5) {\n        $blitz_result_data[1] = SN::$db->db_escape($blitz_result_data[1]);\n        doquery(\n          \"UPDATE `{{blitz_registrations}}` SET\n            `blitz_player_id` = '{$blitz_result_data[0]}',\n            `blitz_online` = '{$blitz_result_data[2]}',\n            `blitz_place` = '{$blitz_result_data[3]}',\n            `blitz_points` = '{$blitz_result_data[4]}'\n          WHERE `blitz_name` = '{$blitz_result_data[1]}' AND `round_number` = {$current_round};\");\n      }\n    }\n    $blitz_result = array();\n  }\n\n  if (SN::$config->game_mode == GAME_BLITZ) {\n    $blitz_result = array(SN::$config->db_loadItem('var_stat_update'));\n    $query        = doquery(\"SELECT id, username, total_rank, total_points, onlinetime FROM {{users}} ORDER BY `id`;\");\n    while ($row = db_fetch($query)) {\n      $blitz_result[] = \"{$row['id']},{$row['username']},{$row['onlinetime']},{$row['total_rank']},{$row['total_points']}\";\n    }\n  } else {\n    $query = doquery(\"SELECT blitz_name, blitz_password, blitz_online FROM {{blitz_registrations}} WHERE `round_number` = {$current_round} ORDER BY `id`;\");\n    while ($row = db_fetch($query)) {\n      $blitz_generated[] = \"{$row['blitz_name']},{$row['blitz_password']}\";\n      $row['blitz_online'] ? $blitz_prize_players_active++ : false;\n      $blitz_players++;\n    }\n    $blitz_prize_dark_matter = $blitz_prize_players_active * 20000;\n    $blitz_prize_places      = ceil($blitz_prize_players_active / 5);\n    /*\n    'Игрок10'\n    'Игрок14'\n    'Игрок23'\n    'Игрок32'\n    'Игрок40'\n    */\n\n    if (sys_get_param_str('prize_calculate') && $blitz_prize_players_active && ($blitz_prize_dark_matter_actual = sys_get_param_int('blitz_prize_dark_matter'))) {\n      // $blitz_prize_dark_matter_actual = sys_get_param_int('blitz_prize_dark_matter');\n      $blitz_prize_places_actual = sys_get_param_int('blitz_prize_places');\n      db_mysql::db_transaction_start();\n      $query = doquery(\"SELECT * FROM {{blitz_registrations}} WHERE `round_number` = {$current_round} ORDER BY `blitz_place` FOR UPDATE;\");\n      while ($row = db_fetch($query)) {\n        if (!$row['blitz_place']) {\n          continue;\n        }\n\n        $blitz_prize_dark_matter_actual = round($blitz_prize_dark_matter_actual / 2);\n        $blitz_prize_places_actual--;\n\n        $reward = $blitz_prize_dark_matter_actual - $row['blitz_reward_dark_matter'];\n        pdump(\"{{$row['id']}} {$row['blitz_name']}, Place {$row['blitz_place']}, Prize places {$blitz_prize_places_actual}, Prize {$reward}\", $row['id']);\n        if ($reward) {\n          rpg_points_change($row['user_id'], RPG_BLITZ, $reward, sprintf(\n            $lang['sys_blitz_reward_log_message'], $row['blitz_place'], $row['blitz_name']\n          ));\n          doquery(\"UPDATE {{blitz_registrations}} SET blitz_reward_dark_matter = blitz_reward_dark_matter + ($reward) WHERE id = {$row['id']} AND `round_number` = {$current_round};\");\n        }\n\n        if (!$blitz_prize_places_actual || $blitz_prize_dark_matter_actual < 1000) {\n          break;\n        }\n      }\n      db_mysql::db_transaction_commit();\n    }\n\n  }\n}\n\n\n$template = SnTemplate::gettemplate('blitz_register', true);\n\n$player_registered = false;\n$query             = doquery(\n  \"SELECT u.*, br.blitz_name, br.blitz_password, br.blitz_place, br.blitz_status, br.blitz_points, br.blitz_reward_dark_matter\n    FROM {{blitz_registrations}} AS br\n    JOIN {{users}} AS u ON u.id = br.user_id\n  WHERE br.`round_number` = {$current_round}\n  order by `blitz_place`, `timestamp`;\");\nwhile ($row = db_fetch($query)) {\n  $tpl_player_data = array(\n    'NAME' => player_nick_render_to_html($row, array('icons' => true, 'color' => true, 'ally' => true)),\n  );\n\n  if (SN::$config->game_blitz_register == BLITZ_REGISTER_DISCLOSURE_NAMES) {\n    // Вот так хитро, что бы не было не единого шанса попадания на страницу данных об игроках Блиц-сервера до закрытия раунда\n    $tpl_player_data = array_merge($tpl_player_data, array(\n      'ID'                       => $row['id'],\n      'BLITZ_NAME'               => $row['blitz_name'],\n      // 'BLITZ_STATUS' => $row['blitz_status'],\n      'BLITZ_PLACE'              => $row['blitz_place'],\n      'BLITZ_POINTS'             => $row['blitz_points'],\n      'BLITZ_REWARD_DARK_MATTER' => $row['blitz_reward_dark_matter'],\n    ));\n  }\n\n  $template->assign_block_vars('registrations', $tpl_player_data);\n  if ($row['id'] == $user['id']) {\n    $player_registered = $row;\n  }\n}\n\n$template->assign_vars(array(\n  'GAME_BLITZ' => SN::$config->game_mode == GAME_BLITZ,\n\n  'REGISTRATION_OPEN'             => SN::$config->game_blitz_register == BLITZ_REGISTER_OPEN,\n  'REGISTRATION_CLOSED'           => SN::$config->game_blitz_register == BLITZ_REGISTER_CLOSED,\n  'REGISTRATION_SHOW_LOGIN'       => SN::$config->game_blitz_register == BLITZ_REGISTER_SHOW_LOGIN,\n  'REGISTRATION_DISCLOSURE_NAMES' => SN::$config->game_blitz_register == BLITZ_REGISTER_DISCLOSURE_NAMES,\n\n  'PLAYER_REGISTERED' => !empty($player_registered),\n  'BLITZ_NAME'        => $player_registered['blitz_name'],\n  'BLITZ_PASSWORD'    => $player_registered['blitz_password'],\n\n  'BLITZ_GENERATED'            => implode(';', $blitz_generated),\n  'BLITZ_RESULT'               => implode(';', $blitz_result),\n  'BLITZ_PRIZE_PLAYERS_ACTIVE' => $blitz_prize_players_active,\n  'BLITZ_PRIZE_DARK_MATTER'    => $blitz_prize_dark_matter,\n  'BLITZ_PRIZE_PLACES'         => $blitz_prize_places,\n));\n\nSnTemplate::display($template, $lang['sys_blitz_global_button']);\n"
  },
  {
    "path": "buddy.php",
    "content": "<?php\n\n/**\n * buddy.php\n *   Friend system\n *\n * v3.0 Fully rewrote by Gorlum for http://supernova.ws\n *   [!] Full rewrote from scratch\n *\n * Idea from buddy.php Created by Perberos. All rights reversed (C) 2006\n * */\n\nuse DBAL\\db_mysql;\n\ninclude('common.' . substr(strrchr(__FILE__, '.'), 1));\n\nlng_include('buddy');\n\n$result = array();\ntry {\n  db_mysql::db_transaction_start();\n\n  if ($buddy_id = sys_get_param_id('buddy_id')) {\n    $buddy_row = doquery(\"SELECT BUDDY_SENDER_ID, BUDDY_OWNER_ID, BUDDY_STATUS FROM {{buddy}} WHERE `BUDDY_ID` = {$buddy_id} LIMIT 1 FOR UPDATE;\", true);\n    if (!is_array($buddy_row)) {\n      throw new exception('buddy_err_not_exist', ERR_ERROR);\n    }\n\n    switch ($mode = sys_get_param_str('mode')) {\n      case 'accept':\n        if ($buddy_row['BUDDY_SENDER_ID'] == $user['id']) {\n          throw new exception('buddy_err_accept_own', ERR_ERROR);\n        }\n\n        if ($buddy_row['BUDDY_OWNER_ID'] != $user['id']) {\n          throw new exception('buddy_err_accept_alien', ERR_ERROR);\n        }\n\n        if ($buddy_row['BUDDY_STATUS'] == BUDDY_REQUEST_ACTIVE) {\n          throw new exception('buddy_err_accept_already', ERR_WARNING);\n        }\n\n        if ($buddy_row['BUDDY_STATUS'] == BUDDY_REQUEST_DENIED) {\n          throw new exception('buddy_err_accept_denied', ERR_ERROR);\n        }\n\n        doquery(\"UPDATE {{buddy}} SET `BUDDY_STATUS` = \" . BUDDY_REQUEST_ACTIVE . \" WHERE `BUDDY_ID` = {$buddy_id} LIMIT 1;\");\n        if (SN::$db->db_affected_rows()) {\n          msg_send_simple_message($buddy_row['BUDDY_SENDER_ID'], $user['id'], SN_TIME_NOW, MSG_TYPE_PLAYER, $user['username'], $lang['buddy_msg_accept_title'],\n            sprintf($lang['buddy_msg_accept_text'], $user['username']));\n          db_mysql::db_transaction_commit();\n          throw new exception('buddy_err_accept_none', ERR_NONE);\n        } else {\n          throw new exception('buddy_err_accept_internal', ERR_ERROR);\n        }\n      break;\n\n      case 'delete':\n        if ($buddy_row['BUDDY_SENDER_ID'] != $user['id'] && $buddy_row['BUDDY_OWNER_ID'] != $user['id']) {\n          throw new exception('buddy_err_delete_alien', ERR_ERROR);\n        }\n\n        if ($buddy_row['BUDDY_STATUS'] == BUDDY_REQUEST_ACTIVE) // Existing friendship\n        {\n          $ex_friend_id = $buddy_row['BUDDY_SENDER_ID'] == $user['id'] ? $buddy_row['BUDDY_OWNER_ID'] : $buddy_row['BUDDY_SENDER_ID'];\n\n          msg_send_simple_message($ex_friend_id, $user['id'], SN_TIME_NOW, MSG_TYPE_PLAYER, $user['username'], $lang['buddy_msg_unfriend_title'],\n            sprintf($lang['buddy_msg_unfriend_text'], $user['username']));\n\n          doquery(\"DELETE FROM {{buddy}} WHERE `BUDDY_ID` = {$buddy_id} LIMIT 1;\");\n          db_mysql::db_transaction_commit();\n          throw new exception('buddy_err_unfriend_none', ERR_NONE);\n        } elseif ($buddy_row['BUDDY_SENDER_ID'] == $user['id']) // Player's outcoming request - either denied or waiting\n        {\n          doquery(\"DELETE FROM {{buddy}} WHERE `BUDDY_ID` = {$buddy_id} LIMIT 1;\");\n          db_mysql::db_transaction_commit();\n          throw new exception('buddy_err_delete_own', ERR_NONE);\n        } elseif ($buddy_row['BUDDY_STATUS'] == BUDDY_REQUEST_WAITING) // Deny incoming request\n        {\n          msg_send_simple_message($buddy_row['BUDDY_SENDER_ID'], $user['id'], SN_TIME_NOW, MSG_TYPE_PLAYER, $user['username'], $lang['buddy_msg_deny_title'],\n            sprintf($lang['buddy_msg_deny_text'], $user['username']));\n\n          doquery(\"UPDATE {{buddy}} SET `BUDDY_STATUS` = \" . BUDDY_REQUEST_DENIED . \" WHERE `BUDDY_ID` = {$buddy_id} LIMIT 1;\");\n          db_mysql::db_transaction_commit();\n          throw new exception('buddy_err_deny_none', ERR_NONE);\n        }\n      break;\n    }\n  }\n\n  // New request?\n  // Checking for user ID - in case if it was request from outside buddy system\n  if ($new_friend_id = sys_get_param_id('request_user_id')) {\n    $new_friend_row = db_user_by_id($new_friend_id, true);\n  } elseif ($new_friend_name = sys_get_param_str_unsafe('request_user_name')) {\n    $new_friend_row  = db_user_by_username($new_friend_name);\n    $new_friend_name = SN::$db->db_escape($new_friend_name);\n  }\n\n  if ($new_friend_row['id'] == $user['id']) {\n    unset($new_friend_row);\n    throw new exception('buddy_err_adding_self', ERR_ERROR);\n  }\n\n  // Checking for user name & request text - in case if it was request to adding new request\n  if (isset($new_friend_row['id']) && ($new_request_text = sys_get_param_str('request_text'))) {\n    $check_relation = doquery(\"SELECT `BUDDY_ID` FROM {{buddy}} WHERE\n      (`BUDDY_SENDER_ID` = {$user['id']} AND `BUDDY_OWNER_ID` = {$new_friend_row['id']})\n      OR\n      (`BUDDY_SENDER_ID` = {$new_friend_row['id']} AND `BUDDY_OWNER_ID` = {$user['id']})\n      LIMIT 1 FOR UPDATE;\"\n      , true);\n    if (isset($check_relation['BUDDY_ID'])) {\n      throw new exception('buddy_err_adding_exists', ERR_WARNING);\n    }\n\n    msg_send_simple_message($new_friend_row['id'], $user['id'], SN_TIME_NOW, MSG_TYPE_PLAYER, $user['username'], $lang['buddy_msg_adding_title'],\n      sprintf($lang['buddy_msg_adding_text'], $user['username']));\n\n    doquery($q = \"INSERT INTO {{buddy}} SET `BUDDY_SENDER_ID` = {$user['id']}, `BUDDY_OWNER_ID` = {$new_friend_row['id']}, `BUDDY_REQUEST` = '{$new_request_text}';\");\n    db_mysql::db_transaction_commit();\n    throw new exception('buddy_err_adding_none', ERR_NONE);\n  }\n} catch (exception $e) {\n  $result[] = array(\n    'STATUS'  => in_array($e->getCode(), array(ERR_NONE, ERR_WARNING, ERR_ERROR)) ? $e->getCode() : ERR_ERROR,\n    'MESSAGE' => $lang[$e->getMessage()],\n  );\n}\n// TODO - Это просто заглушка. Дойдут руки - разобраться, в чём проблема\ndb_mysql::db_transaction_rollback();\n\n$query = db_buddy_list_by_user($user['id']);\nwhile ($row = db_fetch($query)) {\n  $row['BUDDY_REQUEST'] = HelperString::nl2br($row['BUDDY_REQUEST']);\n\n  $row['BUDDY_ACTIVE']   = $row['BUDDY_STATUS'] == BUDDY_REQUEST_ACTIVE;\n  $row['BUDDY_DENIED']   = $row['BUDDY_STATUS'] == BUDDY_REQUEST_DENIED;\n  $row['BUDDY_INCOMING'] = $row['BUDDY_OWNER_ID'] == $user['id'];\n  $row['BUDDY_ONLINE']   = floor((SN_TIME_NOW - $row['onlinetime']) / 60);\n\n  $template_result['.']['buddy'][] = $row;\n}\n\n$template_result += array(\n  'PAGE_HEADER'       => $lang['buddy_buddies'],\n  'PAGE_HINT'         => $lang['buddy_hint'],\n  'USER_ID'           => $user['id'],\n  'REQUEST_USER_ID'   => isset($new_friend_row['id']) ? $new_friend_row['id'] : 0,\n  'REQUEST_USER_NAME' => isset($new_friend_row['username']) ? $new_friend_row['username'] : '',\n);\n\n$template_result['.']['result'] = is_array($template_result['.']['result']) ? $template_result['.']['result'] : array();\n$template_result['.']['result'] += $result;\n\n$template = SnTemplate::gettemplate('buddy', true);\n$template->assign_recursive($template_result);\n\nSnTemplate::display($template);\n"
  },
  {
    "path": "buildings.php",
    "content": "<?php\n\n/**\n * buildings.php\n *\n *  Allow building of... hmm... buildings\n *\n * @version 1.3s Security checks by Gorlum for http://supernova.ws\n * @version 1.3\n// History version\n// 1.0 - Nettoyage modularisation\n// 1.1 - Mise au point, mise en fonction pour linarisation du fonctionnement\n// 1.2 - Liste de construction batiments\n * @copyright 2008 by Chlorel for XNova\n */\n\ninclude('common.' . substr(strrchr(__FILE__, '.'), 1));\n\ndefine('SN_RENDER_NAVBAR_PLANET', true);\n\n$mode = sys_get_param_escaped('mode');\n$mode = (!$mode || $mode == 'buildings') ? QUE_STRUCTURES : ($mode == 'fleet' ? SUBQUE_FLEET : ($mode == 'defense' ? SUBQUE_DEFENSE : ($mode == 'research' ? QUE_RESEARCH : $mode)));\n\nif($building_sort = sys_get_param_id('sort_elements')) {\n  if(!empty($lang['player_option_building_sort'][$building_sort])) {\n    SN::$user_options[array(PLAYER_OPTION_BUILDING_SORT, $mode)] = $building_sort;\n    SN::$user_options[array(PLAYER_OPTION_BUILDING_SORT_INVERSE, $mode)] = sys_get_param_id('sort_elements_inverse', 0);\n  }\n  die();\n}\n\nlng_include('buildings');\nlng_include('infos');\n\n/** @noinspection PhpUnhandledExceptionInspection */\n$planet = SN::$gc->repoV2->getPlanet($planetrow['id']);\n$planet->sn_sys_sector_buy('buildings.php?mode=' . $mode);\n\nrequire_once('includes/includes/eco_bld_structures.php');\nswitch ($mode) {\n//  case UNIT_MERCENARIES:\n//    require_once('includes/includes/eco_bld_structures.php');\n//    eco_build(QUE_MERCENARY, $user, $planetrow);\n//  break;\n\n  case QUE_RESEARCH:\n    defined(\"GAME_RESEARCH_DISABLED\") && GAME_RESEARCH_DISABLED ? eco_build(SUBQUE_FLEET, $user, $planetrow) : eco_build(QUE_RESEARCH, $user, $planetrow);\n  break;\n\n  case SUBQUE_DEFENSE:\n  case SUBQUE_FLEET:\n    defined(\"GAME_DEFENSE_DISABLED\") && GAME_DEFENSE_DISABLED ? eco_build(SUBQUE_FLEET, $user, $planetrow) : eco_build($mode, $user, $planetrow);\n  break;\n\n  case QUE_STRUCTURES:\n  default:\n    defined(\"GAME_STRUCTURES_DISABLED\") && GAME_STRUCTURES_DISABLED ? eco_build(SUBQUE_FLEET, $user, $planetrow) : eco_build(QUE_STRUCTURES, $user, $planetrow);\n  break;\n}\n"
  },
  {
    "path": "captcha.php",
    "content": "<?php\n// Captcha code for registration - really didn't work alot :(\n\nsession_start();\n$en = 100;\n$boy = 25;\n$sayi = mt_rand(0, 9999999);\n$_SESSION['captcha'] = $sayi;\n$tuval = imagecreatetruecolor($en, $boy);\n$b = imagecolorallocate($tuval, 175, 238, 238);\n$s = imagecolorallocate($tuval, 0, 0, 0);\nimagefill($tuval, 0, 0, $s);\nimageline($tuval, 20, 50, $en, $boy, $b);\nimagestring($tuval, 3, 27, 7, $sayi, $b);\nheader(\"content-type:image/gif\");\nimagegif($tuval);\nimagedestroy($tuval);\n"
  },
  {
    "path": "classes/Account.php",
    "content": "<?php\n\nuse DBAL\\db_mysql;\nuse \\DBAL\\DbQuery;\n\n/**\n * User: Gorlum\n * Date: 24.08.2015\n * Time: 6:00\n */\nclass Account {\n  /**\n   * @var int\n   */\n  public $account_id = 0;\n  /**\n   * @var string\n   */\n  public $account_name = '';\n  /**\n   * @var string\n   */\n  public $account_password = '';\n  /**\n   * @var string\n   */\n  public $account_salt = '';\n  /**\n   * @var string\n   */\n  public $account_email = '';\n  /**\n   * @var int\n   */\n  public $account_email_verified = 0;\n  /**\n   * @var string\n   */\n  public $account_register_time = '';\n  /**\n   * @var string\n   */\n  public $account_language = '';\n\n  public $account_metamatter = 0;\n  public $account_metamatter_total = 0;\n\n  /**\n   * @var int\n   */\n  public $is_exists = 0;\n  /**\n   * @var int\n   */\n  public $is_loaded = 0;\n\n  /**\n   * @var db_mysql\n   */\n  public $db;\n\n  protected $sn_root_path = SN_ROOT_RELATIVE;\n  protected $cookie_name = SN_COOKIE;\n  protected $cookie_name_impersonate = SN_COOKIE_I;\n\n  protected $table_check = array(\n    'account' => 'account',\n    'log_metamatter' => 'log_metamatter',\n  );\n\n  public function reset() {\n    $this->account_id = 0;\n    $this->account_name = '';\n    $this->account_password = '';\n    $this->account_salt = '';\n    $this->account_email = '';\n    $this->account_email_verified = 0;\n    $this->account_register_time = '';\n    $this->account_language = '';\n\n    $this->is_exists = 0;\n    $this->is_loaded = 0;\n  }\n  public function __construct($db = null) {\n    $this->reset();\n\n    $this->db = is_object($db) ? $db : SN::$db;\n\n    $this->sn_root_path = SN_ROOT_RELATIVE;\n    $this->cookie_name = SN_COOKIE;\n    $this->cookie_name_impersonate = $this->cookie_name . AUTH_COOKIE_IMPERSONATE_SUFFIX;\n    $this->secret_word = SN::$sn_secret_word;\n\n    $snTableNames = $this->db->schema()->getSnTables();\n    foreach($this->table_check as $table_name) {\n      if(empty($snTableNames[$table_name])) {\n        die('Если вы видите это сообщение первый раз после обновления релиза - просто перегрузите страницу.<br />\n              В противном случае - сообщите Администрации сервера об ошибке.<br/>\n              Не хватает таблицы для работы системы авторизации: ' . $table_name);\n      }\n    }\n  }\n\n  // OK 4.5\n  public function password_check($password_unsafe) {\n    return $this->password_encode($password_unsafe, $this->account_salt) == $this->account_password;\n  }\n\n  /**\n   * Меняет пароль у аккаунта в БД\n   *\n   * @param      $old_password_unsafe\n   * @param      $new_password_unsafe\n   * @param null $salt_unsafe\n   *\n   * @return bool\n   */\n  // OK v4.6\n  public function password_change($old_password_unsafe, $new_password_unsafe, $salt_unsafe = null) {\n    if(!$this->password_check($old_password_unsafe)) {\n      return false;\n    }\n\n    $salt_unsafe === null ? $salt_unsafe = $this->password_salt_generate() : false;\n    $result = $this->db_set_password($new_password_unsafe, $salt_unsafe);\n\n    return $result;\n  }\n\n\n  /**\n   * Заполняет поля объекта значениями результата запроса\n   *\n   * @param array $row\n   *\n   * @return bool\n   */\n  // OK v4.5\n  public function assign_from_db_row($row) {\n    $this->reset();\n    if(empty($row) || !is_array($row)) {\n      return false;\n    }\n    $this->account_id = $row['account_id'];\n    $this->account_name = $row['account_name'];\n    $this->account_password = $row['account_password'];\n    $this->account_salt = $row['account_salt'];\n    $this->account_email = $row['account_email'];\n    $this->account_email_verified = $row['account_email_verified'];\n    $this->account_register_time = $row['account_register_time'];\n    $this->account_language = $row['account_language'];\n\n    $this->account_metamatter = $row['account_metamatter'];\n    $this->account_metamatter_total = $row['account_metamatter_total'];\n\n    $this->is_exists = 1;\n    $this->is_loaded = 1;\n\n    return true;\n  }\n\n  /**\n   * Возвращает аккаунт по его ID\n   *\n   * @param $account_id_unsafe\n   *\n   * @return bool\n   */\n  // OK v4.5\n  public function db_get_by_id($account_id_unsafe) {\n    $this->reset();\n\n    $account_id_safe = round(floatval($account_id_unsafe));\n\n    $account_row = $this->db->doQueryAndFetch(\"SELECT * FROM `{{account}}` WHERE `account_id` = {$account_id_safe}\");\n    return $this->assign_from_db_row($account_row);\n  }\n  /**\n   * Возвращает аккаунт по имени\n   *\n   * @param string $account_name_safe\n   *\n   * @return bool\n   */\n  // OK v4.5\n  public function db_get_by_name($account_name_unsafe) {\n    $this->reset();\n\n    $account_name_safe = $this->db->db_escape($account_name_unsafe);\n\n    $account_row = $this->db->doQueryAndFetch(\"SELECT * FROM `{{account}}` WHERE LOWER(`account_name`) = LOWER('{$account_name_safe}') FOR UPDATE\");\n    return $this->assign_from_db_row($account_row);\n  }\n  /**\n   * Возвращает аккаунт по емейлу\n   *\n   * @param string $email_unsafe\n   *\n   * @return bool\n   */\n  // OK v4.5\n  public function db_get_by_email($email_unsafe) {\n    $this->reset();\n\n    $email_safe = $this->db->db_escape($email_unsafe);\n    if($email_safe) {\n      $account_row = $this->db->doQueryAndFetch(\"SELECT * FROM `{{account}}` WHERE LOWER(`account_email`) = LOWER('{$email_safe}') FOR UPDATE;\");\n\n      return $this->assign_from_db_row($account_row);\n    } else {\n      return false;\n    }\n  }\n  /**\n   * Возвращает аккаунт по имени или аккаунту - проверка уникальных значений\n   *\n   * @param string $account_name_unsafe\n   * @param string $email_unsafe\n   *\n   * @return bool\n   *\n   */\n  // OK v4.5\n  public function db_get_by_name_or_email($account_name_unsafe, $email_unsafe) {\n    $this->reset();\n\n    $account_name_safe = $this->db->db_escape($account_name_unsafe);\n    $email_safe = $this->db->db_escape($email_unsafe);\n\n    $account = $this->db->doQueryAndFetch(\n      \"SELECT * FROM `{{account}}` \n      WHERE \n        LOWER(`account_name`) = LOWER('{$account_name_safe}') \n        OR LOWER(`account_name`) = LOWER('{$email_safe}') \n        OR LOWER(`account_email`) = LOWER('{$email_safe}') \n        FOR UPDATE\"\n    );\n    return $this->assign_from_db_row($account);\n  }\n\n  /**\n   * @param int|string $player_id_unsafe - player ID\n   *\n   * @return bool\n   */\n  public function dbGetByPlayerId($player_id_unsafe) {\n    $translation = PlayerToAccountTranslate::db_translate_get_account_by_user_id($player_id_unsafe, core_auth::$main_provider->provider_id);\n    if (empty($translation[$player_id_unsafe][core_auth::$main_provider->provider_id])) {\n      return false;\n    }\n\n    $account_translation = reset($translation[$player_id_unsafe][core_auth::$main_provider->provider_id]);\n    if (empty($account_translation['provider_account_id'])) {\n      return false;\n    }\n\n    return $this->db_get_by_id($account_translation['provider_account_id']);\n  }\n\n\n  /**\n   * Создает аккаунт\n   *\n   * @throws Exception\n   */\n  // OK v4.5\n  public function db_create($account_name_unsafe, $password_raw, $email_unsafe, $language_unsafe = null, $salt_unsafe = null) {\n    $this->reset();\n\n    $account_name_safe = $this->db->db_escape($account_name_unsafe);\n    $email_safe = $this->db->db_escape($email_unsafe);\n    $language_safe = $this->db->db_escape($language_unsafe === null ? DEFAULT_LANG : $language_unsafe);\n\n    $salt_unsafe === null ? $salt_unsafe = $this->password_salt_generate() : false;\n    $password_salted_safe = $this->db->db_escape($this->password_encode($password_raw, $salt_unsafe));\n    $salt_safe = $this->db->db_escape($salt_unsafe);\n\n    $result = $this->db->doquery(\n      \"INSERT INTO `{{account}}` SET\n        `account_name` = '{$account_name_safe}',\n        `account_password` = '{$password_salted_safe}',\n        `account_salt` = '{$salt_safe}',\n        `account_email` = LOWER('{$email_safe}'),\n        `account_language` = '{$language_safe}'\"\n    );\n    if(!$result) {\n      throw new Exception(REGISTER_ERROR_ACCOUNT_CREATE, ERR_ERROR);\n    }\n\n    if(!($account_id = $this->db->db_insert_id())) {\n      throw new Exception(REGISTER_ERROR_ACCOUNT_CREATE, ERR_ERROR);\n    }\n\n    return $this->db_get_by_id($account_id);\n  }\n\n  /**\n   * Физически меняет пароль аккаунта в БД\n   *\n   * @param string $password_unsafe\n   * @param string $salt_unsafe\n   *\n   * @return bool\n   */\n  // OK v4.5\n  public function db_set_password($password_unsafe, $salt_unsafe) {\n    $password_encoded_unsafe = $this->password_encode($password_unsafe, $salt_unsafe);\n    $password_encoded_safe = $this->db->db_escape($password_encoded_unsafe);\n\n    $account_id_safe = $this->db->db_escape($this->account_id);\n    $salt_safe = $this->db->db_escape($salt_unsafe);\n\n    $result = $this->db->doquery(\n      \"UPDATE `{{account}}` SET\n        `account_password` = '{$password_encoded_safe}',\n        `account_salt` = '{$salt_safe}'\n      WHERE `account_id` = '{$account_id_safe}'\"\n    ) ? true : false;\n\n    if($result) {\n      $result = $this->db_get_by_id($this->account_id);\n    }\n\n    return $result;\n  }\n\n\n\n  /**\n   * Просаливает пароль\n   *\n   * @param $password\n   * @param $salt\n   *\n   * @return string\n   */\n  // OK v4.5\n  protected function password_encode($password, $salt) {\n    return core_auth::password_encode($password, $salt);\n  }\n  /**\n   * Генерирует соль\n   *\n   * @return string\n   */\n  // OK v4.5\n  protected function password_salt_generate() {\n    return core_auth::password_salt_generate();\n  }\n\n  /**\n   * Вставляет запись об изменении количества ММ в лог ММ\n   *\n   * @param $comment\n   * @param $change_type\n   * @param $metamatter\n   *\n   * @return int|string\n   */\n  // OK 4.8\n  protected function db_mm_log_insert($comment, $change_type, $metamatter, $user_id_unsafe) {\n    $provider_id_safe = intval(core_auth::$main_provider->provider_id);\n    //$account_id_safe = $this->db->db_escape($this->account_id);\n    $account_id_safe = intval($this->account_id);\n    $account_name_safe = $this->db->db_escape($this->account_name);\n\n    // $user_id_safe = $this->db->db_escape(core_auth::$user['id']);\n    // $user_id_safe = intval(core_auth::$user['id']);\n    $user_id_safe = intval($user_id_unsafe);\n    $username_safe = !empty(core_auth::$user['username']) ? $this->db->db_escape(core_auth::$user['username']) : '';\n\n    $metamatter = round(floatval($metamatter));\n\n    $comment_safe = $this->db->db_escape($comment);\n\n    $server_name_safe = $this->db->db_escape(SN_ROOT_VIRTUAL);\n    $page_url_safe = $this->db->db_escape($_SERVER['SCRIPT_NAME']);\n\n    $this->db->doquery(\n      \"INSERT INTO `{{log_metamatter}}` \n      SET\n        `provider_id` = {$provider_id_safe},\n        `account_id` = {$account_id_safe},\n        `account_name` = '{$account_name_safe}',\n        `user_id` = {$user_id_safe},\n        `username` = '{$username_safe}',\n        `reason` = {$change_type},\n        `amount` = {$metamatter},\n        `comment` = '{$comment_safe}',\n        `server_name` = '{$server_name_safe}',\n        `page` = '{$page_url_safe}'\n      ;\"\n    );\n    $result = $this->db->db_insert_id();\n\n    return $result;\n  }\n\n  // OK 4.8\n\n  /**\n   * @param int               $change_type\n   * @param float             $metamatter\n   * @param string|array|bool $comment\n   * @param bool              $already_changed\n   *\n   * @return array|bool|int|mysqli_result|null|string\n   */\n  public function metamatter_change($change_type, $metamatter, $comment = false, $already_changed = false) {\n    global $debug, $mm_change_legit, $config;\n\n    if(!$this->is_exists || !($metamatter = round(floatval($metamatter)))) {\n      $debug->error('Ошибка при попытке манипуляции с ММ');\n      return false;\n    }\n\n    $account_id_safe = $this->db->db_escape($this->account_id);\n\n    $mm_change_legit = true;\n    // $sn_data_metamatter_db_name = pname_resource_name(RES_METAMATTER);\n    if($already_changed) {\n      $metamatter_total_delta = 0;\n      $result = -1;\n    } else {\n      $metamatter_total_delta = $metamatter > 0 ? $metamatter : 0;\n\n      $result = $this->db->doquery(\n        \"UPDATE `{{account}}`\n        SET\n          `account_metamatter` = `account_metamatter` + '{$metamatter}'\" .\n          ($metamatter_total_delta ? \", `account_immortal` = IF(`account_metamatter_total` + '{$metamatter_total_delta}' >= {$config->player_metamatter_immortal} AND `account_immortal` IS NULL, NOW(), `account_immortal`), `account_metamatter_total` = `account_metamatter_total` + '{$metamatter_total_delta}'\" : '') .\n        \" WHERE `account_id` = {$account_id_safe}\"\n      );\n      if(!$result) {\n        $debug->error(\"Error adjusting Metamatter for player ID {$this->account_id} (Player Not Found?) with {$metamatter}. Reason: {$comment}\", 'Metamatter Change', 402);\n      }\n      $result = SN::$db->db_affected_rows();\n\n      $this->awardImmortal($metamatter, $config);\n    }\n\n    if(empty(core_auth::$user['id'])) {\n      $user_list = PlayerToAccountTranslate::db_translate_get_users_from_account_list(core_auth::$main_provider->provider_id, $this->account_id);\n      reset($user_list);\n      $user_id_unsafe = key($user_list);\n    } else {\n      $user_id_unsafe = core_auth::$user['id'];\n    }\n    $user_id_safe = $this->db->db_escape($user_id_unsafe);\n\n    if(!$result) {\n      $debug->error(\"Error adjusting Metamatter for player ID {$this->account_id} (Player Not Found?) with {$metamatter}. Reason: {$comment}\", 'Metamatter Change', 402);\n    }\n\n    if(!$already_changed) {\n      $this->account_metamatter += $metamatter;\n      $this->account_metamatter_total += $metamatter_total_delta;\n    }\n\n    if(is_array($comment)) {\n      $comment = call_user_func_array('sprintf', $comment);\n    }\n\n    $result = $this->db_mm_log_insert($comment, $change_type, $metamatter, $user_id_unsafe);\n\n    if($metamatter > 0 && !empty($user_id_safe)) {\n      $old_referral = doquery(\"SELECT * FROM `{{referrals}}` WHERE `id` = {$user_id_safe} LIMIT 1 FOR UPDATE;\", '', true);\n      if($old_referral['id']) {\n        $dark_matter_from_metamatter = $metamatter * AFFILIATE_MM_TO_REFERRAL_DM;\n        doquery(\"UPDATE `{{referrals}}` SET dark_matter = dark_matter + '{$dark_matter_from_metamatter}' WHERE `id` = {$user_id_safe} LIMIT 1;\");\n        $new_referral = doquery(\"SELECT * FROM `{{referrals}}` WHERE `id` = {$user_id_safe} LIMIT 1;\", '', true);\n\n        $partner_bonus = floor($new_referral['dark_matter'] / $config->rpg_bonus_divisor) - ($old_referral['dark_matter'] >= $config->rpg_bonus_minimum ? floor($old_referral['dark_matter'] / $config->rpg_bonus_divisor) : 0);\n        if($partner_bonus > 0 && $new_referral['dark_matter'] >= $config->rpg_bonus_minimum) {\n          rpg_points_change($new_referral['id_partner'], RPG_REFERRAL_BOUGHT_MM, $partner_bonus, \"Incoming MM From Referral ID {$user_id_safe}\");\n        }\n      }\n    }\n\n    $mm_change_legit = false;\n    return $result;\n  }\n\n  /**\n   *\n   * @return bool\n   * @throws Exception\n   */\n  public function cookieSet($rememberMe = false, $domain = null) {\n    if(!$this->is_exists) {\n      throw new Exception(LOGIN_ERROR_NO_ACCOUNT_FOR_COOKIE_SET, ERR_ERROR);\n    }\n\n    $expire_time = $rememberMe ? SN_TIME_NOW + PERIOD_YEAR : 0;\n\n    $password_encoded = $this->password_encode_for_cookie($this->account_password);\n    $cookie = $this->account_id . AUTH_COOKIE_DELIMETER . $password_encoded . AUTH_COOKIE_DELIMETER . $rememberMe;\n//    $this->flog(\"cookie_set() - Устанавливаем куку {$cookie}\");\n\n    return sn_setcookie($this->cookie_name, $cookie, $expire_time, $this->sn_root_path, $domain);\n  }\n\n  /**\n   * Очищает куку аккаунта - совсем или восстанавливая куку текущего имперсонатора\n   */\n  // OK v4.1\n  public function cookieClear($domain = null) {\n    // Автоматически вообще-то - если установлена кука имперсонатора - то чистим обычную, а куку имперсонатора - копируем в неё\n    if(!empty($_COOKIE[$this->cookie_name_impersonate])) {\n      sn_setcookie($this->cookie_name, $_COOKIE[$this->cookie_name_impersonate], SN_TIME_NOW + PERIOD_YEAR, $this->sn_root_path, $domain);\n      sn_setcookie($this->cookie_name_impersonate, '', SN_TIME_NOW - PERIOD_WEEK, $this->sn_root_path, $domain);\n    } else {\n      sn_setcookie($this->cookie_name, '', SN_TIME_NOW - PERIOD_WEEK, $this->sn_root_path, $domain);\n    }\n  }\n\n  public function cookieLogin(&$rememberMe = false) {\n    // Пытаемся войти по куке\n    if(!empty($_COOKIE[$this->cookie_name])) {\n      if(count(explode(\"/%/\", $_COOKIE[$this->cookie_name])) < 4) {\n        list($account_id_unsafe, $cookie_password_hash_salted, $user_remember_me) = explode(AUTH_COOKIE_DELIMETER, $_COOKIE[$this->cookie_name]);\n      } else {\n        list($account_id_unsafe, $user_name, $cookie_password_hash_salted, $user_remember_me) = explode(\"/%/\", $_COOKIE[$this->cookie_name]);\n      }\n\n      if(\n        $this->db_get_by_id($account_id_unsafe)\n        && ($this->password_encode_for_cookie($this->account_password) == $cookie_password_hash_salted)\n      ) {\n        $rememberMe = intval($user_remember_me);\n\n        return true;\n      }\n    }\n\n    // Невалидная кука - чистим\n    $this->cookieClear();\n\n    return false;\n  }\n\n  protected function password_encode_for_cookie($password) {\n    return md5(\"{$password}--\" . $this->secret_word);\n  }\n\n  /**\n   * @param int|float   $metamatter\n   * @param classConfig $config\n   */\n  protected function awardImmortal($metamatter, $config) {\n    if(!is_object($awardModule = moduleAward())) {\n      return;\n    }\n    if ($this->account_metamatter + $metamatter >= $config->player_metamatter_immortal ) {\n      $account_translation = PlayerToAccountTranslate::db_translate_get_users_from_account_list(ACCOUNT_PROVIDER_LOCAL, $this->account_id);\n      if (!empty($account_translation)) {\n        reset($account_translation);\n        $thisUserId = key($account_translation);\n        if ($thisUserId) {\n          $thisUser = ['id' => $thisUserId];\n          if (!mrc_get_level($thisUser, [], UNIT_AWARD_MEMORY_IMMORTAL, true)) {\n            $awardModule->award($thisUserId, UNIT_AWARD_MEMORY, UNIT_AWARD_MEMORY_IMMORTAL);\n          }\n        }\n      }\n    }\n  }\n\n  /**\n   * @param string|array $where\n   * @param string|array $group\n   *\n   * @return bool|mysqli_result|null\n   */\n  public function getMetamatterSum($where = '', $group = '') {\n    if(is_array($where) && !empty($where)) {\n      $where = implode(' AND ', $where);\n    }\n    if(is_array($group) && !empty($group)) {\n      $group = implode(',', $group);\n    }\n\n    $sql = \"SELECT SUM(`amount`) as 'mm', reason, account_id, account_name, user_id, username\n          FROM `{{log_metamatter}}`\"\n      . (!empty($where) ? ' WHERE ' . $where : '')\n      . (!empty($group) ? ' GROUP BY ' . $group : '')\n    ;\n\n    return $this->db->doquery($sql);\n  }\n\n}\n"
  },
  {
    "path": "classes/AjaxController.php",
    "content": "<?php\n\nuse Pages\\IPage;\n\n/**\n * Created by Gorlum 10.02.2017 6:36\n */\nclass AjaxController {\n\n  /**\n   *\n   * Request format:\n   *  http://xxx/index.php?page=ajax&mode=MODE&action=ACTION\n   *\n   * Will call method Pages\\Page<MODE>.<ACTION>()\n   *\n   * @param template|null $template\n   *\n   * @return template|null\n   */\n  public static function controller($template = null) {\n    global $template_result;\n\n    define('IN_AJAX', true);\n\n    if(!is_object($template)) {\n      $template = SnTemplate::gettemplate('_ajax', true);\n    }\n\n    $template_result = array_merge($template_result, $merge = array(\n      'GLOBAL_DISPLAY_HEADER' => false,\n      'GLOBAL_DISPLAY_MENU' => false,\n      'GLOBAL_DISPLAY_NAVBAR' => false,\n\n      'IN_AJAX' => true,\n    ));\n\n    $template->assign_vars($merge);\n\n    !is_array($template_result['AJAX']) ? $template_result['AJAX'] = array() : false;\n\n    $mode = sys_get_param_str('mode');\n\n    if (\n      class_exists($className = 'Pages\\\\PageAjax' . ucfirst($mode))\n      ||\n      // Deprecated. Use 1st form! Only for back compatibility!\n      class_exists($className = 'Pages\\\\Page' . ucfirst($mode))\n    ) {\n      /**\n       * @var IPage $page\n       */\n      $page = new $className();\n      if(method_exists($page, 'loadParams')) {\n        $page->loadParams();\n      }\n\n      if(method_exists($page, $action = sys_get_param_str('action')) && $page->checkAction($action)) {\n        $result = $page->$action();\n        is_array($result) ? HelperArray::merge($template_result['AJAX'], $result) : false;\n      }\n    }\n\n    return $template;\n  }\n\n  /**\n   * @param template|null $template\n   *\n   * @return template|null\n   */\n  public static function view($template = null) {\n    global $template_result;\n\n    $template->assign_var('AJAX_RENDERED', json_encode($template_result['AJAX']));\n\n    return $template;\n  }\n\n}\n"
  },
  {
    "path": "classes/Alliance/Alliance.php",
    "content": "<?php\n/**\n * Created by Gorlum 28.11.2017 6:01\n */\n\nnamespace Alliance;\n\nuse Core\\GlobalContainer;\nuse DBAL\\db_mysql;\nuse \\Exception;\nuse \\HelperString;\nuse Player\\RecordPlayer;\nuse SN;\n\n/**\n * Class Alliance\n *\n * Implements Alliance entity\n *\n * @package Alliance\n */\nclass Alliance extends RecordAlliance {\n  const OWNER_INDEX = -1;\n  const DEFAULT_INDEX = 0;\n  const RIGHTS_ALL = [\n    0 => 'name',\n    1 => 'mail',\n    2 => 'online',\n    3 => 'invite',\n    4 => 'kick',\n    5 => 'admin',\n    6 => 'forum',\n    7 => 'diplomacy'\n  ];\n  const RIGHT_WEIGHTS = [\n    'mail'      => 3,\n    'online'    => 4,\n    'invite'    => 1,\n    'kick'      => 10,\n    'admin'     => 99,\n    'forum'     => 0,\n    'diplomacy' => 5,\n  ];\n\n\n  /**\n   * @var AllianceTitleList $titles\n   */\n  protected $titles;\n  /**\n   * @var AllianceMemberList $members\n   */\n  protected $members;\n\n  /**\n   * @param array $ally\n   *\n   * @return array\n   *\n   * @deprecated\n   */\n  public static function ally_get_ranks(&$ally) {\n    global $ally_rights;\n\n    $ranks = array();\n\n    if ($ally['ranklist']) {\n      $str_ranks = explode(';', $ally['ranklist']);\n      foreach ($str_ranks as $str_rank) {\n        if (!$str_rank) {\n          continue;\n        }\n\n        $tmp     = explode(',', $str_rank);\n        $rank_id = count($ranks);\n        foreach ($ally_rights as $key => $value) {\n          $ranks[$rank_id][$value] = $tmp[$key];\n        }\n      }\n    }\n\n    return $ranks;\n  }\n\n  /**\n   * @param array $user\n   *\n   * @deprecated\n   */\n  public static function sn_ali_fill_user_ally(&$user) {\n    if (!$user['ally_id']) {\n      return;\n    }\n\n    if (!isset($user['ally'])) {\n      $user['ally'] = doquery(\"SELECT * FROM {{alliance}} WHERE `id` = {$user['ally_id']} LIMIT 1;\", true);\n    }\n\n    if (!isset($user['ally']['player'])) {\n      $user['ally']['player'] = db_user_by_id($user['ally']['ally_user_id'], true, false);\n    }\n  }\n\n\n  /**\n   * Alliance constructor.\n   *\n   * @param GlobalContainer|null $services\n   */\n  public function __construct(GlobalContainer $services = null) {\n    parent::__construct($services);\n  }\n\n  /**\n   * @param array $properties\n   */\n  protected function fromProperties(array $properties) {\n    parent::fromProperties($properties);\n\n    $this->titles = new AllianceTitleList($this);\n  }\n\n  /**\n   * @return AllianceMemberList\n   */\n  public function getMemberList() {\n    if (!isset($this->members)) {\n      $this->members = new AllianceMemberList(static::$db, $this);\n    }\n\n    return $this->members;\n  }\n\n  /**\n   * List of titles\n   *\n   * @return AllianceTitleList\n   */\n  public function getTitleList() {\n    return $this->titles;\n  }\n\n  /**\n   * Pass alliance to a member\n   *\n   * @param AllianceMember $newOwnerMember\n   *\n   * @return bool\n   * @throws Exception\n   */\n  public function pass(AllianceMember $newOwnerMember) {\n    try {\n      db_mysql::db_transaction_start();\n\n      if ($newOwnerMember->isOwner()) {\n        throw new Exception('{ Указанный пользователь уже является владельцем указанного Альянса }', ERR_NOTICE);\n      }\n\n      if (!empty($oldOwnerMember = $this->members->getOwner())) {\n        if (!$oldOwnerMember->changeTitle($this->titles->getTitle(static::DEFAULT_INDEX))) {\n          throw new Exception('{ Ошибка изменения ранга у старого владельца }', ERR_ERROR);\n        }\n      }\n\n      if (!$newOwnerMember->changeTitle($this->titles->getTitle(static::OWNER_INDEX))) {\n        throw new Exception('{ Ошибка изменения ранга у нового владельца }', ERR_ERROR);\n      }\n\n      $this->ownerId = $newOwnerMember->getPlayerId();\n      if (!$this->update()) {\n        throw new Exception('{ Ошибка изменения владельца Альянса }', ERR_ERROR);\n      }\n\n      db_mysql::db_transaction_commit();\n    } catch (Exception $e) {\n      db_mysql::db_transaction_rollback();\n\n      throw $e;\n    }\n\n    return true;\n  }\n\n  /**\n   * Get Alliance owner\n   *\n   * @return AllianceMember|null\n   */\n  public function getOwner() {\n    $player = RecordPlayer::findById($this->ownerId);\n    $owner  = !empty($player) ? new AllianceMember($this, $player) : null;\n\n    return $owner;\n  }\n\n  /**\n   * @return array\n   */\n  public function asPtl() {\n//    $ownerName = $this->getMemberList()->getOwner() instanceof AllianceMember ? $this->getMemberList()->getOwner()->getMemberName() : '';\n\n    $owner     = $this->getOwner();\n    $ownerName = $owner instanceof AllianceMember ? $owner->getMemberName() : '';\n\n    return\n      $this->ptlArray()\n      + [\n        '.'                  => [\n          'title' => $this->titles->asPtl()\n        ],\n        'OWNER_NAME_SAFE'    => HelperString::htmlSafe($ownerName),\n        'CREATED_TEXT'       => date(FMT_DATE_TIME_SQL, $this->createdUnixTime),\n        'STAT_POINTS_TEXT'   => HelperString::numberFloorAndFormat($this->statPoints),\n        'DESCRIPTION_HTML'   => AllianceHelper::formatText($this->description),\n        'TEXT_INTERNAL_HTML' => AllianceHelper::formatText($this->textInternal),\n      ];\n  }\n\n  /**\n   * Get list of recommended alliances for player with specified player points\n   *\n   * @param $points\n   *\n   * @return \\mysqli_result|null\n   */\n  public static function recommend($points) {\n    $points = floatval($points);\n\n    $rate = 5;\n\n    $allies = doquery(\n      \"SELECT * \n      FROM {{alliance}} \n      WHERE \n        ally_request_notallow != 1 \n        AND ally_members > 1 \n        AND total_points / ally_members >= {$points} / {$rate} \n        AND total_points / ally_members <= {$points} * {$rate} \n      ORDER BY abs(total_points / ally_members - {$points}) LIMIT 10;\");\n\n    return $allies;\n  }\n\n}\n"
  },
  {
    "path": "classes/Alliance/AllianceHelper.php",
    "content": "<?php\n/**\n * Created by Gorlum 11.11.2018 4:57\n */\n\nnamespace Alliance;\n\nclass AllianceHelper {\n  protected static $patterns = [\n//    \"#\\[fc\\]([a-z0-9\\#]+)\\[/fc\\](.*?)\\[/f\\]#Ssi\" => '<font color=\"\\1\">\\2</font>',\n    \"#\\[fc\\]([a-z0-9\\#]+)\\[/fc\\](.*?)\\[/f\\]#Ssi\" => '<span style=\"color:\\1\">\\2</span>',\n    '#\\[img\\](.*?)\\[/img\\]#Smi'                  => '<img src=\"\\1\" alt=\"\\1\" style=\"border:0;\" />',\n//    \"#\\[fc\\]([a-z0-9\\#\\ \\[\\]]+)\\[/fc\\]#Ssi\"      => '<font color=\"\\1\">',\n//    \"#\\[/f\\]#Ssi\"                                => '</font>',\n    \"#\\[fc\\]([a-z0-9\\#\\ \\[\\]]+)\\[/fc\\]#Ssi\"      => '<span style=\"color:\\1\">',\n    \"#\\[/f\\]#Ssi\"                                => '</span>',\n  ];\n\n  public static function formatText($text) {\n    return nl2br(preg_replace(array_keys(self::$patterns), self::$patterns, $text));\n  }\n\n}\n"
  },
  {
    "path": "classes/Alliance/AllianceMember.php",
    "content": "<?php\n/**\n * Created by Gorlum 28.11.2017 11:15\n */\n\nnamespace Alliance;\n\n\nuse Player\\RecordPlayer;\n\n/**\n * Class AllianceMember\n *\n * Class represents member of Alliance\n *\n * @package Alliance\n */\nclass AllianceMember {\n\n  /**\n   * @var AllianceTitle $title\n   */\n  protected $title;\n  /**\n   * @var array $player\n   */\n  public $player;\n  /**\n   * @var Alliance $alliance\n   */\n  protected $alliance;\n\n\n  public function __construct(Alliance $alliance, RecordPlayer $player) {\n    $this->alliance = $alliance;\n    $this->player = $player;\n\n    $this->title = $this->alliance->getTitleList()->getTitle($this->isOwner() ? Alliance::OWNER_INDEX : $this->player->ally_rank_id);\n  }\n\n  /**\n   * Change title of member\n   *\n   * @param AllianceTitle $title\n   *\n   * @return bool\n   */\n  public function changeTitle(AllianceTitle $title) {\n    if(!$title instanceof AllianceTitle) {\n      return false;\n    }\n\n    $this->player->ally_rank_id = $title->index;\n\n    return $this->player->update();\n  }\n\n  /**\n   * Get player ID of member\n   *\n   * @return mixed\n   */\n  public function getPlayerId() {\n    return $this->player->id;\n  }\n\n  /**\n   * @return mixed\n   */\n  public function getMemberName() {\n    return $this->player->username;\n  }\n\n  /**\n   * @return bool\n   */\n  public function isOwner() {\n    return $this->getPlayerId() == $this->alliance->ownerId;\n  }\n\n  /**\n   * @return array\n   */\n  public function asPtl() {\n    return [\n      'PLAYER_ID'    => $this->getPlayerId(),\n      'PLAYER_NAME'  => $this->player->username,\n      'ONLINE'       => $this->player->onlinetime,\n      'ONLINE_SQL'   => date(FMT_DATE_TIME_SQL, $this->player->onlinetime),\n      'VACATION'     => $this->player->vacation,\n      'VACATION_SQL' => date(FMT_DATE_TIME_SQL, $this->player->vacation),\n      'BAN'          => $this->player->banaday,\n      'BAN_SQL'      => date(FMT_DATE_TIME_SQL, $this->player->banaday),\n      'TITLE'        => $this->title->name,\n      'TITLE_ID'     => $this->title->index,\n      'RIGHTS'       => $this->title->rightsAsString(),\n      'OWNER'        => $this->isOwner(),\n    ];\n  }\n\n}\n"
  },
  {
    "path": "classes/Alliance/AllianceMemberList.php",
    "content": "<?php\n/**\n * Created by Gorlum 28.11.2017 11:16\n */\n\nnamespace Alliance;\n\n\nuse DBAL\\db_mysql;\nuse Player\\RecordPlayer;\n\nclass AllianceMemberList {\n  /**\n   * @var Alliance $alliance\n   */\n  protected $alliance;\n  /**\n   * @var \\DBAL\\db_mysql $db\n   */\n  protected $db;\n\n  /**\n   * @var AllianceMember[] $members\n   */\n  protected $members;\n\n\n  /**\n   * AllianceMemberList constructor.\n   *\n   * @param db_mysql $db\n   * @param Alliance $alliance\n   */\n  public function __construct(db_mysql $db, Alliance $alliance) {\n    $this->alliance = $alliance;\n    $this->db = $db;\n\n    foreach (RecordPlayer::findAll(['ally_id' => $this->alliance->id]) as $member) {\n      $this->members[] = new AllianceMember($this->alliance, $member);\n    }\n//    die('here');\n\n//      $this->members = db_user_list(\"`ally_id`= {$allyId_safe} ORDER BY `ally_rank_id`\", false,\n//        'id, username, galaxy, system, planet, onlinetime, ally_rank_id, ally_register_time, total_points');\n//    foreach (db_user_list(\"`ally_id`= {$allyId_safe} ORDER BY `ally_rank_id`\", false,\n//      'id, username, galaxy, system, planet, onlinetime, ally_rank_id, ally_register_time, total_points') as $player) {\n//      var_dump($player);\n//      die();\n//    }\n//    foreach (db_user_list(\"`ally_id`= {$allyId_safe} ORDER BY `ally_rank_id`\", false,\n//      'id, username, galaxy, system, planet, onlinetime, ally_rank_id, ally_register_time, total_points') as $player) {\n//      var_dump($player);\n//      die();\n//    }\n\n  }\n\n  /**\n   * Get ally's owner\n   *\n   * @return AllianceMember|null\n   */\n  public function getOwner() {\n    foreach ($this->members as $member) {\n      if ($member->isOwner()) {\n        return $member;\n      }\n    }\n\n    return null;\n  }\n\n  /**\n   * @return array\n   */\n  public function asPtl() {\n    $result = [];\n    if ($this->isEmpty()) {\n      return $result;\n    }\n\n    foreach ($this->members as $member) {\n      $result[] = $member->asPtl();\n    }\n\n    return $result;\n  }\n\n  /**\n   * @return bool\n   */\n  public function isEmpty() {\n    return empty($this->members);\n  }\n\n  /**\n   * @param int|float|string $id\n   *\n   * @return AllianceMember|null\n   */\n  public function getById($id) {\n    foreach ($this->members as $member) {\n      if ($member->getPlayerId() == $id) {\n        return $member;\n      }\n    }\n\n    return null;\n  }\n\n}\n"
  },
  {
    "path": "classes/Alliance/AllianceTitle.php",
    "content": "<?php\n/**\n * Created by Gorlum 28.11.2017 7:14\n */\n\nnamespace Alliance;\n\n/**\n * Class AllianceTitle\n *\n * Alliance title and access rights\n *\n * @package Alliance\n */\nclass AllianceTitle {\n  public $name = '';\n\n  public $index = -2;\n\n  public $mail = false;\n  public $online = false;\n  public $invite = false;\n  public $kick = false;\n  public $admin = false;\n  public $forum = false;\n  public $diplomacy = false;\n\n  /**\n   * @var Alliance $alliance\n   */\n  protected $alliance;\n\n\n  /**\n   * AllianceTitle constructor.\n   *\n   * @param Alliance $alliance\n   */\n  public function __construct(Alliance $alliance) {\n    $this->alliance = $alliance;\n  }\n\n  /**\n   * Fills title info from string\n   *\n   * @param int    $titleIndex - Internal Ally ID of title or -1 for owner\n   * @param string $titleString - Title name and access rights or '' for owner\n   */\n  public function fromString($titleIndex, $titleString) {\n    $this->index = $titleIndex;\n\n    if ($titleIndex == Alliance::OWNER_INDEX) {\n      $accessList = array_fill(1, count(Alliance::RIGHTS_ALL) - 1, 1);\n      $this->name = $this->alliance->ownerRankName;\n    } else {\n      $accessList = explode(',', $titleString);\n      $this->name = $accessList[0];\n      unset($accessList[0]);\n    }\n\n    foreach ($accessList as $key => $access) {\n      $this->{Alliance::RIGHTS_ALL[$key]} = $access == 1;\n    }\n\n//    var_dump($this->rightsAsString());\n//    var_dump($this->getWeight());\n  }\n\n  /**\n   * Compact title name and access rights into string\n   *\n   * @return string\n   */\n  public function __toString() {\n    $result = [];\n    foreach (Alliance::RIGHTS_ALL as $index => $rightName) {\n      $result[] = $index == 0 ? $this->$rightName : ($this->$rightName ? 1 : 0);\n    }\n\n    return implode(',', $result);\n  }\n\n  /**\n   * Compiles string with right lists for title\n   *\n   * @return string\n   */\n  public function rightsAsString() {\n    $result = [];\n    foreach (Alliance::RIGHT_WEIGHTS as $rightName => $weight) {\n      $this->$rightName ? $result[] = $rightName : false;\n    }\n\n    return implode(',', $result);\n  }\n\n  /**\n   * Get title right weight\n   *\n   * @return int\n   */\n  public function getWeight() {\n    $totalWeight = 0;\n    foreach (Alliance::RIGHT_WEIGHTS as $rightName => $weight) {\n      $this->$rightName ? $totalWeight += $weight : false;\n    }\n\n    return $totalWeight;\n  }\n\n}\n"
  },
  {
    "path": "classes/Alliance/AllianceTitleList.php",
    "content": "<?php\n/**\n * Created by Gorlum 28.11.2017 7:15\n */\n\nnamespace Alliance;\n\n\nclass AllianceTitleList {\n\n  /**\n   * @var Alliance $alliance\n   */\n  protected $alliance;\n\n  /**\n   * @var AllianceTitle[] $titles\n   */\n  protected $titles = [];\n\n  public function __construct(Alliance $alliance) {\n    $this->alliance = $alliance;\n\n    if (!empty($this->alliance->titleList)) {\n      $this->extractTitles();\n    }\n  }\n\n  /**\n   * @return AllianceTitle[]\n   */\n  public function getTitles() {\n    return $this->titles;\n  }\n\n  /**\n   * Compact all titles into one string\n   *\n   * @return string\n   */\n  public function __toString() {\n    $result = [];\n    foreach ($this->titles as $index => $title) {\n      if ($index == Alliance::OWNER_INDEX) {\n        continue;\n      }\n\n      $result[] = (string)$title;\n    }\n\n    return implode(';', $result);\n  }\n\n  /**\n   * @return int[]\n   */\n  public function getWeights() {\n    $result = [];\n    foreach ($this->titles as $index => $title) {\n      $result[$index] = $title->getWeight();\n    }\n\n    return $result;\n  }\n\n  /**\n   * Updates title names and access rights with current ones\n   */\n  public function updateTitles() {\n    $this->alliance->titleList = (string)$this;\n  }\n\n  protected function extractTitles() {\n    $this->titles = [];\n\n    $this->titles[Alliance::OWNER_INDEX] = new AllianceTitle($this->alliance);\n    $this->titles[Alliance::OWNER_INDEX]->fromString(Alliance::OWNER_INDEX, '');\n\n    $titleList = explode(';', $this->alliance->titleList);\n    foreach ($titleList as $titleIndex => $titleString) {\n      if (empty($titleString)) {\n        continue;\n      }\n      $this->titles[$titleIndex] = new AllianceTitle($this->alliance);\n      $this->titles[$titleIndex]->fromString($titleIndex, $titleString);\n    }\n\n    return $this->titles;\n  }\n\n  /**\n   * @param $titleIndex\n   *\n   * @return AllianceTitle|null\n   */\n  public function getTitle($titleIndex) {\n    return !empty($this->titles[$titleIndex]) ? $this->titles[$titleIndex] : null;\n  }\n\n\n  /**\n   * @return array\n   */\n  public function asPtl() {\n    $result = [];\n    foreach ($this->titles as $title) {\n      $result[] = [\n        'INDEX'       => $title->index,\n        'NAME'        => $title->name,\n        'NAME_SAFE'   => \\HelperString::htmlSafe($title->name),\n        'RIGHTS'      => $title->rightsAsString(),\n        'RIGHTS_TEXT' => \\HelperString::htmlSafe($title->rightsAsString()),\n      ];\n    }\n\n    return $result;\n  }\n\n}\n"
  },
  {
    "path": "classes/Alliance/DBStaticAlly.php",
    "content": "<?php\n\nnamespace Alliance;\n\nuse classLocale;\nuse mysqli_result;\n\nclass DBStaticAlly {\n\n// ALLY *************************************************************************************************************\n  public static function db_ally_list_recalc_counts() {\n    doquery(\"UPDATE `{{alliance}}` AS a LEFT JOIN (SELECT ally_id, count(*) AS ally_memeber_count FROM `{{users}}`\n      WHERE ally_id IS NOT NULL GROUP BY ally_id) AS u ON u.ally_id = a.id SET a.`ally_members` = u.ally_memeber_count;\");\n  }\n\n  public static function db_ally_request_list($ally_id) {\n    return doquery(\"SELECT {{alliance_requests}}.*, {{users}}.username FROM {{alliance_requests}} LEFT JOIN {{users}} ON {{users}}.id = {{alliance_requests}}.id_user WHERE id_ally='{$ally_id}'\");\n  }\n\n  public static function db_ally_request_get_by_user_id($player_id) {\n    return doquery(\"SELECT * FROM {{alliance_requests}} WHERE `id_user` ='{$player_id}' LIMIT 1;\", true);\n  }\n\n  public static function db_ally_count() {\n    $result = doquery('SELECT COUNT(`id`) AS ally_count FROM `{{alliance}}`', true);\n\n    return isset($result['ally_count']) ? $result['ally_count'] : 0;\n  }\n\n  /**\n   * @param $id_ally\n   *\n   * @return array|bool|mysqli_result|null\n   */\n  public static function db_ally_get_by_id($id_ally) {\n    $ally = doquery(\"SELECT * FROM `{{alliance}}` WHERE `id` ='{$id_ally}' LIMIT 1\", true);\n\n    return $ally;\n  }\n\n  /**\n   * @param $searchtext\n   *\n   * @return array|bool|mysqli_result|null\n   */\n  public static function db_ally_list_search($searchtext) {\n    $search = doquery(\"SELECT ally_name, ally_tag, total_rank, ally_members FROM {{alliance}} WHERE ally_tag LIKE '%{$searchtext}%' OR ally_name LIKE '%{$searchtext}%' LIMIT 30\");\n\n    return $search;\n  }\n\n  /**\n   * @param $ally_tag\n   * @param $ally_name\n   *\n   * @return array|bool|mysqli_result|null\n   */\n  public static function db_ally_get_by_name_or_tag($ally_tag, $ally_name) {\n    $query = doquery(\"SELECT ally_tag FROM {{alliance}} WHERE `ally_tag` = '{$ally_tag}' or `ally_name` = '{$ally_name}' LIMIT 1;\", true);\n\n    return $query;\n  }\n\n  /**\n   * @param $ally_name\n   *\n   * @return array|bool|mysqli_result|null\n   */\n  public static function db_ally_get_by_name($ally_name) {\n    $query = doquery(\"SELECT ally_name FROM {{alliance}} WHERE `ally_name` = '{$ally_name}' LIMIT 1;\", true);\n\n    return $query;\n  }\n\n  /**\n   * @param $ally_name\n   * @param $ally_tag\n   * @param $user\n   */\n  public static function db_ally_insert($ally_name, $ally_tag, $user) {\n    $ally = doquery(\"INSERT INTO {{alliance}} SET\n    `ally_name` = '{$ally_name}',\n    `ally_tag` = '{$ally_tag}',\n    `ally_owner` = '{$user['id']}',\n    `ally_owner_range` = '\" . classLocale::$lang['ali_leaderRank'] . \"',\n    `ally_members` = 1,\n    `ranklist` = '\" . classLocale::$lang['ali_defaultRankName'] . \",0,0,0,0,0',\n    `ally_register_time`= \" . SN_TIME_NOW\n    );\n\n    return $ally;\n  }\n\n  /**\n   * @param $ally_user_id\n   * @param $ally_id\n   */\n  public static function db_ally_update_ally_user($ally_user_id, $ally_id) {\n    doquery(\"UPDATE {{alliance}} SET ally_user_id = {$ally_user_id} WHERE id = {$ally_id} LIMIT 1;\");\n  }\n\n  /**\n   * @param $user\n   * @param $id_ally\n   * @param $POST_text\n   */\n  public static function db_ally_request_insert($user, $id_ally, $POST_text) {\n    doquery(\"INSERT INTO {{alliance_requests}} SET `id_user` = {$user['id']}, `id_ally`='{$id_ally}', request_text ='{$POST_text}', request_time=\" . SN_TIME_NOW . \";\");\n  }\n\n  /**\n   * @param $user\n   */\n  public static function db_ally_request_delete_by_user($user) {\n    doquery(\"DELETE FROM {{alliance_requests}} WHERE `id_user` = {$user['id']};\");\n  }\n\n\n  /**\n   * @param $tag\n   *\n   * @return array|bool|mysqli_result|null\n   */\n  public static function db_ally_get_by_tag($tag) {\n    $ally = doquery(\"SELECT * FROM {{alliance}} WHERE ally_tag='{$tag}' LIMIT 1;\", '', true);\n\n    return $ally;\n  }\n\n  /**\n   * @param $ali_search_text\n   *\n   * @return array|bool|mysqli_result|null\n   */\n  public static function db_ally_search_by_name_or_tag($ali_search_text) {\n    $search = doquery(\"SELECT DISTINCT * FROM {{alliance}} WHERE `ally_name` LIKE '%{$ali_search_text}%' OR `ally_tag` LIKE '%{$ali_search_text}%' LIMIT 30\");\n\n    return $search;\n  }\n\n  /**\n   * @param $ally\n   *\n   * @return array|bool|mysqli_result|null\n   */\n  public static function db_ally_request_count_by_id($ally) {\n    $request = doquery(\"SELECT COUNT(*) AS request_count FROM {{alliance_requests}} WHERE `id_ally` ='{$ally['id']}'\", true);\n\n    return $request;\n  }\n\n\n  /**\n   * @param $ally_changeset\n   * @param $ally\n   */\n  public static function db_ally_update_by_changeset($ally_changeset, $ally) {\n    doquery(\"UPDATE {{alliance}} SET \" . implode(',', $ally_changeset) . \" WHERE `id`='{$ally['id']}' LIMIT 1;\");\n  }\n\n  /**\n   * @param $text_list\n   * @param $allyTextID\n   * @param $text\n   * @param $ally\n   */\n  public static function db_ally_update_texts($text_list, $allyTextID, $text, $ally) {\n    doquery(\"UPDATE {{alliance}} SET `{$text_list[$allyTextID]['db_field']}`='{$text['safe']}' WHERE `id`='{$ally['id']}';\");\n  }\n\n  /**\n   * @param $idNewLeader\n   * @param $user\n   */\n  public static function db_ally_update_owner($idNewLeader, $user) {\n    doquery(\"UPDATE {{alliance}} SET `ally_owner`='{$idNewLeader}' WHERE `id`={$user['ally_id']};\");\n  }\n\n  /**\n   * @param $ally\n   */\n  public static function db_ally_delete($ally) {\n    doquery(\"DELETE FROM {{alliance}} WHERE id='{$ally['id']}';\");\n  }\n\n\n  /**\n   * @param $user\n   * @param $alliance_negotiation_contr_ally_id\n   */\n  public static function db_ally_negotiation_delete($user, $alliance_negotiation_contr_ally_id) {\n    doquery(\"DELETE FROM {{alliance_negotiation}} WHERE alliance_negotiation_ally_id = {$user['ally_id']} AND alliance_negotiation_contr_ally_id = {$alliance_negotiation_contr_ally_id} LIMIT 1;\");\n  }\n\n  /**\n   * @param $offer_id\n   *\n   * @return array|bool|mysqli_result|null\n   */\n  public static function db_ally_negotiation_get_by_offer_id($offer_id) {\n    $negotiation = doquery(\"SELECT * FROM {{alliance_negotiation}} WHERE alliance_negotiation_id = {$offer_id} LIMIT 1;\", '', true);\n\n    return $negotiation;\n  }\n\n  /**\n   * @param $offer_id\n   */\n  public static function db_ally_negotiation_delete_by_offer_id($offer_id) {\n    doquery(\"DELETE FROM {{alliance_negotiation}} WHERE alliance_negotiation_id = {$offer_id} LIMIT 1;\");\n  }\n\n  /**\n   * @param $offer_id\n   */\n  public static function db_ally_negotiation_update_status_1($offer_id) {\n    doquery(\"UPDATE {{alliance_negotiation}} SET alliance_negotiation_status = 1 WHERE alliance_negotiation_id = {$offer_id} LIMIT 1;\");\n  }\n\n  /**\n   * @param $negotiation\n   * @param $user\n   */\n  public static function db_ally_negotiatiion_delete_extended($negotiation, $user) {\n    doquery(\n      \"DELETE FROM {{alliance_negotiation}}\n  \t WHERE\n        (alliance_negotiation_ally_id = {$negotiation['alliance_negotiation_ally_id']} AND alliance_negotiation_contr_ally_id = {$user['ally_id']})\n        OR\n        (alliance_negotiation_ally_id = {$user['ally_id']} AND alliance_negotiation_contr_ally_id = {$negotiation['alliance_negotiation_ally_id']});\"\n    );\n  }\n\n  /**\n   * @param $user\n   *\n   * @return array|bool|mysqli_result|null\n   */\n  public static function db_ally_list_get_by_not_user_ally($user) {\n    $query = doquery(\"SELECT id, ally_name, ally_tag FROM {{alliance}} WHERE `id` != {$user['ally_id']} ORDER BY ally_name;\");\n\n    return $query;\n  }\n\n  /**\n   * @param $user\n   *\n   * @return array|bool|mysqli_result|null\n   */\n  public static function db_ally_negotiation_list($user) {\n    $query = doquery(\n      \"SELECT\n    *,\n    if(alliance_negotiation_ally_id = {$user['ally_id']}, 1, 0) AS owner,\n    if(alliance_negotiation_ally_id = {$user['ally_id']}, alliance_negotiation_contr_ally_name, alliance_negotiation_ally_name) AS ally_name\n  FROM\n    {{alliance_negotiation}}\n  WHERE\n    alliance_negotiation_ally_id = {$user['ally_id']} OR alliance_negotiation_contr_ally_id = {$user['ally_id']};\"\n    );\n\n    return $query;\n  }\n\n  /**\n   * @param $d\n   */\n  public static function db_ally_request_deny($d) {\n    doquery(\"UPDATE {{alliance_requests}} SET `request_denied` = 1, `request_text` = '\" . classLocale::$lang['ali_req_deny_reason'] . \"' WHERE `id_user`= {$d} LIMIT 1;\");\n  }\n\n  /**\n   * @param $ally\n   */\n  public static function db_ally_update_member_increase($ally) {\n    doquery(\"UPDATE {{alliance}} SET `ally_members`= `ally_members` + 1 WHERE `id`='{$ally['id']}'\");\n  }\n\n  /**\n   * @param $ally\n   */\n  public static function db_ally_update_member_decrease($ally) {\n    doquery(\"UPDATE {{alliance}} SET `ally_members`= `ally_members` - 1 WHERE `id`='{$ally['id']}' LIMIT 1;\");\n  }\n\n  /**\n   * @param $i\n   * @param $ally\n   */\n  public static function db_ally_update_member_set($i, $ally) {\n    doquery(\"UPDATE {{alliance}} SET `ally_members`='{$i}' WHERE `id`='{$ally['id']}'\");\n  }\n\n  /**\n   * @param $id_user\n   */\n  public static function db_ally_request_delete_by_user_id($id_user) {\n    doquery(\"DELETE FROM {{alliance_requests}} WHERE `id_user`= '{$id_user}' LIMIT 1;\");\n  }\n\n\n  /**\n   * @param $user\n   *\n   * @return array|bool|mysqli_result|null\n   */\n  public static function db_ally_get_members_by_user_as_ally(&$user) {\n    $alliance = doquery(\"SELECT `ally_members` FROM {{alliance}} WHERE `ally_user_id` = {$user['id']}\", true);\n\n    return $alliance;\n  }\n\n  /**\n   * @param $ranklist\n   * @param $user\n   */\n  public static function db_ally_update_ranklist($ranklist, $user) {\n    doquery(\"UPDATE {{alliance}} SET `ranklist` = '{$ranklist}' WHERE `id` ='{$user['ally_id']}';\");\n  }\n\n  /**\n   * @param $ally_from\n   * @param $ally_to\n   *\n   * @return array|bool|mysqli_result|null\n   */\n  public static function db_ally_diplomacy_get_relations($ally_from, $ally_to) {\n    $query = doquery(\n      \"SELECT b.*\n      FROM\n        {{alliance_diplomacy}} AS b,\n        (SELECT alliance_diplomacy_contr_ally_id, MAX(alliance_diplomacy_time) AS alliance_diplomacy_time\n          FROM {{alliance_diplomacy}}\n          WHERE alliance_diplomacy_ally_id = {$ally_from}  {$ally_to}\n          GROUP BY alliance_diplomacy_ally_id, alliance_diplomacy_contr_ally_id\n        ) AS m\n      WHERE b.alliance_diplomacy_contr_ally_id = m.alliance_diplomacy_contr_ally_id\n        AND b.alliance_diplomacy_time = m.alliance_diplomacy_time AND b.alliance_diplomacy_ally_id = {$ally_from}\n      ORDER BY alliance_diplomacy_time, alliance_diplomacy_id;\"\n    );\n\n    return $query;\n  }\n\n  /**\n   * @param $user\n   *\n   * @return array|bool|mysqli_result|null\n   */\n  public static function db_ally_get_ally_count(&$user) {\n    $lab_level = doquery(\"SELECT ally_members AS effective_level FROM {{alliance}} WHERE id = {$user['user_as_ally']} LIMIT 1\", true);\n\n    return $lab_level;\n  }\n\n}"
  },
  {
    "path": "classes/Alliance/RecordAlliance.php",
    "content": "<?php\n/**\n * Created by Gorlum 15.06.2017 10:37\n */\n\nnamespace Alliance;\n\n\nuse DBAL\\ActiveRecord;\n\n/**\n * Class RecordAlliance\n *\n * @package Alliance\n *\n * @property string     $tag\n * @property string     $name\n * @property int        $createdUnixTime\n * @property int|string $ownerId\n * @property string     $ownerRankName\n * @property string     $description\n * @property string     $webPageUrl\n * @property string     $textInternal\n * @property int        $isLogoPresent\n * @property string     $requestTemplate\n * @property int        $isRequestsDisabled\n * @property int        $membersCount\n * @property float      $statPosition\n * @property float      $statPoints\n * @property string     $titleList\n * @property string     $oldTitleList\n * @property int|string $parentPlayerRecordId\n */\nclass RecordAlliance extends ActiveRecord {\n\n  protected static $_fieldsToProperties = [\n    'ally_tag'              => 'tag',\n    'ally_name'             => 'name',\n    'ally_register_time'    => 'createdUnixTime',\n    'ally_owner'            => 'ownerId',\n    'ally_owner_range'      => 'ownerRankName',\n    'ally_description'      => 'description',\n    'ally_web'              => 'webPageUrl',\n    'ally_text'             => 'textInternal',\n    'ally_image'            => 'isLogoPresent',\n    'ally_request'          => 'requestTemplate',\n    'ally_request_notallow' => 'isRequestsDisabled',\n    'ally_members'          => 'membersCount',\n    'total_rank'            => 'statPosition',\n    'total_points'          => 'statPoints',\n    'ranklist'              => 'titleList',\n    'ally_ranks'            => 'oldTitleList',\n    'ally_user_id'          => 'parentPlayerRecordId',\n  ];\n\n\n  /**\n   * @return array\n   */\n  public function ptlArray() {\n    return [\n      'ID'                   => $this->id,\n      'TAG'                  => $this->tag,\n      'NAME'                 => $this->name,\n      'CREATED'              => $this->createdUnixTime,\n      'OWNER_ID'             => $this->ownerId,\n      'OWNER_RANK_NAME'      => $this->ownerRankName,\n      'DESCRIPTION'          => $this->description,\n      'WEB_PAGE'             => $this->webPageUrl,\n      'TEXT_INTERNAL'        => $this->textInternal,\n      'HAVE_LOGO'            => $this->isLogoPresent,\n      'REQUEST_TEMPLATE'     => $this->requestTemplate,\n      'REQUESTS_NOT_ALLOWED' => $this->isRequestsDisabled,\n      'MEMBER_COUNT'         => $this->membersCount,\n      'STAT_POSITION'        => $this->statPosition,\n      'STAT_POINTS'          => $this->statPoints,\n      'TITLE_LIST_UNPARSED'  => $this->titleList,\n      'OLD_TITLE_LIST'       => $this->oldTitleList,\n      'PARENT_PLAYER_ID'     => $this->parentPlayerRecordId,\n    ];\n  }\n\n}\n"
  },
  {
    "path": "classes/AuthProvider.php",
    "content": "<?php\n\n/**\n * Created by PhpStorm.\n * User: Gorlum\n * Date: 24.08.2015\n * Time: 6:00\n */\nclass AuthProvider {\n\n}"
  },
  {
    "path": "classes/BBCodeParser.php",
    "content": "<?php\n\n/**\n * Created by Gorlum 14.02.2017 11:18\n */\nclass BBCodeParser {\n\n  /**\n   * @var Design $design\n   */\n  protected $design;\n\n  public function __construct(Core\\GlobalContainer $gc) {\n    $this->design = $gc->design;\n  }\n\n  protected function applyElements($elements, $text, $authorAccessLevel = AUTH_LEVEL_REGISTERED) {\n    foreach ($elements as $auth_level => $element) {\n      if ($auth_level > $authorAccessLevel) {\n        continue;\n      }\n\n      foreach ($element as $find => $replace) {\n        $text = preg_replace($find, $replace, $text);\n      }\n    }\n\n    return $text;\n  }\n\n  /**\n   * Expands bbCodes and smiles in text\n   *\n   * @param      $text\n   * @param int  $authorAccessLevel\n   * @param int  $encodeOptions - HTML_ENCODE_xxx constants. HTML_ENCODE_MULTILINE by default\n   *\n   * @return mixed\n   */\n  public function expandBbCode($text, $authorAccessLevel = AUTH_LEVEL_REGISTERED, $encodeOptions = HTML_ENCODE_MULTILINE) {\n    $text = HelperString::htmlEncode($text, $encodeOptions);\n\n    $text = $this->applyElements($this->design->getBbCodes(), $text, $authorAccessLevel);\n    $text = $this->applyElements($this->design->getSmiles(), $text, $authorAccessLevel);\n\n    return $text;\n  }\n\n  public function compactBbCode($text, $authorAccessLevel = AUTH_LEVEL_REGISTERED, $encodeOptions = HTML_ENCODE_MULTILINE) {\n    // Replacing news://xxx link with BBCode\n    $text = preg_replace(\"#news\\:\\/\\/(\\d+)#\", \"[news=$1]\", $text);\n    // Replacing news URL with BBCode\n    $text = preg_replace(\"#(?:https?\\:\\/\\/(?:.+)?\\/announce\\.php\\?id\\=(\\d+))#\", \"[news=$1]\", $text);\n    $text = preg_replace(\"#(?:https?\\:\\/\\/(?:.+)?\\/index\\.php\\?page\\=battle_report\\&cypher\\=([0-9a-zA-Z]{32}))#\", \"[ube=$1]\", $text);\n\n    return $text;\n  }\n}\n"
  },
  {
    "path": "classes/BanHammer.php",
    "content": "<?php\n\n/** Created by Gorlum 25.02.2025 13:16 */\n\n/**\n *\n * MySQL: INET_ATON() -> string to int, INET_NTOA() -> int to string\n * PHP: ip2long -> string to int, long2ip() -> int to string\n */\nclass BanHammer {\n  /** Ban record can be of any expirations */\n  const EXPIRED_ANY = null;\n  /** Ban record should NOT be active */\n  const EXPIRED_NOT = false;\n  /** Ban record should ALREADY be expired */\n  const EXPIRED_ALREADY = true;\n\n  /**\n   * Get ban records for specified IPv4 address from ban table with appropriate status\n   *\n   * @param int|string $ipV4 IPv4 address to check. Can be ether string like `192.168.1.1` or integer like 3232235777 (int unsigned)\n   * @param ?bool      $isExpired\n   *\n   * @return array[]|object[]\n   */\n  public static function getByIpV4($ipV4, $isExpired = self::EXPIRED_NOT) {\n    $ipV4 = !is_numeric($ipV4) ? ip2long($ipV4) : (integer)$ipV4;\n\n    $sqlArray = [\n      \"SELECT *, INET_NTOA(`ipv4_from`) as ipv4_from_string, INET_NTOA(`ipv4_to`) as ipv4_to_string\n       FROM {{ban_ip}}\n       WHERE ipv4_from <= $ipV4 AND ipv4_to >= $ipV4\",\n      $isExpired === self::EXPIRED_ANY ? ''\n        : ('AND `expired_at` ' . ($isExpired === self::EXPIRED_NOT ? '>=' : '<') . ' NOW()'),\n    ];\n\n    return SN::$db->dbGetAll(implode(' ', $sqlArray));\n  }\n\n  /**\n   * Check if specified IPv4 address is currently fit specified status\n   *\n   * @param int|string $ipV4      IPv4 address to check. Can be ether string like `192.168.1.1` or integer like 3232235777 (int unsigned)\n   * @param ?bool      $isExpired @see BanHammer::EXPIRED_ANY EXPIRED_XXX class constants\n   *\n   * @return bool\n   */\n  public static function checkIpV4($ipV4, $isExpired = self::EXPIRED_NOT) {\n    return !empty(static::getByIpV4($ipV4, $isExpired));\n  }\n\n}\n"
  },
  {
    "path": "classes/Bonus/BonusAtom.php",
    "content": "<?php\n/**\n * Created by Gorlum 01.12.2017 6:00\n */\n\nnamespace Bonus;\n\n/**\n * Class BonusAtom\n *\n * Info about one single bonus - atomic one\n *\n * @package Bonus\n */\nclass BonusAtom {\n  const RETURN_IF_BASE_NOT_ZERO = true;\n  const RETURN_ALWAYS = false;\n\n  /**\n   * Unit SN ID to use as base to adjust base value\n   *\n   * @var int $snId\n   */\n  public $snId = 0;\n\n  /**\n   * Bonus power is a value which will additionally adjust value amount\n   *\n   * Used for BONUS_PERCENT mainly\n   *\n   * @var int|float $power\n   */\n  public $power = 0;\n\n  /**\n   * Flag that bonus should be applied only if base value is not zero\n   *\n   * @var bool $ifBaseNonZero\n   */\n  public $ifBaseNonZero = true;\n\n  /**\n   * Calculates bonus type basing on provided unit info\n   *\n   * @param array|null $unitInfo\n   *\n   * @return int\n   */\n  public static function calcBonusType($unitInfo) {\n    // BONUS_ADD used as default\n    return isset($unitInfo[P_BONUS_TYPE]) ? $unitInfo[P_BONUS_TYPE] : BONUS_ADD;\n  }\n\n  /**\n   * Calculates bonus power based on provided unit info\n   *\n   * Needs for BONUS_PERCENT, for example\n   *\n   * @param array|null $unitInfo\n   *\n   * @return int\n   */\n  public static function calcBonusPower($unitInfo) {\n    return isset($unitInfo[P_BONUS_VALUE]) ? $unitInfo[P_BONUS_VALUE] :\n      (static::calcBonusType($unitInfo) == BONUS_ABILITY ? 1 : 0);\n  }\n\n  /**\n   * BonusAtom constructor.\n   *\n   * @param int  $bonusId - Unit ID from which base value should be retrieved\n   * @param bool $ifBaseNonZero - Bonus should applied only if base value is not empty when true\n   */\n  public function __construct($bonusId, $ifBaseNonZero) {\n    $this->snId = $bonusId;\n    $this->ifBaseNonZero = $ifBaseNonZero;\n\n    // In conjunction with default BONUS_ADD comes bonus value 0\n    $this->power = static::calcBonusPower(getUnitInfo($bonusId));\n  }\n\n  /**\n   * Checks if bonus should return no result\n   *\n   * Bonus value can be linked to base value\n   * Basic example is below: if base value is zero then we check $ifBaseNonZero flag. If $ifBaseNonZero is set then we return nothing even if there is some bonus value\n   * Setting $ifBaseNonZero to 'false' will return bonus value not matter what base value is\n   *\n   * @param int|float $baseValue\n   *\n   * @return bool\n   */\n  protected function isReturnNothing($baseValue) {\n    return $this->ifBaseNonZero == self::RETURN_IF_BASE_NOT_ZERO && $baseValue == 0;\n  }\n\n  /**\n   * Calculates how much should be added to base value\n   *\n   * @param float|int $currentValue\n   * @param float|int $bonusAmount\n   * @param float|int $baseValue\n   *\n   * @return float|int\n   */\n  public function adjustValue(&$currentValue, $bonusAmount, $baseValue) {\n    return $this->isReturnNothing($baseValue) ? 0 : $this->calcAdjustment($currentValue, $bonusAmount, $baseValue);\n  }\n\n  /**\n   * @param float|int $currentValue\n   * @param float|int $bonusAmount\n   * @param float|int $baseValue\n   *\n   * @return float|int\n   */\n  protected function calcAdjustment(&$currentValue, $bonusAmount, $baseValue) {\n    return 0;\n  }\n\n}\n"
  },
  {
    "path": "classes/Bonus/BonusAtomAbility.php",
    "content": "<?php\n/**\n * Created by Gorlum 02.12.2017 11:59\n */\n\nnamespace Bonus;\n\n\nclass BonusAtomAbility extends BonusAtom {\n\n  /**\n   * @inheritdoc\n   */\n  protected function calcAdjustment(&$currentValue, $bonusAmount, $baseValue) {\n    $result = $bonusAmount ? 1 : 0;\n\n    $currentValue = $currentValue || $result ? 1 : 0;\n\n    return $result;\n  }\n\n}\n"
  },
  {
    "path": "classes/Bonus/BonusAtomAdd.php",
    "content": "<?php\n/**\n * Created by Gorlum 02.12.2017 12:04\n */\n\nnamespace Bonus;\n\n\nclass BonusAtomAdd extends BonusAtom {\n\n  /**\n   * @inheritdoc\n   */\n  protected function calcAdjustment(&$currentValue, $bonusAmount, $baseValue) {\n    $result = $bonusAmount * $this->power;\n\n    $currentValue += $result;\n\n    return $result;\n  }\n\n}\n"
  },
  {
    "path": "classes/Bonus/BonusAtomMultiply.php",
    "content": "<?php\n/**\n * Created by Gorlum 02.12.2017 12:08\n */\n\nnamespace Bonus;\n\n\nclass BonusAtomMultiply extends BonusAtom {\n\n  /**\n   * @inheritdoc\n   */\n  protected function calcAdjustment(&$currentValue, $bonusAmount, $baseValue) {\n    $valueOld = $currentValue;\n\n    $currentValue *= $this->power * $bonusAmount;\n\n    $result = $currentValue - $valueOld;\n\n    return $result;\n  }\n\n}\n"
  },
  {
    "path": "classes/Bonus/BonusAtomPercent.php",
    "content": "<?php\n/**\n * Created by Gorlum 02.12.2017 12:07\n */\n\nnamespace Bonus;\n\n\nclass BonusAtomPercent extends BonusAtom {\n\n  // TODO - pick MINIMUM value from unit description\n\n  /**\n   * @inheritdoc\n   */\n  protected function calcAdjustment(&$currentValue, $bonusAmount, $baseValue) {\n    $result = $baseValue * $bonusAmount * ($this->power / 100);\n\n    $currentValue += $result;\n\n    return $result;\n  }\n\n\n}\n"
  },
  {
    "path": "classes/Bonus/BonusCatalog.php",
    "content": "<?php\n/**\n * Created by Gorlum 01.12.2017 4:31\n */\n\nnamespace Bonus;\n\nuse Core\\GlobalContainer;\n\n/**\n * Class BonusCatalog\n *\n * BonusAtom catalog - list of all possible modifiers for all bonus types\n *\n * @package Bonus\n */\nclass BonusCatalog {\n  /**\n   * @var GlobalContainer $gc\n   */\n  protected $gc;\n\n  /**\n   * List of bonuses for levels\n   *\n   * [(int)$snId => Bonus\\BonusListAtom]\n   *\n   * @var BonusListAtom[] $bonuses\n   */\n  protected $bonuses = [];\n\n  public function __construct(GlobalContainer $gc) {\n    $this->gc = $gc;\n\n    $this->loadDefaults();\n  }\n\n  protected function loadDefaults() {\n    $this->registerBonus(UNIT_PLAYER_EMPIRE_SPY, TECH_SPY, BonusAtom::RETURN_ALWAYS);\n    $this->registerBonus(UNIT_PLAYER_EMPIRE_SPY, MRC_SPY, BonusAtom::RETURN_ALWAYS);\n\n//    $this->registerBonus(UNIT_FLEET_PLANET_SPY, UNIT_PLAYER_EMPIRE_SPY);\n//    $this->registerBonus(UNIT_FLEET_PLANET_SPY, SHIP_SPY, LOC_FLEET);\n  }\n\n  /**\n   * Register atomic bonus description\n   *\n   * @param int  $bonusId - ID of value to which bonus is attached\n   * @param int  $baseBonusId - ID of unit which value will be used to calculate bonus\n   * @param bool $ifBaseNonZero - Bonus should applied only if base value is not empty when true\n   */\n  public function registerBonus($bonusId, $baseBonusId, $ifBaseNonZero = BonusAtom::RETURN_IF_BASE_NOT_ZERO) {\n    // TODO - also register triggers to invalidate\n    if (empty($this->bonuses[$bonusId])) {\n      // TODO - lazy loader\n      $this->bonuses[$bonusId] = new BonusListAtom($bonusId);\n    }\n\n    $this->bonuses[$bonusId]->addUnit($baseBonusId, $ifBaseNonZero);\n  }\n\n  /**\n   * @param $bonusId\n   *\n   * @return BonusListAtom|null\n   */\n  public function getBonusListAtom($bonusId) {\n    return array_key_exists($bonusId, $this->bonuses) ? $this->bonuses[$bonusId] : null;\n  }\n\n}\n"
  },
  {
    "path": "classes/Bonus/BonusFactory.php",
    "content": "<?php\n/**\n * Created by Gorlum 02.12.2017 11:40\n */\n\nnamespace Bonus;\n\n\n/**\n * Class BonusFactory\n *\n * Bonus factory - generate appropriate class depending on unit\n *\n * @package Bonus\n */\nclass BonusFactory {\n  public static $classByType = [\n    BONUS_ABILITY  => BonusAtomAbility::class,\n    BONUS_ADD      => BonusAtomAdd::class,\n    BONUS_PERCENT  => BonusAtomPercent::class,\n    BONUS_MULTIPLY => BonusAtomMultiply::class,\n  ];\n\n  /**\n   * @param int  $sourceUnitId - Unit ID from which base value should be retrieved\n   * @param bool $ifBaseNonZero\n   *\n   * @return BonusAtom\n   */\n  public static function build($sourceUnitId, $ifBaseNonZero) {\n    $bonusType = BonusAtom::calcBonusType(getUnitInfo($sourceUnitId));\n    $bonusClass = isset(static::$classByType[$bonusType]) ? static::$classByType[$bonusType] : BonusAtom::class;\n\n    return new $bonusClass($sourceUnitId, $ifBaseNonZero);\n  }\n\n}\n"
  },
  {
    "path": "classes/Bonus/BonusListAtom.php",
    "content": "<?php\n/**\n * Created by Gorlum 01.12.2017 5:23\n */\n\nnamespace Bonus;\n\n/**\n * Class BonusListAtom\n *\n * Lists all bonuses for one value\n *\n * @package Bonus\n */\nclass BonusListAtom implements \\Countable {\n  /**\n   * @var int $bonusId\n   */\n  public $bonusId;\n\n  /**\n   * [(int)$sourceUnitSnId => (class)BonusAtom]\n   *\n   * @var BonusAtom[] $bonusAtoms\n   */\n  protected $bonusAtoms = [];\n\n  /**\n   * BonusListAtom constructor.\n   *\n   * @param int $bonusId\n   */\n  public function __construct($bonusId) {\n    $this->bonusId = $bonusId;\n  }\n\n  /**\n   * @return BonusAtom[]\n   */\n  public function getBonusAtoms() {\n    return $this->bonusAtoms;\n  }\n\n  /**\n   * Add bonus from specified unit to current bonus list\n   *\n   * @param int  $baseBonusId - Unit ID from which base value should be retrieved\n   * @param bool $ifBaseNonZero - Bonus should applied only if base value is not empty when true\n   */\n  public function addUnit($baseBonusId, $ifBaseNonZero = BonusAtom::RETURN_IF_BASE_NOT_ZERO) {\n    // ToDo - exception on existing (duplicate) bonus ID?\n    $this->bonusAtoms[$baseBonusId] = BonusFactory::build($baseBonusId, $ifBaseNonZero);\n\n    uasort($this->bonusAtoms, [$this, 'bonusSort']);\n  }\n\n\n  protected function bonusSort(BonusAtom $a, BonusAtom $b) {\n    static $bonusOrder = [BonusAtom::class, BonusAtomAbility::class, BonusAtomAdd::class, BonusAtomPercent::class, BonusAtomMultiply::class];\n\n    $indexA = (int)array_search(get_class($a), $bonusOrder);\n    $indexB = (int)array_search(get_class($b), $bonusOrder);\n\n    return $indexA == $indexB ? 0 : ($indexA > $indexB ? +1 : -1);\n  }\n\n  /**\n   * Count elements of an object\n   * @link http://php.net/manual/en/countable.count.php\n   * @return int The custom count as an integer.\n   * </p>\n   * <p>\n   * The return value is cast to an integer.\n   * @since 5.1.0\n   */\n  public function count() {\n    return count($this->bonusAtoms);\n  }\n\n}\n"
  },
  {
    "path": "classes/Bonus/ValueBonused.php",
    "content": "<?php\n/**\n * Created by Gorlum 01.12.2017 8:34\n */\n\nnamespace Bonus;\n\nuse \\SN;\n\nclass ValueBonused {\n\n  public $base = 0;\n  public $value = 0;\n  public $snId = 0;\n  public $context = [];\n\n\n  /**\n   * @var BonusListAtom $bonusList\n   */\n  protected $bonusList;\n\n  /**\n   * [(int)$sourceUnitSnId] => (float)bonusValue\n   *\n   * @var float[] $bonusValues\n   */\n  public $bonusValues = [];\n\n  /**\n   * @var BonusCatalog $bonusCatalog\n   */\n  protected $bonusCatalog;\n\n  protected $calculated = false;\n\n  /**\n   * ValueBonused constructor.\n   *\n   * @param int       $unitSnId\n   * @param int|float $baseValue\n   * @param array     $context\n   */\n  public function __construct($unitSnId, $baseValue, $context = []) {\n    $this->snId = $unitSnId;\n\n    $this->base = $baseValue;\n    $this->value = $this->base;\n    $this->bonusValues = [];\n\n    $this->bonusCatalog = \\SN::$gc->bonusCatalog;\n  }\n\n\n  /**\n   * @param array $context - Context list of locations: [LOC_xxx => (data)]\n   *\n   * @return float|int\n   */\n  public function getValue($context = []) {\n    // Context can differ. However - it shouldn't\n    if ($this->calculated && $this->context == $context) {\n      return $this->value;\n    }\n\n\n    $this->context = $context;\n    $this->value = $this->base;\n    $this->bonusValues = [];\n    $this->calculated = true;\n\n    $this->bonusList = $this->bonusCatalog->getBonusListAtom($this->snId);\n    if (!$this->bonusList instanceof BonusListAtom) {\n      return $this->base;\n    }\n\n    $this->applyBonuses($context);\n\n    return $this->value;\n  }\n\n  /**\n   * Calculates real bonus values within supplied context\n   *\n   * @param array $context - Context list of locations: [LOC_xxx => (data)]\n   */\n  protected function applyBonuses($context = []) {\n    $this->bonusValues = [BONUS_NONE => $this->base];\n    if (!$this->bonusList instanceof BonusListAtom) {\n      return;\n    }\n\n    foreach ($this->bonusList->getBonusAtoms() as $unitId => $bonusAtom) {\n      $amount = SN::$gc->valueStorage->getValue($unitId, $context);\n\n      $this->bonusValues[$unitId] = $bonusAtom->adjustValue($this->value, $amount, $this->base);\n    }\n\n// TODO - проследить, что бы ниже не было отрицательных значений\n//            $mercenary_level = $mercenary_bonus < 0 && $mercenary_level * $mercenary_bonus < -90 ? -90 / $mercenary_bonus : $mercenary_level;\n//            $value += $base_value * $mercenary_level * $mercenary_bonus / 100;\n  }\n\n}\n"
  },
  {
    "path": "classes/Bonus/ValueStorage.php",
    "content": "<?php\n/**\n * Created by Gorlum 01.12.2017 6:54\n */\n\nnamespace Bonus;\n\nuse Common\\ContainerPlus;\nuse \\SN;\n\n\n/**\n * Class ValueStorage\n *\n * Store calculated bonus values\n *\n * In future can be used to cache data in memory cache\n *\n * @package Bonus\n */\nclass ValueStorage extends ContainerPlus {\n  /**\n   * @var \\Core\\GlobalContainer\n   */\n  protected $gc;\n\n  /**\n   * @var ValueBonused[][] $values\n   */\n  protected $values = [];\n\n  /**\n   * @return \\Core\\GlobalContainer\n   */\n  public function getGlobalContainer() {\n    return $this->gc;\n  }\n\n  public function __construct(array $values = array()) {\n    parent::__construct($values);\n\n    $this->gc = SN::$gc;\n\n    $this->initValues();\n  }\n\n  protected function initValues() {\n    $this[UNIT_SERVER_SPEED_BUILDING] = function (ValueStorage $vs) {\n      return new ValueBonused(UNIT_SERVER_SPEED_BUILDING, floatval($vs->getGlobalContainer()->config->game_speed));\n    };\n    $this[UNIT_SERVER_SPEED_MINING] = function (ValueStorage $vs) {\n      return new ValueBonused(UNIT_SERVER_SPEED_MINING, floatval($vs->getGlobalContainer()->config->resource_multiplier));\n    };\n    $this[UNIT_SERVER_SPEED_FLEET] = function (ValueStorage $vs) {\n      return new ValueBonused(UNIT_SERVER_SPEED_FLEET, floatval($vs->getGlobalContainer()->config->fleet_speed));\n    };\n    $this[UNIT_SERVER_SPEED_EXPEDITION] = function (ValueStorage $vs) {\n      return new ValueBonused(UNIT_SERVER_SPEED_EXPEDITION, floatval(1));\n    };\n\n    $this[UNIT_SERVER_FLEET_NOOB_POINTS] = function (ValueStorage $vs) {\n      $config = $vs->getGlobalContainer()->config;\n\n      return new ValueBonused(\n        UNIT_SERVER_FLEET_NOOB_POINTS,\n        floatval($config->game_noob_points * $vs->getBase(UNIT_SERVER_SPEED_MINING))\n      );\n    };\n    $this[UNIT_SERVER_FLEET_NOOB_FACTOR] = function (ValueStorage $vs) {\n      return new ValueBonused(UNIT_SERVER_FLEET_NOOB_FACTOR, floatval($vs->getGlobalContainer()->config->game_noob_factor));\n    };\n\n    $this[UNIT_SERVER_PAYMENT_MM_PER_CURRENCY] = function (ValueStorage $vs) {\n      return new ValueBonused(UNIT_SERVER_PAYMENT_MM_PER_CURRENCY, floatval($vs->getGlobalContainer()->config->payment_currency_exchange_mm_));\n    };\n  }\n\n  /**\n   * Get value object for supplied ID\n   *\n   * Supports only server and player units... for now\n   *\n   * @param int   $unitSnId\n   * @param array $context - Context list of locations: [LOC_xxx => (data)]\n   *\n   * @return ValueBonused|mixed\n   */\n  public function getValueObject($unitSnId, $context = []) {\n    if (isset($this[$unitSnId])) {\n      // Server var\n      $valueObject = $this[$unitSnId];\n    } else {\n      // Not a server var\n      $valueObject = new ValueBonused($unitSnId, $this->getLevelNonServer($unitSnId, $context));\n    }\n\n    if ($valueObject instanceof ValueBonused) {\n      $valueObject->getValue($context);\n    }\n\n    return $valueObject;\n  }\n\n  /**\n   * @param int   $unitSnId\n   * @param array $context - Context list of locations: [LOC_xxx => (data)]\n   *\n   * @return float|int\n   */\n  public function getValue($unitSnId, $context = []) {\n\n    if (($vo = $this->getValueObject($unitSnId, $context)) instanceof ValueBonused) {\n      $result = $vo->getValue();\n    } else {\n      $result = $vo;\n    }\n\n    return $result;\n  }\n\n  /**\n   * @param int   $unitSnId\n   * @param array $context - Context list of locations: [LOC_xxx => (data)]\n   *\n   * @return float|int\n   */\n  public function getBase($unitSnId, $context = []) {\n    if (($vo = $this->getValueObject($unitSnId, $context)) instanceof ValueBonused) {\n      $result = $vo->base;\n    } else {\n      $result = $vo;\n    }\n\n    return $result;\n  }\n\n  /**\n   * @param array $user\n   * @param array $planet\n   * @param int   $unitSnId\n   *\n   * @return int|float|bool\n   */\n  protected function getLevel($user, $planet, $unitSnId) {\n    return mrc_get_level($user, $planet, $unitSnId, true, true);\n  }\n\n  /**\n   * @param int   $unitSnId\n   * @param array $context - Context list of locations: [LOC_xxx => (data)]\n   *\n   * @return int|float|bool\n   */\n  protected function getLevelNonServer($unitSnId, $context = []) {\n//    pdump($unitSnId, 'NON-server');\n//      list($locationType, $locationId) = getLocationFromContext($context);\n//      $fleet = !empty($context[LOC_FLEET]) ? $context[LOC_FLEET] : [];\n    $user = !empty($context[LOC_USER]) ? $context[LOC_USER] : [];\n    $planet = !empty($context[LOC_PLANET]) ? $context[LOC_PLANET] : [];\n\n    return $this->getLevel($user, $planet, $unitSnId);\n  }\n\n}\n"
  },
  {
    "path": "classes/Chat/Chat.php",
    "content": "<?php\n/**\n * Created by Gorlum 13.05.2020 13:02\n */\n\nnamespace Chat;\n\n\nuse classCache;\nuse Common\\Traits\\TSingleton;\nuse SN;\nuse SnTemplate;\n\nclass Chat {\n\n  use TSingleton;\n\n  const CHAT_DEFAULT_LINES_PER_PAGE = 20;\n\n  protected $_chat_aliases = array(\n    'h'         => 'help',\n    '?'         => 'help',\n    'help'      => 'help',\n    'w'         => 'whisper',\n    'whisper'   => 'whisper',\n    'b'         => 'ban',\n    'ban'       => 'ban',\n    'ub'        => 'unban',\n    'unban'     => 'unban',\n    'm'         => 'mute',\n    'mute'      => 'mute',\n    'um'        => 'unmute',\n    'unmute'    => 'unmute',\n    'iv'        => 'invisible',\n    'invisible' => 'invisible',\n    // * /i /ignore\n    // * /ck /kick\n    // * /ci /invite\n    // * /cj /join\n    // * /cc /create\n  );\n\n  protected $_chat_commands = array(\n    'invisible' => array(\n      'access'  => array(0, 1, 2, 3, 4),\n      'options' => array(\n        CHAT_OPTION_SWITCH => CHAT_OPTION_SWITCH,\n      ),\n    ),\n    'whisper'   => array(\n      'access'  => array(0, 1, 2, 3, 4),\n      'options' => array(),\n    ),\n    'mute'      => array(\n      'access'  => array(1, 2, 3, 4),\n      'options' => array(),\n    ),\n    'unmute'    => array(\n      'access'  => array(1, 2, 3, 4),\n      'options' => array(),\n    ),\n    'ban'       => array(\n      'access'  => array(1, 2, 3, 4),\n      'options' => array(),\n    ),\n    'unban'     => array(\n      'access'  => array(1, 2, 3, 4),\n      'options' => array(),\n    ),\n    'help'      => array(\n      'access'  => array(0, 1, 2, 3, 4),\n      'options' => array(),\n    ),\n  );\n\n\n  public static function chatModel() {\n    static::me()->_chatModel();\n  }\n\n  public static function chatAddModel() {\n    static::me()->_chatAddModel();\n  }\n\n  public static function chatView($template = null) {\n    return static::me()->_chatView($template);\n  }\n\n  public static function chatMsgView($template = null) {\n    static::me()->_chatMsgView($template);\n  }\n\n  public static function chatFrameView($template = null) {\n    static::me()->_chatFrameView($template);\n  }\n\n  protected function _chatView($template = null) {\n    defined('IN_AJAX') or define('IN_AJAX', true);\n\n    global $config, $lang;\n\n    $iframe = sys_get_param_id('iframe');\n\n//    $template = $this->addModuleTemplate('chat_body', $template);\n    $template = SnTemplate::gettemplate('chat/chat_body', $template);\n    $template->assign_var('CHAT_REFRESH_RATE', $config->chat_refresh_rate);\n    $template->assign_var('CHAT_MODE', $iframe);\n    $template->assign_var('MENU', !$iframe);\n    $template->assign_var('NAVBAR', !$iframe);\n    $template->assign_var('CHAT_IFRAME', $iframe);\n    foreach ($lang['chat_advanced_command_interval'] as $interval => $locale) {\n      $template->assign_block_vars('chat_advanced_command_interval', array(\n        'INTERVAL' => $interval,\n        'NAME'     => $locale,\n      ));\n    }\n\n    return $template;\n  }\n\n  protected function _chatMsgView($template = null) {\n    defined('IN_AJAX') or define('IN_AJAX', true);\n\n    global $config, $user, $lang;\n\n\n    $history = sys_get_param_str('history');\n    if (!$history) {\n      $config->array_set('users', $user['id'], 'chat_last_refresh', SN_TIME_MICRO);\n      // $chat_player_row = $this->sn_chat_advanced_get_chat_player_record($user['id']);\n      doquery(\"UPDATE {{chat_player}} SET `chat_player_refresh_last` = \" . SN_TIME_NOW . \" WHERE `chat_player_player_id` = {$user['id']} LIMIT 1;\");\n    }\n\n    $page                         = 0;\n    $last_message                 = '';\n    $alliance                     = 0;\n    $template_result['.']['chat'] = array();\n    if (!$history && $config->getMode() != classCache::CACHER_NO_CACHE && $config->chat_timeout && SN_TIME_MICRO - $config->array_get('users', $user['id'], 'chat_last_activity') > $config->chat_timeout) {\n      $result['disable']              = true;\n      $template_result['.']['chat'][] = array(\n        'TIME'    => date(FMT_DATE_TIME, htmlentities(SN_TIME_NOW + SN_CLIENT_TIME_DIFF, ENT_QUOTES, 'utf-8')),\n        'DISABLE' => true,\n      );\n    } else {\n      $alliance = sys_get_param_str('ally') && $user['ally_id'] ? $user['ally_id'] : 0;\n\n      $page_limit = sys_get_param_id('line_per_page', self::CHAT_DEFAULT_LINES_PER_PAGE); // Chat rows Limit\n\n      $where_add    = '';\n      $last_message = 0;\n      if ($history) {\n        $rows       = doquery(\"SELECT count(1) AS CNT\n          FROM {{chat}}\n          WHERE\n          (\n            (ally_id = '{$alliance}' AND `chat_message_recipient_id` IS NULL) OR\n            (ally_id = 0 AND `chat_message_recipient_id` = {$user['id']}) OR\n            (ally_id = 0 AND `chat_message_sender_id` = {$user['id']} AND `chat_message_recipient_id` IS NOT NULL) OR\n            (ally_id = 0 AND `chat_message_sender_id` IS NULL AND `chat_message_recipient_id` IS NULL)\n          )\n        \", true);\n        $page_count = ceil($rows['CNT'] / $page_limit);\n\n        for ($i = 1; $i <= $page_count; $i++) {\n          $template_result['.']['page'][] = array(\n            'NUMBER' => $i\n          );\n        }\n\n        $page = min($page_count, max(1, sys_get_param_int('sheet')));\n      } else {\n        $last_message = sys_get_param_id('last_message');\n        $where_add    = $last_message ? \"AND `messageid` > {$last_message}\" : '';\n        $page         = 1;\n      }\n\n      $start_row = ($page - 1) * $page_limit;// OR `chat_message_recipient_id` = {$user['id']}\n      $start_row = $start_row < 0 ? 0 : $start_row;\n      $query     = doquery(\n        \"SELECT c.*, u.authlevel\n        FROM\n          {{chat}} AS c\n          LEFT JOIN {{users}} AS u ON u.id = c.chat_message_sender_id\n        WHERE\n          (\n            (c.ally_id = '{$alliance}' AND `chat_message_recipient_id` IS NULL) OR\n            (c.ally_id = 0 AND `chat_message_recipient_id` = {$user['id']}) OR\n            (c.ally_id = 0 AND `chat_message_sender_id` = {$user['id']} AND `chat_message_recipient_id` IS NOT NULL) OR\n            (c.ally_id = 0 AND `chat_message_sender_id` IS NULL AND `chat_message_recipient_id` IS NULL)\n          )\n          {$where_add}\n        ORDER BY messageid DESC\n        LIMIT {$start_row}, {$page_limit}\");\n      while ($chat_row = db_fetch($query)) {\n        $chat_row['user']               = player_nick_render_to_html($chat_row['user']);\n        $nick_stripped                  = htmlentities(strip_tags($chat_row['user']), ENT_QUOTES, 'utf-8');\n        $template_result['.']['chat'][] = array(\n          'TIME'                => SN::$gc->bbCodeParser->expandBbCode(date(FMT_DATE_TIME, $chat_row['timestamp'] + SN_CLIENT_TIME_DIFF)),\n          'NICK'                => $chat_row['user'],\n          'NICK_STRIPPED'       => $nick_stripped,\n          'TEXT'                => SN::$gc->bbCodeParser->expandBbCode($chat_row['message'], $chat_row['authlevel']),\n          'SENDER_ID'           => $chat_row['chat_message_sender_id'],\n          'SENDER_NAME'         => $safe_name = sys_safe_output($chat_row['chat_message_sender_name']),\n          'SENDER_NAME_SAFE'    => $safe_name <> $chat_row['chat_message_sender_name'] || strpbrk($safe_name, ' /\\\\\\'') ? '&quot;' . $safe_name . '&quot;' : $safe_name,\n          'RECIPIENT_ID'        => $chat_row['chat_message_recipient_id'],\n          'RECIPIENT_NAME'      => $safe_name_recipient = sys_safe_output($chat_row['chat_message_recipient_name']),\n          'RECIPIENT_NAME_SAFE' => $safe_name_recipient <> $chat_row['chat_message_recipient_name'] || strpbrk($safe_name_recipient, ' /\\\\\\'') ? '&quot;' . $safe_name_recipient . '&quot;' : $safe_name_recipient,\n        );\n\n        $last_message = max($last_message, $chat_row['messageid']);\n      }\n    }\n\n    $template_result['.']['chat'] = array_reverse($template_result['.']['chat']);\n\n    $template_result += array(\n      'PAGE'        => $page,\n      'PAGES_TOTAL' => $page_count,\n      'PAGE_NEXT'   => $page < $page_count ? $page + 1 : $page,\n      'PAGE_PREV'   => $page > 1 ? $page - 1 : $page,\n      'ALLY'        => $alliance,\n      'HISTORY'     => $history,\n      'USER_ID'     => $user['id'],\n    );\n\n//    $template = $this->addModuleTemplate('chat_messages', $template);\n    $template = SnTemplate::gettemplate('chat/chat_messages', $template);\n    $template->assign_recursive($template_result);\n\n    if ($history) {\n      $pageTitle = \"{$lang['chat_history']} - {$lang[$alliance ? 'chat_ally' : 'chat_common']}\";\n      SnTemplate::display($template, $pageTitle);\n    } else {\n      $result['last_message'] = $last_message;\n      $result['html']         = SnTemplate::templateRenderToHtml($template);\n\n//      $template = $this->addModuleTemplate('chat_online', $template);\n      $template                              = SnTemplate::gettemplate('chat/chat_online', null);\n//      $template->assign_recursive($template_result);\n      $chat_online_players                   = 0;\n      $chat_online_invisibles                = 0;\n      $chat_player_invisible                 = 0;\n      $template_result['.']['online_player'] = array();\n      $ally_add                              = $alliance ? \"AND u.ally_id = {$alliance}\" : '';\n\n      $chat_player_list = db_chat_player_list_online($config->chat_refresh_rate, $ally_add);\n\n      // TODO: Добавить ограничение по бану\n      while ($chat_player_row = db_fetch($chat_player_list)) {\n        $chat_online_players++;\n        if ($chat_player_row['chat_player_invisible']) {\n          $chat_online_invisibles++;\n        }\n\n        if (!$chat_player_row['chat_player_invisible'] || $user['authlevel']) {\n          $safe_name                               = sys_safe_output($chat_player_row['username']);\n          $safe_name                               = $chat_player_row['username'] <> $safe_name || strpbrk($safe_name, ' /\\\\\\'') !== false ? '&quot;' . $safe_name . '&quot;' : $safe_name;\n          $template_result['.']['online_player'][] = array(\n            'ID'          => $chat_player_row['id'],\n            'NAME'        => player_nick_render_to_html($chat_player_row, array(\n              'color' => true,\n              'icons' => true,\n              'ally'  => !$alliance,\n              'class' => 'class=\"chat_nick_whisper\" safe_name=\"' . $safe_name . '\"',\n            )),\n            'NAME_SAFE'   => $safe_name,\n            'MUTED'       => $chat_player_row['chat_player_muted'] && $chat_player_row['chat_player_muted'] >= SN_TIME_NOW ? date(FMT_DATE_TIME, $chat_player_row['chat_player_muted']) : false,\n            'MUTE_REASON' => htmlentities($chat_player_row['chat_player_mute_reason'], ENT_COMPAT, 'UTF-8'),\n            'INVISIBLE'   => $chat_player_row['chat_player_invisible'],\n            'AUTH_LEVEL'  => $chat_player_row['authlevel'],\n          );\n        }\n        if ($user['id'] == $chat_player_row['id']) {\n          $chat_player_invisible = $chat_player_row['chat_player_invisible'];\n        }\n      }\n\n      $chat_commands   = &$this->_chat_commands;\n      $template_result = array_merge($template_result, array(\n        'USER_ID'         => $user['id'],\n        'USER_AUTHLEVEL'  => $user['authlevel'],\n        'USER_CAN_BAN'    => in_array($user['authlevel'], $chat_commands['ban']['access']),\n        'USER_CAN_MUTE'   => in_array($user['authlevel'], $chat_commands['mute']['access']),\n        'USER_CAN_UNMUTE' => in_array($user['authlevel'], $chat_commands['unmute']['access']),\n      ));\n      $template->assign_recursive($template_result);\n      $result['online'] = SnTemplate::templateRenderToHtml($template);\n\n      $result['online_players']        = $chat_online_players;\n      $result['online_invisibles']     = $chat_online_invisibles;\n      $result['chat_player_invisible'] = $chat_player_invisible;\n      $result['users_total']           = SN::$config->users_amount;\n      $result['users_online']          = SN::$config->var_online_user_count;\n      print(json_encode($result));\n    }\n    die();\n  }\n\n  protected function _chatFrameView($template = null) {\n    defined('IN_AJAX') or define('IN_AJAX', true);\n\n//    $template = $this->addModuleTemplate('chat_frame', $template);\n    $template = SnTemplate::gettemplate('chat/chat_frame', $template);\n    require_once($template->files['chat_frame']);\n    die();\n  }\n\n\n  protected function _chatModel() {\n    global $user, $template_result, $lang;\n\n    $this->update_chat_activity();\n\n    $mode = sys_get_param_int('mode');\n    switch ($mode) {\n      case CHAT_MODE_ALLY:\n        $template_result['ALLY']      = intval($user['ally_id']);\n        $template_result['CHAT_MODE'] = CHAT_MODE_ALLY;\n        $page_title                   = $lang['chat_ally'];\n      break;\n\n      case CHAT_MODE_COMMON:\n      default:\n        $page_title                   = $lang['chat_common'];\n        $template_result['CHAT_MODE'] = CHAT_MODE_COMMON;\n      break;\n    }\n\n    $template_result['PAGE_HEADER'] = $page_title;\n\n    $template_result['.']['smiles'] = array();\n\n    foreach (SN::$gc->design->getSmilesList() as $auth_level => $replaces) {\n      if ($auth_level > $user['authlevel']) {\n        continue;\n      }\n\n      foreach ($replaces as $bbcode => $filename) {\n        $template_result['.']['smiles'][] = array(\n          'BBCODE'   => htmlentities($bbcode, ENT_COMPAT, 'UTF-8'),\n          'FILENAME' => $filename,\n        );\n      }\n    }\n  }\n\n  protected function _chatAddModel() {\n    defined('IN_AJAX') or define('IN_AJAX', true);\n\n    global $config, $user, $lang;\n//    $chat_commands = get_unit_param(P_CHAT, P_CHAT_COMMANDS);\n//    $chat_aliases  = get_unit_param(P_CHAT, P_CHAT_ALIASES);\n    $chat_commands = &$this->_chat_commands;\n    $chat_aliases  = &$this->_chat_aliases;\n\n    if ($config->getMode() != classCache::CACHER_NO_CACHE && $config->chat_timeout && SN_TIME_MICRO - $config->array_get('users', $user['id'], 'chat_last_activity') > $config->chat_timeout) {\n      die();\n    }\n\n    if (($message = sys_get_param_str_unsafe('message')) && $user['username']) {\n      $this->update_chat_activity();\n\n      $chat_message_sender_id      = 'NULL';\n      $chat_message_sender_name    = '';\n      $chat_message_recipient_id   = $user['id'];\n      $chat_message_recipient_name = SN::$db->db_escape($user['username']);\n      $nick                        = '';\n      $ally_id                     = 0;\n      $chat_command_issued         = '';\n\n      $chat_player_row   = $this->sn_chat_advanced_get_chat_player_record($user['id']);\n      $chat_player_muted = $chat_player_row['chat_player_muted'] && $chat_player_row['chat_player_muted'] >= SN_TIME_NOW ? $chat_player_row['chat_player_muted'] : false;\n      if (preg_match(\"#^\\/([\\w\\?]+)\\s*({on|off|ID\\s*[0-9]+|[а-яёА-ЯЁa-zA-Z0-9\\_\\-\\[\\]\\(\\)\\+\\{\\}]+|\\\".+\\\"}*)*\\s*(.*)#iu\", $message, $chat_command_parsed)) {\n        $chat_command_exists = array_key_exists(strtolower($chat_command_parsed[1]), $chat_aliases) ? true : strtolower($chat_command_parsed[1]);\n        $chat_command_issued = $chat_command_exists === true ? $chat_aliases[$chat_command_parsed[1]] : 'help';\n        if ($chat_command_accessible = in_array($user['authlevel'], $chat_commands[$chat_command_issued]['access'])) {\n          switch ($chat_command_issued) {\n            case 'invisible':\n              //$chat_player_row = $this->sn_chat_advanced_get_chat_player_record($user['id'], '`chat_player_invisible`', true);\n              $chat_directive = strtolower($chat_command_parsed[2]) == 'on' || $chat_command_parsed[2] == 1 ? 1 : (strtolower($chat_command_parsed[2]) == 'off' || (string)$chat_command_parsed[2] === '0' ? 0 : '');\n              if ($chat_directive !== '') {\n                doquery(\"UPDATE {{chat_player}} SET `chat_player_invisible` = {$chat_directive} WHERE `chat_player_player_id` = {$user['id']} LIMIT 1\");\n              } else {\n                $chat_directive = $chat_player_row['chat_player_invisible'];\n              }\n              $message = \"[c=lime]{$lang['chat_advanced_visible'][$chat_directive]}[/c]\";\n            break;\n\n            case 'whisper':\n              if ($chat_player_muted) {\n                $chat_command_issued = '';\n              } elseif ($chat_command_parsed[3] && $chat_command_parsed[2]) {\n                $chat_command_parsed[2] = trim($chat_command_parsed[2], '\"');\n                $recipient_info         = db_user_by_username($chat_command_parsed[2]);\n                $chat_command_parsed[2] = SN::$db->db_escape($chat_command_parsed[2]);\n                if ($recipient_info['id']) {\n                  $message                     = $chat_command_parsed[3];\n                  $nick                        = SN::$db->db_escape(player_nick_compact(player_nick_render_current_to_array($user, array('color' => true, 'icons' => true, 'ally' => false))));\n                  $chat_message_recipient_id   = $recipient_info['id'];\n                  $chat_message_recipient_name = SN::$db->db_escape($recipient_info['username']);\n                  $chat_message_sender_id      = $user['id'];\n                  $chat_message_sender_name    = SN::$db->db_escape($user['username']);\n                } else {\n                  $message = \"[c=red]{$lang['chat_advanced_err_player_name_unknown']}[/c]\";\n                }\n              } elseif (!$chat_command_parsed[2]) {\n                $message = \"[c=red]{$lang['chat_advanced_err_message_player_empty']}[/c]\";\n              } elseif (!$chat_command_parsed[3]) {\n                $message = \"[c=red]{$lang['chat_advanced_err_message_empty']}[/c]\";\n              }\n            break;\n\n            case 'mute':\n            case 'ban':\n            case 'unmute':\n            case 'unban':\n              if ($chat_command_parsed[2] && ($chat_command_parsed[3] || $chat_command_issued == 'unmute' || $chat_command_issued == 'unban')) {\n                $chat_command_parsed[2] = strtolower($chat_command_parsed[2]);\n                if (strpos($chat_command_parsed[2], 'id ') !== false && is_id($player_id = substr($chat_command_parsed[2], 3))) {\n                  $chat_player_subject = db_user_by_id($player_id, false);\n                  if ($chat_player_subject) {\n                    if ($chat_player_subject['id'] == $user['id']) {\n                      $message = \"[c=red]{$lang['chat_advanced_err_player_same']}[/c]\";\n                    } elseif ($chat_player_subject['authlevel'] >= $user['authlevel']) {\n                      $message = \"[c=red]{$lang['chat_advanced_err_player_higher']}[/c]\";\n                    } else {\n                      $chat_message_recipient_id   = 'NULL';\n                      $chat_message_recipient_name = '';\n                      if ($chat_command_issued == 'unmute' || $chat_command_issued == 'unban') {\n                        $temp = SN::$db->db_escape($chat_command_parsed[3]);\n                        if ($chat_command_issued == 'unban') {\n                          sys_admin_player_ban_unset($user, $chat_player_subject, $temp);\n                          $message = $lang['chat_advanced_command_unban'];\n                        } elseif ($chat_command_issued == 'unmute') {\n                          doquery(\"UPDATE {{chat_player}} SET `chat_player_muted` = 0, `chat_player_mute_reason` = '{$temp}' WHERE `chat_player_player_id` = {$chat_player_subject['id']} LIMIT 1\");\n                          $message = $lang['chat_advanced_command_unmute'];\n                        } else {\n                          $message = '';\n                        }\n\n                        if ($message) {\n                          $message = sprintf($message, $chat_player_subject['username']);\n                          $message .= $chat_command_parsed[3] ? sprintf($lang['chat_advanced_command_reason'], $chat_command_parsed[3]) : '';\n                          $message = \"[c=lime]{$message}[/c]\";\n                        }\n                      } elseif (preg_match(\"#(\\d+)(y|m|w|d|h)(\\!)?\\s*(.*)#iu\", $chat_command_parsed[3], $chat_command_parsed_two)) {\n                        //TODO Localize [\\s\\pL\\w]*\n                        $date_to_timestamp = array(\n                          'y' => PERIOD_YEAR,\n                          'm' => PERIOD_MONTH,\n                          'w' => PERIOD_WEEK,\n                          'd' => PERIOD_DAY,\n                          'h' => PERIOD_HOUR,\n                        );\n                        $this->sn_chat_advanced_get_chat_player_record($chat_player_subject['id'], '`chat_player_muted`', false);\n\n                        $term                       = $date_to_timestamp[$chat_command_parsed_two[2]] * $chat_command_parsed_two[1];\n                        $date_compiled              = $term + SN_TIME_NOW;\n                        $chat_command_parsed_two[4] = SN::$db->db_escape($chat_command_parsed_two[4]);\n\n                        doquery(\"UPDATE {{chat_player}} SET `chat_player_muted` = {$date_compiled}, `chat_player_mute_reason` = '{$chat_command_parsed_two[4]}' WHERE `chat_player_player_id` = {$chat_player_subject['id']} LIMIT 1\");\n                        if ($chat_command_issued == 'ban') {\n                          sys_admin_player_ban($user, $chat_player_subject, $term, $chat_command_parsed_two[3] != '!', $chat_command_parsed_two[4]);\n                          $message = $chat_command_parsed_two[3] == '!' ? $lang['chat_advanced_command_ban_no_vacancy'] : $lang['chat_advanced_command_ban'];\n                        } else {\n                          $message = $lang['chat_advanced_command_mute'];\n                        }\n//                        $message = sprintf($message, $chat_player_subject['username'], date(FMT_DATE_TIME, $date_compiled));\n//                        $message .= $chat_command_parsed_two[4] ? sprintf($lang['chat_advanced_command_reason'], $chat_command_parsed_two[4]) : '';\n                        $message = sprintf($message, $chat_player_subject['username'], date(FMT_DATE_TIME, $date_compiled), $chat_command_parsed_two[4] ? sprintf($lang['chat_advanced_command_reason'], $chat_command_parsed_two[4]) : '');\n                        $message = \"[c=red]{$message}[/c]\";\n                      } else {\n                        $message = \"[c=red]{$lang['chat_advanced_err_term_wrong']}[/c]\";\n                      }\n                    }\n                  } else {\n                    $message = \"[c=red]{$lang['chat_advanced_err_player_id_unknown']}[/c]\";\n                  }\n                } else {\n                  $message = \"[c=red]{$lang['chat_advanced_err_player_id_incorrect']}[/c]\";\n                }\n              } elseif (!$chat_command_parsed[2]) {\n                $message = \"[c=red]{$lang['chat_advanced_err_player_id_need']}[/c]\";\n              } elseif (!$chat_command_parsed[3]) {\n                $message = \"[c=red]{$lang['chat_advanced_err_term_need']}[/c]\";\n              }\n            break;\n\n            default:\n              $message                = array();\n              $chat_command_parsed[2] = strtolower($chat_command_parsed[2]);\n\n              $chat_directive = $chat_command_parsed[2] && array_key_exists($chat_command_parsed[2], $chat_aliases) ? $chat_aliases[$chat_command_parsed[2]] : '';\n\n              if (!$chat_directive) {\n                $commands_available = array();\n                $message[]          = $lang['chat_advanced_help_description'];\n                foreach ($chat_commands as $chat_command_listed => $chat_command_info) {\n                  if (in_array($user['authlevel'], $chat_command_info['access'])) {\n                    $commands_available[] = $lang['chat_advanced_help_short'][$chat_command_listed];\n                  }\n                }\n                $message[] = $lang['chat_advanced_help_commands_accessible'] . ' ' . implode(', ', $commands_available);\n              } else {\n                $message[] = sprintf($lang['chat_advanced_help_command'], $chat_directive);\n                $message[] = $lang['chat_advanced_help'][$chat_directive];\n                $aliases   = array();\n                foreach ($chat_aliases as $chat_command_alias => $chat_command_real) {\n                  if ($chat_command_real == $chat_directive) {\n                    $aliases[] = '/' . $chat_command_alias;\n                  }\n                }\n                $message[] = $lang['chat_advanced_help_command_aliases'] . implode(', ', $aliases);\n              }\n              $message = implode(chr(13) . chr(10), $message);\n              $message = \"[c=lime]{$message}[/c]\";\n\n              if ($chat_command_exists !== true) {\n                $message = \"[c=red]{$lang['chat_advanced_err_command_unknown']} \\\"/{$chat_command_exists}\\\"[/c]\" . chr(13) . chr(10) . $message;\n              }\n            break;\n          }\n        } else {\n          $message = \"[c=red]{$lang['chat_advanced_err_command_inacessible']}[/c]\";\n        }\n        $message = \"[b]{$message}[/b]\";\n      }\n\n      if (!$chat_command_issued && !$chat_player_muted) {\n        $chat_message_sender_id      = $user['id'];\n        $chat_message_sender_name    = SN::$db->db_escape($user['username']);\n        $chat_message_recipient_id   = 'NULL';\n        $chat_message_recipient_name = '';\n        $ally_id                     = sys_get_param('ally') && $user['ally_id'] ? $user['ally_id'] : 0;\n        $nick                        = SN::$db->db_escape(player_nick_compact(player_nick_render_current_to_array($user, array('color' => true, 'icons' => true, 'ally' => !$ally_id, 'class' => 'class=\"chat_nick_msg\"'))));\n\n        // Replacing news://xxx link with BBCode\n        $message = preg_replace(\"#news\\:\\/\\/(\\d+)#i\", \"[news=$1]\", $message);\n        // Replacing news URL with BBCode\n        $message = preg_replace(\"#(?:https?\\:\\/\\/(?:.+)?\\/announce\\.php\\?id\\=(\\d+))#\", \"[news=$1]\", $message);\n        $message = preg_replace(\"#(?:https?\\:\\/\\/(?:.+)?\\/index\\.php\\?page\\=battle_report\\&cypher\\=([0-9a-zA-Z]{32}))#\", \"[ube=$1]\", $message);\n\n        if ($color = sys_get_param_str('color')) {\n          $message = \"[c={$color}]{$message}[/c]\";\n        }\n      } elseif (!$chat_command_issued && $chat_player_muted) {\n        $chat_message_recipient_id   = $user['id'];\n        $chat_message_recipient_name = SN::$db->db_escape($user['username']);\n        $message                     = sprintf($lang['chat_advanced_command_mute'], $user['username'], date(FMT_DATE_TIME, $chat_player_muted)) .\n          ($chat_player_row['chat_player_muted_reason'] ? sprintf($lang['chat_advanced_command_mute_reason'], $chat_player_row['chat_player_muted_reason']) : '');\n        $message                     = \"[c=red]{$message}[/c]\";\n      }\n      $message = SN::$db->db_escape($message);\n\n      doquery(\n        \"INSERT INTO\n          {{chat}}\n        SET\n          `user` = '{$nick}',\n          `ally_id` = '{$ally_id}',\n          `message` = '{$message}',\n          `timestamp` = \" . SN_TIME_NOW . \",\n          `chat_message_sender_id` = {$chat_message_sender_id},\n          `chat_message_sender_name` = '{$chat_message_sender_name}',\n          `chat_message_recipient_id` = {$chat_message_recipient_id},\n          `chat_message_recipient_name` = '{$chat_message_recipient_name}'\"\n      );\n    }\n\n    die();\n  }\n\n\n  protected function update_chat_activity($refresh = false) {\n    global $config, $user;\n\n    $config->array_set('users', $user['id'], 'chat_last_activity', SN_TIME_MICRO);\n    $config->array_set('users', $user['id'], 'chat_last_refresh', $refresh ? 0 : SN_TIME_MICRO);\n\n    $activity_row = doquery(\"SELECT `chat_player_id` FROM {{chat_player}} WHERE `chat_player_player_id` = {$user['id']} LIMIT 1\", true);\n    if (!$activity_row) {\n      doquery(\"INSERT INTO {{chat_player}} SET `chat_player_player_id` = {$user['id']}\");\n    } else {\n      doquery(\"UPDATE {{chat_player}} SET `chat_player_activity` = '\" . SN::$db->db_escape(SN_TIME_SQL) . \"' WHERE `chat_player_player_id` = {$user['id']} LIMIT 1\");\n    }\n  }\n\n  protected function sn_chat_advanced_get_chat_player_record($player_id, $fields = '*', $return_data = true) {\n    $result = false;\n    if($player_id) {\n      if(!($result = doquery(\"SELECT {$fields} FROM {{chat_player}} WHERE `chat_player_player_id` = {$player_id} LIMIT 1\", true))) {\n        doquery(\"INSERT INTO {{chat_player}} SET `chat_player_player_id` = {$player_id}\");\n        if($return_data) {\n          $result = doquery(\"SELECT {$fields} FROM {{chat_player}} WHERE `chat_player_player_id` = {$player_id} LIMIT 1\", true);\n        }\n      }\n    }\n\n    return $result;\n  }\n\n\n}\n"
  },
  {
    "path": "classes/Common/AccessAccessors.php",
    "content": "<?php\n\nnamespace Common;\n\n/**\n * Class Common\\AccessAccessors\n *\n * Support accessors for properties: getter, setter, unsetter\n *\n * Direct access to container (passing accessors) made via ArrayAccess like\n *     AccessAccessors[$propertyName]\n *\n * Signature to accessor call:\n *    function ($this, $varName = '', $arg...)\n * $this - current object passing to accessor\n * If $varName omitted then no args can be supplied\n * For setter - third argument is variable value\n *\n * Getter/Unsetter/Issetter is a callable like\n *    function ($that) {}\n *\n * Setter is a callable like\n *    function ($that, $value)  {}\n *\n * To pass accessors and set/get property directly use ArrayAccess (i.e.\n *\n */\nclass AccessAccessors implements \\ArrayAccess {\n  /**\n   * @var AccessorsV2 $accessors\n   */\n  protected $accessors;\n\n  /**\n   * Class name for $_data property\n   *\n   * @var string\n   */\n  protected $_dataClass = 'Common\\AccessMagic';\n  /**\n   * @var \\Common\\Interfaces\\IContainer $_data\n   */\n  protected $_data;\n\n\n  public function __construct() {\n    $this->_data = new $this->_dataClass;\n    $this->accessors = new AccessorsV2();\n  }\n\n  /**\n   * @param AccessorsV2 $accessors\n   */\n  public function setAccessors($accessors) {\n    $this->accessors = $accessors;\n  }\n\n  public function __call($accessor, $arguments) {\n\n    // $varName could be empty - thus we calling some object-wide function\n    $functionName = $accessor . (!empty($arguments[0]) ? $arguments[0] : '');\n    if (isset($this->accessors->$functionName)) {\n      // Inserting link to current object as first argument\n      array_unshift($arguments, $this);\n      $result = $this->accessors->__call($functionName, $arguments);\n\n      return $result;\n    } else {\n      $result = call_user_func_array(array($this->_data, $accessor), $arguments);\n    }\n\n    return $result;\n  }\n\n\n  public function __set($name, $value) {\n    $this->__call(P_ACCESSOR_SET, array($name, $value));\n  }\n\n  public function __get($name) {\n    return $this->__call(P_ACCESSOR_GET, array($name));\n  }\n\n  public function __unset($name) {\n    $this->__call(P_ACCESSOR_UNSET, array($name));\n  }\n\n  public function __isset($name) {\n    return $this->__call(P_ACCESSOR_ISSET, array($name));\n  }\n\n  public function isEmpty() {\n    return $this->_data->isEmpty();\n  }\n\n  public function clear() {\n    $this->_data->clear();\n  }\n\n  /**\n   * Whether a offset exists\n   * @link http://php.net/manual/en/arrayaccess.offsetexists.php\n   *\n   * @param mixed $offset <p>\n   * An offset to check for.\n   * </p>\n   *\n   * @return boolean true on success or false on failure.\n   * </p>\n   * <p>\n   * The return value will be casted to boolean if non-boolean was returned.\n   * @since 5.0.0\n   */\n  public function offsetExists($offset) {\n    return isset($this->_data->$offset);\n  }\n\n  /**\n   * Offset to retrieve\n   * @link http://php.net/manual/en/arrayaccess.offsetget.php\n   *\n   * @param mixed $offset <p>\n   * The offset to retrieve.\n   * </p>\n   *\n   * @return mixed Can return all value types.\n   * @since 5.0.0\n   */\n  public function offsetGet($offset) {\n    return $this->_data->$offset;\n  }\n\n  /**\n   * Offset to set\n   * @link http://php.net/manual/en/arrayaccess.offsetset.php\n   *\n   * @param mixed $offset <p>\n   * The offset to assign the value to.\n   * </p>\n   * @param mixed $value <p>\n   * The value to set.\n   * </p>\n   *\n   * @return void\n   * @since 5.0.0\n   */\n  public function offsetSet($offset, $value) {\n    $this->_data->$offset = $value;\n  }\n\n  /**\n   * Offset to unset\n   * @link http://php.net/manual/en/arrayaccess.offsetunset.php\n   *\n   * @param mixed $offset <p>\n   * The offset to unset.\n   * </p>\n   *\n   * @return void\n   * @since 5.0.0\n   */\n  public function offsetUnset($offset) {\n    unset($this->_data->$offset);\n  }\n\n}\n"
  },
  {
    "path": "classes/Common/AccessLogged.php",
    "content": "<?php\n/**\n * Created by Gorlum 16.06.2017 14:32\n */\n\nnamespace Common;\n\n\nuse Exception;\n\n/**\n * Class AccessLogged\n *\n * Logs property changes. It's necessary for delta and/or partial DB updates\n *\n * On first property change it goes to start values\n *\n * @package Common\n * @deprecated\n */\n// TODO - Should be replaced with AccessLoggedV2\nclass AccessLogged extends AccessLoggedAbstract {\n\n  public function __set($name, $value) {\n    if ($this->_currentOperation === self::ACCESS_SET) {\n      $this->valueSet($name, $value);\n    } else {\n      $this->valueDelta($name, $value);\n    }\n  }\n\n  /**\n   * @param string $name\n   * @param $value\n   *\n   * @throws Exception\n   */\n  protected function valueSet($name, $value) {\n    if ($this->__isset($name)) {\n      $this->blockChange($name);\n\n      $this->_changes[$name] = $value;\n    } else {\n      $this->_startValues[$name] = $value;\n    }\n\n    parent::__set($name, $value);\n  }\n\n  /**\n   * @param string $name\n   * @param mixed $value\n   *\n   * @throws Exception\n   */\n  protected function valueDelta($name, $value) {\n    $this->blockDelta($name);\n\n    !isset($this->_deltas[$name]) ? $this->_deltas[$name] = 0 : false;\n    !isset($this->_startValues[$name]) ? $this->_startValues[$name] = 0 : false;\n\n    $value *= $this->_currentOperation === self::ACCESS_DELTA_DEC ? -1 : +1;\n\n    $this->_deltas[$name] += $value;\n\n    parent::__set($name, parent::__get($name) + $value);\n\n    $this->_currentOperation = self::ACCESS_SET;\n  }\n\n}\n"
  },
  {
    "path": "classes/Common/AccessLoggedAbstract.php",
    "content": "<?php\n/**\n * Created by Gorlum 14.11.2018 0:43\n */\n\nnamespace Common;\n\n\nuse Exception;\n\nabstract class AccessLoggedAbstract extends AccessMagic {\n  const ACCESS_SET = null;\n  const ACCESS_DELTA_INC = +1;\n  const ACCESS_DELTA_DEC = -1;\n\n  /**\n   * Starting values of properties\n   *\n   * @var array $_startValues\n   */\n  protected $_startValues = [];\n  /**\n   * Changed values\n   *\n   * @var array $_changes\n   */\n  protected $_changes = [];\n  /**\n   * Increment/Decrement results AKA deltas\n   *\n   * @var array $_deltas\n   */\n  protected $_deltas = [];\n\n  /**\n   * Current operation: direct store or positive/negative delta which would be applied on next __set() call\n   *\n   * @var int|float $_currentOperation\n   */\n  protected $_currentOperation = self::ACCESS_SET;\n\n  public function clear() {\n    parent::clear();\n    $this->accept();\n  }\n\n  /**\n   * @param string $name\n   *\n   * @throws Exception\n   */\n  protected function blockChange($name) {\n    if (array_key_exists($name, $this->_deltas)) {\n      throw new Exception(get_called_class() . '::' . $name . ' already INCREMENTED/DECREMENTED - can not CHANGE', ERR_ERROR);\n    }\n  }\n\n  /**\n   * @param string $name\n   *\n   * @throws Exception\n   */\n  protected function blockDelta($name) {\n    if (array_key_exists($name, $this->_changes)) {\n      throw new Exception(get_called_class() . '::' . $name . ' already changed - can not use DELTA', ERR_ERROR);\n    }\n  }\n\n//  /**\n//   * Stub to pass call down\n//   *\n//   * @param string $name\n//   * @param mixed  $value\n//   */\n//  public function __set($name, $value) {\n//    parent::__set($name, $value);\n//  }\n\n//  /**\n//   * Stub to pass call down\n//   *\n//   * @param string $name\n//   *\n//   * @return mixed\n//   */\n//  public function __get($name) {\n//    return parent::__get($name);\n//  }\n\n//  public function __set($name, $value) {\n//    if ($this->_currentOperation === self::ACCESS_SET) {\n//      $this->valueSet($name, $value);\n//    } else {\n//      $this->valueDelta($name, $value);\n//    }\n//  }\n\n  /**\n   * @param string $name\n   * @param $value\n   *\n   * @throws Exception\n   */\n  abstract protected function valueSet($name, $value);\n\n  /**\n   * @param string $name\n   * @param mixed $value\n   *\n   * @throws Exception\n   */\n  abstract protected function valueDelta($name, $value);\n\n\n    /**\n   * Mark next set operation as delta increment\n   *\n   * @return $this\n   */\n  public function inc() {\n    $this->_currentOperation = self::ACCESS_DELTA_INC;\n\n    return $this;\n  }\n\n  /**\n   * Mark next set operation as delta decrement\n   *\n   * @return $this\n   */\n  public function dec() {\n    $this->_currentOperation = self::ACCESS_DELTA_DEC;\n\n    return $this;\n  }\n\n  /**\n   * Accepts changes\n   *\n   * Makes current values a start one and resets changes/deltas\n   */\n  public function accept() {\n    $this->_startValues      = $this->values;\n    $this->_currentOperation = self::ACCESS_SET;\n    $this->_changes          = [];\n    $this->_deltas           = [];\n  }\n\n  /**\n   * Rolls changes back\n   */\n  public function reject() {\n    $this->values            = $this->_startValues;\n    $this->_currentOperation = self::ACCESS_SET;\n    $this->_changes          = [];\n    $this->_deltas           = [];\n  }\n\n  /**\n   * Is container was changed?\n   *\n   * @return bool\n   */\n  public function isChanged() {\n    return\n      ! empty($this->_changes)\n      ||\n      ! empty($this->_deltas);\n  }\n\n  public function getChanges() {\n    return $this->_changes;\n  }\n\n  public function getDeltas() {\n    return $this->_deltas;\n  }\n\n}\n"
  },
  {
    "path": "classes/Common/AccessLoggedTranslatedV2.php",
    "content": "<?php\n/**\n * Created by Gorlum 14.11.2018 0:27\n */\n\nnamespace Common;\n\nuse DBAL\\DbFieldDescription;\nuse DBAL\\PropertyDescription;\n\n/**\n * Class AccessLoggedTranslatedV2\n *\n * Class introduces field-to-property translation\n *\n * \"Fields\" - input data with external namespace and vague types translated to \"properties\" - internal representation with own namespace and fixed types\n *\n * @package Common\n */\nclass AccessLoggedTranslatedV2 extends AccessLoggedV2 {\n  /**\n   * Field name translation to property names\n   *\n   * @var string[] $_fieldsToProperties\n   */\n  protected static $_fieldsToProperties = [];\n\n  /**\n   * @var PropertyDescription[] $_fields\n   */\n  protected static $_properties = [];\n  /**\n   * Default values\n   *\n   * @var array $_defaults\n   */\n  protected static $_defaults = [];\n  /**\n   * Mandatory field list\n   *\n   * @var array $_mandatory\n   */\n  protected static $_mandatory = [];\n\n  /**\n   * Imports fields definitions from Storage\n   *\n   * Importing field type, defaults and creating field->property mappings in $_properties array\n   *\n   * @param DbFieldDescription[] $fields\n   */\n  protected function importFieldDefinitions($fields) {\n    static::$_properties = [];\n    static::$_defaults   = [];\n    static::$_mandatory  = [];\n\n    foreach ($fields as $fieldName => $fieldDescription) {\n      $property = new PropertyDescription();\n      $property->fromDbFieldDescription($fieldDescription);\n\n      $propertyName =\n        !empty(static::$_fieldsToProperties[$property->field])\n          ? static::$_fieldsToProperties[$property->field]\n          : $property->field;\n\n      static::$_properties[$propertyName] = $property->setName($propertyName);\n      // TODO - DEFAULT SHOULD BE CONVERTED TO PROPERTY!!!!\n      static::$_defaults[$propertyName]   = $property->default;\n\n      if ($property->mandatory) {\n        static::$_mandatory[$propertyName] = true;\n      }\n    }\n\n//    var_dump(static::$_properties);\n  }\n\n\n  /**\n   * Converts one array (of properties, fields, user inputs, etc) to another with or w/o callback function\n   *\n   * @param array  $array\n   * @param string $callBackName Name of Property callback which will be used. Can be empty for no callback\n   * @param bool   $useDefaults  Should defaults used if property/field is absent from input array\n   * @param bool   $useFieldName Use field name as array key. If empty - property name will be used\n   *\n   * @return array\n   */\n  protected function convertToPropertyWithCallback($array, $callBackName = '', $useDefaults = false, $useFieldName = false) {\n    $result = [];\n\n    foreach (static::$_properties as $propName => $property) {\n      if (array_key_exists($propName, $array)) {\n        $value = $array[$propName];\n      } elseif ($useDefaults) {\n        $value = static::$_defaults[$propName];\n      } else {\n        continue;\n      }\n\n      if (!empty($callBackName) && is_callable($property->$callBackName)) {\n        $call  = $property->$callBackName;\n        $value = $call($value, $property);\n      }\n\n      $result[$useFieldName ? $property->field : $propName] = $value;\n    }\n\n    return $result;\n  }\n\n\n  /**\n   *\n   * (remark) Here too much to redo is with ::convertToPropertyWithCallback()\n   *\n   * @param array $fieldArray\n   *\n   * @return static\n   */\n  public function fromFieldArray(array $fieldArray) {\n    foreach (static::$_properties as $propName => $property) {\n      if (!array_key_exists($property->field, $fieldArray)) {\n        continue;\n      }\n\n      $value = $fieldArray[$property->field];\n\n      if (is_callable($property->toProperty)) {\n        $call  = $property->toProperty;\n        $value = $call($value);\n      }\n\n      $this->$propName = $value;\n    }\n\n    return $this;\n  }\n\n  /**\n   * Converts record to field array\n   *\n   * Field array is basically ready to be saved to Storage (some kind of DB)\n   * Check for mandatory fields also applied\n   *\n   * @param bool $useDefaults - Use default values if there is no original value\n   *\n   * @return array - field array to save\n   */\n  public function toFieldArray($useDefaults = false) {\n    $fieldValues = [];\n\n    // Making local copy of mandatory properties list\n    $mandatory = static::$_mandatory;\n\n    foreach (static::$_properties as $propName => $property) {\n      if (array_key_exists($propName, $this->values)) {\n        $value = $this->$propName;\n      } elseif ($useDefaults) {\n        $value = static::$_defaults[$propName];\n      } else {\n        continue;\n      }\n\n      if (is_callable($property->fromProperty)) {\n        $call  = $property->fromProperty;\n        $value = $call($value);\n      }\n\n      $fieldValues[$property->field] = $value;\n\n      // Removing this property from mandatory list - if it present\n      unset($mandatory[$propName]);\n    }\n\n    if (!empty($mandatory)) {\n      var_dump($mandatory);\n      die('NO MANDATORY FIELDS'); // TODO - Exception\n    }\n\n    return $fieldValues;\n  }\n\n  /**\n   * Convert changes and deltas from properties to fields for update\n   *\n   * @param $propertyArray\n   *\n   * @return array\n   */\n  protected function changesToFields($propertyArray) {\n    return $this->convertToPropertyWithCallback($propertyArray, 'fromProperty', false, true);\n  }\n\n  /**\n   * Mass-assign properties from user input\n   *\n   * Makes internal conversion types\n   * DOES NOT fill default values\n   * TODO - ADD VALIDATION\n   *\n   * @param array $propertyArray\n   */\n  public function assignProperties(array $propertyArray, $validate = false) {\n    $properties = $this->convertToPropertyWithCallback($propertyArray, 'fromUser', false, false);\n\n    foreach ($properties as $propName => $value) {\n      $this->$propName = $value;\n    }\n  }\n\n  /**\n   * Fills empty field with default values\n   */\n  public function fillDefaults() {\n    foreach (static::$_properties as $propertyName => $propertyDescription) {\n      if (!isset($this->$propertyName)) {\n        $this->$propertyName = $propertyDescription->default;\n      }\n    }\n  }\n\n}\n"
  },
  {
    "path": "classes/Common/AccessLoggedV2.php",
    "content": "<?php\n/**\n * Created by Gorlum 16.06.2017 14:32\n */\n\nnamespace Common;\n\n\nuse Exception;\n\n/**\n * Class AccessLoggedV2\n *\n * Logs property changes. It's necessary for delta and/or partial DB updates\n *\n * This class differs from AccessLogged that first assignment does goes into $values and does not fills $_startValues\n *\n * @package Common\n */\nclass AccessLoggedV2 extends AccessLoggedAbstract {\n  public function __set($name, $value) {\n    if ($this->_currentOperation === self::ACCESS_SET) {\n      $this->valueSet($name, $value);\n    } else {\n      $this->valueDelta($name, $value);\n    }\n  }\n\n\n  /**\n   * @param string $name\n   * @param mixed $value\n   *\n   * @throws Exception\n   */\n  protected function valueSet($name, $value) {\n    $this->blockChange($name);\n\n    $this->_changes[$name] = $value;\n\n    parent::__set($name, $value);\n  }\n\n  /**\n   * @param string $name\n   * @param mixed $value\n   *\n   * @throws Exception\n   */\n  protected function valueDelta($name, $value) {\n    $this->blockDelta($name);\n\n    !isset($this->_deltas[$name]) ? $this->_deltas[$name] = 0 : false;\n\n    $value *= $this->_currentOperation === self::ACCESS_DELTA_DEC ? -1 : +1;\n\n    $this->_deltas[$name] += $value;\n\n    parent::__set($name, parent::__get($name) + $value);\n\n    $this->_currentOperation = self::ACCESS_SET;\n  }\n\n}\n"
  },
  {
    "path": "classes/Common/AccessMagic.php",
    "content": "<?php\n\n/**\n * Created by Gorlum 30.07.2016 9:54\n */\n\nnamespace Common;\nuse Core\\GlobalContainer;\nuse \\ArrayObject;\nuse SN;\n\n/**\n * Class AccessMagic\n *\n * Implements all magic methods for accessing non-exists property\n * Used to distinguish how class properties should be accessed like ArrayAccess\n *\n * @package Common\n */\nclass AccessMagic implements Interfaces\\IContainer {\n\n//  /**\n//   * @var GlobalContainer $services\n//   */\n//  protected $services;\n\n  /**\n   * Property values\n   *\n   * @var array|ArrayObject\n   */\n  protected $values = [];\n\n  /**\n   * AccessMagic constructor.\n   *\n   * @param GlobalContainer|null $services\n   */\n  public function __construct(GlobalContainer $services = null) {\n//    $this->services = empty($services) ? SN::$gc : $services;\n  }\n\n\n  /**\n   * Magic setter\n   *\n   * @param string $name\n   * @param mixed  $value\n   */\n  public function __set($name, $value) {\n    $this->values[$name] = $value;\n  }\n\n  /**\n   * Magic checker for property set\n   *\n   * @param string $name\n   *\n   * @return boolean\n   */\n  public function __isset($name) {\n    return array_key_exists($name, $this->values);\n  }\n\n  /**\n   * Magic getter\n   *\n   * @param string $name\n   *\n   * @return mixed\n   */\n  public function __get($name) {\n    return $this->__isset($name) ? $this->values[$name] : null;\n  }\n\n  /**\n   * Magic un-setter\n   *\n   * @param string $name\n   */\n  public function __unset($name) {\n    unset($this->values[$name]);\n  }\n\n  /**\n   * Is container contains no data\n   *\n   * @return bool\n   */\n  public function isEmpty() {\n    return empty($this->values);\n  }\n\n  /**\n   * Clears container contents\n   */\n  public function clear() {\n    $this->values = [];\n  }\n\n  /**\n   * Extracts values as array [$propertyName => $propertyValue]\n   *\n   * @return array|ArrayObject\n   */\n  public function asArray() {\n    return $this->values;\n  }\n\n}\n"
  },
  {
    "path": "classes/Common/AccessorsV2.php",
    "content": "<?php\n/**\n * Created by Gorlum 19.08.2016 20:35\n */\n\nnamespace Common;\n\n/**\n * AccessorsV2 storage\n *\n * V2 will use magic methods - i.e. all accessors name composing would be made in callers\n *\n * TODO - make magic method access to accessors ????????\n *\n * @package Common\n */\nclass AccessorsV2 implements Interfaces\\IContainer {\n\n  /**\n   * Array of accessors - getters/setters/etc\n   *\n   * @var callable[]\n   */\n  protected $accessors = array();\n\n  /**\n   * @var bool[]\n   */\n  protected $shared = array();\n\n  /**\n   * Result of shared function execution\n   *\n   * @var array\n   */\n  protected $executed = array();\n\n  /**\n   * Assign accessor to a named variable\n   *\n   * Different accessors have different signatures - you should look carefully before assigning accessor\n   *\n   * @param string   $functionName\n   * @param callable $callable\n   *\n   * @throws \\Exception\n   */\n  public function __set($functionName, $callable) {\n    if (!is_callable($callable)) {\n      throw new \\Exception(\n        'Error assigning callable in '\n        . get_called_class()\n        . \"::set()! Callable labeled [{$functionName}] is not a callable or not accessible in this scope\"\n      );\n    }\n\n    // Converting method array-callable to closure\n\n//    // This commented code require PHP 5.4+ !!!!!!!!!!\n//    if (is_array($callable) && count($callable) == 2 && is_object($callable[0])) {\n//      $method = new \\ReflectionMethod($callable[0], $callable[1]);\n//      $callable = $method->getClosure($callable[0]);\n//    }\n    if ((is_array($callable) || is_string($callable)) && ($invoker = Invoker::build($callable))) {\n      $callable = $invoker;\n    }\n\n    $this->accessors[$functionName] = $callable;\n  }\n\n  /**\n   * Made shared function - i.e. function called only once\n   *\n   * @param $functionName\n   * @param $callable\n   */\n  public function share($functionName, $callable) {\n    $this->$functionName = $callable;\n    $this->shared[$functionName] = true;\n  }\n\n  /**\n   * @param string $functionName\n   *\n   * @return bool\n   */\n  public function __isset($functionName) {\n    return isset($this->accessors[$functionName]);\n  }\n\n  public function __unset($functionName) {\n    unset($this->accessors[$functionName]);\n    unset($this->shared[$functionName]);\n    unset($this->executed[$functionName]);\n  }\n\n  /**\n   * Gets accessor for later use\n   *\n   * @param string $accessor\n   *\n   * @param string $varName\n   *\n   * @return callable|null\n   */\n  public function __get($functionName) {\n    return isset($this->$functionName) ? $this->accessors[$functionName] : null;\n  }\n\n  /**\n   * @param string $functionName\n   * @param array  $params\n   *\n   * @return mixed\n   * @throws \\Exception\n   */\n  public function __call($functionName, $params) {\n    if (!isset($this->$functionName)) {\n      throw new \\Exception(\"No [{$functionName}] accessor found on \" . get_called_class() . \"::\" . __METHOD__);\n    }\n\n    if (!isset($this->shared[$functionName]) || !array_key_exists($functionName, $this->executed)) {\n      $this->executed[$functionName] = call_user_func_array($this->accessors[$functionName], $params);\n    }\n\n    return $this->executed[$functionName];\n  }\n\n\n  /**\n   * Is container contains no data\n   *\n   * @return bool\n   */\n  public function isEmpty() {\n    return empty($this->accessors);\n  }\n\n  /**\n   * Clears container contents\n   */\n  public function clear() {\n    $this->accessors = array();\n    $this->shared = array();\n    $this->executed = array();\n  }\n\n}\n"
  },
  {
    "path": "classes/Common/ContainerPlus.php",
    "content": "<?php\n\nnamespace Common;\n\nuse Common\\Pimple\\Container;\n\n/**\n * Class ContainerPlus\n *\n * Extends original container to allow access to properties and function to document them\n *\n * @package Pimple\n */\nclass ContainerPlus extends Container implements Interfaces\\IContainer {\n\n  public function __set($name, $value) {\n    $this->offsetSet($name, $value);\n  }\n\n  public function __get($name) {\n    return $this->offsetGet($name);\n  }\n\n  public function __isset($name) {\n    return $this->offsetExists($name);\n  }\n\n  public function __unset($name) {\n    $this->offsetUnset($name);\n  }\n\n  public function __call($name, $arguments) {\n    return call_user_func_array($this->$name, $arguments);\n  }\n\n  /**\n   * Is container contains no data\n   *\n   * @return bool\n   */\n  public function isEmpty() {\n    $keys = $this->keys(); // TODO PHP 5.5\n    return empty($keys);\n  }\n\n  /**\n   * Clears container contents\n   *\n   * @return mixed\n   */\n  public function clear() {\n    throw new \\Exception(get_class() . '::clear() not implemented - inherited from ContainerPlus');\n  }\n\n}\n"
  },
  {
    "path": "classes/Common/EmptyCountableIterator.php",
    "content": "<?php\n/**\n * Created by Gorlum 19.10.2017 15:07\n */\n\nnamespace Common;\n\nuse Common\\Interfaces\\ICountableIterator;\n\nclass EmptyCountableIterator extends \\EmptyIterator implements ICountableIterator {\n\n  /**\n   * Count elements of an object\n   * @link http://php.net/manual/en/countable.count.php\n   * @return int The custom count as an integer.\n   * </p>\n   * <p>\n   * The return value is cast to an integer.\n   * @since 5.1.0\n   */\n  public function count() {\n    return 0;\n  }\n\n}\n"
  },
  {
    "path": "classes/Common/Exceptions/DbalFieldInvalidException.php",
    "content": "<?php\n/**\n * Created by Gorlum 13.07.2017 17:27\n */\n\nnamespace Common\\Exceptions;\n\n\n/**\n * Class DbalFieldInvalidException\n *\n * DBAL exception on invalid ActiveRecord field\n *\n * @package Exceptions\n */\nclass DbalFieldInvalidException extends \\Exception {\n\n}\n"
  },
  {
    "path": "classes/Common/Exceptions/ExceptionSnLocalized.php",
    "content": "<?php\n/**\n * Created by Gorlum 04.06.2017 15:18\n */\n\nnamespace Common\\Exceptions;\n\nuse Throwable;\n\n/**\n * Class ExceptionSnLocalized\n *\n * Localized Exception - uses Message as locale string ID\n *\n * @package Exceptions\n */\nclass ExceptionSnLocalized extends \\Exception {\n\n  /**\n   * @var array $sprintf\n   */\n  protected $sprintf = array();\n\n  /**\n   * ExceptionSnLocalized constructor.\n   *\n   * @param string         $message\n   * @param int            $code\n   * @param Throwable|null $previous\n   * @param array          $sprintf - params for sprintf() call to embed into message\n   */\n  public function __construct($message = \"\", $code = 0, Throwable $previous = null, $sprintf = array()) {\n    parent::__construct($message, $code, $previous);\n    $this->sprintf = $sprintf;\n  }\n\n  /**\n   * @param string $message\n   *\n   * @return string\n   */\n  protected function getPlayerLocalization($message) {\n    global $lang;\n\n    return !empty($lang[$message]) ? $lang[$message] : '';\n  }\n\n  /**\n   * @return string\n   */\n  public function getMessageLocalized() {\n    $message = $this->getPlayerLocalization($this->getMessage());\n    if (is_array($this->sprintf) && !empty($this->sprintf)) {\n      $message = call_user_func_array('sprintf', array_merge(array($message), $this->sprintf));\n    }\n\n    return $message;\n  }\n\n}\n"
  },
  {
    "path": "classes/Common/Hooker/Hooker.php",
    "content": "<?php\n/**\n * Created by Gorlum 18.03.2018 16:31\n */\n\nnamespace Common\\Hooker;\n\nclass Hooker {\n  /**\n   * @var Pimp $pimp\n   */\n  protected $pimp;\n\n  /**\n   * @var callable[][] $clients\n   */\n  protected $clients;\n\n  /**\n   * Hooker constructor.\n   *\n   * @param Pimp $pimp\n   */\n  public function __construct($pimp) {\n    $this->pimp = $pimp;\n  }\n\n  /**\n   * @param callable $callable\n   * @param int      $order\n   */\n  public function addClient($callable, $order = Pimp::ORDER_AS_IS) {\n    // TODO - override\n\n    if (empty($this->clients[$order])) {\n      $this->clients[$order] = [];\n\n      // Rearranging by order\n      asort($this->clients);\n    }\n\n    $this->clients[$order][] = $callable;\n  }\n\n  /**\n   * @param array|mixed $arguments\n   *\n   * @return null|mixed\n   */\n  public function serve($arguments) {\n    $result = null;\n\n    if (!is_array($arguments)) {\n      $arguments = [$arguments];\n    }\n    // Adding link to $result\n    $arguments = array_merge([&$result], $arguments);\n    if (!empty($this->clients)) {\n      foreach ($this->clients as $order => $callables) {\n        foreach ($callables as $callable) {\n          if (is_callable($callable)) {\n            $result = call_user_func_array($callable, $arguments);\n          }\n        }\n      }\n    }\n\n    return $result;\n  }\n\n  /**\n   * Alias for $this->serve()\n   *\n   * @return mixed|null\n   * @see Hooker::serve()\n   */\n  public function __invoke() {\n    // Why it's returns array of params???? Dirty hack here\n    return $this->serve(func_get_args()[0]);\n  }\n\n}\n"
  },
  {
    "path": "classes/Common/Hooker/Pimp.php",
    "content": "<?php\n/**\n * Created by Gorlum 18.03.2018 16:27\n */\n\nnamespace Common\\Hooker;\n\nuse Core\\GlobalContainer;\n\n/**\n * Class Pimp\n *\n * Pimp is a hooker manager\n *\n * @package Common\\Hooker\n */\nclass Pimp {\n  const ORDER_REPLACE = -PHP_INT_MAX + 1; // Replaces current callback (?)\n  const ORDER_FIRST = self::ORDER_REPLACE + 1;\n  const ORDER_AS_IS = 0;\n  const ORDER_LAST = PHP_INT_MAX;\n\n  /**\n   * @var \\Core\\GlobalContainer $gc\n   */\n  protected $gc;\n\n  /**\n   * @var Hooker[] $hookers\n   */\n  protected $hookers = [];\n\n  /**\n   * Pimp constructor.\n   *\n   * @param \\Core\\GlobalContainer $gc\n   */\n  public function __construct($gc) {\n    $this->gc = $gc;\n  }\n\n  /**\n   * @param string   $hookName\n   * @param callable $callable\n   * @param int      $order\n   */\n  public function register($hookName, $callable, $order = Pimp::ORDER_AS_IS) {\n    if (empty($this->hookers[$hookName])) {\n      $this->hookers[$hookName] = new Hooker($this);\n    }\n\n    $this->hookers[$hookName]->addClient($callable, $order);\n  }\n\n  /**\n   * @param $name\n   * @param $arguments\n   *\n   * @return mixed|null\n   */\n  public function __call($name, $arguments) {\n    return\n      !empty($this->hookers[$name])\n        ? $this->hookers[$name]->serve($arguments)\n        : null;\n  }\n\n}\n"
  },
  {
    "path": "classes/Common/Interfaces/IContainer.php",
    "content": "<?php\n\nnamespace Common\\Interfaces;\n/**\n * Created by Gorlum 09.02.2017 11:43\n */\ninterface IContainer {\n\n  /**\n   * @param mixed $name\n   * @param mixed $value\n   *\n   * @return void\n   */\n  public function __set($name, $value);\n\n  /**\n   * @param mixed $name\n   *\n   * @return bool\n   */\n  public function __isset($name);\n\n  /**\n   * @param mixed $name\n   *\n   * @return mixed\n   */\n  public function __get($name);\n\n  /**\n   * @param mixed $name\n   *\n   * @return void\n   */\n  public function __unset($name);\n\n  /**\n   * Is container contains no data\n   *\n   * @return bool\n   */\n  public function isEmpty();\n\n  /**\n   * Clears container contents\n   */\n  public function clear();\n\n\n}\n"
  },
  {
    "path": "classes/Common/Interfaces/ICountableIterator.php",
    "content": "<?php\n/**\n * Created by Gorlum 19.10.2017 15:01\n */\n\nnamespace Common\\Interfaces;\n\n\ninterface ICountableIterator extends \\Iterator, \\Countable {\n\n}\n"
  },
  {
    "path": "classes/Common/Invoker.php",
    "content": "<?php\n/**\n * Created by Gorlum 21.08.2016 13:33\n */\n\nnamespace Common;\n\n/**\n * Class Invoker\n *\n * Invoker incapsulates callable until PHP 5.4+\n *\n * Supports method callable (2 element array), function callable (string) and lambda-functions\n *\n * @package Common\n * @deprecated\n */\n\nclass Invoker {\n\n  protected $callable;\n\n  /**\n   * Invoker constructor.\n   *\n   * @param callable|null $callable\n   */\n  public function __construct($callable) {\n    $this->callable = $callable;\n  }\n\n  /**\n   * @param mixed $callable\n   *\n   * @return static\n   */\n  public static function build($callable) {\n    if (is_array($callable) && count($callable) == 2 && is_object($callable[0])) {\n      return new static($callable);\n    } elseif (is_string($callable) && function_exists($callable)) {\n      return new static($callable);\n    } elseif (is_callable($callable)) {\n      return new static($callable);\n    } else {\n      return new static(null);\n    }\n  }\n\n  public function __invoke() {\n    return is_callable($this->callable) ? call_user_func_array($this->callable, func_get_args()) : null;\n  }\n\n}\n"
  },
  {
    "path": "classes/Common/NullContainer.php",
    "content": "<?php\n/**\n * Created by Gorlum 08.01.2018 15:31\n */\n\nnamespace Common;\n\nuse Core\\Singleton;\n\nclass NullContainer extends Singleton implements Interfaces\\IContainer {\n\n  public function __set($name, $value) {\n  }\n\n  public function __isset($name) {\n    return false;\n  }\n\n  public function __get($name) {\n    return null;\n  }\n\n  public function __unset($name) {\n  }\n\n  /**\n   * Is container contains no data\n   *\n   * @return bool\n   */\n  public function isEmpty() {\n    return true;\n  }\n\n  /**\n   * Clears container contents\n   */\n  public function clear() {\n  }\n\n}\n"
  },
  {
    "path": "classes/Common/OutcomeManager.php",
    "content": "<?php\n/**\n * Created by Gorlum 17.02.2018 23:55\n */\n\nnamespace Common;\n\n/**\n * Class OutcomeManager\n *\n * Manage outcomes for a bunch of random cases\n *\n * @package Common\n */\nclass OutcomeManager implements \\Countable {\n\n  /**\n   * Possible outcomes\n   *\n   * @var array $outcomes\n   */\n  protected $outcomes = [];\n\n  /**\n   * Chances for outcome to roll\n   *\n   * @var int[] $chances\n   */\n  protected $chances = [];\n\n\n  public function __construct() {\n  }\n\n  /**\n   * Roll element from predefined array\n   *\n   * @param iterable|array $iterable - [P_CHANCE => (int), ...(payload)]\n   *\n   * @return mixed|null\n   */\n  public static function rollArray($iterable) {\n    if (!is_iterable($iterable) || empty($iterable)) {\n      return null;\n    }\n\n    $manager = new static();\n\n    foreach ($iterable as $element) {\n      if (!is_array($element) || empty($element[P_CHANCE])) {\n        continue;\n      }\n      $manager->add($element, $element[P_CHANCE]);\n    }\n\n    $result = $manager->rollOutcome();\n    unset($manager);\n\n    return $result;\n  }\n\n  /**\n   * Adds outcome to internal array\n   *\n   * @param mixed $outcome - Outcome which can be selected\n   * @param int   $chance - Chance of this particular outcome. Should be integer. Sum of all chances can be above mt_getrandmax()\n   */\n  public function add($outcome, $chance) {\n    $this->outcomes[] = $outcome;\n    $this->chances[] = $chance;\n  }\n\n  /**\n   * Removed outcome from list\n   *\n   * @param mixed     $outcome - Outcome to be removed\n   * @param bool|null $strict - Strict search flag\n   */\n  public function remove($outcome, $strict = null) {\n    if (($index = array_search($outcome, $this->outcomes, $strict)) !== false) {\n      unset($this->outcomes[$index]);\n      unset($this->chances[$index]);\n    }\n  }\n\n\n  /**\n   * Get max range of all current outcomes to use as maximum in mt_rand()\n   *\n   * @return float|int\n   */\n  public function getMaxRange() {\n    return array_sum($this->chances);\n  }\n\n  /**\n   * Get outcome by rolled chance\n   *\n   * @param int $rolled - Rolled chance\n   *\n   * @return mixed|null - (null) if no outcomes\n   */\n  public function getOutcome($rolled) {\n    foreach ($this->chances as $index => $chance) {\n      if ($rolled <= $chance) {\n        break;\n      }\n      $rolled -= $chance;\n    }\n\n    return array_key_exists($index, $this->outcomes) ? $this->outcomes[$index] : null;\n  }\n\n  /**\n   * All-in-one function\n   *\n   * Code can use this function if it don't want to know what number was rolled\n   *\n   * @return mixed|null\n   */\n  public function rollOutcome() {\n    return $this->getOutcome(mt_rand(1, $this->getMaxRange()));\n  }\n\n  /**\n   * Count elements of an object\n   * @link  http://php.net/manual/en/countable.count.php\n   * @return int The custom count as an integer.\n   * </p>\n   * <p>\n   * The return value is cast to an integer.\n   * @since 5.1.0\n   */\n  public function count() {\n    return count($this->outcomes);\n  }\n\n}\n"
  },
  {
    "path": "classes/Common/Pimple/Container.php",
    "content": "<?php\n\n/*\n * This file is part of Pimple.\n *\n * Copyright (c) 2009 Fabien Potencier\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is furnished\n * to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all\n * copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n\nnamespace Common\\Pimple;\n\n/**\n * Container main class.\n *\n * @author  Fabien Potencier\n */\nclass Container implements \\ArrayAccess\n{\n    private $values = array();\n    private $factories;\n    private $protected;\n    private $frozen = array();\n    private $raw = array();\n    private $keys = array();\n\n    /**\n     * Instantiate the container.\n     *\n     * Objects and parameters can be passed as argument to the constructor.\n     *\n     * @param array $values The parameters or objects.\n     */\n    public function __construct(array $values = array())\n    {\n        $this->factories = new \\SplObjectStorage();\n        $this->protected = new \\SplObjectStorage();\n\n        foreach ($values as $key => $value) {\n            $this->offsetSet($key, $value);\n        }\n    }\n\n    /**\n     * Sets a parameter or an object.\n     *\n     * Objects must be defined as Closures.\n     *\n     * Allowing any PHP callable leads to difficult to debug problems\n     * as function names (strings) are callable (creating a function with\n     * the same name as an existing parameter would break your container).\n     *\n     * @param string $id    The unique identifier for the parameter or object\n     * @param mixed  $value The value of the parameter or a closure to define an object\n     *\n     * @throws \\RuntimeException Prevent override of a frozen service\n     */\n    public function offsetSet($id, $value)\n    {\n        if (isset($this->frozen[$id])) {\n            throw new \\RuntimeException(sprintf('Cannot override frozen service \"%s\".', $id));\n        }\n\n        $this->values[$id] = $value;\n        $this->keys[$id] = true;\n    }\n\n    /**\n     * Gets a parameter or an object.\n     *\n     * @param string $id The unique identifier for the parameter or object\n     *\n     * @return mixed The value of the parameter or an object\n     *\n     * @throws \\InvalidArgumentException if the identifier is not defined\n     */\n    public function offsetGet($id)\n    {\n        if (!isset($this->keys[$id])) {\n            throw new \\InvalidArgumentException(sprintf('Identifier \"%s\" is not defined.', $id));\n        }\n\n        if (\n            isset($this->raw[$id])\n            || !is_object($this->values[$id])\n            || isset($this->protected[$this->values[$id]])\n            || !method_exists($this->values[$id], '__invoke')\n        ) {\n            return $this->values[$id];\n        }\n\n        if (isset($this->factories[$this->values[$id]])) {\n            return $this->values[$id]($this);\n        }\n\n        $raw = $this->values[$id];\n        $val = $this->values[$id] = $raw($this);\n        $this->raw[$id] = $raw;\n\n        $this->frozen[$id] = true;\n\n        return $val;\n    }\n\n    /**\n     * Checks if a parameter or an object is set.\n     *\n     * @param string $id The unique identifier for the parameter or object\n     *\n     * @return bool\n     */\n    public function offsetExists($id)\n    {\n        return isset($this->keys[$id]);\n    }\n\n    /**\n     * Unsets a parameter or an object.\n     *\n     * @param string $id The unique identifier for the parameter or object\n     */\n    public function offsetUnset($id)\n    {\n        if (isset($this->keys[$id])) {\n            if (is_object($this->values[$id])) {\n                unset($this->factories[$this->values[$id]], $this->protected[$this->values[$id]]);\n            }\n\n            unset($this->values[$id], $this->frozen[$id], $this->raw[$id], $this->keys[$id]);\n        }\n    }\n\n    /**\n     * Marks a callable as being a factory service.\n     *\n     * @param callable $callable A service definition to be used as a factory\n     *\n     * @return callable The passed callable\n     *\n     * @throws \\InvalidArgumentException Service definition has to be a closure of an invokable object\n     */\n    public function factory($callable)\n    {\n        if (!method_exists($callable, '__invoke')) {\n            throw new \\InvalidArgumentException('Service definition is not a Closure or invokable object.');\n        }\n\n        $this->factories->attach($callable);\n\n        return $callable;\n    }\n\n    /**\n     * Protects a callable from being interpreted as a service.\n     *\n     * This is useful when you want to store a callable as a parameter.\n     *\n     * @param callable $callable A callable to protect from being evaluated\n     *\n     * @return callable The passed callable\n     *\n     * @throws \\InvalidArgumentException Service definition has to be a closure of an invokable object\n     */\n    public function protect($callable)\n    {\n        if (!method_exists($callable, '__invoke')) {\n            throw new \\InvalidArgumentException('Callable is not a Closure or invokable object.');\n        }\n\n        $this->protected->attach($callable);\n\n        return $callable;\n    }\n\n    /**\n     * Gets a parameter or the closure defining an object.\n     *\n     * @param string $id The unique identifier for the parameter or object\n     *\n     * @return mixed The value of the parameter or the closure defining an object\n     *\n     * @throws \\InvalidArgumentException if the identifier is not defined\n     */\n    public function raw($id)\n    {\n        if (!isset($this->keys[$id])) {\n            throw new \\InvalidArgumentException(sprintf('Identifier \"%s\" is not defined.', $id));\n        }\n\n        if (isset($this->raw[$id])) {\n            return $this->raw[$id];\n        }\n\n        return $this->values[$id];\n    }\n\n    /**\n     * Extends an object definition.\n     *\n     * Useful when you want to extend an existing object definition,\n     * without necessarily loading that object.\n     *\n     * @param string   $id       The unique identifier for the object\n     * @param callable $callable A service definition to extend the original\n     *\n     * @return callable The wrapped callable\n     *\n     * @throws \\InvalidArgumentException if the identifier is not defined or not a service definition\n     */\n    public function extend($id, $callable)\n    {\n        if (!isset($this->keys[$id])) {\n            throw new \\InvalidArgumentException(sprintf('Identifier \"%s\" is not defined.', $id));\n        }\n\n        if (!is_object($this->values[$id]) || !method_exists($this->values[$id], '__invoke')) {\n            throw new \\InvalidArgumentException(sprintf('Identifier \"%s\" does not contain an object definition.', $id));\n        }\n\n        if (!is_object($callable) || !method_exists($callable, '__invoke')) {\n            throw new \\InvalidArgumentException('Extension service definition is not a Closure or invokable object.');\n        }\n\n        $factory = $this->values[$id];\n\n        $extended = function ($c) use ($callable, $factory) {\n            return $callable($factory($c), $c);\n        };\n\n        if (isset($this->factories[$factory])) {\n            $this->factories->detach($factory);\n            $this->factories->attach($extended);\n        }\n\n        return $this[$id] = $extended;\n    }\n\n    /**\n     * Returns all defined value names.\n     *\n     * @return array An array of value names\n     */\n    public function keys()\n    {\n        return array_keys($this->values);\n    }\n\n    /**\n     * Registers a service provider.\n     *\n     * @param ServiceProviderInterface $provider A ServiceProviderInterface instance\n     * @param array                    $values   An array of values that customizes the provider\n     *\n     * @return static\n     */\n    public function register(ServiceProviderInterface $provider, array $values = array())\n    {\n        $provider->register($this);\n\n        foreach ($values as $key => $value) {\n            $this[$key] = $value;\n        }\n\n        return $this;\n    }\n}\n"
  },
  {
    "path": "classes/Common/Pimple/ServiceProviderInterface.php",
    "content": "<?php\n\n/*\n * This file is part of Pimple.\n *\n * Copyright (c) 2009 Fabien Potencier\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is furnished\n * to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all\n * copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n\nnamespace Common\\Pimple;\n\n/**\n * Pimple service provider interface.\n *\n * @author  Fabien Potencier\n * @author  Dominik Zogg\n */\ninterface ServiceProviderInterface\n{\n    /**\n     * Registers services on the given container.\n     *\n     * This method should only be used to configure services and parameters.\n     * It should not get services.\n     *\n     * @param Container $pimple A container instance\n     */\n    public function register(Container $pimple);\n}\n"
  },
  {
    "path": "classes/Common/Tools/VersionCheckerDeprecated.php",
    "content": "<?php\n/**\n * Created by Gorlum 01.10.2017 13:12\n */\n\nnamespace Common\\Tools;\n\nuse \\classConfig;\nuse \\SN;\n\nclass VersionCheckerDeprecated {\n  /**\n   * @var classConfig $config\n   */\n  protected $config;\n\n  /**\n   * @var int $versionCheckResult\n   */\n  public $versionCheckResult = SNC_VER_ERROR_CONNECT;\n\n  /**\n   * @var array $registerResult\n   */\n  public $registerResult = [];\n\n  public static function performCheckVersion() {\n    $that = new static();\n    $that->checkVersion(SNC_MODE_VERSION_CHECK);\n  }\n\n  public static function handleCall() {\n    $that = new static();\n\n    $mode = sys_get_param_int('mode');\n    $ajax = sys_get_param_int('ajax');\n\n    $that->preventDualRegistration($mode, $ajax);\n\n    $that->checkVersion($mode);\n\n    $that->processRegistration($mode);\n\n    if ($ajax) {\n      define('IN_AJAX', true);\n      print($that->versionCheckResult);\n    }\n  }\n\n  public function __construct() {\n    $this->config = SN::$config;\n  }\n\n  /**\n   * @param int $mode - SNC_MODE_xxx constants\n   */\n  protected function checkVersion($mode) {\n    $this->processContent(sn_get_url_contents($this->generateUrl($mode)));\n  }\n\n  /**\n   * @param int $mode - SNC_MODE_xxx constants\n   *\n   * @return string\n   */\n  protected function generateUrl($mode, $debug = false) {\n    $rootServerUrl = $debug ? 'http://localhost/supernova_site/' : 'http://supernova.ws/';\n    $thisSiteUrl = $debug ? 'http://supernova.ws/' : SN_ROOT_VIRTUAL;\n\n    $url =\n      $rootServerUrl . 'version_check.php'\n      . '?mode=' . $mode\n      . '&db=' . DB_VERSION\n      . '&release=' . SN_RELEASE\n      . '&version=' . SN_VERSION\n      . '&key=' . urlencode($this->config->server_updater_key)\n      . '&id=' . urlencode($this->config->server_updater_id)\n      . ($mode == SNC_MODE_REGISTER ? \"&name=\" . urlencode($this->config->game_name) . \"&url=\" . urlencode($thisSiteUrl) : '');\n\n    return $url;\n  }\n\n  /**\n   * @param $content\n   */\n  protected function processContent($content) {\n    if (!$content) {\n      $this->versionCheckResult = SNC_VER_ERROR_CONNECT;\n    } else {\n      if (($decoded = json_decode($content, true)) !== null) {\n        $this->versionCheckResult = isset($decoded['version_check']) ? $decoded['version_check'] : SNC_VER_UNKNOWN_RESPONSE;\n        $this->registerResult = is_array($decoded['site']) ? $decoded['site'] : [];\n      } else {\n        $this->versionCheckResult = $content;\n      }\n    }\n\n    $this->config->pass()->server_updater_check_last = SN_TIME_NOW;\n    $this->config->pass()->server_updater_check_result = $this->versionCheckResult;\n  }\n\n  /**\n   * @param $mode\n   * @param $ajax\n   */\n  protected function preventDualRegistration($mode, $ajax) {\n    if ($mode == SNC_MODE_REGISTER && ($this->config->server_updater_key || $this->config->server_updater_id)) {\n      if ($ajax) {\n        print(SNC_VER_REGISTER_ERROR_REGISTERED);\n      }\n      die();\n    }\n  }\n\n  /**\n   * @param $mode\n   */\n  protected function processRegistration($mode) {\n    if ($mode == SNC_MODE_REGISTER) {\n      $this->versionCheckResult = isset($this->registerResult['result']) ? $this->registerResult['result'] : SNC_VER_UNKNOWN_RESPONSE;\n\n      if ($this->registerResult['result'] == SNC_VER_REGISTER_REGISTERED && $this->registerResult['site_key'] && $this->registerResult['site_id']) {\n        $this->config->pass()->server_updater_key = $this->registerResult['site_key'];\n        $this->config->pass()->server_updater_id = $this->registerResult['site_id'];\n      }\n    }\n  }\n\n}\n"
  },
  {
    "path": "classes/Common/Traits/TContainer.php",
    "content": "<?php\n/**\n * Created by Gorlum 08.01.2018 15:16\n */\n\nnamespace Common\\Traits;\n\nuse Common\\Interfaces\\IContainer;\n\ntrait TContainer {\n  /**\n   * @return ?IContainer\n   */\n  public function _getContainer() {\n    return null;\n  }\n\n  public function __set($name, $value) {\n    if (is_object($this->_getContainer())) {\n      $this->_getContainer()->__set($this->_containerTranslatePropertyName($name), $value);\n    }\n  }\n\n  public function __isset($name) {\n    return is_object($this->_getContainer())\n      ? $this->_getContainer()->__isset($this->_containerTranslatePropertyName($name))\n      : null;\n  }\n\n  public function __get($name) {\n    return is_object($this->_getContainer())\n      ? $this->_getContainer()->__get($this->_containerTranslatePropertyName($name))\n      : null;\n  }\n\n  public function __unset($name) {\n    if (is_object($this->_getContainer())) {\n      $this->_getContainer()->__unset($this->_containerTranslatePropertyName($name));\n    }\n  }\n\n  /**\n   * Is container contains no data\n   *\n   * @return bool\n   */\n  public function isEmpty() {\n    return is_object($this->_getContainer()) ? $this->_getContainer()->isEmpty() : false;\n  }\n\n  /**\n   * Clears container contents\n   */\n  public function clear() {\n    if (is_object($this->_getContainer())) {\n      $this->_getContainer()->clear();\n    }\n  }\n\n  public function __call($name, $arguments) {\n    return is_object($this->_getContainer()) ? call_user_func_array([$this->_getContainer(), $name], $arguments) : null;\n  }\n\n  protected function _containerTranslatePropertyName($name) {\n    return $name;\n  }\n\n}\n"
  },
  {
    "path": "classes/Common/Traits/TJsonSerializable.php",
    "content": "<?php\n/**\n * Created by Gorlum 12.10.2017 13:20\n */\n\nnamespace Common\\Traits;\n\n\ntrait TJsonSerializable {\n\n  /**\n   * @param $json\n   *\n   * @return static\n   */\n  public static function fromJson($json) {\n    $stdobj = json_decode($json);\n    $temp = serialize($stdobj);\n\n    // Replacing default object name with ours\n    $temp = preg_replace('@^O:8:\"stdClass\":@', 'O:' . strlen(static::class) . ':\"' . static::class . '\":', $temp);\n    // Converting default objects to arrays\n    $temp = preg_replace('@O:8:\"stdClass\":@', 'a:', $temp);\n\n    //Unserialize and walk away like nothing happened\n    $that = unserialize($temp);\n\n//    var_dump($that);\n\n    return $that;\n  }\n\n  /**\n   * @return string\n   */\n  public function toJson() {\n    return json_encode($this);\n  }\n\n}\n"
  },
  {
    "path": "classes/Common/Traits/TSingleton.php",
    "content": "<?php\n/**\n * Created by Gorlum 03.09.2019 1:26\n */\n\nnamespace Common\\Traits;\n\n\ntrait TSingleton {\n\n  private static $_me = null;\n\n  /**\n   * @return static|null\n   */\n  public static function me() {\n    if (empty(static::$_me)) {\n      static::$_me = new static();\n    }\n\n    return static::$_me;\n  }\n\n}\n"
  },
  {
    "path": "classes/Common/Vector.php",
    "content": "<?php\n\nnamespace Common;\n\nuse classConfig;\n\nclass Vector {\n\n  const READ_VECTOR = 'readVector';\n  const READ_PARAMS_FLEET = 'readParamsFleet';\n\n  public static $knownGalaxies = 0;\n  public static $knownSystems = 0;\n  public static $knownPlanets = 0;\n  public static $galaxyDistance = 20000;\n  protected static $_isStaticInit = false;\n\n  public $galaxy = 0;\n  public $system = 0;\n  public $planet = 0;\n  public $type = PT_NONE;\n\n  /**\n   * @param classConfig $config\n   */\n  public static function _staticInit($config) {\n    if (static::$_isStaticInit) {\n      return;\n    }\n\n    static::$knownGalaxies = intval($config->game_maxGalaxy);\n    static::$knownSystems = intval($config->game_maxSystem);\n    static::$knownPlanets = intval($config->game_maxPlanet);\n    static::$galaxyDistance = intval($config->uni_galaxy_distance);\n    static::$_isStaticInit = true;\n  }\n\n  /**\n   * @param Vector $vector\n   */\n  public function readFromVector($vector) {\n    $this->galaxy = $vector->galaxy;\n    $this->system = $vector->system;\n    $this->planet = $vector->planet;\n    $this->type = $vector->type;\n  }\n\n  /**\n   * @param string $paramName\n   * @param array  $planetRow\n   *\n   * @return int\n   */\n  protected function getParamInt($paramName, $planetRow) {\n    $default = empty($planetRow[$paramName]) ? 0 : $planetRow[$paramName];\n\n    return sys_get_param_int($paramName, $default);\n  }\n\n  /**\n   * @param array $planetRow\n   */\n  public function readFromParamFleets($planetRow = array()) {\n    $this->galaxy = $this->getParamInt('galaxy', $planetRow);\n    $this->system = $this->getParamInt('system', $planetRow);\n    $this->planet = $this->getParamInt('planet', $planetRow);\n    $this->type = $this->getParamInt('planet_type', $planetRow);\n  }\n\n  /**\n   * Vector constructor.\n   *\n   * @param int|string       $galaxy Vector::READ_VECTOR or Vector::READ_PARAMS_FLEET or integer `galaxy` coordinate\n   * @param int|Vector|array $system If `galaxy` = Vector::READ_VECTOR - instance of Vector class to copy values from\n   *                                 If `galaxy` = Vector::READ_PARAMS_FLEET - array representing `fleet` table record\n   *                                 Otherwise - integer `system` coordinate\n   * @param int              $planet\n   * @param int              $type\n   */\n  public function __construct($galaxy = 0, $system = 0, $planet = 0, $type = PT_NONE) {\n    // static::_staticInit();\n\n    if (is_string($galaxy) && $galaxy == Vector::READ_VECTOR && is_object($system) && $system instanceof Vector) {\n      $this->readFromVector($system);\n    } elseif (is_string($galaxy) && $galaxy == Vector::READ_PARAMS_FLEET && is_array($system)) {\n      $this->readFromParamFleets($system);\n    } else {\n      $this->galaxy = intval($galaxy);\n      $this->system = intval($system);\n      $this->planet = intval($planet);\n      $this->type = intval($type);\n    }\n  }\n\n  /**\n   * @param Vector $vector\n   * @param bool   $returnZero\n   *\n   * @return int|number\n   */\n  public function distance($vector, $returnZero = false) {\n    if ($this->galaxy != $vector->galaxy) {\n      $distance = abs($this->galaxy - $vector->galaxy) * static::$galaxyDistance;\n    } elseif ($this->system != $vector->system) {\n      $distance = abs($this->system - $vector->system) * 5 * 19 + 2700;\n    } elseif ($this->planet != $vector->planet) {\n      $distance = abs($this->planet - $vector->planet) * 5 + 1000;\n    } elseif ($returnZero && $this->type == $vector->type) {\n      // && $this->type != PT_NONE && $vector->type != PT_NONE\n      $distance = 0;\n    } else {\n      $distance = 5;\n    }\n\n    return $distance;\n  }\n\n  /**\n   * @param array  $coordinates\n   * @param string $prefix\n   *\n   * @return static\n   */\n  public static function convertToVector($coordinates, $prefix = '') {\n    $vector = new static();\n    $vector->convertToVectorDynamic($coordinates, $prefix);\n\n    return $vector;\n  }\n\n  /**\n   * @param array  $coordinates\n   * @param string $prefix\n   *\n   * @return static\n   */\n  public function convertToVectorDynamic($coordinates, $prefix = '') {\n    $this->galaxy = isset($coordinates[$prefix . 'galaxy']) ? intval($coordinates[$prefix . 'galaxy']) : 0;\n    $this->system = isset($coordinates[$prefix . 'system']) ? intval($coordinates[$prefix . 'system']) : 0;\n    $this->planet = isset($coordinates[$prefix . 'planet']) ? intval($coordinates[$prefix . 'planet']) : 0;\n    $this->type = isset($coordinates[$prefix . 'type'])\n      ? isset($coordinates[$prefix . 'type'])\n      : (isset($coordinates[$prefix . 'planet_type']) ? intval($coordinates[$prefix . 'planet_type']) : 0);\n\n    return $this;\n  }\n\n  /**\n   * @param string $prefix\n   *\n   * @return array\n   */\n  public function toArray($prefix = '') {\n    $array = array(\n      $prefix . 'galaxy' => $this->galaxy,\n      $prefix . 'system' => $this->system,\n      $prefix . 'planet' => $this->planet,\n      $prefix . 'type'   => $this->type,\n    );\n\n    return $array;\n  }\n\n  /**\n   * @param array $coordinates\n   * @param bool  $returnZero\n   *\n   * @return int|number\n   */\n  public function distanceFromCoordinates($coordinates, $returnZero = false) {\n    return $this->distance(static::convertToVector($coordinates), $returnZero);\n  }\n\n  /**\n   * @param array $from\n   * @param array $to\n   * @param bool  $returnZero\n   *\n   * @return int|number\n   */\n  public static function distanceBetweenCoordinates($from, $to, $returnZero = false) {\n    return static::convertToVector($from)->distanceFromCoordinates($to, $returnZero);\n  }\n\n  /**\n   * @param array $planetRow\n   *\n   * @return bool\n   */\n  public function isSameLocation($planetRow) {\n    return $this->distanceFromCoordinates($planetRow, true) == 0;\n  }\n\n  /**\n   * @return bool\n   */\n  public function isInUniverse() {\n    return\n      $this->galaxy >= 1 && $this->galaxy <= static::$knownGalaxies &&\n      $this->system >= 1 && $this->system <= static::$knownSystems;\n  }\n\n  /**\n   * @return bool\n   */\n  public function isInSystem() {\n    return $this->planet >= 1 && $this->planet <= static::$knownPlanets;\n  }\n\n  /**\n   * @return bool\n   */\n  public function isInKnownSpace() {\n    return $this->isInUniverse() && $this->isInSystem();\n  }\n\n}\n"
  },
  {
    "path": "classes/Common/oldArrayAccessNd.php",
    "content": "<?php\n\nnamespace Common;\nuse ArrayAccess;\n\n/**\n * Класс упрощает операции с многомерными индексами для ArrayAccess - старая версия\n * Многомерные индексы могут передаваться в $offset в виде массива\n * Например: array('test', 1, 2, 3) будет соответствовать обращению test[1][2][3]\n *\n * Таким образом работа с многомерными массивами может быть спроецирована на любой объект, который умеет в пары Ключ-Значение и поддерживает стандартные magic methods __isset, __get, __set и __unset\n *\n * Если объект-потомок поддерживает отложенную запись - ему нужно реализовать так же функцию __flush()\n *\n */\nabstract class oldArrayAccessNd implements ArrayAccess {\n\n  abstract public function __get($offset);\n\n  abstract public function __set($offset, $value = null);\n\n  abstract public function __isset($offset);\n\n  abstract public function __unset($offset);\n\n  public function __flush() {\n    return true;\n  }\n\n  /**\n   * (PHP 5 &gt;= 5.0.0)<br/>\n   * Whether a offset exists\n   * @link http://php.net/manual/en/arrayaccess.offsetexists.php\n   *\n   * @param mixed $offset <p>\n   * An offset to check for.\n   * </p>\n   *\n   * @return boolean true on success or false on failure.\n   * </p>\n   * <p>\n   * The return value will be casted to boolean if non-boolean was returned.\n   */\n  public function offsetExists($offset) {\n    !is_array($offset) ? $offset = array($offset) : false;\n\n    $current_leaf = $this->__get(reset($offset));\n    while (($leaf_index = next($offset)) !== false) {\n      if (!isset($current_leaf) || !is_array($current_leaf) || !isset($current_leaf[$leaf_index])) {\n        unset($current_leaf);\n        break;\n      }\n      $current_leaf = $current_leaf[$leaf_index];\n    }\n\n    return isset($current_leaf);\n  }\n\n  /**\n   * (PHP 5 &gt;= 5.0.0)<br/>\n   * Offset to retrieve\n   * @link http://php.net/manual/en/arrayaccess.offsetget.php\n   *\n   * @param mixed $offset <p>\n   * The offset to retrieve.\n   * </p>\n   *\n   * @return mixed Can return all value types.\n   */\n  public function offsetGet($offset) {\n    $result = null;\n\n    !is_array($offset) ? $offset = array($offset) : false;\n\n    if ($this->offsetExists($offset)) {\n      $result = $this->__get(reset($offset));\n      while (($leaf_index = next($offset)) !== false) {\n        $result = $result[$leaf_index];\n      }\n    }\n\n    return $result;\n  }\n\n\n  /**\n   * (PHP 5 &gt;= 5.0.0)<br/>\n   * Offset to set\n   * @link http://php.net/manual/en/arrayaccess.offsetset.php\n   *\n   * @param mixed $offset <p>\n   * The offset to assign the value to.\n   * </p>\n   * @param mixed $value <p>\n   * The value to set.\n   * </p>\n   *\n   * @return void\n   */\n  public function offsetSet($offset, $value = null) {\n    // Если нет никакого индекса - значит нечего записывать\n    if (!isset($offset) || (is_array($offset) && empty($offset))) {\n      return;\n    }\n\n    // Если в массиве индекса только один элемент - значит это просто индекс\n    if (is_array($offset) && count($offset) == 1) {\n      // Разворачиваем его в индекс\n      $offset = array(reset($offset) => $value);\n      unset($value);\n      // Дальше будет использоваться стандартный код для пары $option, $value\n    }\n\n    // Адресация многомерного массива через массив индексов в $option\n    if (is_array($offset) && isset($value)) {\n      // TODO - а не переделать ли это всё на __isset() ??\n      // Вытаскиваем корневой элемент\n      $root = $this->__get(reset($offset));\n      $current_leaf = &$root;\n      while (($leaf_index = next($offset)) !== false) {\n        !is_array($current_leaf[$leaf_index]) ? $current_leaf[$leaf_index] = array() : false;\n        $current_leaf = &$current_leaf[$leaf_index];\n      }\n      if ($current_leaf != $value) {\n        $current_leaf = $value;\n        // Сохраняем данные с корня\n        $this->__set(reset($offset), $root);\n      }\n    } else {\n      // Пакетная запись из массива ключ -> значение\n      !is_array($offset) ? $offset = array($offset => $value) : false;\n\n      foreach ($offset as $key => $value) {\n        $this->__get($key) !== $value ? $this->__set($key, $value) : false;\n      }\n    }\n\n    $this->__flush(); // Сбрасывем кэш - если есть его поддержка\n  }\n\n  /**\n   * (PHP 5 &gt;= 5.0.0)<br/>\n   * Offset to unset\n   * @link http://php.net/manual/en/arrayaccess.offsetunset.php\n   *\n   * @param mixed $offset <p>\n   * The offset to unset.\n   * </p>\n   *\n   * @return void\n   */\n  public function offsetUnset($offset) {\n    // Если нет никакого индекса - значит нечего записывать\n    if (!isset($offset) || (is_array($offset) && empty($offset))) {\n      return;\n    }\n\n    !is_array($offset) ? $offset = array($offset) : false;\n\n    if ($this->offsetExists($offset)) {\n      // Перематываем массив в конец\n      $key_to_delete = end($offset);\n      $parent_offset = $offset;\n      array_pop($parent_offset);\n      if (!count($parent_offset)) {\n        // В массиве был один элемент - мы удаляем в корне. Просто удаляем элемент\n        $this->__unset($key_to_delete);\n      } else {\n        // Получаем родительское дерево\n        $parent_element = $this->offsetGet($parent_offset);\n        // Удаляем из него элемент\n        unset($parent_element[$key_to_delete]);\n        // Записываем измененное родительское дерево назад\n        $this->offsetSet($parent_offset, $parent_element);\n      }\n    }\n\n    $this->__flush(); // Сбрасывем кэш - если есть его поддержка\n  }\n}\n"
  },
  {
    "path": "classes/Confirmation.php",
    "content": "<?php\n\nuse DBAL\\db_mysql;\n\n/**\n * User: Gorlum\n * Date: 17.09.2015\n * Time: 14:11\n */\nclass Confirmation {\n\n  /**\n   * @var db_mysql\n   */\n  protected $db = null;\n\n  public function __construct($db) {\n    $this->db = $db;\n  }\n\n  // TODO - НЕ ОБЯЗАТЕЛЬНО ОТПРАВЛЯТЬ ЧЕРЕЗ ЕМЕЙЛ! ЕСЛИ ЭТО ФЕЙСБУЧЕК ИЛИ ВКШЕЧКА - МОЖНО ЧЕРЕЗ ЛС ПИСАТЬ!!\n  // TODO - OK 4.6\n  public function db_confirmation_get_latest_by_type_and_email($confirmation_type_safe, $email_unsafe) {\n    $email_safe = $this->db->db_escape($email_unsafe);\n\n    return $this->db->doQueryAndFetch(\n      \"SELECT * \n      FROM {{confirmations}} \n      WHERE\n        `type` = {$confirmation_type_safe} \n        AND `email` = '{$email_safe}' \n      ORDER BY create_time DESC \n      LIMIT 1;\"\n    );\n  }\n  // TODO - OK 4.6\n  public function db_confirmation_delete_by_type_and_email($confirmation_type_safe, $email_unsafe) {\n    $email_safe = $this->db->db_escape($email_unsafe);\n\n    return $this->db->doquery(\n      \"DELETE FROM {{confirmations}} WHERE `type` = {$confirmation_type_safe} AND `email` = '{$email_safe}'\"\n    );\n  }\n  // TODO - OK 4.6\n  public function db_confirmation_get_unique_code_by_type_and_email($confirmation_type_safe, $email_unsafe) {\n    $email_safe = $this->db->db_escape($email_unsafe);\n\n    do {\n      // Ну, если у нас > 999.999 подтверждений - тут нас ждут проблемы...\n      $confirm_code_safe = $this->db->db_escape($confirm_code_unsafe = $this->make_password_reset_code());\n      // $query = static::$db->doquery(\"SELECT `id` FROM {{confirmations}} WHERE `code` = '{$confirm_code_safe}' AND `type` = {$confirmation_type_safe} FOR UPDATE\", true);\n      // Тип не нужен для проверки - код подтверждения должен быть уникален от слова \"совсем\"\n      $query = $this->db->doQueryAndFetch(\n        \"SELECT `id` FROM {{confirmations}} WHERE `code` = '{$confirm_code_safe}' FOR UPDATE\"\n      );\n    } while($query);\n\n    $this->db->doquery(\n      \"REPLACE INTO {{confirmations}}\n        SET `type` = {$confirmation_type_safe}, `code` = '{$confirm_code_safe}', `email` = '{$email_safe}';\"\n    );\n\n    return $confirm_code_unsafe;\n  }\n  // TODO - OK 4.6\n  public function db_confirmation_get_by_type_and_code($confirmation_type_safe, $confirmation_code_unsafe) {\n    $confirmation_code_safe = $this->db->db_escape($confirmation_code_unsafe);\n\n    return $this->db->doQueryAndFetch(\n      \"SELECT * \n      FROM {{confirmations}} \n      WHERE\n        `type` = {$confirmation_type_safe} \n        AND `code` = '{$confirmation_code_safe}' \n      ORDER BY create_time DESC \n      LIMIT 1 FOR UPDATE\"\n    );\n  }\n\n  protected function make_password_reset_code() {\n    return sys_random_string(LOGIN_PASSWORD_RESET_CONFIRMATION_LENGTH, SN_SYS_SEC_CHARS_CONFIRMATION);\n  }\n\n}\n"
  },
  {
    "path": "classes/Core/Autoloader.php",
    "content": "<?php\n/**\n * Created by Gorlum 18.06.2017 13:24\n */\n\nnamespace Core;\n\n/**\n * Class Autoloader\n *\n * One of core class to supply autoload facilities to the engine\n *\n * @package Core\n */\nclass Autoloader {\n  const P_FOLDER = 'P_FOLDER';\n  const P_PREFIX = 'P_PREFIX';\n\n  /**\n   * @var string[][] $folders - [[P_FOLDER => (str)$absoluteFolder, P_PREFIX => (str)$prefixToIgnore]]\n   */\n  protected static $folders = [];\n\n  protected static $autoloaderRegistered = false;\n\n  protected static function _constructorStatic() {\n    if(!static::$autoloaderRegistered) {\n      spl_autoload_register(array(__CLASS__, 'autoloader'));\n      static::$autoloaderRegistered = true;\n    }\n  }\n\n  /**\n   * @param string $class - Fully-qualified path with namespaces\n   */\n  public static function autoloader($class) {\n    static::_constructorStatic();\n\n    foreach(static::$folders as $data) {\n      $theClassFile = $class;\n\n      if($data[static::P_PREFIX] && strrpos($class, $data[static::P_PREFIX]) !== false) {\n        $theClassFile = substr($class, strlen($data[static::P_PREFIX]));\n      }\n\n      $classFullFileName = str_replace('\\\\', '/', $data[static::P_FOLDER] . $theClassFile) . DOT_PHP_EX;\n      if(file_exists($classFullFileName) && is_file($classFullFileName)) {\n        require_once($classFullFileName);\n        if(method_exists($class, '_constructorStatic')) {\n          $class::_constructorStatic();\n        }\n      }\n    }\n  }\n\n  /**\n   * @param string $absoluteClassRoot - absolute path to root class folder\n   * @param string $classPrefix - PHP class prefix to ignore. Can be whole namespace or part of it\n   */\n  public static function register($absoluteClassRoot, $classPrefix = '') {\n    static::_constructorStatic();\n\n    $absoluteClassRoot = str_replace('\\\\', '/', SN_ROOT_PHYSICAL . $absoluteClassRoot);\n\n    if(!($absoluteClassRoot = realpath($absoluteClassRoot))) {\n      // TODO - throw new \\Exception(\"There is some error when installing autoloader for '{$absoluteClassRoot}' class prefix '{$classPrefix}'\");\n      return;\n    }\n    $absoluteClassRoot = str_replace('\\\\', '/', $absoluteClassRoot) . '/';\n\n    if($classPrefix && substr($classPrefix, -1) != '\\\\') {\n      $classPrefix .= '\\\\';\n    }\n\n    static::$folders[] = [\n      static::P_FOLDER => $absoluteClassRoot,\n      static::P_PREFIX => $classPrefix,\n    ];\n  }\n\n//  /**\n//   * @param string $relativeClassRoot - relative path to root class folder from game root (where index.php lies)\n//   * @param string $classPrefix - PHP class prefix to ignore. Can be whole namespace or part of it\n//   */\n//  public static function registerRelative($relativeClassRoot, $classPrefix = '') {\n//    static::register(SN_ROOT_PHYSICAL . $relativeClassRoot, $classPrefix);\n//  }\n\n  public static function reset() {\n    static::$folders = [];\n  }\n\n}\n"
  },
  {
    "path": "classes/Core/Crypto.php",
    "content": "<?php\n/**\n * Created by Gorlum 13.02.2020 11:49\n */\n\nnamespace Core;\n\n/**\n * Cryptography & signature-related tools\n *\n * @package Core\n */\nclass Crypto {\n  /**\n   * @var GlobalContainer $gc\n   */\n  protected $gc;\n\n  public function __construct(GlobalContainer $gc) {\n    $this->gc = $gc;\n  }\n\n  /**\n   * @param string $string\n   *\n   * @return string\n   */\n  public function sign($string) {\n    return $this->hash($string);\n  }\n\n  public function signCheck($string, $sign) {\n    return $this->hash($string) === $sign;\n  }\n\n  /**\n   * Used hash function\n   *\n   * @param $string\n   *\n   * @return string\n   */\n  public function hash($string) {\n    return md5($string);\n  }\n\n}\n"
  },
  {
    "path": "classes/Core/Entity.php",
    "content": "<?php\n/**\n * Created by Gorlum 08.01.2018 14:47\n */\n\nnamespace Core;\n\n/**\n * Class Entity\n *\n * Represents base in-game entity\n *\n * @package Core\n */\n\nclass Entity {\n\n  public function __construct() {\n    // Abstract class - just to remember\n  }\n\n}\n"
  },
  {
    "path": "classes/Core/EntityDb.php",
    "content": "<?php\n/**\n * Created by Gorlum 08.01.2018 14:46\n */\n\nnamespace Core;\n\n\nuse Common\\Interfaces\\IContainer;\nuse DBAL\\db_mysql;\nuse \\DBAL\\DbQuery;\nuse \\DBAL\\ActiveRecord;\nuse Common\\Traits\\TContainer;\nuse Exception;\nuse SN;\n\n/**\n * Basic persistent entity class (lives in DB)\n *\n * Support direct ActiveRecord access\n * Support locked reads on transactions\n *\n * Represents in-game entity which have representation in DB (aka one or more connected ActiveRecords)\n *\n * @package Core\n *\n * @property int|string $id - bigint -\n *\n * @method array asArray() Extracts values as array [$propertyName => $propertyValue] (from ActiveRecord)\n * @method bool update() Updates DB record(s) in DB (from ActiveRecord)\n */\nclass EntityDb extends Entity implements IContainer {\n  use TContainer;\n\n  /**\n   * @var string $_activeClass\n   */\n  protected $_activeClass = ''; // \\\\DBAL\\\\ActiveRecord\n\n  /**\n   * Name translation table\n   *\n   * [entityPropertyName => containerPropertyName]\n   *\n   * @var string[] $_containerTranslateNames\n   */\n  protected $_containerTranslateNames = [];\n\n\n  /**\n   * @var ActiveRecord $_container\n   */\n  protected $_container;\n\n  protected $_isNew = true;\n  protected $_forUpdate = DbQuery::DB_SHARED;\n\n  /**\n   * EntityDb constructor.\n   */\n  public function __construct() {\n    if (empty($this->_activeClass)) {\n      /** @noinspection PhpUnhandledExceptionInspection */\n      throw new Exception(\"Class \" . get_called_class() . \" have no _activeClass property declared!\");\n    }\n\n    $this->reset();\n  }\n\n  /**\n   * @return $this\n   */\n  public function reset() {\n//    unset($this->_container); // Strange thing - after this string it is not possible to set _container property again!\n    $this->_container = new $this->_activeClass();\n\n    $this->_isNew     = true;\n    $this->_forUpdate = DbQuery::DB_SHARED;\n\n    return $this;\n  }\n\n  /**\n   * Reload Entity - if possible\n   *\n   * If entity is write-once - this method should decide if any real I/O would be performed\n   *\n   * @return static\n   *\n   * @throws Exception\n   */\n  public function reload() {\n    // TODO - immutable entities which does not need reload\n    $dbId = !empty($this->_container) ? $this->_container->id : 0;\n\n    // If entity is new or no DB ID supplied - throw exception\n    if ($this->isNew() || !$dbId) {\n      /** @noinspection PhpUnhandledExceptionInspection */\n      throw new Exception(\"Can't reload empty or new Entity: isNew = '{$this->isNew()}', dbId = '{$dbId}', className = \" . get_called_class());\n    }\n\n    return $this->dbLoadRecord($dbId);\n  }\n\n\n  /**\n   * Is entity is new\n   *\n   * True for newly created and deleted entities\n   * Code should decide this from context\n   *\n   * @return bool\n   */\n  public function isNew() {\n    return $this->_isNew;\n  }\n\n  /**\n   * Load entity from DB\n   *\n   * This basic class supports transaction locking\n   *\n   * If entity is multi-tabled - this method should care about loading and arrange all necessary information\n   * If descendant entity is lock-sensitive - this method should issue all necessary locks (including parental locks)\n   *\n   * @param int|float $id\n   *\n   * @return $this\n   */\n  public function dbLoadRecord($id) {\n    $this->reset();\n\n    if (db_mysql::db_transaction_check(false)) {\n      $this->setForUpdate();\n    }\n\n    /** @var ActiveRecord $className */\n    $className = $this->_activeClass;\n    $container = $className::findById($id);\n    if (!empty($container)) {\n      $this->_isNew     = false;\n      $this->_container = $container;\n    }\n\n    return $this;\n  }\n\n  /**\n   * Set \"for update\" flag\n   *\n   * @param bool $forUpdate - DbQuery::DB_FOR_UPDATE | DbQuery::DB_SHARED\n   *\n   * @return $this\n   */\n  public function setForUpdate($forUpdate = DbQuery::DB_FOR_UPDATE) {\n    /** @var ActiveRecord $className */\n    $className = $this->_activeClass;\n    $className::setForUpdate($forUpdate);\n\n    return $this;\n  }\n\n  /**\n   * @return bool\n   */\n  public function dbUpdate() {\n    return $this->_getContainer()->update();\n  }\n\n  /**\n   * @return ActiveRecord\n   */\n  public function _getContainer() {\n    return $this->_container;\n  }\n\n  /**\n   * Translate entity property name to container property name\n   *\n   * Just a little sugar to avoid renaming all and everywhere\n   *\n   * @param string $name\n   *\n   * @return string\n   */\n  protected function _containerTranslatePropertyName($name) {\n    return !empty($this->_containerTranslateNames[$name]) ? $this->_containerTranslateNames[$name] : $name;\n  }\n\n  /**\n   * Saves entity to DB. Also handles updates (and in future - deletes. DELETE CURRENTLY NOT SUPPORTED!)\n   *\n   * @return bool\n   * @throws \\Exception\n   */\n  public function save() {\n    $result = false;\n\n    if ($this->isNew()) {\n      // New record - INSERT\n\n      // TODO - some checks that fleet recrod is valid itself\n      // May be something like method isValid()?\n\n      $result = !$this->isEmpty() && $this->_getContainer()->insert() && SN::$gc->repoV2->set($this);\n    } elseif (!$this->isEmpty()) {\n      // Record not new and not empty - UPDATE\n      $result = $this->_getContainer()->update();\n    } else {\n      // Record not new and empty - DELETE\n\n    }\n\n    return $result;\n  }\n\n}\n"
  },
  {
    "path": "classes/Core/GlobalContainer.php",
    "content": "<?php\n\nnamespace Core;\n\nuse BBCodeParser;\nuse Bonus\\BonusCatalog;\nuse Bonus\\ValueStorage;\nuse classCache;\nuse classConfig;\nuse Common\\ContainerPlus;\nuse Core\\Scheduler\\Watchdog;\nuse DBAL\\db_mysql;\nuse DBAL\\StorageSqlV2;\nuse debug;\nuse Design;\nuse Fleet\\FleetDispatcher;\nuse Modules\\ModulesManager;\nuse Pm\\PlayerIgnore;\nuse SkinModel;\nuse SkinV2;\nuse \\SN;\nuse \\General;\nuse \\Meta\\Economic\\EconomicHelper;\nuse Player\\PlayerLevelHelper;\nuse /** @noinspection PhpDeprecationInspection */ Storage;\nuse TextModel;\nuse TheUser;\n\n\n/**\n * Class GlobalContainer\n *\n * Used to describe internal structures of container\n *\n * Variables ------------------------------------------------------------------------------------------------------------\n * @property string            $cachePrefix\n *\n * Services ------------------------------------------------------------------------------------------------------------\n * @property HttpRequest       $request\n * @property debug             $debug\n * @property db_mysql          $db\n * @property classCache        $cache\n * @property classConfig       $config\n * @property Crypto            $crypto\n * @property Repository        $repository\n * @property Storage           $storage\n * @property RepoV2            $repoV2\n * @property StorageV2         $storageV2\n * @property StorageSqlV2      $storageSqlV2\n * @property Design            $design\n * @property BBCodeParser      $bbCodeParser\n * @property FleetDispatcher   $fleetDispatcher\n * @property Watchdog          $watchdog\n *\n * @property ValueStorage      $valueStorage\n * @property BonusCatalog      $bonusCatalog\n *\n * @property General           $general\n * @property EconomicHelper    $economicHelper\n *\n * @property PlayerLevelHelper $playerLevelHelper\n * @property SnPimp            $pimp\n *\n * @property Worker            $worker\n *\n * @property ModulesManager         $modules\n *\n * @property PlayerIgnore           $ignores\n *\n * Dummy objects -------------------------------------------------------------------------------------------------------\n * @property TheUser               $theUser\n *\n * Models --------------------------------------------------------------------------------------------------------------\n * @property TextModel             $textModel\n * @property string                 $skinEntityClass\n * @property SkinModel             $skinModel\n *\n * @package Common\n *\n */\n\n// * Unused --------------------------------------------------------------------------------------------------------------\n// * @property \\Common\\Types        $types\n// * @property \\DbQueryConstructor  $query\n// * @property \\DbRowDirectOperator $dbGlobalRowOperator\n// * @property \\SnDbCachedOperator $cacheOperator - really DB record operator. But let it be\n// * @property \\classLocale         $localePlayer\n// *\n// * @property string $snCacheClass\n// * @property \\SnCache $snCache\n// *\n// * @property string $buddyClass\n// * @property \\Buddy\\BuddyModel $buddyModel\n// *\n// * @property \\V2Unit\\V2UnitModel $unitModel\n// * @property \\V2Unit\\V2UnitList $unitList\n// *\n// * @property V2FleetModel $fleetModel\n// *\n// * @property PlanetRenderer $planetRenderer\n// * @property \\FleetRenderer $fleetRenderer\n// * @property $dbOperator - makes CRUD to DB:\n\nclass GlobalContainer extends ContainerPlus {\n\n  public function __construct(array $values = array()) {\n    parent::__construct($values);\n\n    $gc = $this;\n\n    // Services --------------------------------------------------------------------------------------------------------\n    // Default db\n    $gc->request = function (GlobalContainer $c) {\n      $httpRequest = new HttpRequest($c);\n      $httpRequest->fillCurrent();\n\n      return $httpRequest;\n    };\n\n    $gc->db = function (GlobalContainer $c) {\n      SN::$db = new db_mysql($c);\n\n      return SN::$db;\n    };\n\n    $gc->debug = function (/** @noinspection PhpUnusedParameterInspection */\n      GlobalContainer $c) {\n      return new debug();\n    };\n\n    $gc->cache = function (GlobalContainer $gc) {\n      return new classCache($gc->cachePrefix);\n    };\n\n    $gc->config = function (GlobalContainer $gc) {\n      return new classConfig($gc->cachePrefix);\n    };\n\n    $gc->crypto = function (GlobalContainer $gc) {\n      return new Crypto($gc);\n    };\n\n    $gc->worker = function (GlobalContainer $gc) {\n      return new Worker($gc);\n    };\n\n    $gc->repository = function (GlobalContainer $gc) {\n      /** @noinspection PhpDeprecationInspection */\n      return new Repository($gc);\n    };\n\n    $gc->storage = function (GlobalContainer $gc) {\n      /** @noinspection PhpDeprecationInspection */\n      return new Storage($gc);\n    };\n\n    $gc->repoV2 = function (GlobalContainer $gc) {\n      return new RepoV2($gc);\n    };\n\n    $gc->storageV2 = function (GlobalContainer $gc) {\n      return new StorageV2($gc);\n    };\n\n    $gc->storageSqlV2 = function (GlobalContainer $gc) {\n      return new StorageSqlV2($gc);\n    };\n\n    $gc->design = function (GlobalContainer $gc) {\n      return new Design($gc);\n    };\n\n    $gc->bbCodeParser = function (GlobalContainer $gc) {\n      return new BBCodeParser($gc);\n    };\n\n    $gc->fleetDispatcher = function (GlobalContainer $gc) {\n      return new FleetDispatcher($gc);\n    };\n\n    $gc->watchdog = function (GlobalContainer $gc) {\n      return new Watchdog($gc);\n    };\n\n    $gc->valueStorage = function (/** @noinspection PhpUnusedParameterInspection */ GlobalContainer $gc) {\n      return new ValueStorage([]);\n    };\n\n    $gc->bonusCatalog = function (GlobalContainer $gc) {\n      return new BonusCatalog($gc);\n    };\n\n    $gc->general = function (GlobalContainer $gc) {\n      return new General($gc);\n    };\n\n    $gc->economicHelper = function (GlobalContainer $gc) {\n      return new EconomicHelper($gc);\n    };\n\n    $gc->playerLevelHelper = function (GlobalContainer $gc) {\n      return new PlayerLevelHelper($gc);\n    };\n\n    $gc->pimp = function (GlobalContainer $gc) {\n      return new SnPimp($gc);\n    };\n\n    $gc->modules = function (GlobalContainer $gc) {\n      return new ModulesManager($gc);\n    };\n\n    // Dummy objects ---------------------------------------------------------------------------------------------------\n    $gc->theUser = function (GlobalContainer $gc) {\n      return new TheUser($gc);\n    };\n\n\n    // Models ----------------------------------------------------------------------------------------------------------\n    $gc->skinEntityClass = SkinV2::class;\n    $gc->skinModel       = function (GlobalContainer $gc) {\n      return new SkinModel($gc);\n    };\n\n    $gc->textModel = function (GlobalContainer $gc) {\n      return new TextModel($gc);\n    };\n\n    $gc->ignores = function (GlobalContainer $gc) {\n      return new PlayerIgnore($gc);\n    };\n\n//    $gc->types = function ($c) {\n//      return new \\Common\\Types();\n//    };\n//\n//    $gc->dbOperator = function (GlobalContainer $c) {\n//      return new \\classConfig(SN::$cache_prefix);\n//    };\n//\n//    $gc->localePlayer = function (GlobalContainer $c) {\n//      return new \\classLocale($c->config->server_locale_log_usage);\n//    };\n//\n//    $gc->dbGlobalRowOperator = function (GlobalContainer $c) {\n//      return new \\DbRowDirectOperator($c->db);\n//    };\n//\n//    $gc->query = $gc->factory(function (GlobalContainer $c) {\n//      return new \\DbQueryConstructor($c->db);\n//    });\n//\n//    $gc->cacheOperator = function (GlobalContainer $gc) {\n//      return new \\SnDbCachedOperator($gc);\n//    };\n//\n//    $gc->snCacheClass = 'SnCache';\n//    $gc->snCache = function (GlobalContainer $gc) {\n//      return $gc->db->snCache;\n//    };\n//\n//    $gc->buddyClass = 'Buddy\\BuddyModel';\n//    $gc->buddyModel = function (GlobalContainer $c) {\n//      return new $c->buddyClass($c);\n//    };\n//\n//    $gc->unitModel = function (GlobalContainer $c) {\n//      return new \\V2Unit\\V2UnitModel($c);\n//    };\n//    $gc->unitList = $this->factory(function (GlobalContainer $c) {\n//      return new \\V2Unit\\V2UnitList($c);\n//    });\n//\n//    $gc->fleetModel = function (GlobalContainer $c) {\n//      return new V2FleetModel($c);\n//    };\n//\n//    $gc->planetRenderer = function (GlobalContainer $c) {\n//      return new PlanetRenderer($c);\n//    };\n//\n//    $gc->fleetRenderer = function (GlobalContainer $c) {\n//      return new \\FleetRenderer($c);\n//    };\n//\n//    $gc->groupFleet = function (GlobalContainer $c) {\n//      return sn_get_groups('fleet');\n//    };\n//\n//    $gc->groupFleetAndMissiles = function (GlobalContainer $c) {\n//      return sn_get_groups(array('fleet', GROUP_STR_MISSILES));\n//    };\n//\n//    $gc->groupRecyclers = function (GlobalContainer $c) {\n//      return sn_get_groups('flt_recyclers');\n//    };\n\n  }\n\n}\n"
  },
  {
    "path": "classes/Core/HttpRequest.php",
    "content": "<?php\n/**\n * Created by Gorlum 13.02.2020 10:28\n */\n\nnamespace Core;\n\nclass HttpRequest {\n  const REQUEST_GET = 'GET';\n  const REQUEST_POST = 'POST';\n\n  /**\n   * @var GlobalContainer $gc\n   */\n  protected $gc;\n\n  public $method = '';\n  /**\n   * @var HttpUrl $url\n   */\n  public $url;\n\n  public function __construct(GlobalContainer $gc) {\n    $this->gc = $gc;\n\n    $this->url = new HttpUrl($this->gc);\n  }\n\n  public function fillCurrent() {\n    $this->url->fillCurrent();\n\n    $this->method = strtoupper($_SERVER['REQUEST_METHOD']);\n\n  }\n\n}\n"
  },
  {
    "path": "classes/Core/HttpUrl.php",
    "content": "<?php\n/**\n * Created by Gorlum 13.02.2020 10:46\n */\n\nnamespace Core;\n\n\nclass HttpUrl {\n  const SCHEME_HTTPS = 'https';\n  const SCHEME_HTTP = 'http';\n\n  const FIELD_SIGNATURE = 'sign';\n  const FIELD_TIMESTAMP_MICRO = '_sn_ts_micro';\n\n  /**\n   * @var GlobalContainer $gc\n   */\n  public $gc;\n\n  /**\n   * Scheme - `http` or `https`\n   *\n   * @var string $scheme\n   */\n  public $scheme = '';\n  /**\n   * Host address\n   *\n   * @var string $host\n   */\n  public $host = '';\n  /**\n   * Path from server root WITH starting slash\n   *\n   * @var string $path\n   */\n  public $path = '';\n  /**\n   * [(str)$paramName => [(mixed)$paramValue, ...], ...]\n   *\n   * @var string[][]\n   */\n  public $params = [];\n  /**\n   * Secure string known only to server for internal call exchange\n   *\n   * @var string $cypher\n   */\n  public $cypher = '';\n\n  /**\n   * A bit of syntax sugar\n   *\n   * @param GlobalContainer $gc\n   *\n   * @return static\n   */\n  public static function spawn(GlobalContainer $gc) {\n    return new static($gc);\n  }\n\n  public function __construct(GlobalContainer $gc) {\n    $this->gc = $gc;\n  }\n\n  public function parseUrl($url) {\n    $parsed = explode('://', $url);\n\n    if (is_array($parsed) && count($parsed) > 1 && strtolower($parsed[0]) === self::SCHEME_HTTPS) {\n      $this->scheme = self::SCHEME_HTTPS;\n    } else {\n      $this->scheme = self::SCHEME_HTTP;\n    }\n\n    if (is_array($parsed)) {\n      array_shift($parsed);\n    } else {\n      $parsed = [];\n    }\n\n    $server = explode('/', implode('://', $parsed));\n    if (is_array($server) && !empty($server)) {\n      $this->host = $server[0];\n      array_shift($server);\n    } else {\n      $this->host = [];\n    }\n\n    return $this->explodeUri(implode('/', $server));\n  }\n\n  /**\n   * Fills URI from current URL\n   *\n   * @return $this\n   */\n  public function fillCurrent() {\n    if (\n      (!empty($_SERVER['HTTPS']) && strtolower($_SERVER['HTTPS']) === 'on')\n      ||\n      (!empty($_SERVER['REQUEST_SCHEME']) && strtolower($_SERVER['REQUEST_SCHEME']) === self::SCHEME_HTTPS)\n    ) {\n      $this->scheme = self::SCHEME_HTTPS;\n    } else {\n      $this->scheme = self::SCHEME_HTTP;\n    }\n\n    $this->host = !empty($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : '';\n\n    $this->path   = '';\n    $this->params = [];\n\n    return $this->explodeUri($_SERVER['REQUEST_URI']);\n  }\n\n  /**\n   * Return server root - i.e. host with scheme like http://localhost/\n   * DO NOT adds trailing slash\n   *\n   * @return string\n   */\n  public function serverRoot() {\n    return \"{$this->scheme}://{$this->host}\";\n  }\n\n  public function url() {\n    return $this->serverRoot() . $this->uri();\n  }\n\n  public function uri() {\n    return $this->path . $this->renderGetParams();\n  }\n\n  /**\n   * @param bool $timeStamp\n   *\n   * @return string\n   */\n  public function urlSigned($timeStamp = true) {\n    if ($timeStamp) {\n      $this->params[self::FIELD_TIMESTAMP_MICRO] = SN_TIME_MICRO;\n    }\n\n    $this->removeSignature();\n\n    $signature = $this->gc->crypto->sign($this->url() . $this->cypher);\n\n    $this->params[self::FIELD_SIGNATURE] = $signature;\n\n    return $this->url();\n  }\n\n  /**\n   * Checks signature in URL\n   *\n   * @return bool\n   */\n  public function isSigned() {\n    $isMatched = false;\n\n    $signatureExists = $this->isSignaturePresent();\n    if ($signatureExists) {\n      $signature = $this->removeSignature();\n      // Checking signature\n      $isMatched = $this->gc->crypto->signCheck($this->url() . $this->cypher, $signature);\n      // Re-adding signature to URL\n      $this->params[self::FIELD_SIGNATURE] = $signature;\n    }\n\n    return $isMatched;\n  }\n\n\n  /**\n   * Explodes provided URI to path and params\n   *\n   * @param string $uri URI part of URL - i.e. path from server root with params\n   *\n   * @return $this\n   */\n  protected function explodeUri($uri) {\n    if (!is_string($uri) || empty($uri)) {\n      return $this;\n    }\n\n    $exploded   = explode('?', $uri);\n    $this->path = array_shift($exploded);\n    if (substr($this->path, 0, 1) !== '/') {\n      $this->path = '/' . $this->path;\n    }\n\n    // Re-imploding left URI parts for a case of malformed request\n    $this->params = $this->parseGetParams(implode('?', $exploded));\n\n    return $this;\n  }\n\n  /**\n   * @param string $paramString\n   *\n   * @return array\n   */\n  protected function parseGetParams($paramString) {\n    $result = [];\n\n    if (!is_string($paramString) || empty($paramString)) {\n      return $result;\n    }\n\n    $params = explode('&', $paramString);\n    if (!is_array($params) || empty($params)) {\n      return $result;\n    }\n\n    foreach ($params as $paramString) {\n      $parsed = explode('=', $paramString);\n\n      $paramName  = array_shift($parsed);\n      $paramValue = implode('&', $parsed);\n\n      // TODO - indexed params support\n      $result[$paramName] = $paramValue;\n    }\n\n    return $result;\n  }\n\n  protected function renderGetParams() {\n    $result = '';\n    if (empty($this->params) || !is_array($this->params)) {\n      return $result;\n    }\n\n    $query = [];\n    foreach ($this->params as $paramName => $paramValue) {\n      $query[] = urlencode($paramName) . '=' . urlencode($paramValue);\n    }\n\n    return '?' . implode('&', $query);\n  }\n\n  public function removeSignature() {\n    $result = '';\n    if ($this->isSignaturePresent()) {\n      $result = $this->params[self::FIELD_SIGNATURE];\n      unset($this->params[self::FIELD_SIGNATURE]);\n    }\n\n    return $result;\n  }\n\n  public function setCypher($cypher) {\n    $this->cypher = $cypher;\n\n    return $this;\n  }\n\n  /**\n   * @param array|string $params\n   *\n   * @return HttpUrl\n   */\n  public function addParams($params) {\n    if (is_string($params)) {\n      $params = $this->parseGetParams($params);\n    }\n\n    if (is_array($params) && !empty($params)) {\n      foreach ($params as $paramName => $value) {\n        $this->params[$paramName] = $value;\n      }\n    }\n\n    return $this;\n  }\n\n  public function addPath($path) {\n    if (substr($this->path, -1) !== '/') {\n      $this->path .= '/';\n    }\n    $this->path .= $path;\n\n    return $this;\n  }\n\n  /**\n   * @return bool\n   */\n  public function isSignaturePresent() {\n    return array_key_exists(self::FIELD_SIGNATURE, $this->params);\n  }\n\n}\n"
  },
  {
    "path": "classes/Core/RepoV2.php",
    "content": "<?php\n/**\n * Created by Gorlum 21.04.2018 16:13\n */\n\nnamespace Core;\n\nuse Common\\Interfaces\\IContainer;\nuse DBAL\\db_mysql;\nuse Exception;\nuse Fleet\\Fleet;\nuse Planet\\Planet;\nuse SN;\n\n/**\n * Entity repository\n *\n * Caches Entities for further use\n *\n * Support synonyms to hold class parents along with a children\n * Support transactions\n * DOES NOT support locking\n *\n * @package Core\n */\nclass RepoV2 implements IContainer {\n\n  /**\n   * List of synonyms\n   *\n   * @var string[] $synonyms\n   */\n  protected $synonyms = [];\n\n  /**\n   * @var EntityDb[][] $repo\n   */\n  protected $repo = [];\n\n  /**\n   * @var int[][] $versionId\n   */\n  protected $versionId = [];\n\n  /**\n   * Core\\Repository constructor.\n   *\n   * @param GlobalContainer $gc\n   */\n  public function __construct(GlobalContainer $gc) { }\n\n  /**\n   * @param string $className\n   * @param string $synonym\n   *\n   * @return $this\n   */\n  public function addSynonym($className, $synonym) {\n    $this->synonyms[$className] = $synonym;\n\n    return $this;\n  }\n\n\n  /**\n   * @param string     $className\n   * @param int|string $objectId\n   *\n   * @return EntityDb|null\n   */\n  public function get($className, $objectId) {\n    return $this->is_set($className, $objectId) ? $this->repo[$className][$objectId] : null;\n  }\n\n  /**\n   * Get entity version\n   *\n   * @param $className\n   * @param $objectId\n   *\n   * @return int\n   */\n  protected function version($className, $objectId) {\n    return !empty($version = $this->versionId[$className][$objectId]) ? $version : -1;\n  }\n\n  /**\n   * Retrieve entity from repository or load it from DB (by entity's own load method)\n   *\n   * Supports transactions and version tracking (between transactions)\n   *\n   * @param string     $className - fully qualified class name\n   * @param int|string $objectId  - entity ID\n   *\n   * @return EntityDb|null\n   *\n   * @throws Exception\n   */\n  public function getOrLoad($className, $objectId) {\n    if ($this->is_set($className, $objectId)) {\n      // If entity exists in repo - getting it\n      $entity = $this->get($className, $objectId);\n\n      // If in transaction and version missmatch - record should be refreshed just for a case\n      if (db_mysql::db_transaction_check(false) && $this->version($className, $objectId) < db_mysql::$transaction_id) {\n        // Entity will care by itself - should it be really reloaded or not\n        $entity->reload();\n      }\n\n      if ($entity->isNew()) {\n        $this->un_set($className, $objectId);\n      }\n    } else {\n      /**\n       * @var EntityDb $entity\n       */\n      $entity = new $className();\n      $entity->dbLoadRecord($objectId);\n\n      if (!$entity->isNew()) {\n        $this->set($entity);\n      }\n    }\n\n    if ($entity->isNew()) {\n      unset($entity);\n      $entity = null;\n    }\n\n    return $entity;\n  }\n\n  /**\n   * @param int|string $planetId\n   *\n   * @return Planet|EntityDb|null\n   *\n   * @throws Exception\n   */\n  public function getPlanet($planetId) {\n    return $this->getOrLoad(Planet::class, $planetId);\n  }\n\n  /**\n   * @param int|string $fleetId\n   *\n   * @return Fleet|EntityDb|null\n   *\n   * @throws Exception\n   */\n  public function getFleet($fleetId) {\n    return $this->getOrLoad(Fleet::class, $fleetId);\n  }\n\n\n  /**\n   * Writes entity to repository\n   *\n   * Entity should not be already in registry - otherwise exception raised\n   *\n   * @param EntityDb $value\n   *\n   * @return bool\n   * @throws Exception\n   */\n  public function set($value) {\n    $className = $this->getClassStoreName(get_class($value));\n    $objectId  = $value->id;\n\n    if (empty($value->id)) {\n      throw new Exception(\"Trying to add in registry entity [\" . implode(',', [$className, $objectId]) . \"] with zero objectId\");\n    }\n\n    if ($this->is_set($className, $objectId) && $this->get($className, $objectId) !== $value) {\n      throw new Exception(\"Trying to overwrite entity [\" . implode(',', [$className, $objectId]) . \"] which already set. Unset it first!\");\n    }\n\n    $this->repo[$className][$objectId]      = $value;\n    $this->versionId[$className][$objectId] = db_mysql::$transaction_id;\n\n    return true;\n  }\n\n\n  /**\n   * @param string $className\n   *\n   * @return string\n   */\n  protected function getClassStoreName($className) {\n    return !empty($this->synonyms[$className]) ? $this->synonyms[$className] : $className;\n  }\n\n  /**\n   * @param string     $className\n   * @param int|string $objectId\n   *\n   * @return bool\n   */\n  public function is_set($className, $objectId) {\n    return is_array($this->repo[$className]) && isset($this->repo[$className][$objectId]);\n  }\n\n  /**\n   * @param string     $className\n   * @param int|string $objectId\n   *\n   * @return void\n   */\n  public function un_set($className, $objectId) {\n    unset($this->repo[$className][$objectId]);\n    unset($this->versionId[$className][$objectId]);\n  }\n\n  /**\n   * @param EntityDb $entity\n   *\n   * @return void\n   */\n  public function unsetByEntity($entity) {\n    $className = $this->getClassStoreName($entity);\n    $objectId  = $entity->id;\n\n    unset($this->repo[$className][$objectId]);\n    unset($this->versionId[$className][$objectId]);\n  }\n\n  /**\n   * Is container contains no data\n   *\n   * @return bool\n   */\n  public function isEmpty() {\n    return empty($this->repo) && empty($this->versionId);\n  }\n\n  /**\n   * Clears container contents\n   */\n  public function clear() {\n    $this->repo      = [];\n    $this->versionId = [];\n  }\n\n  /**\n   * @param array    $name - [entityClassName, objectId]\n   * @param EntityDb $value\n   *\n   * @return void\n   * @throws Exception\n   *\n   * @deprecated\n   * @see RepoV2::set()\n   */\n  public function __set($name, $value) {\n    throw new Exception(\"DEPRECATED! Method RepoV2::__set() is DEPRECATED! Use RepoV2::set() instead!\");\n  }\n\n  /**\n   * @param array $name - [entityClassName, objectId]\n   *\n   * @return void\n   * @throws Exception\n   * @deprecated\n   */\n  public function __get($name) {\n    throw new Exception(\"DEPRECATED! Method RepoV2::__get() is DEPRECATED! Use RepoV2::get() instead!\");\n  }\n\n  /**\n   * Checks if entity with specified DB ID registered in repository\n   *\n   * Repository also holds entity current version (for transaction support)\n   * Repository stores ONLY EXISTING OBJECTS - so it's not caches unsuccessful data retrieves\n   *\n   * @param array $name - [entityClassName, objectId]\n   *\n   * @return void\n   * @throws Exception\n   * @deprecated\n   */\n  public function __isset($name) {\n    throw new Exception(\"DEPRECATED! Method RepoV2::__isset() is DEPRECATED! Use RepoV2::is_set() instead!\");\n  }\n\n  /**\n   * @param array $name - [entityClassName, objectId]\n   *\n   * @return void\n   * @throws Exception\n   * @deprecated\n   */\n  public function __unset($name) {\n    throw new Exception(\"DEPRECATED! Method RepoV2::__unset() is DEPRECATED! Use RepoV2::un_set() instead!\");\n  }\n\n}\n"
  },
  {
    "path": "classes/Core/Repository.php",
    "content": "<?php\n\n/**\n * Created by Gorlum 10.02.2017 0:07\n */\n\nnamespace Core;\n\nuse \\Common\\ContainerPlus;\nuse Core\\GlobalContainer;\nuse TextEntity;\nuse TextModel;\n\n/**\n * Class Core\\Repository\n *\n * Holds current entity objects\n *\n * @deprecated\n */\nclass Repository {\n\n  /**\n   * \"Not an Object\" marker\n   */\n  const NaO = '\\\\NaO';\n\n\n  /**\n   * @var ContainerPlus $_repository\n   */\n  protected $_repository;\n\n /**\n   * @var ContainerPlus $_oldRepo\n   */\n  protected $_oldRepo;\n\n  /**\n   * @var GlobalContainer $gc\n   */\n  protected $gc;\n\n  /**\n   * Core\\Repository constructor.\n   *\n   * @param GlobalContainer $gc\n   */\n  public function __construct(GlobalContainer $gc) {\n    $this->gc = $gc;\n  }\n\n  /**\n   * @param TextModel  $model\n   * @param int|string $id\n   *\n   * @return TextEntity\n   * @deprecated\n   */\n  public function getById($model, $id) {\n\n    // TODO - is_object()\n\n    // Index is fully qualified class name plus ID like Namespace\\ClassName\\$id\n    $entityIndex = get_class($model) . '\\\\' . $id;\n    if (!isset($this->_oldRepo[$entityIndex])) {\n      $entity = $model->loadById($id);\n      if ($entity && !$entity->isEmpty()) {\n        $this->_oldRepo[$entityIndex] = $entity;\n      }\n    } else {\n      $entity = $this->_oldRepo[$entityIndex];\n    }\n\n    return $entity;\n  }\n\n  /**\n   * Returns collection name for supplied object\n   *\n   * @param $object\n   *\n   * @return string\n   */\n  protected function getCollectionName($object) {\n    return is_object($object) ? get_class($object) : self::NaO;\n  }\n\n  public function get($entityClass, $id) {\n    $entityIndex = get_class($entityClass) . '\\\\' . $id;\n    if(!isset($this->_repository[$entityIndex])) {\n\n    }\n\n    return $this->_repository[$entityIndex];\n  }\n\n  protected function getPool($entityClass) {\n\n  }\n\n  public function registerFactory($factory) {\n\n  }\n\n}\n"
  },
  {
    "path": "classes/Core/Scheduler/Lock.php",
    "content": "<?php\n/**\n * Created by Gorlum 08.02.2020 16:42\n */\n\nnamespace Core\\Scheduler;\n\nuse classConfig;\nuse Core\\GlobalContainer;\nuse DBAL\\db_mysql;\n\n/**\n * Locking mechanic\n *\n * @package Core\\Scheduler\n */\nclass Lock {\n  /**\n   * Skip task execution if task is locked but lock grace period is expired\n   */\n  const LOCK_EXPIRED_SKIP_TASK = 0;\n  /**\n   * Treat lock expiration as previous task fail and process task\n   */\n  const LOCK_EXPIRED_IGNORE = 1;\n\n\n  /**\n   * @var GlobalContainer $gc\n   */\n  protected $gc;\n  /**\n   * @var db_mysql\n   */\n  protected $db;\n  /**\n   * @var classConfig $config\n   */\n  protected $config;\n\n  /**\n   * Name for config lock field\n   *\n   * @var string $configName\n   */\n  protected $configName = '';\n  /**\n   * Interval to lock task\n   *\n   * @var int $maxLockInterval\n   */\n  protected $maxLockInterval = PERIOD_MINUTE;\n  /**\n   * Delta for lock to minimize chance of simulate access/lock race\n   *\n   * @var int $intervalDelta\n   */\n  protected $intervalDelta = 0;\n  protected $lockType = classConfig::DATE_TYPE_SQL_STRING;\n\n  /**\n   * Lock constructor.\n   *\n   * @param GlobalContainer $gc\n   * @param string          $configName\n   * @param int             $maxLockInterval\n   * @param int             $intervalDelta\n   * @param int             $lockType\n   */\n  public function __construct($gc, $configName, $maxLockInterval = PERIOD_MINUTE, $intervalDelta = 10, $lockType = classConfig::DATE_TYPE_SQL_STRING) {\n    $this->gc     = $gc;\n    $this->db = $this->gc->db;\n    $this->config = $this->gc->config;\n\n    $this->configName      = $configName;\n    $this->maxLockInterval = $maxLockInterval;\n    $this->intervalDelta   = $intervalDelta;\n    $this->lockType        = $lockType;\n  }\n\n  /**\n   * Checking for lock\n   *\n   * @return int|null Lock time left. Negative if lock time passed. Lock released immediately if SN_TIME_NOW === lockTime\n   */\n  public function isLocked($time = SN_TIME_NOW) {\n    $timeLeft = null;\n    if ($this->configName) {\n      $lockedUntil = $this->config->dateRead($this->configName, classConfig::DATE_TYPE_UNIX);\n\n      if ($lockedUntil == 0) {\n        // There is no current lock - nothing to do\n        $timeLeft = null;\n      } else {\n        $timeLeft = $lockedUntil - $time;\n      }\n//var_dump('LOCK: ' . ($timeLeft !== null ? \"lock present - left {$timeLeft}s\" : 'no lock'));\n    }\n\n    return $timeLeft;\n  }\n\n  /**\n   * Placing lock\n   */\n  public function lock($time = SN_TIME_NOW) {\n    $toWrite = $time + mt_rand($this->maxLockInterval - $this->intervalDelta, $this->maxLockInterval + $this->intervalDelta);\n    $this->configWrite($toWrite);\n\n    return $this;\n  }\n\n  /**\n   * Removing lock\n   */\n  public function unLock($selfTransaction = true) {\n    if ($selfTransaction) {\n      $this->db->transactionStart();\n    }\n    $this->configWrite(0);\n    if ($selfTransaction) {\n      $this->db->transactionCommit();\n    }\n\n    return $this;\n  }\n\n  /**\n   * @param $data\n   */\n  protected function configWrite($data) {\n    if ($this->configName) {\n      $this->config->dateWrite($this->configName, $data, classConfig::DATE_TYPE_SQL_STRING);\n    }\n  }\n\n  /**\n   * Attempt to acquire lock\n   *\n   * @param callable|null $callable External function to determine how to react on lock expiration. On `null` - task is always unlocked on lock expired\n   *\n   * @return bool TRUE if lock was placed FALSE if lock was already enabled\n   */\n  public function attemptLock($callable = null, $time = SN_TIME_NOW) {\n    $this->db->transactionStart();\n    $lockTimeLeft = $this->isLocked($time);\n\n    // Task is still locked\n    if ($lockTimeLeft > 0 || $lockTimeLeft === 0) {\n//var_dump('LOCK: Task still locked');\n      $this->db->transactionRollback();\n\n      return false;\n    }\n\n    // Task lock grace period expired but further processing is locked by caller\n    if (\n      $lockTimeLeft < 0\n      &&\n      (\n        is_callable($callable)\n        &&\n        (($q = $callable()) === self::LOCK_EXPIRED_SKIP_TASK)\n      )\n    ) {\n//var_dump('LOCK: Task lock expired but task is skipped');\n\n      // Then we just rolling back transaction and returning 'false'\n      $this->db->transactionRollback();\n\n      return false;\n    }\n//if (\n//  $lockTimeLeft < 0){\n//  var_dump('LOCK: lock expired - we should re-lock');\n//}\n//var_dump('LOCK: ' . ($lockTimeLeft > 0 ? 'task still locked' : ($lockTimeLeft < 0 ? 'lock expired, unlocking' : 'not locked')));\n\n    // Placing task lock\n    $this->lock();\n\n    $this->db->transactionCommit();\n\n    return true;\n  }\n\n}\n"
  },
  {
    "path": "classes/Core/Scheduler/TaskConditional.php",
    "content": "<?php\n/**\n * Created by Gorlum 08.02.2020 15:31\n */\n\nnamespace Core\\Scheduler;\n\nuse DBAL\\db_mysql;\nuse Core\\GlobalContainer;\nuse classConfig;\nuse SN;\n\n/**\n * Scheduled task is something that need to be done withing certain schedule\n * Supports task locks\n *\n * @package Core\n */\nclass TaskConditional {\n  /**\n   * @var GlobalContainer $gc\n   */\n  protected $gc;\n  /**\n   * @var db_mysql\n   */\n  protected $db;\n  /**\n   * @var classConfig\n   */\n  protected $config;\n\n  /**\n   * Name of config field to monitor\n   *\n   * @var string $configName\n   */\n  protected $configName = '';\n  /**\n   * Force load config value from DB each check\n   *\n   * @var bool $forceConfigLoad\n   */\n  protected $forceConfigLoad = true;\n\n  /**\n   * Lock for a task\n   *\n   * @var null|Lock $lock\n   */\n  protected $lock = null;\n  /**\n   * Force to process task if lock not lifted but expired\n   *\n   * @var bool\n   */\n  protected $actionOnExpiredLock = Lock::LOCK_EXPIRED_IGNORE;\n\n  /**\n   * TaskConditional constructor.\n   *\n   * @param GlobalContainer|null $gc\n   */\n  public function __construct($gc = null) {\n    if (!is_object($gc)) {\n      $this->gc = SN::$gc;\n    } else {\n      $this->gc = $gc;\n    }\n    $this->db     = $this->gc->db;\n    $this->config = $this->gc->config;\n  }\n\n  /**\n   * Invoking task\n   *\n   * Checking lock (if applicable) and task interval, getting lock, performing task, releasing lock\n   *\n   * @return bool|mixed Return FALSE if task not yet possible to execute or task() result\n   */\n  public function __invoke() {\n    if (!$this->isTaskAllowed()) {\n      // If no interval specified or no config field specified - nothing to do\n      return false;\n    }\n\n    // Checking task lock\n    if (is_object($this->lock) && !$this->lock->attemptLock(function () {\n        return $this->proceedLockExpiration();\n      }, SN_TIME_NOW)) {\n\n      return false;\n    }\n\n    if ($this->condition()) {\n      $this->updateTaskLastRunTime(SN_TIME_NOW);\n      // Performing task\n      $result = $this->task();\n\n      $this->updateTaskLastRunTime(time());\n    } else {\n      $result = false;\n    }\n\n    // Unlocking task if any\n    if (is_object($this->lock)) {\n      $this->lock->unLock(true);\n    }\n\n    return $result;\n  }\n\n  /**\n   * Fast pre-checks if task allowed at all in current state\n   *\n   * @return bool\n   */\n  protected function isTaskAllowed() {\n    return !empty($this->configName);\n  }\n\n  /**\n   * This function called if task have a lock and lock grace period expired\n   * Here can be added some checks and even recovery procedures for expired lock\n   *\n   * @return bool TRUE if task can proceed even on lock expiration time\n   */\n  protected function proceedLockExpiration() {\n    return $this->actionOnExpiredLock;\n  }\n\n  /**\n   * This is primary condition to check\n   *\n   * @return bool\n   */\n  protected function condition() {\n    return false;\n  }\n\n  /**\n   * Task to execute\n   *\n   * @return bool\n   */\n  protected function task() {\n    return true;\n  }\n\n  /**\n   * @param int $time\n   */\n  protected function updateTaskLastRunTime($time) {\n    $this->db->transactionWrap(\n      function () use ($time) {\n        $this->config->dateWrite($this->configName, $time, classConfig::DATE_TYPE_SQL_STRING);\n      }\n    );\n  }\n\n}\n"
  },
  {
    "path": "classes/Core/Scheduler/TaskPeriodic.php",
    "content": "<?php\n/**\n * Created by Gorlum 08.02.2020 15:44\n */\n\nnamespace Core\\Scheduler;\n\nuse classConfig;\n\n/**\n * Periodic task - task which runs in time intervals, i.e. each 10 minutes\n *\n * @package Core\n */\nclass TaskPeriodic extends TaskConditional {\n  /**\n   * Interval to run task\n   *\n   * @var int $interval\n   */\n  protected $interval = 0;\n\n  /**\n   * @return bool\n   */\n  protected function isTaskAllowed() {\n    return $this->interval > 0 && parent::isTaskAllowed();\n  }\n\n  /**\n   * @return bool\n   */\n  public function condition() {\n    $this->forceConfigLoad ? $this->config->pass() : false;\n    $configValue = $this->config->dateRead($this->configName, classConfig::DATE_TYPE_UNIX);\n\n    return ($this->interval > 0) && (SN_TIME_NOW - $configValue > $this->interval);\n  }\n\n}\n"
  },
  {
    "path": "classes/Core/Scheduler/Watchdog.php",
    "content": "<?php\n/**\n * Created by Gorlum 15.06.2017 5:07\n */\n\nnamespace Core\\Scheduler;\n\nuse Exception;\nuse classConfig;\nuse Core\\GlobalContainer;\n\nclass Watchdog {\n  /**\n   * @var GlobalContainer $gc\n   */\n  protected $gc;\n  /**\n   * @var \\classConfig $config\n   */\n  protected $config;\n\n  /**\n   * @var TaskConditional[] $taskList\n   */\n  protected $taskList = [];\n\n  /**\n   * Watchdog constructor.\n   *\n   * @param GlobalContainer $gc\n   */\n  public function __construct(GlobalContainer $gc) {\n    $this->gc     = $gc;\n    $this->config = $this->gc->config;\n  }\n\n  /**\n   * @param TaskConditional $task\n   * @param string          $name Optional. Task name\n   */\n  public function register(TaskConditional $task, $name = '') {\n    if (empty($name)) {\n      $this->taskList[] = $task;\n    } else {\n      $this->taskList[$name] = $task;\n    }\n  }\n\n  /**\n   * @param $name\n   *\n   * @return TaskConditional|null\n   */\n  public function getTask($name) {\n    return !empty($this->taskList[$name]) ? $this->taskList[$name] : null;\n  }\n\n  public function execute() {\n    foreach ($this->taskList as $task) {\n      try {\n        $task();\n      } catch (Exception $e) {\n      }\n    }\n  }\n\n  /**\n   * @param string   $configName - config record name\n   * @param int      $timeDiff   - interval from SN_TIME_NOW in seconds\n   * @param callable $callable   - function to call when condition is met\n   * @param int      $configType - type of config record - unixtime or Sql timestamp\n   * @param bool     $forceLoad  - should config value be read from DB\n   *\n   * @deprecated\n   */\n  public function checkConfigTimeDiff($configName, $timeDiff, $callable, $configType = classConfig::DATE_TYPE_UNIX, $forceLoad = false) {\n    $configValue = $forceLoad ? $this->config->db_loadItem($configName) : $this->config[$configName];\n    $configType == classConfig::DATE_TYPE_SQL_STRING ? $configValue = strtotime($configValue, SN_TIME_NOW) : false;\n\n    if (SN_TIME_NOW - $configValue > $timeDiff) {\n      $callable();\n    }\n\n  }\n\n}\n"
  },
  {
    "path": "classes/Core/Singleton.php",
    "content": "<?php\n/**\n * Created by Gorlum 08.01.2018 15:33\n */\n\nnamespace Core;\n\n\nclass Singleton {\n\n  private static $_singleton;\n\n  public static function singleton() {\n    if (empty(static::$_singleton)) {\n      static::$_singleton = new static();\n    }\n\n    return static::$_singleton;\n  }\n\n}\n"
  },
  {
    "path": "classes/Core/SnBootstrap.php",
    "content": "<?php\n/**\n * Created by Gorlum 11.06.2017 9:58\n */\n\nnamespace Core;\n\n\nuse core_auth;\nuse DBAL\\db_mysql;\nuse \\SN;\n\nclass SnBootstrap {\n\n  public static function install_benchmark() {\n    register_shutdown_function(function () {\n      if (defined('IN_AJAX')) {\n        return;\n      }\n\n      global $user, $locale_cache_statistic;\n\n      $now       = microtime(true);\n      $totalTime = round($now - SN_TIME_MICRO, 6);\n      !defined('SN_TIME_RENDER_START') ? define('SN_TIME_RENDER_START', microtime(true)) : false;\n      $executionTime = round(SN_TIME_RENDER_START - SN_TIME_MICRO, 6);\n      $displayTime   = round($now - SN_TIME_RENDER_START, 6);\n\n      $benchmarkResults =\n        '[' . SN_TIME_SQL . '] '\n        . 'Benchmark ' . $totalTime . 's'\n        . (defined('SN_TIME_RENDER_START')\n          ?\n          \" (exec: {$executionTime}s\" .\n          \", display: {$displayTime}s\"\n          . (class_exists('SN') && is_object(SN::$db) ? ', DB: ' . round(SN::$db->time_mysql_total, 6) . 's' : '')\n          . \")\"\n          : ''\n        )\n        . ', memory: ' . number_format(memory_get_usage() - SN_MEM_START)\n        . (!empty($locale_cache_statistic['misses']) ? ', LOCALE MISSED' : '')\n        . '';\n\n      $benchPrefix = '<div id=\"benchmark\" class=\"benchmark\" style=\"flex-grow: 1;flex-shrink: 1;\"><hr>';\n      $benchSuffix = '</div>';\n      if (class_exists(SN::class, false) && SN::$gSomethingWasRendered) {\n//        print \"<script type='text/javascript'>document.body.innerHTML += '{$benchPrefix}\" . htmlentities($benchmarkResults, ENT_QUOTES, 'UTF-8') . \"{$benchSuffix}';</script>\";\n//        print \"<script type='text/javascript'>document.addEventListener('DOMContentLoaded', function() {document.body.innerHTML += '{$benchPrefix}\" . htmlentities($benchmarkResults, ENT_QUOTES, 'UTF-8') . \"{$benchSuffix}';});</script>\";\n        print\n\"<script type='text/javascript'>\n(function (document) {\n    var prefix = '{$benchPrefix}';\n    var suffix = '{$benchSuffix}';\n    var result = '\" . htmlentities($benchmarkResults, ENT_QUOTES, 'UTF-8') . \"';\n    var element = document.getElementById('debug');\n\n    if(element) {\n      element.innerHTML += prefix + result + suffix;\n    } else {\n      document.write(prefix + result + suffix);\n    }\n}(document));\n</script>\";\n      } else {\n        print($benchPrefix . $benchmarkResults . $benchSuffix);\n      }\n\n\n      if (isset($user['authlevel']) && $user['authlevel'] >= 2 && file_exists(SN_ROOT_PHYSICAL . 'badqrys.txt') && @filesize(SN_ROOT_PHYSICAL . 'badqrys.txt') > 0) {\n        echo '<a href=\"badqrys.txt\" target=\"_blank\" style=\"color:red\">', 'HACK ALERT!', '</a>';\n      }\n\n      if (!empty($locale_cache_statistic['misses'])) {\n        print('<!--');\n        pdump($locale_cache_statistic);\n        print('-->');\n      }\n\n      $error = error_get_last();\n      if ($error['type'] === E_ERROR) {\n        $fName  = SN_ROOT_PHYSICAL . '_error.txt';\n        $output = [\n          \"\\n\\n\",\n          SN_TIME_SQL . \" - ERROR\",\n          var_export($error, true),\n//          var_export(debug_backtrace(), true),\n          var_export(core_auth::$device, true),\n        ];\n        file_put_contents($fName, implode(\"\\n\", $output), FILE_APPEND | LOCK_EX);\n\n        if (!empty($error['file']) && strpos($error['file'], 'classCache.php') !== false) {\n          print('<span style=\"color: red\">Looks like cache clearing takes too long... Try to restart your web-server and/or cache engine</span>');\n        }\n      }\n    });\n  }\n\n  public static function init_debug_state() {\n    if ($_SERVER['SERVER_NAME'] == 'localhost' && !defined('BE_DEBUG')) {\n      define('BE_DEBUG', true);\n    }\n\n    // Declaring PHP-constants from server config\n    /** @see \\classConfig::$DEBUG_SQL_FILE_LOG */\n    foreach ([\n      'DEBUG_SQL_FILE_LOG'     => ['DEBUG_SQL_ERROR' => true, 'DEBUG_SQL_COMMENT_LONG' => true,],\n      'DEBUG_SQL_ERROR'        => ['DEBUG_SQL_COMMENT' => true,],\n      'DEBUG_SQL_COMMENT_LONG' => ['DEBUG_SQL_COMMENT' => true,],\n      'DEBUG_SQL_COMMENT'      => []\n    ] as $constantName => $implications) {\n      if (!empty(SN::$config->$constantName) && !defined($constantName)) {\n        define($constantName, true);\n      }\n      foreach ($implications as $impliedConstantName => $impliedValue) {\n        if (!defined($impliedConstantName)) {\n          define($impliedConstantName, $impliedValue);\n        }\n      }\n    }\n\n    if (defined('BE_DEBUG') || SN::$config->debug) {\n      @define('BE_DEBUG', true);\n      @ini_set('display_errors', 1);\n      if(SN::$config->debug == 1) {\n        @error_reporting(E_ALL ^ E_NOTICE ^ E_DEPRECATED ^ E_WARNING);\n      } else {\n        @error_reporting(SN::$config->debug);\n      }\n    } else {\n      @define('BE_DEBUG', false);\n      @ini_set('display_errors', 0);\n    }\n\n  }\n\n  /**\n   * @param \\classConfig $config\n   */\n  public static function performUpdate($config) {\n    if (\n      !file_exists($update_file = SN_ROOT_PHYSICAL . \"includes/update.php\")\n      ||\n      (\n        filemtime($update_file) <= $config->pass()->var_db_update\n        &&\n        $config->pass()->db_version >= DB_VERSION\n      )\n    ) {\n      return;\n    }\n\n    if (defined('IN_ADMIN') || !$config->pass()->game_installed) {\n      db_mysql::db_transaction_start(); // Для защиты от двойного запуска апдейта - начинаем транзакцию. Так запись в базе будет блокирована\n      if (SN_TIME_NOW >= $config->pass()->var_db_update_end) {\n        $config->pass()->var_db_update_end = SN_TIME_NOW + $config->upd_lock_time;\n        db_mysql::db_transaction_commit();\n\n        require_once($update_file);\n\n        $current_time                      = time();\n        $config->pass()->var_db_update     = $current_time;\n        $config->pass()->var_db_update_end = $current_time;\n        $config->pass()->game_installed    = 1;\n      } elseif (filemtime($update_file) > $config->var_db_update) {\n        $timeout = $config->var_db_update_end - SN_TIME_NOW;\n        die(\n        \"Обновляется база данных. Рассчетное время окончания - {$timeout} секунд (время обновления может увеличиваться). Пожалуйста, подождите...<br />\n        Obnovljaetsja baza dannyh. Rasschetnoe vremya okonchanija - {$timeout} secund. Pozhalujsta, podozhdute...<br />\n        Database update in progress. Estimated update time {$timeout} seconds (can increase depending on update process). Please wait...\"\n        );\n      }\n      db_mysql::db_transaction_rollback();\n    } else {\n      die(\n      'Происходит обновление сервера - пожалуйста, подождите...<br />\n      Proishodit obnovlenie servera - pozhalujsta, podozhdute...<br />\n      Server upgrading now - please wait...<br />\n      <a href=\"admin/overview.php\">Admin link</a>'\n      );\n    }\n  }\n\n}\n"
  },
  {
    "path": "classes/Core/SnPimp.php",
    "content": "<?php\n/**\n * Created by Gorlum 18.03.2018 17:53\n */\n\nnamespace Core;\n\n\nuse Common\\Hooker\\Pimp;\nuse Exception;\nuse Meta\\Economic\\BuildDataStatic;\nuse Que\\QueUnitStatic;\nuse SnTemplate;\nuse template;\n\n/**\n * Class SnPimp\n *\n * Used to declare commonly known methods to make it easier for IDEs\n *\n * @package Core\n *\n * @method void tpl_render_topnav(array|callable $user, array $planetRow, template $template) - Add some elements to ResourceBar\n * @method array que_unit_make_sql(int|callable $unit_id, array $user, array $planet = [], array $build_data = [], float $unit_level = 0, float $unit_amount = 1, int $build_mode = BUILD_CREATE)\n * @method float getStructuresTimeDivisor(array|callable $user, array $planet, int $unit_id, array $unit_data)\n *\n * @method void|string allyInfoView(callable $c = null) - renders extra elements on Alliance internal main page\n * @method void|string allyInternalMainModel(callable $c = null) - extra model on main Alliance page\n */\nclass SnPimp extends Pimp {\n  const MODE_NORMAL = 0; // Normal mode - call hooker\n  const MODE_ADD = 1; // Add mode - syntax sugar - add named function\n  const MODE_NAME = 2; // Name mode - syntax sugar - return function name\n\n  protected $mode = self::MODE_NORMAL;\n  protected $registerOrder = Pimp::ORDER_AS_IS;\n\n  public function __construct(GlobalContainer $gc) {\n    parent::__construct($gc);\n\n    /** @noinspection PhpParamsInspection */\n    /** @noinspection PhpUnhandledExceptionInspection */\n    $this->add()->tpl_render_topnav([SnTemplate::class, 'sn_tpl_render_topnav'], [], null);\n    /** @noinspection PhpParamsInspection */\n    /** @noinspection PhpUnhandledExceptionInspection */\n    $this->add()->que_unit_make_sql([QueUnitStatic::class, 'que_unit_make_sql'], []);\n    /** @noinspection PhpParamsInspection */\n    /** @noinspection PhpUnhandledExceptionInspection */\n    $this->add()->getStructuresTimeDivisor([BuildDataStatic::class, 'getStructuresTimeDivisor'], [], 0, []);\n  }\n\n  /**\n   * Changes pimp mode to \"ADD\"\n   *\n   * A bit of syntax sugar\n   * Allows constructions like $this->add()->methodName($callable, ...)\n   * Helps to maintain uniformity of method names throw the code\n   * Also gives hint to IDE about usage of method (phpStorm)\n   *\n   * @param int $order\n   *\n   * @return $this\n   * @throws Exception\n   */\n  public function add($order = Pimp::ORDER_AS_IS) {\n    if ($this->mode != static::MODE_NORMAL) {\n      throw new Exception('Pimp::add() - mode already set');\n    }\n\n    $this->mode = static::MODE_ADD;\n    $this->registerOrder = $order;\n\n    return $this;\n  }\n\n  /**\n   * Changes pimp mode to \"NAME\"\n   *\n   * Calling pimp method in NAME mode will return name of called method\n   *\n   * A bit of syntax sugar\n   * Allows constructions like $this->name()->methodName(...)\n   * Helps to maintain uniformity of method names throw the code\n   * Used mainly as array indexes\n   *\n   * @return $this\n   * @throws Exception\n   */\n  public function name() {\n    if ($this->mode != static::MODE_NORMAL) {\n      throw new Exception('Pimp::name() - mode already set');\n    }\n\n    $this->mode = static::MODE_NAME;\n\n    return $this;\n  }\n\n  /**\n   * @param $name\n   * @param $arguments\n   *\n   * @return static|mixed|null\n   */\n  public function __call($name, $arguments) {\n    if ($this->mode == static::MODE_NORMAL) {\n      return parent::__call($name, $arguments);\n    }\n\n    if ($this->mode == static::MODE_ADD) {\n      $this->register($name, reset($arguments), $this->registerOrder);\n      $this->mode = static::MODE_NORMAL;\n      $this->registerOrder = Pimp::ORDER_AS_IS;\n\n      return $this;\n    }\n\n    if ($this->mode == static::MODE_NAME) {\n      $this->mode = static::MODE_NORMAL;\n\n      return $name;\n    }\n\n    return null;\n  }\n\n  public function getHooker($name) {\n    return !empty($this->hookers[$name]) ? $this->hookers[$name] : null;\n  }\n\n}\n"
  },
  {
    "path": "classes/Core/StorageV2.php",
    "content": "<?php\n/**\n * Created by Gorlum 21.04.2018 16:13\n */\n\nnamespace Core;\n\n\nuse DBAL\\db_mysql;\n\nclass StorageV2 {\n\n  /**\n   * @var GlobalContainer $gc\n   * @deprecated\n   */\n  protected $gc;\n\n  /**\n   * @var db_mysql\n   */\n  protected $db;\n\n  /**\n   * Core\\Repository constructor.\n   *\n   * @param GlobalContainer $gc\n   */\n  public function __construct(GlobalContainer $gc) {\n    $this->gc = $gc;\n    $this->db = $gc->db;\n  }\n\n  /**\n   * @param db_mysql $db\n   */\n  public function setDb(db_mysql $db) {\n    $this->db = $db;\n  }\n\n}\n"
  },
  {
    "path": "classes/Core/Updater.php",
    "content": "<?php /** @noinspection PhpUnnecessaryCurlyVarSyntaxInspection */\n\n/**\n * Created by Gorlum 01.03.2019 6:03\n */\n\nnamespace Core;\n\nuse classConfig;\nuse DBAL\\db_mysql;\nuse DBAL\\DbFieldDescription;\nuse DBAL\\DbIndexDescription;\nuse mysqli_result;\nuse SN;\n\nclass Updater {\n\n  protected $update_tables = [];\n\n  /**\n   * @var classConfig $config\n   */\n  protected $config;\n  /**\n   * @var db_mysql $db\n   */\n  protected $db;\n\n  protected $upd_log = '';\n\n  public $new_version = 0;\n  protected $old_server_status;\n\n  // Does updater terminate successfully\n  public $successTermination = false;\n\n  public function __construct() {\n    $this->config = SN::$gc->config;\n    $this->db     = SN::$gc->db;\n\n    $this->new_version = floatval($this->config->db_version);\n\n    // Closing any transaction that can be opened to this moment\n    $this->upd_do_query('ROLLBACK;', true);\n\n    $this->config->reset();\n    $this->config->db_loadAll();\n\n    $this->config->db_prefix    = $this->db->db_prefix; // Left for compatibility reasons. Deprecated\n    $this->config->cache_prefix = SN::$cache_prefix;\n\n    $this->config->debug = 0;\n\n    $this->checkVersionSupport();\n\n    ini_set('memory_limit', '1G');\n    set_time_limit($this->config->upd_lock_time + 10);\n    $this->upd_do_query('SET FOREIGN_KEY_CHECKS=0;', true);\n\n    $this->upd_log_message('Update started. Disabling server');\n    $this->old_server_status = $this->config->pass()->game_disable;\n    $this->config->db_saveItem('game_disable', GAME_DISABLE_UPDATE);\n    $this->upd_log_message('Server disabled.');\n\n    $this->upd_log_message('Now looking DB for upgrades...');\n  }\n\n  public function __destruct() {\n    if ($this->successTermination) {\n      $this->upd_log_message('Upgrade complete.');\n    } else {\n      $this->upd_log_message('Upgrade was aborted! DB can be in invalid state!');\n    }\n\n    $this->upd_do_query('SET FOREIGN_KEY_CHECKS=1;', true);\n\n    SN::$cache->unset_by_prefix('lng_');\n\n    if ($this->new_version) {\n      $this->config->pass()->db_version = $this->new_version;\n      $this->upd_log_message(\"<span style='color: green;'>DB version is now {$this->new_version}</span>\");\n    } else {\n      $this->upd_log_message(\"DB version didn't changed from {$this->config->db_version}\");\n    }\n\n    $this->config->db_loadAll();\n    /*\n    if($user['authlevel'] >= 3) {\n      print(str_replace(\"\\r\\n\", '<br>', $upd_log));\n    }\n    */\n    $this->db->schema()->clear();\n\n    $this->upd_log_message('Restoring server status');\n    $this->config->pass()->game_disable = $this->old_server_status;\n  }\n\n  public function upd_log_message($message) {\n    global $sys_log_disabled, $debug;\n\n    if (!$sys_log_disabled) {\n      $this->upd_log .= \"{$message}\\r\\n\";\n      $debug->warning($message, 'Database Update', 103);\n    }\n  }\n\n  public function upd_log_version_update() {\n    $this->upd_add_more_time();\n    $this->upd_log_message(\"Detected outdated version {$this->new_version}. Upgrading...\");\n  }\n\n  public function upd_check_key($key, $default_value, $condition = false) {\n    global $sys_log_disabled;\n\n    $this->config->pass()->$key;\n    if ($condition || !isset($this->config->$key)) {\n      $this->upd_add_more_time();\n      if (!$sys_log_disabled) {\n        $this->upd_log_message(\"Updating config key '{$key}' with value '{$default_value}'\");\n      }\n      $this->config->pass()->$key = $default_value;\n    } else {\n      $this->config->pass()->$key = null;\n    }\n  }\n\n\n  /**\n   * @param string       $table_name\n   * @param string|array $declaration\n   * @param string       $tableOptions\n   *\n   * @return bool|mysqli_result\n   */\n  public function upd_create_table($table_name, $declaration, $tableOptions = '') {\n    $result = null;\n\n    if (!$this->isTableExists($table_name)) {\n      if (is_array($declaration)) {\n        $declaration = implode(',', $declaration);\n      }\n      $declaration = trim($declaration);\n      if (substr($declaration, 0, 1) != '(') {\n        $declaration = \"($declaration)\";\n      }\n      $tableOptions = trim($tableOptions);\n      if (!empty($tableOptions)) {\n        $declaration .= $tableOptions;\n      }\n      $result = $this->upd_do_query(\"CREATE TABLE IF NOT EXISTS `{$this->config->db_prefix}{$table_name}` {$declaration}\");\n      $error  = SN::$db->db_error();\n      if ($error) {\n        die(\"Creating error for table `{$table_name}`: {$error}<br />\" . dump($declaration));\n      }\n\n      $this->db->schema()->clear();\n    }\n\n    return $result;\n  }\n\n  /**\n   * @param string          $table\n   * @param string|string[] $alters\n   * @param bool            $condition\n   *\n   * @return bool|mysqli_result|null\n   */\n  public function upd_alter_table($table, $alters, $condition = true) {\n    if (!$condition) {\n      return null;\n    }\n\n    $this->upd_add_more_time();\n    $alters_print = is_array($alters) ? dump($alters) : $alters;\n    $this->upd_log_message(\"Altering table '{$table}' with alterations {$alters_print}\");\n\n    if (!is_array($alters)) {\n      $alters = array($alters);\n    }\n\n    $alters = implode(',', $alters);\n    // $alters = preg_replace('/({{(.+)}})/U', 'lvh1_$2', $alters);\n    $qry = \"ALTER TABLE {$this->config->db_prefix}{$table} {$alters};\";\n\n    $result = $this->upd_do_query($qry);\n    $error  = SN::$db->db_error();\n    if ($error) {\n      die(\"Altering error for table `{$table}`: {$error}<br />{$alters_print}\");\n    }\n\n    $this->db->schema()->clear();\n\n    return $result;\n  }\n\n  public function upd_drop_table($table_name) {\n    $this->db->db_sql_query(\"DROP TABLE IF EXISTS {$this->config->db_prefix}{$table_name};\");\n    $this->db->schema()->clear();\n  }\n\n  /**\n   * @param $table\n   * @param $index\n   *\n   * @return bool|mysqli_result|null\n   */\n  public function indexDropIfExists($table, $index) {\n    return $this->upd_alter_table($table, [\"DROP KEY `{$index}`\",], $this->isIndexExists($table, $index));\n  }\n\n  /**\n   * @param $table\n   * @param $constraint\n   *\n   * @return bool|mysqli_result|null\n   */\n  public function constraintDropIfExists($table, $constraint) {\n    return $this->upd_alter_table($table, [\"DROP FOREIGN KEY `{$constraint}`\",], $this->isConstrainExists($table, $constraint));\n  }\n\n  /**\n   * Replace index in table\n   *\n   * @param string    $table  Table name\n   * @param string    $index  Index name\n   * @param string[]  $fields Field names to make index on\n   * @param ?callable $dropForeign\n   * @param ?callable $createForeign\n   *\n   * @return void\n   */\n  public function indexReplace($table, $index, $fields, $dropForeign = null, $createForeign = null) {\n    $this->transactionStart();\n    $this->upd_do_query('SET FOREIGN_KEY_CHECKS=0;', true);\n\n    if (is_callable($dropForeign)) {\n      $dropForeign();\n    }\n\n    $this->indexDropIfExists($table, $index);\n    $queryFields = implode(',', array_map(function ($value) { return \"`{$value}`\"; }, $fields));\n    if ($queryFields) {\n      $this->upd_alter_table(\n        $table,\n        [\"ADD KEY `{$index}` (\" . $queryFields . \")\"],\n        !$this->isIndexExists($table, $index)\n      );\n    }\n\n    if (is_callable($createForeign)) {\n      $createForeign();\n    }\n\n    $this->upd_do_query('SET FOREIGN_KEY_CHECKS=1;', true);\n    $this->transactionCommit();\n  }\n\n  public function upd_add_more_time($time = 0) {\n    global $sys_log_disabled;\n\n    $time = $time ?: $this->config->upd_lock_time;\n    if (!$sys_log_disabled) {\n      $this->config->pass()->var_db_update_end = SN_TIME_NOW + $time;\n    }\n    set_time_limit($time);\n  }\n\n  /**\n   * @param int $id\n   *\n   * @return bool\n   */\n  public function updPatchExists($id) {\n    $q = $this->upd_do_query(\"SELECT 1 FROM `{{server_patches}}` WHERE `id` = \" . intval($id), true);\n\n    return !empty(db_fetch($q));\n  }\n\n  /**\n   * @param int $id\n   */\n  public function updPatchRegister($id) {\n    $this->upd_do_query(\"INSERT INTO `{{server_patches}}` SET `id` = \" . intval($id));\n  }\n\n  /**\n   * @param int      $patchId\n   * @param callable $callable\n   * @param bool     $preCheck - [PATCH_REGISTER|PATCH_PRE_CHECK]\n   */\n  public function updPatchApply($patchId, $callable, $preCheck = PATCH_REGISTER) {\n    if (!$this->updPatchExists($patchId)) {\n      $callable();\n\n      if ($preCheck == PATCH_REGISTER) {\n        $this->updPatchRegister($patchId);\n      }\n    }\n  }\n\n  /**\n   * @param string $query\n   * @param bool   $no_log\n   *\n   * @return bool|mysqli_result\n   */\n  public function upd_do_query($query, $no_log = false) {\n    $this->upd_add_more_time();\n\n    if (!$no_log) {\n      $this->upd_log_message(\"Performing query '{$query}'\");\n    }\n\n    if (strpos($query, '{{') !== false) {\n      foreach ($this->db->schema()->getSnTables() as $tableName => $cork) {\n        $query = str_replace(\"{{{$tableName}}}\", $this->db->db_prefix . $tableName, $query);\n      }\n    }\n    $result = $this->db->db_sql_query($query) or die('Query error for ' . $query . ': ' . SN::$db->db_error());\n\n    return $result;\n  }\n\n  protected function checkVersionSupport() {\n    if ($this->config->db_version < DB_VERSION_MIN) {\n//  print(\"This version does not supports upgrades from SN below v{$minVersion}. Please, use SN v42 to upgrade old database.<br />\n//Эта версия игры не поддерживает обновление движка версий ниже v{$minVersion}. Пожалуйста, используйте SN v42 для апгрейда со старых версий игры.<br />\");\n      die(\n        'Internal error! Updater detects DB version LESSER then can be handled!<br />\n    Possible you have VERY out-of-date SuperNova version<br />\n    Use first SuperNova version not greater then  ' . DB_VERSION_MIN . ' to make preliminary upgrade and then use newest version again<br />\n    List of available releases <a href=\"https://github.com/supernova-ws/SuperNova/releases\">GIT repository</a>'\n      );\n    } elseif ($this->config->db_version > DB_VERSION) {\n      $this->config->pass()->var_db_update_end = SN_TIME_NOW;\n      die(\n      'Internal error! Updater detects DB version greater then can be handled!<br />\n    Possible you have out-of-date SuperNova version<br />\n    Please upgrade your server from <a href=\"https://github.com/supernova-ws/SuperNova\">GIT repository</a>'\n      );\n    }\n  }\n\n  /**\n   * @param $table\n   * @param $field\n   *\n   * @return DbFieldDescription\n   */\n  public function getFieldDescription($table, $field) {\n    return $this->db->schema()->getTableSchema($table)->fields[$field];\n  }\n\n  /**\n   * @param $table\n   * @param $index\n   *\n   * @return DbIndexDescription\n   */\n  public function getIndexDescription($table, $index) {\n    return $this->db->schema()->getTableSchema($table)->indexes[$index];\n  }\n\n  public function isTableExists($table) {\n    return $this->db->schema()->isSnTableExists($table);\n  }\n\n  public function isFieldExists($table, $field) {\n    return $this->db->schema()->isFieldExists($table, $field);\n  }\n\n  public function isIndexExists($table, $index) {\n    return $this->db->schema()->isIndexExists($table, $index);\n  }\n\n  public function isConstrainExists($table, $index) {\n    return $this->db->schema()->isConstrainExists($table, $index);\n  }\n\n  public function transactionStart() {\n//    $this->upd_do_query('START TRANSACTION;', true);\n    $this->db->transactionStart();\n  }\n\n  public function transactionCommit() {\n//    $this->upd_do_query('COMMIT;', true);\n    $this->db->transactionCommit();\n  }\n\n}\n"
  },
  {
    "path": "classes/Core/Worker.php",
    "content": "<?php\n/**\n * Created by Gorlum 18.02.2020 15:23\n */\n\nnamespace Core;\n\nclass Worker {\n  /**\n   * @var GlobalContainer $gc\n   */\n  protected $gc;\n\n  /**\n   * @var callable[] $workers\n   */\n  protected $workers = [];\n\n  /**\n   * Detaches script from incoming request\n   *\n   * Calling side is released and script continues it's execution\n   */\n  public static function detachIncomingRequest() {\n    // Some dark magic to terminate incoming connection but still keep run a script\n\n    ob_end_clean();\n    ignore_user_abort(true);\n\n    ob_start();\n    header(\"Connection: close\");\n    header('Content-type: text/plain', true);\n    header(\"Content-Length: \" . ob_get_length());\n    ob_end_flush();\n\n    flush();\n    session_write_close();\n\n    if (function_exists('fastcgi_finish_request')) {\n      fastcgi_finish_request();\n    }\n  }\n\n  public function __construct(GlobalContainer $gc) {\n    $this->gc = $gc;\n  }\n\n  public function __call($name, $arguments) {\n    $result = null;\n    if (!empty($this->workers[$name]) && is_callable($this->workers[$name])) {\n      $result = call_user_func_array($this->workers[$name], $arguments);\n    }\n\n    return $result;\n  }\n\n  public function registerWorker($name, $callable) {\n    $this->workers[$name] = $callable;\n\n    return $this;\n  }\n\n}\n"
  },
  {
    "path": "classes/DBAL/ActiveRecord.php",
    "content": "<?php\n/**\n * Created by Gorlum 12.06.2017 14:41\n */\n\nnamespace DBAL;\n\n/**\n * Class ActiveRecord\n *\n * Represent table in DB/one record in DB. Breaking SRP with joy!\n *\n * @package DBAL\n */\nclass ActiveRecord extends ActiveRecordAbstractIndexed {\n\n}\n"
  },
  {
    "path": "classes/DBAL/ActiveRecordAbstract.php",
    "content": "<?php\n/**\n * Created by Gorlum 12.07.2017 10:27\n */\n\nnamespace DBAL;\n\nuse Common\\AccessLogged;\nuse Core\\GlobalContainer;\nuse Common\\Exceptions\\DbalFieldInvalidException;\n\n/**\n * Class ActiveRecordAbstract\n * @package DBAL\n *\n * Adds some DB functionality\n */\nabstract class ActiveRecordAbstract extends AccessLogged {\n  const FIELDS_TO_PROPERTIES = true;\n  const PROPERTIES_TO_FIELDS = false;\n\n  const IGNORE_PREFIX = 'Record';\n\n  /**\n   * @var \\DBAL\\db_mysql $db\n   */\n  protected static $db;\n  /**\n   * Table name for current Active Record\n   *\n   * Can be predefined in class or calculated in run-time\n   *\n   * ALWAYS SHOULD BE OVERRIDEN IN CHILD CLASSES!\n   *\n   * @var string $_tableName\n   */\n  protected static $_tableName = '';\n  /**\n   * Field name translations to property names\n   *\n   * @var string[] $_fieldsToProperties\n   */\n  protected static $_fieldsToProperties = [];\n\n  /**\n   * @var bool $_forUpdate\n   */\n  protected static $_forUpdate = DbQuery::DB_SHARED;\n\n  // AR's service fields\n  /**\n   * Is this field - new field?\n   *\n   * @var bool $_isNew\n   */\n  protected $_isNew = true;\n\n  protected $_isDeleted = false;\n\n\n  /**\n   * Get table name\n   *\n   * @return string\n   */\n  public static function tableName() {\n    empty(static::$_tableName) ? static::$_tableName = static::calcTableName() : false;\n\n    return static::$_tableName;\n  }\n\n  /**\n   * @param \\DBAL\\db_mysql $db\n   */\n  public static function setDb(\\DBAL\\db_mysql $db) {\n    static::$db = $db;\n  }\n\n  /**\n   * Get DB\n   *\n   * @return \\DBAL\\db_mysql\n   */\n  public static function db() {\n    empty(static::$db) ? static::$db = \\SN::services()->db : false;\n\n    return static::$db;\n  }\n\n  /**\n   * Instate ActiveRecord from array of field values - even if it is empty\n   *\n   * @param array $properties List of field values [$propertyName => $propertyValue]\n   *\n   * @return static\n   */\n  public static function buildEvenEmpty(array $properties = []) {\n    $record = new static();\n    if (!empty($properties)) {\n      $record->clear();\n      $record->fromProperties($properties);\n    }\n\n    return $record;\n  }\n\n  /**\n   * Instate ActiveRecord from array of field values\n   *\n   * @param array $properties List of field values [$propertyName => $propertyValue]\n   *\n   * @return static|bool\n   */\n  public static function build(array $properties = []) {\n    if (!is_array($properties) || empty($properties)) {\n      return false;\n    }\n\n    return static::buildEvenEmpty($properties);\n  }\n\n  /**\n   * Set flag \"for update\"\n   *\n   * @param bool $forUpdate - DbQuery::DB_FOR_UPDATE | DbQuery::DB_SHARED\n   */\n  public static function setForUpdate($forUpdate = DbQuery::DB_FOR_UPDATE) {\n    static::$_forUpdate = $forUpdate;\n  }\n\n  /**\n   * Finds records by property - equivalent of SELECT ... WHERE ... AND ...\n   *\n   * @param array $propertyFilter - [$propertyName => $propertyValue]. Pass [] to find all records in table\n   *\n   * @return bool|\\mysqli_result\n   */\n  public static function find($propertyFilter) {\n    $dbq = static::dbPrepareQuery();\n    if (!empty($propertyFilter)) {\n      $dbq->setWhereArray(static::translateNames($propertyFilter, static::PROPERTIES_TO_FIELDS));\n    }\n\n    if (static::$_forUpdate == DbQuery::DB_FOR_UPDATE) {\n      $dbq->setForUpdate();\n      // Restoring default forUpdate state\n      static::$_forUpdate = DbQuery::DB_SHARED;\n    }\n\n    return $dbq->doSelect();\n  }\n\n  /**\n   * Gets first record by $where\n   *\n   * @param array $propertyFilter - [$propertyName => $propertyValue]. Pass [] to find all records in table\n   *\n   * @return string[] - [$field_name => $field_value]\n   */\n  public static function findRecordFirst($propertyFilter) {\n    $result = empty($mysqliResult = static::find($propertyFilter)) ? [] : $mysqliResult->fetch_assoc();\n\n    // Secondary check - for fetch_assoc() result\n    return empty($result) ? [] : $result;\n  }\n\n  /**\n   * Gets all records by $where\n   *\n   * @param array $propertyFilter - [$propertyName => $propertyValue]. Pass [] to find all records in table\n   *\n   * @return array[] - [(int) => [$field_name => $field_value]]\n   */\n  public static function findRecordsAll($propertyFilter) {\n    return empty($mysqliResult = static::find($propertyFilter)) ? [] : $mysqliResult->fetch_all(MYSQLI_ASSOC);\n  }\n\n  /**\n   * Gets first ActiveRecord by $where\n   *\n   * @param array $propertyFilter - [$propertyName => $propertyValue]. Pass [] to find all records in table\n   *\n   * @return static|bool\n   */\n  public static function findFirst($propertyFilter) {\n    $record = false;\n    $fields = static::findRecordFirst($propertyFilter);\n    if (!empty($fields)) {\n      $record = static::build(static::translateNames($fields, self::FIELDS_TO_PROPERTIES));\n      if (is_object($record)) {\n        $record->_isNew = false;\n      }\n    }\n\n    return $record;\n  }\n\n  /**\n   * Gets all ActiveRecords by $where\n   *\n   * @param array $propertyFilter - [$propertyName => $propertyValue]. Pass [] to find all records in table\n   *\n   * @return array|static[] - [(int) => static]\n   */\n  public static function findAll($propertyFilter) {\n    return static::fromRecordList(static::findRecordsAll($propertyFilter));\n  }\n\n\n  /**\n   * ActiveRecord constructor.\n   *\n   * @param \\Core\\GlobalContainer|null $services\n   */\n  public function __construct(GlobalContainer $services = null) {\n    parent::__construct($services);\n    $this->defaultValues();\n  }\n\n  /**\n   * @return bool\n   */\n  // TODO - do a check that all fields present in stored data. I.e. no empty fields with no defaults\n  public function insert() {\n    if ($this->isEmpty()) {\n      return false;\n    }\n    if (!$this->_isNew) {\n      return false;\n    }\n\n    $this->defaultValues();\n\n    if (!$this->dbInsert()) {\n      return false;\n    }\n\n    $this->accept();\n    $this->_isNew = false;\n\n    return true;\n  }\n\n  /**\n   * Normalize array\n   *\n   * Basically - uppercase all field names to make it use in PTL\n   * Can be override by descendants to make more convinient, clear and robust indexes\n   *\n   * @return array\n   */\n  public function ptlArray() {\n    $result = [];\n    foreach ($this->values as $key => $value) {\n      $result[strtoupper(\\HelperString::camelToUnderscore($key))] = $value;\n    }\n\n    return $result;\n  }\n\n  /**\n   * Get default value for field\n   *\n   * @param string $propertyName\n   *\n   * @return mixed\n   */\n  public function getDefault($propertyName) {\n    $fieldName = self::getFieldName($propertyName);\n\n    return\n      isset(static::dbGetFieldsDescription()[$fieldName]->Default)\n        ? static::dbGetFieldsDescription()[$fieldName]->Default\n        : null;\n  }\n\n  /**\n   * Returns default value if original value not set\n   *\n   * @param string $propertyName\n   *\n   * @return mixed\n   */\n  public function __get($propertyName) {\n    return $this->__isset($propertyName) ? parent::__get($propertyName) : $this->getDefault($propertyName);\n  }\n\n  public function __set($propertyName, $value) {\n    $this->shieldName($propertyName);\n    parent::__set($propertyName, $value);\n  }\n\n\n  /**\n   * Calculate table name by class name and fills internal property\n   *\n   * Namespaces does not count - only class name taken into account\n   * Class name converted from CamelCase to underscore_name\n   * Prefix \"Record\" is ignored - can be override\n   *\n   * Examples:\n   * Class \\Namespace\\ClassName will map to table `class_name`\n   * Class \\NameSpace\\RecordLongName will map to table `long_name`\n   *\n   * Can be overriden to provide different name\n   *\n   * @return string - table name in DB\n   *\n   */\n  protected static function calcTableName() {\n    $temp = explode('\\\\', get_called_class());\n    $className = end($temp);\n    if (strpos($className, static::IGNORE_PREFIX) === 0) {\n      $className = substr($className, strlen(static::IGNORE_PREFIX));\n    }\n\n    return \\HelperString::camelToUnderscore($className);\n  }\n\n  /**\n   * Get table fields description\n   *\n   * @return DbFieldDescription[]\n   */\n  protected static function dbGetFieldsDescription() {\n    return static::db()->schema()->getTableSchema(static::tableName())->fields;\n  }\n\n  /**\n   * Prepares DbQuery object for further operations\n   *\n   * @return DbQuery\n   */\n  protected static function dbPrepareQuery() {\n    return DbQuery::build(static::db())->setTable(static::tableName());\n  }\n\n  /**\n   * Is there translation for this field name to property name\n   *\n   * @param string $fieldName\n   *\n   * @return bool\n   */\n  protected static function haveTranslationToProperty($fieldName) {\n    return !empty(static::$_fieldsToProperties[$fieldName]);\n  }\n\n  /**\n   * Check if field exists\n   *\n   * @param string $fieldName\n   *\n   * @return bool\n   */\n  protected static function haveField($fieldName) {\n    return !empty(static::dbGetFieldsDescription()[$fieldName]);\n  }\n\n  /**\n   * Returns field name by property name\n   *\n   * @param string $propertyName\n   *\n   * @return string Field name for property or '' if not field\n   */\n  protected static function getFieldName($propertyName) {\n    $fieldName = array_search($propertyName, static::$_fieldsToProperties);\n    if (\n      // No translation found for property name\n      $fieldName === false\n      &&\n      // AND Property name is not among translatable field names\n      !static::haveTranslationToProperty($propertyName)\n      &&\n      // AND field name exists\n      static::haveField($propertyName)\n    ) {\n      // Returning property name as field name\n      $fieldName = $propertyName;\n    }\n\n    return $fieldName === false ? '' : $fieldName;\n  }\n\n  /**\n   * Does property exists?\n   *\n   * @param string $propertyName\n   *\n   * @return bool\n   */\n  protected static function haveProperty($propertyName) {\n    return !empty(static::getFieldName($propertyName));\n  }\n\n  /**\n   * Translate field name to property name\n   *\n   * @param string $fieldName\n   *\n   * @return string Property name for field if field exists or '' otherwise\n   */\n  protected static function getPropertyName($fieldName) {\n    return\n      // If there translation of field name = returning translation result\n      static::haveTranslationToProperty($fieldName)\n        ? static::$_fieldsToProperties[$fieldName]\n        // No, there is no translation\n        // Is field exists in table? If yes - returning field name as property name\n        : (static::haveField($fieldName) ? $fieldName : '');\n  }\n\n  /**\n   * Converts property-indexed value array to field-indexed via translation table\n   *\n   * @param array $names\n   * @param bool  $fieldToProperties - translation direction:\n   *                                 - self::FIELDS_TO_PROPERTIES - field to props\n   *                                 - self::PROPERTIES_TO_FIELDS - prop to fields\n   *\n   * @return array\n   */\n  // TODO - Throw exception on incorrect field\n  protected static function translateNames(array $names, $fieldToProperties = self::FIELDS_TO_PROPERTIES) {\n    $result = [];\n    foreach ($names as $name => $value) {\n      $exists = $fieldToProperties == self::FIELDS_TO_PROPERTIES ? static::haveField($name) : static::haveProperty($name);\n      if (!$exists) {\n        continue;\n      }\n\n      $name =\n        $fieldToProperties == self::FIELDS_TO_PROPERTIES\n          ? static::getPropertyName($name)\n          : static::getFieldName($name);\n      $result[$name] = $value;\n    }\n\n    return $result;\n  }\n\n  /**\n   * Makes array of object from field/property list array\n   *\n   * Empty records and non-records (non-subarrays) are ignored\n   * Function maintains record indexes\n   *\n   * @param array[] $records           - array of DB records [(int) => [$name => $value]]\n   * @param bool    $fieldToProperties - should names be translated (true - for field records, false - for property records)\n   *\n   * @return array|static[]\n   */\n  protected static function fromRecordList($records, $fieldToProperties = self::FIELDS_TO_PROPERTIES) {\n    $result = [];\n    if (is_array($records) && !empty($records)) {\n      foreach ($records as $key => $recordArray) {\n        if (!is_array($recordArray) || empty($recordArray)) {\n          continue;\n        }\n\n        $fieldToProperties === self::FIELDS_TO_PROPERTIES\n          ? $recordArray = static::translateNames($recordArray, self::FIELDS_TO_PROPERTIES)\n          : false;\n\n        $theRecord = static::build($recordArray);\n        if (is_object($theRecord)) {\n          $theRecord->_isNew = false;\n//          if(!empty($theRecord->id)) {\n//            $key = $theRecord->id;\n//          }\n          $result[$key] = $theRecord;\n        }\n      }\n    }\n\n    return $result;\n  }\n\n  protected function defaultValues() {\n    foreach (static::dbGetFieldsDescription() as $fieldName => $fieldData) {\n      if (array_key_exists($propertyName = static::getPropertyName($fieldName), $this->values)) {\n        continue;\n      }\n\n      // Skipping auto increment fields\n      if (strpos($fieldData->Extra, SN_SQL_EXTRA_AUTO_INCREMENT) !== false) {\n        continue;\n      }\n\n      if ($fieldData->Type == SN_SQL_TYPE_NAME_TIMESTAMP && $fieldData->Default == SN_SQL_DEFAULT_CURRENT_TIMESTAMP) {\n        $this->__set($propertyName, SN_TIME_SQL);\n        continue;\n      }\n\n      $this->__set($propertyName, $fieldData->Default);\n    }\n  }\n\n  /**\n   * Set AR properties from array of PROPERTIES\n   *\n   * DOES NOT override existing values\n   * DOES set default values for empty fields\n   *\n   * @param array $properties\n   */\n  protected function fromProperties(array $properties) {\n    foreach ($properties as $name => $value) {\n      $this->__set($name, $value);\n    }\n\n    $this->defaultValues();\n  }\n\n  /**\n   * Set AR properties from array of FIELDS\n   *\n   * DOES NOT override existing values\n   * DOES set default values for empty fields\n   *\n   * @param array $fields List of field values [$fieldName => $fieldValue]\n   */\n  protected function fromFields(array $fields) {\n    $this->fromProperties(static::translateNames($fields, self::FIELDS_TO_PROPERTIES));\n  }\n\n  /**\n   * @return bool\n   */\n  protected function dbInsert() {\n    return\n      static::dbPrepareQuery()\n        ->setValues(static::translateNames($this->values, self::PROPERTIES_TO_FIELDS))\n        ->doInsert();\n  }\n\n  /**\n   * Protects object from setting non-existing property\n   *\n   * @param string $propertyName\n   *\n   * @throws DbalFieldInvalidException\n   */\n  protected function shieldName($propertyName) {\n    if (!self::haveProperty($propertyName)) {\n      throw new DbalFieldInvalidException(sprintf(\n        '{{{ Свойство \\'%1$s\\' не существует в ActiveRecord \\'%2$s\\' }}}', $propertyName, get_called_class()\n      ));\n    }\n  }\n\n}\n"
  },
  {
    "path": "classes/DBAL/ActiveRecordAbstractIndexed.php",
    "content": "<?php\n/**\n * Created by Gorlum 04.10.2017 8:23\n */\n\nnamespace DBAL;\n\n/**\n * Class ActiveRecordAbstractIndexed\n * @package DBAL\n *\n * @property int|string $id - Record ID name would be normalized to 'id'\n *\n */\nclass ActiveRecordAbstractIndexed extends ActiveRecordAbstract {\n  const ID_PROPERTY_NAME = 'id';\n\n  /**\n   * Autoincrement index field name in DB\n   * Would be normalized to 'id' ($id class property)\n   *\n   * @var string $_primaryIndexField\n   */\n  protected static $_primaryIndexField = 'id';\n\n\n  /**\n   * @param int|string $recordId\n   *\n   * @return string[]\n   */\n  public static function findRecordById($recordId) {\n    return static::findRecordFirst([self::ID_PROPERTY_NAME => $recordId]);\n  }\n\n  /**\n   * @param int|string $recordId\n   *\n   * @return bool|static\n   */\n  public static function findById($recordId) {\n    return static::findFirst([self::ID_PROPERTY_NAME => $recordId]);\n  }\n\n  /**\n   * @return bool\n   */\n  public function update() {\n    if (empty($this->_changes) && empty($this->_deltas)) {\n      return true;\n    }\n\n    $this->defaultValues();\n\n    if (!$this->dbUpdate()) {\n      return false;\n    }\n\n\n    $this->accept();\n\n//    $this->_isNew = false;\n\n    return true;\n  }\n\n  public function delete() {\n    $result = static::dbPrepareQuery()\n      ->setWhereArray([static::$_primaryIndexField => $this->id])\n      ->doDelete();\n\n    if ($result) {\n      $this->id = 0;\n      $this->_isDeleted = true;\n    }\n\n    return $result;\n  }\n\n\n//  /**\n//   * Reload current record from ID\n//   *\n//   * @return bool\n//   */\n//  public function reload() {\n//    //$recordId = $this->id;\n//    $recordId = $this->{self::ID_PROPERTY_NAME};\n//    if (empty($recordId)) {\n//      return false;\n//    }\n//\n//    $this->accept();\n//\n//    $fields = static::findRecordFirst($recordId);\n//    if (empty($fields)) {\n//      return false;\n//    }\n//\n//    $this->fromFields($fields);\n//    $this->_isNew = false;\n//\n//    return true;\n//  }\n//\n//  protected static function dbFetch(\\mysqli_result $mysqliResult) {\n//    return $mysqliResult->fetch_assoc();\n//  }\n\n\n  /**\n   * @return bool\n   */\n  protected function dbUpdate() {\n    return\n      static::dbPrepareQuery()\n        ->setValues(empty($this->_changes) ? [] : static::translateNames($this->_changes, self::PROPERTIES_TO_FIELDS))\n        ->setAdjust(empty($this->_deltas) ? [] : static::translateNames($this->_deltas, self::PROPERTIES_TO_FIELDS))\n        ->setWhereArray([static::$_primaryIndexField => $this->id])\n        ->doUpdate();\n  }\n\n\n\n\n\n\n\n\n  // Some overrides\n\n  /**\n   * @inheritdoc\n   */\n  protected static function getFieldName($propertyName) {\n    return\n      $propertyName == static::ID_PROPERTY_NAME\n        ? static::$_primaryIndexField\n        : parent::getFieldName($propertyName);\n  }\n\n  /**\n   * @inheritdoc\n   */\n  protected static function getPropertyName($fieldName) {\n    return\n      $fieldName == static::$_primaryIndexField\n        ? static::ID_PROPERTY_NAME\n        : parent::getPropertyName($fieldName);\n  }\n\n//  // TODO - Возможно - не нужно наследовать\n//  public function accept() {\n//    parent::accept();\n////    $this->_isNew = empty($this->id);\n//  }\n\n  /**\n   * @inheritdoc\n   */\n  // TODO - do a check that all fields present in stored data. I.e. no empty fields with no defaults\n  public function insert() {\n    if (!parent::insert()) {\n      return false;\n    }\n\n    $this->{self::ID_PROPERTY_NAME} = $this->dbLastInsertId();\n    $this->accept();\n\n    return true;\n  }\n\n  /**\n   * @return int|string\n   */\n  protected function dbLastInsertId() {\n    return static::db()->db_insert_id();\n  }\n\n}\n"
  },
  {
    "path": "classes/DBAL/DbAbstractResultIterator.php",
    "content": "<?php\n/**\n * Created by Gorlum 25.11.2017 20:02\n */\n\nnamespace DBAL;\n\nuse \\mysqli_result;\nuse Common\\Interfaces\\ICountableIterator;\n\n/**\n * Class DbAbstractResultIterator\n *\n * Base class which makes Iterator from internal $mysqli_result\n * Other classes which inherits from this will implement their own methods to obtain mysqli_result (or fill it internally)\n *\n * @package DBAL\n */\nabstract class DbAbstractResultIterator implements ICountableIterator {\n  /**\n   * @var mysqli_result|false $mysqli_result\n   */\n  protected $mysqli_result;\n\n  /**\n   * @var int|float|null\n   */\n  protected $counter = null;\n  /**\n   * @var array|null $currentRow\n   */\n  protected $currentRow = null;\n\n  /**\n   * Seeks mysqli_result to first record\n   */\n  protected function seekToFirst() {\n    if ($this->mysqli_result instanceof mysqli_result) {\n      $this->mysqli_result->data_seek(0);\n    }\n  }\n\n  /**\n   * @inheritdoc\n   */\n  public function rewind() {\n    if ($this->mysqli_result instanceof mysqli_result) {\n      $this->seekToFirst();\n      $this->next();\n    }\n    $this->counter = $this->valid() ? 0 : null;\n  }\n\n  /**\n   * @inheritdoc\n   */\n  public function valid() {\n    return $this->mysqli_result instanceof mysqli_result && $this->currentRow !== null;\n  }\n\n  /**\n   * @inheritdoc\n   */\n  public function next() {\n    if ($this->mysqli_result instanceof mysqli_result) {\n      $this->currentRow = $this->mysqli_result->fetch_assoc();\n      $this->counter++;\n    }\n  }\n\n  /**\n   * @inheritdoc\n   */\n  public function key() {\n    return $this->mysqli_result instanceof mysqli_result ? $this->counter : null;\n  }\n\n  /**\n   * @inheritdoc\n   */\n  public function current() {\n    return $this->currentRow;\n  }\n\n  /**\n   * @inheritdoc\n   */\n  public function count() {\n    return $this->mysqli_result instanceof mysqli_result ? $this->mysqli_result->num_rows : 0;\n  }\n\n}\n"
  },
  {
    "path": "classes/DBAL/DbFieldDescription.php",
    "content": "<?php\n/**\n * Created by Gorlum 04.10.2017 8:55\n */\n\nnamespace DBAL;\n\n/**\n * Class DbFieldDescription\n * @package DBAL\n *\n * Objects of this class contains MySql field description\n */\nclass DbFieldDescription {\n  public $Field;\n  public $Type;\n  public $Collation;\n  public $Null;\n  public $Key;\n  public $Default;\n  public $Extra;\n  public $Privileges;\n  public $Comment;\n\n  /**\n   * @param array $mySqlDescription\n   */\n  public function fromMySqlDescription($mySqlDescription) {\n    $this->Field = isset($mySqlDescription['Field']) ? $mySqlDescription['Field'] : null;\n    $this->Type = isset($mySqlDescription['Type']) ? $mySqlDescription['Type'] : null;\n    $this->Collation = isset($mySqlDescription['Collation']) ? $mySqlDescription['Collation'] : null;\n    $this->Null = isset($mySqlDescription['Null']) ? $mySqlDescription['Null'] : null;\n    $this->Key = isset($mySqlDescription['Key']) ? $mySqlDescription['Key'] : null;\n    $this->Default = isset($mySqlDescription['Default']) ? $mySqlDescription['Default'] : null;\n    $this->Extra = isset($mySqlDescription['Extra']) ? $mySqlDescription['Extra'] : null;\n    $this->Privileges = isset($mySqlDescription['Privileges']) ? $mySqlDescription['Privileges'] : null;\n    $this->Comment = isset($mySqlDescription['Comment']) ? $mySqlDescription['Comment'] : null;\n\n    return $this;\n  }\n\n}\n"
  },
  {
    "path": "classes/DBAL/DbIndexDescription.php",
    "content": "<?php\n/**\n * Created by Gorlum 04.10.2017 8:55\n */\n\nnamespace DBAL;\n\n/**\n * Class DbIndexDescription\n * @package DBAL\n *\n * Objects of this class contains MySql index description\n */\nclass DbIndexDescription {\n  public $Key_name = ''; // Key_name - The name of the index. If the index is the primary key, the name is always PRIMARY.\n  public $Non_unique = 1; // 0 if the index cannot contain duplicates, 1 if it can.\n\n  public $Packed = null; // Indicates how the key is packed. NULL if it is not.\n  public $Index_type = 'BTREE'; // The index method used (BTREE, FULLTEXT, HASH, RTREE).\n  public $Comment = ''; // Information about the index not described in its own column, such as disabled if the index is disabled.\n  public $Index_comment = ''; // Any comment provided for the index with a COMMENT attribute when the index was created.\n  public $Visible; // Whether the index is visible to the optimizer. See Section 8.3.12, “Invisible Indexes”.\n\n  /**\n   * @var DbIndexField[]\n   */\n  public $fields = [];\n\n  protected $sorted = true;\n\n  /**\n   * @param array $indexField\n   *\n   * @return $this\n   */\n  public function addField($indexField) {\n    $this->Key_name      = isset($indexField['Key_name']) ? $indexField['Key_name'] : null;\n    $this->Non_unique    = isset($indexField['Non_unique']) ? $indexField['Non_unique'] : null;\n    $this->Packed        = isset($indexField['Packed']) ? $indexField['Packed'] : null;\n    $this->Index_type    = isset($indexField['Index_type']) ? $indexField['Index_type'] : null;\n    $this->Comment       = isset($indexField['Comment']) ? $indexField['Comment'] : null;\n    $this->Index_comment = isset($indexField['Index_comment']) ? $indexField['Index_comment'] : null;\n    $this->Visible       = isset($indexField['Visible']) ? $indexField['Visible'] : null;\n\n    if (isset($indexField['Column_name']) || isset($indexField['Expression'])) {\n      $fullName = $indexField['Column_name'] .\n        (!empty($indexField['Expression']) ? '|' . $indexField['Expression'] : '');\n\n      $this->fields[$fullName] = new DbIndexField($indexField);\n      $this->sorted            = false;\n    }\n\n    return $this;\n  }\n\n  /**\n   * @return string\n   */\n  public function signature() {\n    $this->sort();\n\n\n    return implode(',', array_keys($this->fields));\n  }\n\n  protected function sort() {\n    if (!$this->sorted) {\n      asort($this->fields, function (DbIndexField $a, DbIndexField $b) {\n        return $a->Seq_in_index - $b->Seq_in_index;\n      });\n    }\n  }\n\n}\n"
  },
  {
    "path": "classes/DBAL/DbIndexField.php",
    "content": "<?php\n/**\n * Created by Gorlum 01.03.2019 9:06\n */\n\nnamespace DBAL;\n\nclass DbIndexField {\n  public $Column_name; // Column_name - The column name. See also the description for the Expression column.\n  public $Seq_in_index; // The column sequence number in the index, starting with 1.\n  public $Collation; // How the column is sorted in the index. This can have values A (ascending), D (descending), or NULL (not sorted).\n  public $Cardinality; // An estimate of the number of unique values in the index. To update this number, run ANALYZE TABLE or (for MyISAM tables) myisamchk -a.\n  public $Sub_part; // The index prefix. That is, the number of indexed characters if the column is only partly indexed, NULL if the entire column is indexed.\n  public $Null; // Contains YES if the column may contain NULL values and '' if not.\n  public $Expression; // MySQL 8.0.13 and higher supports functional key parts (see Functional Key Parts), which affects both the Column_name and Expression columns:\n\n  public function __construct($indexField = []) {\n    is_array($indexField) && !empty($indexField) ? $this->fromMySqlDescription($indexField) : false;\n  }\n\n  /**\n   * @param array $indexField\n   */\n  public function fromMySqlDescription($indexField) {\n    $this->Seq_in_index = isset($indexField['Seq_in_index']) ? $indexField['Seq_in_index'] : null;\n    $this->Column_name  = isset($indexField['Column_name']) ? $indexField['Column_name'] : null;\n    $this->Collation    = isset($indexField['Collation']) ? $indexField['Collation'] : null;\n    $this->Cardinality  = isset($indexField['Cardinality']) ? $indexField['Cardinality'] : null;\n    $this->Sub_part     = isset($indexField['Sub_part']) ? $indexField['Sub_part'] : null;\n    $this->Null         = isset($indexField['Null']) ? $indexField['Null'] : null;\n    $this->Expression   = isset($indexField['Expression']) ? $indexField['Expression'] : null;\n\n    return $this;\n  }\n\n}\n"
  },
  {
    "path": "classes/DBAL/DbMysqliResultIterator.php",
    "content": "<?php\n/**\n * Created by Gorlum 17.10.2017 10:48\n */\n\nnamespace DBAL;\n\nuse \\mysqli_result;\n\n/**\n * Class DbMysqliResultIterator\n *\n * Simplest implementation of DbAbstractResultIterator - getting result from constructor\n *\n * @package DBAL\n */\nclass DbMysqliResultIterator extends DbAbstractResultIterator {\n  /**\n   * DbMysqliResultIterator constructor.\n   *\n   * @param mysqli_result|bool $mysqli_result\n   */\n  public function __construct($mysqli_result) {\n    $this->mysqli_result = $mysqli_result;\n\n    $this->rewind();\n  }\n\n}\n"
  },
  {
    "path": "classes/DBAL/DbQuery.php",
    "content": "<?php\n/**\n * Created by Gorlum 07.08.2016 2:36\n */\n\nnamespace DBAL;\n\nuse \\HelperArray;\nuse DBAL\\db_mysql;\nuse \\SN;\n\n/**\n * Class DbQuery\n *\n * New replacement for DbQueryConstructor\n * Simplified version\n * Chained calls - \"Fluid interface\"\n *\n * @package DBAL\n */\nclass DbQuery {\n\n  const SELECT = 'SELECT';\n  const REPLACE = 'REPLACE';\n  const INSERT = 'INSERT';\n  const INSERT_IGNORE = 'INSERT IGNORE';\n  const UPDATE = 'UPDATE';\n  const DELETE = 'DELETE';\n\n  const DB_INSERT_PLAIN = 0;\n  const DB_INSERT_REPLACE = 1;\n  const DB_INSERT_IGNORE = 2;\n\n  const DB_RECORDS_ALL = false;\n  const DB_RECORD_ONE = true;\n\n  const DB_SHARED = false;\n  const DB_FOR_UPDATE = true;\n\n  /**\n   * @var db_mysql $db\n   */\n  protected $db;\n\n  /**\n   * Which command would be performed\n   *\n   * @var string $command\n   */\n  protected $command;\n\n  protected $table = '';\n\n  /**\n   * Contains field names integer keyed\n   *\n   * For SELECT {fields} FROM\n   * For INSERT/REPLACE {fields} UPDATE ...\n   *\n   * @var array $fields\n   */\n  protected $fields = array();\n  protected $where = array();\n  protected $whereDanger = array();\n\n  /**\n   * Contain array of values - fielded or not\n   *\n   * For INSERT/REPLACE ... SET, UPDATE ... SET - contains fieldName => value\n   * For INSERT/REPLACE ... VALUES - contains values[][]\n   *\n   * @var array\n   */\n  protected $values = array();\n  /**\n   * Contain array of DANGER values for batch INSERT/REPLACE\n   *\n   * @var string[]\n   */\n  protected $valuesDanger = array();\n  protected $adjust = array();\n  protected $adjustDanger = array();\n\n\n  /**\n   * Variable for incremental query build\n   *\n   * @var string[] $build\n   */\n  protected $build = array();\n\n  protected $isOneRow = false;\n\n  protected $forUpdate = false;\n\n  /**\n   * DbQuery constructor.\n   *\n   * @param  null|\\DBAL\\db_mysql $db\n   */\n  // TODO - $db should be supplied externally\n  public function __construct($db = null) {\n    $this->db = empty($db) ? SN::$gc->db : $db;\n  }\n\n  /**\n   * @param null|db_mysql $db\n   *\n   * @return static\n   */\n  public static function build($db = null) {\n    return new static($db);\n  }\n\n\n  public function select() {\n    $this->build = array();\n\n    $this->buildCommand(self::SELECT);\n    $this->build[] = ' *';\n    $this->build[] = \" FROM \" . $this->quoteTable($this->table);\n    $this->buildWhere();\n    $this->buildLimit();\n    $this->buildForUpdate();\n\n    return $this->__toString();\n  }\n\n  public function delete() {\n    $this->build = array();\n\n    $this->buildCommand(self::DELETE);\n    $this->buildWhere();\n    $this->buildLimit();\n\n    return $this->__toString();\n  }\n\n  public function update() {\n    $this->build = array();\n\n    $this->buildCommand(self::UPDATE);\n    $this->buildSetFields();\n    $this->buildWhere();\n    $this->buildLimit();\n\n    return $this->__toString();\n  }\n\n  /**\n   * @param int $replace\n   *\n   * @return string\n   */\n  protected function setInsertCommand($replace) {\n    switch ($replace) {\n      case self::DB_INSERT_IGNORE:\n        $result = self::INSERT_IGNORE;\n      break;\n      case self::DB_INSERT_REPLACE:\n        $result = self::REPLACE;\n      break;\n      default:\n        $result = self::INSERT;\n      break;\n    }\n\n    return $result;\n  }\n\n  /**\n   * @param int  $replace\n   * @param bool $forceSingleInsert\n   *\n   * @return bool\n   */\n  public function doInsert($replace = self::DB_INSERT_PLAIN, $forceSingleInsert = false) {\n    return doquery($this->insert($replace, $forceSingleInsert));\n  }\n\n  public function doUpdate() {\n    return doquery($this->update());\n  }\n\n  public function doUpdateDb() {\n    return $this->db->doquery($this->update());\n  }\n\n  /**\n   * @return array|bool|\\mysqli_result|null\n   * @deprecated\n   */\n  public function doDelete() {\n    return doquery($this->delete());\n  }\n\n  // TODO - Do something with delete when there is no records\n  public function doDeleteDb() {\n    return $this->db->doquery($this->delete());\n  }\n\n  /**\n   * @return array|bool|\\mysqli_result|null\n   */\n  public function doSelect() {\n    return doquery($this->select());\n  }\n\n  /**\n   * @return array|null\n   */\n  public function doSelectFetch() {\n    return doquery($this->select(), true);\n  }\n\n  /**\n   * @param int  $replace\n   * @param bool $forceSingleInsert\n   *\n   * @return string\n   */\n  public function insert($replace = self::DB_INSERT_PLAIN, $forceSingleInsert = false) {\n    $this->build = array();\n\n    $this->buildCommand($this->setInsertCommand($replace));\n\n    if (!$forceSingleInsert && is_array($this->fields) && !empty($this->fields)) {\n      // If there are fields - it's batch insert... unless it forced single insert\n      $this->build[] = \" (\";\n      $this->buildFieldNames(); // used $this->fields\n      $this->build[] = \") VALUES \";\n      $this->buildValuesVector(); // $this->valuesDanger + $this->values\n    } else {\n      // Otherwise - it's single field insert\n      $this->buildSetFields();\n    }\n\n    return $this->__toString();\n  }\n\n\n  /**\n   * @param $table\n   *\n   * @return $this\n   */\n  public function setTable($table) {\n    $this->table = $table;\n\n    return $this;\n  }\n\n  /**\n   * @param bool $oneRow - DB_RECORDS_ALL || DB_RECORD_ONE\n   *\n   * @return $this\n   */\n  public function setOneRow($oneRow = self::DB_RECORD_ONE) {\n    $this->isOneRow = $oneRow;\n\n    return $this;\n  }\n\n  /**\n   * @param bool $forUpdate - DB_FOR_UPDATE || DB_SHARED\n   *\n   * @return $this\n   */\n  public function setForUpdate($forUpdate = self::DB_FOR_UPDATE) {\n    $this->forUpdate = $forUpdate;\n\n    return $this;\n  }\n\n  /**\n   * Set values for a query\n   *\n   * Values used for INSERT/REPLACE ... SET queries as one-dimension array and INSERT/REPLACE ... VALUES as two-dimension array\n   *\n   * @param array|array[] $values - [(str)name => (mixed)value] | [ [(str)name => (mixed)value] ]\n   *\n   * @return $this\n   */\n  public function setValues($values = []) {\n    HelperArray::merge($this->values, $values, HelperArray::MERGE_PHP);\n\n    return $this;\n  }\n\n  /**\n   * @param array $values\n   *\n   * @return $this\n   */\n  public function setValuesDanger($values = array()) {\n    HelperArray::merge($this->valuesDanger, $values, HelperArray::MERGE_PHP);\n\n    return $this;\n  }\n\n  /**\n   * @param array $values\n   *\n   * @return $this\n   */\n  public function setAdjust($values = array()) {\n    HelperArray::merge($this->adjust, $values, HelperArray::MERGE_PHP);\n\n    return $this;\n  }\n\n  /**\n   * @param array $values\n   *\n   * @return $this\n   */\n  public function setAdjustDanger($values = array()) {\n    HelperArray::merge($this->adjustDanger, $values, HelperArray::MERGE_PHP);\n\n    return $this;\n  }\n\n  /**\n   * @param array $fields\n   *\n   * @return $this\n   */\n  public function setFields($fields = array()) {\n    HelperArray::merge($this->fields, $fields, HelperArray::MERGE_PHP);\n\n    return $this;\n  }\n\n  /**\n   * Merges WHERE array as array_merge()\n   *\n   * @param array $whereArray\n   *\n   * @return $this\n   */\n  public function setWhereArray($whereArray = array()) {\n    HelperArray::merge($this->where, $whereArray, HelperArray::MERGE_PHP);\n\n    return $this;\n  }\n\n  /**\n   * Sets DANGER array - where values should be escaped BEFORE entering DBAL\n   *\n   * Deprecated - all values should pass through DBAL\n   *\n   * @param array $whereArrayDanger\n   *\n   * @return $this\n   */\n  public function setWhereArrayDanger($whereArrayDanger = array()) {\n    HelperArray::merge($this->whereDanger, $whereArrayDanger, HelperArray::MERGE_PHP);\n\n    return $this;\n  }\n\n\n  /**\n   * Wrapper for db_escape()\n   *\n   * @param mixed $string\n   *\n   * @return string\n   */\n  protected function escape($string) {\n    return $this->db->db_escape($string);\n  }\n\n  protected function escapeEmulator($value) {\n    // Characters encoded are NUL (ASCII 0), \\n, \\r, \\, ', \", and Control-Z.\n    return str_replace(\n      array(\"\\\\\", \"\\0\", \"\\n\", \"\\r\", \"'\", \"\\\"\", \"\\z\",),\n      array('\\\\\\\\', '\\0', '\\n', '\\r', '\\\\\\'', '\\\"', '\\z',),\n      $value\n    );\n  }\n\n  /**\n   * Escaping string value and quoting it\n   *\n   * @param mixed $value\n   *\n   * @return string\n   */\n  protected function stringValue($value) {\n    return \"'\" . $this->escape((string)$value) . \"'\";\n  }\n\n  /**\n   * Quote mysql DB identifier\n   *\n   * @param mixed $fieldName\n   *\n   * @return string\n   */\n  public function quote($fieldName) {\n    return \"`\" . $this->escape((string)$fieldName) . \"`\";\n  }\n\n  /**\n   * Make \"adjustment\" string like `$fieldValue` = `$fieldValue` + ('$fieldName')\n   * Quotes needs for negative values\n   *\n   * @param mixed      $fieldValue\n   * @param int|string $fieldName\n   *\n   * @return string\n   */\n  public function makeAdjustString($fieldValue, $fieldName) {\n    return is_int($fieldName)\n      ? $this->makeValueSafe($fieldValue)\n      : (\n        ($fieldNameQuoted = $this->quote($fieldName))\n        . \" = \"\n        . $fieldNameQuoted\n        . \" + (\"\n        . $this->makeValueSafe($fieldValue)\n        . \")\"\n      );\n  }\n\n  /**\n   * Make \"equal\" string like `$fieldValue` = '$fieldName'\n   *\n   * @param mixed      $fieldValue\n   * @param int|string $fieldName - field name. Is this param is integer - no field name added\n   *\n   * @return string\n   */\n  public function makeFieldEqualValue($fieldValue, $fieldName) {\n    return is_int($fieldName)\n      ? $this->makeValueSafe($fieldValue)\n      : ($this->quote($fieldName) . \" = \" . $this->makeValueSafe($fieldValue));\n  }\n\n  /**\n   * Quote table name with `{{ }}`\n   *\n   * @param mixed $tableName\n   *\n   * @return string\n   */\n  protected function quoteTable($tableName) {\n    return \"`{{\" . $this->escape((string)$tableName) . \"}}`\";\n  }\n\n  /**\n   * Makes value safe for using in SQL query\n   *\n   * @param mixed $value\n   *\n   * @return int|string\n   */\n  public function makeValueSafe($value) {\n    switch (gettype($value)) {\n      case TYPE_INTEGER:\n      case TYPE_DOUBLE:\n        // do nothing\n      break;\n\n      case TYPE_BOOLEAN:\n        $value = $value ? 1 : 0;\n      break;\n\n      case TYPE_NULL:\n        $value = 'NULL';\n      break;\n\n      case TYPE_EMPTY:\n        // No-type defaults to string\n        /** @noinspection PhpMissingBreakStatementInspection */\n      case TYPE_ARRAY:\n        $value = serialize($value);\n      // Continuing with serialized array value\n      case TYPE_STRING:\n      default:\n        $value = $this->stringValue($value);\n      break;\n    }\n\n    return $value;\n  }\n\n\n  /**\n   * @param $command\n   */\n  protected function buildCommand($command) {\n    switch ($this->command = $command) {\n      case self::UPDATE:\n        $this->build[] = $this->command . \" \" . $this->quoteTable($this->table);\n      break;\n\n      case self::DELETE:\n        $this->build[] = $this->command . \" FROM \" . $this->quoteTable($this->table);\n      break;\n\n      case self::REPLACE:\n      case self::INSERT_IGNORE:\n      case self::INSERT:\n        $this->build[] = $this->command . \" INTO \" . $this->quoteTable($this->table);\n      break;\n\n      case self::SELECT:\n        $this->build[] = $this->command;\n      break;\n    }\n  }\n\n  // UPDATE/INSERT ... SET field = value, ...\n  protected function buildSetFields() {\n    $safeFields = array();\n    // Sets overwritten by Adjusts\n    if ($safeValuesDanger = implode(',', $this->valuesDanger)) {\n      $safeFields[] = &$safeValuesDanger;\n    }\n    if ($safeFieldsEqualValues = implode(',', HelperArray::map($this->values, array($this, 'makeFieldEqualValue'), true))) {\n      $safeFields[] = &$safeFieldsEqualValues;\n    }\n    if ($safeAdjustDanger = implode(',', $this->adjustDanger)) {\n      $safeFields[] = &$safeAdjustDanger;\n    }\n    if ($safeAdjust = implode(',', HelperArray::map($this->adjust, array($this, 'makeAdjustString'), true))) {\n      $safeFields[] = &$safeAdjust;\n    }\n    $safeFieldsString = implode(',', $safeFields);\n\n    if (!empty($safeFieldsString)) {\n      $this->build[] = ' SET ';\n      $this->build[] = $safeFieldsString;\n    }\n  }\n\n  // INSERT ... VALUES\n  /**\n   * Compiles fields list into string list along with quoting fieldnames with \"`\" symbol\n   */\n  protected function buildFieldNames() {\n    $this->build[] = implode(',', HelperArray::map($this->fields, array($this, 'quote')));\n  }\n\n  /**\n   * Vector values is for batch INSERT/REPLACE\n   */\n  // TODO - CHECK!\n  protected function buildValuesVector() {\n    $compiled = array();\n\n    if (!empty($this->valuesDanger)) {\n      $compiled = $this->valuesDanger;\n    }\n\n    foreach ($this->values as $valuesVector) {\n      $compiled[] = '(' . implode(',', HelperArray::map($valuesVector, array($this, 'makeValueSafe'))) . ')';\n    }\n\n    $this->build[] = implode(',', $compiled);\n  }\n\n\n  protected function buildWhere() {\n    $safeWhere = implode(\n      ' AND ',\n      $this->whereDanger +\n      HelperArray::map($this->where, array($this, 'makeFieldEqualValue'), true)\n    );\n\n    if (!empty($safeWhere)) {\n      $this->build[] = \" WHERE {$safeWhere}\";\n    }\n  }\n\n  protected function buildLimit() {\n    if ($this->isOneRow == self::DB_RECORD_ONE) {\n      $this->build[] = ' LIMIT 1';\n    }\n  }\n\n  protected function buildForUpdate() {\n    if ($this->forUpdate == self::DB_FOR_UPDATE) {\n      $this->build[] = ' FOR UPDATE';\n    }\n  }\n\n  public function __toString() {\n    return implode('', $this->build);\n  }\n\n}\n"
  },
  {
    "path": "classes/DBAL/DbSqlPaging.php",
    "content": "<?php\n/**\n * Created by Gorlum 25.11.2017 18:49\n */\n\nnamespace DBAL;\n\nuse mysqli_result;\n\n/**\n * Class DbSqlPaging\n *\n * Another implementation for DbAbstractResultIterator\n *\n * Fetches data from string SQL-query and pages through it\n *\n * @package DBAL\n */\nclass DbSqlPaging extends DbAbstractResultIterator {\n  protected $db = null;\n  protected $sqlQuery;\n  protected $pageSize = PAGING_PAGE_SIZE_DEFAULT;\n  protected $currentPage = 1;\n\n  /**\n   * DbSqlPaging constructor.\n   *\n   * @param string              $sqlQuery\n   * @param int                 $pageSize\n   * @param int                 $currentPage\n   * @param \\DBAL\\db_mysql|null $db\n   */\n  public function __construct($sqlQuery, $pageSize = PAGING_PAGE_SIZE_DEFAULT, $currentPage = 1, $db = null) {\n    $this->sqlQuery = $sqlQuery;\n    $this->pageSize = max(intval($pageSize), PAGING_PAGE_SIZE_MINIMUM);\n    $this->currentPage = max(intval($currentPage), 1);\n\n    $this->db = isset($db) ? $db : \\SN::$gc->db;\n\n    $this->query();\n  }\n\n  /**\n   * @inheritdoc\n   */\n  public function valid() {\n    return parent::valid() && $this->counter < $this->getRecordsOnCurrentPage();\n  }\n\n  /**\n   * @inheritdoc\n   */\n  public function count() {\n    return $this->mysqli_result instanceof mysqli_result ? $this->getRecordsOnCurrentPage() : 0;\n  }\n\n  /**\n   * @inheritdoc\n   */\n  protected function seekToFirst() {\n    if ($this->mysqli_result instanceof mysqli_result) {\n      $this->mysqli_result->data_seek($this->getPageZeroRecordNum());\n    }\n  }\n\n  /**\n   * Get current page number\n   *\n   * @return int\n   */\n  public function getCurrentPageNumber() {\n    return $this->currentPage;\n  }\n\n  /**\n   * Get total number of records in query\n   *\n   * @return int\n   */\n  public function getTotalRecords() {\n    return $this->mysqli_result instanceof mysqli_result ? $this->mysqli_result->num_rows : 0;\n  }\n\n  /**\n   * Calculates total page numbers\n   *\n   * @return float|int\n   */\n  public function getTotalPages() {\n    return $this->mysqli_result instanceof mysqli_result ? ceil($this->getTotalRecords() / $this->pageSize) : 0;\n  }\n\n  /**\n   * Calculates record count on current page\n   *\n   * @return int\n   */\n  public function getRecordsOnCurrentPage() {\n    return min($this->pageSize, $this->getTotalRecords() - $this->getPageZeroRecordNum());\n  }\n\n  /**\n   * Calculates record index from which current page starts\n   *\n   * @return int\n   */\n  protected function getPageZeroRecordNum() {\n    return ($this->currentPage - 1) * $this->pageSize;\n  }\n\n  /**\n   * Queries DB for data and makes all necessary preparations\n   */\n  protected function query() {\n    if ($this->mysqli_result !== null) {\n      unset($this->mysqli_result);\n    }\n    // Performing query\n    $this->mysqli_result = $this->db->doquery($this->sqlQuery);\n\n    // Checking if page number within allowed ranges\n    if (\n      // Is it not a first page?\n      $this->currentPage != 1\n      &&\n      // Is current page beyond limits?\n      $this->currentPage > $this->getTotalPages()\n    ) {\n      // Correcting page number to first\n      $this->currentPage = max(1, $this->getTotalPages());\n    }\n\n    $this->rewind();\n  }\n\n}\n"
  },
  {
    "path": "classes/DBAL/OldDbChangeSet.php",
    "content": "<?php\n\n/**\n * Created by Gorlum 03.03.2017 22:32\n */\n\nnamespace DBAL;\n\nuse SN;\nuse Unit\\DBStaticUnit;\n\n/**\n * Class DBAL\\OldDbChangeSet\n *\n * This class is DEPRECATED!\n * It should not be used in new code from now!\n *\n * @deprecated\n */\nclass OldDbChangeSet {\n\n  /**\n   * @param      $unit_id\n   * @param      $unit_value\n   * @param      $user\n   * @param null $planet_id\n   *\n   * @return array\n   * @deprecated\n   */\n  public static function db_changeset_prepare_unit($unit_id, $unit_value, $user, $planet_id = null) {\n    if (!is_array($user)) {\n      // TODO - remove later\n      print('<h1>СООБЩИТЕ ЭТО АДМИНУ: DBAL\\OldDbChangeSet::db_changeset_prepare_unit() - USER is not ARRAY</h1>');\n      pdump(debug_backtrace());\n      die('USER is not ARRAY');\n    }\n    if (!isset($user['id']) || !$user['id']) {\n      // TODO - remove later\n      print('<h1>СООБЩИТЕ ЭТО АДМИНУ: DBAL\\OldDbChangeSet::db_changeset_prepare_unit() - USER[id] пустой</h1>');\n      pdump($user);\n      pdump(debug_backtrace());\n      die('USER[id] пустой');\n    }\n    $planet_id = is_array($planet_id) && isset($planet_id['id']) ? $planet_id['id'] : $planet_id;\n\n    $unit_location = sys_get_unit_location($user, array(), $unit_id);\n    $location_id = $unit_location == LOC_USER ? $user['id'] : $planet_id;\n    $location_id = $location_id ?: 'NULL';\n\n    $temp = DBStaticUnit::db_unit_by_location($user['id'], $unit_location, $location_id, $unit_id);\n    if (!empty($temp['unit_id'])) {\n      $db_changeset = array(\n        'action'  => SQL_OP_UPDATE,\n        P_VERSION => 1,\n        'where'   => array(\n          \"unit_id\" => $temp['unit_id'],\n        ),\n        'fields'  => array(\n          'unit_level' => array(\n            'delta' => $unit_value\n          ),\n        ),\n      );\n    } else {\n      $db_changeset = array(\n        'action' => SQL_OP_INSERT,\n        'fields' => array(\n          'unit_player_id'     => array(\n            'set' => $user['id'],\n          ),\n          'unit_location_type' => array(\n            'set' => $unit_location,\n          ),\n          'unit_location_id'   => array(\n            'set' => $unit_location == LOC_USER ? $user['id'] : $planet_id,\n          ),\n          'unit_type'          => array(\n            'set' => get_unit_param($unit_id, P_UNIT_TYPE),\n          ),\n          'unit_snid'          => array(\n            'set' => $unit_id,\n          ),\n          'unit_level'         => array(\n            'set' => $unit_value,\n          ),\n        ),\n      );\n    }\n\n    return $db_changeset;\n  }\n\n  public static function db_changeset_condition_compile(&$conditions, &$table_name = '') {\n    if (!$conditions[P_LOCATION] || $conditions[P_LOCATION] == LOC_NONE) {\n      $conditions[P_LOCATION] = LOC_NONE;\n      switch ($table_name) {\n        case 'users':\n        case LOC_USER:\n          $conditions[P_TABLE_NAME] = $table_name = 'users';\n          $conditions[P_LOCATION] = LOC_USER;\n        break;\n\n        case 'planets':\n        case LOC_PLANET:\n          $conditions[P_TABLE_NAME] = $table_name = 'planets';\n          $conditions[P_LOCATION] = LOC_PLANET;\n        break;\n\n        case 'unit':\n        case LOC_UNIT:\n          $conditions[P_TABLE_NAME] = $table_name = 'unit';\n          $conditions[P_LOCATION] = LOC_UNIT;\n        break;\n      }\n    }\n\n    $conditions[P_FIELDS_STR] = '';\n    if ($conditions['fields']) {\n      $fields = array();\n      foreach ($conditions['fields'] as $field_name => $field_data) {\n        $condition = \"`{$field_name}` = \";\n        $value = '';\n        if ($field_data['delta']) {\n          $value = \"`{$field_name}`\" . ($field_data['delta'] >= 0 ? '+' : '') . $field_data['delta'];\n        } elseif ($field_data['set']) {\n          $value = (is_string($field_data['set']) ? \"'{$field_data['set']}'\" : $field_data['set']);\n        }\n\n        if ($value) {\n          $fields[] = $condition . $value;\n        }\n      }\n      $conditions[P_FIELDS_STR] = implode(',', $fields);\n    }\n\n    $conditions[P_WHERE_STR] = '';\n    if (!empty($conditions['where'])) {\n      if ($conditions[P_VERSION] == 1) {\n        $the_conditions = array();\n        foreach ($conditions['where'] as $field_id => $field_value) {\n          // Простое условие - $field_id = $field_value\n          if (is_string($field_id)) {\n            $field_value =\n              $field_value === null ? 'NULL' :\n                (is_string($field_value) ? \"'\" . SN::$db->db_escape($field_value) . \"'\" :\n                  (is_bool($field_value) ? intval($field_value) : $field_value));\n            $the_conditions[] = \"`{$field_id}` = {$field_value}\";\n          } else {\n            die('Неподдерживаемый тип условия');\n          }\n        }\n      } else {\n        $the_conditions = &$conditions['where'];\n      }\n      $conditions[P_WHERE_STR] = implode(' AND ', $the_conditions);\n    }\n\n    switch ($conditions['action']) {\n      case SQL_OP_DELETE:\n        $conditions[P_ACTION_STR] = (\"DELETE FROM {{{$table_name}}}\");\n      break;\n      case SQL_OP_UPDATE:\n        $conditions[P_ACTION_STR] = (\"UPDATE {{{$table_name}}} SET\");\n      break;\n      case SQL_OP_INSERT:\n        $conditions[P_ACTION_STR] = (\"INSERT INTO {{{$table_name}}} SET\");\n      break;\n      // case SQL_OP_REPLACE: $result = doquery(\"REPLACE INTO {{{$table_name}}} SET {$fields}\") && $result; break;\n      default:\n        die('Неподдерживаемая операция в DBAL\\OldDbChangeSet::db_changeset_condition_compile');\n    }\n\n    $conditions[P_QUERY_STR] = $conditions[P_ACTION_STR] . ' ' . $conditions[P_FIELDS_STR] . (' WHERE ' . $conditions[P_WHERE_STR]);\n  }\n\n  /**\n   * @param $db_changeset\n   *\n   * @return bool\n   * @deprecated\n   */\n  public static function db_changeset_apply($db_changeset) {\n    $result = true;\n    if (!is_array($db_changeset) || empty($db_changeset)) {\n      return $result;\n    }\n\n    foreach ($db_changeset as $table_name => &$table_data) {\n      foreach ($table_data as $record_id => &$conditions) {\n        OldDbChangeSet::db_changeset_condition_compile($conditions, $table_name);\n\n        if ($conditions['action'] != SQL_OP_DELETE && !$conditions[P_FIELDS_STR]) {\n          continue;\n        }\n        if ($conditions['action'] == SQL_OP_DELETE && !$conditions[P_WHERE_STR]) {\n          continue;\n        } // Защита от случайного удаления всех данных в таблице\n\n        if ($conditions[P_LOCATION] != LOC_NONE) {\n          switch ($conditions['action']) {\n            case SQL_OP_DELETE:\n              $result = SN::db_del_record_list($conditions[P_LOCATION], $conditions[P_WHERE_STR]) && $result;\n            break;\n            case SQL_OP_UPDATE:\n              $result = SN::db_upd_record_list($conditions[P_LOCATION], $conditions[P_WHERE_STR], $conditions[P_FIELDS_STR]) && $result;\n            break;\n            case SQL_OP_INSERT:\n              $result = SN::db_ins_record($conditions[P_LOCATION], $conditions[P_FIELDS_STR]) && $result;\n            break;\n            default:\n              die('Неподдерживаемая операция в SN::db_changeset_apply');\n          }\n        } else {\n          $result = doquery($conditions[P_QUERY_STR]) && $result;\n        }\n      }\n    }\n\n    return $result;\n  }\n\n}\n"
  },
  {
    "path": "classes/DBAL/PropertyDescription.php",
    "content": "<?php\n/**\n * Created by Gorlum 13.11.2018 15:50\n */\n\nnamespace DBAL;\n\n/**\n * Class PropertyDescription\n *\n * @package DBAL\n */\nclass PropertyDescription {\n  const SQL_DATE_TIME = 'Y-m-d H:i:s';\n\n  /**\n   * Field name\n   *\n   * @var string $field\n   */\n  public $field = '';\n  /**\n   * Property name\n   *\n   * @var string $name\n   */\n  public $name = '';\n\n  /**\n   * Property type\n   *\n   * @var string $type\n   */\n  public $type = '';\n  public $default = null;\n  /**\n   * Is value in this field is mandatory\n   *\n   * @var bool $mandatory\n   */\n  public $mandatory = false;\n\n  /**\n   * Callback to convert from field to property\n   *\n   * function (mixed $value) : mixed;\n   *\n   * @var callable $toProperty\n   */\n  public $toProperty = null;\n  /**\n   * Callback to convert from property to field\n   *\n   * function (mixed $value) : mixed;\n   *\n   * @var callable $fromProperty\n   */\n  public $fromProperty = null;\n\n  /**\n   * Callback to convert from user input to property\n   *\n   * If no $description specified - some defaults are applied\n   *\n   * function (mixed $value, PropertyDescription $description = null) : mixed;\n   *\n   * @var callable $fromUser\n   */\n  public $fromUser = null;\n\n//  /**\n//   * function (string $name, mixed $value) : [string $ptlName, mixed $ptlValue];\n//   *\n//   * @var callable $toPtl\n//   * @deprecated\n//   */\n//  public $toPtl = null;\n\n  /**\n   * Callback to add extra information/convert data from property for PTL\n   *\n   * function (string $name, mixed $value, array $ptlResult) : array $ptlResult;\n   *\n   * @var callable $toPtl\n   */\n  public $toPtl = null;\n\n  /**\n   * @param string $propertyName\n   *\n   * @return $this\n   */\n  public function setName($propertyName) {\n    $this->name = $propertyName;\n\n    return $this;\n  }\n\n  /**\n   * @param DbFieldDescription $fieldDescription\n   */\n  public function fromDbFieldDescription(DbFieldDescription $fieldDescription) {\n    $this->field = $fieldDescription->Field;\n    $this->setName($fieldDescription->Field);\n\n    $nonMandatory = false;\n\n    switch (true) {\n      case strpos($fieldDescription->Type, 'int') === 0:\n      case strpos($fieldDescription->Type, 'tinyint') === 0:\n        $this->type       = 'integer';\n        $this->default    = !empty($fieldDescription->Default) ? intval($fieldDescription->Default) : 0;\n        $this->toProperty = [self::class, 'toInteger'];\n        $this->fromUser   = [self::class, 'intFromUser'];\n      break;\n\n      /** @noinspection PhpMissingBreakStatementInspection */\n      case strpos($fieldDescription->Type, 'mediumtext') === 0:\n        $nonMandatory = true;\n      case strpos($fieldDescription->Type, 'varchar') === 0:\n        $this->type    = 'string';\n        $this->default = !empty($fieldDescription->Default) || $fieldDescription->Default === null\n          ? $fieldDescription->Default : '';\n      break;\n\n      case strpos($fieldDescription->Type, 'datetime') === 0:\n        $this->type = 'datetime';\n//        $this->default      = !empty($fieldDescription->Default) || $fieldDescription->Default === null\n//          ? $fieldDescription->Default : '0000-00-00 00:00:00';\n        // TODO - current timestamp ????\n        if ($fieldDescription->Default === null) {\n          $this->default = null;\n        } elseif (!empty($fieldDescription->Default)) {\n          $this->default = strtotime($fieldDescription->Default);\n        } else {\n          $this->default = 0;\n        }\n        $this->toProperty   = [self::class, 'toUnixTime'];\n        $this->fromProperty = [self::class, 'fromUnixTime'];\n        $this->fromUser     = [self::class, 'datetimeFromUser'];\n//        $this->toPtl        = [self::class, 'ptlUnixTime'];\n      break;\n\n      default:\n        die(\"Unsupported field type '{$fieldDescription->Type}' in \" . get_called_class());\n      break;\n    }\n\n    // Main index is non mandatory\n    if ($fieldDescription->Extra === 'auto_increment' && $fieldDescription->Key === 'PRI') {\n      $nonMandatory = true;\n    }\n\n\n    // If this field is not null and default value is not set - then this field is mandatory\n    if ($fieldDescription->Null === 'NO' && $fieldDescription->Default === null && !$nonMandatory) {\n      $this->mandatory = true;\n    }\n\n  }\n\n  /**\n   * @param string|null $value\n   *\n   * @return int\n   */\n  public static function toInteger($value) {\n    return $value === null ? null : intval($value);\n  }\n  public static function intFromUser($value, PropertyDescription $description = null) {\n    if ($value === null) {\n      $value = $description->default;\n    } else {\n      $value = intval($value);\n    }\n\n    return $value;\n  }\n\n  public static function toUnixTime($value) {\n    return $value !== null ? strtotime($value) : $value;\n  }\n\n  public static function fromUnixTime($value) {\n    return $value !== null ? date(self::SQL_DATE_TIME, $value) : $value;\n  }\n\n  /**\n   * Converts input from user to datetime (unix timestamp internally)\n   *\n   * @param mixed                    $value\n   * @param PropertyDescription|null $description\n   *\n   * @return mixed\n   */\n  public static function datetimeFromUser($value, PropertyDescription $description = null) {\n    $value = strtotime($value);\n    if ($value === null) {\n      $value = $description->default;\n    }\n\n    return $value;\n  }\n\n  public static function jsonDecode($value) {\n    $value = json_decode($value, true);\n\n    return $value;\n  }\n\n  public static function jsonEncode($value) {\n    $value = json_encode($value);\n\n    return $value;\n  }\n\n//  /**\n//   * @param string $name\n//   * @param mixed  $value\n//   *\n//   * @return array\n//   */\n//  public static function ptlUnixTime($name, $value) {\n//    return [$name . '_STRING', empty($value) ? '' : date(self::SQL_DATE_TIME, $value)];\n//  }\n\n}\n"
  },
  {
    "path": "classes/DBAL/RecordV2.php",
    "content": "<?php\n/**\n * Created by Gorlum 13.11.2018 14:07\n */\n\nnamespace DBAL;\n\n\nuse Common\\AccessLoggedTranslatedV2;\nuse Core\\GlobalContainer;\nuse Exception;\nuse SN;\n\n/**\n * RecordV2 interface for access Storage data\n *\n * Classes starting from this one and above inheritance tree made for translation DB field names to properties with all bells and whistles\n *\n * Class itself represents single table where derived object represents single record\n * Object  handles insert/delete/update with single ::save() function based on it's internal state\n *\n *\n * @property int|string $id\n *\n * @package DBAL\n */\nclass RecordV2 extends AccessLoggedTranslatedV2 {\n  const _INDEX_FIELD = 'id';\n  const _PREFIX1 = 'Record';\n  const _PREFIX2 = 'RecordV2';\n\n  /**\n   * @var StorageSqlV2|null $storage\n   */\n  // TODO - replace with IStorage\n  protected static $storage = 0;\n\n  protected static $_tableName = '';\n  protected static $_indexField = self::_INDEX_FIELD;\n\n\n  protected $isNew = true;\n  protected $toDelete = false;\n  protected $deleted = false;\n\n  public static function tableName() {\n    if (empty(static::$_tableName)) {\n      if (strpos(static::class, self::_PREFIX1) !== 0) {\n        static::$_tableName = substr(static::class, strlen(self::_PREFIX1));\n      } elseif (strpos(static::class, self::_PREFIX2) !== 0) {\n        static::$_tableName = substr(static::class, strlen(self::_PREFIX2));\n      } else {\n        static::$_tableName = static::class;\n      }\n    }\n\n    return static::$_tableName;\n  }\n\n  public static function indexField() {\n    return static::$_indexField;\n  }\n\n  public function __construct(GlobalContainer $services = null) {\n    parent::__construct($services);\n\n    static::setStorage($services->storageSqlV2);\n\n    if (empty(static::$_properties)) {\n      $fields = static::$storage->fields(static::tableName());\n\n      $this->importFieldDefinitions($fields);\n    }\n  }\n\n  /**\n   * @param StorageSqlV2 $storage\n   */\n  public static function setStorage(StorageSqlV2 $storage) {\n    if (static::$storage === 0) {\n      die('CRITICAL! No \"static::$storage\" property declared in \"' . get_called_class() . '\" class! Report to developer!');\n    } elseif (empty(static::$storage)) {\n      static::$storage = $storage;\n    }\n  }\n\n  /**\n   * @return StorageSqlV2|null\n   */\n  public static function getStorage() {\n    return static::$storage;\n  }\n\n\n  /**\n   * Handles saving object to storage\n   *\n   * @throws Exception\n   */\n  public function save() {\n    if ($this->toDelete) {\n      $this->delete();\n    } elseif ($this->isEmpty()) {\n      // Object is empty - no valuable info here\n      if ($this->isNew) {\n        /*\n        Do nothing\n       TODO - Exception ?\n       Basically it's throw-out instance of class which can be forgotten\n       May be it's was made for access to some non-static property. Or for something else - it's doesn't matters\n       What matters that this instance shouldn't (and can't) be stored in Storage\n        */\n      } else {\n        // Deleting empty object\n        $this->delete();\n      }\n    } else {\n      if ($this->isNew) {\n        $this->insert();\n      } else {\n        $this->update();\n      }\n    }\n  }\n\n  protected function delete() {\n    if (!empty($this->id)) {\n      static::$storage->delete(\n        static::tableName(),\n        [static::$_properties[self::_INDEX_FIELD]->field => $this->id]\n      );\n    } else {\n      $this->id = null;\n    }\n\n    $this->accept();\n\n    $this->toDelete = true;\n    $this->isNew    = true;\n  }\n\n  public function toFieldArray($useDefaults = false, $skipId = false) {\n    $array = parent::toFieldArray($useDefaults);\n    if ($skipId) {\n      unset($array[static::$_properties[self::_INDEX_FIELD]->field]);\n    }\n\n    return $array;\n  }\n\n  /**\n   * @return int|null|string\n   *\n   * @throws Exception\n   */\n  protected function insert() {\n\n    // Filling absent property as default\n    // TODO - add `mandatory` field that only them was filled with defaults\n//    foreach (static::$_properties as $name => $description) {\n//      if (!isset($this->$name)) {\n//        $this->$name = static::$_defaults[$name];\n//      }\n//    }\n\n    // Inserting records\n    $fieldValues = $this->toFieldArray(false, true);\n\n    $id = static::$storage->insert(static::tableName(), $fieldValues);\n    if (!empty($id)) {\n      $this->id = $id;\n      $this->accept();\n\n      $this->isNew = false;\n\n      $this->findById($id);\n    }\n\n    return $id;\n  }\n\n  protected function update() {\n    static::$storage->update(\n      static::tableName(),\n      [static::$_properties[self::_INDEX_FIELD]->field => $this->id],\n      $this->changesToFields($this->_changes),\n      $this->changesToFields($this->_deltas)\n    );\n\n    $this->accept();\n\n    $this->isNew = false;\n\n    $this->findById($this->id);\n  }\n\n  /**\n   * @param int|string $id\n   *\n   * @return $this\n   */\n  public function findById($id) {\n    $array = static::$storage->findFirst(static::tableName(), [static::$_properties[self::_INDEX_FIELD]->field => $id]);\n\n    $this->replaceFromFieldArray($array);\n\n    return $this;\n  }\n\n  protected function replaceFromFieldArray(array $array) {\n    $this->clear();\n    if (!empty($array)) {\n      $this->fromFieldArray($array);\n\n      $this->accept();\n\n      $this->isNew = false;\n    }\n  }\n\n  public function clear() {\n    parent::clear();\n\n    $this->isNew    = true;\n    $this->toDelete = false;\n  }\n\n  public function isDeleted() {\n    return $this->toDelete && $this->isNew;\n  }\n\n  /**\n   * Record is empty if one of the mandatory field(s) is empty\n   *\n   * @return bool\n   */\n  public function isEmpty() {\n    $isEmpty = false;\n\n    foreach (static::$_mandatory as $name => $cork) {\n      if (!isset($this->$name)) {\n        $isEmpty = true;\n        break;\n      }\n    }\n\n    return $isEmpty;\n  }\n\n\n  public function markDelete($mark = true) {\n    $this->toDelete = $mark;\n  }\n\n\n  /**\n   * @param $id\n   *\n   * @return null|static\n   */\n  public static function findByIdStatic($id) {\n    $record = new static(SN::$gc);\n    $record->findById($id);\n\n    return $record->isNew ? null : $record;\n  }\n\n\n  /**\n   * @param GlobalContainer $services\n   *\n   * @return $this[]\n   */\n  public static function findAllStatic(GlobalContainer $services) {\n    $result = [];\n\n    static::setStorage($services->storageSqlV2);\n\n    $iter = static::$storage->findIterator(static::tableName(), []);\n\n    foreach ($iter as $fields) {\n      $promo = new static($services);\n      $promo->fromFieldArray($fields);\n      $promo->isNew = false;\n      $result[]     = $promo;\n    }\n\n    return $result;\n  }\n\n  /**\n   * @return bool\n   */\n  public function isNew() {\n    return $this->isNew;\n  }\n}\n"
  },
  {
    "path": "classes/DBAL/RecordV3.php",
    "content": "<?php\n/** Created by Gorlum 23.10.2025 20:24 */\n\nnamespace DBAL;\n\nclass RecordV3 {\n\n  /**\n   * @param array|object $data\n   *\n   * @return static\n   */\n  public static function castTo($data) {\n    if (is_object($data)) {\n      $data = get_object_vars($data);\n    }\n\n    $object = new static();\n    foreach ($data as $key => $value) {\n      if (property_exists($object, $key)) {\n        $object->$key = $value;\n      }\n    }\n\n    return $object;\n  }\n\n}"
  },
  {
    "path": "classes/DBAL/Schema.php",
    "content": "<?php\n/**\n * Created by Gorlum 12.06.2017 15:29\n */\n\nnamespace DBAL;\n\n\nclass Schema {\n  /**\n   * @var db_mysql $db\n   */\n  protected $db;\n  /**\n   * List of all table names\n   *\n   * @var string[] $tablesAll\n   */\n  protected $tablesAll = null;\n  /**\n   * @var string[] $tablesSn\n   */\n  protected $tablesSn = null;\n  /**\n   * @var TableSchema[] $tableSchemas\n   */\n  protected $tableSchemas = [];\n\n  public function __construct(db_mysql $db) {\n    $this->db = $db;\n  }\n\n  public function getDb() {\n    return $this->db;\n  }\n\n  public function clear() {\n    $this->tablesAll    = null;\n    $this->tablesSn     = null;\n    $this->tableSchemas = [];\n  }\n\n  protected function loadTableNamesFromDb() {\n    $this->clear();\n    $this->tablesAll = array();\n    $this->tablesSn  = array();\n\n    $query = $this->db->mysql_get_table_list();\n\n    $prefix_length = strlen($this->db->db_prefix);\n\n    while ($row = $this->db->db_fetch($query)) {\n      foreach ($row as $table_name) {\n        $this->tablesAll[$table_name] = $table_name;\n\n        if (strpos($table_name, $this->db->db_prefix) === 0) {\n          $table_name_sn = substr($table_name, $prefix_length);\n\n          $this->tablesSn[$table_name_sn] = $table_name_sn;\n        }\n      }\n    }\n  }\n\n  /**\n   * Get names of all tables in this DB\n   *\n   * @return string[]\n   */\n  public function getAllTables() {\n    if (!isset($this->tablesAll)) {\n      $this->loadTableNamesFromDb();\n    }\n\n    return $this->tablesAll;\n  }\n\n  /**\n   * Get un-prefixed table names potentially used by game\n   *\n   * @return string[]\n   */\n  public function getSnTables() {\n    if (!isset($this->tablesSn)) {\n      $this->loadTableNamesFromDb();\n    }\n\n    return $this->tablesSn;\n  }\n\n  /**\n   * Checks if SN table exists\n   *\n   * @param $tableName\n   *\n   * @return bool\n   */\n  public function isSnTableExists($tableName) {\n    return isset($this->getSnTables()[$tableName]);\n  }\n\n  /**\n   * @param string $tableName\n   *\n   * @return TableSchema\n   */\n  public function getTableSchema($tableName) {\n    if (empty($this->tableSchemas[$tableName])) {\n      $this->tableSchemas[$tableName] = new TableSchema($tableName, $this);\n    }\n\n    return $this->tableSchemas[$tableName];\n  }\n\n  /**\n   * @param $table\n   * @param $index\n   *\n   * @return bool\n   */\n  public function isIndexExists($table, $index) {\n    return $this->isSnTableExists($table) && $this->getTableSchema($table)->isIndexExists($index);\n  }\n\n  public function isFieldExists($table, $field) {\n    return $this->isSnTableExists($table) && $this->getTableSchema($table)->isFieldExists($field);\n  }\n\n  public function isConstrainExists($table, $constrain) {\n    return $this->isSnTableExists($table) && $this->getTableSchema($table)->isConstrainExists($constrain);\n  }\n\n}\n"
  },
  {
    "path": "classes/DBAL/StorageSqlV2.php",
    "content": "<?php\n/**\n * Created by Gorlum 13.11.2018 14:08\n */\n\nnamespace DBAL;\n\n\nuse Core\\GlobalContainer;\nuse SN;\n\nclass StorageSqlV2 {\n  /**\n   * @var null|db_mysql $db\n   */\n  protected static $db = null;\n\n  protected $forUpdate = false;\n\n  public function __construct(GlobalContainer $services = null) {\n    static::$db = !empty($services) ? $services->db : SN::$gc->db;\n  }\n\n  /**\n   * @return $this\n   */\n  public function forUpdate() {\n    $this->forUpdate = true;\n\n    return $this;\n  }\n\n//  /**\n//   * @param $tableName\n//   * @param $conditions\n//   *\n//   * @return array|null\n//   */\n//  public function findFirstDbq($tableName, $conditions) {\n//    $dbq = new DbQuery(static::$db);\n//    $dbq\n//      ->setTable($tableName)\n//      ->setOneRow()\n//      ->setWhereArray($conditions);\n//\n//    $array = static::$db->dbqSelectAndFetch($dbq);\n//\n//    return $array;\n//  }\n\n  /**\n   * @param $tableName\n   * @param $conditions\n   *\n   * @return DbMysqliResultIterator\n   */\n  public function findIterator($tableName, $conditions) {\n    $dbq = new DbQuery(static::$db);\n    $dbq\n      ->setTable($tableName)\n      ->setWhereArray($conditions);\n\n    if ($this->forUpdate) {\n      $dbq->setForUpdate();\n    }\n\n    return static::$db->selectIterator($dbq->select());\n  }\n\n  /**\n   * @param $tableName\n   * @param $conditions\n   *\n   * @return array|null\n   */\n  public function findFirst($tableName, $conditions) {\n    $iterator = $this->findIterator($tableName, $conditions);\n\n    $iterator->rewind();\n\n    return $iterator->valid() ? $iterator->current() : [];\n  }\n\n  /**\n   * @param $tableName\n   * @param $conditions\n   *\n   * @return array\n   */\n  public function findAll($tableName, $conditions) {\n    $result = [];\n\n    foreach ($this->findIterator($tableName, $conditions) as $record) {\n      $result[] = $record;\n    }\n\n    return $result;\n  }\n\n//  /**\n//   * @param IRecordIndexed $record\n//   * @param                $index\n//   *\n//   * @return IRecordIndexed\n//   */\n//  public function findById(IRecordIndexed $record, $index, $tableName, $conditions) {\n//    // TODO: Remake it for Storage not to know how record implemented\n//\n//    $dbq = new DbQuery(static::$db);\n//    $dbq\n//      ->setTable($tableName)\n//      ->setOneRow()\n//      ->setWhereArray([$record::indexField() => $index]);\n//\n//    $array = static::$db->dbqSelectAndFetch($dbq);\n//    if (!empty($array)) {\n//\n//    }\n//\n//    return $record;\n//  }\n\n  /**\n   * @param string $tableName\n   *\n   * @return DbFieldDescription[]\n   */\n  public function fields($tableName) {\n    return static::$db->schema()->getTableSchema($tableName)->fields;\n  }\n\n\n  /**\n   * @param string $tableName\n   * @param array  $fieldList\n   *\n   * @return int|null|string\n   */\n  public function insert($tableName, array $fieldList) {\n    $dbq = new DbQuery(static::$db);\n    $dbq\n      ->setTable($tableName)\n      ->setValues($fieldList);\n\n    $result = static::$db->doquery($dbq->insert(DbQuery::DB_INSERT_PLAIN, true));\n\n    return $result ? static::$db->db_insert_id() : null;\n  }\n\n  /**\n   * @param string $tableName\n   * @param array  $conditions\n   *\n   * @return bool|null\n   */\n  public function delete($tableName, $conditions) {\n    $dbq = new DbQuery(static::$db);\n    $dbq\n      ->setTable($tableName)\n      ->setWhereArray($conditions);\n\n    return $dbq->doDeleteDb();\n  }\n\n  public function update($tableName, $conditions, $changes, $deltas) {\n    $dbq = new DbQuery(static::$db);\n    $dbq\n      ->setTable($tableName)\n      ->setValues($changes)\n      ->setAdjust($deltas)\n      ->setWhereArray($conditions);\n\n    return $dbq->doUpdate();\n  }\n\n}\n"
  },
  {
    "path": "classes/DBAL/TableSchema.php",
    "content": "<?php\n/**\n * Created by Gorlum 19.06.2017 15:21\n */\n\nnamespace DBAL;\n\n\nclass TableSchema {\n\n  /**\n   * @var \\DBAL\\db_mysql $db\n   */\n  protected $db;\n\n  public $tableName = '';\n\n  /**\n   * @var DbFieldDescription[] $fields\n   */\n  public $fields = [];\n  /**\n   * @var DbIndexDescription[] $indexesObject\n   */\n  public $indexes = [];\n  /**\n   * @var \\array[] $constraints\n   */\n  public $constraints = [];\n\n  /**\n   * TableSchema constructor.\n   *\n   * @param string $tableName\n   * @param Schema $dbSchema\n   */\n  public function __construct($tableName, Schema $dbSchema) {\n    $this->db = $dbSchema->getDb();\n\n    $this->tableName = $tableName;\n\n    $this->fields      = $this->db->mysql_get_fields($this->tableName);\n    $this->indexes     = $this->db->mysql_get_indexes($this->tableName);\n    $this->constraints = $this->db->mysql_get_constraints($this->tableName);\n  }\n\n  public function isFieldExists($field) {\n    return isset($this->fields[$field]);\n  }\n\n  public function isIndexExists($index) {\n    return isset($this->indexes[$index]);\n  }\n\n  public function isConstrainExists($index) {\n    return isset($this->constraints[$index]);\n  }\n\n}\n"
  },
  {
    "path": "classes/DBAL/db_mysql.php",
    "content": "<?php /** @noinspection PhpRedundantOptionalArgumentInspection */\n\n/** @noinspection PhpDeprecationInspection */\n/** @noinspection PhpUnnecessaryCurlyVarSyntaxInspection */\n\nnamespace DBAL;\n\nuse classConfig;\nuse Core\\GlobalContainer;\nuse debug;\nuse mysqli;\nuse mysqli_result;\nuse SN;\nuse Unit\\DBStaticUnit;\n\n/**\n * User: Gorlum\n * Date: 01.09.2015\n * Time: 15:58\n */\nclass db_mysql {\n  const TRANSACTION_LEVEL_SERIALIZABLE = 'SERIALIZABLE';\n  const TRANSACTION_LEVEL_REPEATABLE_READ = 'REPEATABLE READ';\n  const TRANSACTION_LEVEL_READ_COMMITTED = 'READ COMMITTED';\n  const TRANSACTION_LEVEL_READ_UNCOMMITTED = 'READ UNCOMMITTED';\n\n  const TRANSACTION_LEVELS_ALLOWED = [\n    self::TRANSACTION_LEVEL_SERIALIZABLE,\n    self::TRANSACTION_LEVEL_REPEATABLE_READ,\n    self::TRANSACTION_LEVEL_READ_COMMITTED,\n    self::TRANSACTION_LEVEL_READ_UNCOMMITTED,\n  ];\n  const DB_TRANSACTION_WHATEVER = false;\n  const DB_TRANSACTION_SHOULD_BE = true;\n  const DB_TRANSACTION_SHOULD_NOT_BE = null;\n  public static $transaction_id = 0;\n  public static $db_in_transaction = false;\n  public static $transactionDepth = 0;\n\n  const TABLE_USERS = 'users';\n\n  const TABLE_ID_FIELD = [\n    'fleets' => 'fleet_id',\n  ];\n\n  // Order in which tables should be locked\n  const TABLE_LOCK_ORDER = [\n    'users',\n    'planets',\n    'que',\n    'unit',\n    'fleets',\n  ];\n\n  /**\n   * DB schemes\n   *\n   * @var Schema|null $schema\n   */\n  protected static $schema = null;\n\n  /**\n   * Статус соединения с MySQL\n   *\n   * @var bool\n   */\n  public $connected = false;\n  /**\n   * Префикс названий таблиц в БД\n   *\n   * @var string\n   */\n  public $db_prefix = '';\n  public $dbName = '';\n  /**\n   * Настройки БД\n   *\n   * @var array\n   */\n  protected $dbsettings = [];\n//  /**\n//   * Драйвер для прямого обращения к MySQL\n//   *\n//   * @var db_mysql_v5 $driver\n//   */\n//  public $driver = null;\n\n  /**\n   * Общее время запросов\n   *\n   * @var float $time_mysql_total\n   */\n  public $time_mysql_total = 0.0;\n\n  /**\n   * @var bool $inTransaction\n   */\n  protected $inTransaction = false;\n\n\n  /**\n   * Соединение с MySQL\n   *\n   * @var mysqli $link\n   */\n  public $link;\n\n\n  /**\n   * DBAL\\db_mysql constructor.\n   *\n   * @param GlobalContainer $gc\n   */\n  public function __construct($gc) {\n//    $this->transaction = new \\DBAL\\DbTransaction($gc, $this);\n//    $this->snCache = new $gc->snCacheClass($gc, $this);\n//    $this->operator = new DbRowDirectOperator($this);\n  }\n\n  /**\n   * @return Schema\n   */\n  public function schema() {\n    if (!isset(self::$schema)) {\n      self::$schema = new Schema($this);\n    }\n\n    return self::$schema;\n  }\n\n  public function load_db_settings() {\n    $dbsettings = array();\n\n    require(SN_CONFIG_PATH);\n\n    $this->setDbSettings($dbsettings);\n  }\n\n  public function sn_db_connect($external_db_settings = null) {\n    $this->db_disconnect();\n\n    if (!empty($external_db_settings) && is_array($external_db_settings)) {\n      $this->setDbSettings($external_db_settings);\n    }\n\n    if (empty($this->dbsettings)) {\n      $this->load_db_settings();\n    }\n\n    // TODO - фатальные (?) ошибки на каждом шагу\n    try {\n    if (!empty($this->dbsettings)) {\n      // $driver_name = 'DBAL\\\\' . (empty($this->dbsettings['sn_driver']) ? 'db_mysql_v5' : $this->dbsettings['sn_driver']);\n      // $this->driver = new $driver_name();\n      $this->db_prefix = $this->dbsettings['prefix'];\n\n      $this->connected = $this->connected || $this->mysql_connect_driver($this->dbsettings);\n\n      if ($this->connected && empty($this->schema()->getSnTables())) {\n        die('DB error - cannot find any table. Halting...');\n      }\n\n      $this->doQueryFast('SET SESSION TRANSACTION ISOLATION LEVEL ' . self::TRANSACTION_LEVEL_SERIALIZABLE);\n    } else {\n      $this->connected = false;\n    }\n    } catch (\\Exception $e) {\n      $this->connected = false;\n\n      if($e->getCode() == 1044) {\n        die('Access denied for specified DB user to specified database. Check DB settings in game and user privileges on DB server');\n      } else {\n        die('Error happens while connecting to DB. Check DB settings in game and DB accessibility');\n      }\n    }\n\n    return $this->connected;\n  }\n\n  public function mysql_connect_driver($settings) {\n    global $debug;\n\n    static $need_keys = array('server', 'user', 'pass', 'name', 'prefix');\n\n    if ($this->connected) {\n      return true;\n    }\n\n    if (empty($settings) || !is_array($settings) || array_intersect($need_keys, array_keys($settings)) != $need_keys) {\n      $debug->error_fatal('There is miss-configuration in your config.php. Check it again');\n    }\n\n    @$this->link = mysqli_connect($settings['server'], $settings['user'], $settings['pass'], $settings['name']);\n    if (!is_object($this->link) || $this->link->connect_error) {\n      $debug->error_fatal('DB Error - cannot connect to server error #' . $this->link->connect_errno, $this->link->connect_error);\n    }\n\n    $this->db_sql_query(\"/*!40101 SET NAMES 'utf8' */\")\n    or $debug->error_fatal('DB error - cannot set names 1 error #' . $this->link->errno, $this->link->error);\n    $this->db_sql_query(\"SET NAMES 'utf8';\")\n    or $debug->error_fatal('DB error - cannot set names 2 error #' . $this->link->errno, $this->link->error);\n\n    //mysql_select_db($settings['name']) or $debug->error_fatal('DB error - cannot find DB on server', $this->mysql_error());\n    $this->db_sql_query('SET SESSION TRANSACTION ISOLATION LEVEL ' . self::TRANSACTION_LEVEL_SERIALIZABLE . ';')\n    or $debug->error_fatal('DB error - cannot set desired isolation level error #' . $this->link->errno, $this->link->error);\n\n    $this->connected = true;\n\n    return true;\n  }\n\n  public function db_disconnect() {\n    if ($this->connected) {\n      /** @noinspection PhpFieldImmediatelyRewrittenInspection */\n      $this->connected = !$this->driver_disconnect();\n      $this->connected = false;\n    }\n\n    return !$this->connected;\n  }\n\n  /**\n   * @param int    $errno\n   * @param string $errStr\n   * @param string $errFile\n   * @param int    $errLine\n   * @param array  $errContext\n   *\n   * @noinspection PhpUnusedParameterInspection\n   */\n  public function handlerQueryWarning($errno, $errStr, $errFile, $errLine, $errContext) {\n    static $alreadyHandled;\n\n    // Error was suppressed with the @-operator\n    if (0 === error_reporting()) {\n      return false;\n    }\n\n    if (!$alreadyHandled) {\n      print(SN_TIME_SQL . '<br />Server is busy. Please try again in several minutes...<br />Сервер занят. Попробуйте снова через несколько минут...<br />Server zanyat. Poprobujte snova cherez neskolko minut...');\n      $alreadyHandled = true;\n    }\n\n    return true;\n  }\n\n  public function prefixReplace($sql) {\n    if (strpos($sql, '{{') !== false) {\n      foreach ($this->schema()->getSnTables() as $tableName) {\n        $sql = str_replace(\"{{{$tableName}}}\", $this->db_prefix . $tableName, $sql);\n      }\n    }\n\n    return $sql;\n  }\n\n  /** @noinspection SpellCheckingInspection */\n  public function doquery($query, $fetch = false, $skip_query_check = false) {\n    /**\n     * @var debug       $debug\n     * @var classConfig $config\n     */\n    global $numqueries, $debug, $config;\n\n    if (!$this->connected) {\n      $this->sn_db_connect();\n    }\n\n    $query = trim($query);\n    $this->security_watch_user_queries($query);\n    $skip_query_check or $this->security_query_check_bad_words($query);\n\n    $sql = $this->prefixReplace($query);\n\n    if ($config->debug) {\n      $numqueries++;\n      $arr   = debug_backtrace();\n      $array = explode('/', $arr[0]['file']);\n      $file  = end($array);\n      $line  = $arr[0]['line'];\n      $debug->add(\"<tr><th>Query $numqueries: </th><th>$query</th><th>$file($line)</th><th>&nbsp;</th><th>$fetch</th></tr>\");\n    }\n\n    if (defined('DEBUG_SQL_COMMENT')) {\n      $sql = $debug->comment_query(debug_backtrace(), $sql);\n    }\n\n    set_error_handler([$this, 'handlerQueryWarning']);\n    $sqlquery = $this->db_sql_query($sql);\n    if (!$sqlquery) {\n      $debug->error(SN::$db->db_error() . \"\\n$sql\\n\", 'SQL Error');\n    }\n    restore_error_handler();\n\n    return $fetch ? $this->db_fetch($sqlquery) : $sqlquery;\n  }\n\n  /**\n   * Get all records for a query as array of arrays or objects\n   *\n   * @param string $query          SQL query to execute\n   * @param bool   $skipQueryCheck Should query skip security check\n   * @param bool   $asArray        Should records be returned as array? If false - records would be returned as StdObject\n   *\n   * @return array\n   */\n  public function dbGetAll($query, $skipQueryCheck = false, $asArray = true) {\n    $queryResult = $this->doquery($query, false, $skipQueryCheck);\n\n    $result = [];\n    while ($row = $this->db_fetch($queryResult)) {\n      $result[] = $asArray ? $row : (object)$row;\n    }\n\n    return $result;\n  }\n\n  public function doQueryAndFetch($query) {\n    return $this->doquery($query, true);\n  }\n\n  public function doQueryFast($query, $fetch = false) {\n    $sql = $this->prefixReplace($query);\n\n    set_error_handler([$this, 'handlerQueryWarning']);\n    $sqlQuery = $this->db_sql_query($sql) or SN::$debug->error(SN::$db->db_error() . \"<br />$sql<br />\", 'SQL Error');\n    restore_error_handler();\n\n    return $fetch ? $this->db_fetch($sqlQuery) : $sqlQuery;\n  }\n\n  /**\n   * @param string $query\n   * @param bool   $skip_query_check\n   *\n   * @return DbMysqliResultIterator\n   */\n  public function selectIterator($query, $skip_query_check = false) {\n    return new DbMysqliResultIterator($this->doquery($query, false, $skip_query_check));\n  }\n\n  /**\n   * @param string $query\n   * @param bool   $skip_query_check\n   *\n   * @return int|null\n   */\n  public function selectValue($query, $skip_query_check = false) {\n    $row = $this->doquery($query, true, $skip_query_check);\n\n    return !empty($row) ? intval(reset($row)) : null;\n  }\n\n  /**\n   * @param DbQuery $dbQuery\n   *\n   * @return array|null\n   */\n  public function dbqSelectAndFetch(DbQuery $dbQuery) {\n    return $this->doQueryAndFetch($dbQuery->select());\n  }\n\n\n  public function security_watch_user_queries($query) {\n    // TODO Заменить это на новый логгер\n    global $config, $is_watching, $user, $debug;\n\n    if (!$is_watching && $config->game_watchlist_array && in_array($user['id'], $config->game_watchlist_array)) {\n      if (!preg_match('/^(select|commit|rollback|start transaction)/i', $query)) {\n        $is_watching = true;\n        $msg         = \"\\$query = \\\"{$query}\\\"\\n\\r\";\n        if (!empty($_POST)) {\n          $msg .= \"\\n\\r\" . dump($_POST, '$_POST');\n        }\n        if (!empty($_GET)) {\n          $msg .= \"\\n\\r\" . dump($_GET, '$_GET');\n        }\n        $debug->warning($msg, \"Watching user {$user['id']}\", 399, array('base_dump' => true));\n        $is_watching = false;\n      }\n    }\n  }\n\n\n  /** @noinspection SpellCheckingInspection */\n  public function security_query_check_bad_words($query) {\n    global $user, $dm_change_legit, $mm_change_legit;\n\n    switch (true) {\n      case stripos($query, 'RUNCATE TABL') != false:\n      case stripos($query, 'ROP TABL') != false:\n      case stripos($query, 'ENAME TABL') != false:\n      case stripos($query, 'REATE DATABAS') != false:\n      case stripos($query, 'REATE TABL') != false:\n      case stripos($query, 'ET PASSWOR') != false:\n      case stripos($query, 'EOAD DAT') != false:\n      case stripos($query, 'RPG_POINTS') != false && stripos(trim($query), 'UPDATE ') === 0 && !$dm_change_legit:\n      case stripos($query, 'METAMATTER') != false && stripos(trim($query), 'UPDATE ') === 0 && !$mm_change_legit:\n      case stripos($query, 'AUTHLEVEL') != false && $user['authlevel'] < 3 && stripos($query, 'SELECT') !== 0:\n        $report = \"Hacking attempt (\" . date(\"d.m.Y H:i:s\") . \" - [\" . time() . \"]):\\n\";\n        $report .= \">Database Inforamation\\n\";\n        $report .= \"\\tID - \" . $user['id'] . \"\\n\";\n        $report .= \"\\tUser - \" . $user['username'] . \"\\n\";\n        $report .= \"\\tAuth level - \" . $user['authlevel'] . \"\\n\";\n        $report .= \"\\tAdmin Notes - \" . $user['adminNotes'] . \"\\n\";\n        $report .= \"\\tCurrent Planet - \" . $user['current_planet'] . \"\\n\";\n        $report .= \"\\tUser IP - \" . $user['user_lastip'] . \"\\n\";\n        $report .= \"\\tUser IP at Reg - \" . $user['ip_at_reg'] . \"\\n\";\n        $report .= \"\\tUser Agent- \" . $_SERVER['HTTP_USER_AGENT'] . \"\\n\";\n        $report .= \"\\tCurrent Page - \" . $user['current_page'] . \"\\n\";\n        $report .= \"\\tRegister Time - \" . $user['register_time'] . \"\\n\";\n        $report .= \"\\n\";\n\n        $report .= \">Query Information\\n\";\n        $report .= \"\\tQuery - \" . $query . \"\\n\";\n        $report .= \"\\n\";\n\n        $report .= \">\\$_SERVER Information\\n\";\n        $report .= \"\\tIP - \" . $_SERVER['REMOTE_ADDR'] . \"\\n\";\n        $report .= \"\\tHost Name - \" . $_SERVER['HTTP_HOST'] . \"\\n\";\n        $report .= \"\\tUser Agent - \" . $_SERVER['HTTP_USER_AGENT'] . \"\\n\";\n        $report .= \"\\tRequest Method - \" . $_SERVER['REQUEST_METHOD'] . \"\\n\";\n        $report .= \"\\tCame From - \" . $_SERVER['HTTP_REFERER'] . \"\\n\";\n        $report .= \"\\tPage is - \" . $_SERVER['SCRIPT_NAME'] . \"\\n\";\n        $report .= \"\\tUses Port - \" . $_SERVER['REMOTE_PORT'] . \"\\n\";\n        $report .= \"\\tServer Protocol - \" . $_SERVER['SERVER_PROTOCOL'] . \"\\n\";\n\n        $report .= \"\\n--------------------------------------------------------------------------------------------------\\n\";\n\n        $fp = fopen(SN_ROOT_PHYSICAL . 'badqrys.txt', 'a');\n        fwrite($fp, $report);\n        fclose($fp);\n\n        $message = 'Привет, я не знаю то, что Вы пробовали сделать, но команда, которую Вы только послали базе данных, не выглядела очень дружественной и она была заблокированна.<br /><br />Ваш IP, и другие данные переданны администрации сервера. Удачи!.';\n        die($message);\n        /** @noinspection PhpUnreachableStatementInspection */\n      break;\n    }\n  }\n\n  public function mysql_get_table_list() {\n    return $this->db_sql_query('SHOW TABLES;');\n  }\n\n  public function mysql_get_innodb_status() {\n    return $this->db_sql_query('SHOW ENGINE INNODB STATUS;');\n  }\n\n  /**\n   * @param string $tableName_unsafe\n   *\n   * @return array[]\n   */\n  public function mysql_get_fields($tableName_unsafe) {\n    $result = [];\n\n    $prefixedTableName_safe = $this->db_escape($this->db_prefix . $tableName_unsafe);\n    $q1                     = $this->db_sql_query(\"SHOW FULL COLUMNS FROM `{$prefixedTableName_safe}`;\");\n    while ($r1 = db_fetch($q1)) {\n      $dbf = new DbFieldDescription();\n      $dbf->fromMySqlDescription($r1);\n\n      $result[$r1['Field']] = $dbf;\n    }\n\n    return $result;\n  }\n\n  /**\n   * @param string $tableName_unsafe\n   *\n   * @return DbIndexDescription[]\n   */\n  public function mysql_get_indexes($tableName_unsafe) {\n    /**\n     * @var DbIndexDescription[] $result\n     */\n    $result = [];\n\n    $prefixedTableName_safe = $this->db_escape($this->db_prefix . $tableName_unsafe);\n    $q1                     = $this->db_sql_query(\"SHOW INDEX FROM {$prefixedTableName_safe};\");\n    while ($r1 = db_fetch($q1)) {\n      $indexName = $r1['Key_name'];\n      if (empty($result[$indexName])) {\n        $result[$indexName] = new DbIndexDescription();\n      }\n      $result[$indexName]->addField($r1);\n    }\n\n    return $result;\n  }\n\n  /**\n   * @param string $tableName_unsafe\n   *\n   * @return array[]\n   */\n  public function mysql_get_constraints($tableName_unsafe) {\n    $result = [];\n\n    $prefixedTableName_safe = $this->db_escape($this->db_prefix . $tableName_unsafe);\n\n    $q1 = $this->db_sql_query(\"SELECT * FROM `information_schema`.`KEY_COLUMN_USAGE` WHERE `TABLE_SCHEMA` = '\" . SN::$db->db_escape(SN::$db_name) . \"' AND `TABLE_NAME` = '{$prefixedTableName_safe}' AND `REFERENCED_TABLE_NAME` IS NOT NULL;\");\n    while ($r1 = db_fetch($q1)) {\n      $indexName = $r1['CONSTRAINT_NAME'];\n\n      $table_referenced = str_replace($this->db_prefix, '', $r1['REFERENCED_TABLE_NAME']);\n\n      $result[$indexName]['name']                       = $indexName;\n      $result[$indexName]['signature'][]                = \"{$r1['COLUMN_NAME']}=>{$table_referenced}.{$r1['REFERENCED_COLUMN_NAME']}\";\n      $r1['REFERENCED_TABLE_NAME']                      = $table_referenced;\n      $r1['TABLE_NAME']                                 = $tableName_unsafe;\n      $result[$indexName]['fields'][$r1['COLUMN_NAME']] = $r1;\n    }\n\n    foreach ($result as &$constraint) {\n      $constraint['signature'] = implode(',', $constraint['signature']);\n    }\n\n    return $result;\n  }\n\n\n  /**\n   * @param $query_string\n   *\n   * @return bool|mysqli_result\n   */\n  public function db_sql_query($query_string) {\n    $mt = microtime(true);\n\n    $result = $this->link->query($query_string);\n\n    $this->time_mysql_total += microtime(true) - $mt;\n\n    return $result;\n//    return $this->driver->mysql_query($query_string);\n  }\n\n  /**\n   * @param mysqli_result $query_result\n   *\n   * @return array|null\n   */\n  public function db_fetch($query_result) {\n    $mt = microtime(true);\n\n    $result = mysqli_fetch_assoc($query_result);\n\n    $this->time_mysql_total += microtime(true) - $mt;\n\n    return $result;\n  }\n\n//  public function db_fetch_row(&$query) {\n//    return mysqli_fetch_row($query);\n//  }\n\n  public function db_escape($unescaped_string) {\n    return mysqli_real_escape_string($this->link, $unescaped_string);\n  }\n\n  public function driver_disconnect() {\n    if (is_object($this->link)) {\n      $this->link->close();\n      $this->connected = false;\n      unset($this->link);\n    }\n\n    return true;\n  }\n\n  public function db_error() {\n    return mysqli_error($this->link);\n  }\n\n  /**\n   * @return int|string\n   */\n  public function db_insert_id() {\n    return mysqli_insert_id($this->link);\n  }\n\n  public function db_num_rows($result) {\n    return mysqli_num_rows($result);\n  }\n\n  public function db_affected_rows() {\n    return mysqli_affected_rows($this->link);\n  }\n\n  public function getClientInfo() {\n    return mysqli_get_client_info($this->link);\n  }\n\n  public function getServerInfo() {\n    return mysqli_get_server_info($this->link);\n  }\n\n  public function getHostInfo() {\n    return mysqli_get_host_info($this->link);\n  }\n\n  public function getServerStat() {\n    return mysqli_stat($this->link);\n  }\n\n  /**\n   * @param array $dbSettings\n   */\n  public function setDbSettings($dbSettings) {\n    $this->dbsettings = $dbSettings;\n    $this->dbName     = $this->dbsettings['name'];\n\n    return $this;\n  }\n\n  public function getDbSettings() {\n    return $this->dbsettings;\n  }\n\n  /**\n   * @param $level\n   *\n   * @return void\n   */\n  public function transactionStart($level = '') {\n    $this->inTransaction = true;\n\n    if ($level && in_array($level, self::TRANSACTION_LEVELS_ALLOWED)) {\n      $this->db_sql_query(\"SET TRANSACTION ISOLATION LEVEL {$level};\");\n    }\n\n    $this->doquery('START TRANSACTION; /* transaction start */', false);\n  }\n\n  /**\n   * @return void\n   */\n  public function transactionCommit() {\n    $this->doquery('COMMIT; /* transaction commit */');\n    $this->inTransaction = false;\n  }\n\n  /**\n   * @return void\n   */\n  public function transactionRollback() {\n    $this->doquery('ROLLBACK; /* transaction rollback */');\n    $this->inTransaction = false;\n  }\n\n  /**\n   * Check if transaction started\n   *\n   * @return bool\n   */\n  public function transactionCheck() {\n    return $this->inTransaction;\n  }\n\n  /**\n   * Wrap callback in transaction\n   *\n   * @param callable $callback\n   * @param string   $level\n   *\n   * @return mixed\n   */\n  public function transactionWrap($callback, $level = '') {\n    $this->transactionStart($level);\n    $result = $callback();\n    $this->transactionCommit();\n\n    return $result;\n  }\n\n  public static function db_transaction_start($level = '') {\n    self::db_transaction_check(db_mysql::DB_TRANSACTION_SHOULD_NOT_BE);\n\n    SN::$gc->db->transactionStart($level);\n\n    self::$transaction_id++;\n\n    if (SN::$config->db_manual_lock_enabled) {\n      SN::$config->db_loadItem('var_db_manually_locked');\n      SN::$config->db_saveItem('var_db_manually_locked', SN_TIME_SQL);\n    }\n\n    self::$db_in_transaction = true;\n    self::$transactionDepth++;\n    DBStaticUnit::cache_clear();\n\n    return self::$transaction_id;\n  }\n\n  public static function db_transaction_rollback() {\n    // static::db_transaction_check(true); // TODO - вообще-то тут тоже надо проверять есть ли транзакция\n    DBStaticUnit::cache_clear();\n\n    SN::$gc->db->transactionRollback();\n\n    self::$db_in_transaction = false;\n    self::$transactionDepth--;\n\n    return self::$transaction_id;\n  }\n\n  /**\n   * Блокирует указанные таблицу/список таблиц\n   *\n   * @param string|array $tables Таблица/список таблиц для блокировки. Названия таблиц - без префиксов\n   *                             <p>string - название таблицы для блокировки</p>\n   *                             <p>array - массив, где ключ - имя таблицы, а значение - условия блокировки элементов</p>\n   */\n  public static function db_lock_tables($tables) {\n    $tables = is_array($tables) ? $tables : array($tables => '');\n    foreach ($tables as $table_name => $condition) {\n      SN::$db->doquery(\n        \"SELECT 1 FROM {{{$table_name}}}\" . ($condition ? ' WHERE ' . $condition : '')\n      );\n    }\n  }\n\n  public static function db_transaction_commit() {\n    self::db_transaction_check(db_mysql::DB_TRANSACTION_SHOULD_BE);\n\n    DBStaticUnit::cache_clear();\n    SN::$gc->db->transactionCommit();\n\n    self::$db_in_transaction = false;\n    self::$transactionDepth--;\n\n    return self::$transaction_id++;\n  }\n\n  /**\n   * Эта функция проверяет статус транзакции\n   *\n   * Это - низкоуровневая функция. В нормальном состоянии движка её сообщения никогда не будут видны\n   *\n   * @param null|true|false $status Должна ли быть запущена транзакция в момент проверки\n   *                                <p>null - транзакция НЕ должна быть запущена</p>\n   *                                <p>true - транзакция должна быть запущена - для совместимости с $for_update</p>\n   *                                <p>false - всё равно - для совместимости с $for_update</p>\n   *\n   * @return bool Текущий статус транзакции\n   */\n  public static function db_transaction_check($status = self::DB_TRANSACTION_WHATEVER) {\n    $error_msg = false;\n    if ($status && !self::$db_in_transaction) {\n      $error_msg = 'No transaction started for current operation';\n    } elseif ($status === null && self::$db_in_transaction) {\n      $error_msg = 'Transaction is already started';\n    }\n\n    if ($error_msg) {\n      // TODO - Убрать позже\n      print('<h1>СООБЩИТЕ ЭТО АДМИНУ: sn_db_transaction_check() - ' . $error_msg . '</h1>');\n      $backtrace = debug_backtrace();\n      array_shift($backtrace);\n      pdump($backtrace);\n      die($error_msg);\n    }\n\n    return self::$db_in_transaction;\n  }\n\n  /**\n   * Lock specified records in specified tables\n   *\n   * @param array[] $locks ['$tableName' => [$idToLock, ...],],\n   *\n   * @return array|bool|mysqli_result|null\n   */\n  public function lockRecords($locks) {\n    // Reordering lock order for known tables\n    $newLocks = [];\n    foreach (self::TABLE_LOCK_ORDER as $tableName) {\n      if (array_key_exists($tableName, $locks)) {\n        $newLocks[$tableName] = $locks[$tableName];\n        unset($locks[$tableName]);\n      }\n    }\n    // Adding tables left - their lock order is not important\n    $newLocks += $locks;\n\n    // Compiling SQL queries to lock records\n    $queries = [];\n    foreach ($newLocks as $tableName => $lockedIds) {\n      if (!empty($lockedIds)) {\n        // Detecting primary key name\n        $idFieldName = !empty(static::TABLE_ID_FIELD[$tableName]) ? static::TABLE_ID_FIELD[$tableName] : 'id';\n        // Making ID mysql-safe\n        array_walk($lockedIds, function (&$id) { $id = idval($id); });\n        /** @noinspection SqlResolve */\n        $queries[] = \"(SELECT 1 FROM `{{{$tableName}}}` WHERE `{$idFieldName}` IN (\" . implode(',', $lockedIds) . \") FOR UPDATE)\";\n      }\n    }\n\n//    return !empty($query) ? doquery(implode(' UNION ', $query)) : false;\n    foreach ($queries as $query) {\n      doquery($query);\n    }\n\n    return true;\n  }\n\n}\n"
  },
  {
    "path": "classes/Design.php",
    "content": "<?php\n\n/**\n * Created by Gorlum 15.02.2017 10:08\n */\n\nuse Core\\GlobalContainer;\n\n/**\n * Class Design\n *\n * Describes design elements. For now it's a smileset and bbCodes\n *\n */\nclass Design {\n  /**\n   * @var classConfig $gameConfig\n   */\n  protected $gameConfig;\n\n  protected $smileList = array();\n  protected $smiles = array();\n  protected $bbCodes = array();\n\n  /**\n   * Design constructor.\n   *\n   * @param GlobalContainer $gc\n   */\n  public function __construct(GlobalContainer $gc) {\n    $this->gameConfig = SN::$gc->config;\n\n    // Prefix faq:// resolves to FAQ's URL - if configured\n    if (is_object($this->gameConfig) && !empty($this->gameConfig->url_faq)) {\n      $this->addBbCodes(array(\n        '#faq://#isU' => $this->gameConfig->url_faq,\n      ), AUTH_LEVEL_REGISTERED);\n    }\n\n    global $sn_data_bbCodes, $sn_data_smiles;\n    $this->addBbCodes($sn_data_bbCodes);\n    $this->addSmiles($sn_data_smiles);\n  }\n\n  protected function detectElementsFormat($elements, $accessLevel = AUTH_LEVEL_REGISTERED) {\n    $firstElement = reset($elements);\n    if (!is_array($firstElement)) {\n      // Plain element list - making access list\n      $elements = array($accessLevel => $elements);\n    }\n\n    return $elements;\n  }\n\n  /**\n   * @param array               $array\n   * @param string[]|string[][] $elements <p>list of smiles. Can be:</p>\n   *    <p>- plain list of smiles in format ['text' => 'imageName',...], i.e. [':p:' => 'tongue']</p>\n   *    <p>- access list of plaint list of smiles in format [AUTH_LEVEL_xxx => ['text' => 'imageName',...], ...], i.e. [AUTH_LEVEL_REGISTERED => [':p:' => 'tongue']]</p>\n   *    <p>'text' can be a regex - otherwise it would be forcibly formatted to regex '#text#isU'</p>\n   * @param int                 $accessLevel - access level for plain element list. Default - AUTH_LEVEL_REGISTERED\n   */\n  protected function addElements(&$array, $elements, $accessLevel = AUTH_LEVEL_REGISTERED) {\n    if (!is_array($elements) || empty($elements)) {\n      return; // TODO - Exception\n    }\n\n    $elements = $this->detectElementsFormat($elements, $accessLevel);\n\n    HelperArray::merge($array, $elements, HelperArray::MERGE_RECURSIVE_NUMERIC);\n  }\n\n  /**\n   * Add bbCodes to global collection\n   *\n   * @param string[]|string[][] $bbCodes <p>list of bbCodes formatted like in Design::addElements</p>\n   * @param int                 $accessLevel - access level for plain element list. Default - AUTH_LEVEL_REGISTERED\n   *\n   * @see Design::addElements\n   */\n  public function addBbCodes($bbCodes, $accessLevel = AUTH_LEVEL_REGISTERED) {\n    $this->addElements($this->bbCodes, $bbCodes, $accessLevel);\n  }\n\n  /**\n   * Add smiles to global collection\n   *\n   * @param string[]|string[][] $smiles <p>list of smiles formatted like in Design::addElements</p>\n   * @param int                 $accessLevel - access level for plain element list. Default - AUTH_LEVEL_REGISTERED\n   *\n   * @see Design::addElements\n   */\n  public function addSmiles($smiles, $accessLevel = AUTH_LEVEL_REGISTERED) {\n    $smiles = $this->detectElementsFormat($smiles, $accessLevel);\n\n    // Detecting regexps or plain text - converting plain text to regexps\n    // This is for back compatibility with Festival data\n    foreach ($smiles as $elementAuth => &$elementList) {\n      $surelyRegexps = array();\n      foreach ($elementList as $find => $replace) {\n        // Caching smiles as-is for smile list generation\n        $this->smileList[$elementAuth][$find] = $replace;\n\n        // Now converting smile original data to preg_replace() params\n        $replace = \"<img src=\\\"design/images/smileys/\" . $replace . \".gif\\\" align=\\\"absmiddle\\\" title=\\\"\" . $find . \"\\\" alt=\\\"\" . $find . \"\\\">\";\n\n        // Check for regexp\n        if (($firstChar = substr($find, 0, 1)) != '#' && $firstChar != '/') {\n          $find = '#' . addcslashes($find, '()[]{}') . '#isU';\n        }\n\n        // To maintain element order even when index change\n        $surelyRegexps[$find] = $replace;\n      }\n      $elementList = $surelyRegexps;\n    }\n\n    $this->addElements($this->smiles, $smiles, $accessLevel);\n  }\n\n  // Getters/Setters +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n  public function getSmiles() {\n    return $this->smiles;\n  }\n\n  public function getBbCodes() {\n    return $this->bbCodes;\n  }\n\n  /**\n   * Return smiles for making smile's list\n   *\n   * @return array\n   */\n  public function getSmilesList() {\n    return $this->smileList;\n  }\n\n}\n"
  },
  {
    "path": "classes/Fleet/Constants.php",
    "content": "<?php\n/** Created by Gorlum 10.05.2025 23:27 */\n\nnamespace Fleet;\n\nclass Constants {\n  // Mission outcome types\n  /** @var int Something bad happens */\n  const OUTCOME_TYPE_BAD = -1;\n  /** @var int Nothing really happens */\n  const OUTCOME_TYPE_NEUTRAL = 0;\n  /** @var int Something good happens */\n  const OUTCOME_TYPE_GOOD = 1;\n\n  // Global mission outcomes\n  /** @var int Outcome was not calculated yet */\n  const OUTCOME_NOT_CALCULATED = -1;\n  /** @var int Nothing happens during mission */\n  const OUTCOME_NONE = 0;\n\n  // Expedition outcomes\n  /** @var int Some units were lost */\n  const EXPEDITION_OUTCOME_LOST_FLEET = 1;\n  /** @var int Found some units */\n  const EXPEDITION_OUTCOME_FOUND_FLEET = 2;\n  /** @var int Found some resources */\n  const EXPEDITION_OUTCOME_FOUND_RESOURCES = 3;\n  /** @var int Found Dark Matter */\n  const EXPEDITION_OUTCOME_FOUND_DM = 4;\n//  /** @var int Found Artifact */\n//  const EXPEDITION_OUTCOME_FOUND_ARTIFACT = 5;\n  /** @var int Fleet lost */\n  const EXPEDITION_OUTCOME_LOST_FLEET_ALL = 6;\n\n  /** @var int Mission expedition base chance that nothing will be found in 1 hour. Decreased with each hour spent in expedition */\n  const OUTCOME_EXPEDITION_NOTHING_DEFAULT_CHANCE = 200;\n\n  /** @var string Hook name to bind explore addons */\n  const HOOK_MISSION_EXPLORE_ADDON = 'flt_mission_explore_addon';\n\n  // Key names in arrays\n  const K_OUTCOME = 'outcome';\n  const K_OUTCOME_TYPE = 'outcome_type';\n  const K_OUTCOME_SECONDARY = 'secondary';\n\n}\n"
  },
  {
    "path": "classes/Fleet/DbFleetStatic.php",
    "content": "<?php /** @noinspection SqlResolve */\n\n/**\n * Created by Gorlum 26.04.2018 14:00\n */\n\nnamespace Fleet;\n\nuse DBAL\\DbQuery;\nuse Exception;\nuse mysqli_result;\nuse SN;\n\n/**\n * Class DbFleetStatic\n * @package Fleet\n *\n * deprecated\n */\nclass DbFleetStatic {\n\n  /* HELPERS ******************************************************************************************************** */\n  /**\n   * @return DbQuery\n   */\n  protected static function dbq() {\n    return DbQuery::build()->setTable('fleets');\n  }\n\n\n\n  /* FLEET CRUD ===================================================================================================== */\n  /**\n   * Inserts fleet record by ID with array\n   *\n   * @param array $fieldArray - [fieldName => fieldValue]\n   *\n   * @return int|string - fleet inserted ID or 0 if no fleets inserted\n   */\n  public static function fleet_insert_set_dbq($fieldArray) {\n    if (!empty($fieldArray)) {\n      static::dbq()\n        ->setValues($fieldArray)\n        ->doInsert(DbQuery::DB_INSERT_PLAIN, true);\n\n      $fleet_id = SN::$db->db_insert_id();\n    } else {\n      $fleet_id = 0;\n    }\n\n    return $fleet_id;\n  }\n\n  /**\n   * Updates fleet record by ID with SET\n   *\n   * @param int   $fleet_id\n   * @param array $set   - REPLACE-set, i.e. replacement of existing values\n   * @param array $delta - DELTA-set, i.e. changes to existing values\n   *\n   * @return array|bool|mysqli_result|null\n   */\n  public static function fleet_update_set($fleet_id, $set, $delta = array()) {\n    $result = false;\n\n    $fleet_id_safe   = idval($fleet_id);\n    $set_string_safe = db_set_make_safe_string($set);\n    !empty($delta) ? $set_string_safe = implode(',', array($set_string_safe, db_set_make_safe_string($delta, true))) : false;\n    if (!empty($fleet_id_safe) && !empty($set_string_safe)) {\n      $result = static::db_fleet_update_set_safe_string($fleet_id, $set_string_safe);\n    }\n\n    return $result;\n  }\n\n  /**\n   * UPDATE - Updates fleet record by ID with SET\n   *\n   * @param int    $fleet_id\n   * @param string $set_safe_string\n   *\n   * @return array|bool|mysqli_result|null\n   */\n  protected static function db_fleet_update_set_safe_string($fleet_id, $set_safe_string) {\n    $fleet_id_safe = idval($fleet_id);\n    if (!empty($fleet_id_safe) && !empty($set_safe_string)) {\n      /** @noinspection SqlResolve */\n      /** @noinspection SqlWithoutWhere */\n      $result = doquery(\"UPDATE `{{fleets}}` SET {$set_safe_string} WHERE `fleet_id` = {$fleet_id_safe} LIMIT 1;\");\n    } else {\n      $result = false;\n    }\n\n    return $result;\n  }\n\n\n  /**\n   * READ - Gets fleet record by ID\n   *\n   * @param int $fleet_id\n   *\n   * @return array|false\n   */\n  public static function db_fleet_get($fleet_id) {\n    $fleet_id_safe = idval($fleet_id);\n    /** @noinspection SqlResolve */\n    $result = doquery(\"SELECT * FROM `{{fleets}}` WHERE `fleet_id` = {$fleet_id_safe} LIMIT 1 FOR UPDATE;\", true);\n\n    return is_array($result) ? $result : false;\n  }\n\n  /**\n   * DELETE\n   *\n   * @param $fleet_id\n   *\n   * @return array|bool|mysqli_result|null\n   */\n  public static function db_fleet_delete($fleet_id) {\n    $fleet_id_safe = idval($fleet_id);\n    if (!empty($fleet_id_safe)) {\n      /** @noinspection SqlResolve */\n      $result = doquery(\"DELETE FROM `{{fleets}}` WHERE `fleet_id` = {$fleet_id_safe} LIMIT 1;\");\n    } else {\n      $result = false;\n    }\n\n    return $result;\n  }\n\n\n\n  /* FLEET LIST & COUNT CRUD ===========================================================================================*/\n  /**\n   * COUNT - Get fleet count by condition\n   *\n   * @param string $where_safe\n   *\n   * @return int\n   */\n  public static function db_fleet_count($where_safe) {\n    /** @noinspection SqlResolve */\n    $result = doquery(\"SELECT COUNT(`fleet_id`) as 'fleet_count' FROM `{{fleets}}` WHERE {$where_safe}\", true);\n\n    return !empty($result['fleet_count']) ? intval($result['fleet_count']) : 0;\n  }\n\n\n  /**\n   * LIST - Get fleet list by condition\n   *\n   * @param string $where_safe\n   *\n   * @return array[]\n   *\n   * TODO - This function should NOT be used to query fleets to all planets - some aggregate function should be used\n   */\n  public static function db_fleet_list($where_safe, $for_update = DB_SELECT_FOR_UPDATE) {\n    $row_list = [];\n\n    $dbq = DbQuery::build()\n      ->setTable('fleets');\n\n    if (!empty($where_safe)) {\n      $dbq->setWhereArrayDanger([$where_safe]);\n    }\n\n    if ($for_update == DB_SELECT_FOR_UPDATE) {\n      $dbq->setForUpdate(DbQuery::DB_FOR_UPDATE);\n    }\n\n    $query = $dbq->doSelect();\n\n    while ($row = db_fetch($query)) {\n      $row_list[$row['fleet_id']] = $row;\n    }\n\n    return $row_list;\n  }\n\n  /**\n   * LIST DELETE\n   *\n   * @param $owner_id\n   *\n   * @return array|bool|mysqli_result|null\n   * @deprecated\n   *\n   * TODO - fleets should be deleted by DB itself via InnoDB FOREIHN KEY\n   */\n  public static function db_fleet_list_delete_by_owner($owner_id) {\n    return doquery(\"DELETE FROM `{{fleets}}` WHERE `fleet_owner` = '{$owner_id}';\");\n  }\n\n  /**\n   * LIST STAT - DEPRECATED\n   *\n   * @return array|bool|mysqli_result|null\n   *\n   * TODO - deprecated\n   */\n  public static function db_fleet_list_query_all_stat() {\n    return doquery(\"SELECT fleet_owner, fleet_array, fleet_resource_metal, fleet_resource_crystal, fleet_resource_deuterium FROM `{{fleets}}`;\");\n  }\n\n\n\n  /* FLEET FUNCTIONS ===================================================================================================*/\n  /**\n   * Sends fleet back\n   *\n   * @param $fleet_row\n   *\n   * @return array|bool|mysqli_result|null\n   *\n   * TODO - Add another field which mark fleet as processed/completed task on destination point\n   * By this flag fleet dispatcher should immediately return fleet (change STATUS/mess flag) to source planet\n   */\n  public static function fleet_send_back(&$fleet_row) {\n    $fleet_id = round(!empty($fleet_row['fleet_id']) ? $fleet_row['fleet_id'] : $fleet_row);\n    if (!$fleet_id) {\n      return false;\n    }\n\n    $result = static::fleet_update_set($fleet_id, array(\n      'fleet_mess' => 1,\n    ));\n\n    return $result;\n  }\n\n\n  /* FLEET COUNT FUNCTIONS =============================================================================================*/\n  /**\n   * Get flying fleet count\n   *\n   * @param int $player_id  - Player ID\n   * @param int $mission_id - mission ID. \"0\" means \"all\"\n   *\n   * @return int\n   *\n   * TODO - Player lock should be issued before to prevent fleet number change\n   */\n  public static function fleet_count_flying($player_id, $mission_id = 0) {\n    $player_id_safe = idval($player_id);\n    if (!empty($player_id_safe)) {\n      $mission_id_safe = intval($mission_id);\n      $result          = static::db_fleet_count(\n        \"`fleet_owner` = {$player_id_safe}\" .\n        ($mission_id_safe ? \" AND `fleet_mission` = {$mission_id_safe}\" : '')\n      );\n    } else {\n      $result = 0;\n    }\n\n    return $result;\n  }\n\n  /**\n   * Returns amount of incoming fleets to planet\n   *\n   * @param int $galaxy\n   * @param int $system\n   * @param int $planet\n   *\n   * @return int\n   *\n   * TODO - Через fleet_list_by_planet_coords() ????\n   */\n  public static function fleet_count_incoming($galaxy, $system, $planet) {\n    return static::db_fleet_count(\n      \"(`fleet_start_galaxy` = {$galaxy} AND `fleet_start_system` = {$system} AND `fleet_start_planet` = {$planet})\n    OR\n    (`fleet_end_galaxy` = {$galaxy} AND `fleet_end_system` = {$system} AND `fleet_end_planet` = {$planet})\"\n    );\n  }\n\n  /* FLEET LIST FUNCTIONS =============================================================================================*/\n  /**\n   * Get fleet list by owner\n   *\n   * @param int $fleet_owner_id - Fleet owner record/ID. Can't be empty\n   *\n   * @return array[]\n   */\n  public static function fleet_list_by_owner_id($fleet_owner_id) {\n    $fleet_owner_id_safe = idval($fleet_owner_id);\n\n    return $fleet_owner_id_safe ? static::db_fleet_list(\"`fleet_owner` = {$fleet_owner_id_safe}\", DB_SELECT_PLAIN) : array();\n  }\n\n  /**\n   * Get fleet list flying/returning to planet/system coordinates\n   *\n   * @param int $galaxy\n   * @param int $system\n   * @param int $planet      - planet position. \"0\" means \"any\"\n   * @param int $planet_type - planet type. \"PT_ALL\" means \"any type\"\n   *\n   * @return array\n   * TODO - safe params\n   */\n  public static function fleet_list_by_planet_coords($galaxy, $system, $planet = 0, $planet_type = PT_ALL, $for_phalanx = false) {\n    return static::db_fleet_list(\n      \"(\n    fleet_start_galaxy = {$galaxy}\n    AND fleet_start_system = {$system}\" .\n      ($planet ? \" AND fleet_start_planet = {$planet}\" : '') .\n      ($planet_type != PT_ALL ? \" AND fleet_start_type = {$planet_type}\" : '') .\n      ($for_phalanx ? '' : \" AND fleet_mess = 1\") .\n      \")\n    OR\n    (\n    fleet_end_galaxy = {$galaxy}\n    AND fleet_end_system = {$system}\" .\n      ($planet ? \" AND fleet_end_planet = {$planet}\" : '') .\n      ($planet_type != PT_ALL ? \" AND fleet_end_type = {$planet_type} \" : '') .\n      ($for_phalanx ? '' : \" AND fleet_mess = 0\") .\n      \")\"\n      , DB_SELECT_PLAIN\n    );\n  }\n\n  /**\n   * Fleets on hold on planet orbit\n   *\n   * @param int $galaxy\n   * @param int $system\n   * @param int $planet\n   * @param int $planet_type\n   * @param int $ube_time\n   *\n   * @return array\n   *\n   * TODO - safe params\n   */\n  public static function fleet_list_on_hold($galaxy, $system, $planet, $planet_type, $ube_time) {\n    /** @noinspection PhpRedundantOptionalArgumentInspection */\n    return DbFleetStatic::db_fleet_list(\n      \"`fleet_end_galaxy` = {$galaxy}\n    AND `fleet_end_system` = {$system}\n    AND `fleet_end_planet` = {$planet}\n    AND `fleet_end_type` = {$planet_type}\n    AND `fleet_start_time` <= {$ube_time}\n    AND `fleet_end_stay` >= {$ube_time}\n    AND `fleet_mess` = 0\"\n      , DB_SELECT_FOR_UPDATE\n    );\n  }\n\n  /**\n   * Get aggressive fleet list of chosen player on selected planet\n   *\n   * @param $fleet_owner_id\n   * @param $planet_row\n   *\n   * @return array\n   */\n  public static function fleet_list_bashing($fleet_owner_id, $planet_row) {\n    return static::db_fleet_list(\n      \"`fleet_end_galaxy` = {$planet_row['galaxy']}\n    AND `fleet_end_system` = {$planet_row['system']}\n    AND `fleet_end_planet` = {$planet_row['planet']}\n    AND `fleet_end_type`   = {$planet_row['planet_type']}\n    AND `fleet_owner` = {$fleet_owner_id}\n    AND `fleet_mission` IN (\" . MT_ATTACK . \",\" . MT_AKS . \",\" . MT_DESTROY . \")\n    AND `fleet_mess` = 0\"\n      , DB_SELECT_FOR_UPDATE\n    );\n  }\n\n  /**\n   * Get fleets in group\n   *\n   * @param $group_id\n   *\n   * @return array\n   */\n  public static function fleet_list_by_group($group_id) {\n    return static::db_fleet_list(\"`fleet_group` = {$group_id}\", DB_SELECT_FOR_UPDATE);\n  }\n\n\n\n  /* MISSILE CRUD *******************************************************************************************************/\n  /* MISSILE LIST & COUNT CRUD =========================================================================================*/\n  /**\n   * LIST - Get missile attack list by condition\n   *\n   * @param string $where      - WHERE condition - SQL SAFE!\n   * @param bool   $for_update - lock record with FOR UPDATE statement\n   *\n   * @return array - lift of fleet records from DB\n   */\n  public static function db_missile_list($where, $for_update = DB_SELECT_FOR_UPDATE) {\n    $row_list = [];\n\n    /** @noinspection SqlResolve */\n    $query = doquery(\n      \"SELECT * FROM `{{iraks}}`\" .\n      (!empty($where) ? \" WHERE {$where}\" : '') .\n      ($for_update == DB_SELECT_FOR_UPDATE ? \" FOR UPDATE\" : '')\n    );\n    while ($row = db_fetch($query)) {\n      $row_list[$row['id']] = $row;\n    }\n\n    return $row_list;\n  }\n\n\n\n  /* FLEET/MISSILES LIST FUNCTIONS =====================================================================================*/\n  /**\n   * Get fleet and missile list by coordinates\n   *\n   * @param array $coordinates\n   * @param bool  $for_phalanx - If true - this is phalanx scan so limiting output with fleet_mess\n   *\n   * @return array\n   */\n  public static function fleet_and_missiles_list_by_coordinates($coordinates, $for_phalanx = false) {\n    if (empty($coordinates) || !is_array($coordinates)) {\n      return array();\n    }\n\n    $fleet_db_list = static::fleet_list_by_planet_coords($coordinates['galaxy'], $coordinates['system'], $coordinates['planet'], $coordinates['planet_type'], $for_phalanx);\n\n    $missile_db_list = static::db_missile_list(\n      \"(\n      fleet_start_galaxy = {$coordinates['galaxy']}\n      AND fleet_start_system = {$coordinates['system']}\n      AND fleet_start_planet = {$coordinates['planet']}\n      AND fleet_start_type = {$coordinates['planet_type']}\n    )\n    OR\n    (\n      fleet_end_galaxy = {$coordinates['galaxy']}\n      AND fleet_end_system = {$coordinates['system']}\n      AND fleet_end_planet = {$coordinates['planet']}\n      AND fleet_end_type = {$coordinates['planet_type']}\n    )\"\n      , DB_SELECT_PLAIN\n    );\n\n    missile_list_convert_to_fleet($missile_db_list, $fleet_db_list);\n\n    return $fleet_db_list;\n  }\n\n  /**\n   * Get fleet and missile list by that flies from player's planets OR to player's planets\n   *\n   * @param int $owner_id\n   *\n   * @return array\n   */\n  public static function fleet_and_missiles_list_incoming($owner_id) {\n    $owner_id_safe = idval($owner_id);\n    if (empty($owner_id_safe)) {\n      return array();\n    }\n\n    $where           = \"`fleet_owner` = '{$owner_id_safe}' OR `fleet_target_owner` = '{$owner_id_safe}'\";\n    $fleet_db_list   = static::db_fleet_list($where, DB_SELECT_PLAIN);\n    $missile_db_list = static::db_missile_list($where, DB_SELECT_PLAIN);\n\n    missile_list_convert_to_fleet($missile_db_list, $fleet_db_list);\n\n    return $fleet_db_list;\n  }\n\n\n\n  /* ACS ****************************************************************************************************************/\n  /**\n   * Purges ACS list\n   */\n  public static function db_fleet_acs_purge() {\n    DbQuery::build()\n      ->setTable('aks')\n      ->setWhereArrayDanger(['`id` NOT IN (SELECT DISTINCT `fleet_group` FROM `{{fleets}}`)'])\n      ->doDelete();\n  }\n\n  public static function dbAcsGetById($acsId) {\n    return DbQuery::build()\n      ->setTable('aks')\n      ->setWhereArray(['id' => $acsId])\n      ->doSelectFetch();\n  }\n\n  public static function dbAcsGetByFleet($fleetId) {\n    return DbQuery::build()\n      ->setTable('aks')\n      ->setWhereArray(['flotten' => $fleetId])\n      ->doSelectFetch();\n  }\n\n  /**\n   * @param int|array $fleetList [(int|string)fleetId,...]\n   *\n   * @return int\n   */\n  public static function dbAcsDelete($fleetList) {\n    if (!is_array($fleetList) && !empty($fleetList)) {\n      $fleetList = [$fleetList];\n    }\n\n    if (empty($fleetList)) {\n      return 0;\n    }\n\n    $whereId = [];\n    foreach ($fleetList as $fleetId) {\n      $whereId[] = \"'\" . SN::$db->db_escape($fleetId) . \"'\";\n    }\n\n    return SN::$db->doquery(\"DELETE FROM `{{aks}}` WHERE `id` IN (\" . implode(',', $whereId) . \")\");\n  }\n\n  /**\n   * @param $userToAddID\n   * @param $fleetid\n   *\n   * @return array|bool|mysqli_result|null\n   */\n  public static function dbAcsAddUserByFleetId($userToAddID, $fleetid) {\n    return SN::$db->doquery(\"UPDATE `{{aks}}` SET `eingeladen` = concat(`eingeladen`, ',{$userToAddID}') WHERE `flotten` = {$fleetid};\");\n  }\n\n  /**\n   * @param int   $userId\n   * @param int   $fleetid\n   * @param array $fleet\n   *\n   * @return array|bool|mysqli_result|null\n   */\n  public static function dbAcsInsert($userId, $fleetid, $fleet) {\n    return doquery(\"INSERT INTO `{{aks}}` SET\n          `name` = '\" . SN::$db->db_escape(SN::$lang['flt_acs_prefix'] . $fleetid) . \"',\n          `teilnehmer` = '\" . $userId . \"',\n          `flotten` = '\" . $fleetid . \"',\n          `ankunft` = '\" . $fleet['fleet_start_time'] . \"',\n          `galaxy` = '\" . $fleet['fleet_end_galaxy'] . \"',\n          `system` = '\" . $fleet['fleet_end_system'] . \"',\n          `planet` = '\" . $fleet['fleet_end_planet'] . \"',\n          `planet_type` = '\" . $fleet['fleet_end_type'] . \"',\n          `eingeladen` = '\" . $userId . \"',\n          `fleet_end_time` = '\" . $fleet['fleet_end_time'] . \"'\");\n  }\n\n  /**\n   * @return array|bool|mysqli_result|null\n   */\n  public static function dbAcsGetAll() {\n    return doquery('SELECT * FROM `{{aks}}`;');\n  }\n\n\n  /**\n   * @param $user\n   * @param $fleet\n   * @param $userToAddRecord\n   *\n   * @return array\n   * @throws Exception\n   */\n  public static function acsAddUser($user, $fleet, $userToAddRecord) {\n    $userToAddID = !empty($userToAddRecord['id']) ? $userToAddRecord['id'] : 0;\n\n    if (empty($userToAddID)) {\n      throw new Exception(SN::$lang['fl_aks_player_error']);\n    }\n\n    if ($fleet['fleet_target_owner'] == $userToAddID) {\n      throw new Exception(SN::$lang['flt_aks_player_same']);\n    }\n\n    $aks = DbFleetStatic::dbAcsGetByFleet($fleet['fleet_id']);\n\n    $aks = DbFleetStatic::acsAddUser2($aks, $fleet, $userToAddID, $user['id']);\n\n    return $aks;\n  }\n\n\n  /**\n   * @param array $aks\n   * @param array $fleet\n   * @param       $userToAddID\n   * @param       $userId\n   *\n   * @return array\n   *\n   * @throws Exception\n   */\n  public static function acsAddUser2($aks, $fleet, $userToAddID, $userId) {\n    $fleetid = $fleet['fleet_id'];\n\n    if (!$aks) {\n      // No AСS exists - making one\n      if (!$fleet['fleet_group']) {\n        DbFleetStatic::dbAcsInsert($userId, $fleetid, $fleet);\n        $aks = DbFleetStatic::dbAcsGetByFleet($fleetid);\n\n        DbFleetStatic::fleet_update_set($fleetid, array(\n          'fleet_group'   => $aks['id'],\n          'fleet_mission' => MT_AKS,\n        ));\n        $fleet['fleet_group']   = $aks['id'];\n        $fleet['fleet_mission'] = MT_AKS;\n      } else {\n        throw new Exception(SN::$lang['fl_aks_already_in_aks']);\n      }\n    }\n\n    $invited_ar = explode(\",\", $aks['eingeladen']);\n    if (count($invited_ar) >= 5) {\n      throw new Exception(SN::$lang['flt_aks_error_too_much_players']);\n    }\n\n    $acsPoints    = 0;\n    $isUserExists = false;\n    foreach ($invited_ar as $inv) {\n      if ($userToAddID == $inv) {\n        $isUserExists = true;\n      }\n\n      $invitedUserRecord = db_user_by_id($inv);\n      if (!empty($invitedUserRecord)) {\n        $acsPoints += $invitedUserRecord['total_points'];\n      }\n    }\n\n    $attackedPlayer = db_user_by_id($fleet['fleet_target_owner']);\n    if (\n      !empty($attackedPlayer['total_points'])\n      &&\n      SN::$gc->general->playerIs1stStrongerThen2nd($acsPoints, $attackedPlayer['total_points'])\n    ) {\n      throw new Exception(SN::$lang['fl_aks_too_power']);\n    }\n\n\n    if (self::acsIsAcsFull($aks['id'])) {\n      throw new Exception(SN::$lang['fl_aks_too_power']);\n    }\n\n    if ($isUserExists) {\n//      if ($userToAddID != $userId)\n      throw new Exception(SN::$lang['fl_aks_player_invited_already']);\n    } else {\n      DbFleetStatic::dbAcsAddUserByFleetId($userToAddID, $fleetid);\n      $aks['eingeladen'] .= ',' . $userToAddID;\n    }\n\n    return $aks;\n  }\n\n  /**\n   * @param int $acsId\n   *\n   * @return bool\n   */\n  public static function acsIsAcsFull($acsId) {\n    $fleetInAcs      = self::fleet_list_by_group($acsId);\n    $isMaxSubReached = count($fleetInAcs) >= 5;\n\n    return $isMaxSubReached;\n  }\n\n  /**\n   * @param array $user\n   *\n   * @return array\n   */\n  public static function tpl_get_fleets_flying(&$user) {\n    $fleet_flying_list = array();\n\n    $fleet_flying_list[0] = DbFleetStatic::fleet_list_by_owner_id($user['id']);\n    foreach ($fleet_flying_list[0] as $fleet_id => $fleet_flying_row) {\n      $fleet_flying_list[$fleet_flying_row['fleet_mission']][$fleet_id] = &$fleet_flying_list[0][$fleet_id];\n    }\n\n    return $fleet_flying_list;\n  }\n\n}\n"
  },
  {
    "path": "classes/Fleet/Fleet.php",
    "content": "<?php\n/**\n * Created by Gorlum 18.04.2018 16:30\n */\n\nnamespace Fleet;\n\n\nuse Core\\EntityDb;\nuse SN;\n\n/**\n * Class Fleet\n * @package Fleet\n *\n * @property int|string $id                       - bigint     -\n * @property int|string $ownerId                  - bigint     - Fleet player owner ID\n * @property int        $fleet_mission            - int        -\n * @property int|string $fleet_amount             - bigint     -\n * @property string     $fleet_array              - mediumtext -\n * @property int        $timeLaunch               - int        - Fleet launched from source planet (unix)\n * @property int        $timeArrive               - int        - Time fleet arrive to destination (unix)\n * @property int        $timeEndStay              - int        - Time when fleet operation on destination complete (if any) (unix)\n * @property int        $timeReturn               - int        - Time fleet would return to source planet (unix)\n * @property int|string $fleet_start_planet_id    - bigint     -\n * @property int        $fleet_start_galaxy       - int        -\n * @property int        $fleet_start_system       - int        -\n * @property int        $fleet_start_planet       - int        -\n * @property int        $fleet_start_type         - int        -\n * @property int|string $fleet_end_planet_id      - bigint     -\n * @property int        $fleet_end_galaxy         - int        -\n * @property int        $fleet_end_system         - int        -\n * @property int        $fleet_end_planet         - int        -\n * @property int        $fleet_end_type           - int        -\n * @property int|string $fleet_resource_metal     - decimal    -\n * @property int|string $fleet_resource_crystal   - decimal    -\n * @property int|string $fleet_resource_deuterium - decimal    -\n * @property int|string $fleet_target_owner       - int        -\n * @property int|string $fleet_group              - varchar    -\n * @property int        $status                   - int        - Current fleet status: flying to destination; returning\n *\n * Old fields for direct access\n * @property int        $fleet_id\n * @property int        $fleet_owner\n * @property int        $start_time               Time when fleet launched from source\n * @property int        $fleet_start_time         Time when fleet will arrive to destination point. Wrong name - should be `fleet_dst_arrive`\n * @property int        $fleet_end_stay           Time when fleet will end its mission on destination point. Should be `fleet_dst_stay_until`\n * @property int        $fleet_end_time           Time when fleet will return to source point. Should be `fleet_return_to_src`\n * @property int        $fleet_mess\n *\n */\nclass Fleet extends EntityDb {\n\n  /**\n   * @var string $_activeClass\n   */\n  protected $_activeClass = '\\\\Fleet\\\\RecordFleet';\n\n  /**\n   * @var RecordFleet $_container\n   */\n  protected $_container;\n\n  protected $_containerTranslateNames = [\n    'ownerId' => 'fleet_owner',\n\n    'timeLaunch'  => 'start_time',\n    'timeArrive'  => 'fleet_start_time',\n    'timeEndStay' => 'fleet_end_stay',\n    'timeReturn'  => 'fleet_end_time',\n\n    'status' => 'fleet_mess',\n  ];\n\n  /**\n   * Information about ships\n   *\n   * @var array[] $shipInfo\n   */\n  protected static $shipInfo = [];\n\n  protected $speedPercentTenth = 10;\n\n  /**\n   * @var null|array $ownerRecord\n   */\n  protected $ownerRecord = null;\n  /**\n   * @var null|array $sourcePlanet\n   */\n  protected $sourcePlanet = null;\n\n\n  /**\n   * Fleet constructor.\n   *\n   * @throws \\Exception\n   */\n  public function __construct() {\n    parent::__construct();\n  }\n\n  // Real fleet actions ------------------------------------------------------------------------------------------------\n\n  /**\n   * Forced return fleet to source planet\n   *\n   * @param int|string $byPlayerId\n   *\n   * @return bool\n   */\n  public function returnForce($byPlayerId) {\n    if ($this->ownerId != $byPlayerId) {\n      return false;\n    }\n\n    if ($this->status == FLEET_STATUS_RETURNING) {\n      return true;\n    }\n\n//    $ReturnFlyingTime = ($this->timeEndStay != 0 && $this->timeArrive < SN_TIME_NOW ? $this->timeArrive : SN_TIME_NOW) - $this->timeLaunch + SN_TIME_NOW + 1;\n    $timeToReturn     = SN_TIME_NOW - $this->timeLaunch + 1;\n    $ReturnFlyingTime = (!empty($this->timeEndStay) && $this->timeArrive < SN_TIME_NOW ? $this->timeArrive : SN_TIME_NOW) + $timeToReturn;\n\n    // TODO - Those two lines should be removed - fleet times should be filtered on interface side\n    $this->timeArrive = SN_TIME_NOW;\n    !empty($this->timeEndStay) ? $this->timeEndStay = SN_TIME_NOW : false;\n\n    $this->timeReturn = $ReturnFlyingTime;\n    $this->status     = FLEET_STATUS_RETURNING;\n\n    return $this->dbUpdate();\n  }\n\n  // Service functions  -----------------------------------------------------------------------------------------------\n\n  /**\n   * @return RecordFleet\n   */\n  public function _getContainer() {\n    return $this->_container;\n  }\n\n  /**\n   * @param int $shipId\n   *\n   * @return array\n   */\n  protected static function getUnitInfo($shipId) {\n    if (!isset(static::$shipInfo[$shipId])) {\n      static::$shipInfo[$shipId] = get_unit_param($shipId);\n    }\n\n    return static::$shipInfo[$shipId];\n  }\n\n  /**\n   * @param int $resourceId\n   *\n   * @return float[] - [(int)$shipId => (float)costInMetal]\n   */\n  public function getShipsBasicCosts($resourceId = RES_METAL) {\n    $result = [];\n    foreach ($this->getShipListArray() as $shipId => $shipAmount) {\n      $result[$shipId] = getStackableUnitsCost([$shipId => 1], $resourceId);\n    }\n\n    return $result;\n  }\n\n  /**\n   * Get cost of single ship in metal\n   *\n   * @param int $shipId\n   *\n   * @return int|float\n   */\n  public function getShipCostInMetal($shipId) {\n    return getStackableUnitsCost([$shipId => 1], RES_METAL);\n//\n//    if(!isset(static::getUnitInfo($shipId)[P_COST_METAL])) {\n//      static::$shipInfo[$shipId][P_COST_METAL] = get_unit_cost_in(static::getUnitInfo($shipId)[P_COST], RES_METAL);\n//    }\n//\n//    return static::getUnitInfo($shipId)[P_COST_METAL];\n  }\n\n  /**\n   * Get fleet cost in metal\n   *\n   * @return float|int\n   */\n  public function getCostInMetal() {\n    return getStackableUnitsCost($this->getShipListArray(), RES_METAL);\n//\n//    $result = 0;\n//    foreach($this->getShipList() as $shipId => $amount) {\n//      $result += $amount * $this->getShipCostInMetal($shipId);\n//    }\n//\n//    return $result;\n  }\n\n  /**\n   * Get single ship basic capacity\n   *\n   * @param int $shipId\n   *\n   * @return int|mixed\n   */\n  public function getShipCapacity($shipId) {\n    if (!isset(static::getUnitInfo($shipId)[P_CAPACITY])) {\n      static::$shipInfo[$shipId][P_CAPACITY] = 0;\n    }\n\n    return static::getUnitInfo($shipId)[P_CAPACITY];\n  }\n\n  /**\n   * Get current fleet capacity counting loaded resources and fuel\n   *\n   * @return float|int\n   */\n  public function getCapacityActual() {\n    $result = 0;\n    foreach ($this->getShipListArray() as $shipId => $amount) {\n      $result += $amount * $this->getShipCapacity($shipId);\n    }\n\n    $travelData = $this->getTravelData();\n\n    $result = max(0, $result - array_sum($this->getResourceList()) - $travelData['consumption']);\n\n    return $result;\n  }\n\n  public function isEmpty() {\n    return $this->getShipCount() < 1;\n  }\n\n  // Using RecordFleet functions ---------------------------------------------------------------------------------------\n\n  /**\n   * @param int   $shipSnId\n   * @param float $shipCount\n   *\n   * @throws \\Exception\n   */\n  public function changeShipCount($shipSnId, $shipCount) {\n    $this->_getContainer()->changeShipCount($shipSnId, $shipCount);\n  }\n\n  /**\n   * @param int   $resourceId\n   * @param float $resourceCount\n   *\n   * @throws \\Exception\n   */\n  public function changeResource($resourceId, $resourceCount) {\n    $this->_getContainer()->changeResource($resourceId, $resourceCount);\n  }\n\n  /**\n   * @return float[] - [shipSnId => $shipAmount]\n   */\n  public function getShipListArray() {\n    return $this->_getContainer()->getShipList();\n  }\n\n  /**\n   * @return float[] - [$resourceSnId => $resourceAmount]\n   */\n  public function getResourceList() {\n    return $this->_getContainer()->getResourceList();\n  }\n\n  /**\n   * @return float|int\n   */\n  public function getShipCount() {\n    return array_sum($this->getShipListArray());\n  }\n\n  /**\n   * @param float $multiplier\n   *\n   * @return int[]|float[]\n   */\n  public function calcShipLossByMultiplier($multiplier) {\n    $result = [];\n\n    foreach ($this->getShipListArray() as $unit_id => $unit_amount) {\n      $shipsLost        = ceil($unit_amount * $multiplier);\n      $result[$unit_id] += $shipsLost;\n    }\n\n    return $result;\n  }\n\n  /**\n   * @param int $missionId\n   *\n   * @return Fleet\n   */\n  public function setMission($missionId) {\n    $this->fleet_mission = $missionId;\n    $this->status        = FLEET_STATUS_FLYING;\n\n    return $this;\n  }\n\n  /**\n   * @param array $playerRecord\n   *\n   * @return Fleet\n   */\n  public function setFleetOwnerRecord($playerRecord) {\n    $this->ownerRecord = $playerRecord;\n    !empty($this->ownerRecord['id']) ? $this->ownerId = $this->ownerRecord['id'] : false;\n\n    return $this;\n  }\n\n  /**\n   * @return array|false|null\n   */\n  public function getFleetOwnerRecord() {\n    if (!isset($this->ownerRecord['id']) && !empty($this->ownerId)) {\n      // Trying to get owner record by id\n      empty($this->ownerRecord = db_user_by_id($this->ownerId)) ? $this->ownerRecord = null : false;\n    }\n\n    return $this->ownerRecord;\n  }\n\n  /**\n   * @param array $from\n   *\n   * @return Fleet\n   */\n  public function setSourceFromPlanetRecord($from) {\n    empty($this->ownerId) && !empty($from['id_owner']) ? $this->ownerId = $from['id_owner'] : false;\n\n    $this->fleet_start_planet_id = !empty($from['id']) && intval($from['id']) ? $from['id'] : null;\n\n    $this->fleet_start_galaxy = $from['galaxy'];\n    $this->fleet_start_system = $from['system'];\n    $this->fleet_start_planet = $from['planet'];\n    $this->fleet_start_type   = $from['planet_type'];\n\n    $this->sourcePlanet = $from;\n\n    return $this;\n  }\n\n\n  /**\n   * @param $to\n   *\n   * @return Fleet\n   */\n  public function setDestinationFromPlanetRecord($to) {\n    empty($this->fleet_target_owner) ? $this->fleet_target_owner = !empty($to['id_owner']) && intval($to['id_owner']) ? $to['id_owner'] : 0 : false;\n\n    $this->fleet_end_planet_id = !empty($to['id']) && intval($to['id']) ? $to['id'] : null;\n\n    $this->fleet_end_galaxy = $to['galaxy'];\n    $this->fleet_end_system = $to['system'];\n    $this->fleet_end_planet = $to['planet'];\n    $this->fleet_end_type   = $to['planet_type'];\n\n    return $this;\n  }\n\n  /**\n   * @param array $fleet - list of units [(int)unitId => (float)unitAmount]\n   *\n   * @return Fleet\n   * @throws \\Exception\n   */\n  public function setUnits($fleet) {\n    foreach ($fleet as $unit_id => $amount) {\n      if (!$amount || !$unit_id) {\n        continue;\n      }\n\n      if (in_array($unit_id, sn_get_groups('fleet'))) {\n        /** @noinspection PhpUnhandledExceptionInspection */\n        $this->changeShipCount($unit_id, $amount);\n      } elseif (in_array($unit_id, sn_get_groups('resources_loot'))) {\n        /** @noinspection PhpUnhandledExceptionInspection */\n        $this->changeResource($unit_id, $amount);\n      }\n    }\n\n    return $this;\n  }\n\n  /**\n   * @param int $speedPercentTenth - fleet speed percent in 10% from 1..10 - i.e. 1 = 10%, 10 = 100%\n   *\n   * @return Fleet\n   */\n  public function setSpeedPercentInTenth($speedPercentTenth) {\n    $this->speedPercentTenth = max(0, min(10, intval($speedPercentTenth)));\n\n    return $this;\n  }\n\n  /**\n   * @param int $launchTime   - unix timestamp when fleet leave source planet\n   * @param int $stayDuration - seconds how long fleet should stay executing mission task (i.e. HOLD or EXPLORE)\n   *\n   * @return array\n   */\n  public function calcTravelTimes($launchTime = SN_TIME_NOW, $stayDuration = 0) {\n    $this->timeLaunch = $launchTime;\n\n    $travel_data = $this->getTravelData();\n\n    $this->timeArrive  = $this->timeLaunch + $travel_data['duration'];\n    $this->timeEndStay = $this->fleet_mission == MT_EXPLORE || $this->fleet_mission == MT_HOLD ? $this->timeArrive + $stayDuration : 0;\n    $this->timeReturn  = $this->timeArrive + $stayDuration + $travel_data['duration'];\n\n    return $travel_data;\n  }\n\n\n  public function save() {\n    return parent::save(); // TODO: Change the autogenerated stub\n  }\n\n  /**\n   * @return array\n   */\n  protected function getTravelData() {\n    $travel_data = flt_travel_data(\n      $this->getFleetOwnerRecord(),\n      ['galaxy' => $this->fleet_start_galaxy, 'system' => $this->fleet_start_system, 'planet' => $this->fleet_start_planet,],\n      ['galaxy' => $this->fleet_end_galaxy, 'system' => $this->fleet_end_system, 'planet' => $this->fleet_end_planet,],\n      $this->getShipListArray(),\n      $this->speedPercentTenth\n    );\n\n    return $travel_data;\n  }\n\n}\n"
  },
  {
    "path": "classes/Fleet/FleetDispatchEvent.php",
    "content": "<?php /** @noinspection PhpUnnecessaryCurlyVarSyntaxInspection, PhpCastIsUnnecessaryInspection, PhpDeprecationInspection */\n\n/** Created by Gorlum 27.10.2024 18:42 */\n\nnamespace Fleet;\n\nuse mysqli_result;\nuse Planet\\DBStaticPlanet;\nuse SN;\n\n/**\n * @property array      $fleet          Fleet on which event time happens\n * @property string     $eventTimeStamp When event happened in timeline\n * @property string     $event          Event type\n *\n * @property int        $fleetId\n * @property ?int       $srcPlanetId\n * @property bool|array $srcPlanetRow\n * @property ?int       $fleetOwnerId\n * @property ?int       $dstPlanetId\n * @property bool|array $dstPlanetRow\n * @property ?int       $dstPlanetOwnerId\n *\n * @property array      $missionInfoNew\n * @property int        $missionId\n */\nclass FleetDispatchEvent {\n  const IS_ATTACK = 'isAttack';\n  const IS_TRANSPORT = 'isTransport';\n\n  const F_FLEET_ID = 'fleet_id';\n  const F_FLEET_OWNER_ID = 'fleet_owner';\n  const F_PLANET_ID = 'id';\n  const F_PLANET_OWNER_ID = 'id_owner';\n  const IS_LOCK_SOURCE = 'isLockSource';\n\n  /** @var array[] $sn_groups_mission */\n  public static $sn_groups_mission = [];\n  /** @var int[] $userIdsToLock */\n  public $userIdsToLock = [];\n  /** @var int[] $planetIdsToLock */\n  public $planetIdsToLock = [];\n  /** @var int[] $fleetIdsToLock */\n  public $fleetIdsToLock = [];\n  /** @var int $processedIPR Processed IPR on this run */\n  public static $processedIPR = -1;\n\n  public static $MISSIONS = [\n    MT_ATTACK    => [self::IS_TRANSPORT => false, self::IS_ATTACK => true,],\n    MT_AKS       => [self::IS_TRANSPORT => false, self::IS_ATTACK => true,],\n    MT_DESTROY   => [self::IS_TRANSPORT => false, self::IS_ATTACK => true,],\n    MT_HOLD      => [self::IS_TRANSPORT => false,],\n    MT_SPY       => [self::IS_TRANSPORT => false, self::IS_LOCK_SOURCE => true, 'AJAX' => true,],\n    MT_TRANSPORT => [self::IS_TRANSPORT => true, self::IS_LOCK_SOURCE => true,],\n    MT_RELOCATE  => [self::IS_TRANSPORT => true, self::IS_LOCK_SOURCE => true,],\n    MT_RECYCLE   => [self::IS_TRANSPORT => false, 'AJAX' => true,],\n    MT_EXPLORE   => [self::IS_TRANSPORT => false,],\n    MT_COLONIZE  => [self::IS_TRANSPORT => true,],\n    MT_MISSILE   => [self::IS_TRANSPORT => false, 'AJAX' => true,],\n  ];\n\n  public function __construct($fleetRow, $eventType) {\n    if (empty(self::$sn_groups_mission)) {\n      self::$sn_groups_mission = sn_get_groups('missions');\n    }\n\n    $this->event = $eventType;\n    $this->fleet = $fleetRow;\n\n    $this->fleetId = $fleetRow[self::F_FLEET_ID];\n\n    $this->fleetOwnerId = $fleetRow[self::F_FLEET_OWNER_ID];\n\n    $this->missionId      = (int)$this->fleet[FleetDispatcher::F_FLEET_MISSION];\n    $this->missionInfoNew = self::$MISSIONS[$this->missionId];\n\n    $this->eventTimeStamp = (int)$this->fleet[$this->event === EVENT_FLT_ARRIVE ? 'fleet_start_time' :\n      ($this->event === EVENT_FLT_ACCOMPLISH ? 'fleet_end_stay' : /* EVENT_FLT_RETURN */\n        'fleet_end_time')];\n\n    /** @var Fleet $fleetObject */\n    $fleetObject = (object)$this->fleet;\n\n    // Always locking flying fleet and fleet owner\n    $this->userIdsToLock   = [(int)$fleetObject->fleet_owner ?: 0 => true,];\n    $this->fleetIdsToLock  = [(int)$fleetObject->fleet_id => true,];\n    $this->planetIdsToLock = [];\n\n    // Some locks make sense only on specific event types\n    if ($this->event === EVENT_FLT_RETURN) {\n      // There is no means or ways how returning fleet can influence other planet(s) then starting one\n      $isLockSource = true;\n    } else {\n      // Locking destination planet always\n      // There are no means or sense to make a mission which does not affect destination planet\n      $this->getDstPlanetRowFromFleet();\n      // Locking dest planet\n      $this->planetIdsToLock[$this->dstPlanetId] = true;\n      // Locking destination planet always implies locking destination user\n      // Locking planet owner ID\n      $this->userIdsToLock[$this->dstPlanetOwnerId] = true;\n\n      // If the fleet is a part of existing fleet group - locking also fleets in group and their respected owners\n      // Only doing it for EVENT_FLT_ARRIVE - currently no other event types does trigger fleet group actions\n      if (!empty($fleetObject->fleet_group) && $this->event === EVENT_FLT_ARRIVE) {\n        $fleetGroupId = (int)$fleetObject->fleet_group;\n        foreach (DbFleetStatic::db_fleet_list(\"`fleet_group` = {$fleetGroupId}\") as $fleetInGroup) {\n          $this->userIdsToLock[(int)$fleetInGroup[self::F_FLEET_OWNER_ID] ?: 0] = true;\n          $this->fleetIdsToLock[(int)$fleetInGroup[self::F_FLEET_ID] ?: 0]      = true;\n        }\n      }\n\n      // We may need to lock source planet/user for some extra info such as planet name/username, coordinates etc\n      $isLockSource = !empty($this->missionInfoNew[self::IS_LOCK_SOURCE]);\n    }\n\n    if ($isLockSource) {\n      $this->getSrcPlanetRowFromFleet();\n      $this->planetIdsToLock[$this->srcPlanetId] = true;\n      $this->userIdsToLock[$this->fleetOwnerId]  = true;\n    }\n\n    if (!empty($this->missionInfoNew[self::IS_ATTACK])) {\n      $fleetsOnHold = DbFleetStatic::fleet_list_on_hold(\n        $fleetObject->fleet_end_galaxy,\n        $fleetObject->fleet_end_system,\n        $fleetObject->fleet_end_planet,\n        $fleetObject->fleet_end_type,\n        $this->eventTimeStamp\n      );\n      foreach ($fleetsOnHold as $aFleet) {\n        $this->userIdsToLock[(int)$aFleet[self::F_FLEET_OWNER_ID] ?: 0] = true;\n        $this->fleetIdsToLock[(int)$aFleet[self::F_FLEET_ID] ?: 0]      = true;\n      }\n    }\n\n    unset($this->userIdsToLock[0]);\n    unset($this->planetIdsToLock[0]);\n    unset($this->fleetIdsToLock[0]);\n\n    $this->userIdsToLock   = array_keys($this->userIdsToLock);\n    $this->planetIdsToLock = array_keys($this->planetIdsToLock);\n    $this->fleetIdsToLock  = array_keys($this->fleetIdsToLock);\n  }\n\n  /**\n   * LOCK - Lock all records which can be used with mission\n   *\n   * @return array|bool|mysqli_result|null\n   */\n  public function lockEventRecords() {\n    $locks = [];\n\n    if (!empty($this->userIdsToLock)) {\n      $locks['users'] = $this->userIdsToLock;\n    }\n    if (!empty($this->planetIdsToLock)) {\n      $locks['planets'] = $this->planetIdsToLock;\n    }\n    if (!empty($this->fleetIdsToLock)) {\n      $locks['fleets'] = $this->fleetIdsToLock;\n    }\n    return SN::$gc->db->lockRecords($locks);\n  }\n\n  public static function sortEvents(&$eventList) {\n    uasort($eventList, function (FleetDispatchEvent $a, FleetDispatchEvent $b) {\n      return\n        // Сравниваем время флотов - кто раньше, тот и первый обрабатывается\n          $a->eventTimeStamp > $b->eventTimeStamp ? 1 : ($a->eventTimeStamp < $b->eventTimeStamp ? -1 :\n          // Если время - одинаковое, сравниваем события флотов\n          // Если события - одинаковые, то флоты равны\n          ($a->event == $b->event ? 0 :\n            // Если события разные - первыми считаем прибывающие флоты\n            ($a->event == EVENT_FLT_ARRIVE ? 1 : ($b->event == EVENT_FLT_ARRIVE ? -1 :\n              // Если нет прибывающих флотов - дальше считаем флоты, которые закончили миссию\n              ($a->event == EVENT_FLT_ACCOMPLISH ? 1 : ($b->event == EVENT_FLT_ACCOMPLISH ? -1 :\n                // Если нет флотов, закончивших задание - остались возвращающиеся флоты, которые равны между собой\n                // TODO: Добавить еще проверку по ID флота и/или времени запуска - что бы обсчитывать их в порядке запуска\n                (\n                0 // Вообще сюда доходить не должно - будет отсекаться на равенстве событий\n                )\n              ))\n            ))\n          )\n        );\n    });\n  }\n\n  /**\n   */\n  public function refreshMissionData() {\n    if (!empty($this->srcPlanetId) && !empty($this->fleetOwnerId)) {\n//       $this->srcPlanetRow = DBStaticPlanet::db_planet_by_vector($this->fleet, 'fleet_start_');\n      $updateResult = sys_o_get_updated($this->fleetOwnerId, $this->srcPlanetId, $this->eventTimeStamp);\n\n      $this->updateSrcPlanetRow($updateResult['planet']);\n    }\n\n    if (!empty($this->dstPlanetId) && !empty($this->dstPlanetOwnerId)) {\n      $updateResult = sys_o_get_updated($this->dstPlanetOwnerId, $this->dstPlanetId, $this->eventTimeStamp);\n\n      $this->updateDstPlanetRow($updateResult['planet']);\n    }\n  }\n\n  /**\n   * @return array|false\n   */\n  public function refreshFleet() {\n    return $this->fleet = DbFleetStatic::db_fleet_get($this->fleetId);\n  }\n\n  public function getSrcPlanetRowFromFleet() {\n    $this->srcPlanetRow = DBStaticPlanet::db_planet_by_vector($this->fleet, 'fleet_start_');\n\n    $this->updateSrcPlanetRow($this->srcPlanetRow);\n\n    return $this->srcPlanetRow;\n  }\n\n  /**\n   * @param array $srcPlanetRow\n   *\n   * @return array|bool\n   */\n  public function updateSrcPlanetRow($srcPlanetRow) {\n//    $this->srcPlanetRow = is_array($srcPlanetRow) ? $srcPlanetRow : DBStaticPlanet::db_planet_by_vector($this->fleet, 'fleet_start_');\n    $this->srcPlanetRow = $srcPlanetRow;\n\n    $this->srcPlanetId = !empty($this->srcPlanetRow[self::F_PLANET_ID]) ? (int)$this->srcPlanetRow[self::F_PLANET_ID] : 0;\n    // Starting planet can change owner while fleet mission - and even change planet ID\n    // It can happen due to teleport shenanigans or because of planet capturing (in certain game modes)\n    $this->fleetOwnerId = !empty($this->srcPlanetRow[self::F_PLANET_OWNER_ID]) ? (int)$this->srcPlanetRow[self::F_PLANET_OWNER_ID] : $this->fleetOwnerId;\n\n    return $this->srcPlanetRow;\n  }\n\n\n  public function getDstPlanetRowFromFleet() {\n    $this->dstPlanetRow = DBStaticPlanet::db_planet_by_vector($this->fleet, 'fleet_end_');\n\n    $this->updateDstPlanetRow($this->dstPlanetRow);\n\n    return $this->dstPlanetRow;\n  }\n\n  /**\n   * @param ?array $dstPlanetRow\n   *\n   * @return array|bool|null\n   */\n  public function updateDstPlanetRow($dstPlanetRow = null) {\n    $this->dstPlanetRow = $dstPlanetRow;\n\n    $this->dstPlanetId = !empty($this->dstPlanetRow[self::F_PLANET_ID]) ? (int)$this->dstPlanetRow[self::F_PLANET_ID] : 0;\n    // Retrieving destination owner ID\n    $this->dstPlanetOwnerId = !empty($this->dstPlanetRow[self::F_PLANET_OWNER_ID]) ? (int)$this->dstPlanetRow[self::F_PLANET_OWNER_ID] : 0;\n\n    return $this->dstPlanetRow;\n  }\n\n}\n"
  },
  {
    "path": "classes/Fleet/FleetDispatcher.php",
    "content": "<?php /** @noinspection PhpUnnecessaryCurlyVarSyntaxInspection */\n/** @noinspection PhpDeprecationInspection */\n\n/**\n * Created by Gorlum 15.06.2017 4:12\n */\n\nnamespace Fleet;\n\nuse Core\\Scheduler\\Lock;\nuse DBAL\\db_mysql;\nuse SN;\nuse debug;\nuse classConfig;\nuse Core\\GlobalContainer;\n\n/**\n * Class Fleet\\FleetDispatcher\n *\n */\nclass FleetDispatcher {\n  const TASK_COMPLETE = 0;\n//  const TASK_TERMINATED = 1;\n\n//  const F_FLEET_EVENT = 'fleet_event';\n  const F_FLEET_MISSION = 'fleet_mission';\n//  /** @var array[] $fleet_list */\n//  public static $fleet_list = [];\n  /** @var FleetDispatchEvent[] $fleet_event_list */\n  public static $fleet_event_list = [];\n  /** @var int[] $missions_used */\n  public $missions_used = [];\n  /**\n   * @var GlobalContainer $gc\n   */\n  protected $gc;\n\n  /**\n   * @var classConfig $gameConfig\n   */\n  protected $gameConfig;\n\n  /**\n   * @var debug $debug\n   */\n  protected $debug;\n\n  public function __construct(GlobalContainer $gc) {\n    $this->gc = $gc;\n\n    $this->gameConfig = $gc->config;\n    $this->debug      = $gc->debug;\n  }\n\n//  /**\n//   * @deprecated\n//   */\n//  public function dispatch() {\n//    if (\n//      SN::$options[PAGE_OPTION_FLEET_UPDATE_SKIP]\n//      ||\n//      SN::gameIsDisabled()\n//      ||\n//      !$this->getLockOld()\n//    ) {\n//      return;\n//    }\n//\n//    $this->flt_flying_fleet_handler();\n//\n//    $this->releaseLock();\n//\n//    set_time_limit(60);\n//  }\n\n\n  /**\n   * @return bool\n   * @deprecated\n   */\n  protected function getLockOld() {\n    db_mysql::db_transaction_start();\n\n    // Watchdog timer\n    if ($this->gameConfig->db_loadItem('fleet_update_lock')) {\n//      var_dump($this->gameConfig->db_loadItem('fleet_update_lock'));\n//      var_dump(SN_TIME_NOW - strtotime($this->gameConfig->fleet_update_lock));\n//      if (SN_TIME_NOW - strtotime($this->gameConfig->fleet_update_lock) <= mt_rand(90, 120)) {\n      if (SN_TIME_NOW - strtotime($this->gameConfig->fleet_update_lock) <= mt_rand(20, 40)) {\n        db_mysql::db_transaction_rollback();\n\n        return false;\n      } else {\n        $this->debug->warning('Fleet dispatcher was locked too long - watchdog unlocked', 'FFH Error', 504);\n      }\n    }\n\n    $this->gameConfig->db_saveItem('fleet_update_last', SN_TIME_SQL);\n    $this->gameConfig->db_saveItem('fleet_update_lock', SN_TIME_SQL);\n    db_mysql::db_transaction_commit();\n\n    return true;\n  }\n\n  /**\n   * @deprecated\n   */\n  protected function releaseLock() {\n    db_mysql::db_transaction_start();\n    $this->gameConfig->db_saveItem('fleet_update_lock', '');\n    db_mysql::db_transaction_commit();\n  }\n\n\n  // ------------------------------------------------------------------\n\n  public static $log = [];\n\n  public static function mark($message = '') {\n    static $mt;\n\n    if(empty($mt)) {\n      $mt = microtime(true);\n    }\n\n    $newMt       = microtime(true);\n    self::$log[] = $message . ' ' . number_format($newMt - $mt, 5);\n    $mt = $newMt;\n  }\n\n  /**\n   * @return int|int[]\n   */\n  public function flt_flying_fleet_handler() {\n//    $this->log_file('Dispatch started');\n    $watchdog = new FleetWatchdog();\n    if (($result = $watchdog->acquireLock()) == FleetWatchdog::TASK_ALREADY_LOCKED) {\n      return $result;\n    }\n\n    $result = ['code' => self::TASK_COMPLETE];\n\n    set_time_limit(max(3, SN::$gc->config->fleet_update_max_run_time - 3));\n\n    //log_file('Начинаем обсчёт флотов');\n\n//    $this->log_file('Обсчёт ракет');\n    FleetDispatchEvent::$processedIPR = coe_o_missile_calculate();\n\n    // Filling self::$fleet_event_list with FleetDispatchEvent\n    self::$fleet_event_list = $this->getFleetEvents();\n    $this->loadMissionFiles();\n\n    $sn_groups_mission = sn_get_groups('missions');\n    foreach (self::$fleet_event_list as $fleetEvent) {\n      $result['code'] = $watchdog->begin($fleetEvent);\n      if ($result['code'] === FleetWatchdog::TASK_TOO_LONG) {\n        $result['message'] = $watchdog->getTerminationMessage();\n        break;\n      } elseif ($result['code'] === FleetWatchdog::FLEET_IS_EMPTY) {\n        continue;\n      }\n\n      // TODO crouch\n      if (\n        in_array($fleetEvent->missionId, [MT_HOLD, MT_EXPLORE])\n        && $fleetEvent->event == EVENT_FLT_ARRIVE\n      ) {\n        $this->mark('skipping ' . $fleetEvent->event . ' ' . $fleetEvent->missionId);\n        continue;\n      }\n\n      db_mysql::db_transaction_start();\n      // Locking further fleet dispatcher tasks\n      SN::$gc->config->pass()->fleet_update_last = date(FMT_DATE_TIME_SQL, time());\n\n      // Locking all event-related records\n      $fleetEvent->lockEventRecords();\n\n      // Refreshing fleet record\n      if (empty($fleetEvent->refreshFleet())) {\n        // Fleet was destroyed in course of previous actions\n        db_mysql::db_transaction_commit();\n        continue;\n      } elseif ($fleetEvent->event == EVENT_FLT_RETURN && $fleetEvent->fleet['fleet_mess'] == FLEET_STATUS_RETURNING) {\n        // Fleet returns to planet\n        RestoreFleetToPlanet($fleetEvent->fleet, true, false, true);\n        db_mysql::db_transaction_commit();\n        continue;\n      } elseif ($fleetEvent->event == EVENT_FLT_ARRIVE && $fleetEvent->fleet['fleet_mess'] != FLEET_STATUS_FLYING) {\n        // При событии EVENT_FLT_ARRIVE флот всегда должен иметь fleet_mess == 0 / FLEET_STATUS_FLYING\n        // В противном случае это означает, что флот уже был обработан ранее - например, при САБе\n        db_mysql::db_transaction_commit();\n        continue;\n      }\n\n      // From now on we have only events of types [EVENT_FLT_ARRIVE, EVENT_FLT_ACCOMPLISH] and $fleet_row['fleet_mess'] == FLEET_STATUS_FLYING (0)\n\n      // Here we refresh dstPlanetRow (by calling sys_o_get_updated() and using its result - so below this call we will have actual dst planet/dst user records\n      // In same vein we refresh srcPlanetRow\n      $fleetEvent->refreshMissionData();\n\n      switch ($fleetEvent->missionId) {\n        // Для боевых атак нужно обновлять по САБу и по холду - таки надо возвращать данные из обработчика миссий!\n        case MT_ATTACK:\n        case MT_AKS:\n        case MT_DESTROY:\n          flt_mission_attack($fleetEvent);\n        break;\n\n        case MT_TRANSPORT:\n          flt_mission_transport($fleetEvent);\n        break;\n\n        case MT_HOLD:\n          // TODO - shortcut\n          if ($fleetEvent->event == EVENT_FLT_ACCOMPLISH) {\n          flt_mission_hold($fleetEvent);\n          }\n        break;\n\n        case MT_RELOCATE:\n          flt_mission_relocate($fleetEvent);\n        break;\n\n        case MT_EXPLORE:\n          // TODO - shortcut\n          if ($fleetEvent->event == EVENT_FLT_ACCOMPLISH) {\n          $outcome = new MissionExploreResult();\n          $outcome->flt_mission_explore($fleetEvent);\n          }\n        break;\n\n        case MT_RECYCLE:\n          flt_mission_recycle($fleetEvent);\n        break;\n\n        case MT_COLONIZE:\n          flt_mission_colonize($fleetEvent);\n        break;\n\n        case MT_SPY:\n          require_once(SN_ROOT_PHYSICAL . 'includes/includes/coe_simulator_helpers.php');\n\n          $theMission = MissionEspionage::buildFromArray($fleetEvent);\n          $theMission->flt_mission_spy();\n\n          unset($theMission);\n        break;\n\n        case MT_MISSILE:  // Missiles !!\n        break;\n\n//      default:\n//        doquery(\"DELETE FROM `{{fleets}}` WHERE `fleet_id` = '{$fleet_row['fleet_id']}' LIMIT 1;\");\n//      break;\n      }\n      db_mysql::db_transaction_commit();\n    }\n\n    $watchdog->unlock();\n\n//    $that->log_file('Dispatch finished - NORMAL SHUTDOWN');\n\n    return $result;\n  }\n\n  /**\n   * @param $workTime\n   * @param $eventsProcessed\n   * @param $lastMissionId\n   * @param $lastEventId\n   * @param $lastEventLength\n   * @param $totalEvents\n   */\n  public function logTermination($workTime, $eventsProcessed, $lastMissionId, $lastEventId, $lastEventLength, $totalEvents) {\n    SN::$debug->warning(sprintf(\n      'Flying fleet handler works %1$s (> %2$s) seconds - skip rest. Processed %3$d / %7$d events. Last event: mission %4$s event %6$s (%5$ss)',\n      number_format($workTime, 4),\n      SN::$config->fleet_update_dispatch_time,\n      $eventsProcessed,\n      $lastMissionId ? SN::$lang['type_mission'][$lastMissionId] : '!TERMINATED BY TIMEOUT!',\n      number_format($lastEventLength, 4),\n      $lastEventId ? SN::$lang['fleet_events'][$lastEventId] : '!TERMINATED BY TIMEOUT!',\n      $totalEvents\n    ),\n      'FFH Warning',\n      504\n    );\n  }\n\n  /**\n   * @return Lock\n   */\n  public function buildLock() {\n    return new Lock($this->gc, classConfig::FLEET_UPDATE_RUN_LOCK, SN::$gc->config->fleet_update_max_run_time, 1, classConfig::DATE_TYPE_UNIX);\n  }\n\n  public function getFleetEvents() {\n    $fleet_event_list = [];\n\n    // Gets active fleets on current tick for Flying Fleet Handler\n    $fleet_list_current_tick = DbFleetStatic::db_fleet_list(\n      \"\n        (`fleet_start_time` <= \" . SN_TIME_NOW . \" AND `fleet_mess` = 0)\n        OR\n        (`fleet_end_stay` <= \" . SN_TIME_NOW . \" AND `fleet_end_stay` > 0 AND `fleet_mess` = 0)\n        OR\n        (`fleet_end_time` <= \" . SN_TIME_NOW . \")\n      \", DB_SELECT_PLAIN\n    );\n\n    foreach ($fleet_list_current_tick as $fleet_row) {\n      if ($fleet_row['fleet_start_time'] <= SN_TIME_NOW && $fleet_row['fleet_mess'] == 0) {\n        $fleet_event_list[] = new FleetDispatchEvent($fleet_row, EVENT_FLT_ARRIVE);\n      }\n\n      if ($fleet_row['fleet_end_stay'] > 0 && $fleet_row['fleet_end_stay'] <= SN_TIME_NOW && $fleet_row['fleet_mess'] == 0) {\n        $fleet_event_list[] = new FleetDispatchEvent($fleet_row, EVENT_FLT_ACCOMPLISH);\n      }\n\n      if ($fleet_row['fleet_end_time'] <= SN_TIME_NOW) {\n        $fleet_event_list[] = new FleetDispatchEvent($fleet_row, EVENT_FLT_RETURN);\n      }\n\n      $this->missions_used[$fleet_row[self::F_FLEET_MISSION]] = 1;\n    }\n\n    FleetDispatchEvent::sortEvents($fleet_event_list);\n\n    return $fleet_event_list;\n  }\n\n  /**\n   * @return void\n   */\n  public function loadMissionFiles() {\n    $mission_files = [\n      MT_ATTACK  => 'flt_mission_attack',\n      MT_AKS     => 'flt_mission_attack',\n      MT_DESTROY => 'flt_mission_attack',\n\n      MT_TRANSPORT => 'flt_mission_transport',\n      MT_RELOCATE  => 'flt_mission_relocate',\n      MT_HOLD      => 'flt_mission_hold',\n      MT_SPY       => '',\n      MT_COLONIZE  => 'flt_mission_colonize',\n      MT_RECYCLE   => 'flt_mission_recycle',\n      // MT_MISSILE => 'flt_mission_missile.php',\n      // MT_EXPLORE   => 'flt_mission_explore',\n    ];\n    foreach ($this->missions_used as $mission_id => $cork) {\n      if (!empty($mission_files[$mission_id])) {\n        require_once(SN_ROOT_PHYSICAL . \"includes/includes/{$mission_files[$mission_id]}\" . DOT_PHP_EX);\n      }\n    }\n  }\n\n  /**\n   * @param string $msg\n   *\n   * @noinspection PhpUnused\n   */\n  public function log_file($msg) {\n    file_put_contents(__DIR__ . '/../../.ffh-event.log', date(FMT_DATE_TIME_SQL, time()) . ' ' . $msg . \"\\r\\n\", FILE_APPEND);\n  }\n\n}\n"
  },
  {
    "path": "classes/Fleet/FleetRowObject.php",
    "content": "<?php\n/** Created by Gorlum 09.05.2025 22:39 */\n\nnamespace Fleet;\n\n/**\n * Class FleetRowObject\n * @package Fleet\n *\n * @property int        $fleet_id                 - bigint     -\n * @property int        $fleet_owner              - bigint     - Fleet player owner ID\n * @property int        $fleet_mission            - int        -\n * @property int|string $fleet_amount             - bigint     -\n * @property string     $fleet_array              - mediumtext -\n * @property int        $fleet_start_time         Time when fleet will arrive to destination point. Wrong name - should be `fleet_dst_arrive`\n * @property int|string $fleet_start_planet_id    - bigint     -\n * @property int        $fleet_start_galaxy       - int        -\n * @property int        $fleet_start_system       - int        -\n * @property int        $fleet_start_planet       - int        -\n * @property int        $fleet_start_type         - int        -\n * @property int        $fleet_end_time           Time when fleet will return to source point. Should be `fleet_return_to_src`\n * @property int        $fleet_end_stay           Time when fleet will end its mission on destination point. Should be `fleet_dst_stay_until`\n * @property int|string $fleet_end_planet_id      - bigint     -\n * @property int        $fleet_end_galaxy         - int        -\n * @property int        $fleet_end_system         - int        -\n * @property int        $fleet_end_planet         - int        -\n * @property int        $fleet_end_type           - int        -\n * @property int|string $fleet_resource_metal     - decimal    -\n * @property int|string $fleet_resource_crystal   - decimal    -\n * @property int|string $fleet_resource_deuterium - decimal    -\n * @property int|string $fleet_target_owner       - int        -\n * @property int|string $fleet_group              - varchar    -\n * @property int        $fleet_mess               - int        - Current fleet status: flying to destination; returning\n * @property int        $start_time               Time when fleet launched from source\n *\n */\nclass FleetRowObject {\n\n}\n"
  },
  {
    "path": "classes/Fleet/FleetStatic.php",
    "content": "<?php\n/**\n * Created by Gorlum 21.03.2018 13:27\n */\n\nnamespace Fleet;\n\nuse SN;\nuse Common\\EmptyCountableIterator;\nuse \\DBAL\\DbMysqliResultIterator;\n\nclass FleetStatic {\n\n  /**\n   * @param array $planetIds\n   * @param int   $time\n   *\n   * @return DbMysqliResultIterator|EmptyCountableIterator\n   */\n  public static function dbFleetsOnHoldOnPlanetsByIds($planetIds, $time = SN_TIME_NOW) {\n    if(empty($planetIds) || !is_array($planetIds)) {\n      return new EmptyCountableIterator();\n    }\n\n    return SN::$db->selectIterator(\"SELECT `{{fleets}}`.*\n      FROM `{{fleets}}`\n        LEFT JOIN `{{users}}` ON id = fleet_owner\n      WHERE\n        fleet_end_planet_id IN (\" . implode(',', $planetIds) . \")\n        AND fleet_mess = 0\n        AND fleet_start_time <= \" . $time . \"\n        AND fleet_end_stay >= \" . $time . \"\n      FOR UPDATE\");\n  }\n\n  public static function flt_fleet_speed($user, $fleet, $shipData = []) {\n    if (!is_array($fleet)) {\n      $fleet = array($fleet => 1);\n    }\n\n    $speeds = array();\n    if (!empty($fleet)) {\n      foreach ($fleet as $ship_id => $amount) {\n        if ($amount && in_array($ship_id, sn_get_groups(['fleet', 'missile',]))) {\n          $single_ship_data = !empty($shipData[$ship_id]) ? $shipData[$ship_id] : get_ship_data($ship_id, $user);\n          $speeds[]         = $single_ship_data['speed'];\n        }\n      }\n    }\n\n    return empty($speeds) ? 0 : min($speeds);\n  }\n\n}\n"
  },
  {
    "path": "classes/Fleet/FleetWatchdog.php",
    "content": "<?php\n/** Created by Gorlum 27.10.2024 18:43 */\n\nnamespace Fleet;\n\nuse Core\\Scheduler\\Lock;\nuse SN;\n\nclass FleetWatchdog {\n  /** @var $workBegin */\n  public static $workBegin;\n\n\n  /** @var float $eventStartedAt */\n  public static $eventStartedAt = SN_TIME_NOW;\n//  /** @var float $lastEventEnd */\n//  public static $lastEventEnd = SN_TIME_NOW;\n\n  /** @var int $currentMission */\n  public static $currentMission = MT_NONE;\n  /** @var int $eventsProcessed */\n  public static $eventsProcessed = 0;\n  /** @var int $currentEvent */\n  public static $currentEvent = EVENT_FLEET_NONE;\n  /** @var int $processedIPR Processed IPR on this run */\n  public static $processedIPR = -1;\n\n  const TASK_COMPLETE = 0;\n  const CONTINUE_EXECUTION = 2;\n  const TASK_TOO_LONG = 1;\n  const TASK_ALREADY_LOCKED = -1;\n  const LOCK_ACQUIRED = -2;\n  const FLEET_IS_EMPTY = 'FLEET_EMPTY';\n\n  const EVENT_DISPATCH_STARTED = 'EVENT_DISPATCH_STARTED';\n  /** @var Lock $runLock */\n  private static $runLock;\n\n\n  public function __construct() {\n    // Dispatch started\n    self::$workBegin = microtime(true);\n\n    self::$eventStartedAt  = self::$workBegin;\n    self::$currentMission  = MT_NONE;\n    self::$currentEvent    = EVENT_FLEET_NONE;\n    self::$eventsProcessed = 0;\n  }\n\n  /**\n   * @return int\n   */\n  public function acquireLock() {\n    // Trying to acquire lock for current task\n    self::$runLock = $runLock = SN::$gc->fleetDispatcher->buildLock();\n    if (!$runLock->attemptLock()) {\n      return self::TASK_ALREADY_LOCKED;\n    }\n\n    register_shutdown_function(function () use ($runLock) {\n//      $this->log_file('Shutting down');\n      $timeLock = $runLock->isLocked();\n      if ($timeLock > 0 || $timeLock === 0) {\n        $runLock->unLock(true);\n        $this->logTermination();\n//        $this->log_file('UNLOCKING');\n      }\n//      $this->log_file(SN::$gc->config->pass()->fleet_update_run_lock);\n//      $this->log_file('ALL RELEASED');\n    });\n\n    return self::LOCK_ACQUIRED;\n  }\n\n  public function begin(FleetDispatchEvent $fleetEvent) {\n    // Watchdog timer\n    // If flying fleet handler works more than `fleet_update_dispatch_time` seconds - stopping it\n    // Let next run handle rest of fleets\n// var_dump(microtime(true), self::$workBegin, (microtime(true) - self::$workBegin) >= SN::$config->fleet_update_dispatch_time, $fleetEvent->fleet);\n    if ((microtime(true) - self::$workBegin) >= SN::$config->fleet_update_dispatch_time) {\n      $this->logTermination();\n\n      return FleetWatchdog::TASK_TOO_LONG;\n    }\n\n    if (empty($fleetEvent->fleet)) {\n      // Fleet was destroyed in course of previous actions\n      return FleetWatchdog::FLEET_IS_EMPTY;\n    }\n\n    self::$processedIPR = $fleetEvent::$processedIPR;\n\n    self::$eventStartedAt = microtime(true);\n    self::$currentMission = !empty($fleetEvent->fleet[FleetDispatcher::F_FLEET_MISSION]) ? $fleetEvent->fleet[FleetDispatcher::F_FLEET_MISSION] : MT_NONE;\n    self::$currentEvent   = !empty($fleetEvent->event) ? $fleetEvent->event : MT_NONE;\n    self::$eventsProcessed++;\n\n    return FleetWatchdog::CONTINUE_EXECUTION;\n  }\n\n  /**\n   */\n  public function logTermination() {\n    SN::$debug->warning(\n      $this->getTerminationMessage(),\n      'FFH Warning',\n      504\n    );\n  }\n\n  public function unlock() {\n    self::$runLock->unLock(true);\n  }\n\n  /**\n   * @return string\n   */\n  public function getTerminationMessage() {\n    return sprintf(\n      'Flying fleet handler works %1$s seconds (> %2$s) - skipping rest. Processed %8$d IPRs, %3$d / %7$d events. Last event: mission %4$s event %6$s (%5$ss)',\n      number_format(microtime(true) - self::$workBegin, 4),\n      SN::$config->fleet_update_dispatch_time,\n      self::$eventsProcessed,\n      !empty(SN::$lang['type_mission'][self::$currentMission]) ? SN::$lang['type_mission'][self::$currentMission] : '!TERMINATED BY TIMEOUT!',\n      number_format(microtime(true) - self::$eventStartedAt, 4),\n      !empty(SN::$lang['fleet_events'][self::$currentEvent]) ? SN::$lang['fleet_events'][self::$currentEvent] : '!TERMINATED BY TIMEOUT!',\n      count(FleetDispatcher::$fleet_event_list),\n      self::$processedIPR\n    );\n  }\n\n}\n"
  },
  {
    "path": "classes/Fleet/MissionData.php",
    "content": "<?php\n/**\n * Created by Gorlum 11.10.2017 13:17\n */\n\nnamespace Fleet;\n\n\nuse Core\\GlobalContainer;\n\nclass MissionData {\n\n  /**\n   * @var Fleet $fleetEntity\n   */\n  protected $fleetEntity;\n\n  /** @var array|null $fleet Fleet row from DB */\n  public $fleet;\n  /** @var array|null */\n  public $dstUserRow;\n  /** @var array|null */\n  public $dstPlanetRow;\n  /** @var array|null */\n  public $fleetOwnerRow;\n  /** @var array|null */\n  public $srcPlanetRow;\n  /** @var array|null */\n  public $fleet_event;\n\n  /** @var \\General $general */\n  protected $general;\n  /** @var \\classLocale $lang */\n  protected $lang;\n\n  /**\n   * @param FleetDispatchEvent $fleetEvent\n   *\n   * @return static\n   */\n  public static function buildFromArray($fleetEvent) {\n    return new static($fleetEvent);\n  }\n\n  /**\n   * MissionData constructor.\n   *\n   * @param ?FleetDispatchEvent $fleetEvent\n   */\n  public function __construct($fleetEvent) {\n    $this->general = $this->getDefaultGeneral();\n    $this->lang    = $this->getDefaultLang();\n\n    $this->fromMissionArray($fleetEvent);\n  }\n\n  /**\n   * @param GlobalContainer $gc\n   */\n  public function changeGc($gc) {\n    $this->general = $gc->general;\n  }\n\n  /**\n   * @param ?FleetDispatchEvent $fleetEvent\n   */\n  protected function fromMissionArray($fleetEvent) {\n    $this->fleet         = $fleetEvent->fleet;\n    $this->dstUserRow    = $fleetEvent->dstPlanetOwnerId ? db_user_by_id($fleetEvent->dstPlanetOwnerId) : null;\n    $this->dstPlanetRow  = $fleetEvent->dstPlanetId ? $fleetEvent->dstPlanetRow : null;\n    $this->fleetOwnerRow = $fleetEvent->fleetOwnerId ? db_user_by_id($fleetEvent->fleet['fleet_owner']) : null;\n    $this->srcPlanetRow  = $fleetEvent->srcPlanetId ? $fleetEvent->srcPlanetRow : null;\n    $this->fleet_event   = $fleetEvent->event;\n\n    $this->fleetEntity = new Fleet();\n    $this->fleetEntity->dbLoadRecord($this->fleet['fleet_id']);\n  }\n\n  protected function dbFleetFindRecordById($fleetId) {\n    return RecordFleet::findById($fleetId);\n  }\n\n  /**\n   * @return \\classLocale\n   */\n  protected function getDefaultLang() {\n    return \\SN::$lang;\n  }\n\n  /**\n   * @return \\General\n   */\n  protected function getDefaultGeneral() {\n    return \\SN::$gc->general;\n  }\n\n}\n"
  },
  {
    "path": "classes/Fleet/MissionEspionage.php",
    "content": "<?php\n/**\n * Created by Gorlum 11.10.2017 13:16\n */\n\nnamespace Fleet;\n\nuse SN;\nuse Planet\\DBStaticPlanet;\n\nclass MissionEspionage extends MissionData {\n\n  /**\n   * @var MissionEspionageReport $missionReport\n   */\n  public $missionReport;\n\n  private $target_message = '';\n\n  public function flt_mission_spy() {\n    $lang = SN::$lang;\n    $fleet_array = sys_unit_str2arr($this->fleet['fleet_array']);\n\n    if (isset($this->dstUserRow['id']) && isset($this->dstPlanetRow['id']) && isset($this->fleetOwnerRow['id']) && $fleet_array[SHIP_SPY] >= 1) {\n      // TODO: Наемники, губернаторы, артефакты и прочее имперское\n      $this->doSpying();\n\n      msg_send_simple_message($this->fleetOwnerRow['id'], '', $this->fleet['fleet_start_time'], MSG_TYPE_SPY, $lang['sys_mess_qg'], $lang['sys_mess_spy_report'],\n        json_encode($this->missionReport, JSON_UNESCAPED_UNICODE), STRING_NEED_ESCAPING, false, STRING_IS_JSON_ENCODED);\n\n      $this->target_message = \"{$lang['sys_mess_spy_enemy_fleet']} {$this->srcPlanetRow['name']} \" . uni_render_coordinates_href($this->srcPlanetRow, '', 3);\n      $this->target_message .= \" {$lang['sys_mess_spy_seen_at']} {$this->dstPlanetRow['name']} \" . uni_render_coordinates($this->dstPlanetRow);\n      if ($this->missionReport->isSpyDetected()) {\n        $this->target_message .= \"<br />{$lang['sys_mess_spy_destroyed_enemy']}\";\n      }\n\n      msg_send_simple_message(\n        $this->fleet['fleet_target_owner'],\n        '',\n        $this->fleet['fleet_start_time'],\n        MSG_TYPE_SPY,\n        $lang['sys_mess_spy_control'],\n        $lang['sys_mess_spy_activity'],\n        $this->target_message\n      );\n    }\n\n    $this->dbApplyChanges();\n  }\n\n  protected function scanGroup($group_name) {\n    foreach ($this->general->getGroupsByName($group_name) as $unit_id) {\n      $this->missionReport->addUnit($unit_id, mrc_get_level($this->dstUserRow, $this->dstPlanetRow, $unit_id, false, true));\n    }\n  }\n\n  protected function doSpying() {\n    $this->missionReport = new MissionEspionageReport($this);\n\n    $spy_diff_empire = $this->missionReport->getEmpireSpyDiff();\n    $planetSpyDiff = $this->missionReport->getPlanetSpyDiff();\n\n    if ($planetSpyDiff >= 2) {\n      $this->scanGroup('fleet');\n    }\n    if ($planetSpyDiff >= 3) {\n      $this->scanGroup('defense');\n    }\n    if ($planetSpyDiff >= 5) {\n      $this->scanGroup('structures');\n    }\n\n    if ($spy_diff_empire >= 0) {\n      $this->scanGroup('tech');\n    }\n\n    // Launching detection calculations\n    $this->missionReport->isSpyDetected();\n  }\n\n  protected function dbApplyChanges() {\n    if (is_object($this->missionReport) && $this->missionReport->isSpyDetected()) {\n      DbFleetStatic::db_fleet_delete($this->fleet['fleet_id']);\n\n      $debris_planet_id = $this->dstPlanetRow['planet_type'] == PT_PLANET ? $this->dstPlanetRow['id'] : $this->dstPlanetRow['parent_planet'];\n\n      $spy_cost = get_unit_param(SHIP_SPY, P_COST);\n\n      DBStaticPlanet::db_planet_set_by_id($debris_planet_id,\n        \"`debris_metal` = `debris_metal` + \" . floor($this->missionReport->getProbesNumber() * $spy_cost[RES_METAL] * 0.3) . \", `debris_crystal` = `debris_crystal` + \" . floor($this->missionReport->getProbesNumber() * $spy_cost[RES_CRYSTAL] * 0.3));\n    } else {\n      DbFleetStatic::fleet_send_back($this->fleet);\n    }\n  }\n\n}\n"
  },
  {
    "path": "classes/Fleet/MissionEspionageReport.php",
    "content": "<?php\n/**\n * Created by Gorlum 12.10.2017 13:10\n */\n\nnamespace Fleet;\n\nuse Common\\Traits\\TJsonSerializable;\n\n\n/**\n * Class MissionEspionageReport\n *\n * Result of MISSION_SPY\n *\n * @package Fleet\n */\nclass MissionEspionageReport {\n  use TJsonSerializable;\n\n  const SIMULATOR_GROUPS = [UNIT_SHIPS, UNIT_DEFENCE];\n  const SIMULATOR_UNITS = [TECH_WEAPON, TECH_SHIELD, TECH_ARMOR, RES_METAL, RES_CRYSTAL, RES_DEUTERIUM];\n\n  /**\n   * Actual report time\n   *\n   * @var float $reportTime\n   */\n  public $reportTime = 0.0;\n  /**\n   * Fleet arrival time - i.e. when report supported to be made\n   * Can differ from actual spy time due to delays in fleet dispatcher routines\n   *\n   * @var int $fleetTime\n   */\n  public $fleetTime = 0;\n\n  public $attackerPlayerId = 0;\n  public $attackerPlayerName = '';\n  public $attackerPlayerAllyTag = '';\n  public $attackerPlanetId = 0;\n  public $attackerPlanetName = '';\n  public $attackerPlanetGalaxy = 0;\n  public $attackerPlanetSystem = 0;\n  public $attackerPlanetPlanet = 0;\n  public $attackerPlanetPlanetType = PT_NONE;\n\n  public $targetPlayerId = 0;\n  public $targetPlayerName = '';\n  public $targetPlayerAllyTag = '';\n  public $targetPlanetId = 0;\n  public $targetPlanetName = '';\n  public $targetPlanetGalaxy = 0;\n  public $targetPlanetSystem = 0;\n  public $targetPlanetPlanet = 0;\n  public $targetPlanetPlanetType = PT_NONE;\n\n  public $targetSpyLevel = 0;\n  public $attackerSpyLevel = 0;\n\n  public $fleetUnits = [];\n\n  public $spiedUnits = [];\n\n  private $simulatorLink = '';\n\n  /**\n   * Chance for target to detect spying fleet\n   *\n   * @var null|float $detectionChance\n   */\n//  public $detectionChance = null;\n  public $rolledChance = null;\n\n  public $enemyShips = 0;\n\n  /**\n   * MissionEspionageReport constructor.\n   *\n   * @param MissionData $missionData\n   */\n  public function __construct(MissionData $missionData) {\n    $this->reportTime = microtime(true);\n    $this->fleetTime = $missionData->fleet['fleet_end_time'];\n\n    $this->attackerPlayerId = $missionData->fleetOwnerRow['id'];\n    $this->attackerPlayerName = $missionData->fleetOwnerRow['username'];\n    $this->attackerPlayerAllyTag = $missionData->fleetOwnerRow['ally_tag'];\n    $this->attackerPlanetId = $missionData->srcPlanetRow['id'];\n    $this->attackerPlanetName = $missionData->srcPlanetRow['name'];\n    $this->attackerPlanetGalaxy = intval($missionData->srcPlanetRow['galaxy']);\n    $this->attackerPlanetSystem = intval($missionData->srcPlanetRow['system']);\n    $this->attackerPlanetPlanet = intval($missionData->srcPlanetRow['planet']);\n    $this->attackerPlanetPlanetType = intval($missionData->srcPlanetRow['planet_type']);\n\n    $this->targetPlayerId = $missionData->dstUserRow['id'];\n    $this->targetPlayerName = $missionData->dstUserRow['username'];\n    $this->targetPlayerAllyTag = $missionData->dstUserRow['ally_tag'];\n    $this->targetPlanetId = $missionData->dstPlanetRow['id'];\n    $this->targetPlanetName = $missionData->dstPlanetRow['name'];\n    $this->targetPlanetGalaxy = intval($missionData->dstPlanetRow['galaxy']);\n    $this->targetPlanetSystem = intval($missionData->dstPlanetRow['system']);\n    $this->targetPlanetPlanet = intval($missionData->dstPlanetRow['planet']);\n    $this->targetPlanetPlanetType = intval($missionData->dstPlanetRow['planet_type']);\n\n    $this->targetSpyLevel = intval(GetSpyLevel($missionData->dstUserRow));\n    $this->attackerSpyLevel = intval(GetSpyLevel($missionData->fleetOwnerRow));\n\n    $this->fleetUnits = sys_unit_str2arr($missionData->fleet['fleet_array']);\n\n    $this->spiedUnits[RES_METAL] = floor($missionData->dstPlanetRow['metal']);\n    $this->spiedUnits[RES_CRYSTAL] = floor($missionData->dstPlanetRow['crystal']);\n    $this->spiedUnits[RES_DEUTERIUM] = floor($missionData->dstPlanetRow['deuterium']);\n    $this->spiedUnits[RES_ENERGY] = floor($missionData->dstPlanetRow['energy_max']);\n\n    $this->enemyShips = 0;\n    foreach (sn_get_groups('fleet') as $unit_id) {\n      $this->enemyShips += max(0, mrc_get_level($missionData->dstUserRow, $missionData->dstPlanetRow, $unit_id, false, true));\n    }\n\n  }\n\n\n  public function getEmpireSpyDiff() {\n    return $this->attackerSpyLevel - $this->targetSpyLevel;\n  }\n\n  /**\n   * @return float|int\n   */\n  public function getProbesNumber() {\n    return !empty($this->fleetUnits[SHIP_SPY]) && $this->fleetUnits[SHIP_SPY] >= 1 ? floor($this->fleetUnits[SHIP_SPY]) : 0;\n  }\n\n  public function getAntiSpyDiff() {\n    $u = ['id' => $this->targetPlayerId];\n    $p = [\n      'id'       => $this->targetPlanetId,\n      'id_owner' => $this->targetPlayerId,\n    ];\n    $onPlanet = mrc_get_level($u, $p, SHIP_SATELLITE_SPUTNIK, false, true);\n\n    return !empty($onPlanet) && $onPlanet >= 1\n      ? floor(pow($onPlanet, 0.52))\n      : 0;\n  }\n\n  public function getPlanetSpyDiff() {\n    return $this->getEmpireSpyDiff() + sqrt($this->getProbesNumber()) - 1 - $this->getAntiSpyDiff();\n  }\n\n  /**\n   * @param int       $unitId\n   * @param int|float $unitAmount\n   */\n  public function addUnit($unitId, $unitAmount) {\n    if (($unitAmount = floor($unitAmount)) >= 1) {\n      $this->spiedUnits[intval($unitId)] = floor($unitAmount);\n      $this->simulatorLink = '';\n    }\n  }\n\n  public function getSimulatorLink() {\n    if (empty($this->simulatorLink)) {\n      $combat_pack[0] = [];\n      foreach ($this->spiedUnits as $unitId => $unitAmount) {\n        $unitGroup = get_unit_param($unitId, P_UNIT_TYPE);\n        if (in_array($unitGroup, static::SIMULATOR_GROUPS) || in_array($unitId, static::SIMULATOR_UNITS)) {\n          $combat_pack[0][$unitId] = $unitAmount;\n        }\n      }\n      $this->simulatorLink = sn_ube_simulator_encode_replay($combat_pack, 'D');\n    }\n\n    return $this->simulatorLink;\n  }\n\n  /**\n   * Chance for target to detect spying fleet\n   *\n   * @return float|null\n   */\n  public function getDetectionTrashold() {\n    return $this->getProbesNumber() * $this->enemyShips / 4 * pow(2, -$this->getEmpireSpyDiff());\n  }\n\n  public function rollChance() {\n    if ($this->rolledChance === null) {\n      $this->rolledChance = mt_rand(0, 99);\n    }\n\n    return $this->rolledChance;\n  }\n\n  public function isSpyDetected() {\n    return $this->rollChance() < $this->getDetectionTrashold();\n//    return $this->getDetectionChance() > 99 || $this->getDetectionTrashold() > $this->getDetectionChance();\n  }\n\n}\n"
  },
  {
    "path": "classes/Fleet/MissionExploreResult.php",
    "content": "<?php /** @noinspection PhpDeprecationInspection */\n\n/** Created by Gorlum 09.05.2025 18:56 */\n\nnamespace Fleet;\n\nuse SN;\n\nclass MissionExploreResult {\n  /** @var string Key in outcome config for roll value */\n  const K_ROLL_VALUE = 'value';\n\n  /** @var int Max DM can be found in 1 expedition */\n  const MAX_DM = CONST_10K;\n\n  /** @var int $valueRolled mt_rand() value rolled [0,max_chance] that determine current expedition outcome */\n  public $valueRolled = Constants::OUTCOME_NOT_CALCULATED;\n  /** @var int $outcome Expedition outcome */\n  public $outcome = Constants::OUTCOME_NOT_CALCULATED;\n  /**@var array $currentOutcomeConfig Current outcome config */\n  public $currentOutcomeConfig = [];\n\n  // Outcomes with variants - sub-outcomes\n  /** @var float $subOutcomeProbability Normalized probability [0,1] of sub-outcome */\n  public $subOutcomeProbability = Constants::OUTCOME_NOT_CALCULATED;\n  /** @var int $subOutcome Secondary outcome (sub-outcome) for variable outcomes */\n  public $subOutcome = Constants::OUTCOME_NOT_CALCULATED;\n  /** @var float $gainShare Share of total resources to gain - depends on sub-outcome */\n  public $gainShare = 0;\n\n  // Units/Resources changes\n  /** @var int[] $shipsFound Ships found during current expedition */\n  public $shipsFound = [];\n  /** @var int[] $shipsLost Ships lost during current expedition */\n  public $shipsLost = [];\n  /** @var float[] $resourcesFound Resource amounts found during current expedition */\n  public $resourcesFound = [];\n  /** @var int $darkMatterFound Dark Matter found during current expedition */\n  public $darkMatterFound = 0;\n\n  // Other variables\n  /** @var array $ships Current list of ships within mission. CAN be changed by outcomes and SHOULD be changed if ships lost/found */\n  public $ships = [];\n  /** @var int $fleetCapacityFree Free fleet capacity left */\n  public $fleetCapacityFree = 0;\n  /** @var int $shipsCostInMetal Total ships cost in metal */\n  public $shipsCostInMetal = 0;\n\n  /** @var ?FleetDispatchEvent $fleetEvent Event currently processed */\n  public $fleetEvent = null;\n\n  /** @var float $timeStart Timestamp with ms when started expedition processing */\n  protected $timeStart = 0;\n\n  /** @var array[] $configs */\n  public static $configs = [\n    Constants::OUTCOME_NONE                       => [\n      Constants::K_OUTCOME      => Constants::OUTCOME_NONE,\n      Constants::K_OUTCOME_TYPE => Constants::OUTCOME_TYPE_NEUTRAL,\n      P_CHANCE                  => Constants::OUTCOME_EXPEDITION_NOTHING_DEFAULT_CHANCE,\n    ],\n    Constants::EXPEDITION_OUTCOME_LOST_FLEET      => [\n      Constants::K_OUTCOME      => Constants::EXPEDITION_OUTCOME_LOST_FLEET,\n      Constants::K_OUTCOME_TYPE => Constants::OUTCOME_TYPE_BAD,\n      P_CHANCE                  => 9,\n    ],\n    Constants::EXPEDITION_OUTCOME_LOST_FLEET_ALL  => [\n      Constants::K_OUTCOME      => Constants::EXPEDITION_OUTCOME_LOST_FLEET_ALL,\n      Constants::K_OUTCOME_TYPE => Constants::OUTCOME_TYPE_BAD,\n      P_CHANCE                  => 3,\n    ],\n    Constants::EXPEDITION_OUTCOME_FOUND_FLEET     => [\n      Constants::K_OUTCOME           => Constants::EXPEDITION_OUTCOME_FOUND_FLEET,\n      Constants::K_OUTCOME_TYPE      => Constants::OUTCOME_TYPE_GOOD,\n      P_CHANCE                       => 200,\n      'percent'                      => [0 => 0.1, 1 => 0.02, 2 => 0.01,],\n      Constants::K_OUTCOME_SECONDARY => [\n        [P_CHANCE => 90, P_MULTIPLIER => 0.01, P_MESSAGE_ID => 2,],\n        [P_CHANCE => 9, P_MULTIPLIER => 0.02, P_MESSAGE_ID => 1,],\n        [P_CHANCE => 1, P_MULTIPLIER => 0.10, P_MESSAGE_ID => 0,],\n      ],\n    ],\n    Constants::EXPEDITION_OUTCOME_FOUND_RESOURCES => [\n      Constants::K_OUTCOME           => Constants::EXPEDITION_OUTCOME_FOUND_RESOURCES,\n      Constants::K_OUTCOME_TYPE      => Constants::OUTCOME_TYPE_GOOD,\n      P_CHANCE                       => 300,\n      'percent'                      => [0 => 0.1, 1 => 0.050, 2 => 0.025,],\n      Constants::K_OUTCOME_SECONDARY => [\n        [P_CHANCE => 90, P_MULTIPLIER => 0.025, P_MESSAGE_ID => 2,],\n        [P_CHANCE => 9, P_MULTIPLIER => 0.050, P_MESSAGE_ID => 1,],\n        [P_CHANCE => 1, P_MULTIPLIER => 0.100, P_MESSAGE_ID => 0,],\n      ],\n    ],\n    Constants::EXPEDITION_OUTCOME_FOUND_DM        => [\n      Constants::K_OUTCOME           => Constants::EXPEDITION_OUTCOME_FOUND_DM,\n      Constants::K_OUTCOME_TYPE      => Constants::OUTCOME_TYPE_GOOD,\n      P_CHANCE                       => 100,\n      'percent'                      => [0 => 0.0100, 1 => 0.0040, 2 => 0.0010,],\n      Constants::K_OUTCOME_SECONDARY => [\n        [P_CHANCE => 90, P_MULTIPLIER => 0.0010, /*P_MESSAGE_ID => 2,*/],\n        [P_CHANCE => 9, P_MULTIPLIER => 0.0040, /*P_MESSAGE_ID => 1,*/],\n        [P_CHANCE => 1, P_MULTIPLIER => 0.0100, /*P_MESSAGE_ID => 0,*/],\n      ],\n    ],\n    /*\n    FLT_EXPEDITION_OUTCOME_FOUND_ARTIFACT => array(\n      'outcome' => FLT_EXPEDITION_OUTCOME_FOUND_ARTIFACT,\n      P_CHANCE => 10,\n    ),\n    */\n  ];\n\n  /** @var array[] $shipData */\n  public static $shipData = [];\n  /** @var float[] $rates Resources exchange rates */\n  public static $rates = [];\n\n  public function __construct() {\n    self::getShipData();\n    self::getExchangeRates();\n  }\n\n  /**\n   * @return int\n   */\n  public function flt_mission_explore(FleetDispatchEvent $fleetEvent) {\n    if ($fleetEvent->event != EVENT_FLT_ACCOMPLISH) {\n      return CACHE_NONE;\n    }\n\n    // Preparing for expedition\n    $this->timeStart = microtime(true);\n\n    $this->resetExpedition($fleetEvent);\n\n    $this->calcSecondaryData();\n\n    // Calculating mission outcome\n\n    // Making a copy of outcome configs to tamper with\n    $outcomeConfigs = static::$configs;\n\n    $outcomeConfigs = $this->adjustNoneChance($outcomeConfigs);\n\n    list($outcomeConfigs, $chance_max) = $this->calculateRollValues($outcomeConfigs);\n\n\n    // Rolling value which wil determine outcome\n    $this->valueRolled = mt_rand(0, ceil($chance_max));\n    // NOTHING => 200, LOST_FLEET => 209, LOST_FLEET_ALL => 212, FOUND_FLEET => 412, RESOURCES => 712, FOUND_DM => 812\n    // $this->valueRolled = 409; // DEBUG  comment!\n    // Determining outcome\n    foreach ($outcomeConfigs as $key1 => $config) {\n      if (!$config[P_CHANCE]) {\n        continue;\n      }\n      $this->outcome        = $key1;\n      if ($this->valueRolled <= $config [self::K_ROLL_VALUE]) {\n        break;\n      }\n    }\n    // Fallback in case something went wrong\n    if ($this->outcome == Constants::OUTCOME_NOT_CALCULATED) {\n      $this->outcome = Constants::OUTCOME_NONE;\n    }\n    $this->currentOutcomeConfig = $outcomeConfigs[$this->outcome];\n\n    // Вычисляем вероятность выпадения данного числа в общем пуле\n    $this->subOutcomeProbability = ($this->currentOutcomeConfig[self::K_ROLL_VALUE] - $this->valueRolled) / $this->currentOutcomeConfig[P_CHANCE];\n    $this->subOutcome = $this->subOutcomeProbability >= 0.99 ? 0 : ($this->subOutcomeProbability >= 0.90 ? 1 : 2);\n    $this->gainShare  = !empty($this->currentOutcomeConfig['percent'][$this->subOutcome])\n      ? $this->currentOutcomeConfig['percent'][$this->subOutcome]\n      : Constants::OUTCOME_NOT_CALCULATED;\n\n    // Outcome CAN change ONLY object properties and SHOULD NOT mess with real fleet values\n    switch ($this->outcome) {\n      case Constants::OUTCOME_NONE:\n        $this->subOutcome = Constants::OUTCOME_NOT_CALCULATED;\n      break;\n\n      case Constants::EXPEDITION_OUTCOME_LOST_FLEET:\n        $this->outcomeShipsLostPartially();\n        $this->subOutcome = Constants::OUTCOME_NOT_CALCULATED;\n      break;\n\n      case Constants::EXPEDITION_OUTCOME_LOST_FLEET_ALL:\n        $this->outcomeLostFleetAll();\n        $this->subOutcome = Constants::OUTCOME_NOT_CALCULATED;\n      break;\n\n      case Constants::EXPEDITION_OUTCOME_FOUND_FLEET:\n        $this->outcomeFoundShips();\n      break;\n\n      case Constants::EXPEDITION_OUTCOME_FOUND_RESOURCES:\n        $this->outcomeFoundResources();\n      break;\n\n      case Constants::EXPEDITION_OUTCOME_FOUND_DM:\n        $this->outcomeFoundDm();\n      break;\n\n      //case FLT_EXPEDITION_OUTCOME_FOUND_ARTIFACT:\n      //break;\n\n      default:\n      break;\n    }\n\n    // Calling extra\n    $this->flt_mission_explore_addon($this);\n\n    // Applying expedition changes to real fleet data\n    $this->applyFleetChanges();\n\n    // Saving expedition result to DB\n    $this->saveResult();\n\n    // Sending expedition report to player\n    $this->sendReport($this->fleetEvent->fleet);\n\n    return CACHE_FLEET | CACHE_USER_SRC;\n  }\n\n  /**\n   * @param array $theFleet\n   *\n   * @return string\n   */\n  protected function renderUnits(array $theFleet) {\n    $add = '';\n    foreach ($theFleet as $ship_id => $ship_amount) {\n      $add .= SN::$lang['tech'][$ship_id] . ' - ' . $ship_amount . \"\\r\\n\";\n    }\n\n    return $add;\n  }\n\n  /**\n   * @param static $outcome\n   *\n   * @return static\n   */\n  protected function flt_mission_explore_addon(MissionExploreResult $outcome) {\n    /** @see core_festival::expedition_result_adjust(), FestivalActivityPuzzleExpedition::fleet_explore_adjust_result() */\n    return sn_function_call(Constants::HOOK_MISSION_EXPLORE_ADDON, [$outcome]);\n  }\n\n  /**\n   * Reset current expedition state\n   *\n   * @param ?FleetDispatchEvent $fleetEvent\n   *\n   * @return void\n   */\n  protected function resetExpedition(FleetDispatchEvent $fleetEvent = null) {\n    $this->fleetEvent = $fleetEvent;\n\n    // Fleet's ship list\n    $this->ships = !empty($this->fleetEvent->fleet['fleet_array'])\n      ? sys_unit_str2arr($this->fleetEvent->fleet['fleet_array'])\n      : [];\n\n    $this->shipsLost       = [];\n    $this->shipsFound      = [];\n    $this->resourcesFound  = [];\n    $this->darkMatterFound = 0;\n\n    $this->valueRolled           = -1;\n    $this->outcome               = Constants::OUTCOME_NOT_CALCULATED;\n    $this->subOutcomeProbability = -1;\n    $this->subOutcome            = Constants::OUTCOME_NOT_CALCULATED;\n    $this->gainShare             = 0;\n\n    $this->fleetCapacityFree = 0;\n    $this->shipsCostInMetal  = 0;\n  }\n\n  /**\n   * Get data for ships\n   *\n   * @return array[]\n   */\n  protected static function getShipData() {\n    if (empty(static::$shipData)) {\n      foreach (sn_get_groups('fleet') as $unit_id) {\n        $unit_info = get_unit_param($unit_id);\n        if ($unit_info[P_UNIT_TYPE] != UNIT_SHIPS || empty($unit_info['engine'][0]['speed'])) {\n          continue;\n        }\n        $unit_info[P_COST_METAL] = get_unit_cost_in($unit_info[P_COST]);\n\n        static::$shipData[$unit_id] = $unit_info;\n      }\n    }\n\n    return static::$shipData;\n  }\n\n  /**\n   * Get resource exchange rates\n   *\n   * @return float[]\n   */\n  protected static function getExchangeRates() {\n    if (empty(static::$rates)) {\n      static::$rates = SN::$gc->economicHelper->getResourcesExchange();\n    }\n\n    return static::$rates;\n  }\n\n  /**\n   * Saving expedition results to DB\n   *\n   * @return void\n   */\n  protected function saveResult() {\n    // Increasing expedition XP - 1 point per Expedition\n    db_user_set_by_id($this->fleetEvent->fleetOwnerId, \"`player_rpg_explore_xp` = `player_rpg_explore_xp` + 1\");\n    // Saving changed data to DB\n    if ($this->darkMatterFound >= 1) {\n      rpg_points_change($this->fleetEvent->fleetOwnerId, RPG_EXPEDITION, $this->darkMatterFound, 'Expedition Bonus');\n    }\n\n    // Checking if the fleet was destroyed entirely\n    if (($fleetAmount = array_sum($this->ships)) >= 1) {\n      // No - some ships left\n      $query_data =\n        // Routing fleet to return path\n        ['fleet_mess' => FLEET_STATUS_RETURNING]\n        // If there were some changes to the fleet - propagating them to DB\n        + (!empty($this->shipsLost) || !empty($this->shipsFound)\n          ? [\n            'fleet_amount' => $fleetAmount,\n            'fleet_array'  => sys_unit_arr2str($this->ships),\n          ]\n          : []\n        );\n\n      $query_delta = [];\n      // If we found some resources - adding them to cargo bays\n      if (!empty($this->resourcesFound) && array_sum($this->resourcesFound) > 0) {\n        $query_delta = [\n          'fleet_resource_metal'     => $this->resourcesFound[RES_METAL],\n          'fleet_resource_crystal'   => $this->resourcesFound[RES_CRYSTAL],\n          'fleet_resource_deuterium' => $this->resourcesFound[RES_DEUTERIUM],\n        ];\n      }\n\n      DbFleetStatic::fleet_update_set($this->fleetEvent->fleetId, $query_data, $query_delta);\n    } else {\n      // Fleet empty? Removing fleet from DB\n      DbFleetStatic::db_fleet_delete($this->fleetEvent->fleetId);\n    }\n  }\n\n  /**\n   * Compile and send expedition report\n   *\n   * @param array $fleetRow\n   *\n   * @return void\n   */\n  protected function sendReport(array $fleetRow) {\n    // Generating PM for user\n    $langExpeditions = SN::$lang['flt_mission_expedition'];\n    // Generating outcome-specific details\n    $msg_text_addon = '';\n    if ($this->outcome == Constants::EXPEDITION_OUTCOME_FOUND_DM) {\n      $msg_text_addon = $this->darkMatterFound >= 1\n        ? sprintf($langExpeditions['found_dark_matter'], $this->darkMatterFound)\n        : $langExpeditions['outcomes'][$this->outcome]['no_result'];\n    }\n\n    if ($this->outcome == Constants::EXPEDITION_OUTCOME_FOUND_RESOURCES) {\n      if (array_sum($this->resourcesFound) >= 1) {\n        $msg_text_addon = $langExpeditions['found_resources'];\n        $msg_text_addon .= $this->renderUnits($this->resourcesFound);\n      } else {\n        $msg_text_addon = $langExpeditions['outcomes'][$this->outcome]['no_result'];\n      }\n    }\n\n    if (!empty($this->shipsLost)) {\n      $msg_text_addon = $langExpeditions['lost_fleet'];\n      $msg_text_addon .= $this->renderUnits($this->shipsLost);\n    }\n\n    if ($this->outcome == Constants::EXPEDITION_OUTCOME_FOUND_FLEET) {\n      if (empty($this->shipsFound)) {\n        $msg_text_addon = $langExpeditions['outcomes'][$this->outcome]['no_result'];\n      }\n    }\n\n    if (!empty($this->ships) && array_sum($this->ships) >= 1) {\n      if (!empty($this->shipsFound)) {\n        $msg_text_addon = $langExpeditions['found_fleet'] . $this->renderUnits($this->shipsFound);\n      }\n    }\n\n    $messages = $langExpeditions['outcomes'][$this->outcome]['messages'];\n    if (\n      // Outcome have sub-outcomes\n      !empty($this->currentOutcomeConfig['percent'])\n      // Some outcome rolled\n      && $this->subOutcome >= 0\n      // Messages are different for different outcomes\n      && is_array($messages)\n    ) {\n      // Selecting messages for specific outcome\n      $messages = &$messages[$this->subOutcome];\n    }\n\n    $msg_text = is_string($messages)\n      // If we have only one variant for message - using it\n      ? $messages\n      // If we have several message variations - selecting one randomly\n      : (is_array($messages) ? $messages[mt_rand(0, count($messages) - 1)] : '');\n\n    $msg_text = sprintf(\n        $msg_text,\n        $this->fleetEvent->fleetId,\n        uni_render_coordinates($fleetRow, 'fleet_end_')\n      ) . ($msg_text_addon ? \"\\r\\n\" . $msg_text_addon : '');\n\n    msg_send_simple_message(\n      $this->fleetEvent->fleetOwnerId,\n      '',\n      $fleetRow['fleet_end_stay'],\n      MSG_TYPE_EXPLORE,\n      $langExpeditions['msg_sender'],\n      $langExpeditions['msg_title'],\n      $msg_text\n    );\n  }\n\n  /**\n   * @return void\n   */\n  protected function applyFleetChanges() {\n    // Shortcut to access and change fleet data in event\n    $fleetRow = &$this->fleetEvent->fleet;\n\n    // Adding found ships\n    foreach (!empty($this->shipsFound) ? $this->shipsFound : [] as $unit_id => $unit_amount) {\n      $this->ships[$unit_id] += $unit_amount;\n    }\n    // Removing lost ships\n    foreach (!empty($this->shipsLost) ? $this->shipsLost : [] as $shipLostId => $shipLostCount) {\n      $this->ships[$shipLostId] -= $shipLostCount;\n      if ($this->ships[$shipLostId] < 1) {\n        unset($this->ships[$shipLostId]);\n      }\n    }\n    // Adjusting ship data in real fleet record\n    $fleetRow['fleet_amount'] = array_sum($this->ships);\n    $fleetRow['fleet_array']  = sys_unit_arr2str($this->ships);\n\n    // Adjusting resources data in real fleet record\n    if (array_sum($this->resourcesFound) >= 1) {\n      $fleetRow['fleet_resource_metal']     += $this->resourcesFound[RES_METAL];\n      $fleetRow['fleet_resource_crystal']   += $this->resourcesFound[RES_CRYSTAL];\n      $fleetRow['fleet_resource_deuterium'] += $this->resourcesFound[RES_DEUTERIUM];\n    }\n\n    // Setting fleet to return route\n    $fleetRow['fleet_mess'] = FLEET_STATUS_RETURNING;\n  }\n\n  /**\n   * Calculating fleet cost in metal and free capacity\n   *\n   * @return void\n   */\n  protected function calcSecondaryData() {\n    // Calculating ship's free capacity and fleet cost in metal\n    foreach ($this->ships as $ship_id => $ship_amount) {\n      $this->fleetCapacityFree += $ship_amount * static::$shipData[$ship_id][P_CAPACITY];\n      $this->shipsCostInMetal  += $ship_amount * static::$shipData[$ship_id][P_COST_METAL];\n    }\n    // Calculating rest of fleet capacity - room which not occupied with resources\n    $this->fleetCapacityFree = max(\n      0,\n      $this->fleetCapacityFree\n      - $this->fleetEvent->fleet['fleet_resource_metal']\n      - $this->fleetEvent->fleet['fleet_resource_crystal']\n      - $this->fleetEvent->fleet['fleet_resource_deuterium']\n    );\n  }\n\n  /**\n   * @param array $outcomeConfigs\n   *\n   * @return array\n   */\n  protected function adjustNoneChance(array $outcomeConfigs) {\n    // Calculating how many hours spent in expedition\n    $flt_stay_hours =\n      ($this->fleetEvent->fleet['fleet_end_stay'] - $this->fleetEvent->fleet['fleet_start_time']) / 3600\n      * (SN::$config->game_speed_expedition ?: 1);\n    // Adjusting chance for empty outcome - expedition found nothing\n    $outcomeConfigs[Constants::OUTCOME_NONE][P_CHANCE] = ceil(Constants::OUTCOME_EXPEDITION_NOTHING_DEFAULT_CHANCE / max(0.1, pow($flt_stay_hours, 1 / 1.7)));\n\n    return $outcomeConfigs;\n  }\n\n  /**\n   * @param array $outcomeConfigs\n   *\n   * @return array{0: int, 1: array}\n   */\n  protected function calculateRollValues(array $outcomeConfigs) {\n    // Calculating max chance can be rolled for current expedition\n    $chance_max = 0;\n    foreach ($outcomeConfigs as $key => &$outcomeConfig) {\n      // Removing invalid outcomes - with no chances set or zero chances\n      if (empty($outcomeConfig[P_CHANCE])) {\n        unset($outcomeConfigs[$key]);\n        continue;\n      }\n      $outcomeConfig[self::K_ROLL_VALUE] = $chance_max = $outcomeConfig[P_CHANCE] + $chance_max;\n    }\n\n    return [$outcomeConfigs, $chance_max];\n  }\n\n  /**\n   * Outcome - ships partially lost\n   *\n   * @return void\n   */\n  protected function outcomeShipsLostPartially() {\n    // 1-3 pack of 20-30%% -> 20-90%% lost totally\n    // Calculating lost share per fleet to maintain mathematical consistency for math model\n    $lostShare = mt_rand(1, 3) * (mt_rand(200000, 300000) / CONST_1M);\n    foreach ($this->ships as $shipId => $shipCount) {\n      $this->shipsLost[$shipId] = ceil($shipCount * $lostShare);\n    }\n  }\n\n  /**\n   * Outcome - lost all fleet\n   *\n   * @return void\n   */\n  protected function outcomeLostFleetAll() {\n    foreach ($this->ships as $shipsId => $shipCount) {\n      $this->shipsLost[$shipsId] += $this->ships[$shipsId];\n    }\n  }\n\n  /**\n   * @return void\n   */\n  protected function outcomeFoundResources() {\n    // Calculating found resources amount in metal\n    $found_in_metal = ceil(\n      min($this->gainShare * $this->shipsCostInMetal, game_resource_multiplier(true) * CONST_10M, $this->fleetCapacityFree)\n      // 95-105%% [0.95 - 1.05]\n      * (mt_rand(95 * 10000, 105 * 10000) / CONST_1M)\n    ); // game_speed\n\n    // 30-70%% of resources found are found in metal. Large numbers used to add more variability\n    $this->resourcesFound[RES_METAL] = floor($found_in_metal * mt_rand(3 * CONST_100K, 7 * CONST_100K) / CONST_1M);\n    // Deducing found metal from pool\n    $found_in_metal -= $this->resourcesFound[RES_METAL];\n\n    // Converting rest of found metal to crystals. Large numbers used to add more variability\n    $found_in_metal = floor($found_in_metal * static::$rates[RES_METAL] / static::$rates[RES_CRYSTAL]);\n    // 50-100%% of rest resources are found in crystals\n    $this->resourcesFound[RES_CRYSTAL] = floor($found_in_metal * mt_rand(5 * CONST_100K, 10 * CONST_100K) / CONST_1M);\n    // Deducing found crystals from pool\n    $found_in_metal -= $this->resourcesFound[RES_CRYSTAL];\n\n    // Converting rest of found crystals to deuterium\n    $found_in_metal = floor($found_in_metal * static::$rates[RES_CRYSTAL] / static::$rates[RES_DEUTERIUM]);\n    // 100% of resources rest are in deuterium\n    $this->resourcesFound[RES_DEUTERIUM] = $found_in_metal;\n  }\n\n  /**\n   *\n   * @return void\n   */\n  protected function outcomeFoundShips() {\n    // Рассчитываем эквивалент найденного флота в метале\n    $found_in_metal = min($this->gainShare * $this->shipsCostInMetal, game_resource_multiplier(true) * CONST_10M);\n    //  13 243 754 000 g x1\n    //  60 762 247 000 a x10\n    // 308 389 499 488 000 b x500\n\n    // Рассчитываем стоимость самого дорого корабля в металле\n    $shipMaxCostInMetal = 0;\n    foreach ($this->ships as $ship_id => $ship_amount) {\n      $shipMaxCostInMetal = max($shipMaxCostInMetal, static::$shipData[$ship_id][P_COST_METAL]);\n    }\n\n    // Ограничиваем корабли только теми, чья стоимость в металле меньше или равно стоимости самого дорогого корабля\n    $can_be_found = [];\n\n    foreach (static::$shipData as $ship_id => $ship_info) {\n      if (\n        $ship_info[P_COST_METAL] <= $shipMaxCostInMetal\n        // and not race ship\n        && empty($ship_info[P_RACE_SHIP])\n        // and not event-related ship\n        && empty($ship_info[P_REQUIRE_HIGHSPOT])\n      ) {\n        $can_be_found[$ship_id] = $ship_info[P_COST_METAL];\n      }\n    }\n\n    // Убираем колонизаторы и шпионов - миллиарды шпионов и колонизаторов нам не нужны\n    unset($can_be_found[SHIP_COLONIZER]);\n    unset($can_be_found[SHIP_SPY]);\n\n    while (count($can_be_found) && $found_in_metal >= max($can_be_found)) {\n      $found_index     = mt_rand(1, count($can_be_found)) - 1;\n      $found_ship      = array_slice($can_be_found, $found_index, 1, true);\n      $found_ship_cost = reset($found_ship);\n      $found_ship_id   = key($found_ship);\n\n      if ($found_ship_cost > $found_in_metal) {\n        unset($can_be_found[$found_ship_id]);\n      } else {\n        $found_ship_count                 = mt_rand(1, floor($found_in_metal / $found_ship_cost));\n        $this->shipsFound[$found_ship_id] += $found_ship_count;\n        $found_in_metal                   -= $found_ship_count * $found_ship_cost;\n      }\n    }\n  }\n\n  /**\n   * @return void\n   */\n  protected function outcomeFoundDm() {\n    // Рассчитываем количество найденной ТМ\n    $this->darkMatterFound = floor(\n      min(\n        $this->gainShare * $this->shipsCostInMetal / static::$rates[RES_DARK_MATTER],\n        self::MAX_DM\n      )\n      // 75-100%% of calculated value\n      * mt_rand(750000, CONST_1M) / CONST_1M\n    );\n  }\n\n}\n"
  },
  {
    "path": "classes/Fleet/RecordFleet.php",
    "content": "<?php\n/**\n * Created by Gorlum 07.12.2017 14:38\n */\n\nnamespace Fleet;\n\n\nuse Core\\GlobalContainer;\nuse DBAL\\ActiveRecord;\n\n/**\n * Class RecordFleet\n * @package Fleet\n *\n * property int|string $fleet_id                 - bigint     -\n * @property int|string $fleet_owner              - bigint     -\n * @property int        $fleet_mission            - int        -\n * @property int|string $fleet_amount             - bigint     -\n * @property string     $fleet_array              - mediumtext -\n * @property int        $fleet_start_time         - int        -\n * @property int|string $fleet_start_planet_id    - bigint     -\n * @property int        $fleet_start_galaxy       - int        -\n * @property int        $fleet_start_system       - int        -\n * @property int        $fleet_start_planet       - int        -\n * @property int        $fleet_start_type         - int        -\n * @property int        $fleet_end_time           - int        -\n * @property int        $fleet_end_stay           - int        -\n * @property int|string $fleet_end_planet_id      - bigint     -\n * @property int        $fleet_end_galaxy         - int        -\n * @property int        $fleet_end_system         - int        -\n * @property int        $fleet_end_planet         - int        -\n * @property int        $fleet_end_type           - int        -\n * @property int|string $fleet_resource_metal     - decimal    -\n * @property int|string $fleet_resource_crystal   - decimal    -\n * @property int|string $fleet_resource_deuterium - decimal    -\n * @property int|string $fleet_target_owner       - int        -\n * @property int|string $fleet_group              - varchar    -\n * @property int        $fleet_mess               - int        -\n * @property int        $start_time               - int        -\n *\n */\nclass RecordFleet extends ActiveRecord {\n  protected static $_primaryIndexField = 'fleet_id';\n\n  protected static $_tableName = 'fleets';\n\n  /**\n   * List of fleet ships\n   *\n   * @var float[] $shipList\n   */\n  protected $shipList = [];\n\n  /**\n   * @var float[] $resources\n   */\n  protected $resources = [\n    RES_METAL     => 0,\n    RES_CRYSTAL   => 0,\n    RES_DEUTERIUM => 0,\n  ];\n\n  /**\n   * RecordFleet constructor.\n   *\n   * @param GlobalContainer|null $services\n   */\n  public function __construct(GlobalContainer $services = null) {\n    parent::__construct($services);\n  }\n\n  /**\n   * @inheritdoc\n   */\n  protected function fromProperties(array $properties) {\n    parent::fromProperties($properties);\n\n    $this->shipList = !empty($this->fleet_array) ? sys_unit_str2arr($this->fleet_array) : [];\n\n    $this->resources = [\n      RES_METAL     => !empty($this->fleet_resource_metal) ? floatval($this->fleet_resource_metal) : 0,\n      RES_CRYSTAL   => !empty($this->fleet_resource_crystal) ? floatval($this->fleet_resource_crystal) : 0,\n      RES_DEUTERIUM => !empty($this->fleet_resource_deuterium) ? floatval($this->fleet_resource_deuterium) : 0,\n    ];\n  }\n\n  /**\n   * @inheritdoc\n   */\n  public function update() {\n    if($this->getShipCount() < 1) {\n      return $this->delete();\n    } else {\n      return parent::update();\n    }\n  }\n\n//  /**\n//   * @param int $shipId\n//   *\n//   * @return int|float\n//   */\n//  public function getShipCostInMetal($shipId) {\n//    return !empty(static::$shipInfo[$shipId][P_COST_METAL]) ? static::$shipInfo[$shipId][P_COST_METAL] : 0;\n//  }\n//\n//  /**\n//   * Get fleet cost in metal\n//   *\n//   * @return float|int\n//   */\n//  public function getCostInMetal() {\n//    $result = 0;\n//    foreach($this->shipList as $shipId => $amount) {\n//      $result += $amount * $this->getShipCostInMetal($shipId);\n//    }\n//\n//    return $result;\n//  }\n//\n//  /**\n//   * @param int $shipId\n//   *\n//   * @return int|mixed\n//   */\n//  public function getShipCapacity($shipId) {\n//    return !empty(static::$shipInfo[$shipId][P_CAPACITY]) ? static::$shipInfo[$shipId][P_CAPACITY] : 0;\n//  }\n//\n//  /**\n//   * @return float|int\n//   */\n//  public function getCapacity() {\n//    $result = 0;\n//    foreach($this->shipList as $shipId => $amount) {\n//      $result += $amount * $this->getShipCapacity($shipId);\n//    }\n//\n//    $result = max(0, $result - array_sum($this->resources));\n//\n//    return $result;\n//  }\n\n  /**\n   * @param int   $shipSnId\n   * @param float $shipCount\n   *\n   * @throws \\Exception\n   */\n  public function changeShipCount($shipSnId, $shipCount) {\n    !isset($this->shipList[$shipSnId]) ? $this->shipList[$shipSnId] = 0 : false;\n\n    $shipCount = floor($shipCount);\n\n    if($this->shipList[$shipSnId] + $shipCount < 0) {\n      throw new \\Exception(\"Trying to deduct more ships [{$shipSnId}] '{$shipCount}' when fleet [{$this->id}] has only {$this->shipList[$shipSnId]}\");\n    }\n\n    $this->shipList[$shipSnId] += $shipCount;\n    if($this->shipList[$shipSnId] < 1) {\n      unset($this->shipList[$shipSnId]);\n    }\n\n    $this->fleet_array = sys_unit_arr2str($this->shipList);\n    $this->fleet_amount = $this->getShipCount();\n  }\n\n  /**\n   * @param int   $resourceId\n   * @param float $resourceCount\n   *\n   * @throws \\Exception\n   */\n  public function changeResource($resourceId, $resourceCount) {\n    if (empty($resourceCount)) {\n      return;\n    }\n\n    if (!array_key_exists($resourceId, $this->resources)) {\n      throw new \\Exception(\"FLEET ERROR! Trying to change unknown resource type [{$resourceId}] '{$resourceCount}' on fleet [{$this->id}]\");\n    }\n\n    $resourceCount = ceil($resourceCount);\n\n    if($this->resources[$resourceId] + $resourceCount < 0) {\n      throw new \\Exception(\"FLEET ERROR! Trying to deduct more resources [{$resourceId}] '{$resourceCount}' when fleet [{$this->id}] has only {$this->resources[$resourceId]}\");\n    }\n\n    $this->resources[$resourceId] += $resourceCount;\n\n    $fieldName = 'fleet_resource_' . pname_resource_name($resourceId);\n    $this->inc()->$fieldName = $resourceCount;\n\n//    $this->fleet_resource_metal = $this->resources[RES_METAL];\n//    $this->fleet_resource_crystal = $this->resources[RES_CRYSTAL];\n//    $this->fleet_resource_deuterium = $this->resources[RES_DEUTERIUM];\n  }\n\n\n\n  public function isEmpty() {\n    return $this->getShipCount() < 1 && $this->getResourceCount() < 1;\n  }\n\n  public function getShipCount() {\n    return array_sum($this->getShipList());\n  }\n\n  // Getters/Setters ---------------------------------------------------------------------------------------------------\n  /**\n   * @return float[] - [shipSnId => $shipAmount]\n   */\n  public function getShipList() {\n    return $this->shipList;\n  }\n\n  /**\n   * @return float|int\n   */\n  public function getResourceCount() {\n    return array_sum($this->getResourceList());\n  }\n\n  /**\n   * @return float[] - [$resourceSnId => $resourceAmount]\n   */\n  public function getResourceList() {\n    return $this->resources;\n  }\n\n}\n"
  },
  {
    "path": "classes/Fleet/TaskDispatchFleets.php",
    "content": "<?php\n/**\n * Created by Gorlum 08.02.2020 18:54\n */\n\nnamespace Fleet;\n\nuse Core\\GlobalContainer;\nuse Core\\HttpUrl;\nuse SN;\nuse classConfig;\nuse Core\\Scheduler\\TaskPeriodic;\nuse Core\\Scheduler\\Lock;\n\nclass TaskDispatchFleets extends TaskPeriodic {\n  /**\n   * Name of config field to monitor\n   *\n   * @var string $configName\n   */\n  protected $configName = 'fleet_update_last';\n\n  /**\n   * @param GlobalContainer $gc\n   *\n   * @return Lock\n   */\n  public static function getLock($gc) {\n    return new Lock($gc, 'fleet_update_lock', PERIOD_MINUTE, 10, classConfig::DATE_TYPE_UNIX);\n//    return new Lock($gc, 'fleet_update_lock', 10, 0, classConfig::DATE_TYPE_UNIX);\n  }\n\n  /**\n   * TaskDispatchFleets constructor.\n   *\n   * @param GlobalContainer|null $gc\n   */\n  public function __construct($gc = null) {\n    parent::__construct($gc);\n\n    $this->interval = $this->config->fleet_update_interval;\n    $this->lock     = $this::getLock($this->gc);\n  }\n\n  protected function isTaskAllowed() {\n    return !SN::$options[PAGE_OPTION_FLEET_UPDATE_SKIP] && !SN::gameIsDisabled() && parent::isTaskAllowed();\n  }\n\n  /**\n   * This function called if task have a lock and lock grace period expired\n   * Here can be added some checks and even recovery procedures for expired lock\n   *\n   * @return bool True if task can proceed even on lock expiration time\n   */\n  protected function proceedLockExpiration() {\n    $this->gc->debug->warning('Fleet dispatcher was locked too long - unlocked by watchdog', 'FFH Error', 504);\n\n    return Lock::LOCK_EXPIRED_IGNORE;\n  }\n\n\n  protected function task() {\n    $url = HttpUrl::spawn($this->gc)\n      ->parseUrl(SN_ROOT_VIRTUAL)\n      ->addPath('index.php')\n      ->addParams(['page' => 'worker', 'mode' => 'dispatchFleets',]);\n\n    sn_get_url_contents($url->urlSigned());\n\n//    invokeUrl($url);\n\n    return true;\n  }\n\n}\n"
  },
  {
    "path": "classes/General/Helpers/PagingRenderer.php",
    "content": "<?php\n/**\n * Created by Gorlum 25.11.2017 21:45\n */\n\nnamespace General\\Helpers;\n\nuse DBAL\\DbSqlPaging;\nuse SnTemplate;\n\nclass PagingRenderer {\n  const KEYWORD = 'sheet';\n\n  /**\n   * @var string $rootUrl\n   */\n  protected $rootUrl = '';\n\n  /**\n   * @var DbSqlPaging $pager\n   */\n  protected $pager;\n\n  protected $delta = PAGING_SIZE_MAX_DELTA;\n\n  protected $result = [];\n\n  protected $current;\n  protected $total;\n\n  protected $from;\n  protected $to;\n\n  public function __construct(DbSqlPaging $pager, $rootUrl = '') {\n    $this->pager = $pager;\n    $this->rootUrl = URLHelper::addParam($rootUrl, self::KEYWORD);\n  }\n\n  /**\n   * @param int $delta\n   *\n   * @return $this\n   */\n  public function setDelta($delta) {\n    $this->delta = $delta;\n\n    return $this;\n  }\n\n  protected function href($pageNum, $link, $style = '', $href = true, $active = true) {\n    return\n      [\n        'HREF'     => $active ? $href : false,\n        'STYLE'    => $active ? $style : 'inactive',\n        'PAGE_NUM' => $pageNum,\n        'TEXT'     => $link,\n      ];\n  }\n\n  protected function addNumbers() {\n    $this->from = max(\n      1,\n      $this->current - $this->delta - max(0, $this->current + $this->delta - $this->total)\n    );\n    $this->to = min(\n      $this->total,\n      $this->current + $this->delta - min(0, $this->current - $this->delta - 1)\n    );\n\n    for ($i = $this->from; $i <= $this->to; $i++) {\n      array_push($this->result, $this->href($i, $i, $i == $this->current ? 'current' : 'before-after', $i != $this->current));\n    }\n  }\n\n  protected function addEllipsis() {\n    if ($this->from > 1) {\n      array_unshift($this->result, $this->href(0, '...', 'ellipsis', false));\n    }\n    if ($this->to < $this->total) {\n      array_push($this->result, $this->href(0, '...', 'ellipsis', false));\n    }\n  }\n\n  protected function addPrevNext() {\n    $prevActive = $this->current > 1;\n    array_unshift($this->result, $this->href($this->current - 1, '&lt;&lt;', 'prev-next', $prevActive, $prevActive));\n\n    $nextActive = $this->current < $this->total;\n    array_push($this->result, $this->href($this->current + 1, '&gt;&gt;', 'prev-next', $nextActive, $nextActive));\n  }\n\n  protected function addFirstLast() {\n    $firstActive = $this->current != 1;\n    array_unshift($this->result, $this->href(1, '&#124;&lt;-', 'first-last', $firstActive, $firstActive));\n\n    $lastActive = $this->current != $this->total;\n    array_push($this->result, $this->href($this->total, '-&gt;&#124;', 'first-last', $lastActive, $lastActive));\n  }\n\n  /**\n   * @return string\n   */\n  public function render() {\n    $output = '';\n\n    $this->result = [];\n\n    $this->current = $this->pager->getCurrentPageNumber();\n    $this->total = $this->pager->getTotalPages();\n\n    if ($this->total > 1) {\n      $this->addNumbers();\n      $this->addEllipsis();\n      $this->addPrevNext();\n      $this->addFirstLast();\n    }\n\n    if(!empty($this->result)) {\n      $template = SnTemplate::gettemplate('_paging');\n      $template->assign_recursive([\n        'PAGING_ROOT' => $this->rootUrl,\n        '.'           => ['paging' => $this->result]\n      ]);\n      $output = $template->assign_display('_paging');\n    }\n\n    return $output;\n  }\n\n}\n"
  },
  {
    "path": "classes/General/Helpers/URLHelper.php",
    "content": "<?php\n/**\n * Created by Gorlum 25.11.2017 22:10\n */\n\nnamespace General\\Helpers;\n\n\nclass URLHelper {\n\n  public static function addParam($url, $param, $value = '') {\n    list($urlItself, $strParams) = explode('?', $url);\n\n    $paramList = explode('&', $strParams);\n    foreach ($paramList as $key => $val) {\n      list($thisName, $thisParam) = explode('=', $val);\n      if ($thisName == $param) {\n        unset($paramList[$key]);\n      }\n    }\n\n    if(!empty($paramList)) {\n      $strParams = implode('&', $paramList);\n    }\n\n    $url = $urlItself . '?' . $strParams;\n\n    return $url . (strpos($url, '?') === false ? '?' : '&') . $param . '=' . $value;\n  }\n\n}\n"
  },
  {
    "path": "classes/General/LogCounterShrinker.php",
    "content": "<?php\n/**\n * Created by Gorlum 17.10.2017 8:36\n */\n\nnamespace General;\n\nuse Core\\GlobalContainer;\nuse \\DBAL\\DbMysqliResultIterator;\n\n/**\n * Class LogCounterShrinker\n *\n * This class shrinking user visit log (table `counter`) by merging conclusive records with same signature\n *\n * @package General\n */\nclass LogCounterShrinker extends VisitMerger {\n  const RESERVE_WEEKS = 0; // How much weeks of logs left unshrinked\n  const BATCH_DELETE_PER_LOOP = 10000;\n  const BATCH_UPDATE_PER_LOOP = 10000;\n  const BATCH_DELETE_PER_QUERY = 25;\n\n  protected $batchSize = 10000;\n\n  /**\n   * @var array\n   */\n  protected $batchDelete = [];\n  /**\n   * @var VisitAccumulator[] $batchUpdate\n   */\n  protected $batchUpdate = [];\n\n  /**\n   * Records skipped in current batch - not changed during process\n   *\n   * @var int $batchSkipped\n   */\n  protected $batchSkipped = 0;\n  protected $totalProcessed = 0;\n\n\n  public function __construct(GlobalContainer $gc) {\n    parent::__construct($gc);\n  }\n\n  /**\n   * @param string           $sign\n   * @param VisitAccumulator $logRecord\n   *\n   * @return bool\n   */\n\n  /**\n   * @return DbMysqliResultIterator\n   */\n  protected function buildIterator() {\n    return $this->gc->db->selectIterator(\n      \"SELECT * \n        FROM `{{counter}}`\n        WHERE `visit_time` < DATE_SUB(NOW(), INTERVAL \" . static::RESERVE_WEEKS . \" WEEK)\n        AND `counter_id` > {$this->batchEnd} \n        ORDER BY `visit_time`, `counter_id` \n        LIMIT {$this->batchSize};\"\n    );\n  }\n\n\n  /**\n   * @inheritdoc\n   */\n  public function process($cutTails = true) {\n    ini_set('memory_limit', '1000M');\n\n    $result = [];\n\n    while (($iter = $this->buildIterator()) && $iter->count()) {\n      $this->setIterator($iter);\n\n      parent::process(false);\n\n      $this->batchSave();\n\n      $this->totalProcessed += $this->batchProcessed;\n\n      if ($this->prevBatchStart == $this->batchStart && $this->prevBatchEnd == $this->batchEnd) {\n        $result = [\n          'STATUS'  => ERR_WARNING,\n          'MESSAGE' => \"{Зациклились с размером блока {$this->batchSize} - [{$this->batchStart},{$this->batchEnd}].\",\n        ];\n        break;\n      }\n\n      if ($this->batchProcessed < $this->batchSize) {\n        $result = [\n          'STATUS'  => ERR_WARNING,\n          'MESSAGE' => \"{Размер текущего блока {$this->batchProcessed} меньше максимального {$this->batchSize} между ID [{$this->batchStart},{$this->batchEnd}].\",\n        ];\n        break;\n      }\n    }\n\n    $this->cutTails();\n    $this->batchSave(true);\n\n    if (is_array($result)) {\n      $result['MESSAGE'] .= \" {Обработано} {$this->totalProcessed}, {пропущено} {$this->batchSkipped}\";\n    }\n\n    return $result;\n  }\n\n  protected function batchSave($forceSave = false) {\n    $this->gc->db->transactionStart();\n\n    if (count($this->batchDelete) >= static::BATCH_DELETE_PER_LOOP || $forceSave) {\n      $this->deleteMergedRecords($this->batchDelete);\n      $this->batchDelete = [];\n    }\n\n    if (count($this->batchUpdate) >= static::BATCH_UPDATE_PER_LOOP || $forceSave) {\n      foreach ($this->batchUpdate as $record) {\n        if (!$record->isChanged()) {\n          $this->batchSkipped++;\n          continue;\n        }\n\n        $this->addMoreTime();\n        $this->gc->db->doQueryFast(\n          'UPDATE `{{counter}}`\n          SET ' .\n          '`visit_length` = ' . $record->length . ',' .\n          '`hits` = ' . $record->hits .\n          ' WHERE `counter_id` = ' . $record->counterId . ';'\n        );\n      }\n      $this->batchUpdate = [];\n    }\n\n    $this->gc->db->transactionCommit();\n  }\n\n  protected function flushVisit($sign) {\n    if (!empty($this->data[$sign])) {\n      $this->batchUpdate[] = $this->data[$sign];\n    }\n\n    if (!empty($this->mergedIds[$sign]) && is_array($this->mergedIds[$sign])) {\n      $this->batchDelete = array_merge($this->batchDelete, $this->mergedIds[$sign]);\n    }\n  }\n\n\n  /**\n   * @param array $array\n   */\n  protected function deleteMergedRecords($array) {\n    if (!is_array($array) || empty($array)) {\n      return;\n    }\n\n    // Batch delete\n    $i = 0;\n    $tempArray = [];\n    foreach ($array as $recordDeleteId) {\n      $tempArray[] = $recordDeleteId;\n      if ($i++ > static::BATCH_DELETE_PER_QUERY) {\n        $this->addMoreTime();\n        $this->dbDeleteExecute($tempArray);\n        $i = 0;\n      }\n    }\n\n    // Emptying possible tails\n    if (!empty($tempArray)) {\n      $this->dbDeleteExecute($tempArray);\n    }\n  }\n\n  /**\n   * @param array $toDeleteArray\n   */\n  protected function dbDeleteExecute(&$toDeleteArray) {\n    $this->gc->db->doQueryFast('DELETE FROM `{{counter}}` WHERE `counter_id` IN (' . implode(',', $toDeleteArray) . ')');\n    $toDeleteArray = [];\n  }\n\n}\n"
  },
  {
    "path": "classes/General/VisitAccumulator.php",
    "content": "<?php\n/**\n * Created by Gorlum 17.10.2017 7:28\n */\n\nnamespace General;\n\n/**\n * Class VisitAccumulator\n *\n * Class used for `counter` table recalculation\n *\n * @package General\n */\nclass VisitAccumulator {\n  public $counterId = 0;\n  public $userId = 0;\n  public $playerEntryId = 0;\n  public $time = 0;\n  public $length = 0;\n  public $hits = 1;\n//  public $deviceId = 0;\n//  public $browserId = 0;\n//  public $ip = 0;\n//  public $proxies = 0;\n\n  public $defaultLength = 0;\n  public $defaultHits = 1;\n\n  /**\n   * @param $row\n   *\n   * @return self\n   */\n  public static function build($row) {\n    $me                = new self();\n\n    $me->counterId     = $row['counter_id'];\n    $me->userId        = $row['user_id'];\n    $me->playerEntryId = $row['player_entry_id'];\n    $me->time          = strtotime($row['visit_time']);\n    $me->length        = $row['visit_length'];\n    $me->hits          = $row['hits'];\n//    $me->deviceId = $row['device_id'];\n//    $me->browserId = $row['browser_id'];\n//    $me->ip = $row['user_ip'];\n//    $me->proxies = $row['user_proxy'];\n\n    $me->defaultLength = $me->length;\n    $me->defaultHits   = $me->hits;\n\n    return $me;\n  }\n\n  public function isChanged() {\n    return $this->defaultLength != $this->length || $this->defaultHits != $this->hits;\n  }\n\n}\n"
  },
  {
    "path": "classes/General/VisitMerger.php",
    "content": "<?php\n/**\n * Created by Gorlum 17.10.2017 10:20\n */\n\nnamespace General;\n\nuse Common\\EmptyCountableIterator;\nuse Core\\GlobalContainer;\nuse Common\\Interfaces\\ICountableIterator;\n\n/**\n * Class VisitMerger\n *\n * Class merges supplied visits with provided criterias\n *\n * @package General\n */\nabstract class VisitMerger {\n  const PLAYER_ACTIVITY_MAX_INTERVAL = PERIOD_MINUTE_15;\n\n  public $maxInterval = self::PLAYER_ACTIVITY_MAX_INTERVAL;\n  /**\n   * Time to extend PHP execution time each loop\n   *\n   * @var int $_extendTime\n   */\n  protected $_extendTime = 30;\n\n\n  /**\n   * @var \\Core\\GlobalContainer $gc\n   */\n  protected $gc;\n\n  /**\n   * @var VisitAccumulator[] $data\n   */\n  protected $data = [];\n  /**\n   * List of IDs of log record that was merged with upper ones\n   *\n   * @var array $mergedIds\n   */\n  protected $mergedIds = [];\n\n  /**\n   * @var ICountableIterator|null\n   */\n  protected $iterator = null;\n\n  protected $prevBatchStart = 0;\n  protected $prevBatchEnd = 0;\n  protected $batchStart = 0;\n  protected $batchEnd = 0;\n  /**\n   * Records processed in current batch - read from DB\n   *\n   * @var int $batchProcessed\n   */\n  protected $batchProcessed = 0;\n\n  /**\n   * General constructor.\n   *\n   * @param \\Core\\GlobalContainer $gc\n   */\n  public function __construct(GlobalContainer $gc) {\n    $this->gc = $gc;\n    $this->iterator = new EmptyCountableIterator();\n  }\n\n  /**\n   * @param ICountableIterator $iterator\n   */\n  public function setIterator(ICountableIterator $iterator) {\n    $this->iterator = $iterator;\n  }\n\n  /**\n   * Is supplied log record could be merged to data array, indicated by $sign\n   *\n   * Can (and should!) be overrode by successors to provide different groupping mechanic\n   *\n   * @param string           $sign\n   * @param VisitAccumulator $logRecord\n   *\n   * @return bool\n   */\n  protected function isSameVisit($sign, $logRecord) {\n    return $this->data[$sign]->time + $this->data[$sign]->length + $this->maxInterval >= $logRecord->time;\n  }\n\n  /**\n   * Class entry point\n   *\n   * @param bool $cutTails - should we cut tails?\n   *\n   * @return array - []|['STATUS' => (int)errorLevel, 'MESSAGE' => (string)message]\n   */\n  public function process($cutTails = true) {\n    $this->batchProcessed = 0;\n\n    $this->prevBatchStart = $this->batchStart;\n    $this->prevBatchEnd = $this->batchEnd;\n\n    if ($this->iterator->valid()) {\n      $logRecord = VisitAccumulator::build($this->iterator->current());\n      $this->batchStart = $logRecord->counterId;\n    }\n\n    foreach ($this->iterator as $row) {\n      $this->addMoreTime();\n      $logRecord = VisitAccumulator::build($row);\n      $this->processRecord($logRecord);\n      $this->batchProcessed++;\n    }\n    $this->batchEnd = isset($logRecord) ? $logRecord->counterId : $this->batchStart;\n\n    if ($cutTails) {\n      $this->cutTails();\n    }\n\n    return [];\n  }\n\n  /**\n   * @param VisitAccumulator $logRecord\n   */\n  protected function processRecord($logRecord) {\n    $sign = $this->calcSignature($logRecord);\n\n    if (empty($this->data[$sign])) {\n      $this->newVisit($sign, $logRecord);\n    } else {\n      if ($this->isSameVisit($sign, $logRecord)) {\n        $this->extendVisit($sign, $logRecord);\n      } else {\n        $this->resetVisit($sign, $logRecord);\n      }\n    }\n  }\n\n  /**\n   * Cutting tales - working on opened visits after all rows processed\n   */\n  protected function cutTails() {\n    foreach ($this->data as $sign => $tails) {\n      $this->addMoreTime();\n      $this->resetVisit($sign, null);\n    }\n  }\n\n  /**\n   * Getting signature (name of group to which this visit can belong) from logged visit\n   *\n   * Signature made from user ID, device ID, browser ID and IPs\n   *\n   * @param VisitAccumulator $logRecord\n   *\n   * @return string\n   */\n  protected function calcSignature($logRecord) {\n//    return\n//      $logRecord->userId . '_' .\n//      $logRecord->deviceId . '_' .\n//      $logRecord->browserId . '_' .\n//      $logRecord->ip . '_' .\n//      $logRecord->proxies;\n    return $logRecord->userId . '_'  . $logRecord->playerEntryId;\n  }\n\n  /**\n   * Starting new visit from supplied log record\n   *\n   * @param string           $sign\n   * @param VisitAccumulator $logRecord\n   */\n  protected function newVisit($sign, $logRecord) {\n    $this->data[$sign] = $logRecord;\n    $this->mergedIds[$sign] = [];\n  }\n\n\n  /**\n   * Extending current visit with supplied log record\n   *\n   * @param string           $sign\n   * @param VisitAccumulator $logRecord\n   */\n  protected function extendVisit($sign, $logRecord) {\n    // Adjusting current visit length\n    $this->data[$sign]->length = max(\n    // Newly calculated visit length\n      $logRecord->time - $this->data[$sign]->time + $logRecord->length,\n      // Fallback to current visit length logged record start later but is shorter then already calculated visit\n      // Should never happen, honestly\n      $this->data[$sign]->length\n    );\n\n    // Adding hit count\n    $this->data[$sign]->hits += $logRecord->hits;\n\n    // Marking current log record for deletion\n    $this->mergedIds[$sign][] = $logRecord->counterId;\n  }\n\n  /**\n   * Called in resetVisit to perform necessary storage operations for current visit - if any\n   *\n   * Doing nothing here - just a callback to easy override in successors\n   *\n   * @param string $sign\n   */\n  protected function flushVisit($sign) { }\n\n  /**\n   * Closing previous visit and starting new one\n   *\n   * @param string                $sign\n   * @param VisitAccumulator|null $logRecord\n   */\n  protected function resetVisit($sign, $logRecord) {\n    // Making any necessary operations on current visit data before they went to thrash\n    $this->flushVisit($sign);\n\n    unset($this->mergedIds[$sign]);\n    // Current row now is a visit start\n    if ($logRecord === null) {\n      unset($this->data[$sign]);\n    } else {\n      $this->data[$sign] = $logRecord;\n    }\n  }\n\n  protected function addMoreTime() {\n    $this->_extendTime ? set_time_limit($this->_extendTime) : false;\n  }\n\n  protected function resetArrays() {\n    $this->data = [];\n    $this->mergedIds = [];\n  }\n\n}\n"
  },
  {
    "path": "classes/General.php",
    "content": "<?php\n/**\n * Created by Gorlum 12.10.2017 15:19\n */\n\nuse Core\\GlobalContainer;\n\n/**\n * Class General\n *\n * Wrapper for /includes/general.php\n * Will make unit testing easier\n *\n */\nclass General {\n  /**\n   * @var GlobalContainer $gc\n   */\n  protected $gc;\n\n  /**\n   * @var \\Bonus\\ValueStorage $valueStorage\n   */\n  protected $valueStorage;\n\n  /**\n   * General constructor.\n   *\n   * @param GlobalContainer $gc\n   */\n  public function __construct(GlobalContainer $gc) {\n    $this->gc = $gc;\n    $this->valueStorage = $this->gc->valueStorage;\n  }\n\n  /**\n   * @param string|string[] $groupNameList\n   *\n   * @return array|array[]\n   */\n  public function getGroupsByName($groupNameList) {\n    return sn_get_groups($groupNameList);\n  }\n\n  /**\n   * @param int|string|array $groupIdList\n   *\n   * @return array|array[]\n   */\n  public function getGroupsById($groupIdList) {\n    if (!is_array($groupIdList)) {\n      $groupIdList = [$groupIdList];\n    }\n\n    $idToName = $this->getGroupsByName(GROUP_GROUP_ID_TO_NAMES);\n    foreach ($groupIdList as &$groupId) {\n      if (!empty($idToName[$groupId])) {\n        $groupId = $idToName[$groupId];\n      }\n\n    }\n\n    return $this->getGroupsByName($groupIdList);\n  }\n\n  /**\n   * @return float|int\n   */\n  public function fleetNoobPoints() {\n    return $this->valueStorage->getValue(UNIT_SERVER_FLEET_NOOB_POINTS) * game_resource_multiplier(true);\n  }\n\n  /**\n   * Is player qualified as \"noob\" aka \"New Inexpirienced player\"\n   *\n   * @param float $playerTotalPoints\n   *\n   * @return bool\n   */\n  public function playerIsNoobByPoints($playerTotalPoints) {\n    return $playerTotalPoints <= $this->fleetNoobPoints();\n  }\n\n  /**\n   * Is player 1 stronger then player 2 counting game noob factor?\n   *\n   * @param float $player1TotalPoints\n   * @param float $player2TotalPoints\n   *\n   * @return bool\n   */\n  public function playerIs1stStrongerThen2nd($player1TotalPoints, $player2TotalPoints) {\n    $gameNoobFactor = $this->valueStorage->getValue(UNIT_SERVER_FLEET_NOOB_FACTOR);\n\n    return $gameNoobFactor && $player1TotalPoints > $player2TotalPoints * $gameNoobFactor;\n  }\n\n}\n"
  },
  {
    "path": "classes/HelperArray.php",
    "content": "<?php\n\nclass HelperArray {\n\n  /**\n   * Overwrites old array with new\n   */\n  const MERGE_OVERWRITE = 0;\n  /**\n   * Merges old array with new with array_merge()\n   * String keys replaced, numeric keys renumbered\n   */\n  const MERGE_PHP = 1;\n\n  /**\n   * Merges old array with new with array_merge_recursive()\n   * String keys replaced, numeric keys renumbered\n   */\n  const MERGE_RECURSIVE = 2;\n\n  /**\n   * Merges old array with new recursive\n   * String keys merged, numeric keys merged\n   */\n  const MERGE_RECURSIVE_NUMERIC = 3;\n\n\n  /**\n   * No array cloning - just stay with same objects\n   */\n  const CLONE_ARRAY_NONE = 0;\n  /**\n   * Clone objects on first level of array\n   */\n  const CLONE_ARRAY_SHALLOW = 1;\n  /**\n   * Clone objects recursive on any array level\n   */\n  const CLONE_ARRAY_RECURSIVE = 2;\n\n  /**\n   * Convert $delimiter delimited string to array\n   *\n   * @param mixed  $string\n   * @param string $delimiter\n   *\n   * @return array\n   */\n  public static function stringToArray($string, $delimiter = ',') {\n    return is_string($string) && !empty($string) ? explode($delimiter, $string) : array();\n  }\n\n  /**\n   * Convert single value to array by reference\n   *\n   * @param mixed &$value\n   */\n  public static function makeArrayRef(&$value, $index = 0) {\n    if (!is_array($value)) {\n      $value = array($index => $value);\n    }\n  }\n\n\n  /**\n   * Convert single value to array\n   *\n   * @param mixed $value\n   *\n   * @return array\n   */\n  public static function makeArray($value, $index = 0) {\n    static::makeArrayRef($value, $index);\n\n    return $value;\n  }\n\n  /**\n   * Filters array by callback\n   *\n   * @param mixed    $array\n   * @param callable $callback\n   *\n   * @return array\n   */\n  public static function filter(&$array, $callback) {\n    $result = array();\n\n    if (is_array($array) && !empty($array)) {\n      // TODO - array_filter\n      foreach ($array as $value) {\n        if (call_user_func($callback, $value)) {\n          $result[] = $value;\n        }\n      }\n    }\n\n    return $result;\n  }\n\n  /**\n   * Filters array by callback\n   *\n   * @param mixed    $array\n   * @param callable $callback\n   * @param bool     $withKeys\n   *\n   * @return array\n   */\n  public static function map(&$array, $callback, $withKeys = false) {\n    $result = array();\n\n    if (is_array($array) && !empty($array)) {\n      if ($withKeys) {\n        $result = array_map($callback, $array, array_keys($array));\n      } else {\n        $result = array_map($callback, $array);\n      }\n    }\n\n    return $result;\n  }\n\n  /**\n   * Filter empty() values from array\n   *\n   * @param $array\n   *\n   * @return array\n   */\n  public static function filterEmpty($array) {\n    return static::filter($array, 'Validators::isNotEmpty');\n  }\n\n  /**\n   * @param string $string\n   * @param string $delimiter\n   *\n   * @return array\n   */\n  public static function stringToArrayFilterEmpty($string, $delimiter = ',') {\n    return static::filterEmpty(static::stringToArray($string, $delimiter));\n  }\n\n  /**\n   * @param mixed|array &$arrayOld\n   * @param mixed|array $arrayNew\n   * @param int         $mergeStrategy - default is HelperArray::MERGE_OVERWRITE\n   */\n  public static function merge(&$arrayOld, $arrayNew = array(), $mergeStrategy = self::MERGE_OVERWRITE) {\n    static::makeArrayRef($arrayNew);\n    static::makeArrayRef($arrayOld);\n\n    switch ($mergeStrategy) {\n      case self::MERGE_PHP:\n        $arrayOld = array_merge($arrayOld, $arrayNew);\n      break;\n\n      case self::MERGE_RECURSIVE:\n        $arrayOld = array_merge_recursive($arrayOld, $arrayNew);\n      break;\n\n      case self::MERGE_RECURSIVE_NUMERIC:\n        foreach ($arrayNew as $key => $value) {\n          !isset($arrayOld[$key]) || !is_array($arrayOld[$key]) ? $arrayOld[$key] = $value : self::merge($arrayOld[$key], $value, self::MERGE_RECURSIVE_NUMERIC);\n        }\n      break;\n\n      default:\n        $arrayOld = $arrayNew;\n      break;\n    }\n  }\n\n  /**\n   * Checks if key exists in array. If yes - return value on key otherwise return default value\n   *\n   * @param array &$array\n   * @param mixed $key\n   * @param mixed $default\n   *\n   * @return mixed\n   */\n  public static function keyExistsOr(&$array, $key, $default) {\n    return array_key_exists($key, $array) ? $array[$key] : $default;\n  }\n\n  /**\n   * Clone objects in array\n   *\n   * @param array &$array - Any dimensional array with presumed objects in there\n   * @param int   $deep - HelperArray::CLONE_ARRAY_xxx constants\n   */\n  public static function cloneDeep(&$array, $deep = self::CLONE_ARRAY_RECURSIVE) {\n    if ($deep == self::CLONE_ARRAY_NONE) {\n      return;\n    }\n\n    foreach ($array as &$value) {\n      if (is_object($value)) {\n        $value = clone $value;\n      } elseif (is_array($value) && $deep == self::CLONE_ARRAY_RECURSIVE) {\n        static::cloneDeep($value, $deep);\n      }\n    }\n  }\n\n  /**\n   * Repacking array to provided level, removing null elements\n   *\n   * @param array &$array\n   * @param int   $level\n   */\n  public static function array_repack(&$array, $level = 0) {\n    if (!is_array($array)) {\n      return;\n    }\n\n    foreach ($array as $key => &$value) {\n      if ($value === null) {\n        unset($array[$key]);\n      } elseif ($level > 0 && is_array($value)) {\n        static::array_repack($value, $level - 1);\n        if (empty($value)) {\n          unset($array[$key]);\n        }\n      }\n    }\n  }\n\n\n  /**\n   * Parses array of stringified parameters to array of parameter\n   *\n   * Stringified parameter is a string of \"<parmName><delimeter><paramValue>\"\n   *\n   * @param array|string $array\n   * @param string       $delimiter\n   *\n   * @return array\n   */\n  public static function parseParamStrings($array, $delimiter = '=') {\n    !is_array($array) ? $array = array((string)$array) : false;\n\n    $result = array();\n    foreach ($array as $param) {\n      $exploded = explode($delimiter, $param);\n      $paramName = $exploded[0];\n      unset($exploded[0]);\n      $result[$paramName] = implode($delimiter, $exploded);\n    }\n\n    return $result;\n  }\n\n  /**\n   * Finds maximum value of field in subarrays\n   *\n   * @param array[] $array\n   * @param mixed   $fieldName\n   *\n   * @return float|null\n   */\n  public static function maxValueByField(&$array, $fieldName) {\n    return array_reduce($array, function ($carry, $item) use ($fieldName) {\n      if(is_array($item) && isset($item[$fieldName]) && (!isset($carry) || $carry < $item[$fieldName])) {\n        $carry = $item[$fieldName];\n      }\n\n      return $carry;\n    });\n  }\n\n  /**\n   * @param $array\n   * @param $fieldName\n   */\n  public static function topRecordsByField(&$array, $fieldName) {\n    $maxValue = self::maxValueByField($array, $fieldName);\n\n    return\n      array_reduce($array,\n        function ($carry, $item) use (&$fieldName, $maxValue) {\n          if(is_array($item) && isset($item[$fieldName]) && $item[$fieldName] == $maxValue) {\n            $carry[] = $item;\n          }\n\n          return $carry;\n        },\n        array()\n      );\n  }\n\n  public static function intersectByKeys(array &$array1, array &$array2) {\n    return array_uintersect_assoc($array1, $array2, function ($a, $b) {return 0;});\n  }\n\n  /**\n   * Get first key of array\n   * Polyfill until PHP 7.3\n   *\n   * @param array $array\n   *\n   * @return mixed|null\n   */\n  public static function array_key_first(&$array) {\n    if (!is_array($array)) {\n      return null;\n    }\n    reset($array);\n\n    return key($array);\n  }\n\n}\n"
  },
  {
    "path": "classes/HelperString.php",
    "content": "<?php\n\n/**\n * Created by Gorlum 12.02.2017 11:12\n */\nclass HelperString {\n\n  /**\n   * Encodes non-html characters in UTF-8\n   *\n   * @param string $string\n   * @param int    $params - bit mask:\n   *    HTML_ENCODE_PREFORM\n   *    HTML_ENCODE_NL2BR\n   *    HTML_ENCODE_STRIP_HTML\n   *    HTML_ENCODE_JS_SAFE\n   *\n   * @return string\n   */\n  public static function htmlEncode($string, $params = HTML_ENCODE_PREFORM) {\n    $params & HTML_ENCODE_STRIP_HTML ? $string = strip_tags($string) : false;\n    $params & HTML_ENCODE_PREFORM ? $string = self::htmlSafe($string) : false;\n    $params & HTML_ENCODE_NL2BR ? $string = self::nl2br($string) : false;\n    $params & HTML_ENCODE_JS_SAFE ? $string = self::jsSafe($string) : false;\n    $params & HTML_ENCODE_SPACE ? $string = self::space2nbsp($string) : false;\n\n    return $string;\n  }\n\n  /**\n   * @param $string\n   *\n   * @return string\n   */\n  public static function htmlSafe($string) {\n    return htmlentities($string, ENT_COMPAT, 'UTF-8');\n  }\n\n\n  /**\n   * @param $string\n   *\n   * @return mixed\n   */\n  public static function nl2br($string) {\n    return str_replace(array(\"\\r\", \"\\n\"), '', nl2br($string));\n  }\n\n  /**\n   * Make string JS-safe\n   *\n   * @param $string\n   *\n   * @return mixed\n   */\n  public static function jsSafe($string) {\n    return str_replace(array(\"\\r\", \"\\n\"), array('\\r', '\\n'), addslashes($string));\n  }\n\n  /**\n   * @param $string\n   *\n   * @return mixed\n   */\n  public static function space2nbsp($string) {\n    return str_replace(' ', '&nbsp;', $string);\n  }\n\n  protected function explodeCallable(&$string) {\n    $string = explode('>', $string);\n  }\n\n  protected function implodeCallable(&$array) {\n    $array = implode('>', $array);\n  }\n\n  protected function encode2ndValue(&$value) {\n    if (is_array($value) && !empty($value)) {\n      $value[count($value) - 1] = self::htmlEncode($value[count($value) - 1]);\n    }\n  }\n\n  protected function encodeSkipHtml($string) {\n    $lt = explode('<', $string);\n    array_walk($lt, array($this, 'explodeCallable'));\n    array_walk($lt, array($this, 'encode2ndValue'));\n    array_walk($lt, array($this, 'implodeCallable'));\n    // Now every [0] element in all subarray is a HTML tag\n    $string = implode('<', $lt);\n\n    return $string;\n  }\n\n  public static function camelToUnderscore($string) {\n    return strtolower(trim(preg_replace(/** @lang RegExp */ '/(?<!^)[A-Z]/', '_\\0', $string), '_'));\n  }\n\n  /**\n   * Just format number to Cyrillic format\n   *\n   * @param int|float $number\n   * @param int       $decimals\n   */\n  public static function numberFormat($number, $decimals) {\n    return number_format($number, $decimals, ',', '.');\n  }\n\n  /**\n   * Formats FLOORED (!) number to string in Cyrillic format\n   *\n   * @param int|float $number\n   * @param int       $decimals\n   *\n   * @return string\n   */\n  public static function numberFloorAndFormat($number) {\n    return static::numberFormat(floor($number), 0);\n  }\n\n}\n"
  },
  {
    "path": "classes/Meta/Economic/BuildDataStatic.php",
    "content": "<?php\n/**\n * Created by Gorlum 24.03.2018 21:54\n */\n\nnamespace Meta\\Economic;\n\n\nuse SN;\n\nclass BuildDataStatic {\n\n  /**\n   * @param float $prevDivisor - because this is Hooker's client so prev result variable should be declared\n   * @param array $user\n   * @param array $planet\n   * @param int   $unit_id\n   * @param array $unit_data\n   *\n   * @return float\n   */\n  public static function getStructuresTimeDivisor($prevDivisor, $user, $planet, $unit_id, $unit_data) {\n    $result = 1;\n    if (in_array($unit_id, sn_get_groups('structures'))) {\n      $result = pow(2, mrc_get_level($user, $planet, STRUC_FACTORY_NANO)) * (mrc_get_level($user, $planet, STRUC_FACTORY_ROBOT) + 1);\n    } elseif (in_array($unit_id, sn_get_groups('defense'))) {\n      $result = pow(2, mrc_get_level($user, $planet, STRUC_FACTORY_NANO)) * (mrc_get_level($user, $planet, STRUC_FACTORY_HANGAR) + 1);\n    } elseif (in_array($unit_id, sn_get_groups('fleet'))) {\n      $result = pow(2, mrc_get_level($user, $planet, STRUC_FACTORY_NANO)) * (mrc_get_level($user, $planet, STRUC_FACTORY_HANGAR) + 1);\n    } elseif (in_array($unit_id, sn_get_groups('tech'))) {\n      $result = eco_get_lab_max_effective_level($user, intval($unit_data[P_REQUIRE][STRUC_LABORATORY]));\n    }\n\n    return $result;\n  }\n\n  /**\n   * @param int $unit_id\n   *\n   * @return float\n   */\n  public static function getMercenaryTimeDivisor($user, $planet, $unit_id) {\n    if (in_array($unit_id, sn_get_groups('structures'))) {\n      $mercenary = MRC_ENGINEER;\n    } elseif (in_array($unit_id, sn_get_groups('tech'))) {\n      $mercenary = MRC_ACADEMIC;\n    } elseif (in_array($unit_id, sn_get_groups('defense'))) {\n      $mercenary = MRC_FORTIFIER;\n    } elseif (in_array($unit_id, sn_get_groups('fleet'))) {\n      $mercenary = MRC_ENGINEER;\n    } else {\n      $mercenary = 0;\n    }\n\n    return $mercenary ? mrc_modify_value($user, $planet, $mercenary, 1) : 1;\n  }\n\n  /**\n   * @param array $user\n   * @param array $planet\n   * @param int   $unit_id\n   *\n   * @return float|int\n   */\n  public static function getCapitalTimeDivisor($user, $planet, $unit_id) {\n    static $capitalUnitGroups;\n    empty($capitalUnitGroups) ? $capitalUnitGroups = sn_get_groups(sn_get_groups(GROUP_CAPITAL_BUILDING_BONUS_GROUPS)) : false;\n\n    if (\n      // If planet is capital\n      $user['id_planet'] == $planet['id']\n      &&\n      // There is capital building rate set\n      SN::$gc->config->planet_capital_building_rate > 0\n      &&\n      // Unit is subject to Capital bonus\n      in_array($unit_id, $capitalUnitGroups)\n    ) {\n      $capitalTimeDivisor = SN::$gc->config->planet_capital_building_rate;\n    } else {\n      $capitalTimeDivisor = 1;\n    }\n\n    return $capitalTimeDivisor;\n  }\n\n  /**\n   * @param array $user\n   * @param array $planet\n   * @param int   $unit_id\n   * @param array $cost\n   *\n   * @return float - Time need to destroy current level of unit in seconds\n   */\n  public static function getDestroyStatus($user, $planet, $unit_id, $cost) {\n    static $groupStructures;\n    empty($groupStructures) ? $groupStructures = sn_get_groups('structures') : false;\n\n    $result = !in_array($unit_id, $groupStructures) ? BUILD_INDESTRUCTABLE :\n      (!mrc_get_level($user, $planet, $unit_id, false, true) ? BUILD_NO_UNITS :\n        (\n        !($cost['CAN'][BUILD_DESTROY]) ? BUILD_NO_RESOURCES :\n          ($cost['RESULT'][BUILD_CREATE] == BUILD_UNIT_BUSY ? BUILD_UNIT_BUSY : BUILD_ALLOWED)\n        )\n      );\n\n    return $result;\n  }\n\n  /**\n   * @param array $user\n   * @param array $planet\n   * @param array $cost\n   *\n   * @return float\n   */\n  public static function getAutoconvertCount($user, $planet, $cost) {\n    static $groupResourcesLoot;\n    empty($groupResourcesLoot) ? $groupResourcesLoot = sn_get_groups('resources_loot') : false;\n\n    $cost_in_metal          = 0;\n    $planetResourcesInMetal = 0;\n    foreach ($groupResourcesLoot as $resource_id) {\n      $planetResourcesInMetal += floor(get_unit_cost_in([$resource_id => mrc_get_level($user, $planet, $resource_id)]));\n      !empty($cost[BUILD_CREATE][$resource_id])\n        ? $cost_in_metal += get_unit_cost_in([$resource_id => $cost[BUILD_CREATE][$resource_id]])\n        : false;\n    }\n\n    return floor($planetResourcesInMetal / $cost_in_metal);\n  }\n\n  /**\n   * @param $user\n   * @param $planet\n   * @param $unit_data\n   * @param $unit_level\n   *\n   * @return array\n   */\n  public static function getBasicData(&$user, $planet, $unit_data, $unit_level) {\n    static $groupResourcesLoot;\n    empty($groupResourcesLoot) ? $groupResourcesLoot = sn_get_groups('resources_loot') : false;\n\n    $cost = [\n      P_OPTIONS => [\n        P_TIME_RAW => 0,\n      ],\n    ];\n\n    $unit_factor      = !empty($unit_data[P_COST][P_FACTOR]) ? $unit_data[P_COST][P_FACTOR] : 1;\n    $levelPriceFactor = pow($unit_factor, $unit_level);\n\n    $only_dark_matter = 0;\n    $canDestroyAmount = 1000000000000;\n    $canBuildAmount   = !empty($unit_data[P_MAX_STACK]) ? $unit_data[P_MAX_STACK] : 1000000000000;\n    foreach ($unit_data[P_COST] as $resourceId => $costResource) {\n      if ($resourceId === P_FACTOR || !($levelPrice = ceil($costResource * $levelPriceFactor))) {\n        continue;\n      }\n\n      $only_dark_matter = $only_dark_matter ? $only_dark_matter : $resourceId;\n\n      $cost[BUILD_CREATE][$resourceId]  = $levelPrice;\n      $cost[BUILD_DESTROY][$resourceId] = ceil($levelPrice / 2);\n\n      if (in_array($resourceId, $groupResourcesLoot)) {\n        $cost[P_OPTIONS][P_TIME_RAW] += get_unit_cost_in([$resourceId => $levelPrice], RES_DEUTERIUM);\n      }\n\n      $resource_got = !empty($user)\n        ? BuildDataStatic::eco_get_resource_on_location($user, $planet, $resourceId, $groupResourcesLoot)\n        : 0;\n\n      $canBuildAmount   = min($canBuildAmount, floor($resource_got / $cost[BUILD_CREATE][$resourceId]));\n      $canDestroyAmount = min($canDestroyAmount, floor($resource_got / $cost[BUILD_DESTROY][$resourceId]));\n    }\n    $cost['CAN'][BUILD_CREATE]           = $canBuildAmount > 0 ? floor($canBuildAmount) : 0;\n    $cost['CAN'][BUILD_DESTROY]          = $canDestroyAmount > 0 ? floor($canDestroyAmount) : 0;\n    $cost[P_OPTIONS][P_ONLY_DARK_MATTER] = $only_dark_matter == RES_DARK_MATTER;\n\n    return $cost;\n  }\n\n  /**\n   * Special function to get resource on location\n   *\n   * @param array $user\n   * @param array $planet\n   * @param int   $resource_id\n   * @param array $groupResourcesLoot\n   *\n   * @return float\n   */\n  public static function eco_get_resource_on_location($user, $planet, $resource_id, $groupResourcesLoot) {\n    if (in_array($resource_id, $groupResourcesLoot)) {\n      $resource_got = mrc_get_level($user, $planet, $resource_id);\n    } elseif ($resource_id == RES_DARK_MATTER) {\n      $resource_got = mrc_get_level($user, [], $resource_id);\n    } elseif ($resource_id == RES_ENERGY) {\n      $resource_got = max(0, $planet['energy_max'] - $planet['energy_used']);\n    } else {\n      $resource_got = 0;\n    }\n\n    return $resource_got;\n  }\n\n  /**\n   * @param array $user\n   * @param array $planet\n   * @param int   $unit_id\n   * @param float $costCanBuildCreate\n   *\n   * @return int|mixed\n   */\n  public static function getBuildStatus(&$user, $planet, $unit_id, $costCanBuildCreate) {\n    $result = eco_can_build_unit($user, $planet, $unit_id);\n\n    // Additional check for resources. If no units can be built - it means that there are not enough resources\n    return $result == BUILD_ALLOWED && $costCanBuildCreate < 1 ? BUILD_NO_RESOURCES : $result;\n  }\n\n\n}\n"
  },
  {
    "path": "classes/Meta/Economic/EconomicHelper.php",
    "content": "<?php\n/**\n * Created by Gorlum 01.10.2017 15:07\n */\n\nnamespace Meta\\Economic;\n\nuse Core\\GlobalContainer;\n\nclass EconomicHelper {\n\n  /**\n   * @var \\classConfig $config\n   */\n  protected $config;\n\n  /**\n   * @var array[] $resourceExchangeRates\n   */\n  protected $resourceExchangeRates = [];\n\n  /**\n   * EconomicHelper constructor.\n   *\n   * @param GlobalContainer $gc\n   */\n  public function __construct(GlobalContainer $gc) {\n//    if ($gc instanceof \\classConfig) {\n//      $this->config = $gc;\n//    } elseif ($gc instanceof GlobalContainer) {\n//      $this->config = $gc->config;\n//    } else {\n//      $this->config = \\SN::$config;\n//    }\n\n    $this->config = $gc->config;\n  }\n\n  /**\n   * Resets internal exchange cache\n   */\n  public function resetResourcesExchange() {\n    $this->resourceExchangeRates = [];\n  }\n\n  /**\n   * Gets current exchange rates\n   *\n   * @return float[] - [int resourceId] -> [float configuredExchangeRate]\n   */\n  public function getResourcesExchange() {\n    if (empty($this->resourceExchangeRates[UNIT_ANY])) {\n      $this->resourceExchangeRates[UNIT_ANY] = array(\n        RES_METAL       => 'rpg_exchange_metal',\n        RES_CRYSTAL     => 'rpg_exchange_crystal',\n        RES_DEUTERIUM   => 'rpg_exchange_deuterium',\n        RES_DARK_MATTER => 'rpg_exchange_darkMatter',\n      );\n\n      foreach ($this->resourceExchangeRates[UNIT_ANY] as &$rate) {\n        ($rate = floatval($this->config->$rate)) <= 0 ? $rate = 1 : false;\n      }\n    }\n\n    return $this->resourceExchangeRates[UNIT_ANY];\n  }\n\n  /**\n   * Get cost of all resources normalized to selected one\n   *\n   * Say, get cost of all resources in metal - for main calculations for comparing units between each other\n   *\n   * @param int $resourceId\n   *\n   * @return float[] - [int resourceId] -> [float ratesNormalizedToResourceCost]\n   */\n  public function getResourceExchangeIn($resourceId) {\n    if(empty($this->resourceExchangeRates[$resourceId])) {\n      $defaultRates = $this->getResourcesExchange();\n\n      $this->resourceExchangeRates[$resourceId] = [];\n      foreach($defaultRates as $defaultResourceId => $defaultRate) {\n        $this->resourceExchangeRates[$resourceId][$defaultResourceId] = $defaultRate / $defaultRates[$resourceId];\n      }\n    }\n\n    return $this->resourceExchangeRates[$resourceId];\n  }\n\n}\n"
  },
  {
    "path": "classes/Meta/Economic/ResourceCalculations.php",
    "content": "<?php\n/**\n * Created by Gorlum 13.10.2017 2:34\n */\n\nnamespace Meta\\Economic;\n\nuse \\SN;\n\nclass ResourceCalculations {\n  /**\n   * @var float[][] $storageMatrix - [resourceId => [unitId => storageSize]]\n   */\n  public $storageMatrix = null;\n  /**\n   * @var float[][] $productionFullMatrix - [resourceId => [productionUnitId => productionAmount]]\n   */\n  public $productionFullMatrix = [];\n  public $productionCurrentMatrix = [];\n  public $efficiency = 0;\n  public $energy = [];\n\n  protected $groupModifiers = [];\n  protected $groupPlanetDensities = [];\n\n  protected static $groupFactories = [];\n\n  protected static $storageCapacityFuncs = [];\n\n  protected static $staticInitialized = false;\n  protected static $mineSpeedCurrent = 1;\n  protected static $mineSpeedNormal = 1;\n  protected static $storageScaling = 1;\n\n  protected static $basicPlanetIncomeTable = [];\n  protected static $basicPlanetStorageTable = [];\n\n  public static function initStatic() {\n    if (static::$staticInitialized) {\n      return;\n    }\n\n    static::$mineSpeedNormal  = game_resource_multiplier(true);\n    static::$mineSpeedCurrent = game_resource_multiplier();\n    static::$storageScaling   = SN::$config->eco_scale_storage ? static::$mineSpeedNormal : 1;\n\n    static::$groupFactories = sn_get_groups('factories');\n\n    // Filling capacity functions list\n    foreach (sn_get_groups('storages') as $unit_id) {\n      foreach (get_unit_param($unit_id, P_STORAGE) as $resource_id => $function) {\n        static::$storageCapacityFuncs[$unit_id][$resource_id] = $function;\n      }\n    }\n\n    static::$basicPlanetIncomeTable[RES_METAL][0]     = floatval(SN::$config->metal_basic_income);\n    static::$basicPlanetIncomeTable[RES_CRYSTAL][0]   = floatval(SN::$config->crystal_basic_income);\n    static::$basicPlanetIncomeTable[RES_DEUTERIUM][0] = floatval(SN::$config->deuterium_basic_income);\n    static::$basicPlanetIncomeTable[RES_ENERGY][0]    = floatval(SN::$config->energy_basic_income);\n\n    static::$basicPlanetStorageTable[RES_METAL][0]     = floatval(SN::$config->eco_planet_storage_metal);\n    static::$basicPlanetStorageTable[RES_CRYSTAL][0]   = floatval(SN::$config->eco_planet_storage_crystal);\n    static::$basicPlanetStorageTable[RES_DEUTERIUM][0] = floatval(SN::$config->eco_planet_storage_deuterium);\n    static::$basicPlanetStorageTable[RES_ENERGY][0]    = 0;\n  }\n\n  public function __construct() {\n    static::initStatic();\n\n    $this->groupModifiers       = sn_get_groups(GROUP_MODIFIERS_NAME);\n    $this->groupPlanetDensities = sn_get_groups('planet_density');\n  }\n\n  public function eco_get_planet_caps(&$user, &$planet_row, $production_time = 0) {\n    // TODO Считать $production_time для термоядерной электростанции\n    $this->storageMatrix = static::$basicPlanetStorageTable;\n    foreach (static::$storageCapacityFuncs as $unit_id => $capacityFuncList) {\n      foreach ($capacityFuncList as $resource_id => $function) {\n        $this->storageMatrix[$resource_id][$unit_id] = floor(static::$storageScaling *\n          mrc_modify_value($user, $planet_row, $this->groupModifiers[MODIFIER_RESOURCE_CAPACITY], $function(mrc_get_level($user, $planet_row, $unit_id)))\n        );\n      }\n    }\n\n    $planet_row['metal_max']     = $this->getStorage(RES_METAL);\n    $planet_row['crystal_max']   = $this->getStorage(RES_CRYSTAL);\n    $planet_row['deuterium_max'] = $this->getStorage(RES_DEUTERIUM);\n\n    if ($planet_row['planet_type'] == PT_MOON) {\n      $planet_row['metal_perhour']     = 0;\n      $planet_row['crystal_perhour']   = 0;\n      $planet_row['deuterium_perhour'] = 0;\n      $planet_row['energy_used']       = 0;\n      $planet_row['energy_max']        = 0;\n\n      return $this;\n    }\n\n    $this->fillProductionMatrix($user, $planet_row);\n    $this->applyDensityModifiers($planet_row['density_index']);\n    $this->applyProductionSpeed();\n    $this->applyCapitalRates($user['id_planet'] == $planet_row['id']);\n    $this->applyProductionModifiers($user, $planet_row);\n\n    $this->productionCurrentMatrix = $this->productionFullMatrix;\n\n    $this->calculateEnergyBalance();\n    $this->applyEfficiency();\n\n    $planet_row['metal_perhour']     = $this->getProduction(RES_METAL);\n    $planet_row['crystal_perhour']   = $this->getProduction(RES_CRYSTAL);\n    $planet_row['deuterium_perhour'] = $this->getProduction(RES_DEUTERIUM);\n\n    $planet_row['energy_max']  = $this->energy[BUILD_CREATE];\n    $planet_row['energy_used'] = $this->energy[BUILD_DESTROY];\n\n    return $this;\n  }\n\n  public function getStorage($resourceId) {\n    return\n      is_array($this->storageMatrix[$resourceId]) && !empty($this->storageMatrix[$resourceId])\n        ? array_sum($this->storageMatrix[$resourceId])\n        : 0;\n  }\n\n  public function getProductionFull($resourceId) {\n    return\n      is_array($this->productionFullMatrix[$resourceId]) && !empty($this->productionFullMatrix[$resourceId])\n        ? array_sum($this->productionFullMatrix[$resourceId])\n        : 0;\n  }\n\n  public function getProduction($resourceId) {\n    $production = is_array($this->productionCurrentMatrix[$resourceId]) && !empty($this->productionCurrentMatrix[$resourceId])\n      ? array_sum($this->productionCurrentMatrix[$resourceId])\n      : 0;\n\n    return $production >= 0 ? floor($production) : ceil($production);\n  }\n\n  /**\n   * Applying core resource production multipliers to all mining info, including basic mining\n   *\n   * @param $planetDensity\n   */\n  protected function applyDensityModifiers($planetDensity) {\n    if (!empty($densityInfo = $this->groupPlanetDensities[$planetDensity][UNIT_RESOURCES])) {\n      foreach ($densityInfo as $resourceId => $densityMultiplier) {\n        if (!empty($this->productionFullMatrix[$resourceId])) {\n          foreach ($this->productionFullMatrix[$resourceId] as $miningUnitId => &$miningAmount) {\n            $miningAmount *= $densityMultiplier;\n          }\n        }\n      }\n    }\n  }\n\n  /**\n   * Applying game speed to mining rates\n   *\n   * @param bool $isCapital\n   */\n  protected function applyCapitalRates($isCapital) {\n    if (\n      !$isCapital\n      ||\n      empty(SN::$gc->config->planet_capital_mining_rate)\n      ||\n      SN::$gc->config->planet_capital_mining_rate == 1\n    ) {\n      return;\n    }\n\n    foreach ($this->productionFullMatrix as $resourceId => &$miningData) {\n      foreach ($miningData as $miningUnitId => &$miningAmount) {\n        $miningAmount *= SN::$gc->config->planet_capital_mining_rate;\n      }\n    }\n  }\n\n  /**\n   * Applying game speed to mining rates\n   */\n  protected function applyProductionSpeed() {\n    foreach ($this->productionFullMatrix as $resourceId => &$miningData) {\n      foreach ($miningData as $miningUnitId => &$miningAmount) {\n        $miningAmount *= ($resourceId == RES_ENERGY ? static::$mineSpeedNormal : static::$mineSpeedCurrent);\n      }\n    }\n  }\n\n  /**\n   * Applying resource production modifiers\n   *\n   * @param $user\n   * @param $planet_row\n   */\n  protected function applyProductionModifiers(&$user, &$planet_row) {\n    foreach ($this->productionFullMatrix as &$resourceProductionTable) {\n      foreach ($resourceProductionTable as $mineId => &$mineProduction) {\n        $mineProduction = floor(mrc_modify_value($user, $planet_row, $this->groupModifiers[MODIFIER_RESOURCE_PRODUCTION], $mineProduction));\n      }\n    }\n  }\n\n  protected function calculateEnergyBalance() {\n    if ($this->productionCurrentMatrix[RES_ENERGY][STRUC_MINE_FUSION]) {\n      $deuterium_balance = array_sum($this->productionCurrentMatrix[RES_DEUTERIUM]);\n      $energy_balance    = array_sum($this->productionCurrentMatrix[RES_ENERGY]);\n      if ($deuterium_balance < 0 || $energy_balance < 0) {\n        $this->productionCurrentMatrix[RES_DEUTERIUM][STRUC_MINE_FUSION] = $this->productionCurrentMatrix[RES_ENERGY][STRUC_MINE_FUSION] = 0;\n      }\n    }\n\n    foreach ($this->productionCurrentMatrix[RES_ENERGY] as $energy) {\n      $this->energy[$energy >= 0 ? BUILD_CREATE : BUILD_DESTROY] += $energy;\n    }\n\n    $this->energy[BUILD_DESTROY] = -$this->energy[BUILD_DESTROY];\n  }\n\n  /**\n   * Calculate total efficiency and applying to production\n   */\n  protected function applyEfficiency() {\n    $this->efficiency = $this->energy[BUILD_DESTROY] > $this->energy[BUILD_CREATE]\n      ? $this->energy[BUILD_CREATE] / $this->energy[BUILD_DESTROY]\n      : 1;\n\n    // If mines on 100% efficiency - doing nothing\n    if ($this->efficiency == 1) {\n      return;\n    }\n\n    // Adjusting current production with efficiency\n    foreach ($this->productionCurrentMatrix as $resource_id => &$resource_data) {\n      foreach ($resource_data as $unit_id => &$resource_production) {\n        if (!($unit_id == STRUC_MINE_FUSION && $resource_id == RES_DEUTERIUM) && $unit_id != 0 && !($resource_id == RES_ENERGY && $resource_production >= 0)) {\n          $resource_production = $resource_production * $this->efficiency;\n        }\n      }\n    }\n  }\n\n  /**\n   * @param $user\n   * @param $planet_row\n   */\n  protected function fillProductionMatrix(&$user, &$planet_row) {\n    $this->productionFullMatrix = static::$basicPlanetIncomeTable;\n    foreach (static::$groupFactories as $unit_id) {\n      $unit_data_production = get_unit_param($unit_id, P_UNIT_PRODUCTION);\n      $unit_level           = mrc_get_level($user, $planet_row, $unit_id);\n      $unit_load            = $planet_row[pname_factory_production_field_name($unit_id)];\n\n      foreach ($unit_data_production as $resource_id => $function) {\n        $this->productionFullMatrix[$resource_id][$unit_id] = $function($unit_level, $unit_load, $user, $planet_row);\n      }\n    }\n  }\n\n}\n"
  },
  {
    "path": "classes/Modules/ModulesManager.php",
    "content": "<?php\n/**\n * Created by Gorlum 19.03.2018 19:56\n */\n\nnamespace Modules;\n\nuse Modules\\sn_module;\nuse Core\\GlobalContainer;\n\n\n/**\n * Class ModulesManager\n *\n * Modules ModulesManager\n * Replacement for removed $sn_module and $sn_module_list global variables\n *\n * @package Modules\n */\nclass ModulesManager {\n  /**\n   * @var \\Core\\GlobalContainer $gc\n   */\n  protected $gc;\n\n  /**\n   * Is modules already loaded?\n   *\n   * @var bool $modulesAreLoaded\n   */\n  protected $modulesAreLoaded = false;\n\n  /**\n   * Plain list of available modules\n   *\n   * @var sn_module[] $modules - [(string)$moduleName => (sn_module)$module]\n   */\n  protected $modules = [];\n\n  /**\n   * Modules arranged per package\n   *\n   * @var sn_module[][] $packages - [[(string)$package] => [(string)$moduleName => (sn_module)$module]]\n   */\n  protected $packages = [];\n\n\n  /**\n   * ModulesManager constructor.\n   *\n   * @param GlobalContainer $gc\n   */\n  public function __construct($gc) {\n    $this->gc = $gc;\n\n    $this->loadModules(SN_ROOT_MODULES);\n  }\n\n\n  /**\n   * @param string $dir_name\n   */\n  public function loadModules($dir_name = SN_ROOT_MODULES) {\n    if ($this->modulesAreLoaded) {\n      return;\n    }\n\n    if (file_exists($dir_name) && is_dir($dir_name)) {\n      $this->loadModulesFromDirectory($dir_name, PHP_EX);\n    }\n\n    $this->modulesAreLoaded = true;\n  }\n\n  /**\n   *\n   */\n  public function initModules() {\n    $loadOrder = $this->getLoadOrder();\n\n    // Инициализируем модули\n    // По нормальным делам это должна быть загрузка модулей и лишь затем инициализация - что бы минимизировать размер процесса в памяти\n    foreach ($loadOrder as $moduleName => $place) {\n      $module = $this->modules[$moduleName];\n\n      if ($place >= 0) {\n        $module->check_status();\n        if (!$module->isActive()) {\n          unset($this->modules[$moduleName]);\n          continue;\n        }\n\n        $module->initialize();\n      } else {\n        unset($module);\n      }\n    }\n  }\n\n  /**\n   * @param $dir_name\n   * @param $load_extension\n   */\n  protected function loadModulesFromDirectory($dir_name, $load_extension) {\n    $dir = opendir($dir_name);\n    while (($file = readdir($dir)) !== false) {\n      if ($file == '..' || $file == '.') {\n        continue;\n      }\n\n      $full_filename = $dir_name . $file;\n      if (is_dir($full_filename) && file_exists($full_filename = \"{$full_filename}/{$file}.{$load_extension}\")) {\n        require_once($full_filename);\n\n        // Registering module\n        if (class_exists($file)) {\n          // TODO Module SHOULD register himself!\n//          new $file($full_filename)\n          $this->registerModule($file, new $file($full_filename));\n        }\n      }\n    }\n  }\n\n  /**\n   * Get module load order counting requirements and each module load order\n   *\n   * @return int[] - [(string)$moduleName => (int)$loadOrder]\n   */\n  protected function getLoadOrder() {\n    // Генерируем список требуемых модулей\n    $loadOrder = [];\n    $sn_req = [];\n\n    foreach ($this->modules as $loaded_module_name => $module) {\n      $loadOrder[$loaded_module_name] = $module->getLoadOrder();\n      if (!empty($module->manifest[sn_module::M_REQUIRE])) {\n        foreach ($module->manifest[sn_module::M_REQUIRE] as $require_name) {\n          $sn_req[$loaded_module_name][$require_name] = 0;\n        }\n      }\n    }\n\n    // Создаем последовательность инициализации модулей\n    // По нормальным делам надо сначала читать их конфиги - вдруг какой-то модуль отключен?\n    do {\n      $prev_order = $loadOrder;\n\n      foreach ($sn_req as $loaded_module_name => &$req_data) {\n        $level = 1;\n        foreach ($req_data as $req_name => &$req_level) {\n          if ($loadOrder[$req_name] == -1 || !isset($loadOrder[$req_name])) {\n            $level = $req_level = -1;\n            break;\n          } else {\n            $level += $loadOrder[$req_name];\n          }\n          $req_level = $loadOrder[$req_name];\n        }\n        if ($level > $loadOrder[$loaded_module_name] || $level == -1) {\n          $loadOrder[$loaded_module_name] = $level;\n        }\n      }\n    } while ($prev_order != $loadOrder);\n\n    asort($loadOrder);\n\n    return $loadOrder;\n  }\n\n  /**\n   * @param string|array $groups - Module group name or '' for any group\n   * @param bool         $active - returns only active modules\n   *\n   * @return int\n   */\n  public function countModulesInGroup($groups = '', $active = true) {\n    return count($this->getModulesInGroup($groups, $active));\n  }\n\n  /**\n   * Getting list of active modules\n   *\n   * @param string|array $groups - Module group name or ''|[] for any group\n   * @param bool         $active - returns only active modules\n   *\n   * @return sn_module[]\n   */\n  public function getModulesInGroup($groups = [], $active = true) {\n    // If no groups specified - iterating all groups\n    if (empty($groups)) {\n      $groups = array_keys($this->packages);\n    }\n\n    if (!is_array($groups)) {\n      $groups = [$groups];\n    }\n\n    $activeModules = [];\n    foreach ($groups as $groupName) {\n      if (is_array($this->packages[$groupName]) && !empty($this->packages[$groupName])) {\n        foreach ($this->packages[$groupName] as $moduleName => $module) {\n          if (!$active || $module->isActive()) {\n            $activeModules[$moduleName] = $module;\n          }\n        }\n      }\n    }\n\n    return $activeModules;\n  }\n\n\n  /**\n   * Get module by name\n   *\n   * @param string $moduleName\n   * @param bool   $active - return module only if it is active\n   *\n   * @return sn_module|null\n   */\n  public function getModule($moduleName, $active = true) {\n    return !empty($this->modules[$moduleName]) && (!$active || $this->modules[$moduleName]->isActive()) ? $this->modules[$moduleName] : null;\n  }\n\n  /**\n   * Register module for further use\n   *\n   * Basically modules does not exists anywhere except in Modules ModulesManager\n   *\n   * @param string    $moduleName\n   * @param sn_module $module\n   */\n  public function registerModule($moduleName, $module) {\n    $this->modules[$moduleName] = $module;\n    $this->packages[$module->manifest['package']][$moduleName] = &$this->modules[$moduleName];\n  }\n\n}\n"
  },
  {
    "path": "classes/Modules/sn_module.php",
    "content": "<?php\n\nnamespace Modules;\n\nuse Common\\Hooker\\Pimp;\nuse Core\\Autoloader;\nuse Exception;\nuse SN;\nuse SnTemplate;\nuse template;\n\nclass sn_module {\n  // Manifest params\n  const M_REQUIRE = 'require';\n  const M_ROOT_RELATIVE = 'root_relative';\n  const M_LOAD_ORDER = 'load_order';\n\n  const MANIFEST_CSS = 'css';\n\n  /**\n   * SN version in which module was committed. Can be treated as version in which module guaranteed to work\n   * @var string $versionCommitted\n   */\n  public $versionCommitted = '#46d0#';\n  /**\n   * Is module currently active?\n   *\n   * @var bool $active\n   */\n  protected $active = true;\n  /**\n   * Is all module DB artifacts presents?\n   *\n   * Check for module's tables, settings etc\n   *\n   * @var bool $installed\n   */\n  protected $installed = true;\n\n  public $manifest = [\n    'package'   => 'core',\n    'name'      => 'Modules\\sn_module',\n    'version'   => '1c0',\n    'copyright' => 'Project \"SuperNova.WS\" #46d0# copyright © 2009-2025 Gorlum',\n\n    self::M_LOAD_ORDER => MODULE_LOAD_ORDER_DEFAULT,\n\n    self::M_REQUIRE       => [],\n    self::M_ROOT_RELATIVE => '',\n\n    // 'constants' array - contents of this array would be defined in SN\n    'constants'           => [\n//      'UNIT_STRUCTURE_NEW' => 999999,\n    ],\n\n    'vars'      => [], // Just a placeholder. vars assigned via special method __assign_vars(). Need 'cause of new constants that can be defined within module. See below\n\n    // 'functions' array - this functions would be installed as hooks\n    // Key: overwritable function name to replace\n    // Value: which method to use. Format: [*][<object_name>][.]<method>\n    // '*' means that new function would replace old one\n    // If object_name is ommited but \".\" is present - hook linked to global function\n    // If only \"method\" present - overwritable linked to appropriate method of current object\n    // Function/Method should be accessible on module init\n    'functions' => [\n//      'test_object_test_method' => 'test_object.test_method',\n//      'test_function' => '.my_test_function',\n//      'this_object_test_method' => 'test_method',\n    ],\n\n    // 'menu' array - this menu items would be merged into main game menu\n    // Array element almost identical to $sn_menu with additional param 'LOCATION'.\n    // 'LOCATION' => '-news', // Special atrtribute for modules\n    // [-|+][<menu_item_id>]\n    // <menu_item_id> identifies menu item aginst new menu item would be placed. When ommited new item placed against whole menu\n    // -/+ indicates that new item should be placed before/after identified menu item (or whole menu). If ommited and menu item exists - new item will replace previous one\n    // Empty or non-existent LOCATION equivalent to '+' - place item at end of menu\n    // Non-existent menu_item_id treated as ommited\n    'menu'      => [],\n\n    // 'page' array - defines pages which will handle this module and appropriate handlers\n    'page'      => [],\n\n    /**\n     * 'mvc' subarray\n     * [\n     *    FIELD_MODEL =>\n     *    FIELD_VIEW =>\n     *    MVC_OPTIONS =>\n     * ]\n     */\n    'mvc'       => [],\n  ];\n\n  /**\n   * New way to add functions instead of manifest['functions']\n   *\n   * [\n   *   (string)$functionName => [\n   *     (callable)$callable,\n   *     (string)'methodName',                             // Local method name aka $this->methodName\n   *     (callable array)[$this|objectName, 'methodName'], // Callable array\n   *   ],\n   * ]\n   *\n   * @var array $functions\n   */\n  protected $functions = [];\n\n  protected $hooks = [];\n\n  protected $config = array();\n\n  protected $module_full_class_path = __FILE__;\n\n  protected $filename = '';\n\n  /**\n   * @param string   $functionName\n   * @param callable $callable\n   */\n  public function addFunctionHook($functionName, $callable) {\n    $this->functions[$functionName][] = $callable;\n  }\n\n  /**\n   * Динамическое назначение переменных\n   *\n   * Актуально, когда записываемые данные зависят от статуса игры\n   * Например - назначаются константы внутри модуля\n   *\n   * @return array\n   */\n  protected function __assign_vars() {\n    return array(/*\n      'sn_data' => array(\n        UNIT_STRUCTURE_NEW => array( // Will honor new constants\n          'name' => 'robot_factory',\n          'type' => UNIT_STRUCTURE,\n          'location' => LOC_PLANET,\n          'cost' => array(\n            RES_METAL     => 400,\n            RES_CRYSTAL   => 120,\n            RES_DEUTERIUM => 200,\n            RES_ENERGY    => 0,\n            'factor' => 2,\n          ),\n          'metal' => 400,\n          'crystal' => 120,\n          'deuterium' => 200,\n          'energy' => 0,\n          'factor' => 2,\n        ),\n      ),\n*/\n    );\n  }\n\n  /**\n   * sn_module constructor.\n   *\n   * @param string $filename\n   *\n   * @throws Exception\n   */\n  public function __construct($filename = __FILE__) {\n    $this->filename = $filename;\n\n    // Getting module PHP class name\n    $class_module_name = get_called_class();\n\n    // Validating source settings. Some fields are mandatory in each manifest\n    // Should be removed when manifest would be parsed to separate fields\n    foreach (['name', 'package', 'version'] as $mandatoryManifest) {\n      if (!array_key_exists($mandatoryManifest, $this->manifest)) {\n        throw new Exception('{ There is no mandatory field \"' . $mandatoryManifest . '\" in manifest of module } \"' . $class_module_name . '\"');\n      }\n    }\n\n    // Getting module root relative to SN\n    $this->manifest[static::M_ROOT_RELATIVE] = str_replace(\n      [SN_ROOT_PHYSICAL, basename($this->filename)],\n      '',\n      str_replace('\\\\', '/', $this->filename)\n    );\n\n    // TODO: Load configuration from DB. Manifest setting\n    // Trying to load configuration from file\n    $config_exists = false;\n    // Конфигурация может лежать в config_path в манифеста или в корне модуля\n    if (isset($this->manifest['config_path']) && file_exists($config_filename = $this->manifest['config_path'] . '/' . SN_MODULE_CONFIG_NAME)) {\n      $config_exists = true;\n    } elseif (file_exists($config_filename = dirname($filename) . '/' . SN_MODULE_CONFIG_NAME)) {\n      $config_exists = true;\n    }\n\n    if ($config_exists) {\n      /** @noinspection PhpIncludeInspection */\n      include($config_filename);\n      $module_config_array = $class_module_name . '_config';\n      $this->config        = $$module_config_array;\n    }\n\n    // Registering classes with autoloader\n    Autoloader::register($this->getRootRelative() . 'classes/');\n\n    // TODO - currently not possible because each module is not a service\n    // When it's done - remove double registration from loadModulesFromDirectory()\n    // Registering module in manager\n//    SN::$gc->modules->registerModule($class_module_name, $this);\n  }\n\n  protected function __patch_menu(&$sn_menu_extra, &$menu_patch, $admin = false) {\n    if (isset($menu_patch) && is_array($menu_patch) && !empty($menu_patch)) {\n      foreach ($menu_patch as $menu_item_name => $menu_item_data) {\n        $sn_menu_extra[$menu_item_name] = $menu_item_data;\n      }\n    }\n  }\n\n\n  /**\n   *\n   */\n  public function initialize() {\n    // Checking module status - is it installed and active\n    $this->check_status();\n    if (!$this->isActive()) {\n      return;\n    }\n\n    // Setting constants - if any\n    if (isset($this->manifest['constants']) && is_array($this->manifest['constants']) && !empty($this->manifest['constants'])) {\n      foreach ($this->manifest['constants'] as $constant_name => $constant_value) {\n        defined($constant_name) or define($constant_name, $constant_value);\n      }\n    }\n\n    // Adding vars - if any\n    // Due to possible introduce of new constants in previous step vars is assigned via special method to honor new constants\n    // Assignation can work with simple variables and with multidimensional arrays - for ex. 'sn_data[groups][test]'\n    // New values from module variables will overwrite previous values (for root variables) and array elements with corresponding indexes (for arrays)\n    // Constants as array indexes are honored - it's make valid such declarations as 'sn_data[ques][QUE_STRUCTURES]'\n    $this->manifest['vars'] = $this->__assign_vars();\n    if (!empty($this->manifest['vars'])) {\n      $vars_assigned = array();\n      foreach ($this->manifest['vars'] as $var_name => $var_value) {\n        $sub_vars = explode('[', str_replace(']', '', $var_name));\n        $var_name = $sub_vars[0];\n\n        if (!isset($vars_assigned[$var_name])) {\n          $vars_assigned[$var_name] = true;\n          global $$var_name;\n        }\n\n        $pointer = &$$var_name;\n        if (($n = count($sub_vars)) > 1) {\n          for ($i = 1; $i < $n; $i++) {\n            if (defined($sub_vars[$i])) {\n              $sub_vars[$i] = constant($sub_vars[$i]);\n            }\n\n            if (!isset($pointer[$sub_vars[$i]]) && $i != $n) {\n              $pointer[$sub_vars[$i]] = array();\n            }\n            $pointer = &$pointer[$sub_vars[$i]];\n          }\n        }\n\n        if (!isset($pointer) || !is_array($pointer)) {\n          $pointer = $var_value;\n        } elseif (is_array($$var_name)) {\n          $pointer = array_merge_recursive_numeric($pointer, $var_value);\n        }\n      }\n    }\n\n    // Overriding function if any\n    global $functions;\n    sn_sys_handler_add($functions, $this->manifest['functions'], $this);\n\n    foreach ($this->functions as $functionName => $callableList) {\n      !is_array($callableList) ? $callableList = [$callableList] : false;\n      foreach ($callableList as $callable) {\n        sys_handler_add_one($functions, $functionName, $callable, static::class, '');\n      }\n    }\n\n    $this->registerHooks();\n\n    // Patching game menu - if any\n    global $sn_menu_extra, $sn_menu_admin_extra;\n    isset($this->manifest['menu']) and $this->__patch_menu($sn_menu_extra, $this->manifest['menu']);\n    isset($this->manifest['menu_admin']) and $this->__patch_menu($sn_menu_admin_extra, $this->manifest['menu_admin'], true);\n\n    global $sn_mvc;\n    foreach ($sn_mvc as $handler_type => &$handler_data) {\n      if ($handler_type == MVC_OPTIONS) {\n        continue;\n      }\n      sn_sys_handler_add($handler_data, $this->manifest['mvc'][$handler_type], $this, $handler_type);\n    }\n\n    if (!empty($this->manifest['mvc'][MVC_OPTIONS])) {\n      foreach ($this->manifest['mvc'][MVC_OPTIONS] as $pageName => $pageOptions) {\n        if (empty($pageOptions)) {\n          continue;\n        }\n\n        !is_array($sn_mvc['pages'][$pageName][MVC_OPTIONS]) ? $sn_mvc['pages'][$pageName][MVC_OPTIONS] = [] : false;\n        $sn_mvc['pages'][$pageName][MVC_OPTIONS] = array_merge($sn_mvc['pages'][$pageName][MVC_OPTIONS], $pageOptions);\n      }\n    }\n\n    if (isset($this->manifest['i18n']) && is_array($this->manifest['i18n']) && !empty($this->manifest['i18n'])) {\n      foreach ($this->manifest['i18n'] as $i18n_page_name => &$i18n_file_list) {\n        foreach ($i18n_file_list as &$i18n_file_data) {\n          if (is_array($i18n_file_data) && !$i18n_file_data['path']) {\n            $i18n_file_data['path'] = $this->getRootRelative();\n          }\n        }\n        if (!isset($sn_mvc['i18n'][$i18n_page_name])) {\n          $sn_mvc['i18n'][$i18n_page_name] = array();\n        }\n        $sn_mvc['i18n'][$i18n_page_name] += $i18n_file_list;\n      }\n    }\n\n    if (!empty($this->manifest['javascript']) && is_array($this->manifest['javascript'])) {\n      foreach ($this->manifest['javascript'] as $javascript_page_name => &$javascript_list) {\n        !isset($sn_mvc['javascript'][$javascript_page_name]) ? $sn_mvc['javascript'][$javascript_page_name] = array() : false;\n        foreach ($javascript_list as $script_name => &$script_content) {\n          $sn_mvc['javascript'][$javascript_page_name][$script_name] = $script_content;\n        }\n      }\n    }\n\n    if (!empty($this->manifest['css']) && is_array($this->manifest['css'])) {\n      foreach ($this->manifest['css'] as $javascript_page_name => &$javascript_list) {\n        !isset($sn_mvc['css'][$javascript_page_name]) ? $sn_mvc['css'][$javascript_page_name] = array() : false;\n        foreach ($javascript_list as $script_name => &$script_content) {\n          $sn_mvc['css'][$javascript_page_name][$script_name] = $script_content;\n        }\n      }\n    }\n\n    if (!empty($this->manifest['navbar_prefix_button']) && is_array($this->manifest['navbar_prefix_button'])) {\n      foreach ($this->manifest['navbar_prefix_button'] as $button_image => $button_url_relative) {\n        $sn_mvc['navbar_prefix_button'][$button_image] = $button_url_relative;\n      }\n    }\n\n    if (!empty($this->manifest['navbar_main_button']) && is_array($this->manifest['navbar_main_button'])) {\n      foreach ($this->manifest['navbar_main_button'] as $button_image => $button_url_relative) {\n        $sn_mvc['navbar_main_button'][$button_image] = $button_url_relative;\n      }\n    }\n  }\n\n  public function check_status() {\n  }\n\n  /**\n   * Checks if module is active\n   *\n   * @return bool\n   */\n  public function isActive() {\n    return !empty($this->active) && $this->isInstalled();\n  }\n\n  /**\n   * Checks if module is installed\n   *\n   * @return bool\n   */\n  public function isInstalled() {\n    return !empty($this->installed);\n  }\n\n  /**\n   * Register pages in $manifest['mvc']['pages'] for further use\n   *\n   * @param string[] $pages - array of records ['pageName' => 'pageFile']. 'pageFile' is currently unused\n   *\n   * @deprecated\n   */\n  protected function __mvcRegisterPagesOld($pages) {\n    !is_array($this->manifest['mvc']['pages']) ? $this->manifest['mvc']['pages'] = [] : false;\n    if (is_array($pages) && !empty($pages)) {\n      $this->manifest['mvc']['pages'] = array_merge($this->manifest['mvc']['pages'], $pages);\n    }\n  }\n\n  protected function registerHooks() {\n    foreach ($this->hooks as $hookName => $hookRecord) {\n      // Priority can be first element of hook array\n      $priority = Pimp::ORDER_AS_IS;\n      if (is_array($hookRecord) && count($hookRecord) > 1 && is_numeric(reset($hookRecord))) {\n        $priority = intval(reset($hookRecord));\n        array_shift($hookRecord);\n      }\n\n      // There is 2 elements in callable array\n      if (is_array($hookRecord) && 2 == count($hookRecord)) {\n        // Checking - if first should be replaced with $this\n        if (THIS_STRING === reset($hookRecord)) {\n          $hookRecord = [$this, end($hookRecord)];\n        }\n      }\n\n      SN::$gc->pimp->register($hookName, $hookRecord, $priority);\n    }\n  }\n\n  public function getLoadOrder() {\n    return !empty($this->manifest[self::M_LOAD_ORDER]) ? $this->manifest[self::M_LOAD_ORDER] : MODULE_LOAD_ORDER_DEFAULT;\n  }\n\n  public function getRootRelative() {\n    if (empty($this->manifest[static::M_ROOT_RELATIVE])) {\n      $this->manifest[static::M_ROOT_RELATIVE] = str_replace([SN_ROOT_PHYSICAL, basename($this->filename)], '', str_replace('\\\\', '/', $this->filename));\n    }\n\n    return $this->manifest[static::M_ROOT_RELATIVE];\n  }\n\n  protected function getTemplateRootRelative() {\n    return $this->getRootRelative() . SnTemplate::SN_TEMPLATES_PARTIAL_PATH;\n  }\n\n  /**\n   *\n   * Should stay public due using in Festivals (?)\n   *\n   * @param string        $templateName\n   * @param template|null $template\n   *\n   * @return template\n   */\n  public function addModuleTemplate($templateName, $template) {\n    return SnTemplate::gettemplate(\n      $templateName,\n      $template,\n      SN_ROOT_PHYSICAL . $this->getTemplateRootRelative(),\n      is_object($template) ? $template->root : SnTemplate::getCurrentTemplate()->getPathFull()\n    );\n  }\n\n  /**\n   * @param string $jsName\n   *\n   * @return array\n   */\n  protected function addModuleJavascript($jsName) {\n    global $template_result, $sn_mvc;\n\n    if (empty($sn_mvc['javascript_filenames'])) {\n      $sn_mvc['javascript_filenames'] = [];\n    }\n    $sn_mvc['javascript_filenames'][] = $this->getRootRelative() . $jsName;\n\n    return $template_result;\n  }\n\n  /**\n   * Get module version\n   *\n   * @return string\n   */\n  public function getVersion() {\n    if (!empty($this->versionCommitted)) {\n      $version = $this->versionCommitted;\n    } else {\n      $version = $this->manifest['version'];\n    }\n\n    return trim($version, '#');\n  }\n\n  /**\n   * Get module full name as registered in module manager\n   *\n   * @return string\n   */\n  public function getFullName() {\n    return get_called_class();\n  }\n\n}\n"
  },
  {
    "path": "classes/Note/Note.php",
    "content": "<?php\n/**\n * Created by Gorlum 04.12.2017 5:10\n */\n\nnamespace Note;\n\nuse template;\nuse HelperString;\n\nclass Note {\n\n  /**\n   * @param template $template\n   * @param array    $note_row\n   *\n   * @deprecated\n   */\n  public static function note_assign(&$template, $note_row) {\n    global $note_priority_classes, $lang;\n\n    $template->assign_block_vars('note', [\n      'ID'                     => $note_row['id'],\n      'TIME'                   => $note_row['time'],\n      'TIME_TEXT'              => date(FMT_DATE_TIME, $note_row['time']),\n      'PRIORITY'               => $note_row['priority'],\n      'PRIORITY_CLASS'         => $note_priority_classes[$note_row['priority']],\n      'PRIORITY_TEXT'          => $lang['sys_notes_priorities'][$note_row['priority']],\n      'TITLE'                  => htmlentities($note_row['title'], ENT_COMPAT, 'UTF-8'),\n      'GALAXY'                 => intval($note_row['galaxy']),\n      'SYSTEM'                 => intval($note_row['system']),\n      'PLANET'                 => intval($note_row['planet']),\n      'PLANET_TYPE'            => intval($note_row['planet_type']),\n      'PLANET_TYPE_TEXT'       => $lang['sys_planet_type'][$note_row['planet_type']],\n      'PLANET_TYPE_TEXT_SHORT' => $lang['sys_planet_type_sh'][$note_row['planet_type']],\n      'TEXT'                   => HelperString::htmlEncode($note_row['text'], HTML_ENCODE_MULTILINE),\n      'TEXT_EDIT'              => htmlentities($note_row['text'], ENT_COMPAT, 'UTF-8'),\n      'STICKY'                 => intval($note_row['sticky']),\n    ]);\n  }\n\n}\n"
  },
  {
    "path": "classes/Notification/RecordNotification.php",
    "content": "<?php\n\n/**\n * Created by Gorlum 12.06.2017 14:47\n */\n\nnamespace Notification;\n\nuse DBAL\\ActiveRecord;\n\n/**\n * Class RecordNotification\n *\n * @property string     $createdSql\n * @property int|string $ownerId\n * @property string     $text\n *\n * @package Notification\n *\n */\nclass RecordNotification extends ActiveRecord {\n  protected static $_tableName = 'notifications';\n\n  protected static $_fieldsToProperties = [\n    'timestamp' => 'createdSql',\n    'owner'     => 'ownerId',\n    'text'      => 'text',\n  ];\n\n}\n"
  },
  {
    "path": "classes/Old/Avatar.php",
    "content": "<?php\n/**\n * Created by Gorlum 15.08.2019 6:16\n */\n\nnamespace Old;\n\nuse Exception;\n\nclass Avatar {\n\n  public static function sys_avatar_upload($subject_id, &$avatar_field, $prefix = 'avatar') {\n    global $config, $lang, $user;\n\n    try {\n      $avatar_filename = $fullsize_filename = SN_PATH_AVATAR . $prefix . '_' . $subject_id;\n      $avatar_filename .= '.png';\n      $fullsize_filename .= '_full.png';\n      if (sys_get_param_int('avatar_remove')) {\n        if (file_exists($avatar_filename) && !unlink($avatar_filename)) {\n          throw new Exception($lang['opt_msg_avatar_error_delete'], ERR_ERROR);\n        }\n        $avatar_field = 0;\n        throw new Exception($lang['opt_msg_avatar_removed'], ERR_NONE);\n      } elseif ($_FILES['avatar']['size']) {\n        if (!in_array($_FILES['avatar']['type'], array('image/gif', 'image/jpeg', 'image/jpg', 'image/pjpeg', 'image/png')) || $_FILES['avatar']['size'] > 204800) {\n          throw new Exception($lang['opt_msg_avatar_error_unsupported'], ERR_WARNING);\n        }\n\n        if ($_FILES['avatar']['error']) {\n          throw new Exception(sprintf($lang['opt_msg_avatar_error_upload'], $_FILES['avatar']['error']), ERR_ERROR);\n        }\n\n        if (!($avatar_image = imagecreatefromstring(file_get_contents($_FILES['avatar']['tmp_name'])))) {\n          throw new Exception($lang['opt_msg_avatar_error_unsupported'], ERR_WARNING);\n        }\n\n        $avatar_size = getimagesize($_FILES['avatar']['tmp_name']);\n        $avatar_max_width = $config->avatar_max_width;\n        $avatar_max_height = $config->avatar_max_height;\n        if ($avatar_size[0] > $avatar_max_width || $avatar_size[1] > $avatar_max_height) {\n          $aspect_ratio = min($avatar_max_width / $avatar_size[0], $avatar_max_height / $avatar_size[1]);\n          $avatar_image_new = imagecreatetruecolor($avatar_size[0] * $aspect_ratio, $avatar_size[0] * $aspect_ratio);\n          $result = imagecopyresized($avatar_image_new, $avatar_image, 0, 0, 0, 0, $avatar_size[0] * $aspect_ratio, $avatar_size[0] * $aspect_ratio, $avatar_size[0], $avatar_size[1]);\n          imagedestroy($avatar_image);\n          $avatar_image = $avatar_image_new;\n        }\n\n        if (file_exists($avatar_filename) && !unlink($avatar_filename)) {\n          throw new Exception($lang['opt_msg_avatar_error_delete'], ERR_ERROR);\n        }\n\n        if (!imagepng($avatar_image, $avatar_filename, 9)) {\n          throw new Exception($lang['opt_msg_avatar_error_writing'], ERR_ERROR);\n        }\n\n        $avatar_field = 1;\n        imagedestroy($avatar_image);\n        throw new Exception($lang['opt_msg_avatar_uploaded'], ERR_NONE);\n      }\n    } catch (Exception $e) {\n      return array(\n        'STATUS'  => in_array($e->getCode(), array(ERR_NONE, ERR_WARNING, ERR_ERROR)) ? $e->getCode() : ERR_ERROR,\n        'MESSAGE' => $e->getMessage()\n      );\n    }\n  }\n\n}\n"
  },
  {
    "path": "classes/PTLTag.php",
    "content": "<?php\n\n/**\n * Created by Gorlum 23.02.2017 14:34\n */\nclass PTLTag {\n\n  /**Raw tag\n   *\n   * @var string $raw\n   */\n  public $raw = '';\n  /**\n   * Fully resolved tag without params\n   *\n   * @var mixed|string $resolved\n   */\n  public $resolved = '';\n  /**\n   * Namespace for block refs\n   *\n   * @var string $namespace\n   */\n  public $namespace = '';\n  /**\n   * Array of tag params\n   *\n   * @var array $params\n   */\n  public $params = array();\n\n  /**\n   * Template via which this tag is compiled\n   *\n   * @var template $template\n   */\n  public $template;\n\n  /**\n   * Ресолвит переменные и парсит тэг\n   *\n   * @param string        $stringTag - tag from PTL\n   * @param template|null $template - template object which used to resolve tags\n   * @param array         $allowedParamsAsKeys - array of param name as a key like array('paramName' => mixed)\n   */\n  public function __construct($stringTag, $template = null, $allowedParamsAsKeys = array()) {\n    $this->template = $template;\n    $this->raw = $stringTag;\n\n    // Separating params - so there are will be no false-positives for template's variable names\n    if (strpos($this->raw, '|') !== false) {\n      $this->params = explode('|', $this->raw);\n      $this->resolved = $this->params[0];\n      unset($this->params[0]);\n    } else {\n      $this->params = array();\n      $this->resolved = $this->raw;\n    }\n\n    // Is there any template variables? Template variables passed in square brackets\n    if (strpos($this->resolved, '[') !== false && is_object($this->template)) {\n      $this->resolved = $this->resolveTemplateVars($this->resolved);\n    }\n\n    // Is there any namespaces in tag?\n    if (strpos($this->resolved, '.') !== false) {\n      $this->namespace = substr($this->resolved, 0, strrpos($this->resolved, '.'));\n    }\n\n    // Here so we potentially can use 2nd-level params - i.e. in template's variables\n    if (count($this->params)) {\n      $this->params = HelperArray::parseParamStrings($this->params);\n      $this->params = array_intersect_key($this->params, $allowedParamsAsKeys);\n      ksort($this->params);\n    }\n  }\n\n  /**\n   * Resolves template's variables in tag\n   *\n   * Template's variables defined in square brackets:\n   *    [$DEFINE] - Defines is a DEFINE-d variable like <!-- DEFINE $VAR = 'value' -->\n   *    [VAR_NAME] - Root variable like $template->assign_var($var_name, $value)\n   *    [block_name.BLOCK_VAR_NAME] - Block variable like $template->assign_block_var($block_name, array($block_var => $value));\n   *                                  Use last level block name for access multilevel blocks - i.e. [e.E1] for accessing current value of q.w.e.E1\n   *\n   * Other constructions (like {D_xxx}, {C_xxx} etc) is not supported\n   *\n   * @param string $rawTag\n   *\n   * @return mixed|string\n   */\n  protected function resolveTemplateVars($rawTag) {\n    // Многоуровневые вложения ?! Надо ли и где их можно применить\n    preg_match_all('#(\\[.+?\\])#', $rawTag, $matches);\n\n    foreach ($matches[0] as &$match) {\n      $var_name = str_replace(array('[', ']'), '', $match);\n\n      if (strpos($var_name, '.') !== false) {\n        // Block variables\n        list($block_name, $block_var) = explode('.', $var_name);\n        isset($this->template->_block_value[$block_name][$block_var])\n          ? $rawTag = str_replace($match, $this->template->_block_value[$block_name][$block_var], $rawTag) : false;\n      } elseif (strpos($var_name, '$') !== false) {\n        // Defines\n        // Only one $VAR can be used per square bracket\n        $define_name = substr($var_name, 1);\n        isset($this->template->_tpldata['DEFINE']['.'][$define_name])\n          ? $rawTag = str_replace($match, $this->template->_tpldata['DEFINE']['.'][$define_name], $rawTag) : false;\n      } else {\n        // Root variable\n        isset($this->template->_rootref[$var_name])\n          ? $rawTag = str_replace($match, $this->template->_rootref[$var_name], $rawTag) : false;\n      }\n    }\n\n    return $rawTag;\n  }\n\n\n  public function removeParam($paramName) {\n    if (!array_key_exists($paramName, $this->params)) {\n      return;\n    }\n\n    unset($this->params[$paramName]);\n  }\n\n  /**\n   * Generates unique cache key for tag\n   *\n   * @return string\n   */\n  public function getCacheKey() {\n    $result = [$this->resolved];\n\n    foreach ($this->params as $key => $value) {\n      $result[] = $key . '=' . $value;\n    }\n\n    return implode('|', $result);\n  }\n\n}\n"
  },
  {
    "path": "classes/Pages/Deprecated/PageAdminMining.php",
    "content": "<?php\n/**\n * Created by Gorlum 05.03.2018 12:57\n */\n\nnamespace Pages\\Deprecated;\n\nuse SN;\nuse DBAL\\DbSqlPaging;\nuse General\\Helpers\\PagingRenderer;\nuse SnTemplate;\n\nclass PageAdminMining extends PageDeprecated {\n  const PAGE_SORT_BY_PRODUCTION = 0;\n  const PAGE_SORT_BY_RANK = 1;\n  const PAGE_SORT_BY_ID = 2;\n  const PAGE_SORT_BY_NAME = 3;\n  const PAGE_SORT_BY_POINTS = 4;\n\n  public static function viewStatic() {\n    $sorting = [\n      static::PAGE_SORT_BY_PRODUCTION => [\n        'ID'        => static::PAGE_SORT_BY_PRODUCTION,\n        'HTML_ID'   => 'sortByTotalProduction',\n        'HTML_NAME' => '{ byTotalProduction }',\n        'SQL_SORT'  => ['$specialField'],\n      ],\n      self::PAGE_SORT_BY_RANK   => [\n        'ID'        => self::PAGE_SORT_BY_RANK,\n        'HTML_ID'   => 'byTotalRank',\n        'HTML_NAME' => '{ byTotalRank }',\n        'SQL_SORT'  => ['u.total_rank',],\n      ],\n      self::PAGE_SORT_BY_ID => [\n        'ID'        => self::PAGE_SORT_BY_ID,\n        'HTML_ID'   => 'byId',\n        'HTML_NAME' => '{ byId }',\n        'SQL_SORT'  => ['u.id',],\n      ],\n      self::PAGE_SORT_BY_NAME => [\n        'ID'        => self::PAGE_SORT_BY_NAME,\n        'HTML_ID'   => 'byName',\n        'HTML_NAME' => '{ byName }',\n        'SQL_SORT'  => ['u.username',],\n      ],\n\n      self::PAGE_SORT_BY_POINTS => [\n        'ID'        => self::PAGE_SORT_BY_POINTS,\n        'HTML_ID'   => 'byTotalPoints',\n        'HTML_NAME' => '{ byTotalPoints }',\n        'SQL_SORT'  => ['u.total_points DESC'],\n      ],\n    ];\n\n    // Currently unused - because it duplicates sorting by rank for now\n    unset($sorting[self::PAGE_SORT_BY_POINTS]);\n\n    define('IN_ADMIN', true);\n\n    lng_include('admin');\n\n    $specialField =\n      'sum(metal_perhour) * ' . get_unit_cost_in([RES_METAL => 1]) . ' + ' .\n      'sum(crystal_perhour) * ' . get_unit_cost_in([RES_CRYSTAL => 1]) . ' + ' .\n      'sum(deuterium_perhour) * ' . get_unit_cost_in([RES_DEUTERIUM => 1]);\n    $sorting[static::PAGE_SORT_BY_PRODUCTION]['SQL_SORT'] = [$specialField . ' DESC'];\n\n    $filterActive = sys_get_param_int('ACTIVE_STATUS', 0);\n    $sortBy = sys_get_param_int('SORT_BY', static::PAGE_SORT_BY_PRODUCTION);\n    array_key_exists($sortBy, $sorting) ?: ($sortBy = static::PAGE_SORT_BY_PRODUCTION);\n\n    $stringSqlQuery = \"\nSELECT\n  u.id, username, total_rank, total_points,\n  sum(metal_perhour)      AS `metal_prod`,\n  sum(crystal_perhour)    AS `crystal_prod`,\n  sum(deuterium_perhour)  AS `deuterium_prod`,\n  {$specialField} AS `total_prod`, \n  avg(metal_mine_porcent) AS `average_metal_percent`,\n  avg(crystal_mine_porcent)  AS `average_crystal_percent`,\n  avg(deuterium_sintetizer_porcent)  AS `average_deuterium_percent`\nFROM\n  `{{planets}}` AS p\n  LEFT JOIN `{{users}}` AS u ON u.id = p.id_owner\nWHERE\n  u.total_rank <> 0\n\tAND metal_mine_porcent != 0\n\tAND crystal_mine_porcent != 0\n\tAND deuterium_sintetizer_porcent != 0\n\tAND `user_as_ally` IS NULL\"\n      . ($filterActive == self::PAGE_SORT_BY_NAME ? \" AND FROM_UNIXTIME(u.`onlinetime`) >= DATE_SUB(NOW(), INTERVAL \" . PLAYER_INACTIVE_TIMEOUT_LONG . \" SECOND) \" : '')\n      . ($filterActive == self::PAGE_SORT_BY_ID ? \" AND FROM_UNIXTIME(u.`onlinetime`) >= DATE_SUB(NOW(), INTERVAL \" . PLAYER_INACTIVE_TIMEOUT . \" SECOND) \" : '')\n      . \"\nGROUP BY\n  id_owner \n\" . \" ORDER BY \" . implode(',', $sorting[$sortBy]['SQL_SORT']);\n\n    // View all at one page //    $iterator = SN::$db->selectIterator($stringSqlQuery);\n    $iterator = new DbSqlPaging($stringSqlQuery, 50, sys_get_param_int(PagingRenderer::KEYWORD));\n\n    $render = [];\n    foreach ($iterator as $row) {\n      $render[] = [\n        'ID'         => $row['id'],\n        'NAME'       => player_nick_render_to_html($row, true),\n        'NAME_clear' => $row['username'],\n        'RANK'       => $row['total_rank'],\n        'POINTS'     => $row['total_points'],\n        'METAL'      => $row['metal_prod'],\n        'CRYSTAL'    => $row['crystal_prod'],\n        'DEUTERIUM'  => $row['deuterium_prod'],\n        'TOTAL'      => $row['total_prod'],\n        'TOTAL_CALC' => get_unit_cost_in([\n          RES_METAL     => $row['metal_prod'],\n          RES_CRYSTAL   => $row['crystal_prod'],\n          RES_DEUTERIUM => $row['deuterium_prod'],\n        ]),\n        'PERCENT'    =>\n          round($row['average_metal_percent'], 2) . '/' .\n          round($row['average_crystal_percent'], 2) . '/' .\n          round($row['average_deuterium_percent'], 2),\n      ];\n    }\n\n    $template = SnTemplate::gettemplate('admin/admin_mining');\n\n    $sorting[$sortBy]['CHECKED'] = true;\n    $template->assign_recursive(['.' => ['sorting' => $sorting]]);\n\n    $pager = new PagingRenderer($iterator,\n      'index.php?page=admin/admin_mining' .\n      '&ACTIVE_STATUS=' . intval($filterActive) .\n      '&SORT_BY=' . intval($sortBy)\n    );\n\n    $template->assign_recursive([\n      'PAGE_NAME' => SN::$lang['menu_admin_mining'],\n\n      'PAGER_MESSAGES' => $pager ? $pager->render() : '',\n\n      'SORT_BY'       => $sortBy,\n      'ACTIVE_STATUS' => $filterActive,\n\n      '.' => ['production' => $render],\n    ]);\n    SnTemplate::display($template, SN::$lang['menu_admin_mining']);\n  }\n\n}\n"
  },
  {
    "path": "classes/Pages/Deprecated/PageAdminModules.php",
    "content": "<?php\n/**\n * Created by Gorlum 05.03.2018 12:57\n */\n\nnamespace Pages\\Deprecated;\n\nuse Modules\\sn_module;\nuse SN;\nuse SnTemplate;\n\nclass PageAdminModules extends PageDeprecated {\n  const SORT_BY_PACKAGE = 0;\n  const SORT_BY_ACTIVITY = 1;\n\n  private static $sorting = [\n    self::SORT_BY_PACKAGE  => ['PACKAGE', 'NAME', '!ACTIVE', '!INSTALLED',],\n    self::SORT_BY_ACTIVITY => ['!ACTIVE', '!INSTALLED', 'PACKAGE', 'NAME',],\n  ];\n\n  private static $sortFields = [];\n\n  /**\n   * @param array $a\n   * @param array $b\n   *\n   * @return int\n   */\n  private static function sortBy($a, $b) {\n    $result = 0;\n    foreach (static::$sortFields as $fieldName) {\n      if (strpos($fieldName, '!') !== false) {\n        $fieldName = substr($fieldName, 1);\n        $c = $a;\n        $a = $b;\n        $b = $c;\n      }\n      if ($result = $a[$fieldName] > $b[$fieldName] ? 1 : ($a[$fieldName] < $b[$fieldName] ? -1 : 0)) {\n        break;\n      }\n    }\n\n    return $result;\n  }\n\n  public static function viewStatic($template = null) {\n    define('IN_ADMIN', true);\n\n    lng_include('admin');\n\n    $modules = SN::$gc->modules->getModulesInGroup([], false);\n\n    $render = [];\n    foreach ($modules as $module) {\n      $render[] = [\n        'PACKAGE'   => $module->manifest['package'],\n        'NAME'      => $module->manifest['name'],\n        'VERSION'   => $module->getVersion(),\n        'ACTIVE'    => $module->isActive(),\n        'INSTALLED' => $module->isInstalled(),\n      ];\n    }\n\n    $sortBy = sys_get_param_int('SORT_BY', self::SORT_BY_ACTIVITY);\n    array_key_exists($sortBy, static::$sorting) ?: ($sortBy = self::SORT_BY_ACTIVITY);\n    static::$sortFields = static::$sorting[$sortBy];\n    usort($render, [static::class, 'sortBy']);\n\n    $template = SnTemplate::gettemplate('admin/admin_modules');\n    $template->assign_recursive([\n      '.'         => ['modules' => $render],\n      'SORT_BY'   => $sortBy,\n      'PAGE_NAME' => SN::$lang['menu_admin_modules'],\n    ]);\n\n    SnTemplate::display($template, SN::$lang['menu_admin_modules']);\n  }\n\n}\n"
  },
  {
    "path": "classes/Pages/Deprecated/PageAdminPayment.php",
    "content": "<?php\n/**\n * Created by Gorlum 05.03.2018 12:57\n */\n\nnamespace Pages\\Deprecated;\n\nuse SN;\nuse DBAL\\DbSqlPaging;\nuse General\\Helpers\\PagingRenderer;\nuse SnTemplate;\n\nclass PageAdminPayment extends PageDeprecated {\n\n  public static function viewStatic() {\n    define('INSIDE', true);\n    define('INSTALL', false);\n    define('IN_ADMIN', true);\n\n    global $lang;\n\n    SnTemplate::messageBoxAdminAccessDenied(AUTH_LEVEL_ADMINISTRATOR);\n\n    $template = SnTemplate::gettemplate('admin/admin_payment', true);\n\n\n    $flt_payer = sys_get_param_int('flt_payer', -1);\n    $flt_module = sys_get_param_str('flt_module');\n    $flt_status = sys_get_param_int('flt_status', PAYMENT_STATUS_ALL);\n    $flt_test = sys_get_param_int('flt_test', PAYMENT_TEST_REAL);\n    $flt_currency = sys_get_param_int('flt_currency', -1);\n    $flt_stat = sys_get_param_int('flt_stat', PAYMENT_FILTER_STAT_NORMAL);\n    $flt_stat_type = sys_get_param_int('flt_stat_type', -1);\n\n\n    // TODO - remove debug\n//    $flt_stat = PAYMENT_FILTER_STAT_YEAR;\n//    $flt_stat = PAYMENT_FILTER_STAT_MONTH;\n\n    $stats = [\n      PAYMENT_FILTER_STAT_NORMAL => [\n        'sql_fields' => [],\n      ],\n      PAYMENT_FILTER_STAT_MONTH  => [\n        'sql_fields' => [\n          'payment_status',\n//          'payment_user_id',\n//          'payment_user_name',\n          'sum(if(payment_currency = \"UAH\", payment_amount / 11, payment_amount)) as payment_amount',\n          'if(payment_currency = \"UAH\", \"USD\", payment_currency) as payment_currency',\n          'sum(payment_dark_matter_paid) as payment_dark_matter_paid',\n          'sum(payment_dark_matter_gained) as payment_dark_matter_gained',\n          'date_format(payment_date, \"%Y-%m\") as payment_date',\n          'payment_comment',\n          'payment_module_name',\n          'payment_external_id',\n          'payment_external_date',\n          'payment_external_lots',\n          'sum(payment_external_amount) as payment_external_amount',\n          'payment_external_currency',\n          'payment_test',\n        ],\n        'sql_group'  => [\n          'date_format(payment_date, \"%Y-%m\")',\n          'if(payment_currency = \"UAH\", \"USD\", payment_currency)',\n          'payment_external_currency',\n          'payment_test',\n          'payment_status',\n        ],\n        'sql_order'  => 'date_format(payment_date, \"%Y-%m\") desc',\n      ],\n      PAYMENT_FILTER_STAT_YEAR   => [\n        'sql_fields' => [\n          'payment_status',\n//          'payment_user_id',\n//          'payment_user_name',\n          'sum(if(payment_currency = \"UAH\", payment_amount / 11, payment_amount)) as payment_amount',\n          'if(payment_currency = \"UAH\", \"USD\", payment_currency) as payment_currency',\n          'payment_dark_matter_paid',\n          'payment_dark_matter_gained',\n          'date_format(payment_date, \"%Y\") as payment_date',\n          'payment_comment',\n          'payment_module_name',\n          'payment_external_id',\n          'payment_external_date',\n          'payment_external_lots',\n          'sum(payment_external_amount) as payment_external_amount',\n          'payment_external_currency',\n          'payment_test',\n        ],\n        'sql_group'  => [\n          'date_format(payment_date, \"%Y\")',\n          'if(payment_currency = \"UAH\", \"USD\", payment_currency)',\n          'payment_external_currency',\n          'payment_test',\n          'payment_status',\n        ],\n        'sql_order'  => 'date_format(payment_date, \"%Y\") desc',\n      ],\n      PAYMENT_FILTER_STAT_ALL  => [\n        'sql_fields' => [\n          'payment_status',\n//          'payment_user_id',\n//          'payment_user_name',\n          'sum(if(payment_currency = \"UAH\", payment_amount / 11, payment_amount)) as payment_amount',\n          'if(payment_currency = \"UAH\", \"USD\", payment_currency) as payment_currency',\n          'payment_dark_matter_paid',\n          'payment_dark_matter_gained',\n//          'date_format(payment_date, \"%Y\") as payment_date',\n          'payment_comment',\n          'payment_module_name',\n          'payment_external_id',\n          'payment_external_date',\n          'payment_external_lots',\n          'sum(payment_external_amount) as payment_external_amount',\n          'payment_external_currency',\n          'payment_test',\n        ],\n        'sql_group'  => [\n//          'date_format(payment_date, \"%Y\")',\n          'if(payment_currency = \"UAH\", \"USD\", payment_currency)',\n          'payment_external_currency',\n          'payment_test',\n          'payment_status',\n        ],\n        'sql_order'  => 'date_format(payment_date, \"%Y\") desc',\n      ],\n    ];\n\n\n    if (!isset($stats[$flt_stat])) {\n      $flt_stat = PAYMENT_FILTER_STAT_NORMAL;\n    }\n\n    $theStat = $stats[$flt_stat];\n\n    if (!empty($theStat['sql_fields']) && $flt_payer != -1) {\n      $theStat['sql_fields'] = array_merge($theStat['sql_fields'], [\n        'payment_user_id',\n        'payment_user_name',\n      ]);\n    }\n\n    $query = new \\DBAL\\DbSqlPaging(\n      \"SELECT \" .\n      (!empty($theStat['sql_fields']) ? implode(',', $theStat['sql_fields']) : '*') .\n      \" FROM `{{payment}}` WHERE 1 \" .\n      ($flt_payer > 0 ? \"AND payment_user_id = {$flt_payer} \" : '') .\n      ($flt_status >= 0 ? \"AND payment_status = {$flt_status} \" : '') .\n      ($flt_test >= 0 ? \"AND payment_test = {$flt_test} \" : '') .\n      ($flt_module ? \"AND payment_module_name = '{$flt_module}' \" : '') .\n\n      (!empty($theStat['sql_group']) ? ' GROUP BY ' . implode(',', $theStat['sql_group']) : '') .\n      ' ORDER BY ' . (!empty($theStat['sql_order']) ? $theStat['sql_order'] : 'payment_id desc')\n      ,\n      PAGING_PAGE_SIZE_DEFAULT_PAYMENTS,\n      sys_get_param_int(PagingRenderer::KEYWORD)\n    );\n\n    $pager = new PagingRenderer($query, 'index.php?' . $_SERVER['QUERY_STRING']);\n    $pager->setDelta(10);\n\n    foreach ($query as $row) {\n      $row2 = array();\n      foreach ($row as $key => $value) {\n        $row2[strtoupper($key)] = $value;\n      }\n      $template->assign_block_vars('payment', $row2);\n    }\n\n    SnTemplate::tpl_assign_select($template, 'payer', self::getPayers());\n    SnTemplate::tpl_assign_select($template, 'module', self::getUsedModules());\n    SnTemplate::tpl_assign_select($template, 'status', SN::$lang['adm_pay_filter_status']);\n    SnTemplate::tpl_assign_select($template, 'test', SN::$lang['adm_pay_filter_test']);\n    SnTemplate::tpl_assign_select($template, 'flt_stat', SN::$lang['adm_pay_filter_stat']);\n    SnTemplate::tpl_assign_select($template, 'flt_currency', self::getCurrencies());\n\n    $template->assign_vars(array(\n      'FLT_PAYER'    => $flt_payer,\n      'FLT_STATUS'   => $flt_status,\n      'FLT_TEST'     => $flt_test,\n      'FLT_MODULE'   => $flt_module,\n      'FLT_CURRENCY' => $flt_currency,\n      'FLT_STAT'     => $flt_stat,\n\n      'PAGER_PAYMENTS' => $pager->render(),\n    ));\n\n    SnTemplate::display($template, $lang['adm_pay_stats']);\n  }\n\n  /**\n   * @return array\n   */\n  protected static function getUsedModules() {\n    $module_list = array(\n      '' => SN::$lang['adm_pay_filter_all'],\n    );\n    $query = doquery(\"SELECT distinct payment_module_name FROM `{{payment}}` ORDER BY payment_module_name\");\n    while ($row = db_fetch($query)) {\n      $module_list[$row['payment_module_name']] = $row['payment_module_name'];\n    }\n\n    return $module_list;\n  }\n\n  /**\n   * @return array\n   */\n  protected static function getPayers() {\n    $payer_list = array(\n      -1 => SN::$lang['adm_pay_filter_all'],\n    );\n    $query = doquery(\"SELECT payment_user_id, payment_user_name FROM `{{payment}}` GROUP BY payment_user_id ORDER BY payment_user_name\");\n    while ($row = db_fetch($query)) {\n      $payer_list[$row['payment_user_id']] = '[' . $row['payment_user_id'] . '] ' . $row['payment_user_name'];\n    }\n\n    return $payer_list;\n  }\n\n  /**\n   * @return array\n   */\n  protected static function getCurrencies() {\n    $payer_list = array(\n      -1 => SN::$lang['adm_pay_filter_all'],\n    );\n    $query = doquery(\"SELECT distinct payment_external_currency FROM `{{payment}}`\");\n    while ($row = db_fetch($query)) {\n      $payer_list[$row['payment_external_currency']] = $row['payment_external_currency'];\n    }\n\n    return $payer_list;\n  }\n\n}\n"
  },
  {
    "path": "classes/Pages/Deprecated/PageAdminUserView.php",
    "content": "<?php\n/**\n * Created by Gorlum 05.03.2018 12:57\n */\n\nnamespace Pages\\Deprecated;\n\nuse Account;\nuse Planet\\DBStaticPlanet;\nuse SN;\nuse SnTemplate;\nuse template;\n\nclass PageAdminUserView extends PageDeprecated {\n  private static $formats = [\n    'sys_time_human_system'     => [\n      'register_time',\n      'onlinetime',\n      'ally_register_time',\n      'news_lastread',\n      'banaday',\n      'vacation',\n      'vacation_next',\n      'deltime',\n      'que_processed',\n    ],\n    'prettyNumberStyledDefault' => [\n      'account_metamatter',\n      'account_metamatter_total',\n\n      'metal',\n      'crystal',\n      'deuterium',\n      'dark_matter',\n      'dark_matter_total',\n      'metamatter',\n      'metamatter_total',\n\n      'player_rpg_explore_xp',\n      'player_rpg_explore_level',\n      'lvl_minier',\n      'xpminier',\n      'player_rpg_tech_xp',\n      'player_rpg_tech_level',\n      'lvl_raid',\n      'xpraid',\n      'raids',\n      'raidsloose',\n      'raidswin',\n      'total_rank',\n      'total_points',\n\n      // Message counts\n      'new_message',\n      'mnl_alliance',\n      'mnl_joueur',\n      'mnl_attaque',\n      'mnl_spy',\n      'mnl_exploit',\n      'mnl_transport',\n      'mnl_expedition',\n      'mnl_buildlist',\n      'msg_admin',\n    ],\n  ];\n\n  private static $blocks = [\n    'Аккаунт'                       => [\n//      'account_id',\n      'account_name',\n      'account_password',\n      'account_salt',\n      'account_email',\n      'account_email_verified',\n      'account_register_time',\n      'account_language',\n      'account_metamatter',\n      'account_metamatter_total',\n    ],\n    'Основная информация'           => [\n      'id',\n      'username',\n      'password',\n      'salt',\n      'user_last_browser_id',\n      'browser_user_agent',\n      'user_lastip',\n      'user_last_proxy',\n      'register_time',\n      'onlinetime',\n      'authlevel',\n      'admin_protection',\n      'id_planet',\n      'galaxy',\n      'system',\n      'planet',\n      'current_planet',\n      'server_name',\n    ],\n    'Профиль пользователя'          => [\n      'email',\n      'email_2',\n      'gender',\n      'avatar',\n      'sign',\n      'user_birthday',\n    ],\n    'Ресурсы'                       => [\n      'metal',\n      'crystal',\n      'deuterium',\n      'dark_matter',\n      'dark_matter_total',\n      'metamatter',\n      'metamatter_total',\n      P_RACE,\n    ],\n    'Альянс'                        => [\n      'ally_id',\n      'ally_tag',\n      'ally_name',\n      'ally_register_time',\n      'ally_rank_id',\n      'user_as_ally',\n    ],\n    'Очки'                          => [\n      'player_rpg_explore_xp',\n      'player_rpg_explore_level',\n      'lvl_minier',\n      'xpminier',\n      'player_rpg_tech_xp',\n      'player_rpg_tech_level',\n      'lvl_raid',\n      'xpraid',\n      'raids',\n      'raidsloose',\n      'raidswin',\n      'total_rank',\n      'total_points',\n    ],\n    'Блокировка, отпуск и удаление' => [\n      'immortal',\n      'bana',\n      'banaday',\n      'vacation',\n      'vacation_next',\n      'deltime',\n    ],\n    'Основные настройки интерфейса' => [\n      'lang',\n      'template',\n      'skin',\n      'design',\n    ],\n    'Новости и сообщения'           => [\n      'news_lastread',\n      'new_message',\n      'mnl_alliance',\n      'mnl_joueur',\n      'mnl_attaque',\n      'mnl_spy',\n      'mnl_exploit',\n      'mnl_transport',\n      'mnl_expedition',\n      'mnl_buildlist',\n      'msg_admin',\n    ],\n    'Прочие настройки'              => [\n      'noipcheck',\n      'options',\n      'user_time_measured',\n    ],\n    'Системные поля'                => [\n      'que_processed',\n      'user_birthday_celebrated',\n      'user_bot',\n      'parent_account_id',\n      'parent_account_global',\n    ],\n  ];\n\n  private static $extras = [\n    'account_password' => [\n      'TYPE' => 'password',\n    ],\n  ];\n\n  /**\n   * @param null|template $template\n   *\n   * @return null|template\n   */\n  public static function modelStatic($template = null) {\n    global $user;\n\n    define('IN_ADMIN', true);\n\n    SnTemplate::messageBoxAdminAccessDenied(AUTH_LEVEL_ADMINISTRATOR);\n\n    $user_id = sys_get_param_id('uid');\n    if (!($user_row = db_user_by_id($user_id))) {\n      return $template;\n    }\n\n    if (empty($user['authlevel']) || $user['authlevel'] < $user_row['authlevel']) {\n      SnTemplate::messageBoxAdmin(SN::$lang['admin_title_access_denied']);\n    }\n\n\n    if (!empty(sys_get_param('account_password_change')) && ($password = trim(sys_get_param('account_password')))) {\n      $account = new Account();\n      $account->dbGetByPlayerId($user_id);\n\n      $account->db_set_password($password, '');\n    }\n\n    return $template;\n  }\n\n  public static function viewStatic($template = null) {\n    global $user;\n\n    define('IN_ADMIN', true);\n\n    SnTemplate::messageBoxAdminAccessDenied(AUTH_LEVEL_ADMINISTRATOR);\n\n    $user_id = sys_get_param_id('uid');\n    if (!($user_row = db_user_by_id($user_id))) {\n      SnTemplate::messageBoxAdmin(sprintf(SN::$lang['adm_dm_user_none'], $user_id));\n    }\n\n    if (empty($user['authlevel']) || $user['authlevel'] <= $user_row['authlevel']) {\n      SnTemplate::messageBoxAdmin(SN::$lang['admin_title_access_denied']);\n    }\n\n    $account = new \\Account();\n    $account->dbGetByPlayerId($user_id);\n    foreach ([\n      'account_id',\n      'account_name',\n      'account_password',\n      'account_salt',\n      'account_email',\n      'account_email_verified',\n      'account_register_time',\n      'account_language',\n      'account_metamatter',\n      'account_metamatter_total',\n    ] as $accountField) {\n      $user_row[$accountField] = $account->$accountField;\n    }\n\n    if (!empty($user_row['user_last_browser_id'])) {\n      $temp = doquery(\"SELECT `browser_user_agent` FROM `{{security_browser}}` WHERE `browser_id` = {$user_row['user_last_browser_id']}\", true);\n      $user_row['browser_user_agent'] = $temp['browser_user_agent'];\n    }\n\n    foreach (self::$formats as $callable => $field_list) {\n      foreach ($field_list as $field_name) {\n        $user_row[$field_name] = call_user_func($callable, $user_row[$field_name]);\n      }\n    }\n\n    $template = SnTemplate::gettemplate('admin/admin_user', true);\n\n    $result = [\n      'PAGE_HEADER' => \"[{$user_row['id']}] {$user_row['username']}\",\n      'USER_ID'     => $user_row['id'],\n    ];\n    $exclude = $user_row;\n    foreach (self::$blocks as $title => $fields) {\n//      $template->assign_recursive(['.' => ['block' => [self::userBlockAssign($exclude, $title, $fields)]]]);\n      $result['.']['block'][] = self::userBlockAssign($exclude, $title, $fields);\n    }\n\n    if (!empty($exclude)) {\n      $result['.']['block'][] = self::userBlockAssign($exclude, '!!! НЕИЗВЕСТНЫЕ ПАРАМЕТРЫ !!!', array_keys($exclude));\n    }\n\n//    $pl = reset(DBStaticPlanet::db_planet_list_sorted($user_row));\n//    var_dump($pl);\n//    var_dump(uni_render_planet_full($pl, '', false, true));\n\n    foreach(DBStaticPlanet::db_planet_list_sorted($user_row) as $planetRow) {\n      $result['.']['planet'][] = [\n        'ID' => $planetRow['id'],\n        'NAME' => $planetRow['name'],\n        'NAME_RENDERED' => uni_render_planet_full($planetRow, '', false, true),\n      ];\n    }\n\n    $template->assign_recursive($result);\n\n    return $template;\n  }\n\n  /**\n   * @param array      $exclude\n   * @param int|string $title\n   * @param array      $fields\n   *\n   * @return array\n   */\n  private static function userBlockAssign(&$exclude, $title, $fields) {\n    $block = [\n      'TITLE' => $title,\n    ];\n    foreach ($fields as $field) {\n      $fieldBlock = [\n        'NAME'  => $field,\n        'VALUE' => isset($exclude[$field]) ? $exclude[$field] : 'N/A',\n      ];\n      $fieldBlock += self::renderExtra($field);\n      $block['.']['field'][] = $fieldBlock;\n      unset($exclude[$field]);\n    }\n\n    return $block;\n\n  }\n\n  private static function renderExtra($field) {\n    $result = [];\n\n    if (isset(self::$extras[$field])) {\n      foreach (self::$extras[$field] as $extraName => $extraContent) {\n        $result[$extraName] = $extraContent;\n      }\n    }\n\n    return $result;\n  }\n\n}\n"
  },
  {
    "path": "classes/Pages/Deprecated/PageDeprecated.php",
    "content": "<?php\n/**\n * Created by Gorlum 08.10.2017 17:36\n */\n\nnamespace Pages\\Deprecated;\n\nuse \\template;\nuse \\ResultMessages;\n\n/**\n * Class PageDeprecated\n *\n * Simple class to streamline somehow refactored page classes\n *\n * @package Deprecated\n */\nclass PageDeprecated {\n  /**\n   * @var ResultMessages $resultMessageList\n   */\n  protected $resultMessageList;\n\n\n  public function __construct() {\n    $this->resultMessageList = new ResultMessages();\n  }\n\n  protected function loadParams() {\n\n  }\n\n  /**\n   * Router/Controller for page\n   */\n  public function route() {\n\n  }\n\n\n  // TODO - move to separate class\n\n  /**\n   * @param string $message\n   * @param int    $status\n   */\n  protected function resultAdd($message, $status = ERR_NONE) {\n    $this->resultMessageList->add($message, $status);\n  }\n\n  /**\n   * @return int\n   */\n  protected function resultCount() {\n    return count($this->resultMessageList);\n  }\n\n  /**\n   * @param template $template\n   */\n  protected function resultTemplatize(template $template) {\n    $this->resultMessageList->templateAdd($template);\n  }\n\n  protected function resultReset() {\n    $this->resultMessageList->reset();\n  }\n  // TODO - move to separate class\n\n}\n"
  },
  {
    "path": "classes/Pages/Deprecated/PageFleet5Gathering.php",
    "content": "<?php\n/**\n * Created by Gorlum 30.09.2017 11:01\n */\n\nnamespace Pages\\Deprecated;\n\nuse DBAL\\db_mysql;\nuse Planet\\DBStaticPlanet;\nuse \\HelperString;\nuse SN;\nuse SnTemplate;\n\nclass PageFleet5Gathering {\n\n  /**\n   * @var \\classLocale $lang\n   */\n  protected $lang;\n\n  /**\n   * @var float[] $infoTransportCapacity\n   */\n  protected $infoTransportCapacity;\n\n  public function __construct() {\n    global $lang;\n\n    $this->lang = $lang;\n    $this->infoTransportCapacity = $this->getTransportUnitsCapacity();\n  }\n\n  /**\n   * @param array     $user\n   * @param array     $planetCurrent\n   * @param float[][] $resources_taken - [int|string $planetId][int $resourceId] => float $resourceAmount\n   *\n   * @return array\n   */\n  public function flt_build_gathering(&$user, &$planetCurrent, $resources_taken = []) {\n    // Caching sn_data names for quick access\n    $planet_list = [];\n\n    $takeAllResources = !is_array($resources_taken) || empty($resources_taken);\n    if ($takeAllResources) {\n      $query = '';\n    } else {\n      $query = implode(',', array_keys($resources_taken));\n      $query = \" AND `destruyed` = 0 AND `id` IN ({$query})\";\n    }\n\n    $planets_db_list = DBStaticPlanet::db_planet_list_sorted($user, $planetCurrent['id'], $query);\n    if (!is_array($planets_db_list)) {\n      $planets_db_list = [];\n    }\n\n    foreach ($planets_db_list as $planet_id => &$planetRecord) {\n      // begin planet loop\n      db_mysql::db_transaction_start();\n      // Вот тут надо посчитать - отработать очереди и выяснить, сколько ресов на каждой планете\n      $planetRecord = sys_o_get_updated($user['id'], $planetRecord['id'], SN_TIME_NOW, true);\n      $planetRecord = $planetRecord['planet'];\n      db_mysql::db_transaction_commit();\n\n      if ($takeAllResources) {\n        $resources_taken[$planet_id] = 1;\n      }\n\n      $planetResources = $this->calcPlanetResources($user, $resources_taken, $planetRecord);\n      $fleetCapacityList = $this->calcFleetCapacity($user, $planetRecord);\n      $fleetFullCapacity = array_sum($fleetCapacityList);\n\n      $fleet = $this->calcShipAmount($fleetCapacityList, min($planetResources, $fleetFullCapacity));\n\n      $result = ATTACK_NO_FLEET;\n      $travel_data = null;\n      if (!empty($fleet)) {\n        $travel_data = flt_travel_data($user, $planetCurrent, $planetRecord, $fleet, 10);\n\n        if (floor(mrc_get_level($user, $planetRecord, RES_DEUTERIUM, true)) >= $travel_data['consumption']) {\n          $will_take = min($planetResources, $fleetFullCapacity) - $travel_data['consumption'];\n\n          $resourcesTaken = $this->fillFleetResources($user, $resources_taken, $planetRecord, $will_take, $fleet);\n          $result = ATTACK_ALLOWED;\n        } else {\n          $result = ATTACK_NO_FUEL;\n        }\n      }\n\n      $planet_list[$planet_id] =\n        [\n          'PLANET_DB_DATA' => $planetRecord,\n          'ID'             => $planetRecord['id'],\n          'NAME'           => $planetRecord['name'],\n          'GALAXY'         => $planetRecord['galaxy'],\n          'SYSTEM'         => $planetRecord['system'],\n          'PLANET'         => $planetRecord['planet'],\n          'TYPE'           => $planetRecord['planet_type'],\n          'TYPE_PRINT'     => $this->lang['sys_planet_type'][$planetRecord['planet_type']],\n          'METAL'          => floor($planetRecord['metal']),\n          'CRYSTAL'        => floor($planetRecord['crystal']),\n          'DEUTERIUM'      => floor($planetRecord['deuterium']),\n          'METAL_TEXT'     => HelperString::numberFloorAndFormat($planetRecord['metal']),\n          'CRYSTAL_TEXT'   => HelperString::numberFloorAndFormat($planetRecord['crystal']),\n          'DEUTERIUM_TEXT' => HelperString::numberFloorAndFormat($planetRecord['deuterium']),\n          'RESOURCES'      => $planetResources,\n          'RESOURCES_TEXT' => HelperString::numberFloorAndFormat($planetResources),\n\n          'FLEET'               => $fleet,\n          'FLEET_RESOURCES'     => $resourcesTaken,\n          'FLEET_CAPACITY'      => $fleetFullCapacity,\n          'FLEET_CAPACITY_TEXT' => prettyNumberStyledCompare($fleetFullCapacity, -$planetResources),\n\n          'RESULT' => $result,\n//          'MESSAGE' => $this->lang['fl_attack_error'][$result],\n        ]\n        + (!empty($travel_data) ?\n          [\n            'FLEET_SPEED'   => $travel_data['fleet_speed'],\n            'DISTANCE'      => $travel_data['distance'],\n            'DURATION'      => $travel_data['duration'],\n            'DURATION_TEXT' => $travel_data['duration'] ? pretty_time($travel_data['duration']) : $this->lang['flt_no_fuel'],\n            'CONSUMPTION'   => $travel_data['consumption'],\n          ]\n          : []);\n    } // end planet loop\n\n    return $planet_list;\n  }\n\n  /**\n   * @return array\n   */\n  protected function getTransportUnitsCapacity() {\n    $transports = [];\n    foreach (sn_get_groups('flt_transports') as $transport_id) {\n      $transports[$transport_id] = get_unit_param($transport_id, P_CAPACITY);\n    }\n    arsort($transports);\n\n    return $transports;\n  }\n\n  /**\n   * @param array $user\n   * @param array $resources_taken\n   * @param array $planet_db_data\n   *\n   * @return float\n   */\n  protected function calcPlanetResources(&$user, $resources_taken, $planet_db_data) {\n    $planet_resources = 0;\n    foreach (sn_get_groups('resources_loot') as $resource_id) {\n      if ($resources_taken[$planet_db_data['id']] == 1 || $resources_taken[$planet_db_data['id']][$resource_id] > 0) {\n        $planet_resources += floor(mrc_get_level($user, $planet_db_data, $resource_id, true, true));\n      }\n    }\n\n    return $planet_resources;\n  }\n\n  /**\n   * @param array $user\n   * @param array $planet_db_data\n   *\n   * @return float[]\n   */\n  protected function calcFleetCapacity(&$user, $planet_db_data) {\n    $fleetCapacityList = [];\n    foreach ($this->infoTransportCapacity as $ship_id => $ship_capacity) {\n      if (($ship_count = mrc_get_level($user, $planet_db_data, $ship_id, true, true)) > 0) {\n        $fleetCapacityList[$ship_id] = $ship_count * $ship_capacity;\n      }\n    }\n\n    return $fleetCapacityList;\n  }\n\n  /**\n   * @param float[] $fleetCapacityList - List of capacities per ship\n   * @param float   $maxResourcesToTake - Maximum resources that can be taken from this planet with whole transport fleet\n   *\n   * @return array\n   */\n  protected function calcShipAmount($fleetCapacityList, $maxResourcesToTake) {\n    $fleet = [];\n    foreach ($fleetCapacityList as $ship_id => $shipCapacity) {\n      $can_take = min($maxResourcesToTake, $shipCapacity);\n      if ($can_take <= 0) {\n        continue;\n      }\n\n      $fleet[$ship_id] = ceil($can_take / $this->infoTransportCapacity[$ship_id]);\n\n      $maxResourcesToTake -= $can_take;\n      if ($maxResourcesToTake <= 0) {\n        break;\n      }\n    }\n\n    return $fleet;\n  }\n\n  /**\n   * @param array $user\n   * @param array $resources_taken\n   * @param array $planetRecord\n   * @param float $will_take\n   * @param array $fleet\n   */\n  protected function fillFleetResources(&$user, $resources_taken, $planetRecord, $will_take, &$fleet) {\n    $result = [];\n    foreach (sn_get_groups('resources_loot') as $resource_id) {\n      if ($resources_taken[$planetRecord['id']] != 1 && !$resources_taken[$planetRecord['id']][$resource_id]) {\n        continue;\n      }\n\n      $resource_amount = floor(mrc_get_level($user, $planetRecord, $resource_id, true, true));\n\n      $result[$resource_id] = min($will_take, $resource_amount);\n      $will_take -= $resource_amount;\n\n      if ($will_take <= 0) {\n        break;\n      }\n    }\n\n    return $result;\n  }\n\n\n  /**\n   * @param array     $playerRecord\n   * @param array     $planetRecord\n   * @param \\template $template\n   */\n  public function modelFleet5Gathering(&$playerRecord, &$planetRecord, $template) {\n    if (empty($resources_taken = sys_get_param('resources')) || !is_array($resources_taken)) {\n      return;\n    }\n\n    $planet_list = $this->flt_build_gathering($playerRecord, $planetRecord, $resources_taken);\n\n    foreach ($planet_list as $planet_id => $planet_data) {\n      if ($planet_data['RESULT'] == ATTACK_ALLOWED) {\n        /** @noinspection PhpUnhandledExceptionInspection */\n        $planet_data['RESULT'] = flt_t_send_fleet(\n          $playerRecord,\n          $planet_data['PLANET_DB_DATA'],\n          $planetRecord,\n          $planet_data['FLEET'],\n          $planet_data['FLEET_RESOURCES'],\n          MT_TRANSPORT);\n      }\n\n      $planet_data['MESSAGE'] = $this->lang['fl_attack_error'][$planet_data['RESULT']];\n\n      $template->assign_block_vars('results', $planet_data);\n      if (!empty($planet_data['FLEET']) && $planet_data['RESULT'] == ATTACK_ALLOWED) {\n        foreach ($planet_data['FLEET'] as $unit_id => $amount) {\n          $template->assign_block_vars('results.units', [\n            'ID'     => $unit_id,\n            'NAME'   => $this->lang['tech'][$unit_id],\n            'AMOUNT' => $amount\n          ]);\n        }\n      }\n    }\n  }\n\n  /**\n   * @param array     $user\n   * @param array     $planetrow\n   * @param \\template $template\n   */\n  public function viewPage5Gathering(&$user, &$planetrow, $template) {\n    $planet_list = $this->flt_build_gathering($user, $planetrow, []);\n    foreach ($planet_list as $planet_data) {\n//      $planet_data['DURATION'] = $planet_data['DURATION'] ? pretty_time($planet_data['DURATION']) : $this->lang['flt_no_fuel'];\n      $template->assign_block_vars('colonies', $planet_data);\n    }\n\n    $template->assign_vars([\n      'PAGE_HINT'      => $this->lang['fl_page5_hint'],\n      'METAL_NEED'     => HelperString::numberFloorAndFormat(max(0, -sys_get_param_float('metal'))),\n      'CRYSTAL_NEED'   => HelperString::numberFloorAndFormat(max(0, -sys_get_param_float('crystal'))),\n      'DEUTERIUM_NEED' => HelperString::numberFloorAndFormat(max(0, -sys_get_param_float('deuterium'))),\n    ]);\n\n    tpl_set_resource_info($template, $planetrow, []);\n\n    SnTemplate::display($template, $this->lang['fl_title']);\n  }\n\n}\n"
  },
  {
    "path": "classes/Pages/Deprecated/PageImperium.php",
    "content": "<?php\n/**\n * Created by Gorlum 01.10.2017 17:53\n */\n\nnamespace Pages\\Deprecated;\n\nuse DBAL\\db_mysql;\nuse Fleet\\DbFleetStatic;\nuse SN;\nuse SnTemplate;\nuse \\template;\nuse \\classLocale;\nuse Planet\\DBStaticPlanet;\nuse \\HelperString;\n\nclass PageImperium {\n\n  const GROUPS_TO_NAMES = [\n    UNIT_STRUCTURES         => 'structures',\n    UNIT_STRUCTURES_SPECIAL => 'structures',\n    UNIT_SHIPS              => 'fleet',\n    UNIT_DEFENCE            => 'defense',\n  ];\n\n  /**\n   * @var classLocale $lang ;\n   */\n  protected $lang;\n\n\n  /**\n   * @param template|null $template\n   */\n  public static function viewStatic(template $template = null) {\n    $that = new static();\n\n    $template = $that->view($template);\n    unset($that);\n\n    return $template;\n  }\n\n  /**\n   * @param template|null $template\n   */\n  public static function modelStatic(template $template = null) {\n    $that = new static();\n\n    $that->modelAdjustMinePercent();\n    unset($that);\n\n    return $template;\n  }\n\n  public function __construct() {\n    global $lang;\n    $this->lang = $lang;\n  }\n\n  /**\n   * @param template|null $template\n   *\n   * @return template\n   */\n  public function view(template $template = null) {\n    global $user;\n\n    list($planets, $ques) = $this->getUpdatedUserPlanetsAndQues($user);\n    $fleets = $this->fleetGetFlyingToPlanets($planets);\n\n    $template = SnTemplate::gettemplate('imperium', $template);\n\n    $template->assign_recursive(templateFillPercent());\n\n    $template->assign_recursive($this->tplRenderPlanets($user, $planets, $fleets));\n    $template->assign_recursive($this->tplRenderFleets($planets, $fleets));\n    $this->tplTotalPlanetInfo($template, $planets);\n\n    foreach (self::GROUPS_TO_NAMES as $unit_group_id => $internalGroupName) {\n      $template->assign_block_vars('prods', array(\n        'NAME' => $this->lang['tech'][$unit_group_id],\n      ));\n      $this->imperiumTemplatizeUnitGroup($user, $template, $unit_group_id, $planets, $ques, $fleets);\n    }\n\n    $template->assign_var('amount', count($planets) + 2);\n    $this->tplAddGlobals($user, $template);\n\n    return $template;\n  }\n\n  /**\n   * Store current mines load in DB\n   */\n  protected function modelAdjustMinePercent() {\n    global $user;\n\n    if (!sys_get_param('save_production') || !is_array($production = sys_get_param('percent')) || empty($production)) {\n      return;\n    }\n\n    $sn_group_factories = sn_get_groups('factories');\n\n    foreach (DBStaticPlanet::db_planet_list_sorted($user, false) as $planetId => $planet) {\n      $query = [];\n      foreach ($sn_group_factories as $factory_unit_id) {\n        $unit_db_name_porcent = pname_factory_production_field_name($factory_unit_id);\n        if (\n          // Changes required to mine production\n          isset($production[$factory_unit_id][$planet['id']])\n          // If mine is managed\n          && get_unit_param($factory_unit_id, P_MINING_IS_MANAGED)\n          // Input value is valid\n          && ($actual_porcent = intval($production[$factory_unit_id][$planet['id']] / 10)) >= 0\n          && $actual_porcent <= 10\n          // And changes really should be stored to DB\n          && $actual_porcent != $planet[$unit_db_name_porcent]\n        ) {\n          $actual_porcent = intval($actual_porcent);\n          $query[] = \"`{$unit_db_name_porcent}` = {$actual_porcent}\";\n        }\n      }\n\n      if (!empty($query)) {\n        DBStaticPlanet::db_planet_set_by_id($planet['id'], implode(',', $query));\n      }\n    }\n  }\n\n  /**\n   * @param array $user\n   *\n   * @return array[]\n   */\n  protected function getUpdatedUserPlanetsAndQues($user) {\n    $planets = array();\n    $ques = array();\n    $planet_row_list = DBStaticPlanet::db_planet_list_sorted($user);\n    foreach ($planet_row_list as $planet) {\n      db_mysql::db_transaction_start();\n      $global_data = sys_o_get_updated($user['id'], $planet['id'], SN_TIME_NOW, false, true);\n      $planets[$planet['id']] = $global_data['planet'];\n      $ques[$planet['id']] = $global_data['que'];\n      db_mysql::db_transaction_commit();\n    }\n\n    return array($planets, $ques);\n  }\n\n  /**\n   * @param array    $user\n   * @param template $template\n   * @param int      $unit_group_id\n   * @param array[]  $planets\n   * @param array[]  $ques\n   * @param array    $fleets\n   */\n  protected function imperiumTemplatizeUnitGroup(&$user, $template, $unit_group_id, $planets, $ques, $fleets) {\n    $sn_group_factories = sn_get_groups('factories');\n\n    foreach (get_unit_param('techtree', $unit_group_id) as $unit_id) {\n      $unit_count = $unit_count_abs = 0;\n      $block_vars = array();\n      $unit_is_factory = in_array($unit_id, $sn_group_factories) && get_unit_param($unit_id, P_MINING_IS_MANAGED);\n      foreach ($planets as $planet) {\n        $unit_level_plain = mrc_get_level($user, $planet, $unit_id, false, true);\n\n        $levelGreen = 0;\n        $levelYellow = 0;\n\n        switch ($unit_group_id) {\n          /** @noinspection PhpMissingBreakStatementInspection */\n          case UNIT_SHIPS:\n            $levelYellow = !empty($fleets[$planet['id']]['own']['total'][$unit_id]) ? floatval($fleets[$planet['id']]['own']['total'][$unit_id]) : 0;\n\n          case UNIT_STRUCTURES:\n          case UNIT_STRUCTURES_SPECIAL:\n          case UNIT_DEFENCE:\n            $levelGreen = floatval($ques[$planet['id']]['in_que'][que_get_unit_que($unit_id)][$user['id']][$planet['id']][$unit_id]);\n          break;\n\n          default:\n          break;\n        }\n        $unitsPresentOrChanged = $unit_level_plain + abs($levelYellow) + abs($levelGreen);\n\n        $unit_count += $unit_level_plain;\n        $unit_count_abs += $unitsPresentOrChanged;\n\n        $block_vars[] = [\n          'ID'                     => $planet['id'],\n          'TYPE'                   => $planet['planet_type'],\n          'LEVEL'                  => $unitsPresentOrChanged ? $unit_level_plain : '-',\n          'LEVEL_PLUS_YELLOW'      => $levelYellow,\n          'LEVEL_PLUS_GREEN'       => $levelGreen,\n          'LEVEL_TEXT'             => $unitsPresentOrChanged ? HelperString::numberFloorAndFormat($unit_level_plain) : '-',\n          'LEVEL_PLUS_YELLOW_TEXT' => SnTemplate::tplPrettyPlus($levelYellow),\n          'LEVEL_PLUS_GREEN_TEXT'  => SnTemplate::tplPrettyPlus($levelGreen),\n          'PERCENT'                => $unit_is_factory ? ($unit_level_plain ? $planet[pname_factory_production_field_name($unit_id)] * 10 : -1) : -1,\n          'FACTORY'                => $unit_is_factory,\n        ];\n      }\n\n      if ($unit_count_abs) {\n        $this->tplRenderUnitLine($template, $unit_id, $block_vars, $unit_count, $unit_is_factory);\n      }\n    }\n  }\n\n  /**\n   * Renders line of unit for each planet\n   *\n   * @param template $template\n   * @param int      $unit_id\n   * @param array    $block_vars\n   * @param int      $unit_count\n   * @param bool     $unit_is_factory\n   *\n   */\n  protected function tplRenderUnitLine($template, $unit_id, $block_vars, $unit_count, $unit_is_factory) {\n    // Adding unit cell name\n    $template->assign_block_vars('prods', [\n      'ID'    => $unit_id,\n      'FIELD' => 'unit_' . $unit_id, // TODO Делать это прямо в темплейте\n      'NAME'  => $this->lang['tech'][$unit_id],\n      'MODE'  => static::GROUPS_TO_NAMES[get_unit_param($unit_id, P_UNIT_TYPE)],\n    ]);\n\n    $imperiumYellows = [];\n    $imperiumGreens = [];\n    // Adding data for each planet for this unit\n    foreach ($block_vars as $block_var) {\n      $imperiumYellows[$unit_id] += $block_var['LEVEL_PLUS_YELLOW'];\n      $imperiumGreens[$unit_id] += $block_var['LEVEL_PLUS_GREEN'];\n      $template->assign_block_vars('prods.planet', $block_var);\n    }\n\n    // Adding final cell with Imperium total stat about this unit\n    $template->assign_block_vars('prods.planet', [\n      'ID'                     => 0,\n      'LEVEL'                  => $unit_count,\n      'LEVEL_TEXT'             => HelperString::numberFloorAndFormat($unit_count),\n      'LEVEL_PLUS_YELLOW'      => $imperiumYellows[$unit_id],\n      'LEVEL_PLUS_GREEN'       => $imperiumGreens[$unit_id],\n      'LEVEL_PLUS_YELLOW_TEXT' => $imperiumYellows[$unit_id] == 0 ? '' : SnTemplate::tplPrettyPlus($imperiumYellows[$unit_id]),\n      'LEVEL_PLUS_GREEN_TEXT'  => $imperiumGreens[$unit_id] == 0 ? '' : SnTemplate::tplPrettyPlus($imperiumGreens[$unit_id]),\n      'PERCENT'                => $unit_is_factory ? '' : -1,\n      'FACTORY'                => $unit_is_factory,\n    ]);\n  }\n\n  /**\n   * @param array[] $planets\n   * @param array   $fleets\n   *\n   * @return array[][]\n   */\n  protected function tplRenderPlanets($user, &$planets, $fleets) {\n    $result = [];\n\n    $planet_density = sn_get_groups('planet_density');\n\n    foreach ($planets as $planetId => $planet) {\n      $templatizedPlanet = tpl_parse_planet($user, $planet);\n\n      if($planet['planet_type'] == PT_MOON) {\n        $parentPlanet = DBStaticPlanet::db_planet_by_id($planet['parent_planet']);\n      } else {\n        $parentPlanet = $planet;\n      }\n\n      $fleet_list = $fleets[$planetId];\n      foreach ([RES_METAL, RES_CRYSTAL, RES_DEUTERIUM] as $resourceId) {\n        if (empty($fleet_list['own']['total'][$resourceId])) {\n          $templatizedPlanet['RES_' . $resourceId] = 0;\n        } else {\n          $templatizedPlanet['RES_' . $resourceId] = $fleet_list['own']['total'][$resourceId];\n          $templatizedPlanet['RES_' . $resourceId . '_TEXT'] = HelperString::numberFloorAndFormat($fleet_list['own']['total'][$resourceId]);\n        }\n      }\n      $templatizedPlanet += tpl_parse_planet_result_fleet($planet, $fleet_list);\n\n      $templatizedPlanet += [\n        'METAL_CUR'  => prettyNumberStyledCompare($planet['metal'], $planet['metal_max']),\n        'METAL_PROD_TEXT' => HelperString::numberFloorAndFormat($planet['metal_perhour']),\n\n        'CRYSTAL_CUR'  => prettyNumberStyledCompare($planet['crystal'], $planet['crystal_max']),\n        'CRYSTAL_PROD_TEXT' => HelperString::numberFloorAndFormat($planet['crystal_perhour']),\n\n        'DEUTERIUM_CUR'  => prettyNumberStyledCompare($planet['deuterium'], $planet['deuterium_max']),\n        'DEUTERIUM_PROD_TEXT' => HelperString::numberFloorAndFormat($planet['deuterium_perhour']),\n\n        'ENERGY_CUR' => $planet['energy_max'] - $planet['energy_used'],\n        'ENERGY_MAX' => $planet['energy_max'],\n\n        'TEMP_MIN' => $planet['temp_min'],\n        'TEMP_MAX' => $planet['temp_max'],\n\n        'DENSITY_CLASS'      => $planet['density_index'],\n        'DENSITY_RICHNESS'   => $planet_density[$planet['density_index']][UNIT_PLANET_DENSITY_RICHNESS],\n        'DENSITY_CLASS_TEXT' => $this->lang['uni_planet_density_types'][$planet['density_index']],\n\n        '_PARENT_PLANET' => &$parentPlanet,\n      ];\n\n      $templatizedPlanet['IS_CAPITAL'] = $parentPlanet['id'] == $user['id_planet'];\n\n      $result[] = $templatizedPlanet;\n    }\n\n    return [\n      '.' => [\n        'planet' => $result\n      ]\n    ];\n  }\n\n  /**\n   * @param array[] $planets\n   * @param array[] $fleets\n   *\n   * @return array\n   */\n  protected function tplRenderFleets($planets, $fleets) {\n    $fleetsRendered = [];\n    foreach ($fleets as $planetId => $fleet_list) {\n      if (!empty($fleet_list['own']['count'])) {\n        $fleetsRendered[$planets[$planetId]['id']] = tpl_parse_fleet_sn($fleet_list['own']['total'], getUniqueFleetId(['id' => $planetId]));\n      }\n    }\n\n    return tpl_assign_fleet_generate($fleetsRendered);\n  }\n\n  /**\n   * @param array[] $planets\n   *\n   * @return array\n   */\n  protected function fleetGetFlyingToPlanets(&$planets) {\n    $fleets = [];\n    foreach ($planets as $planetId => &$planet) {\n      $fleets[$planet['id']] = flt_get_fleets_to_planet($planet);\n    }\n\n    return $fleets;\n  }\n\n  /**\n   * @param template $template\n   * @param array    $planets\n   */\n  function tplTotalPlanetInfo($template, $planets) {\n    $imperiumStats = [\n      'temp_min' => 1000,\n      'temp_max' => -999,\n    ];\n\n    foreach ($planets as $planetId => &$planet) {\n      $imperiumStats['fields'] += $planet['field_current'];\n      $imperiumStats['metal'] += $planet['metal'];\n      $imperiumStats['crystal'] += $planet['crystal'];\n      $imperiumStats['deuterium'] += $planet['deuterium'];\n      $imperiumStats['energy'] += $planet['energy_max'] - $planet['energy_used'];\n\n      $imperiumStats['fields_max'] += eco_planet_fields_max($planet);\n      $imperiumStats['metal_perhour'] += $planet['metal_perhour'];\n      $imperiumStats['crystal_perhour'] += $planet['crystal_perhour'];\n      $imperiumStats['deuterium_perhour'] += $planet['deuterium_perhour'];\n      $imperiumStats['energy_max'] += $planet['energy_max'];\n\n      $imperiumStats['temp_min'] = min($planet['temp_min'], $imperiumStats['temp_min']);\n      $imperiumStats['temp_max'] = max($planet['temp_max'], $imperiumStats['temp_max']);\n    }\n\n    $template->assign_block_vars('planet', array_merge([\n      'ID'   => 0,\n      'NAME' => $this->lang['sys_total'],\n\n      'FIELDS_CUR' => $imperiumStats['fields'],\n      'FIELDS_MAX' => $imperiumStats['fields_max'],\n\n      'METAL_CUR'  => HelperString::numberFloorAndFormat($imperiumStats['metal']),\n      'METAL_PROD_TEXT' => HelperString::numberFloorAndFormat($imperiumStats['metal_perhour']),\n\n      'CRYSTAL_CUR'  => HelperString::numberFloorAndFormat($imperiumStats['crystal']),\n      'CRYSTAL_PROD_TEXT' => HelperString::numberFloorAndFormat($imperiumStats['crystal_perhour']),\n\n      'DEUTERIUM_CUR'  => HelperString::numberFloorAndFormat($imperiumStats['deuterium']),\n      'DEUTERIUM_PROD_TEXT' => HelperString::numberFloorAndFormat($imperiumStats['deuterium_perhour']),\n\n      'ENERGY_CUR' => $imperiumStats['energy'],\n      'ENERGY_MAX' => $imperiumStats['energy_max'],\n\n      'TEMP_MIN' => $imperiumStats['temp_min'],\n      'TEMP_MAX' => $imperiumStats['temp_max'],\n    ]));\n  }\n\n  /**\n   * @param array    $user\n   * @param template $template\n   */\n  protected function tplAddGlobals(&$user, $template) {\n    $template->assign_vars([\n      'COLONIES_CURRENT' => get_player_current_colonies($user),\n      'COLONIES_MAX'     => get_player_max_colonies($user),\n\n      'EXPEDITIONS_CURRENT' => DbFleetStatic::fleet_count_flying($user['id'], MT_EXPLORE),\n      'EXPEDITIONS_MAX'     => get_player_max_expeditons($user),\n\n      'PLANET_DENSITY_RICHNESS_NORMAL'  => PLANET_DENSITY_RICHNESS_NORMAL,\n      'PLANET_DENSITY_RICHNESS_AVERAGE' => PLANET_DENSITY_RICHNESS_AVERAGE,\n      'PLANET_DENSITY_RICHNESS_GOOD'    => PLANET_DENSITY_RICHNESS_GOOD,\n      'PLANET_DENSITY_RICHNESS_PERFECT' => PLANET_DENSITY_RICHNESS_PERFECT,\n\n      'PAGE_HEADER' => $this->lang['imp_overview'],\n    ]);\n  }\n\n}\n"
  },
  {
    "path": "classes/Pages/Deprecated/PageMercenary.php",
    "content": "<?php\n/**\n * Created by Gorlum 30.09.2017 8:28\n */\n\nnamespace Pages\\Deprecated;\n\nuse DBAL\\db_mysql;\nuse \\Exception;\nuse \\SN;\nuse SnTemplate;\nuse Unit\\DBStaticUnit;\nuse \\template;\n\nclass PageMercenary {\n\n  /**\n   * @var \\classConfig $config\n   */\n  protected $config;\n  /**\n   * @var \\classLocale $lang\n   */\n  protected $lang;\n\n  /**\n   * @var float[] $sn_powerup_buy_discounts\n   */\n  protected $sn_powerup_buy_discounts;\n\n  /**\n   * What we purchasing - Plans or Mercenaries?\n   * @var int $mode\n   */\n  protected $mode = UNIT_MERCENARIES; // Or UNIT_PLANS\n\n  /**\n   * If purchased units are permanent?\n   * @var bool $isUnitsPermanent\n   */\n  protected $isUnitsPermanent = false;\n\n  /**\n   * Multiplier for Alliance's purchases\n   *\n   * @var float $cost_alliance_multiplier\n   */\n  protected $cost_alliance_multiplier;\n\n  public function __construct() {\n    global $lang, $sn_powerup_buy_discounts;\n\n    lng_include('mrc_mercenary');\n    lng_include('infos');\n\n    $this->config = SN::$config;\n    $this->lang = $lang;\n    $this->sn_powerup_buy_discounts = $sn_powerup_buy_discounts;\n\n    $this->loadParams();\n  }\n\n  protected function loadParams() {\n    // Getting page mode\n    $this->mode = sys_get_param_int('mode', UNIT_MERCENARIES);\n    $this->mode = in_array($this->mode, array(UNIT_MERCENARIES, UNIT_PLANS)) ? $this->mode : UNIT_MERCENARIES;\n\n    $this->isUnitsPermanent = $this->mode == UNIT_PLANS || !$this->config->empire_mercenary_temporary;\n    $this->cost_alliance_multiplier = min(1, defined('SN_IN_ALLY') && (SN_IN_ALLY === true) && $this->mode == UNIT_PLANS ? $this->config->ali_bonus_members : 1);\n  }\n\n\n  /**\n   * @param array $user\n   */\n  public function mrc_mercenary_render($user) {\n    $template = SnTemplate::gettemplate('mrc_mercenary_hire');\n\n    $operation_result = $this->modelMercenaryHire($user);\n    if (!empty($operation_result)) {\n      $template->assign_block_vars('result', $operation_result);\n    }\n\n    $this->fillDiscountTable($template);\n\n    $user_dark_matter = mrc_get_level($user, [], RES_DARK_MATTER);\n    foreach (sn_get_groups($this->mode == UNIT_PLANS ? 'plans' : 'mercenaries') as $mercenary_id) {\n      $mercenary = get_unit_param($mercenary_id);\n\n      $mercenary_level = mrc_get_level($user, [], $mercenary_id, false, true);\n      $mercenary_level_bonus = max(0, mrc_get_level($user, [], $mercenary_id) - $mercenary_level);\n\n      $currentUnitCostDM = 0;\n      if ($this->isUnitsPermanent) {\n        $currentUnitCostDM = eco_get_total_cost($mercenary_id, $mercenary_level);\n        $currentUnitCostDM = $currentUnitCostDM[BUILD_CREATE][RES_DARK_MATTER] * $this->cost_alliance_multiplier;\n      }\n      $nextLevelCostData = eco_get_total_cost($mercenary_id, $mercenary_level + 1);\n      $nextLevelCostDM = $nextLevelCostData[BUILD_CREATE][RES_DARK_MATTER] * $this->cost_alliance_multiplier;\n\n      $mercenary_unit = DBStaticUnit::db_unit_by_location($user['id'], LOC_USER, $user['id'], $mercenary_id);\n      $mercenary_time_start = strtotime($mercenary_unit['unit_time_start']);\n      $mercenary_time_finish = strtotime($mercenary_unit['unit_time_finish']);\n      $unitIsOutdated = $mercenary_time_finish && $mercenary_time_finish >= SN_TIME_NOW;\n      $template->assign_block_vars('officer', array(\n        'ID'                => $mercenary_id,\n        'NAME'              => $this->lang['tech'][$mercenary_id],\n        'DESCRIPTION'       => $this->lang['info'][$mercenary_id]['description'],\n        'EFFECT'            => $this->lang['info'][$mercenary_id]['effect'],\n        'COST'              => $nextLevelCostDM - $currentUnitCostDM,\n        'COST_TEXT'         => prettyNumberStyledCompare($nextLevelCostDM - $currentUnitCostDM, $user_dark_matter),\n        'LEVEL'             => $mercenary_level,\n        'LEVEL_BONUS'       => $mercenary_level_bonus,\n        'LEVEL_MAX'         => $mercenary['max'],\n        'BONUS'             => SnTemplate::tpl_render_unit_bonus_data($mercenary),\n        'BONUS_TYPE'        => $mercenary[P_BONUS_TYPE],\n        'HIRE_END'          => $unitIsOutdated ? date(FMT_DATE_TIME, $mercenary_time_finish) : '',\n        'HIRE_LEFT_PERCENT' => $unitIsOutdated ? round(($mercenary_time_finish - SN_TIME_NOW) / ($mercenary_time_finish - $mercenary_time_start) * 100, 1) : 0,\n        'CAN_BUY'           => $this->mrc_officer_accessible($user, $mercenary_id),\n      ));\n\n      $this->renderMercenaryLevelsAvail($user_dark_matter, $mercenary_id, $currentUnitCostDM, $template, $mercenary_level, $mercenary['max']);\n\n      $this->renderMercenaryReq($user, $mercenary, $template);\n    }\n\n    $template->assign_vars(array(\n      'PAGE_HEADER'                => $this->lang['tech'][$this->mode],\n      'MODE'                       => $this->mode,\n      'IS_PERMANENT'               => intval($this->isUnitsPermanent),\n      'EMPIRE_MERCENARY_TEMPORARY' => $this->config->empire_mercenary_temporary,\n      'DARK_MATTER'                => $user_dark_matter,\n    ));\n\n    SnTemplate::display($template, $this->lang['tech'][$this->mode]);\n  }\n\n  protected function mrc_mercenary_hire($user, $mercenary_id) {\n    if (!in_array($mercenary_id, sn_get_groups($this->mode == UNIT_PLANS ? 'plans' : 'mercenaries'))) {\n      throw new Exception('mrc_msg_error_wrong_mercenary', ERR_ERROR);\n    }\n\n    if (!$this->mrc_officer_accessible($user, $mercenary_id)) {\n      throw new Exception('mrc_msg_error_requirements', ERR_ERROR);\n    }\n\n    $mercenary_level = sys_get_param_int('mercenary_level');\n    if ($mercenary_level < 0 || $mercenary_level > get_unit_param($mercenary_id, P_MAX_STACK)) {\n      throw new Exception('mrc_msg_error_wrong_level', ERR_ERROR);\n    }\n\n    $mercenary_period = sys_get_param_int('mercenary_period');\n    if ($mercenary_level && !array_key_exists($mercenary_period, $this->sn_powerup_buy_discounts)) {\n      throw new Exception('mrc_msg_error_wrong_period', ERR_ERROR);\n    }\n\n    db_mysql::db_transaction_start();\n\n    $mercenary_level_old = mrc_get_level($user, [], $mercenary_id, true, true);\n    if ($this->config->empire_mercenary_temporary && $mercenary_level_old && $mercenary_level) {\n      throw new Exception('mrc_msg_error_already_hired', ERR_ERROR); // Can't hire already hired temp mercenary - dismiss first\n    } elseif ($this->config->empire_mercenary_temporary && !$mercenary_level_old && !$mercenary_level) {\n      throw new Exception('', ERR_NONE); // Can't dismiss (!$mercenary_level) not hired (!$mercenary_level_old) temp mercenary. But no error\n    }\n\n    if ($mercenary_level) {\n      $darkmater_cost = eco_get_total_cost($mercenary_id, $mercenary_level);\n      if (!$this->config->empire_mercenary_temporary && $mercenary_level_old) {\n        $darkmater_cost_old = eco_get_total_cost($mercenary_id, $mercenary_level_old);\n        $darkmater_cost[BUILD_CREATE][RES_DARK_MATTER] -= $darkmater_cost_old[BUILD_CREATE][RES_DARK_MATTER];\n      }\n      $darkmater_cost = ceil($darkmater_cost[BUILD_CREATE][RES_DARK_MATTER] * $mercenary_period * $this->sn_powerup_buy_discounts[$mercenary_period] / $this->config->empire_mercenary_base_period);\n    } else {\n      $darkmater_cost = 0;\n    }\n    $darkmater_cost *= $this->cost_alliance_multiplier;\n\n    if (mrc_get_level($user, [], RES_DARK_MATTER) < $darkmater_cost) {\n      throw new Exception('mrc_msg_error_no_resource', ERR_ERROR);\n    }\n\n    $this->mercenaryDismiss($user, $mercenary_id, $darkmater_cost, $mercenary_level);\n\n    if ($darkmater_cost && $mercenary_level) {\n      DBStaticUnit::db_unit_set_insert(\n        \"unit_player_id = {$user['id']},\n        unit_location_type = \" . LOC_USER . \",\n        unit_location_id = {$user['id']},\n        unit_type = {$this->mode},\n        unit_snid = {$mercenary_id},\n        unit_level = {$mercenary_level},\n        unit_time_start = \" . (!$this->isUnitsPermanent ? 'FROM_UNIXTIME(' . SN_TIME_NOW . ')' : 'null') . \",\n        unit_time_finish = \" . (!$this->isUnitsPermanent ? 'FROM_UNIXTIME(' . (SN_TIME_NOW + $mercenary_period) . ')' : 'null')\n      );\n\n      rpg_points_change($user['id'], $this->mode == UNIT_PLANS ? RPG_PLANS : RPG_MERCENARY, -($darkmater_cost),\n        sprintf($this->lang[$this->mode == UNIT_PLANS ? 'mrc_plan_bought_log' : 'mrc_mercenary_hired_log'], $this->lang['tech'][$mercenary_id], $mercenary_id, $darkmater_cost, round($mercenary_period / PERIOD_DAY)));\n    }\n    db_mysql::db_transaction_commit();\n    sys_redirect($_SERVER['REQUEST_URI']);\n  }\n\n  protected function mrc_officer_accessible(&$user, $mercenary_id) {\n    $mercenary_info = get_unit_param($mercenary_id);\n\n    if ($this->config->empire_mercenary_temporary || $mercenary_info[P_UNIT_TYPE] == UNIT_PLANS) {\n      return true;\n    }\n\n    return eco_can_build_unit($user, [], $mercenary_id) == BUILD_ALLOWED;\n  }\n\n  /**\n   * @param \\template $template\n   */\n  protected function fillDiscountTable($template) {\n    foreach ($this->sn_powerup_buy_discounts as $hire_period => $hire_discount) {\n      $template->assign_block_vars('period', array(\n        'LENGTH'   => $hire_period,\n        'TEXT'     => $this->lang['mrc_period_list'][$hire_period],\n        'DISCOUNT' => $hire_period / $this->config->empire_mercenary_base_period * $hire_discount,\n        'SELECTED' => $hire_period == $this->config->empire_mercenary_base_period,\n      ));\n    }\n  }\n\n  /**\n   * @param $user\n   *\n   * @return array\n   */\n  protected function modelMercenaryHire($user) {\n    $operation_result = [];\n    if ($mercenary_id = sys_get_param_int('mercenary_id')) {\n      try {\n        $this->mrc_mercenary_hire($user, $mercenary_id);\n      } catch (Exception $e) {\n        db_mysql::db_transaction_rollback();\n        $operation_result = array(\n          'STATUS'  => in_array($e->getCode(), array(ERR_NONE, ERR_WARNING, ERR_ERROR)) ? $e->getCode() : ERR_ERROR,\n          'MESSAGE' => $this->lang[$e->getMessage()],\n        );\n      }\n    }\n\n    return $operation_result;\n  }\n\n  /**\n   * @param array $user\n   * @param int   $mercenary_id\n   * @param float $darkmater_cost\n   * @param int   $mercenary_level\n   */\n  protected function mercenaryDismiss($user, $mercenary_id, $darkmater_cost, $mercenary_level) {\n    if ((!$darkmater_cost || !$mercenary_level) && $this->isUnitsPermanent) {\n      return;\n    }\n\n    $unit_row = DBStaticUnit::db_unit_by_location($user['id'], LOC_USER, $user['id'], $mercenary_id);\n    if (is_array($unit_row) && ($dismiss_left_days = floor((strtotime($unit_row['unit_time_finish']) - SN_TIME_NOW) / PERIOD_DAY))) {\n      $dismiss_full_cost = eco_get_total_cost($mercenary_id, $unit_row['unit_level']);\n      $dismiss_full_cost = $dismiss_full_cost[BUILD_CREATE][RES_DARK_MATTER];\n\n      $dismiss_full_days = round((strtotime($unit_row['unit_time_finish']) - strtotime($unit_row['unit_time_start'])) / PERIOD_DAY);\n      rpg_points_change($user['id'], RPG_MERCENARY_DISMISSED, 0,\n        sprintf($this->lang['mrc_mercenary_dismissed_log'], $this->lang['tech'][$mercenary_id], $mercenary_id, $dismiss_full_cost, $dismiss_full_days,\n          $unit_row['unit_time_start'], $unit_row['unit_time_finish'], $dismiss_left_days, floor($dismiss_full_cost * $dismiss_left_days / $dismiss_full_days)\n        ));\n    }\n    DBStaticUnit::db_unit_list_delete($user['id'], LOC_USER, $user['id'], $mercenary_id);\n  }\n\n  /**\n   * @param           $user\n   * @param           $mercenary\n   * @param template  $template\n   */\n  protected function renderMercenaryReq(&$user, $mercenary, $template) {\n    if (empty($mercenary[P_REQUIRE]) || !is_array($mercenary[P_REQUIRE])) {\n      return;\n    }\n\n    foreach ($mercenary[P_REQUIRE] as $requireUnitId => $requireLevel) {\n      $template->assign_block_vars('officer.require', $q = [\n        'ID'             => $requireUnitId,\n        'LEVEL_GOT'      => mrc_get_level($user, [], $requireUnitId),\n        'LEVEL_REQUIRED' => $requireLevel,\n        'NAME'           => \\HelperString::htmlSafe($this->lang['tech'][$requireUnitId])\n      ]);\n    }\n  }\n\n  /**\n   * @param float    $user_dark_matter\n   * @param int      $mercenary_id\n   * @param float    $currentCost\n   * @param template $template\n   * @param int      $mercenary_level\n   * @param          $mercenary_max_level\n   */\n  protected function renderMercenaryLevelsAvail($user_dark_matter, $mercenary_id, $currentCost, $template, $mercenary_level, $mercenary_max_level) {\n    $upgrade_cost = 1;\n    for (\n      $i = $this->config->empire_mercenary_temporary ? 1 : $mercenary_level + 1;\n      $mercenary_max_level ? ($i <= $mercenary_max_level) : $upgrade_cost <= $user_dark_matter;\n      $i++\n    ) {\n      $newCost = eco_get_total_cost($mercenary_id, $i);\n      $upgrade_cost = $newCost[BUILD_CREATE][RES_DARK_MATTER] * $this->cost_alliance_multiplier - $currentCost;\n      $template->assign_block_vars('officer.level', array(\n        'VALUE' => $i,\n        'PRICE' => $upgrade_cost,\n      ));\n    }\n  }\n\n}\n"
  },
  {
    "path": "classes/Pages/Deprecated/PageMessage.php",
    "content": "<?php /** @noinspection PhpDeprecationInspection */\n/** @noinspection SqlIdentifier */\n/** @noinspection SqlRedundantOrderingDirection */\n/** @noinspection SqlResolve */\n\n/**\n * Created by Gorlum 08.10.2017 17:14\n */\n\nnamespace Pages\\Deprecated;\n\nuse DBAL\\db_mysql;\nuse \\SN;\nuse \\classLocale;\nuse DBAL\\DbSqlPaging;\nuse Fleet\\MissionEspionageReport;\nuse General\\Helpers\\PagingRenderer;\nuse Pm\\DecodeEspionage;\nuse SnTemplate;\nuse \\template;\n\n/**\n * Class PageMessage\n * @package Deprecated\n *\n * Refactor of /messages.php\n */\nclass PageMessage extends PageDeprecated {\n  /**\n   * List messages amount per category\n   */\n  const MESSAGES_MODE_CATEGORIES = '';\n  /**\n   * Delete message(s)\n   */\n  const MESSAGES_MODE_DELETE = 'delete';\n  /**\n   * Write personal message\n   */\n  const MESSAGES_MODE_COMPOSE = 'write';\n  /**\n   * Show messages in category\n   */\n  const MESSAGES_MODE_MESSAGES = 'show';\n\n  const MESSAGES_DELETE_RANGE_NONE = '';\n//  const MESSAGES_DELETE_RANGE_UNCHECKED = 'unchecked';\n  const MESSAGES_DELETE_RANGE_CHECKED = 'checked';\n  const MESSAGES_DELETE_RANGE_CLASS = 'class';\n  const MESSAGES_DELETE_RANGE_ALL = 'all';\n\n  const MESSAGES_DELETE_RANGES_ALLOWED = [\n//    self::MESSAGES_DELETE_RANGE_UNCHECKED,\n    self::MESSAGES_DELETE_RANGE_CHECKED,\n    self::MESSAGES_DELETE_RANGE_CLASS,\n    self::MESSAGES_DELETE_RANGE_ALL,\n  ];\n\n  /**\n   * @var array[] $messageClassList - [int => ['name' => string, 'switchable' => bool, 'email' => bool]]\n   */\n  protected $messageClassList = [];\n\n  /**\n   * @var classLocale $lang\n   */\n  protected $lang;\n\n  /**\n   * Current page mode\n   *\n   * @var $mode\n   * // TODO - change type to INT when finish\n   */\n  protected $mode;\n\n  /**\n   * @var int $current_class\n   */\n  protected $current_class;\n\n  /**\n   * @var string $recipient_name_unsafe\n   */\n  // TODO - UNSAFE USAGES!\n  protected $recipient_name_unsafe = '';\n\n  /**\n   * @var int|string $recipient_id_unsafe\n   */\n  protected $recipient_id_unsafe = 0;\n\n  protected $showAll = false;\n\n  /**\n   * @var string $subject_unsafe\n   */\n  protected $subject_unsafe = '';\n\n  /**\n   * Text to send\n   *\n   * @var string $sendText_unsafe\n   */\n  protected $sendText_unsafe = '';\n\n  /**\n   * Reference to current PLAYER DB record\n   *\n   * @var array $user\n   */\n  protected $user;\n\n  /**\n   * @var int|string\n   */\n  protected $playerId = 0;\n\n  /**\n   * @var mixed $doSend\n   */\n  protected $doSend = false;\n\n  /**\n   * @var string $deleteRange\n   */\n  protected $deleteRange = '';\n\n  /**\n   * @var db_mysql $db\n   */\n  protected $db;\n\n  /**\n   * @var array\n   */\n  protected $markedMessageIdList = [];\n\n\n  /**\n   * PageMessage constructor.\n   */\n  public function __construct() {\n    parent::__construct();\n\n    global $sn_message_class_list;\n\n    $this->lang = SN::$lang;\n    $this->messageClassList = $sn_message_class_list;\n\n    $this->db = SN::$gc->db;\n\n    $this->loadParams();\n  }\n\n  public function route() {\n    $this->getUserRef();\n\n    $template = null;\n\n    switch ($this->mode) {\n      case static::MESSAGES_MODE_COMPOSE:\n        $this->modelCompose();\n        $template = $this->viewCompose();\n      break;\n\n      /** @noinspection PhpMissingBreakStatementInspection */\n      case static::MESSAGES_MODE_DELETE:\n        $this->modelDelete();\n\n      case static::MESSAGES_MODE_MESSAGES:\n        if (sys_get_param_int('return')) {\n          sys_redirect('messages.php');\n        }\n\n        $template = $this->viewMessageList();\n      break;\n\n      default:\n        $template = $this->viewCategories();\n      break;\n    }\n\n    SnTemplate::display($template, $this->lang['msg_page_header']);\n  }\n\n  protected function modelCompose() {\n    $this->getRecipientData();\n\n    if ($this->recipient_id_unsafe == $this->playerId) {\n      $this->resultAdd($this->lang['msg_err_self_send'], ERR_ERROR);\n    }\n\n    if ($this->doSend) {\n      if (empty($this->recipient_id_unsafe)) {\n        $this->resultAdd($this->lang['msg_err_player_not_found'], ERR_ERROR);\n      }\n\n      if (!$this->sendText_unsafe) {\n        $this->resultAdd($this->lang['msg_err_no_text'], ERR_ERROR);\n      }\n\n      if (!$this->resultCount()) {\n        $this->wrapSendPm();\n\n        $this->sendText_unsafe = '';\n\n        $this->resultAdd($this->lang['msg_not_message_sent'], ERR_NONE);\n      }\n    }\n  }\n\n  /**\n   * @return template\n   */\n  protected function viewCompose() {\n    $template = SnTemplate::gettemplate('msg_message_compose', true);\n    $template->assign_vars([\n      'RECIPIENT_ID'   => $this->recipient_id_unsafe,\n      'RECIPIENT_NAME' => htmlspecialchars($this->recipient_name_unsafe),\n      'SUBJECT'        => htmlspecialchars($this->subject_unsafe),\n      'TEXT'           => htmlspecialchars($this->sendText_unsafe),\n    ]);\n\n    $this->resultTemplatize($template);\n\n    $recipientIdSafe = SN::$db->db_escape($this->recipient_id_unsafe);\n    $message_query = doquery(\n      \"SELECT * FROM {{messages}}\n        WHERE\n          `message_type` = '\" . MSG_TYPE_PLAYER . \"' AND\n          ((`message_owner` = '{$this->playerId}' AND `message_sender` = '{$recipientIdSafe}')\n          OR\n          (`message_sender` = '{$this->playerId}' AND `message_owner` = '{$recipientIdSafe}')) \n        ORDER BY `message_time` DESC LIMIT 20;\");\n    while ($message_row = db_fetch($message_query)) {\n      $template->assign_block_vars('messages', array(\n        'ID'   => $message_row['message_id'],\n        'DATE' => date(FMT_DATE_TIME, $message_row['message_time'] + SN_CLIENT_TIME_DIFF),\n        'FROM' => htmlspecialchars($message_row['message_from']),\n        'SUBJ' => htmlspecialchars($message_row['message_subject']),\n        'TEXT' => in_array($message_row['message_type'], array(MSG_TYPE_PLAYER, MSG_TYPE_ALLIANCE)) && $message_row['message_sender'] ? nl2br(htmlspecialchars($message_row['message_text'])) : nl2br($message_row['message_text']),\n\n        'FROM_ID' => $message_row['message_sender'],\n//        'SUBJ_SANITIZED' => htmlspecialchars($message_row['message_subject']),\n      ));\n    }\n\n    return $template;\n  }\n\n\n  protected function modelDelete() {\n    // No range specified - nothing to do\n    if ($this->deleteRange == static::MESSAGES_DELETE_RANGE_NONE) {\n      return;\n    }\n    // Сurrent range is CHECKED and NO messages marked - nothing to do\n    if ($this->deleteRange == static::MESSAGES_DELETE_RANGE_CHECKED && empty($this->markedMessageIdList)) {\n      return;\n    }\n\n    $query_add = '';\n\n    switch ($this->deleteRange) {\n//      case static::MESSAGES_DELETE_RANGE_UNCHECKED:\n      /** @noinspection PhpMissingBreakStatementInspection */\n      case static::MESSAGES_DELETE_RANGE_CHECKED:\n        $query_add = implode(',', $this->markedMessageIdList);\n        if ($query_add) {\n          $query_add = \"IN ({$query_add})\";\n//          if ($this->deleteRange == static::MESSAGES_DELETE_RANGE_UNCHECKED) {\n//            $query_add = \"NOT {$query_add}\";\n//          }\n          $query_add = \" AND `message_id` {$query_add}\";\n        }\n\n      /** @noinspection PhpMissingBreakStatementInspection */\n      case static::MESSAGES_DELETE_RANGE_CLASS:\n        if ($this->current_class != MSG_TYPE_OUTBOX && $this->current_class != MSG_TYPE_NEW) {\n          $query_add .= \" AND `message_type` = {$this->current_class}\";\n        }\n      case static::MESSAGES_DELETE_RANGE_ALL:\n        $query_add = $query_add ? $query_add : true;\n      break;\n    }\n\n    if ($this->deleteRange && $query_add) {\n      $query_add = $query_add === true ? '' : $query_add;\n      doquery(\"DELETE FROM `{{messages}}` WHERE `message_owner` = '{$this->playerId}'{$query_add};\");\n    }\n  }\n\n  /**\n   * @return template\n   */\n  protected function viewMessageList() {\n    require_once('includes/includes/coe_simulator_helpers.php');\n\n    $pager = null;\n\n    if ($this->current_class == MSG_TYPE_OUTBOX) {\n      $message_query = \"SELECT {{messages}}.message_id, {{messages}}.message_owner, {{users}}.id AS message_sender, {{messages}}.message_time,\n          {{messages}}.message_type, {{users}}.username AS message_from, {{messages}}.message_subject, {{messages}}.message_text\n       FROM\n         {{messages}} LEFT JOIN {{users}} ON {{users}}.id = {{messages}}.message_owner WHERE `message_sender` = '{$this->playerId}' AND `message_type` = 1\n       ORDER BY `message_time` DESC;\";\n    } else {\n      if ($this->current_class == MSG_TYPE_NEW) {\n        $SubUpdateQry = array();\n        foreach ($this->messageClassList as $message_class_id => $message_class) {\n          if ($message_class_id != MSG_TYPE_OUTBOX) {\n            $SubUpdateQry[] = \"`{$message_class['name']}` = '0'\";\n            $this->user[$message_class['name']] = 0;\n          }\n        }\n        $SubUpdateQry = implode(',', $SubUpdateQry);\n        $SubSelectQry = '';\n      } else {\n        $messageClassNameNew = $this->messageClassList[MSG_TYPE_NEW]['name'];\n        $messageClassNameCurrent = $this->messageClassList[$this->current_class]['name'];\n        $SubUpdateQry = \"`{$messageClassNameCurrent}` = '0', `{$messageClassNameNew}` = `{$messageClassNameNew}` - '{$this->user[$messageClassNameCurrent]}'\";\n        $SubSelectQry = \"AND `message_type` = '{$this->current_class}'\";\n\n        $this->user[$messageClassNameNew] -= $this->user[$messageClassNameCurrent];\n        $this->user[$messageClassNameCurrent] = 0;\n      }\n\n      db_user_set_by_id($this->playerId, $SubUpdateQry);\n      $message_query =\n        \"SELECT m.*, sender.authlevel as sender_auth \n        FROM `{{messages}}` as m \n          LEFT JOIN `{{users}}` as sender on sender.id = m.message_sender\n        WHERE m.`message_owner` = '{$this->playerId}' {$SubSelectQry} \n        ORDER BY m.`message_time` DESC;\";\n    }\n\n    if ($this->showAll) {\n      $message_query = $this->db->selectIterator($message_query);\n    } else {\n      $message_query = new DbSqlPaging($message_query, PAGING_PAGE_SIZE_DEFAULT_MESSAGES, sys_get_param_int(PagingRenderer::KEYWORD));\n      $pager = new PagingRenderer($message_query, 'messages.php?mode=show&message_class=' . $this->current_class);\n    }\n\n    $wasIgnored = 0;\n    $template = SnTemplate::gettemplate('msg_message_list', true);\n    foreach ($message_query as $message_row) {\n      if(\n        $message_row['message_type'] == MSG_TYPE_PLAYER\n        &&\n        SN::$gc->ignores->isIgnored(floatval($message_row['message_owner']), floatval($message_row['message_sender']))\n      ) {\n        $wasIgnored++;\n        continue;\n      }\n\n      $text = $message_row['message_text'];\n      if ($message_row['message_json']) {\n        switch ($message_row['message_type']) {\n          case MSG_TYPE_SPY:\n            $text = DecodeEspionage::decode(MissionEspionageReport::fromJson($text));\n          break;\n          default:\n            $text = '{ Unauthorised access - please contact Administration! }';\n          break;\n\n        }\n      } else {\n        if (in_array($message_row['message_type'], [MSG_TYPE_PLAYER, MSG_TYPE_ALLIANCE]) && $message_row['message_sender']) {\n          if ($message_row['sender_auth'] >= AUTH_LEVEL_ADMINISTRATOR) {\n            $text = SN::$gc->bbCodeParser->expandBbCode($message_row['message_text'], intval($message_row['sender_auth']), HTML_ENCODE_NONE);\n          } else {\n            $text = htmlspecialchars($message_row['message_text']);\n          }\n        }\n        $text = nl2br($text);\n      }\n\n      $template->assign_block_vars('messages', array(\n        'ID'   => $message_row['message_id'],\n        'DATE' => date(FMT_DATE_TIME, $message_row['message_time'] + SN_CLIENT_TIME_DIFF),\n        'FROM' => htmlspecialchars($message_row['message_from']),\n        'SUBJ' => htmlspecialchars($message_row['message_subject']),\n        'TEXT' => $text,\n\n        'CAN_IGNORE' => $message_row['message_type'] == MSG_TYPE_PLAYER,\n\n        'FROM_ID'        => $message_row['message_sender'],\n        'SUBJ_SANITIZED' => htmlspecialchars($message_row['message_subject']),\n        'STYLE'          => $this->current_class == MSG_TYPE_OUTBOX ? $this->messageClassList[MSG_TYPE_OUTBOX]['name'] : $this->messageClassList[$message_row['message_type']]['name'],\n      ));\n    }\n\n    $current_class_text = $this->lang['msg_class'][$this->current_class];\n\n    $template->assign_vars(array(\n      \"MESSAGE_CLASS\"      => $this->current_class,\n      \"MESSAGE_CLASS_TEXT\" => $current_class_text,\n      \"PAGER_MESSAGES\"     => $pager ? $pager->render() : '',\n      \"MESSAGES_IGNORED\"   => $wasIgnored,\n    ));\n\n    return $template;\n  }\n\n  /**\n   * @return template\n   */\n  protected function viewCategories() {\n    $messages_total = [];\n\n    $query = doquery(\n      \"SELECT `message_owner`, `message_type`, COUNT(`message_id`) AS `message_count` \n         FROM `{{messages}}` \n         WHERE `message_owner` = {$this->playerId} \n         GROUP BY `message_owner`, `message_type` \n         ORDER BY `message_owner` ASC, `message_type`;\"\n    );\n    while ($message_row = db_fetch($query)) {\n      $messages_total[$message_row['message_type']] = $message_row['message_count'];\n      $messages_total[MSG_TYPE_NEW] += $message_row['message_count'];\n    }\n\n    $query = doquery(\n      \"SELECT COUNT(`message_id`) AS message_count \n         FROM `{{messages}}` \n         WHERE `message_sender` = {$this->playerId} AND `message_type` = \" . MSG_TYPE_PLAYER .\n      \" GROUP BY `message_sender`;\",\n      '',\n      true\n    );\n    $messages_total[MSG_TYPE_OUTBOX] = intval($query['message_count']);\n\n    $template = SnTemplate::gettemplate('msg_message_class', true);\n    foreach ($this->messageClassList as $message_class_id => $message_class) {\n      $template->assign_block_vars('message_class', array(\n        'ID'     => $message_class_id,\n        'STYLE'  => $message_class['name'],\n        'TEXT'   => $this->lang['msg_class'][$message_class_id],\n        'UNREAD' => $this->user[$message_class['name']],\n        'TOTAL'  => intval($messages_total[$message_class_id]),\n      ));\n    }\n\n    $template->assign_vars(array(\n      'PAGE_HINT' => $this->lang['msg_page_hint_class'],\n    ));\n\n    return $template;\n  }\n\n\n  protected function getRecipientData() {\n    if (!empty($this->recipient_name_unsafe)) {\n      $recipient_row = db_user_by_username($this->recipient_name_unsafe);\n    }\n\n    if (empty($recipient_row) && !empty($this->recipient_id_unsafe)) {\n      $recipient_row = db_user_by_id($this->recipient_id_unsafe);\n    }\n\n    if (is_array($recipient_row) && !empty($recipient_row)) {\n      $this->recipient_id_unsafe = $recipient_row['id'];\n      $this->recipient_name_unsafe = $recipient_row['username'];\n    } else {\n      $this->recipient_id_unsafe = 0;\n      $this->recipient_name_unsafe = '';\n    }\n  }\n\n  protected function getUserRef() {\n    global $user;\n\n    $this->user = &$user;\n    $this->playerId = idval($user['id']);\n  }\n\n  /**\n   * Working on subject\n   */\n  protected function transformSubject() {\n    $re = 0;\n    // Removing extra Re:Re:Re:... from subject start\n    if ($this->subject_unsafe) {\n      $reLength = strlen($this->lang['msg_answer_prefix']);\n      while (strpos($this->subject_unsafe, $this->lang['msg_answer_prefix']) === 0) {\n        $this->subject_unsafe = trim(substr($this->subject_unsafe, $reLength));\n        $re++;\n      }\n    }\n\n    if ($this->subject_unsafe) {\n      $re ? $this->subject_unsafe = $this->lang['msg_answer_prefix'] . $this->subject_unsafe : false;\n    } else {\n      $this->subject_unsafe = $this->lang['msg_subject_default'];\n    }\n  }\n\n  protected function loadParams() {\n    parent::loadParams();\n\n    $this->current_class = sys_get_param_int('message_class');\n    if (!isset($this->messageClassList[$this->current_class])) {\n      $this->current_class = 0;\n      $this->mode = static::MESSAGES_MODE_CATEGORIES;\n    } else {\n      $this->mode = sys_get_param_str('msg_delete') ? static::MESSAGES_MODE_DELETE : sys_get_param_str('mode');\n    }\n\n    if ($this->showAll = sys_get_param_str('msg_show_all') ? true : false) {\n      $this->mode = static::MESSAGES_MODE_MESSAGES;\n    }\n\n    $this->loadParamsCompose();\n    $this->loadParamsDelete();\n  }\n\n  protected function loadParamsCompose() {\n    $this->recipient_name_unsafe = sys_get_param_str_unsafe('recipient_name');\n    $this->recipient_id_unsafe = sys_get_param_id('id');\n    // Removing starting and trailing blank chars\n    $this->subject_unsafe = trim(sys_get_param_str_unsafe('subject'));\n    $this->transformSubject();\n    $this->sendText_unsafe = sys_get_param_str_unsafe('text');\n    $this->doSend = sys_get_param_str('msg_send');\n  }\n\n  protected function loadParamsDelete() {\n    $this->deleteRange = sys_get_param_str('message_range', static::MESSAGES_DELETE_RANGE_NONE);\n    // Incorrect range - do nothing\n    if (!in_array($this->deleteRange, static::MESSAGES_DELETE_RANGES_ALLOWED)) {\n      return;\n    }\n\n    $this->markedMessageIdList = [];\n    $unsafeMarkList = sys_get_param('mark', []);\n\n    foreach ($unsafeMarkList as $unsafeMark) {\n      if (!empty($unsafeMark = idval($unsafeMark))) {\n        $this->markedMessageIdList[] = $unsafeMark;\n      }\n    }\n  }\n\n  protected function wrapSendPm() {\n    msg_send_simple_message(\n      $this->recipient_id_unsafe,\n      $this->playerId,\n      SN_TIME_NOW,\n      MSG_TYPE_PLAYER,\n      \"{$this->user['username']} [{$this->user['galaxy']}:{$this->user['system']}:{$this->user['planet']}]\",\n      $this->subject_unsafe,\n      $this->sendText_unsafe,\n      STRING_NEED_ESCAPING\n    );\n  }\n\n}\n"
  },
  {
    "path": "classes/Pages/Deprecated/PageOverview.php",
    "content": "<?php\n/**\n * Created by Gorlum 25.01.2018 7:50\n */\n\nnamespace Pages\\Deprecated;\n\nuse DBAL\\db_mysql;\nuse Fleet\\DbFleetStatic;\nuse Planet\\DBStaticPlanet;\nuse SN;\nuse HelperString;\nuse Exception;\nuse Planet\\Planet;\nuse SnTemplate;\nuse template;\n\nclass PageOverview extends PageDeprecated {\n  /**\n   * @var \\classConfig $config\n   */\n  protected $config;\n  /**\n   * @var \\core_auth $auth\n   */\n  protected $auth;\n  /**\n   * @var \\classLocale $lang\n   */\n  protected $lang;\n\n  /**\n   * @var \\Core\\RepoV2 $repo\n   */\n  protected $repo;\n\n  /**\n   * @var Planet $planet\n   */\n  protected $planet;\n\n  public function __construct() {\n    parent::__construct();\n\n    $this->lang   = SN::$lang;\n    $this->config = SN::$config;\n    $this->auth   = SN::$auth;\n    $this->repo   = SN::$gc->repoV2;\n\n    lng_include('overview');\n    lng_include('mrc_mercenary');\n  }\n\n\n  public function setPlanetById($planetId) {\n    /** @noinspection PhpUnhandledExceptionInspection */\n    return $this->planet = $this->repo->getPlanet($planetId);\n  }\n\n  public function getPlanet() {\n    if (empty($this->planet)) {\n      throw new Exception('No planet in Overview');\n    }\n\n    return $this->planet;\n  }\n\n  public function route() {\n    global $user, $planetrow, $que, $user_option_list;\n\n    $this->setPlanetById($planetrow['id']);\n\n    switch ($mode = sys_get_param_str('mode')) {\n      case 'manage':\n        $this->manage($user, $planetrow);\n      break;\n\n      default:\n        $this->overview($user, $planetrow, $que, $user_option_list);\n      break;\n    }\n  }\n\n  /**\n   * @param $user\n   * @param &$planetrow\n   * @param $que\n   * @param $user_option_list\n   */\n  public function overview($user, &$planetrow, $que, $user_option_list) {\n    $this->planet->sn_sys_sector_buy();\n\n    rpg_level_up($user, RPG_STRUCTURE);\n    rpg_level_up($user, RPG_RAID);\n    rpg_level_up($user, RPG_TECH);\n    rpg_level_up($user, RPG_EXPLORE);\n\n    if (sys_get_param_str('rename') && $new_name = sys_get_param_str('new_name')) {\n      $planetrow['name'] = $new_name;\n      $new_name_safe     = SN::$db->db_escape($new_name);\n      DBStaticPlanet::db_planet_set_by_id($planetrow['id'], \"`name` = '{$new_name_safe}'\");\n      $planetrow = DBStaticPlanet::db_planet_by_id($planetrow['id'], true);\n      $this->planet->reload();\n    }\n\n    if (!empty($theResult = $this->planet->sn_sys_planet_core_transmute($user))) {\n      $this->resultMessageList->add($theResult['MESSAGE'], $theResult['STATUS']);\n    }\n\n    $template = SnTemplate::gettemplate('planet_overview', true);\n\n    $user_dark_matter = mrc_get_level($user, false, RES_DARK_MATTER);\n    $template->assign_recursive($this->planet->tpl_planet_density_info($user_dark_matter));\n\n    $fleets_to_planet = [];\n    $planet_count     = 0;\n    $planets_query    = DBStaticPlanet::db_planet_list_sorted($user, false);\n    foreach ($planets_query as $an_id => $planetRecord) {\n      $fleet_list = flt_get_fleets_to_planet($planetRecord);\n      if (!empty($fleet_list['own']['count'])) {\n        $fleets_to_planet[$an_id] = tpl_parse_fleet_sn($fleet_list['own']['total'], getUniqueFleetId($planetRecord));\n      }\n\n      if ($planetRecord['planet_type'] == PT_MOON) {\n        continue;\n      }\n\n      $planet_count++;\n\n      db_mysql::db_transaction_start();\n      $updatedData = sys_o_get_updated($user['id'], $planetRecord['id'], SN_TIME_NOW, false, true);\n      db_mysql::db_transaction_commit();\n\n      $templatizedPlanet = tpl_parse_planet($user, $updatedData['planet']);\n      $templatizedPlanet += tpl_parse_planet_result_fleet($updatedData['planet'], $fleet_list);\n      $templatizedPlanet += tpl_parse_planet_moon($planetRecord['id']);\n\n      $template->assign_block_vars('planet', $templatizedPlanet);\n    }\n\n    $fleets = flt_parse_fleets_to_events(DbFleetStatic::fleet_and_missiles_list_incoming($user['id']));\n    tpl_assign_fleet($template, $fleets_to_planet);\n    tpl_assign_fleet($template, $fleets);\n\n    $lune = $planetrow['planet_type'] == PT_PLANET ? DBStaticPlanet::db_planet_by_parent($planetrow['id']) : DBStaticPlanet::db_planet_by_id($planetrow['parent_planet']);\n    if ($lune) {\n      $template->assign_vars([\n        'MOON_ID'   => $lune['id'],\n        'MOON_IMG'  => $lune['image'],\n        'MOON_NAME' => $lune['name'],\n      ]);\n    }\n\n    $template->assign_recursive($this->planet->int_planet_pretemplate($user));\n\n    if (!defined('GAME_STRUCTURES_DISABLED') || !GAME_STRUCTURES_DISABLED) {\n      $this->templateQue($template, QUE_STRUCTURES, $que);\n    }\n\n    $this->templateQue($template, SUBQUE_FLEET, $que);\n    if (!defined('GAME_DEFENSE_DISABLED') || !GAME_DEFENSE_DISABLED) {\n      $this->templateQue($template, SUBQUE_DEFENSE, $que);\n    }\n\n    $template->assign_vars($this->templateGridSizes($user, $user_option_list, $planet_count));\n\n    $template->assign_vars($this->templateSector($user, $user_dark_matter));\n\n    $planet_recyclers_orbiting = 0;\n    foreach (sn_get_groups('flt_recyclers') as $recycler_id) {\n      $planet_recyclers_orbiting += mrc_get_level($user, $planetrow, $recycler_id);\n    }\n    $governor_level = $planetrow['PLANET_GOVERNOR_ID'] ? mrc_get_level($user, $planetrow, $planetrow['PLANET_GOVERNOR_ID'], false, true) : 0;\n    $template->assign_vars([\n      'USER_ID'        => $user['id'],\n      'user_username'  => $user['username'],\n      'USER_AUTHLEVEL' => $user['authlevel'],\n\n      'NEW_MESSAGES' => $user['new_message'],\n      // TODO\n      // 'NEW_LEVEL_MINER' => $level_miner,\n      // 'NEW_LEVEL_RAID'  => $level_raid,\n\n      'planet_diameter' => HelperString::numberFloorAndFormat($planetrow['diameter']),\n\n      'metal_debris'         => HelperString::numberFloorAndFormat($planetrow['debris_metal']),\n      'crystal_debris'       => HelperString::numberFloorAndFormat($planetrow['debris_crystal']),\n      'PLANET_RECYCLERS'     => $planet_recyclers_orbiting,\n      'planet_image'         => $planetrow['image'],\n      'planet_temp_min'      => $planetrow['temp_min'],\n      'planet_temp_avg'      => round(($planetrow['temp_min'] + $planetrow['temp_max']) / 2),\n      'planet_temp_max'      => $planetrow['temp_max'],\n      'planet_density'       => $planetrow['density'],\n      'planet_density_index' => $planetrow['density_index'],\n      'planet_density_text'  => $this->lang['uni_planet_density_types'][$planetrow['density_index']],\n\n      'GATE_LEVEL'          => mrc_get_level($user, $planetrow, STRUC_MOON_GATE),\n      'GATE_JUMP_REST_TIME' => uni_get_time_to_jump($planetrow),\n\n      'ADMIN_EMAIL' => $this->config->game_adminEmail,\n\n      'PLANET_GOVERNOR_ID'         => $planetrow['PLANET_GOVERNOR_ID'],\n      'PLANET_GOVERNOR_LEVEL'      => $governor_level,\n      'PLANET_GOVERNOR_LEVEL_PLUS' => mrc_get_level($user, $planetrow, $planetrow['PLANET_GOVERNOR_ID']) - $governor_level,\n      'PLANET_GOVERNOR_NAME'       => $this->lang['tech'][$planetrow['PLANET_GOVERNOR_ID']],\n\n      'IS_CAPITAL' => $planetrow['id'] == $user['id_planet'],\n      'IS_MOON'    => $planetrow['planet_type'] == PT_MOON,\n\n      'DARK_MATTER' => $user_dark_matter,\n\n      'PAGE_HEADER' => $this->lang['ov_overview'] . \" - \" . $this->lang['sys_planet_type'][$planetrow['planet_type']] . \" {$planetrow['name']} [{$planetrow['galaxy']}:{$planetrow['system']}:{$planetrow['planet']}]\",\n    ]);\n    tpl_set_resource_info($template, $planetrow, $fleets_to_planet);\n\n    $this->resultMessageList->templateAdd($template);\n\n    SnTemplate::display($template);\n  }\n\n  /**\n   * @param $user\n   * @param &$planetrow\n   */\n  public function manage($user, &$planetrow) {\n    $this->planet->sn_sys_sector_buy('overview.php?mode=manage');\n\n    $user_dark_matter = mrc_get_level($user, false, RES_DARK_MATTER);\n    if (!empty($theResult = $this->planet->sn_sys_planet_core_transmute($user))) {\n      $this->resultMessageList->add($theResult['MESSAGE'], $theResult['STATUS']);\n    }\n\n    $template  = SnTemplate::gettemplate('planet_manage', true);\n    $planet_id = sys_get_param_id('planet_id');\n\n    if (sys_get_param_str('rename') && $new_name = sys_get_param_str('new_name')) {\n      $planetrow['name'] = $new_name;\n      DBStaticPlanet::db_planet_set_by_id($planetrow['id'], \"`name` = '{$new_name}'\");\n    } elseif (sys_get_param_str('action') == 'make_capital') {\n      try {\n        db_mysql::db_transaction_start();\n        $user      = db_user_by_id($user['id'], true);\n        $planetrow = DBStaticPlanet::db_planet_by_id($planetrow['id'], true);\n\n        if ($planetrow['planet_type'] != PT_PLANET) {\n          throw new exception($this->lang['ov_capital_err_not_a_planet'], ERR_ERROR);\n        }\n\n        if ($planetrow['id'] == $user['id_planet']) {\n          throw new exception($this->lang['ov_capital_err_capital_already'], ERR_ERROR);\n        }\n\n        if ($user_dark_matter < $this->config->planet_capital_cost) {\n          throw new exception($this->lang['ov_capital_err_no_dark_matter'], ERR_ERROR);\n        }\n\n        rpg_points_change($user['id'], RPG_CAPITAL, -$this->config->planet_capital_cost,\n          array('Planet %s ID %d at coordinates %s now become Empire Capital', $planetrow['name'], $planetrow['id'], uni_render_coordinates($planetrow))\n        );\n\n        db_user_set_by_id($user['id'], \"id_planet = {$planetrow['id']}, galaxy = {$planetrow['galaxy']}, system = {$planetrow['system']}, planet = {$planetrow['planet']}\");\n\n        $user['id_planet'] = $planetrow['id'];\n        $this->resultMessageList->add($this->lang['ov_capital_err_none'], ERR_NONE);\n        db_mysql::db_transaction_commit();\n        sys_redirect('overview.php?mode=manage');\n      } catch (exception $e) {\n        db_mysql::db_transaction_rollback();\n        $this->resultMessageList->add($e->getMessage(), $e->getCode());\n      }\n    } elseif (sys_get_param_str('action') == 'planet_teleport') {\n      try {\n        if (!uni_coordinates_valid($new_coordinates = array(\n          'galaxy' => sys_get_param_int('new_galaxy'),\n          'system' => sys_get_param_int('new_system'),\n          'planet' => sys_get_param_int('new_planet')))\n        ) {\n          throw new exception($this->lang['ov_teleport_err_wrong_coordinates'], ERR_ERROR);\n        }\n\n        db_mysql::db_transaction_start();\n        // При телепорте обновлять данные не надо - просто получить текущие данные и залочить их\n        $user      = db_user_by_id($user['id'], true);\n        $planetrow = DBStaticPlanet::db_planet_by_id($planetrow['id'], true);\n\n        $can_teleport = uni_planet_teleport_check($user, $planetrow, $new_coordinates);\n        if ($can_teleport['result'] != ERR_NONE) {\n          throw new exception($can_teleport['message'], $can_teleport['result']);\n        }\n\n        rpg_points_change($user['id'], RPG_TELEPORT, -$this->config->planet_teleport_cost,\n          array($this->lang['ov_teleport_log_record'], $planetrow['name'], $planetrow['id'], uni_render_coordinates($planetrow), uni_render_coordinates($new_coordinates))\n        );\n        $planet_teleport_next = SN_TIME_NOW + $this->config->planet_teleport_timeout;\n        DBStaticPlanet::db_planet_set_by_gspt($planetrow['galaxy'], $planetrow['system'], $planetrow['planet'], \"galaxy = {$new_coordinates['galaxy']}, system = {$new_coordinates['system']}, planet = {$new_coordinates['planet']}, planet_teleport_next = {$planet_teleport_next}\",\n          PT_ALL);\n\n        if ($planetrow['id'] == $user['id_planet']) {\n          db_user_set_by_id($user['id'], \"galaxy = {$new_coordinates['galaxy']}, system = {$new_coordinates['system']}, planet = {$new_coordinates['planet']}\");\n        }\n        db_mysql::db_transaction_commit();\n\n        $user      = db_user_by_id($user['id'], true);\n        $planetrow = DBStaticPlanet::db_planet_by_id($planetrow['id'], true);\n        $this->resultMessageList->add($this->lang['ov_teleport_err_none'], ERR_NONE);\n        sys_redirect('overview.php?mode=manage');\n      } catch (exception $e) {\n        db_mysql::db_transaction_rollback();\n        $this->resultMessageList->add($e->getMessage(), $e->getCode());\n      }\n    } elseif (sys_get_param_str('action') == 'planet_abandon') {\n      if ($this->auth->password_check(sys_get_param('abandon_confirm'))) {\n        if ($user['id_planet'] != $user['current_planet'] && $user['current_planet'] == $planet_id) {\n          $destroyed = SN_TIME_NOW + 60 * 60 * 24;\n          DBStaticPlanet::db_planet_set_by_id($user['current_planet'], \"`destruyed`='{$destroyed}', `id_owner`=0\");\n          DBStaticPlanet::db_planet_set_by_parent($user['current_planet'], \"`destruyed`='{$destroyed}', `id_owner`=0\");\n          db_user_set_by_id($user['id'], '`current_planet` = `id_planet`');\n          SnTemplate::messageBox($this->lang['ov_delete_ok'], $this->lang['colony_abandon'], 'overview.php?mode=manage');\n        } else {\n          SnTemplate::messageBox($this->lang['ov_delete_wrong_planet'], $this->lang['colony_abandon'], 'overview.php?mode=manage');\n        }\n      } else {\n        SnTemplate::messageBox($this->lang['ov_delete_wrong_pass'], $this->lang['colony_abandon'], 'overview.php?mode=manage');\n      }\n    } elseif (($hire = sys_get_param_int('hire')) && in_array($hire, sn_get_groups('governors'))) {\n      $this->planet->governorHire($hire);\n\n      sys_redirect('overview.php?mode=manage');\n      die();\n    }\n\n    // TODO - refresh planet by itself\n//    $this->setPlanetById($planetrow['id']);\n    $this->planet->dbLoadRecord($planetrow['id']);\n    $template->assign_recursive($this->planet->int_planet_pretemplate($user));\n\n    foreach (sn_get_groups('governors') as $governor_id) {\n      if ($planetrow['planet_type'] == PT_MOON && $governor_id == MRC_TECHNOLOGIST) {\n        continue;\n      }\n\n      $governor_level = $planetrow['PLANET_GOVERNOR_ID'] == $governor_id ? $planetrow['PLANET_GOVERNOR_LEVEL'] : 0;\n      $build_data     = eco_get_build_data($user, $planetrow, $governor_id, $governor_level);\n      $template->assign_block_vars('governors', array(\n        'ID'         => $governor_id,\n        'NAME'       => $this->lang['tech'][$governor_id],\n        'COST'       => $build_data[BUILD_CREATE][RES_DARK_MATTER],\n        'MAX'        => get_unit_param($governor_id, P_MAX_STACK),\n        'LEVEL'      => $governor_level,\n        'LEVEL_PLUS' => mrc_get_level($user, $planetrow, $governor_id) - $governor_level,\n      ));\n    }\n\n    $user_dark_matter = mrc_get_level($user, false, RES_DARK_MATTER);\n    $template->assign_recursive($this->planet->tpl_planet_density_info($user_dark_matter));\n\n    $template->assign_vars($this->templateSector($user, $user_dark_matter));\n\n    $can_teleport = uni_planet_teleport_check($user, $planetrow);\n    $template->assign_vars(array(\n      'DARK_MATTER' => $user_dark_matter,\n\n      'CAN_TELEPORT'         => $can_teleport['result'] == ERR_NONE,\n      'CAN_NOT_TELEPORT_MSG' => $can_teleport['message'],\n      'TELEPORT_COST_TEXT'   => prettyNumberStyledCompare($this->config->planet_teleport_cost, $user_dark_matter),\n\n      'IS_CAPITAL'        => $planetrow['id'] == $user['id_planet'],\n      'CAN_CAPITAL'       => $user_dark_matter >= $this->config->planet_capital_cost,\n      'CAPITAL_COST_TEXT' => prettyNumberStyledCompare($this->config->planet_capital_cost, $user_dark_matter),\n\n      'PAGE_HINT' => $this->lang['ov_manage_page_hint'],\n    ));\n\n    $this->resultMessageList->templateAdd($template);\n\n    SnTemplate::display($template, $this->lang['rename_and_abandon_planet']);\n  }\n\n  /**\n   * @param $user\n   * @param $user_dark_matter\n   *\n   * @return array\n   */\n  protected function templateSector($user, $user_dark_matter) {\n    $sector_cost = eco_get_build_data($user, $this->planet->asArray(), UNIT_SECTOR, mrc_get_level($user, $this->planet->asArray(), UNIT_SECTOR), true);\n    $sector_cost = $sector_cost[BUILD_CREATE][RES_DARK_MATTER];\n    $planet_fill = floor($this->planet->field_current / eco_planet_fields_max($this->planet->asArray()) * 100);\n    $planet_fill = $planet_fill > 100 ? 100 : $planet_fill;\n    $vararray    = [\n      'planet_field_current' => $this->planet->field_current,\n      'planet_field_max'     => eco_planet_fields_max($this->planet->asArray()),\n      'PLANET_FILL'          => floor($this->planet->field_current / eco_planet_fields_max($this->planet->asArray()) * 100),\n      'PLANET_FILL_BAR'      => $planet_fill,\n      'SECTOR_CAN_BUY'       => $sector_cost <= $user_dark_matter,\n      'SECTOR_COST'          => $sector_cost,\n      'SECTOR_COST_TEXT'     => HelperString::numberFloorAndFormat($sector_cost),\n    ];\n\n    return $vararray;\n  }\n\n\n  /**\n   * @param template $template\n   * @param int      $que_type\n   * @param          $que\n   */\n  protected function templateQue($template, $que_type, $que) {\n//    $que = que_get($planet->id_owner, $planet->id, $que_type);\n    $que = $que['ques'][$que_type][$this->planet->id_owner][$this->planet->id];\n\n    $que_length = 0;\n    if (!empty($que)) {\n      foreach ($que as $que_item) {\n        $template->assign_block_vars('que', que_tpl_parse_element($que_item));\n      }\n      $que_length = count($que);\n    }\n\n    $template->assign_block_vars('ques', [\n      'ID'     => $que_type,\n      'NAME'   => $this->lang['sys_ques'][$que_type],\n      'LENGTH' => $que_length,\n    ]);\n  }\n\n  /**\n   * Calculates planet grid sizes (horizontal and vertical) for Planet Overview\n   *\n   * @param $user\n   * @param $user_option_list\n   * @param $planet_count\n   *\n   * @return array\n   */\n  protected function templateGridSizes($user, $user_option_list, $planet_count) {\n    $overview_planet_rows    = $user['opt_int_overview_planet_rows'];\n    $overview_planet_columns = $user['opt_int_overview_planet_columns'];\n\n    if ($overview_planet_rows <= 0 && $overview_planet_columns <= 0) {\n      $overview_planet_rows    = $user_option_list[OPT_INTERFACE]['opt_int_overview_planet_rows'];\n      $overview_planet_columns = $user_option_list[OPT_INTERFACE]['opt_int_overview_planet_columns'];\n    }\n\n    if ($overview_planet_rows > 0 && $overview_planet_columns <= 0) {\n      $overview_planet_columns = ceil($planet_count / $overview_planet_rows);\n    }\n    $vararray = [\n      'LIST_ROW_COUNT'    => $overview_planet_rows,\n      'LIST_COLUMN_COUNT' => $overview_planet_columns,\n    ];\n\n    return $vararray;\n  }\n\n}\n"
  },
  {
    "path": "classes/Pages/Helpers/PageHelperAlly.php",
    "content": "<?php\n/**\n * Created by Gorlum 19.08.2019 19:12\n */\n\nnamespace Pages\\Helpers;\n\n\nuse Alliance\\Alliance;\nuse classLocale;\nuse mysqli_result;\nuse SN;\nuse SnTemplate;\nuse template;\n\nclass PageHelperAlly {\n  /**\n   * @param             $mode\n   * @param array       $user\n   * @param classLocale $lang\n   */\n\n  public static function pageExternalSearch($mode, array $user, classLocale $lang) {\n    if (!defined('SN_IN_ALLY') || SN_IN_ALLY !== true) {\n      SN::$debug->error(\"Attempt to call ALLIANCE page mode {$mode} directly - not from alliance.php\", 'Forbidden', 403);\n    }\n\n    $template = SnTemplate::gettemplate('ali_search', true);\n\n    $ali_search_text = sys_get_param_str('searchtext');\n\n    if ($ali_search_text) {\n      $template->assign_var('SEARCH_TEXT', $ali_search_text);\n\n      $search = doquery(\"SELECT DISTINCT * FROM {{alliance}} WHERE `ally_name` LIKE '%{$ali_search_text}%' OR `ally_tag` LIKE '%{$ali_search_text}%' LIMIT 30\");\n      if (SN::$db->db_num_rows($search)) {\n        PageHelperAlly::allyFetchFromResult($template, $search, $user['total_points']);\n      }\n    }\n\n    self::externalSearchRecommend($user, $template);\n\n    SnTemplate::display($template, $lang['ali_search_title']);\n  }\n\n\n  /**\n   * @param template           $template\n   * @param mysqli_result|null $result\n   * @param int                $playerPoints\n   */\n  public static function allyFetchFromResult(template $template, $result, $playerPoints = 0) {\n    while ($ally_row = db_fetch($result)) {\n      $perPlayer  = $ally_row['total_points'] / $ally_row['ally_members'];\n      $pointsDiff = round($playerPoints - $perPlayer);\n\n      $pointsRate = $playerPoints / $perPlayer;\n      $pointsRate = round($pointsRate, 2);\n\n//    $pointsRate && $pointsRate < 1 ? $pointsRate = \"1 / \" . round(1 / $pointsRate, 2) : false;\n\n      $template->assign_block_vars('alliances', array(\n        'ID'          => $ally_row['id'],\n        'TAG'         => $ally_row['ally_tag'],\n        'NAME'        => $ally_row['ally_name'],\n        'MEMBERS'     => $ally_row['ally_members'],\n        'NO_REQUESTS' => $ally_row['ally_request_notallow'],\n        'DIFF'        => $pointsDiff,\n        'RATE'        => $pointsRate,\n      ));\n    }\n  }\n\n  /**\n   * @param array    $user\n   * @param template $template\n   */\n  public static function externalSearchRecommend(array $user, template $template) {\n    if (empty($user['ally_id'])) {\n      $recommended = Alliance::recommend($user['total_points']);\n      if (SN::$db->db_num_rows($recommended)) {\n        $template->assign_block_vars('alliances', array(\n          'ID' => -1,\n        ));\n        PageHelperAlly::allyFetchFromResult($template, $recommended, $user['total_points']);\n      }\n    }\n\n    $template->assign_vars(['PAGE_HINT' => SN::$lang['ali_search_result_tip']]);\n  }\n\n}\n"
  },
  {
    "path": "classes/Pages/IPage.php",
    "content": "<?php\n/**\n * Created by Gorlum 04.03.2017 16:36\n */\n\nnamespace Pages;\n\n\ninterface IPage {\n\n  /**\n   * IPage constructor.\n   */\n  public function __construct();\n\n  /**\n   * Loading page data from page params\n   */\n  public function loadParams();\n\n  /**\n   * @param string $action\n   *\n   * @return bool\n   */\n  public function checkAction($action);\n\n}\n"
  },
  {
    "path": "classes/Pages/PageAjax.php",
    "content": "<?php\n/**\n * Created by Gorlum 04.03.2017 16:38\n */\n\nnamespace Pages;\n\nuse Player\\userOptions;\n\n/**\n * Class PageAjax\n *\n *\n *\n * @package Pages\n */\nclass PageAjax implements IPage {\n  /**\n   * List of allowed actions on page to prevent unauthorized user's access to methods\n   * Also index of array is the name of the class method called\n   *\n   * @var array[] $allowedActions [(string)action] => {true|any data}\n   */\n  protected $allowedActions = [];\n\n  /**\n   * @var userOptions $userOptions\n   */\n  protected $userOptions;\n\n\n  /**\n   * @inheritdoc\n   */\n  public function __construct() {\n\n  }\n\n  /**\n   * @inheritdoc\n   */\n  public function loadParams() {\n  }\n\n  /**\n   * @inheritdoc\n   */\n  public function checkAction($action) {\n    return !empty($this->allowedActions[$action]);\n  }\n\n}\n"
  },
  {
    "path": "classes/Pages/PageAjaxIgnore.php",
    "content": "<?php\n/**\n * Created by Gorlum 14.08.2019 22:41\n */\n\nnamespace Pages;\n\nuse SN;\n\nclass PageAjaxIgnore extends PageAjax {\n  /**\n   * List of allowed actions for this page\n   *\n   * @var array[] $allowedActions [(string)action] => true\n   */\n  protected $allowedActions = [\n    'ignorePlayer'   => true,\n    'unIgnorePlayer' => true,\n  ];\n\n  protected $subjectPlayerId = 0;\n\n  /**\n   * Loading page data from page params\n   */\n  public function loadParams() {\n    $this->subjectPlayerId = sys_get_param_id('subjectId', 0);\n  }\n\n  // Page Actions ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n  public function ignorePlayer() {\n    global $user;\n\n    SN::$gc->ignores->ignore($user['id'], $this->subjectPlayerId);\n\n    return [\n      'player' => [\n        'id' => $user['id'],\n        'ignores' => [\n          $this->subjectPlayerId => true,\n        ],\n      ],\n    ];\n  }\n\n  public function unIgnorePlayer() {\n    global $user;\n\n    SN::$gc->ignores->unIgnore($user['id'], $this->subjectPlayerId);\n\n    return [\n      'player' => [\n        'id' => $user['id'],\n        'ignores' => [\n          $this->subjectPlayerId => false,\n        ],\n      ],\n    ];\n  }\n\n}\n"
  },
  {
    "path": "classes/Pages/PageQuest.php",
    "content": "<?php\n/**\n * Created by Gorlum 12.02.2017 8:51\n */\n\nnamespace Pages;\n\nuse \\SN;\n\nclass PageQuest extends PageAjax {\n  /**\n   * List of allowed actions for this page\n   *\n   * @var array[] $allowedActions [(string)action] => true\n   */\n  protected $allowedActions = [\n    'saveFilter' => true,\n  ];\n\n  protected $filterQuestStatus = QUEST_STATUS_ALL;\n\n  /**\n   * Loading page data from page params\n   */\n  public function loadParams() {\n    $this->filterQuestStatus = sys_get_param_int('filterQuestStatus', QUEST_STATUS_ALL);\n    $this->checkParams();\n  }\n\n  protected function checkParams() {\n    static $statuses = array(\n      QUEST_STATUS_ALL => '',\n      QUEST_STATUS_EXCEPT_COMPLETE => '',\n      QUEST_STATUS_NOT_STARTED => '',\n      QUEST_STATUS_STARTED => '',\n      QUEST_STATUS_COMPLETE => '',\n    );\n\n    if(!isset($statuses[$this->filterQuestStatus])) {\n      $this->filterQuestStatus = QUEST_STATUS_ALL;\n    }\n  }\n\n  // Page Actions ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n  /**\n   * Finishes current tutorial\n   */\n  public function saveFilter() {\n    SN::$user_options[PLAYER_OPTION_QUEST_LIST_FILTER] = $this->filterQuestStatus;\n    return array(\n      'quest' => array(\n        'filterQuestStatus' => $this->filterQuestStatus,\n      ),\n    );\n  }\n\n}\n"
  },
  {
    "path": "classes/Pages/PageTutorial.php",
    "content": "<?php\n/**\n * Created by Gorlum 12.02.2017 8:51\n */\n\nnamespace Pages;\n\nuse \\SN;\nuse Player\\userOptions;\n\nclass PageTutorial extends PageAjax {\n\n  protected $allowedActions = array(\n    'ajax' => true,\n    'ajaxNext' => true,\n    'ajaxPrev' => true,\n    'ajaxFinish' => true,\n  );\n\n  protected $id = 1;\n\n  /**\n   * @var \\Player\\userOptions $userOptions\n   */\n  protected $userOptions;\n\n  /**\n   * @inheritdoc\n   */\n  public function loadParams() {\n    $this->id = sys_get_param_id('id', 1);\n  }\n\n  protected function ajaxRender($method = 'getById') {\n    /**\n     * @var \\TextEntity $text\n     */\n    $text = SN::$gc->textModel->$method($this->id);\n    $result = $text->toArrayParsedBBC(HTML_ENCODE_MULTILINE_JS);\n    if (!$text->isEmpty()) {\n      SN::$user_options[PLAYER_OPTION_TUTORIAL_CURRENT] = $text->id;\n    }\n\n    return array(\n      'tutorial' => $result,\n    );\n  }\n\n  /**\n   * @return array\n   */\n  protected function block2ValueList() {\n    $result = array();\n\n    $text = SN::$gc->textModel->getById($this->id);\n    $array = $text->toArrayParsedBBC(HTML_ENCODE_MULTILINE_JS);\n    foreach ($array as $key => $value) {\n      $result[] = array(\n        'KEY'   => $key,\n        'VALUE' => $value,\n      );\n    }\n\n    return $result;\n  }\n\n  /**\n   * @return bool\n   */\n  protected function isBlockEnabled() {\n    if ($this->userOptions[PLAYER_OPTION_TUTORIAL_DISABLED]) {\n      return false;\n    }\n\n    if (SN::$gc->textModel->getById($this->userOptions[PLAYER_OPTION_TUTORIAL_CURRENT])->isEmpty()) {\n      return false;\n    }\n\n    // Checking if there is new tutorial appears after user finished old one\n    if ($this->userOptions[PLAYER_OPTION_TUTORIAL_FINISHED]) {\n      $next = SN::$gc->textModel->next($this->userOptions[PLAYER_OPTION_TUTORIAL_CURRENT]);\n      if (!$next->isEmpty()) {\n        $this->userOptions[PLAYER_OPTION_TUTORIAL_FINISHED] = 0;\n      } else {\n        return false;\n      }\n    }\n\n    return true;\n  }\n\n  // Page Actions ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n  /**\n   * Action 'ajax'\n   *\n   * @return array\n   */\n  public function ajax() {\n    return $this->ajaxRender('getById');\n  }\n\n  /**\n   * Returns ajax-ready next element in chain\n   *\n   * @return array\n   */\n  public function ajaxNext() {\n    return $this->ajaxRender('next');\n  }\n\n  /**\n   * Returns ajax-ready prev element in chain\n   *\n   * @return array\n   */\n  public function ajaxPrev() {\n    return $this->ajaxRender('prev');\n  }\n\n  /**\n   * Finishes current tutorial\n   */\n  public function ajaxFinish() {\n    SN::$user_options[PLAYER_OPTION_TUTORIAL_FINISHED] = 1;\n  }\n\n  // Renderers +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n  /**\n   * @param \\template $template\n   *\n   * @return bool - should tutorial block be included\n   */\n  public static function renderNavBar($template) {\n    $block = new self();\n\n    if ($result = $block\n      ->setUserOptions(SN::$user_options)\n      ->setId(SN::$user_options[PLAYER_OPTION_TUTORIAL_CURRENT])\n      ->isBlockEnabled()\n    ) {\n      $template->assign_recursive(array(\n        '.' => array(\n          'tutorial' => $block->block2ValueList())\n      ));\n    }\n\n//    $template->assign_var('PLAYER_OPTION_TUTORIAL_WINDOWED', SN::$user_options[PLAYER_OPTION_TUTORIAL_WINDOWED]);\n\n    return $result;\n  }\n\n  // Getters/Setters +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n  /**\n   * @param int|string $id\n   *\n   * @return $this\n   */\n  public function setId($id) {\n    $this->id = $id;\n\n    return $this;\n  }\n\n  /**\n   * @param \\Player\\userOptions $userOptions\n   *\n   * @return $this\n   */\n  public function setUserOptions($userOptions) {\n    $this->userOptions = $userOptions;\n\n    return $this;\n  }\n\n}\n"
  },
  {
    "path": "classes/Payment/PaymentMethods.php",
    "content": "<?php\n/**\n * Created by Gorlum 21.08.2019 11:53\n */\n\nnamespace Payment;\n\nuse SN;\nuse sn_module_payment;\n\nclass PaymentMethods {\n  const P_CURRENCY = 'currency';\n\n  /**\n   * @var array $payment_methods\n   */\n  public static $payment_methods = [\n    PAYMENT_METHOD_BANK_CARD => [\n      /*\n      PAYMENT_METHOD_id => [\n        'currency' => 'WMR', // Currency code 3 letter\n        'image' => 'design/images/payments/emoney/webmoney.png', // Optional - image location from root. Setting image disables buttoning and name printing\n        'name' => true, // Optional. Forces method name printing with 'image' set\n        'button' => true, // Optional. Forces method buttoning with 'image' set\n      ],\n      */\n      PAYMENT_METHOD_BANK_CARD_STANDARD         => [\n        self::P_CURRENCY => 'RUB',\n        'image'          => 'design/images/payments/card/generic.png',\n        'button'         => true,\n      ],\n      PAYMENT_METHOD_BANK_CARD_LIQPAY           => [\n        self::P_CURRENCY => 'UAH',\n        'image'          => 'design/images/payments/card/liqpay.png',\n        'button'         => true,\n      ],\n      PAYMENT_METHOD_BANK_CARD_EASYPAY          => [\n        self::P_CURRENCY => 'UAH',\n        'image'          => 'design/images/payments/card/easypay.png',\n        'button'         => true,\n      ],\n      PAYMENT_METHOD_BANK_CARD_AMERICAN_EXPRESS => [\n        self::P_CURRENCY => 'USD',\n        'image'          => 'design/images/payments/card/american_express.png',\n        'button'         => true,\n      ],\n      PAYMENT_METHOD_BANK_CARD_JCB              => [\n        self::P_CURRENCY => 'USD',\n        'image'          => 'design/images/payments/card/jcb.png',\n        'button'         => true,\n      ],\n      PAYMENT_METHOD_BANK_CARD_UNIONPAY         => [\n        self::P_CURRENCY => 'USD',\n        'image'          => 'design/images/payments/card/unionpay.png',\n        'button'         => true,\n      ],\n    ],\n\n    PAYMENT_METHOD_EMONEY => [\n      PAYMENT_METHOD_EMONEY_YANDEX       => [\n        self::P_CURRENCY => 'RUB',\n        'image'          => 'design/images/payments/emoney/yandexmoney.png',\n        'button'         => true,\n      ],\n      PAYMENT_METHOD_EMONEY_QIWI         => [\n        self::P_CURRENCY => 'RUB',\n        'image'          => 'design/images/payments/emoney/qiwi.png',\n        'button'         => true,\n      ],\n      PAYMENT_METHOD_EMONEY_PAYPAL       => [\n        self::P_CURRENCY => 'RUB',\n        'image'          => 'design/images/payments/emoney/paypal.png',\n        'button'         => true,\n      ],\n      PAYMENT_METHOD_EMONEY_WEBMONEY_WMR => [\n//        'currency' => 'WMR',\n        self::P_CURRENCY => 'RUB',\n        'image'          => 'design/images/payments/emoney/webmoney_wmr.gif',\n        'button'         => true,\n      ],\n      PAYMENT_METHOD_EMONEY_WEBMONEY_WMZ => [\n//        'currency' => 'WMZ',\n        self::P_CURRENCY => 'RUB',\n        'image'          => 'design/images/payments/emoney/webmoney_wmz.gif',\n        'button'         => true,\n      ],\n      PAYMENT_METHOD_EMONEY_WEBMONEY_WMU => [\n//        'currency' => 'WMU',\n        self::P_CURRENCY => 'RUB',\n        'image'          => 'design/images/payments/emoney/webmoney_wmu.gif',\n        'button'         => true,\n      ],\n      PAYMENT_METHOD_EMONEY_WEBMONEY_WME => [\n//        'currency' => 'WME',\n        self::P_CURRENCY => 'RUB',\n        'image'          => 'design/images/payments/emoney/webmoney_wme.gif',\n        'button'         => true,\n      ],\n      PAYMENT_METHOD_EMONEY_WEBMONEY_WMB => [\n//        'currency' => 'WMB',\n        self::P_CURRENCY => 'RUB',\n        'image'          => 'design/images/payments/emoney/webmoney_wmb.gif',\n        'button'         => true,\n      ],\n      PAYMENT_METHOD_EMONEY_TELEMONEY    => [\n        self::P_CURRENCY => 'RUB',\n        'image'          => 'design/images/payments/emoney/telemoney.gif',\n        'button'         => true,\n      ],\n      PAYMENT_METHOD_EMONEY_ELECSNET     => [\n        self::P_CURRENCY => 'RUB',\n        'image'          => 'design/images/payments/emoney/elecsnet.png',\n        'button'         => true,\n      ],\n      PAYMENT_METHOD_EMONEY_EASYPAY      => [\n        self::P_CURRENCY => 'RUB',\n        'image'          => 'design/images/payments/emoney/easypay.png',\n        'button'         => true,\n      ],\n      PAYMENT_METHOD_EMONEY_RUR_W1R      => [\n        self::P_CURRENCY => 'RUB',\n        'image'          => 'design/images/payments/emoney/walletone.png',\n        'button'         => true,\n      ],\n      PAYMENT_METHOD_EMONEY_MAILRU       => [\n        self::P_CURRENCY => 'RUB',\n        'image'          => 'design/images/payments/emoney/mailru.gif',\n      ],\n    ],\n\n    PAYMENT_METHOD_MOBILE => [\n      PAYMENT_METHOD_MOBILE_SMS         => [\n        self::P_CURRENCY => 'RUB',\n        'image'          => 'design/images/payments/mobile/sms.png',\n        'name'           => true,\n        'button'         => true,\n      ],\n      PAYMENT_METHOD_MOBILE_PAYPAL_ZONG => [\n        self::P_CURRENCY => 'USD',\n        'image'          => 'design/images/payments/mobile/paypal_zong.png',\n        'name'           => true,\n        'button'         => true,\n      ],\n\n\n      PAYMENT_METHOD_MOBILE_MEGAPHONE => [\n        self::P_CURRENCY => 'RUB',\n        'image'          => 'design/images/payments/mobile/megafon.png',\n        'button'         => true,\n      ],\n      PAYMENT_METHOD_MOBILE_MTS       => [\n        self::P_CURRENCY => 'RUB',\n        'image'          => 'design/images/payments/mobile/mts.png',\n        'button'         => true,\n      ],\n      PAYMENT_METHOD_MOBILE_BEELINE   => [\n        self::P_CURRENCY => 'RUB',\n//        'image'    => 'design/images/payments/mobile/mts.png',\n        'name'           => true,\n//        'button'   => true,\n      ],\n      PAYMENT_METHOD_MOBILE_KYIVSTAR  => [\n        self::P_CURRENCY => 'UAH',\n        'image'          => 'design/images/payments/mobile/kyivstar.png',\n        'button'         => true,\n      ],\n    ],\n\n    PAYMENT_METHOD_BANK_INTERNET => [\n      PAYMENT_METHOD_BANK_INTERNET_PRIVAT24         => [\n        self::P_CURRENCY => 'UAH',\n        'image'          => 'design/images/payments/bank_internet/privat24.png',\n        'button'         => true,\n      ],\n      PAYMENT_METHOD_BANK_INTERNET_BANK24           => [\n        self::P_CURRENCY => 'UAH',\n        'image'          => 'design/images/payments/bank_internet/bank24.png',\n        'button'         => true,\n      ],\n      PAYMENT_METHOD_BANK_INTERNET_ALFA_BANK        => [\n        self::P_CURRENCY => 'RUB',\n        'image'          => 'design/images/payments/bank_internet/alfa_bank.png',\n        'button'         => true,\n      ],\n      PAYMENT_METHOD_BANK_INTERNET_SBERBANK         => [\n        self::P_CURRENCY => 'RUB',\n        'image'          => 'design/images/payments/bank_internet/sberbank.png',\n        'button'         => true,\n      ],\n      PAYMENT_METHOD_BANK_INTERNET_PROSMVYAZBANK    => [\n        self::P_CURRENCY => 'RUB',\n        'image'          => 'design/images/payments/bank_internet/prosmvyazbank.png',\n        'button'         => true,\n      ],\n      PAYMENT_METHOD_BANK_INTERNET_HANDY_BANK       => [\n        self::P_CURRENCY => 'RUB',\n        'image'          => 'design/images/payments/bank_internet/handy_bank.png',\n        'button'         => true,\n      ],\n      PAYMENT_METHOD_BANK_INTERNET_RUSSKIY_STANDART => [\n        self::P_CURRENCY => 'RUB',\n        'image'          => 'design/images/payments/bank_internet/russkiy_standart.gif',\n      ],\n      PAYMENT_METHOD_BANK_INTERNET_VTB24            => [\n        self::P_CURRENCY => 'RUB',\n        'image'          => 'design/images/payments/bank_internet/vtb24.gif',\n      ],\n      PAYMENT_METHOD_BANK_INTERNET_OCEAN_BANK       => [\n        self::P_CURRENCY => 'RUB',\n        'image'          => 'design/images/payments/bank_internet/ocean_bank.gif',\n      ],\n      PAYMENT_METHOD_BANK_INTERNET_007              => [\n        self::P_CURRENCY => 'RUB',\n      ],\n      PAYMENT_METHOD_BANK_INTERNET_008              => [\n        self::P_CURRENCY => 'RUB',\n      ],\n      PAYMENT_METHOD_BANK_INTERNET_009              => [\n        self::P_CURRENCY => 'RUB',\n      ],\n      PAYMENT_METHOD_BANK_INTERNET_010              => [\n        self::P_CURRENCY => 'RUB',\n      ],\n      PAYMENT_METHOD_BANK_INTERNET_011              => [\n        self::P_CURRENCY => 'RUB',\n      ],\n      PAYMENT_METHOD_BANK_INTERNET_012              => [\n        self::P_CURRENCY => 'RUB',\n      ],\n      PAYMENT_METHOD_BANK_INTERNET_013              => [\n        self::P_CURRENCY => 'RUB',\n      ],\n      PAYMENT_METHOD_BANK_INTERNET_014              => [\n        self::P_CURRENCY => 'RUB',\n      ],\n      PAYMENT_METHOD_BANK_INTERNET_015              => [\n        self::P_CURRENCY => 'RUB',\n      ],\n      PAYMENT_METHOD_BANK_INTERNET_016              => [\n        self::P_CURRENCY => 'RUB',\n      ],\n      PAYMENT_METHOD_BANK_INTERNET_017              => [\n        self::P_CURRENCY => 'RUB',\n      ],\n      PAYMENT_METHOD_BANK_INTERNET_018              => [\n        self::P_CURRENCY => 'RUB',\n      ],\n      PAYMENT_METHOD_BANK_INTERNET_019              => [\n        self::P_CURRENCY => 'RUB',\n      ],\n      PAYMENT_METHOD_BANK_INTERNET_020              => [\n        self::P_CURRENCY => 'RUB',\n      ],\n      PAYMENT_METHOD_BANK_INTERNET_021              => [\n        self::P_CURRENCY => 'RUB',\n      ],\n    ],\n\n    PAYMENT_METHOD_BANK_TRANSFER => [],\n\n    PAYMENT_METHOD_TERMINAL => [\n      PAYMENT_METHOD_TERMINAL_UKRAINE    => [\n        self::P_CURRENCY => 'UAH',\n        'image'          => 'design/images/payments/terminal/ukraine.png',\n        'button'         => true,\n        'name'           => true,\n      ],\n      PAYMENT_METHOD_TERMINAL_IBOX       => [\n        self::P_CURRENCY => 'UAH',\n        'image'          => 'design/images/payments/terminal/ibox.png',\n        'button'         => true,\n      ],\n      PAYMENT_METHOD_TERMINAL_EASYPAY    => [\n        self::P_CURRENCY => 'UAH',\n        'image'          => 'design/images/payments/terminal/easypay.png',\n        'button'         => true,\n      ],\n      PAYMENT_METHOD_TERMINAL_RUSSIA     => [\n        self::P_CURRENCY => 'RUB',\n        'image'          => 'design/images/payments/terminal/russia.png',\n        'button'         => true,\n        'name'           => true,\n      ],\n      PAYMENT_METHOD_TERMINAL_QIWI       => [\n        self::P_CURRENCY => 'RUB',\n        'image'          => 'design/images/payments/terminal/qiwi.png',\n        'button'         => true,\n      ],\n      PAYMENT_METHOD_TERMINAL_ELECSNET   => [\n        self::P_CURRENCY => 'RUB',\n        'image'          => 'design/images/payments/terminal/elecsnet.png',\n        'button'         => true,\n      ],\n      PAYMENT_METHOD_TERMINAL_TELEPAY    => [\n        self::P_CURRENCY => 'RUB',\n        'image'          => 'design/images/payments/terminal/telepay.png',\n        'button'         => true,\n      ],\n      PAYMENT_METHOD_TERMINAL_ELEMENT    => [\n        self::P_CURRENCY => 'RUB',\n        'image'          => 'design/images/payments/terminal/element.gif',\n      ],\n      PAYMENT_METHOD_TERMINAL_KASSIRANET => [\n        self::P_CURRENCY => 'RUB',\n        'image'          => 'design/images/payments/terminal/kassira_net.gif',\n        'button'         => true,\n      ],\n    ],\n\n    PAYMENT_METHOD_OTHER => [\n      PAYMENT_METHOD_OTHER_EVROSET          => [\n        self::P_CURRENCY => 'RUB',\n        'image'          => 'design/images/payments/other/evroset.gif',\n      ],\n      PAYMENT_METHOD_OTHER_SVYAZNOY         => [\n        self::P_CURRENCY => 'RUB',\n        'image'          => 'design/images/payments/other/svyaznoy.gif',\n      ],\n      PAYMENT_METHOD_OTHER_ROBOKASSA_MOBILE => [\n        self::P_CURRENCY => 'RUB',\n        'image'          => 'design/images/payments/other/robokassa_mobile.gif',\n        'name'           => true,\n      ],\n    ],\n\n    PAYMENT_METHOD_GENERIC => [],\n  ];\n\n//  /**\n//   * Ordered list of payment types\n//   *\n//   * @var int[] $paymentTypes\n//   */\n//  protected static $paymentTypes = [\n//    PAYMENT_METHOD_BANK_CARD     => PAYMENT_METHOD_BANK_CARD,\n//    PAYMENT_METHOD_EMONEY        => PAYMENT_METHOD_EMONEY,\n//    PAYMENT_METHOD_MOBILE        => PAYMENT_METHOD_MOBILE,\n//    PAYMENT_METHOD_BANK_INTERNET => PAYMENT_METHOD_BANK_INTERNET,\n//    PAYMENT_METHOD_BANK_TRANSFER => PAYMENT_METHOD_BANK_TRANSFER,\n//    PAYMENT_METHOD_TERMINAL      => PAYMENT_METHOD_TERMINAL,\n//    PAYMENT_METHOD_OTHER         => PAYMENT_METHOD_OTHER,\n//    PAYMENT_METHOD_GENERIC       => PAYMENT_METHOD_GENERIC,\n//  ];\n\n  protected static $methodsActive = null;\n\n  /**\n   * Add payment method to repository\n   *\n   * @param int   $type   Provider type\n   * @param int   $id     Provider ID\n   * @param array $data   Info about method [\n   *                      'currency' => (str)currencyId3Letters,\n   *                      'image'    => (str)$pathToImage,\n   *                      'name'     => (bool)$showName,\n   *                      'button'   => (bool)$showButton,\n   *                      ]\n   */\n  public static function addPaymentProvider($type, $id, array $data) {\n    PaymentMethods::$payment_methods[$type][$id] = $data;\n  }\n\n  /**\n   * @return array\n   */\n  public static function renderPaymentMethodList() {\n    // Доступные платежные методы\n    $paymentMethodList = [];\n\n    foreach (PaymentMethods::$payment_methods as $payment_type_id => $payment_methods_of_type) {\n      if (empty($payment_methods_of_type)) {\n        continue;\n      }\n\n      foreach ($payment_methods_of_type as $payment_method_id => $methodDetails) {\n        if (!self::getActiveMethods()->getModuleCount($payment_method_id)) {\n          continue;\n        }\n\n        $paymentMethodList[$payment_type_id]['.']['method'][$payment_method_id] = [\n          'ID'         => $payment_method_id,\n          'NAME'       => SN::$lang['pay_methods'][$payment_method_id],\n          'IMAGE'      => !empty($methodDetails['image']) ? $methodDetails['image'] : '',\n          'NAME_FORCE' => !empty($methodDetails['name']),\n          'BUTTON'     => !empty($methodDetails['button']),\n        ];\n        foreach (self::getActiveMethods()->getModulesOnMethod($payment_method_id) as $payment_module_name => $payment_module) {\n          $paymentMethodList[$payment_type_id]['.']['method'][$payment_method_id]['.']['module'][] = [\n            'MODULE' => $payment_module_name,\n          ];\n        }\n      }\n\n      if (!empty($paymentMethodList[$payment_type_id]['.'])) {\n        $paymentMethodList[$payment_type_id] = array_merge(\n          $paymentMethodList[$payment_type_id],\n          [\n            'ID'   => $payment_type_id,\n            'NAME' => SN::$lang['pay_methods'][$payment_type_id],\n          ]\n        );\n      }\n    }\n\n    return $paymentMethodList;\n  }\n\n  /**\n   * @param $paymentMethodId\n   * @param $player_currency\n   * @param $request\n   *\n   * @return array\n   */\n  public static function renderModulesForMethod($paymentMethodId, $player_currency, $request) {\n    $block = [];\n    foreach (self::getActiveMethods()->getModulesOnMethod($paymentMethodId) as $module_name => $module) {\n      /**\n       * @var sn_module_payment $module\n       */\n      $aPrice = $module->getPrice($paymentMethodId, $player_currency, $request['metamatter']);\n\n      $row = [\n        'ID'          => $module_name,\n        'NAME'        => SN::$lang[\"module_{$module_name}_name\"],\n        'DESCRIPTION' => SN::$lang[\"module_{$module_name}_description\"],\n      ];\n\n      if (is_array($aPrice) && !empty($aPrice)) {\n        $row['COST']     = $aPrice[$module::FIELD_SUM];\n        $row['CURRENCY'] = $aPrice[$module::FIELD_CURRENCY];\n      }\n\n      $block[] = $row;\n    }\n\n    return $block;\n  }\n\n  /**\n   * @param $moduleName\n   * @param $paymentMethodId\n   *\n   * @return string\n   */\n  public static function getCurrencyFromMethod($moduleName, $paymentMethodId) {\n    $result = '';\n\n    /**\n     * @var sn_module_payment[] $modules\n     */\n    $modules = self::getActiveMethods()->getModulesOnMethod($paymentMethodId);\n    if ($moduleName\n      && $paymentMethodId\n      && is_object($modules[$moduleName])\n      && $modules[$moduleName] instanceof sn_module_payment\n    ) {\n      $result = $modules[$moduleName]->getMethodCurrency($paymentMethodId);\n    }\n\n    return $result;\n  }\n\n\n  /**\n   * @param $paymentMethodId\n   *\n   * @return string\n   *               TODO REDO\n   */\n  public static function getDefaultCurrency($paymentMethodId) {\n    $result = '';\n\n    foreach (self::$payment_methods as $paymentTypeId => $methodList) {\n      foreach ($methodList as $methodId => $details) {\n        if ($methodId == $paymentMethodId) {\n          $result = !empty($details[self::P_CURRENCY]) ? $details[self::P_CURRENCY] : '';\n        }\n      }\n    }\n\n    return $result;\n  }\n\n\n  public static function getActiveMethods() {\n    if (empty(self::$methodsActive)) {\n      self::$methodsActive = new PaymentsMethodsActive();\n    }\n\n    return self::$methodsActive;\n  }\n\n}\n"
  },
  {
    "path": "classes/Payment/PaymentsMethodsActive.php",
    "content": "<?php\n/**\n * Created by Gorlum 22.08.2019 3:38\n */\n\nnamespace Payment;\n\n\nuse HelperArray;\nuse SN;\nuse sn_module_payment;\n\nclass PaymentsMethodsActive {\n  const P_MODULES = 'modules';\n\n  /**\n   * List of installed payment modules\n   *\n   * @var sn_module_payment[] $modulesInstalled\n   */\n  protected $modulesInstalled = [];\n\n  /**\n   * Generated list of available payment methods and corresponding modules that supports this method\n   *\n   * @var sn_module_payment[][][] $methodsAvailableV2 [\n   *                                  (int)$paymentMethodId => [\n   *                                    self::P_MODULES => [(str)$moduleName => (sn_payment_module)$module, ...]\n   *                                  ], ...\n   *                                ]\n   */\n  protected $methodsAvailableV2 = [];\n\n  public function __construct() {\n    // А теперь из каждого модуля вытаскиваем методы, которые он поддерживает\n    $paymentModuleList = SN::$gc->modules->getModulesInGroup('payment', true);\n    foreach ($paymentModuleList as $module_name => $module) {\n      /**\n       * @var sn_module_payment $module\n       */\n      if (!is_object($module) || !$module instanceof sn_module_payment || !$module->isActive()) {\n        continue;\n      }\n\n      lng_include($module_name, $module->getRootRelative());\n\n      foreach (PaymentMethods::$payment_methods as $payment_type_id => $available_methods) {\n        foreach ($available_methods as $payment_method => $payment_currency) {\n          if ($module->isMethodSupported($payment_method)) {\n            $this->registerModuleOnMethod($module, $payment_method);\n          }\n        }\n      }\n    }\n  }\n\n  /**\n   * Register module to support payment method\n   *\n   * @param sn_module_payment $module\n   * @param int               $paymentMethodId\n   */\n  public function registerModuleOnMethod($module, $paymentMethodId) {\n    $module_name = $module->getFullName();\n\n    $this->methodsAvailableV2[$paymentMethodId][self::P_MODULES][$module_name] = $module;\n\n    $this->modulesInstalled[$module_name] = $module;\n  }\n\n  /**\n   * @param $paymentMethodId\n   *\n   * @return sn_module_payment[]\n   */\n  public function getModulesOnMethod($paymentMethodId) {\n    return\n      is_array($this->methodsAvailableV2[$paymentMethodId][self::P_MODULES])\n        ? $this->methodsAvailableV2[$paymentMethodId][self::P_MODULES]\n        : [];\n  }\n\n  /**\n   * Return count of registered modules for supplied payment method\n   *\n   * @param $paymentMethodId\n   *\n   * @return int\n   */\n  public function getModuleCount($paymentMethodId) {\n    return count($this->getModulesOnMethod($paymentMethodId));\n  }\n\n  /**\n   * @param $paymentMethodId\n   *\n   * @return mixed|null\n   */\n  public function getFirstModuleNameOnMethod($paymentMethodId) {\n    return HelperArray::array_key_first($this->methodsAvailableV2[$paymentMethodId][self::P_MODULES]);\n  }\n\n  /**\n   * @param $moduleName\n   *\n   * @return bool\n   */\n  public function isModuleInstalled($moduleName) {\n    return in_array($moduleName, array_keys($this->modulesInstalled));\n  }\n\n  /**\n   * @return int\n   */\n  public function getInstalledModuleCount() {\n    return count($this->modulesInstalled);\n  }\n\n\n  /**\n   * @return mixed\n   */\n  public function getFirstInstalledModuleName() {\n    return HelperArray::array_key_first($this->modulesInstalled);\n  }\n\n\n  /**\n   * Does module supports this payment method?\n   *\n   * @param string $moduleName\n   * @param int    $paymentMethodId\n   *\n   * @return bool\n   */\n  public function isModuleSupportMethod($moduleName, $paymentMethodId) {\n    return !empty($this->methodsAvailableV2[$paymentMethodId][self::P_MODULES][$moduleName]);\n  }\n\n  /**\n   * Checks input params for validity\n   *\n   * @param string $moduleName\n   * @param int    $paymentMethodId\n   *\n   * @return array\n   */\n  public function processInputParams($moduleName, $paymentMethodId) {\n    $installedModulesCount = $this->getInstalledModuleCount();\n    if (!$installedModulesCount) {\n      // Если нет инсталлированных модулей - то как вообще мы тут оказались?\n      $moduleName      = '';\n      $paymentMethodId = 0;\n    } elseif ($installedModulesCount == 1) {\n      // Если у нас единственный модуль платежа - то только его можно и использовать\n      $moduleName = $this->getFirstInstalledModuleName();\n\n      // И если этот модуль не поддерживает выбранный метод платежа - значит метод платежа никуда не годится\n      if (!$this->isModuleSupportMethod($moduleName, $paymentMethodId)) {\n        $paymentMethodId = 0;\n      }\n\n      if(!$paymentMethodId) {\n        $module = $this->modulesInstalled[$moduleName];\n        $methodsOnModule = $module->getMethodList();\n        if(count($methodsOnModule) == 1) {\n          $paymentMethodId = HelperArray::array_key_first($methodsOnModule);\n        }\n      }\n    } elseif (!$paymentMethodId) {\n      // Если метод платежа не указан - то и модуль под него выбрать нельзя\n      $moduleName = '';\n    } else {\n      // Если у нас всего модулей больше одного и метод платежа указан - то тут становится интереснее\n\n      $modulesCount = $this->getModuleCount($paymentMethodId);\n      if (!$modulesCount) {\n        // Если метод не поддерживается ни одном модулем - значит левая фигня. Сбрасываем всё в ноль\n        $moduleName      = '';\n        $paymentMethodId = 0;\n      } elseif ($modulesCount == 1) {\n        // Если выбран метод платежа и у нас один-единственный модуль - то выбираем его в качестве активного\n        $moduleName = $this->getFirstModuleNameOnMethod($paymentMethodId);\n      } /** @noinspection PhpStatementHasEmptyBodyInspection */ else {\n        // А тут ничего делать и не надо - у нас выбран метод платежа и модуль, который его поддерживает\n      }\n    }\n\n    return [$moduleName, $paymentMethodId];\n  }\n\n}\n"
  },
  {
    "path": "classes/Planet/DBStaticPlanet.php",
    "content": "<?php /** @noinspection PhpUnnecessaryCurlyVarSyntaxInspection */\n\nnamespace Planet;\nuse SN;\n\nclass DBStaticPlanet {\n  /**\n   * @param int  $planet_id\n   * @param bool $for_update\n   *\n   * @return array|null\n   */\n  public static function db_planet_by_id($planet_id, $for_update = false) {\n    $result = SN::db_get_record_by_id(LOC_PLANET, $planet_id);\n\n    return empty($result) ? null : $result;\n  }\n\n  /**\n   * @param int $galaxy\n   * @param int $system\n   * @param int $planet\n   * @param int $planet_type\n   *\n   * @return bool|mixed\n   */\n  public static function db_planet_by_gspt_safe($galaxy, $system, $planet, $planet_type) {\n    return SN::db_get_record_list(LOC_PLANET,\n      \"{{planets}}.`galaxy` = {$galaxy} AND {{planets}}.`system` = {$system} AND {{planets}}.`planet` = {$planet} AND {{planets}}.`planet_type` = {$planet_type}\", true);\n  }\n\n  public static function db_planet_by_gspt($galaxy, $system, $planet, $planet_type) {\n    return DBStaticPlanet::db_planet_by_gspt_safe(intval($galaxy), intval($system), intval($planet), intval($planet_type));\n  }\n\n  public static function db_planet_by_vector($vector, $prefix = '') {\n    $galaxy = isset($vector[$prefix . 'galaxy']) ? intval($vector[$prefix . 'galaxy']) : 0;\n    $system = isset($vector[$prefix . 'system']) ? intval($vector[$prefix . 'system']) : 0;\n    $planet = isset($vector[$prefix . 'planet']) ? intval($vector[$prefix . 'planet']) : 0;\n    $planet_type = isset($vector[$prefix . 'planet_type']) ? intval($vector[$prefix . 'planet_type']) :\n      (isset($vector[$prefix . 'type']) ? intval($vector[$prefix . 'type']) : 0);\n    $planet_type = $planet_type == PT_DEBRIS ? PT_PLANET : $planet_type;\n\n    return DBStaticPlanet::db_planet_by_gspt_safe($galaxy, $system, $planet, $planet_type);\n  }\n\n//  /**\n//   * @param Vector $vector\n//   * @param bool   $for_update\n//   * @param string $fields\n//   *\n//   * @return array\n//   */\n//  public static function db_planet_by_vector_object($vector, $for_update = false, $fields = '*') {\n//    $planet_type = $vector->type == PT_DEBRIS ? PT_PLANET : $vector->type;\n//    $result = Planet\\DBStaticPlanet::db_planet_by_gspt_safe($vector->galaxy, $vector->system, $vector->planet, $planet_type, $for_update, $fields);\n//\n//    return !empty($result) ? $result : array();\n//  }\n\n  public static function db_planet_by_parent($parent_id, $for_update = false, $fields = '*') {\n    if (!($parent_id = idval($parent_id))) {\n      return false;\n    }\n\n    return SN::db_get_record_list(LOC_PLANET,\n      \"`parent_planet` = {$parent_id} AND `planet_type` = \" . PT_MOON, true);\n  }\n\n  public static function db_planet_by_id_and_owner($planet_id, $owner_id, $for_update = false, $fields = '*') {\n    if (!($planet_id = idval($planet_id)) || !($owner_id = idval($owner_id))) {\n      return false;\n    }\n\n    return SN::db_get_record_list(LOC_PLANET,\n      \"`id` = {$planet_id} AND `id_owner` = {$owner_id}\", true);\n  }\n\n\n  public static function db_planet_list_moon_other($user_id, $this_moon_id) {\n    if (!($user_id = idval($user_id)) || !($this_moon_id = idval($this_moon_id))) {\n      return false;\n    }\n\n    return SN::db_get_record_list(LOC_PLANET,\n      \"`planet_type` = \" . PT_MOON . \" AND `id_owner` = {$user_id} AND `id` != {$this_moon_id}\");\n  }\n\n  public static function db_planet_list_in_system($galaxy, $system) {\n    $galaxy = intval($galaxy);\n    $system = intval($system);\n\n    return SN::db_get_record_list(LOC_PLANET,\n      \"`galaxy` = {$galaxy} AND `system` = {$system}\");\n  }\n\n  public static function db_planet_list_sorted($user_row, $skip_planet_id = false, $conditions = '') {\n    if (!is_array($user_row)) {\n      return false;\n    }\n    $conditions .= $skip_planet_id ? \" AND `id` <> {$skip_planet_id} \" : '';\n\n    $sort_orders = array(\n      SORT_ID       => '{{planets}}.`id`',\n      SORT_LOCATION => '{{planets}}.`galaxy`, {{planets}}.`system`, {{planets}}.`planet`, {{planets}}.`planet_type`',\n      SORT_NAME     => '`name`',\n      SORT_SIZE     => '({{planets}}.`field_max`)',\n    );\n    $order_by = SN::$user_options[PLAYER_OPTION_PLANET_SORT];\n    if (empty($sort_orders[$order_by])) {\n      $order_by = SORT_ID;\n    }\n    $order_by = $sort_orders[$order_by] . ' ' . (SN::$user_options[PLAYER_OPTION_PLANET_SORT_INVERSE] == SORT_ASCENDING ? 'ASC' : 'DESC');\n\n    // Compilating query\n    return SN::db_get_record_list(LOC_PLANET,\n      \"`id_owner` = '{$user_row['id']}' {$conditions} ORDER BY {$order_by}\");\n  }\n\n  public static function db_planet_list_by_user_or_planet($user_id, $planet_id) {\n    if (!($user_id = idval($user_id)) && !($planet_id = idval($planet_id))) {\n      return false;\n    }\n\n    return SN::db_get_record_list(LOC_PLANET,\n      $planet_id = idval($planet_id) ? \"{{planets}}.`id` = {$planet_id}\" : \"`id_owner` = {$user_id}\", $planet_id);\n  }\n\n  public static function db_planet_set_by_id($planet_id, $set) {\n    if (!($planet_id = idval($planet_id))) {\n      return false;\n    }\n\n    return SN::db_upd_record_by_id(LOC_PLANET, $planet_id, $set);\n  }\n\n  public static function db_planet_set_by_gspt($ui_galaxy, $ui_system, $ui_planet, $set, $ui_planet_type = PT_ALL) {\n    if (!($set = trim($set))) {\n      return false;\n    }\n\n    $si_galaxy = intval($ui_galaxy);\n    $si_system = intval($ui_system);\n    $si_planet = intval($ui_planet);\n    $si_planet_type = ($si_planet_type = intval($ui_planet_type)) ? \"AND `planet_type` = {$si_planet_type}\" : '';\n\n    return SN::db_upd_record_list(LOC_PLANET, \"`galaxy` = {$si_galaxy} AND `system` = {$si_system} AND `planet` = {$si_planet} {$si_planet_type}\", $set);\n  }\n\n  public static function db_planet_set_by_parent($ui_parent_id, $ss_set) {\n    if (!($si_parent_id = idval($ui_parent_id)) || !($ss_set = trim($ss_set))) {\n      return false;\n    }\n\n    return SN::db_upd_record_list(LOC_PLANET, \"`parent_planet` = {$si_parent_id}\", $ss_set);\n  }\n\n  public static function db_planet_set_by_owner($ui_owner_id, $ss_set) {\n    if (!($si_owner_id = idval($ui_owner_id)) || !($ss_set = trim($ss_set))) {\n      return false;\n    }\n\n    return SN::db_upd_record_list(LOC_PLANET, \"`id_owner` = {$si_owner_id}\", $ss_set);\n  }\n\n\n  public static function db_planet_delete_by_id($planet_id) {\n    if (!($planet_id = idval($planet_id))) {\n      return false;\n    }\n    SN::db_del_record_by_id(LOC_PLANET, $planet_id);\n    SN::db_del_record_list(LOC_UNIT, \"`unit_location_type` = \" . LOC_PLANET . \" AND `unit_location_id` = \" . $planet_id);\n\n    // Очереди очистятся автоматически по FOREIGN KEY\n    return true;\n  }\n\n  public static function db_planet_list_delete_by_owner($ui_owner_id) {\n    if (!($si_owner_id = idval($ui_owner_id))) {\n      return false;\n    }\n    SN::db_del_record_list(LOC_PLANET, \"`id_owner` = {$si_owner_id}\");\n    SN::db_del_record_list(LOC_UNIT, \"`unit_location_type` = \" . LOC_PLANET . \" AND `unit_player_id` = \" . $si_owner_id);\n\n    // Очереди очистятся автоматически по FOREIGN KEY\n    return true;\n  }\n\n\n  public static function db_planet_count_by_type($ui_user_id, $ui_planet_type = PT_PLANET) {\n    $si_user_id = idval($ui_user_id);\n    $si_planet_type = intval($ui_planet_type);\n\n    // Лочим запись-родителя - если она есть и еще не залочена\n    $record_list = SN::db_get_record_list(LOC_PLANET, \"`id_owner` = {$si_user_id} AND `planet_type` = {$si_planet_type}\");\n\n    return is_array($record_list) ? count($record_list) : 0;\n  }\n\n  public static function db_planet_list_resources_by_owner() {\n    /** @noinspection SqlResolve */\n    return SN::$db->doquery(\"SELECT `id_owner`, sum(metal) AS metal, sum(crystal) AS crystal, sum(deuterium) AS deuterium FROM {{planets}} WHERE id_owner <> 0 /*AND id_owner is not null*/ GROUP BY id_owner;\");\n  }\n\n  public static function dbDeletePlanetsWithoutUsers() {\n    /** @noinspection SqlResolve */\n    SN::$db->doquery(\"DELETE FROM `{{planets}}` WHERE id_owner NOT IN (SELECT id FROM `{{users}}`)\");\n  }\n\n}\n"
  },
  {
    "path": "classes/Planet/Planet.php",
    "content": "<?php\n\n/**\n * Created by Gorlum 08.01.2018 14:30\n */\n\nnamespace Planet;\n\n\nuse Core\\EntityDb;\nuse DBAL\\db_mysql;\nuse Unit\\Governor;\nuse Exception;\nuse HelperString;\nuse SN;\n\n/**\n * Class Planet\n * @package Planet\n *\n * @method bool insert()\n *\n * @property int|string $id        - Record ID name would be normalized to 'id'\n * @property string     $name\n * @property int|float  $id_owner\n * @property int        $galaxy\n * @property int        $system\n * @property int        $planet\n * @property int        $planet_type\n * @property int|float  $metal\n * @property int|float  $crystal\n * @property int|float  $deuterium\n * property int|float $energy_max\n * property int|float $energy_used\n * property int $last_jump_time\n * property int $metal_perhour\n * property int $crystal_perhour\n * property int $deuterium_perhour\n * property int $metal_mine_porcent\n * property int $crystal_mine_porcent\n * property int $deuterium_sintetizer_porcent\n * property int $solar_plant_porcent\n * property int $fusion_plant_porcent\n * property int $solar_satelit_porcent\n * property int $last_update\n * property int $que_processed\n * @property string $image\n * property int|float $points\n * property int|float $ranks\n * property int $id_level\n * property int $destruyed\n * property int $diameter\n * @property int        $field_max - maximum allowed number of fields\n * property int $field_current\n * property int $temp_min\n * property int $temp_max\n * property int|float $metal_max\n * property int|float $crystal_max\n * property int|float $deuterium_max\n * property int|float $parent_planet\n * @property int|float  $debris_metal\n * @property int|float  $debris_crystal\n * @property int        $PLANET_GOVERNOR_ID\n * @property int        $PLANET_GOVERNOR_LEVEL\n * property int       $planet_teleport_next\n * property int $ship_sattelite_sloth_porcent\n * @property int        $density\n * @property int        $density_index\n * property int $position_original\n * property int $field_max_original\n * property int $temp_min_original\n * property int $temp_max_original\n */\nclass Planet extends EntityDb {\n\n  /**\n   * @var string $_activeClass\n   */\n  protected $_activeClass = '\\\\Planet\\\\RecordPlanet';\n\n  /**\n   * @var RecordPlanet $_container\n   */\n  protected $_container;\n\n  /**\n   * @var float[] $resources\n   */\n  protected $resources = [\n    RES_METAL     => 0,\n    RES_CRYSTAL   => 0,\n    RES_DEUTERIUM => 0,\n  ];\n\n  /**\n   * @var Governor $governor\n   */\n  protected $governor;\n\n  /**\n   * Planet constructor.\n   */\n  public function __construct() {\n    parent::__construct();\n  }\n\n  public function getGovernor() {\n    if (empty($this->governor)) {\n      $this->governor = new Governor();\n      $this->governor->setPlanet($this);\n    }\n\n    return $this->governor;\n  }\n\n  public function governorHire($hireId) {\n    $this->getGovernor()->hire($hireId);\n  }\n\n  /**\n   * @param string $redirect\n   *\n   * @deprecated\n   * TODO - change saveing\n   */\n  public function sn_sys_sector_buy($redirect = 'overview.php') {\n    if (!sys_get_param_str('sector_buy') || $this->planet_type != PT_PLANET) {\n      return;\n    }\n\n    db_mysql::db_transaction_start();\n    $user = db_user_by_id($this->id_owner, true);\n    $this->setForUpdate()->dbLoadRecord($this->id);\n\n    $sector_cost = eco_get_build_data($user, $this->asArray(), UNIT_SECTOR, mrc_get_level($user, $this->asArray(), UNIT_SECTOR), true);\n    $sector_cost = $sector_cost[BUILD_CREATE][RES_DARK_MATTER];\n    if ($sector_cost <= mrc_get_level($user, [], RES_DARK_MATTER)) {\n      $planet_name_text = uni_render_planet($this->asArray());\n      if (rpg_points_change($user['id'], RPG_SECTOR, -$sector_cost,\n        sprintf(\n          SN::$lang['sys_sector_purchase_log'],\n          $user['username'],\n          $user['id'],\n          $planet_name_text,\n          SN::$lang['sys_planet_type'][$this->planet_type],\n          $this->id,\n          $sector_cost\n        )\n      )) {\n        $this->field_max++;\n        $this->update();\n      } else {\n        db_mysql::db_transaction_rollback();\n      }\n    }\n    db_mysql::db_transaction_commit();\n\n    sys_redirect($redirect);\n  }\n\n  /**\n   * @param $user\n   *\n   * @return array\n   *\n   * @deprecated\n   * TODO - change saveing\n   */\n  public function sn_sys_planet_core_transmute(&$user) {\n    if (!sys_get_param_str('transmute')) {\n      return array();\n    }\n\n    try {\n      if ($this->planet_type != PT_PLANET) {\n        throw new exception(SN::$lang['ov_core_err_not_a_planet'], ERR_ERROR);\n      }\n\n      if ($this->density_index == ($new_density_index = sys_get_param_id('density_type'))) {\n        throw new exception(SN::$lang['ov_core_err_same_density'], ERR_WARNING);\n      }\n\n      db_mysql::db_transaction_start();\n      $user = db_user_by_id($user['id'], true);\n      $this->setForUpdate()->dbLoadRecord($this->id);\n\n      $planet_density_index = $this->density_index;\n      $density_price_chart = $this->planet_density_price_chart();\n\n      if (!isset($density_price_chart[$new_density_index])) {\n        // Hack attempt\n        throw new exception(SN::$lang['ov_core_err_denisty_type_wrong'], ERR_ERROR);\n      }\n\n      $user_dark_matter = mrc_get_level($user, false, RES_DARK_MATTER);\n      $transmute_cost = $density_price_chart[$new_density_index];\n      if ($user_dark_matter < $transmute_cost) {\n        throw new exception(SN::$lang['ov_core_err_no_dark_matter'], ERR_ERROR);\n      }\n\n      $sn_data_planet_density = sn_get_groups('planet_density');\n      foreach ($sn_data_planet_density as $key => $value) {\n        if ($key == $new_density_index) {\n          break;\n        }\n        $prev_density_index = $key;\n      }\n\n      $new_density = round(($sn_data_planet_density[$new_density_index][UNIT_PLANET_DENSITY] + $sn_data_planet_density[$prev_density_index][UNIT_PLANET_DENSITY]) / 2);\n\n      rpg_points_change($user['id'], RPG_PLANET_DENSITY_CHANGE, -$transmute_cost,\n        array(\n          'Planet %1$s ID %2$d at coordinates %3$s changed density type from %4$d \"%5$s\" to %6$d \"%7$s\". New density is %8$d kg/m3',\n          $this->name,\n          $this->id,\n          uni_render_coordinates($this->asArray()),\n          $planet_density_index,\n          SN::$lang['uni_planet_density_types'][$planet_density_index],\n          $new_density_index,\n          SN::$lang['uni_planet_density_types'][$new_density_index],\n          $new_density\n        )\n      );\n\n      DBStaticPlanet::db_planet_set_by_id($this->id, \"`density` = {$new_density}, `density_index` = {$new_density_index}\");\n      db_mysql::db_transaction_commit();\n\n      $this->density = $new_density;\n      $this->density_index = $new_density_index;\n      $result = array(\n        'STATUS'  => ERR_NONE,\n        'MESSAGE' => sprintf(SN::$lang['ov_core_err_none'], SN::$lang['uni_planet_density_types'][$planet_density_index], SN::$lang['uni_planet_density_types'][$new_density_index], $new_density),\n      );\n    } catch (Exception $e) {\n      db_mysql::db_transaction_rollback();\n      $result = array(\n        'STATUS'  => $e->getCode(),\n        'MESSAGE' => $e->getMessage(),\n      );\n    }\n\n    return $result;\n  }\n\n  /**\n   * @return array\n   */\n  public function planet_density_price_chart() {\n    $sn_data_density = sn_get_groups('planet_density');\n    $density_price_chart = array();\n\n    foreach ($sn_data_density as $density_id => $density_data) {\n      // Отсекаем записи с RARITY = 0 - служебные записи и супер-ядра\n      $density_data[UNIT_PLANET_DENSITY_RARITY] ? $density_price_chart[$density_id] = $density_data[UNIT_PLANET_DENSITY_RARITY] : false;\n    }\n    unset($density_price_chart[PLANET_DENSITY_NONE]);\n\n    $total_rarity = array_sum($density_price_chart);\n\n    foreach ($density_price_chart as &$density_data) {\n      $density_data = ceil($total_rarity / $density_data * $this->field_max * PLANET_DENSITY_TO_DARK_MATTER_RATE);\n    }\n\n    return $density_price_chart;\n  }\n\n  /**\n   * @param int $user_dark_matter\n   *\n   * @return array\n   */\n  public function tpl_planet_density_info($user_dark_matter) {\n    $result = [];\n\n    $density_price_chart = Planet::planet_density_price_chart();\n\n    foreach ($density_price_chart as $density_price_index => &$density_price_data) {\n      $density_cost = $density_price_data;\n      $density_price_data = array(\n        'COST'            => $density_cost,\n        'COST_TEXT'       => HelperString::numberFloorAndFormat($density_cost),\n        'COST_TEXT_CLASS' => prettyNumberGetClass($density_cost, $user_dark_matter),\n        'REST'            => $user_dark_matter - $density_cost,\n        'ID'              => $density_price_index,\n        'TEXT'            => SN::$lang['uni_planet_density_types'][$density_price_index],\n      );\n      $result[] = $density_price_data;\n    }\n\n    $planet_density_index = $this->density_index;\n\n    return [\n      '.'                    => [\n        'densities' => $result,\n      ],\n      'PLANET_DENSITY_INDEX' => $planet_density_index,\n      'PLANET_CORE_TEXT'     => \\SN::$lang['uni_planet_density_types'][$planet_density_index],\n    ];\n  }\n\n  /**\n   * @param array $user\n   *\n   * @return array\n   */\n  public function int_planet_pretemplate($user) {\n    $governor_id = $this->PLANET_GOVERNOR_ID;\n    $governor_level_plain = mrc_get_level($user, $this->asArray(), $governor_id, false, true);\n\n    return [\n      'PLANET_ID'        => $this->id,\n      'PLANET_NAME'      => htmlentities($this->name, ENT_QUOTES, 'UTF-8'),\n      'PLANET_NAME_JS'   => htmlentities(js_safe_string($this->name), ENT_QUOTES, 'UTF-8'),\n      'PLANET_GALAXY'    => $this->galaxy,\n      'PLANET_SYSTEM'    => $this->system,\n      'PLANET_PLANET'    => $this->planet,\n      'PLANET_TYPE'      => $this->planet_type,\n      'PLANET_TYPE_TEXT' => SN::$lang['sys_planet_type'][$this->planet_type],\n      'PLANET_DEBRIS'    => $this->debris_metal + $this->debris_crystal,\n      'PLANET_IMAGE'     => $this->image,\n\n      'PLANET_GOVERNOR_ID'         => $governor_id,\n      'PLANET_GOVERNOR_NAME'       => SN::$lang['tech'][$governor_id],\n      'PLANET_GOVERNOR_LEVEL'      => $governor_level_plain,\n      'PLANET_GOVERNOR_LEVEL_PLUS' => mrc_get_level($user, $this->asArray(), $governor_id, false, false) - $governor_level_plain,\n      'PLANET_GOVERNOR_LEVEL_MAX'  => get_unit_param($governor_id, P_MAX_STACK),\n    ];\n  }\n\n  public function reset() {\n    $this->governor = null;\n\n    $this->resources = [\n      RES_METAL     => 0,\n      RES_CRYSTAL   => 0,\n      RES_DEUTERIUM => 0,\n    ];\n\n    return parent::reset();\n  }\n\n  /**\n   * @return RecordPlanet\n   */\n  public function _getContainer() {\n    return $this->_container;\n  }\n\n\n  /**\n   * @param int   $resourceId\n   * @param float $resourceCount\n   *\n   * @throws \\Exception\n   */\n  public function changeResource($resourceId, $resourceCount) {\n    if (empty($resourceCount)) {\n      return;\n    }\n\n    if (!array_key_exists($resourceId, $this->resources)) {\n      throw new \\Exception(\"PLANET ERROR! Trying to change unknown resource type [{$resourceId}] '{$resourceCount}' on planet [{$this->id}]\");\n    }\n\n    $resourceCount = ceil($resourceCount);\n\n    if ($this->resources[$resourceId] + $resourceCount < 0) {\n      throw new \\Exception(\"PLANET ERROR! Trying to deduct more resources [{$resourceId}] '{$resourceCount}' when planet [{$this->id}] has only {$this->resources[$resourceId]} - deficiency \" . ($this->resources[$resourceId] + $resourceCount));\n    }\n\n    $this->resources[$resourceId] += $resourceCount;\n\n    $fieldName = pname_resource_name($resourceId);\n    $this->_getContainer()->inc()->$fieldName = $resourceCount;\n\n//    $this->metal = $this->resources[RES_METAL];\n//    $this->crystal = $this->resources[RES_CRYSTAL];\n//    $this->deuterium = $this->resources[RES_DEUTERIUM];\n  }\n\n  public function dbLoadRecord($id) {\n    $result = parent::dbLoadRecord($id);\n\n    if(!$this->isNew()) {\n      $this->resources[RES_METAL] = $this->_getContainer()->metal;\n      $this->resources[RES_CRYSTAL] = $this->_getContainer()->crystal;\n      $this->resources[RES_DEUTERIUM] = $this->_getContainer()->deuterium;\n    }\n\n    return $result;\n  }\n\n}\n"
  },
  {
    "path": "classes/Planet/RecordPlanet.php",
    "content": "<?php\n\n/**\n * Created by Gorlum 08.01.2018 14:24\n */\n\nnamespace Planet;\n\nuse DBAL\\ActiveRecord;\n\n/**\n * Class RecordPlanet\n * @package Planet\n *\n * @property string    $name\n * @property int|float $id_owner\n * @property int       $galaxy\n * @property int       $system\n * @property int       $planet\n * @property int       $planet_type\n * @property int|float $metal\n * @property int|float $crystal\n * @property int|float $deuterium\n * @property int|float $energy_max\n * @property int|float $energy_used\n * @property int       $last_jump_time\n * @property int       $metal_perhour\n * @property int       $crystal_perhour\n * @property int       $deuterium_perhour\n * @property int       $metal_mine_porcent\n * @property int       $crystal_mine_porcent\n * @property int       $deuterium_sintetizer_porcent\n * @property int       $solar_plant_porcent\n * @property int       $fusion_plant_porcent\n * @property int       $solar_satelit_porcent\n * @property int       $last_update\n * @property int       $que_processed\n * @property string    $image\n * @property int|float $points\n * @property int|float $ranks\n * @property int       $id_level\n * @property int       $destruyed\n * @property int       $diameter\n * @property int       $field_max\n * @property int       $field_current\n * @property int       $temp_min\n * @property int       $temp_max\n * @property int|float $metal_max\n * @property int|float $crystal_max\n * @property int|float $deuterium_max\n * @property int|float $parent_planet\n * @property int|float $debris_metal\n * @property int|float $debris_crystal\n * @property int       $PLANET_GOVERNOR_ID\n * @property int       $PLANET_GOVERNOR_LEVEL\n * @property int       $planet_teleport_next\n * @property int       $ship_sattelite_sloth_porcent\n * @property int       $density\n * @property int       $density_index\n * @property int       $position_original\n * @property int       $field_max_original\n * @property int       $temp_min_original\n * @property int       $temp_max_original\n *\n */\nclass RecordPlanet extends ActiveRecord {\n  protected static $_tableName = 'planets';\n\n}\n"
  },
  {
    "path": "classes/Player/PlayerLevelHelper.php",
    "content": "<?php\n/**\n * Created by Gorlum 13.12.2017 15:45\n */\n\nnamespace Player;\n\nuse Core\\GlobalContainer;\nuse Bonus\\ValueStorage;\nuse HelperString;\nuse SN;\nuse classConfig;\n\nclass PlayerLevelHelper {\n  /**\n   * @var float[] $playerLevels - [(int)level => (float)maxPointsForLevel]\n   */\n  protected $playerLevels = [];\n\n  /**\n   * @var GlobalContainer $gc\n   */\n  protected $gc;\n\n  /**\n   * @var classConfig $config\n   */\n  protected $config;\n\n  /**\n   * @var ValueStorage $valueStorage\n   */\n  protected $valueStorage;\n\n  /**\n   * PlayerLevelHelper constructor.\n   *\n   * @param GlobalContainer $gc\n   */\n  public function __construct(GlobalContainer $gc) {\n    $this->gc = $gc;\n    $this->config = $this->gc->config;\n    $this->valueStorage = $this->gc->valueStorage;\n  }\n\n  /**\n   * Get player level table\n   *\n   * @return float[]\n   */\n  public function getPlayerLevels() {\n    if ($this->isLevelExpired() || empty($this->playerLevels)) {\n      $this->loadLevels();\n    }\n\n    return $this->playerLevels;\n  }\n\n  /**\n   * Calculate level by supplied points\n   *\n   * Presumed that it's a total points but can be any others\n   *\n   * @param float $totalPoints\n   *\n   * @return int\n   */\n  public function getPointLevel($totalPoints, $authLevel = false) {\n    if ($authLevel && SN::$config->stats_hide_admins) {\n      return PLAYER_RANK_MAX;\n    }\n\n    $playerLevels = $this->getPlayerLevels();\n\n    $theLevel = null;\n    foreach ($playerLevels as $level => $points) {\n      if ($totalPoints <= $points) {\n        $theLevel = $level;\n        break;\n      }\n    }\n\n    // If no levels found - it means that points is above max calculated level\n    // We will address it tomorrow - when levels recalculates. For now just give use +1 level - would be ok for a day\n    if ($theLevel === null) {\n      end($playerLevels);\n      $theLevel = key($playerLevels) + 1;\n    }\n    if ($theLevel > PLAYER_RANK_MAX) {\n      $theLevel = PLAYER_RANK_MAX;\n    }\n\n    return $theLevel;\n  }\n\n  /**\n   * @param int  $playerRank\n   * @param bool $noTitle - do not add title to image\n   *\n   * @return string\n   */\n  public function renderRank($playerRank, $noTitle = false) {\n    $title = (empty($noTitle)\n      ? \" title='\" . HelperString::htmlEncode(\"[{$playerRank}] \" . SN::$lang['ranks'][$playerRank]) . \"'\"\n      : ''\n    );\n\n    return \"<div class='rank nick rank-{$playerRank}'{$title}></div>\";\n  }\n\n  /**\n   * @param float $playerPoints\n   * @param int   $authLevel\n   * @param bool  $noTitle - do not add title to image\n   *\n   * @return string\n   */\n  public function renderRankFromPoints($playerPoints, $authLevel = AUTH_LEVEL_REGISTERED, $noTitle = false) {\n    return $this->renderRank($this->getPointLevel($playerPoints, $authLevel), $noTitle);\n  }\n\n  /**\n   * Should level be recalculated?\n   *\n   * @return bool\n   */\n  protected function isLevelExpired() {\n    return datePart(strtotime($this->config->player_levels_calculated)) < datePart(SN_TIME_NOW);\n  }\n\n  /**\n   * Loading level data from DB\n   */\n  protected function loadLevels() {\n    if ($this->isLevelExpired()) {\n      $levelArray = [];\n    } else {\n      $levelArray = json_decode($this->config->player_levels, true);\n    }\n\n    if (empty($levelArray) || !is_array($levelArray)) {\n      $this->calcLevels();\n      $this->storeLevels();\n    } else {\n      $this->playerLevels = $levelArray;\n    }\n  }\n\n  /**\n   * Storing level data in DB\n   */\n  protected function storeLevels() {\n    $this->config->pass()->player_levels_calculated = SN_TIME_SQL;\n    $this->config->pass()->player_levels = json_encode($this->playerLevels);\n  }\n\n  /**\n   * Calculate level table\n   */\n  protected function calcLevels() {\n    $this->playerLevels = [];\n\n    $multiplier = 5; // $this->valueStorage->getValue(UNIT_SERVER_FLEET_NOOB_FACTOR);\n    !$multiplier ? $multiplier = 5 : false;\n\n    $levelPoints = $this->valueStorage->getValue(UNIT_SERVER_FLEET_NOOB_POINTS);\n    !$levelPoints ? $levelPoints = 5000 * $this->valueStorage->getValue(UNIT_SERVER_SPEED_MINING) : false;\n\n    for ($level = 0; $level <= PLAYER_RANK_MAX; $level++) {\n      $this->playerLevels[$level] = $levelPoints;\n      $levelPoints *= $multiplier;\n    }\n  }\n\n}\n"
  },
  {
    "path": "classes/Player/PlayerStatic.php",
    "content": "<?php\n/** @noinspection PhpDeprecationInspection */\n/** @noinspection SqlResolve */\n/** @noinspection PhpUnnecessaryCurlyVarSyntaxInspection */\n\n/**\n * Created by Gorlum 05.03.2018 20:42\n */\n\nnamespace Player;\n\nuse DBAL\\db_mysql;\nuse Fleet\\DbFleetStatic;\nuse mysqli_result;\nuse SN;\nuse Planet\\DBStaticPlanet;\n\n/**\n * Class PlayerStatic\n * @package Player\n *\n * @deprecated\n */\nclass PlayerStatic {\n\n  public static function getPlayerProduction($userId) {\n    /** @noinspection SqlResolve */\n    return doquery(\"       \n       SELECT\n         sum(metal_perhour) + sum(crystal_perhour) * 2 + sum(deuterium_perhour) * 4 AS `total`,\n         sum(metal_perhour)                                                         AS `metal`,\n         sum(crystal_perhour)                                                       AS `crystal`,\n         sum(deuterium_perhour)                                                     AS `deuterium`,\n         avg(metal_mine_porcent) AS `avg_metal_percent`,\n         avg(crystal_mine_porcent) AS `avg_crystal_percent`,\n         avg(deuterium_sintetizer_porcent) AS `avg_deuterium_percent`\n       FROM\n         `{{planets}}` AS p\n       WHERE\n         p.`id_owner` = {$userId}\n       GROUP BY\n         id_owner\", true);\n  }\n\n  /**\n   * @param $UserID\n   *\n   * @deprecated\n   *\n   * TODO: Full rewrite\n   */\n  public static function DeleteSelectedUser($UserID) {\n    $internalTransaction = false;\n    if (!db_mysql::db_transaction_check(false)) {\n      db_mysql::db_transaction_start();\n\n      $internalTransaction = true;\n    }\n\n    $TheUser = db_user_by_id($UserID);\n\n    if (!empty($TheUser['ally_id'])) {\n      /** @noinspection SqlResolve */\n      $TheAlly                 = doquery(\"SELECT * FROM `{{alliance}}` WHERE `id` = '\" . $TheUser['ally_id'] . \"';\", '', true);\n      $TheAlly['ally_members'] -= 1;\n      /** @noinspection SqlResolve */\n      doquery(\"UPDATE `{{alliance}}` SET `ally_members` = '\" . $TheAlly['ally_members'] . \"' WHERE `id` = '\" . $TheAlly['id'] . \"';\");\n\n//      if ( $TheAlly['ally_members'] > 0 ) {\n//        doquery ( \"UPDATE `{{alliance}}` SET `ally_members` = '\" . $TheAlly['ally_members'] . \"' WHERE `id` = '\" . $TheAlly['id'] . \"';\");\n//      } else {\n//        doquery ( \"DELETE FROM `{{alliance}}` WHERE `id` = '\" . $TheAlly['id'] . \"';\");\n//        doquery ( \"DELETE FROM `{{statpoints}}` WHERE `stat_type` = '2' AND `id_owner` = '\" . $TheAlly['id'] . \"';\");\n//      }\n    }\n\n    // Deleting all fleets\n    DbFleetStatic::db_fleet_list_delete_by_owner($UserID);\n\n    // Deleting all planets\n    DBStaticPlanet::db_planet_list_delete_by_owner($UserID);\n\n    // TEMPORARY player messages not deleted\n//    doquery(\"DELETE FROM `{{messages}}` WHERE `message_sender` = '\" . $UserID . \"';\");\n//    doquery(\"DELETE FROM `{{messages}}` WHERE `message_owner` = '\" . $UserID . \"';\");\n\n    doquery(\"DELETE FROM `{{notes}}` WHERE `owner` = '\" . $UserID . \"';\");\n\n    doquery(\"DELETE FROM `{{buddy}}` WHERE `BUDDY_SENDER_ID` = '\" . $UserID . \"';\");\n    doquery(\"DELETE FROM `{{buddy}}` WHERE `BUDDY_OWNER_ID` = '\" . $UserID . \"';\");\n\n    doquery(\"DELETE FROM `{{referrals}}` WHERE (`id` = '{$UserID}') OR (`id_partner` = '{$UserID}');\");\n\n    doquery(\"DELETE FROM `{{statpoints}}` WHERE `stat_type` = '1' AND `id_owner` = '\" . $UserID . \"';\");\n\n    // Deleting all units\n    SN::db_del_record_list(LOC_UNIT, \"`unit_location_type` = \" . LOC_PLAYER . \" AND `unit_player_id` = \" . $UserID);\n\n    // Deleting player's record\n    SN::db_del_record_by_id(LOC_USER, $UserID);\n    dbUpdateUsersCount(SN::$config->pass()->users_amount - 1);\n\n    if ($internalTransaction) {\n      db_mysql::db_transaction_commit();\n    }\n  }\n\n  public static function dbUpdateBotStatus($botType, $onlineTime = SN_TIME_NOW) {\n    SN::$db->doquery(\"UPDATE `{{users}}` SET `onlinetime` = \" . $onlineTime . \" WHERE `user_bot` = \" . $botType);\n  }\n\n  /**\n   * @param string $query\n   *\n   * @return array|bool|mysqli_result|null\n   * @deprecated\n   */\n  public static function dbSelectOne($query) {\n    $query .= ' LIMIT 1';\n    $query .= db_mysql::db_transaction_check(db_mysql::DB_TRANSACTION_WHATEVER) ? ' FOR UPDATE' : '';\n\n    return SN::$db->doquery($query, true);\n  }\n\n}\n"
  },
  {
    "path": "classes/Player/RecordPlayer.php",
    "content": "<?php\n/**\n * Created by Gorlum 13.06.2017 14:09\n */\n\nnamespace Player;\n\n\nuse DBAL\\ActiveRecord;\n\n\n/**\n * Class RecordPlayer\n *\n * @package Player\n *\n * @property $id\n * @property $username\n * @property $authlevel\n * @property $vacation\n * @property $banaday\n * @property $dark_matter\n * @property $dark_matter_total\n * @property $player_rpg_explore_xp\n * @property $player_rpg_explore_level\n * @property $ally_id\n * @property $ally_tag\n * @property $ally_name\n * @property $ally_register_time\n * @property $ally_rank_id\n * @property $lvl_minier\n * @property $xpminier\n * @property $player_rpg_tech_xp\n * @property $player_rpg_tech_level\n * @property $lvl_raid\n * @property $xpraid\n * @property $raids\n * @property $raidsloose\n * @property $raidswin\n * @property $new_message\n * @property $mnl_alliance\n * @property $mnl_joueur\n * @property $mnl_attaque\n * @property $mnl_spy\n * @property $mnl_exploit\n * @property $mnl_transport\n * @property $mnl_expedition\n * @property $mnl_buildlist\n * @property $msg_admin\n * @property $bana\n * @property $deltime\n * @property $news_lastread\n * @property $total_rank\n * @property $total_points\n * @property $password\n * @property $salt\n * @property $email\n * @property $email_2\n * @property $lang\n * @property $avatar\n * @property $sign\n * @property $id_planet\n * @property $galaxy\n * @property $system\n * @property $planet\n * @property $current_planet\n * @property $user_lastip\n * @property $user_last_proxy\n * @property $user_last_browser_id\n * @property $register_time\n * @property $onlinetime\n * @property $que_processed\n * @property $template\n * @property $skin\n * @property $design\n * @property $noipcheck\n * @property $options\n * @property $user_as_ally\n * @property $metal\n * @property $crystal\n * @property $deuterium\n * @property $user_birthday\n * @property $user_birthday_celebrated\n * @property $player_race\n * @property $vacation_next\n * @property $metamatter\n * @property $metamatter_total\n * @property $admin_protection\n * @property $user_bot\n * @property $gender\n * @property $immortal\n * @property $parent_account_id\n * @property $parent_account_global\n * @property $server_name\n *\n */\nclass RecordPlayer extends ActiveRecord {\n  protected static $_tableName = 'users';\n\n}\n"
  },
  {
    "path": "classes/Player/playerTimeDiff.php",
    "content": "<?php\n\nnamespace Player;\n/**\n * User: Gorlum\n * Date: 14.10.2015\n * Time: 0:28\n */\nclass playerTimeDiff {\n  const TIME_DIFF = 0; // Чистая разница в ходе часов в секундах\n  const TIME_DIFF_UTC_OFFSET = 1; // Разница между часовыми поясами в секундах\n  const TIME_DIFF_FORCED = 2;\n  const TIME_DIFF_MEASURE_TIME = 3; // Время когда происходил замер\n\n  /**\n   * Разница в ходе часов в секундах. Т.е. разница между GMT-временами браузера и сервера\n   * @var int\n   */\n  public $gmt_diff = 0;\n  /**\n   * Разница в секундах между часовыми поясами браузера и сервера\n   * @var int\n   */\n  public $zone_offset = 0;\n  /**\n   * Форсированный пересчёт времени\n   *\n   * @var int\n   */\n  public $force_measure = 0;\n  /**\n   * Метка времени прошлой синхронизации часов\n   *\n   * @var string\n   */\n  public $last_measure_time = '2000-01-01';\n\n\n  static protected function user_time_diff_probe() {\n    // Определяем время в браузере\n    $client_time = strtotime(sys_get_param('client_gmt')); // Попытка определить по GMT-времени браузера. В нём будет часовой пояс (GMT), поэтому время будет автоматически преобразовано в часовой пояс сервера\n    !$client_time ? $client_time = round(sys_get_param_float('timeBrowser') / 1000) : false; // Попытка определить по Date.valueOf() - миллисекунды с начала эпохи UNIX_TIME\n    !$client_time ? $client_time = SN_TIME_NOW : false; // Если все попытки провалились - тупо берем время сервера\n\n    $result = array(\n      self::TIME_DIFF              => $client_time - SN_TIME_NOW,\n      self::TIME_DIFF_UTC_OFFSET   => ($browser_utc_offset = sys_get_param_int('utc_offset')) ? $browser_utc_offset - date('Z') : 0,\n      self::TIME_DIFF_FORCED       => sys_get_param_int('PLAYER_OPTION_TIME_DIFF_FORCED'),\n      self::TIME_DIFF_MEASURE_TIME => SN_TIME_SQL,\n    );\n\n    return $result;\n  }\n\n  static protected function user_time_diff_set($user_time_diff) {\n    // Переопределяем массив, что бы элементы были в правильном порядке\n    !is_array($user_time_diff) ? $user_time_diff = [] : false;\n    $user_time_diff = self::sortDiffArray($user_time_diff);\n    $user_time_diff[self::TIME_DIFF_MEASURE_TIME] = SN_TIME_SQL;\n\n    $user_time_diff_str = implode(';', $user_time_diff);\n    sn_setcookie(SN_COOKIE_T, $user_time_diff_str, SN_TIME_NOW + PERIOD_MONTH);\n  }\n\n  static protected function user_time_diff_get() {\n    $user_time_diff = !empty($_COOKIE[SN_COOKIE_T]) ? explode(';', $_COOKIE[SN_COOKIE_T]) : null;\n    !is_array($user_time_diff) ? $user_time_diff = [] : false;\n    $user_time_diff = self::sortDiffArray($user_time_diff);\n\n    return $user_time_diff;\n  }\n\n  static public function sn_options_timediff($timeDiff, $force, $clear) {\n    $user_time_diff = playerTimeDiff::user_time_diff_get();\n    if ($force) {\n      playerTimeDiff::user_time_diff_set(array(\n        self::TIME_DIFF              => $timeDiff,\n        self::TIME_DIFF_UTC_OFFSET   => 0,\n        self::TIME_DIFF_FORCED       => 1,\n        self::TIME_DIFF_MEASURE_TIME => SN_TIME_SQL,\n      ));\n    } elseif ($clear || $user_time_diff[self::TIME_DIFF_FORCED]) {\n      playerTimeDiff::user_time_diff_set(array(\n        self::TIME_DIFF              => '',\n        self::TIME_DIFF_UTC_OFFSET   => 0,\n        self::TIME_DIFF_FORCED       => 0,\n        self::TIME_DIFF_MEASURE_TIME => SN_TIME_SQL,\n      ));\n    }\n  }\n\n  /**\n   * @return int\n   */\n  static public function timeProbeAjax() {\n    $userTimeDiff = playerTimeDiff::user_time_diff_get();\n    if ($userTimeDiff[self::TIME_DIFF_FORCED]) {\n      $time_diff = intval($userTimeDiff[self::TIME_DIFF]);\n    } else {\n      $userTimeDiff = playerTimeDiff::user_time_diff_probe();\n      playerTimeDiff::user_time_diff_set($userTimeDiff);\n      $time_diff = $userTimeDiff[self::TIME_DIFF] + $userTimeDiff[self::TIME_DIFF_UTC_OFFSET];\n    }\n\n    return $time_diff;\n  }\n\n  /**\n   * @return mixed\n   */\n  static public function defineTimeDiff() {\n    $user_time_diff = playerTimeDiff::user_time_diff_get();\n\n    $time_diff = (float)$user_time_diff[self::TIME_DIFF] + $user_time_diff[self::TIME_DIFF_UTC_OFFSET];\n\n    define('SN_CLIENT_TIME_DIFF', $time_diff);\n    define('SN_CLIENT_TIME_LOCAL', SN_TIME_NOW + SN_CLIENT_TIME_DIFF);\n    define('SN_CLIENT_TIME_DIFF_GMT', $user_time_diff[self::TIME_DIFF]);\n\n    return $time_diff; // Разница в GMT-времени между клиентом и сервером. Реальная разница в ходе часов\n  }\n\n  /**\n   * @return int\n   */\n  static public function timeDiffTemplate() {\n    $user_time_diff          = playerTimeDiff::user_time_diff_get();\n    $user_time_measured_unix = intval(isset($user_time_diff[self::TIME_DIFF_MEASURE_TIME]) ? strtotime($user_time_diff[self::TIME_DIFF_MEASURE_TIME]) : 0);\n    $measureTimeDiff         = intval(\n      empty($user_time_diff[self::TIME_DIFF_FORCED])\n      &&\n      (SN_TIME_NOW - $user_time_measured_unix > PERIOD_HOUR || $user_time_diff[self::TIME_DIFF] == '')\n    );\n\n    return $measureTimeDiff;\n  }\n\n  /**\n   * @return int\n   */\n  static public function getTimeDiffForced() {\n    $user_time_diff        = playerTimeDiff::user_time_diff_get();\n    $user_time_diff_forced = $user_time_diff[self::TIME_DIFF_FORCED];\n\n    return $user_time_diff_forced;\n  }\n\n  /**\n   * @param array $user_time_diff\n   *\n   * @return array\n   */\n  protected static function sortDiffArray(array $user_time_diff) {\n    $user_time_diff = [\n      self::TIME_DIFF              => isset($user_time_diff[self::TIME_DIFF]) ? $user_time_diff[self::TIME_DIFF] : '',\n      self::TIME_DIFF_UTC_OFFSET   => isset($user_time_diff[self::TIME_DIFF_UTC_OFFSET]) ? $user_time_diff[self::TIME_DIFF_UTC_OFFSET] : 0,\n      self::TIME_DIFF_FORCED       => isset($user_time_diff[self::TIME_DIFF_FORCED]) ? $user_time_diff[self::TIME_DIFF_FORCED] : 0,\n      self::TIME_DIFF_MEASURE_TIME => isset($user_time_diff[self::TIME_DIFF_MEASURE_TIME]) ? $user_time_diff[self::TIME_DIFF_MEASURE_TIME] : '2000-01-01',\n    ];\n\n    return $user_time_diff;\n  }\n\n}\n"
  },
  {
    "path": "classes/Player/userOptions.php",
    "content": "<?php\n\nnamespace Player;\n\nuse Common\\oldArrayAccessNd;\nuse SN;\n\n/**\n * Class Player\\userOptions\n *\n * Загрузка и сохранение настроек пользователя в БД\n *\n * Унаследованная поддержка многомерных индексов\n * Многоуровневое кэширование: память PHP -> память системы (xCache) -> БД\n * isset() работает на кэше в памяти PHP и не проверяет дальше\n * Поддержка удаления записей из БД и кэша через unset()\n * Поддержка отложенной записи\n *\n */\nclass userOptions extends oldArrayAccessNd {\n  protected $user_id = 0;\n  protected $loaded = false;\n  protected $defaults = array(\n    PLAYER_OPTION_MENU_SORT             => '',\n    PLAYER_OPTION_MENU_HIDE_SHOW_BUTTON => PLAYER_OPTION_MENU_HIDE_SHOW_BUTTON_FIXED,\n    PLAYER_OPTION_MENU_SHOW_ON_BUTTON   => 0,\n    PLAYER_OPTION_MENU_HIDE_ON_BUTTON   => 0,\n    PLAYER_OPTION_MENU_HIDE_ON_LEAVE    => 0,\n    PLAYER_OPTION_MENU_UNPIN_ABSOLUTE   => 0,\n    PLAYER_OPTION_MENU_ITEMS_AS_BUTTONS => 0,\n    PLAYER_OPTION_MENU_WHITE_TEXT       => 0,\n    PLAYER_OPTION_MENU_OLD              => 0,\n\n    PLAYER_OPTION_SOUND_ENABLED => 0,\n\n    PLAYER_OPTION_FLEET_SHIP_SORT         => PLAYER_OPTION_SORT_DEFAULT,\n    PLAYER_OPTION_FLEET_SHIP_SORT_INVERSE => PLAYER_OPTION_SORT_ORDER_PLAIN,\n\n    PLAYER_OPTION_CURRENCY_DEFAULT => 'RUR',\n\n    PLAYER_OPTION_FLEET_SPY_DEFAULT => 1,\n    // PLAYER_OPTION_FLEET_MESS_AMOUNT_MAX => 99,\n\n    PLAYER_OPTION_UNIVERSE_ICON_SPYING      => 1,\n    PLAYER_OPTION_UNIVERSE_ICON_MISSILE     => 1,\n    PLAYER_OPTION_UNIVERSE_ICON_PM          => 1,\n    PLAYER_OPTION_UNIVERSE_ICON_STATS       => 1,\n    PLAYER_OPTION_UNIVERSE_ICON_PROFILE     => 1,\n    PLAYER_OPTION_UNIVERSE_ICON_BUDDY       => 1,\n    PLAYER_OPTION_UNIVERSE_OLD              => 0,\n    PLAYER_OPTION_UNIVERSE_DISABLE_COLONIZE => 0,\n\n    PLAYER_OPTION_PLANET_SORT         => 0,\n    PLAYER_OPTION_PLANET_SORT_INVERSE => 0,\n    PLAYER_OPTION_TOOLTIP_DELAY       => 500,\n\n    PLAYER_OPTION_BASE_FONT_SIZE => FONT_SIZE_PERCENT_DEFAULT_STRING,\n\n    PLAYER_OPTION_BUILD_AUTOCONVERT_HIDE => 0,\n\n    PLAYER_OPTION_NAVBAR_PLANET_VERTICAL        => 0,\n    PLAYER_OPTION_NAVBAR_RESEARCH_WIDE          => 0,\n    PLAYER_OPTION_NAVBAR_DISABLE_EXPEDITIONS    => 0,\n    PLAYER_OPTION_NAVBAR_DISABLE_FLYING_FLEETS  => 0,\n    PLAYER_OPTION_NAVBAR_DISABLE_RESEARCH       => 0,\n    PLAYER_OPTION_NAVBAR_DISABLE_PLANET         => 0,\n    PLAYER_OPTION_NAVBAR_DISABLE_HANGAR         => 0,\n    PLAYER_OPTION_NAVBAR_DISABLE_DEFENSE        => 0,\n    PLAYER_OPTION_NAVBAR_DISABLE_QUESTS         => 0,\n    PLAYER_OPTION_NAVBAR_DISABLE_META_MATTER    => 0,\n    PLAYER_OPTION_NAVBAR_PLANET_OLD             => 0,\n    PLAYER_OPTION_NAVBAR_PLANET_DISABLE_STORAGE => 0,\n\n    PLAYER_OPTION_BUILDING_SORT         => array(\n      QUE_RESEARCH   => PLAYER_OPTION_SORT_DEFAULT,\n      QUE_STRUCTURES => PLAYER_OPTION_SORT_DEFAULT,\n      SUBQUE_FLEET   => PLAYER_OPTION_SORT_DEFAULT,\n      SUBQUE_DEFENSE => PLAYER_OPTION_SORT_DEFAULT,\n    ),\n    PLAYER_OPTION_BUILDING_SORT_INVERSE => array(\n      QUE_RESEARCH   => PLAYER_OPTION_SORT_ORDER_PLAIN,\n      QUE_STRUCTURES => PLAYER_OPTION_SORT_ORDER_PLAIN,\n      SUBQUE_FLEET   => PLAYER_OPTION_SORT_ORDER_PLAIN,\n      SUBQUE_DEFENSE => PLAYER_OPTION_SORT_ORDER_PLAIN,\n    ),\n\n    PLAYER_OPTION_ANIMATION_DISABLED     => 0,\n    PLAYER_OPTION_DESIGN_DISABLE_BORDERS => 0,\n    PLAYER_OPTION_TECH_TREE_TABLE        => 0,\n\n    PLAYER_OPTION_PROGRESS_BARS_DISABLED => 0,\n\n    PLAYER_OPTION_FLEET_SHIP_SELECT_OLD       => 0,\n    PLAYER_OPTION_FLEET_SHIP_HIDE_SPEED       => 0,\n    PLAYER_OPTION_FLEET_SHIP_HIDE_CAPACITY    => 0,\n    PLAYER_OPTION_FLEET_SHIP_HIDE_CONSUMPTION => 0,\n\n    PLAYER_OPTION_TUTORIAL_DISABLED => 1,\n    PLAYER_OPTION_TUTORIAL_WINDOWED => 0,\n    PLAYER_OPTION_TUTORIAL_CURRENT  => 1,\n    PLAYER_OPTION_TUTORIAL_FINISHED => 0,\n\n    PLAYER_OPTION_QUEST_LIST_FILTER => QUEST_STATUS_ALL,\n\n    PLAYER_OPTION_LOGIN_REWARDED_LAST       => SN_DATE_PREHISTORIC_SQL,\n    PLAYER_OPTION_LOGIN_REWARD_STREAK_BEGAN => SN_DATE_PREHISTORIC_SQL,\n  );\n\n  public $data = array(); // Container // TODO - make protected\n\n  public $to_write = array(); // TODO - make protected\n  public $to_delete = array(); // TODO - make protected\n\n  public function __get($offset) {\n    // TODO: Implement __get() method.\n    return $this->__isset($offset) ? $this->data[$offset] : null;\n  }\n\n  public function __set($offset, $value = null) {\n//    if(!$this->__isset($offset) || $this->__get($offset) != $value)\n    {\n      $this->data[$offset] = $value; // Сразу записываем данные во внутренний кэш\n      $this->to_write[$offset] = 1; // Индекс измененного элемента для работы подсистемы отложенной записи\n    }\n  }\n\n  public function __isset($offset) {\n    // TODO: Implement __isset() method.\n    return isset($this->data[$offset]);\n  }\n\n  public function __unset($offset) {\n    // TODO: Implement __unset() method.\n    unset($this->data[$offset]);\n    $this->to_delete[$offset] = 1;\n  }\n\n  public function __flush() {\n    // TODO Implement\n\n    $update_cache = false;\n\n    if (!empty($this->to_write)) {\n      foreach ($this->to_write as $key => $cork) {\n        $value = is_array($this->data[$key]) ? serialize($this->data[$key]) : $this->data[$key]; // Сериализация для массивов при сохранении в БД\n        $this->to_write[$key] = \"({$this->user_id}, '\" . SN::$db->db_escape($key) . \"', '\" . SN::$db->db_escape($value) . \"')\";\n      }\n\n      doquery(\"REPLACE INTO `{{player_options}}` (`player_id`, `option_id`, `value`) VALUES \" . implode(',', $this->to_write));\n\n      $this->to_write = array();\n      $update_cache = true;\n    }\n\n    if (!empty($this->to_delete)) {\n      foreach ($this->to_delete as $key => &$value) {\n        $value = is_string($key) ? \"'\" . SN::$db->db_escape($key) . \"'\" : $key;\n      }\n\n      doquery(\"DELETE FROM `{{player_options}}` WHERE `player_id` = {$this->user_id} AND `option_id` IN (\" . implode(',', $this->to_delete) . \") \");\n\n      $this->to_delete = array();\n      $update_cache = true;\n    }\n\n    if ($update_cache) {\n      global $sn_cache;\n\n      $field_name = $this->cached_name();\n      $sn_cache->$field_name = $this->data;\n    }\n\n    return true;\n  }\n\n\n  public function __construct($user_id) {\n    $this->user_change($user_id);\n  }\n\n  public function user_change($user_id, $forceLoad = false) {\n    $this->loaded = false;\n    $this->user_id = round(floatval($user_id));\n    $this->load($forceLoad);\n  }\n\n  protected function cached_name() {\n    return 'options_' . $this->user_id;\n  }\n\n  protected function load($forceLoad = false) {\n    global $sn_cache;\n\n    if ($this->loaded) {\n      return;\n    }\n\n    $this->data = $this->defaults;\n    $this->to_write = array();\n    $this->to_delete = array();\n\n    if (!$this->user_id) {\n      $this->loaded = true;\n\n      return;\n    }\n\n    $field_name = $this->cached_name();\n    if (!$forceLoad) {\n      $a_data = $sn_cache->$field_name;\n\n      if (!empty($a_data)) {\n        $this->data = array_replace_recursive($this->data, $a_data);\n\n        return;\n      }\n    }\n\n    $query = doquery(\"SELECT * FROM `{{player_options}}` WHERE `player_id` = {$this->user_id} FOR UPDATE\");\n    while ($row = db_fetch($query)) {\n      // $this->data[$row['option_id']] = $row['value'];\n      $this->data[$row['option_id']] = is_string($row['value']) && ($temp = unserialize($row['value'])) !== false ? $temp : $row['value']; // Десериализация\n    }\n    $sn_cache->$field_name = $this->data;\n  }\n\n}\n"
  },
  {
    "path": "classes/PlayerToAccountTranslate.php",
    "content": "<?php\n\n/**\n * User: Gorlum\n * Date: 18.09.2015\n * Time: 11:36\n */\n\nuse DBAL\\db_mysql;\n\n/**\n * Class PlayerToAccountTranslate\n */\nclass PlayerToAccountTranslate {\n  /**\n   * БД из которой читать данные\n   *\n   * @var db_mysql $db\n   */\n  protected static $db = null;\n  protected static $is_init = false;\n\n  protected static function init() {\n    if(!empty(static::$db)) {\n      return;\n    }\n    static::$db = SN::$db;\n  }\n\n  /**\n   * Регистрирует игрока на указанный аккаунт указанного провайдера\n   *\n   * @param $provider_id_safe\n   * @param $provider_account_id_safe\n   * @param $user_id_safe\n   *\n   * @return array|resource\n   */\n  // OK v4.7\n  public static function db_translate_register_user($provider_id_unsafe, $provider_account_id_unsafe, $user_id_unsafe) {\n    static::init();\n\n    $provider_id_safe = static::$db->db_escape($provider_id_unsafe);\n    $provider_account_id_safe = static::$db->db_escape($provider_account_id_unsafe);\n    $user_id_safe = static::$db->db_escape($user_id_unsafe);\n\n    return static::$db->doquery(\n      \"INSERT INTO `{{account_translate}}` (`provider_id`, `provider_account_id`, `user_id`) VALUES\n                  ({$provider_id_safe}, {$provider_account_id_safe}, {$user_id_safe});\"\n    );\n  }\n\n  /**\n   * Возвращает из `account_translate` список пользователей, которые прилинкованы к списку аккаунтов на указанном провайдере\n   * @version 4.5\n   *\n   * @param int $provider_id_unsafe Идентификатор провайдера авторизации\n   * @param int|int[] $account_list\n   *\n   * @return array\n   */\n  // OK v4.7\n  public static function db_translate_get_users_from_account_list($provider_id_unsafe, $account_list) {\n    static::init();\n\n    $account_translation = array();\n\n    $provider_id_safe = intval($provider_id_unsafe);\n    !is_array($account_list) ? $account_list = array($account_list) : false;\n\n    foreach($account_list as $provider_account_id_unsafe) {\n      $provider_account_id_safe = intval($provider_account_id_unsafe);\n\n      // TODO - Здесь могут отсутствовать аккаунты - проверять провайдером\n      $query = static::$db->doquery(\n        \"SELECT `user_id` FROM {{account_translate}} WHERE `provider_id` = {$provider_id_safe} AND `provider_account_id` = {$provider_account_id_safe} FOR UPDATE\"\n      );\n      while($row = static::$db->db_fetch($query)) {\n        $account_translation[$row['user_id']][$provider_id_unsafe][$provider_account_id_unsafe] = true;\n      }\n    }\n\n    return $account_translation;\n  }\n\n  /**\n   * @param int|string $user_id_unsafe\n   * @param int        $provider_id_unsafe\n   *\n   * @return array - array[playerId][providerId][providerAccountId]\n   */\n  public static function db_translate_get_account_by_user_id($user_id_unsafe, $provider_id_unsafe = 0) {\n    static::init();\n\n    $user_id_safe = static::$db->db_escape($user_id_unsafe);\n    $provider_id_safe = static::$db->db_escape($provider_id_unsafe);\n\n    $account_translation = array();\n\n    $query = static::$db->doquery(\n      \"SELECT * FROM {{account_translate}} WHERE `user_id` = {$user_id_safe} \" .\n      ($provider_id_unsafe ? \"AND `provider_id` = {$provider_id_safe} \" : '') .\n      \"ORDER BY `timestamp` FOR UPDATE\"\n    );\n    while($row = static::$db->db_fetch($query)) {\n      $account_translation[$row['user_id']][$row['provider_id']][$row['provider_account_id']] = $row;\n    }\n\n    return $account_translation;\n  }\n\n  public static function db_translate_unregister_user($user_id_unsafe) {\n    static::init();\n\n    $user_id_safe = static::$db->db_escape($user_id_unsafe);\n    return static::$db->doquery(\n      \"DELETE FROM `{{account_translate}}` WHERE `user_id` = {$user_id_safe});\"\n    );\n  }\n\n}\n"
  },
  {
    "path": "classes/Pm/DecodeEspionage.php",
    "content": "<?php\n/**\n * Created by Gorlum 12.10.2017 15:54\n */\n\nnamespace Pm;\n\nuse \\Fleet\\MissionEspionageReport;\nuse \\HelperString;\nuse SnTemplate;\n\nclass DecodeEspionage {\n  const ALLOWED_UNITS = [UNIT_RESOURCES, UNIT_SHIPS, UNIT_DEFENCE, UNIT_STRUCTURES, UNIT_TECHNOLOGIES];\n\n  /**\n   * @param MissionEspionageReport $missionReport\n   */\n  public static function decode($missionReport) {\n    $lang = \\SN::$lang;\n    $general = \\SN::$gc->general;\n\n    $template = SnTemplate::gettemplate('msg_message_spy');\n\n    $groups = [];\n    foreach ($missionReport->spiedUnits as $unitId => $unitAmount) {\n      $groups[get_unit_param($unitId, P_UNIT_TYPE)][$unitId] = $unitAmount;\n    }\n\n    foreach(static::ALLOWED_UNITS as $groupId) {\n      if(empty($groups[$groupId])) {\n        continue;\n      }\n\n      $template->assign_block_vars('group', [\n        'ID' => $groupId,\n        'NAME' => $lang['tech'][$groupId],\n      ]);\n\n      foreach($general->getGroupsById($groupId) as $unitId) {\n        if((!isset($groups[$groupId][$unitId]) || floor($groups[$groupId][$unitId]) < 1) && $unitId != RES_ENERGY) {\n          continue;\n        }\n\n        $template->assign_block_vars('group.unit', [\n          'ID' => $unitId,\n          'NAME' => $lang['tech'][$unitId],\n          'AMOUNT' => HelperString::numberFloorAndFormat($groups[$groupId][$unitId]),\n        ]);\n      }\n    }\n\n    $template->assign_vars([\n      'REPORT_TIME' => date(FMT_DATE_TIME, $missionReport->fleetTime),\n\n      'TARGET_PLAYER_ID' => $missionReport->targetPlayerId,\n      'TARGET_PLAYER_NAME' => $missionReport->targetPlayerName,\n      'TARGET_PLAYER_ALLY_TAG' => $missionReport->targetPlayerAllyTag,\n\n      'TARGET_PLANET_NAME' => $missionReport->targetPlanetName,\n      'TARGET_PLANET_GALAXY' => $missionReport->targetPlanetGalaxy,\n      'TARGET_PLANET_SYSTEM' => $missionReport->targetPlanetSystem,\n      'TARGET_PLANET_PLANET' => $missionReport->targetPlanetPlanet,\n      'TARGET_PLANET_TYPE' => $missionReport->targetPlanetPlanetType,\n      'TARGET_PLANET_TYPE_TEXT_SH' => $lang['sys_planet_type_sh'][$missionReport->targetPlanetPlanetType],\n\n      'SPIES_DETECTION_CHANCE' => round($missionReport->getDetectionTrashold()),\n      'SPIES_DESTROYED' => $missionReport->isSpyDetected(),\n\n      'SIMULATOR_DATA' => $missionReport->getSimulatorLink(),\n    ]);\n\n    return $template->assign_display('msg_message_spy');\n  }\n\n}\n"
  },
  {
    "path": "classes/Pm/PlayerIgnore.php",
    "content": "<?php\n/** @noinspection PhpDeprecationInspection */\n/** @noinspection SqlResolve */\n\n/**\n * Created by Gorlum 14.08.2019 1:37\n */\n\nnamespace Pm;\n\nuse Core\\GlobalContainer;\nuse HelperString;\n\nclass PlayerIgnore {\n  const IGNORE_PM = 1;\n\n  /**\n   * @var bool[][][] $ignores [$playerId][$ignoredId][$subsystem] = {false|true}\n   */\n  protected $ignores = [];\n\n  public function __construct(GlobalContainer $gc) {\n  }\n\n  public function ignore($playerId, $ignoredId, $subsystem = self::IGNORE_PM) {\n    if (!$this->isIgnored($playerId, $ignoredId, $subsystem)) {\n      doquery(\"REPLACE INTO `{{player_ignore}}` SET `player_id` = {$playerId}, `ignored_id` = {$ignoredId}, `subsystem` = {$subsystem}\");\n\n      $this->ignores[$playerId][$ignoredId][$subsystem] = true;\n    }\n  }\n\n  public function unIgnore($playerId, $ignoredId, $subsystem = self::IGNORE_PM) {\n    if ($this->isIgnored($playerId, $ignoredId, $subsystem)) {\n      doquery(\"DELETE FROM `{{player_ignore}}` WHERE `player_id` = {$playerId} AND `ignored_id` = {$ignoredId} AND `subsystem` = {$subsystem}\");\n\n      $this->ignores[$playerId][$ignoredId][$subsystem] = false;\n    }\n  }\n\n  public function isIgnored($playerId, $ignoredId, $subsystem = self::IGNORE_PM) {\n    if (!isset($this->ignores[$playerId][$ignoredId][$subsystem])) {\n      $ignored = doquery(\"SELECT * FROM `{{player_ignore}}` WHERE `player_id` = {$playerId} AND `ignored_id` = {$ignoredId} AND `subsystem` = {$subsystem}\", true);\n\n      $this->ignores[$playerId][$ignoredId][$subsystem] = !empty($ignored);\n    }\n\n    return $this->ignores[$playerId][$ignoredId][$subsystem];\n  }\n\n  public function getIgnores($playerId, $htmlEncode = true) {\n    $result = [];\n\n    $ignores = doquery(\n      \"SELECT pi.*, u.username\n        FROM `{{player_ignore}}` AS pi\n            LEFT JOIN `{{users}}` AS u ON u.id = pi.ignored_id\n        WHERE `player_id` = {$playerId} \n        ORDER BY `player_id`, `ignored_id`,`subsystem`\"\n    );\n    while ($row = db_fetch($ignores)) {\n      $name = $htmlEncode ? HelperString::htmlEncode($row['username']) : $row['username'];\n\n      $result[] = [\n        'ID'   => $row['ignored_id'],\n        'NAME' => $name,\n      ];\n    }\n\n    return $result;\n  }\n\n}\n"
  },
  {
    "path": "classes/Pm/RecordPlayerIgnore.php",
    "content": "<?php\n/**\n * Created by Gorlum 14.08.2019 1:39\n */\n\nnamespace Pm;\n\n\nuse DBAL\\RecordV2;\nuse DBAL\\StorageSqlV2;\n\n/**\n * Class RecordPlayerIgnore\n *\n * @property int|string $playerId  - Player ID which ignores other player\n * @property int|string $ignoredId - Ignored player ID\n * @property int        $pm        - Flag to ignore Personal Messages\n *\n *\n * @package Pm\n */\nclass RecordPlayerIgnore extends RecordV2 {\n  /**\n   * @var StorageSqlV2|null $storage\n   */\n  // TODO - replace with IStorage\n  protected static $storage = null;\n\n  protected static $_tableName = 'player_ignore';\n\n  protected static $_fieldsToProperties = [\n    'player_id'  => 'playerId',\n    'ignored_id' => 'ignoredId',\n  ];\n\n\n}\n"
  },
  {
    "path": "classes/Ptl/PtlVariableDecorator.php",
    "content": "<?php\n/**\n * Created by Gorlum 05.02.2018 12:31\n */\n\nnamespace Ptl;\n\nuse PTLTag;\nuse \\template;\n\nclass PtlVariableDecorator {\n  // Numeric decorators\n  const PARAM_NUMERIC = 'num'; // define numeric decorator\n  const PARAM_NUMERIC_FLOOR = 'floor';\n  const PARAM_NUMERIC_CEIL = 'ceil';\n  const PARAM_NUMERIC_ROUND = 'round'; // round=<decimal numbers>\n\n  const PARAM_NUMERIC_FORMAT = 'format';\n  const PARAM_NUMERIC_MONEY = 'money';\n  const PARAM_NUMERIC_COLOR = 'color'; // Color values: red - negative, yellow - zero, green - positive. Implies \"floor\" and \"format\"\n//  const PARAM_NUMERIC_LIMIT = 'percent'; // _number_color_value replacement - see Decorators in PTL test\n//  const PARAM_NUMERIC_LIMIT = 'limit';\n\n  const PARAM_DATETIME = 'datetime'; // define date and/or time decorator\n\n  /**\n   * Сортированный список поддерживаемых параметров\n   *\n   * @var string[] $allowedParams\n   */\n  protected static $allowedParams = array(\n    self::PARAM_NUMERIC       => '',\n    // Will be dumped for all tags which does not have |num\n    self::PARAM_NUMERIC_CEIL  => self::PARAM_NUMERIC,\n    self::PARAM_NUMERIC_FLOOR => self::PARAM_NUMERIC,\n    self::PARAM_NUMERIC_ROUND => self::PARAM_NUMERIC,\n\n    self::PARAM_NUMERIC_FORMAT => self::PARAM_NUMERIC,\n    self::PARAM_NUMERIC_MONEY  => self::PARAM_NUMERIC,\n    self::PARAM_NUMERIC_COLOR  => self::PARAM_NUMERIC,\n//    self::PARAM_NUMERIC_LIMIT  => self::PARAM_NUMERIC,\n\n    self::PARAM_DATETIME       => self::PARAM_DATETIME,\n  );\n\n  /**\n   * @param string   $strTagFull - full PTL tag with enclosing curly braces\n   * @param string   $phpCompiledVar - compiled var reference ready for ECHO command\n   * @param template $template - template to apply\n   *\n   * @return mixed\n   */\n  public static function decorate($strTagFull, $phpCompiledVar, $template) {\n    $ptlTag = new PTLTag(substr($strTagFull, 1, strlen($strTagFull) - 2), $template, static::$allowedParams);\n\n    $phpCompiledVar = static::num($phpCompiledVar, $ptlTag);\n    $phpCompiledVar = static::datetime($phpCompiledVar, $ptlTag);\n\n    return $phpCompiledVar;\n  }\n\n  /**\n   * Return function call\n   *\n   * @param string   $funcName\n   * @param string   $value\n   * @param string[] $params\n   *\n   * @return string\n   */\n  protected static function func($funcName, $value, $params = []) {\n    return $funcName . '(' . $value . (!empty($params) ? ',' . implode(',', $params) : '') . ')';\n  }\n\n  protected static function func2($funcName, $params = []) {\n    return $funcName . '(' . (!empty($params) ? implode(',', $params) : '') . ')';\n  }\n\n  /**\n   * @param        $phpCompiledVar\n   * @param PTLTag $ptlTag\n   */\n  protected static function num($phpCompiledVar, $ptlTag) {\n    $result = $phpCompiledVar;\n\n    if (array_key_exists(self::PARAM_NUMERIC, $ptlTag->params)) {\n      // Just dump other params\n      foreach (static::$allowedParams as $paramName => $limitTag) {\n        if ($limitTag != self::PARAM_NUMERIC || !array_key_exists($paramName, $ptlTag->params)) {\n          continue;\n        }\n\n        switch ($paramName) {\n          case self::PARAM_NUMERIC_CEIL:\n          case self::PARAM_NUMERIC_FLOOR:\n            $result = static::func($paramName, $result, '');\n          break;\n\n          case self::PARAM_NUMERIC_ROUND:\n            $result = static::func($paramName, $result, [intval($ptlTag->params[$paramName])]);\n          break;\n\n          case self::PARAM_NUMERIC_FORMAT:\n            $result = static::func('HelperString::numberFormat', $result, [0]);\n          break;\n\n          case self::PARAM_NUMERIC_MONEY:\n            $result = static::func('HelperString::numberFormat', $result, [2]);\n          break;\n\n          case self::PARAM_NUMERIC_COLOR:\n            $result = static::func('prettyNumberStyledDefault', $result);\n          break;\n        }\n      }\n\n    }\n\n    return $result;\n  }\n\n  /**\n   * @param        $phpCompiledVar\n   * @param PTLTag $ptlTag\n   */\n  protected static function datetime($phpCompiledVar, $ptlTag) {\n    $result = $phpCompiledVar;\n\n    if (array_key_exists(self::PARAM_DATETIME, $ptlTag->params)) {\n      // Just dump other params\n      foreach (static::$allowedParams as $paramName => $limitTag) {\n        if ($limitTag != self::PARAM_DATETIME || !array_key_exists($paramName, $ptlTag->params)) {\n          continue;\n        }\n\n        switch ($paramName) {\n          case self::PARAM_DATETIME:\n            $result = \"empty($result) ? '' : \" . static::func2('date', [\"'\" . FMT_DATE_TIME_SQL . \"'\", $result]);\n          break;\n        }\n      }\n\n    }\n\n    return $result;\n  }\n\n}\n"
  },
  {
    "path": "classes/Que/DBStaticQue.php",
    "content": "<?php\n\nnamespace Que;\nuse SN;\n\nclass DBStaticQue {\n\n  public static function db_que_list_by_type_location($user_id, $planet_id = null, $que_type = false, $for_update = false) {\n    return SN::db_que_list_by_type_location($user_id, $planet_id, $que_type, $for_update);\n  }\n\n  public static function db_que_list_stat() {\n    return doquery(\"SELECT que_player_id, sum(que_unit_amount) AS que_unit_amount, que_unit_price FROM `{{que}}` GROUP BY que_player_id, que_unit_price;\");\n  }\n\n  public static function db_que_set_time_left_by_id($que_id, $que_time_left) {\n    return SN::db_upd_record_by_id(LOC_QUE, $que_id, \"`que_time_left` = {$que_time_left}\");\n  }\n\n  public static function db_que_set_insert($set) {\n    return SN::db_ins_record(LOC_QUE, $set);\n  }\n\n  public static function db_que_delete_by_id($que_id) {\n    return SN::db_del_record_by_id(LOC_QUE, $que_id);\n  }\n\n  public static function db_que_planet_change_owner($planet_id, $new_owner_id) {\n    return doquery(\"UPDATE {{que}} SET `que_player_id` = {$new_owner_id} WHERE `que_planet_id` = {$planet_id}\");\n  }\n\n  public static function db_que_research_change_origin($planet_id, $new_planet_id) {\n    return doquery(\"UPDATE {{que}} SET `que_planet_id_origin` = {$new_planet_id} WHERE `que_planet_id_origin` = {$planet_id}\");\n  }\n\n}"
  },
  {
    "path": "classes/Que/QueUnitStatic.php",
    "content": "<?php\n/**\n * Created by Gorlum 24.03.2018 19:45\n */\n\nnamespace Que;\n\nuse DBAL\\db_mysql;\nuse Planet\\DBStaticPlanet;\nuse SN;\n\nclass QueUnitStatic {\n\n  /**\n   * @param array $prevSqlBlock\n   * @param int   $unit_id\n   * @param array $user\n   * @param array $planet\n   * @param array $build_data\n   * @param int   $unit_level\n   * @param int   $unit_amount\n   * @param int   $build_mode\n   *\n   * @return array\n   */\n  public static function que_unit_make_sql($prevSqlBlock = [], $unit_id, $user = array(), $planet = array(), $build_data, $unit_level = 0, $unit_amount = 1, $build_mode = BUILD_CREATE) {\n    // TODO Унифицировать проверки\n\n    // TODO que_process() тут\n\n    db_mysql::db_transaction_check(true);\n\n    $build_mode = $build_mode == BUILD_CREATE ? BUILD_CREATE : BUILD_DESTROY;\n\n    // TODO: Some checks\n    db_change_units($user, $planet, array(\n      RES_METAL     => -$build_data[$build_mode][RES_METAL] * $unit_amount,\n      RES_CRYSTAL   => -$build_data[$build_mode][RES_CRYSTAL] * $unit_amount,\n      RES_DEUTERIUM => -$build_data[$build_mode][RES_DEUTERIUM] * $unit_amount,\n    ));\n\n    $que_type         = que_get_unit_que($unit_id);\n    $planet_id_origin = $planet['id'] ? floatval($planet['id']) : null;\n    $planet_id        = $que_type == QUE_RESEARCH ? null : $planet_id_origin;\n    if (is_numeric($planet_id)) {\n      DBStaticPlanet::db_planet_set_by_id($planet_id, \"`que_processed` = UNIX_TIMESTAMP(NOW())\");\n    } elseif (is_numeric($user['id'])) {\n      db_user_set_by_id($user['id'], '`que_processed` = UNIX_TIMESTAMP(NOW())');\n    }\n\n    $resource_list = sys_unit_arr2str($build_data[$build_mode]);\n\n    $result = [\n      'que_player_id'         => $user['id'],\n      'que_planet_id'         => $planet_id,\n      'que_planet_id_origin'  => $planet_id_origin,\n      'que_type'              => $que_type,\n      'que_time_left'         => $build_data[RES_TIME][$build_mode],\n      'que_unit_id'           => $unit_id,\n      'que_unit_amount'       => $unit_amount,\n      'que_unit_mode'         => $build_mode,\n      'que_unit_level'        => $unit_level,\n      'que_unit_time'         => $build_data[RES_TIME][$build_mode],\n      'que_unit_price'        => $resource_list,\n      'que_unit_one_time_raw' => $build_data[P_OPTIONS][P_TIME_RAW],\n    ];\n\n    return $result;\n  }\n\n}\n"
  },
  {
    "path": "classes/RequestInfo.php",
    "content": "<?php\n\n/**\n * User: Gorlum\n * Date: 29.08.2015\n * Time: 16:49\n */\n\nuse DBAL\\db_mysql;\n\n/**\n * Подробности о запросе\n */\nclass RequestInfo {\n  /**\n   * Идентификационная строка устройства\n   *\n   * @var string\n   */\n  protected $device_cypher = '';\n  /**\n   * Идентификатор устройства\n   *\n   * @var string\n   */\n  public $device_id = 0;\n\n  /**\n   * Строка User-agent пользовательского браузера\n   *\n   * @var string\n   */\n  protected $user_agent = '';\n  /**\n   * Внутренний идентификатор строки браузера\n   *\n   * @var int\n   */\n  public $browser_id = 0;\n\n  /**\n   * Полный URL строки запроса\n   *\n   * @var string\n   */\n  protected $page_address = '';\n  /**\n   * ID запроса в таблице УРЛов\n   *\n   * @var int\n   */\n  public $page_address_id = 0;\n\n  /**\n   * Query param\n   *\n   * @var string $queryString\n   */\n  protected $queryString = '';\n  /**\n   * Query param ID\n   *\n   * @var int\n   */\n  public $queryStringId = 0;\n\n  /**\n   * Player entry ID - pointer to combination of player ID, device ID, browser ID, user IP, user proxy\n   *\n   * @var int $playerEntryId\n   */\n  protected $playerEntryId = 0;\n\n  /**\n   * Адрес IPv4 в виде строки\n   *\n   * @var string\n   */\n  public $ip_v4_string = '';\n  /**\n   * Адрес IPv4 в виде целого\n   *\n   * @var int\n   */\n  public $ip_v4_int = 0;\n  /**\n   * Цепочка прокси IPv4\n   *\n   * @var string\n   */\n  public $ip_v4_proxy_chain = '';\n\n  protected $write_full_url = false;\n\n  public function __construct() {\n    // TODO - CHANGE!!!!\n    global $skip_log_query;\n\n    $this->write_full_url = !SN::$config->security_write_full_url_disabled;\n\n    // Инфа об устройстве и браузере - общая для всех\n    db_mysql::db_transaction_start();\n    $this->device_cypher = $_COOKIE[SN_COOKIE_D];\n    if ($this->device_cypher) {\n      $cypher_safe = SN::$db->db_escape($this->device_cypher);\n      /** @noinspection SqlResolve */\n      $device_id = doquery(\"SELECT `device_id` FROM `{{security_device}}` WHERE `device_cypher` = '{$cypher_safe}' LIMIT 1 FOR UPDATE\", true);\n      if (!empty($device_id['device_id'])) {\n        $this->device_id = $device_id['device_id'];\n      }\n    }\n\n    if ($this->device_id <= 0) {\n      do {\n        $cypher_safe = SN::$db->db_escape($this->device_cypher = sys_random_string());\n\n        /** @noinspection SqlResolve */\n        $row = doquery(\"SELECT `device_id` FROM `{{security_device}}` WHERE `device_cypher` = '{$cypher_safe}' LIMIT 1 FOR UPDATE\", true);\n      } while (!empty($row));\n      doquery(\"INSERT INTO {{security_device}} (`device_cypher`) VALUES ('{$cypher_safe}');\");\n      $this->device_id = SN::$db->db_insert_id();\n      sn_setcookie(SN_COOKIE_D, $this->device_cypher, PERIOD_FOREVER, SN_ROOT_RELATIVE);\n    }\n    db_mysql::db_transaction_commit();\n\n    $this->user_agent = !empty($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : '';\n    $this->browser_id = db_get_set_unique_id_value('security_browser', 'browser_id', ['browser_user_agent' => $this->user_agent,]);\n\n    $this->page_address    = substr($_SERVER['PHP_SELF'], strlen(SN_ROOT_RELATIVE));\n    $this->page_address_id = db_get_set_unique_id_value('security_url', 'url_id', ['url_string' => $this->page_address,]);\n\n    // Not a simulator - because it can have loooooong string\n    if (strpos($_SERVER['REQUEST_URI'], '/simulator.php') !== 0 && !$skip_log_query) {\n      $this->queryString = !empty($_SERVER['QUERY_STRING']) ? $_SERVER['QUERY_STRING'] : '';\n      $this->queryStringId = db_get_set_unique_id_value('security_query_strings', 'id', ['query_string' => $this->queryString,]);\n    }\n\n    $ip                      = sec_player_ip();\n    $this->ip_v4_string      = $ip['ip'];\n    $this->ip_v4_int         = ip2longu($this->ip_v4_string);\n    $this->ip_v4_proxy_chain = $ip['proxy_chain'];\n\n    $this->playerEntryId = db_get_set_unique_id_value(\n      'security_player_entry',\n      'id',\n      [\n        'device_id'  => $this->device_id,\n        'browser_id' => $this->browser_id,\n        'user_ip'    => $this->ip_v4_int,\n        'user_proxy' => $this->ip_v4_proxy_chain,\n      ]\n    );\n  }\n\n  /**\n   * Вставляет запись системы безопасности\n   *\n   * @param $userId\n   *\n   * @return int\n   * @deprecated\n   */\n  // TODO - remove\n  public function db_security_entry_insert($userId) {\n    // TODO $user_id = !empty(self::$user['id']) ? self::$user['id'] : 'NULL';\n    if (empty($userId)) {\n      // self::flog('Нет ИД пользователя');\n      return true;\n    }\n\n    $pEntry = db_get_set_unique_id_value(\n      'security_player_entry',\n      'id',\n      [\n//        'player_id'  => $userId,\n        'device_id'  => $this->device_id,\n        'browser_id' => $this->browser_id,\n        'user_ip'    => $this->ip_v4_int,\n        'user_proxy' => $this->ip_v4_proxy_chain,\n      ]\n    );\n\n    return $pEntry;\n\n\n    // self::flog('Вставляем запись системы безопасности');\n  }\n\n  /**\n   * Вставляет данные в счётчик\n   *\n   * @param $user_id_unsafe\n   */\n  public function db_counter_insert($user_id_unsafe) {\n    global $config, $sys_stop_log_hit, $is_watching;\n\n    if ($sys_stop_log_hit || !$config->game_counter) {\n      return;\n    }\n\n    $user_id_safe = SN::$db->db_escape($user_id_unsafe);\n    $proxy_safe   = SN::$db->db_escape($this->ip_v4_proxy_chain);\n\n    $is_watching = true;\n    doquery(\n      \"INSERT INTO {{counter}} SET\n        `visit_time` = '\" . SN_TIME_SQL . \"',\n        `user_id` = {$user_id_safe},\n        `player_entry_id` = {$this->playerEntryId},\n        `page_url_id` = {$this->page_address_id},\n        `query_string_id` = {$this->queryStringId}\" .\n      \";\");\n\n//    `device_id` = {$this->device_id},\n//        `browser_id` = {$this->browser_id},\n//        `user_ip` = {$this->ip_v4_int},\n//        `user_proxy` = '{$proxy_safe}',\n\n    $is_watching = false;\n  }\n\n}\n"
  },
  {
    "path": "classes/ResultMessages.php",
    "content": "<?php\n/**\n * Created by Gorlum 25.01.2018 8:36\n */\n\nclass ResultMessages implements Countable {\n  // TODO - move to separate class\n  /**\n   * @var array[] $resultMessages\n   */\n  protected $resultMessages = [];\n\n  public function __construct() {\n    $this->reset();\n  }\n\n  /**\n   * @param array $result\n   */\n  public function add($message, $status = ERR_NONE) {\n    $this->resultMessages[] = ['MESSAGE' => $message, 'STATUS' => $status];\n  }\n\n  /**\n   * @return int\n   */\n  public function count() {\n    return count($this->resultMessages);\n  }\n\n  /**\n   * @param template $template\n   */\n  public function templateAdd(template $template) {\n    foreach ($this->resultMessages as $error_message) {\n      $template->assign_block_vars('result', $error_message);\n    }\n  }\n\n  public function reset() {\n    $this->resultMessages = [];\n  }\n\n}\n"
  },
  {
    "path": "classes/SN.php",
    "content": "<?php\n/** @noinspection PhpDeprecationInspection */\n/** @noinspection SqlResolve */\n\n/** @noinspection PhpUnnecessaryCurlyVarSyntaxInspection */\n\nuse DBAL\\db_mysql;\nuse Player\\userOptions;\nuse Common\\Vector;\nuse Core\\GlobalContainer;\nuse Unit\\DBStaticUnit;\n\n/**\n * Class SN\n *\n * Singleton\n */\nclass SN {\n  /**\n   * Flag that something was rendered\n   *\n   * @var bool\n   */\n  public static $gSomethingWasRendered = false;\n\n  /**\n   * @var GlobalContainer $gc\n   */\n  public static $gc;\n\n  /**\n   * Основная БД для доступа к данным\n   *\n   * @var db_mysql $db\n   */\n  public static $db;\n  public static $db_name = '';\n\n  /**\n   * Настройки из файла конфигурации\n   *\n   * @var string\n   */\n  public static $cache_prefix = 'sn_';\n  public static $sn_secret_word = '';\n\n  /**\n   * Конфигурация игры\n   *\n   * @var classConfig $config\n   */\n  public static $config;\n\n\n  /**\n   * Кэш игры\n   *\n   * @var classCache $cache\n   */\n  public static $cache;\n\n  /**\n   * @var classLocale $lang\n   */\n  public static $lang;\n\n\n  /**\n   * @var core_auth $auth\n   */\n  public static $auth = null;\n\n\n  public static $user = array();\n  /**\n   * @var userOptions\n   */\n  public static $user_options;\n\n  /** @var ?debug $debug */\n  public static $debug = null;\n\n\n  public static $options = array();\n\n  /**\n   * Is header already rendered?\n   *\n   * @var bool $headerRendered\n   */\n  public static $headerRendered = false;\n\n  /** @var bool $sys_user_logged_in Is user logged in? TODO - move to user-related */\n  public static $sys_user_logged_in = false;\n\n  /*\n  TODO Кэш:\n  1. Всегда дешевле использовать процессор, чем локальную память\n  2. Всегда дешевле использовать локальную память, чем общую память всех процессов\n  3. Всегда дешевле использовать общую память всех процессов, чем обращаться к БД\n\n  Кэш - многоуровневый: локальная память-общая память-БД\n  БД может быть сверх-кэширующей - см. HyperNova. Это реализуется на уровне СН-драйвера БД\n  Предусмотреть вариант, когда уровни кэширования совпадают, например когда нет xCache и используется общая память\n  */\n  //public static $cache; // Кэширующий объект - либо встроенная память, либо кэш в памяти с блокировками - находится внутри $db!!!!\n  //public static $db; // Объект-БД - либо кэширующий объект с блокировками, либо БД\n\n  // protected static $info = array(); // Кэш информации - инфо о юнитах, инфо о группах итд\n\n  // TODO Автоматически заполнять эту таблицу. В случае кэша в памяти - делать show table при обращении к таблице\n  public static $location_info = array(\n    LOC_USER => array(\n      P_TABLE_NAME => 'users',\n      P_ID         => 'id',\n      P_OWNER_INFO => array(),\n    ),\n\n    LOC_PLANET => array(\n      P_TABLE_NAME => 'planets',\n      P_ID         => 'id',\n      P_OWNER_INFO => array(\n        LOC_USER => array(\n          P_LOCATION    => LOC_USER,\n          P_OWNER_FIELD => 'id_owner',\n        ),\n      ),\n    ),\n\n    LOC_UNIT => array(\n      P_TABLE_NAME => 'unit',\n      P_ID         => 'unit_id',\n      P_OWNER_INFO => array(\n        LOC_USER => array(\n          P_LOCATION    => LOC_USER,\n          P_OWNER_FIELD => 'unit_player_id',\n        ),\n      ),\n    ),\n\n    LOC_QUE => array(\n      P_TABLE_NAME => 'que',\n      P_ID         => 'que_id',\n      P_OWNER_INFO => array(\n        array(\n          P_LOCATION    => LOC_USER,\n          P_OWNER_FIELD => 'que_player_id',\n        ),\n\n        array(\n          P_LOCATION    => LOC_PLANET,\n          P_OWNER_FIELD => 'que_planet_id_origin',\n        ),\n\n        array(\n          P_LOCATION    => LOC_PLANET,\n          P_OWNER_FIELD => 'que_planet_id',\n        ),\n      ),\n    ),\n\n    LOC_FLEET => array(\n      P_TABLE_NAME => 'fleets',\n      P_ID         => 'fleet_id',\n      P_OWNER_INFO => array(\n        array(\n          P_LOCATION    => LOC_USER,\n          P_OWNER_FIELD => 'fleet_owner',\n        ),\n\n        array(\n          P_LOCATION    => LOC_USER,\n          P_OWNER_FIELD => 'fleet_target_owner',\n        ),\n\n        array(\n          P_LOCATION    => LOC_PLANET,\n          P_OWNER_FIELD => 'fleet_start_planet_id',\n        ),\n\n        array(\n          P_LOCATION    => LOC_PLANET,\n          P_OWNER_FIELD => 'fleet_end_planet_id',\n        ),\n      ),\n    ),\n  );\n\n  /**\n   * @var callable[] $afterInit\n   */\n  public static $afterInit = [];\n\n  public function __construct() {\n\n  }\n\n\n  public static function log_file($message, $spaces = 0) {\n    if (self::$debug) {\n      self::$debug->log_file($message, $spaces);\n    }\n  }\n\n\n  /**\n   * Возвращает информацию о записи по её ID\n   *\n   * @param int       $location_type\n   * @param int|array $record_id_unsafe\n   *    <p>int - ID записи</p>\n   *    <p>array - запись пользователя с установленным полем P_ID</p>\n   *\n   * @return array|false\n   *    <p>false - Нет записи с указанным ID</p>\n   *    <p>array - запись</p>\n   */\n  public static function db_get_record_by_id($location_type, $record_id_unsafe) {\n    $id_field       = static::$location_info[$location_type][P_ID];\n    $record_id_safe = idval(is_array($record_id_unsafe) && isset($record_id_unsafe[$id_field]) ? $record_id_unsafe[$id_field] : $record_id_unsafe);\n\n    return static::db_get_record_list($location_type, \"`{$id_field}` = {$record_id_safe}\", true);\n  }\n\n  public static function db_get_record_list($location_type, $filter = '', $fetch = false, $no_return = false) {\n    $location_info = &static::$location_info[$location_type];\n    $id_field      = $location_info[P_ID];\n    $tableName     = $location_info[P_TABLE_NAME];\n\n    $result = false;\n\n//    $sqlResult = static::db_query_select(\n//      \"SELECT * FROM {{{$tableName}}}\" . (($filter = trim($filter)) ? \" WHERE {$filter}\" : '')\n//    );\n    $query = \"SELECT * FROM {{{$tableName}}}\" . (($filter = trim($filter)) ? \" WHERE {$filter}\" : '');\n    $query .= db_mysql::db_transaction_check(db_mysql::DB_TRANSACTION_WHATEVER) ? ' FOR UPDATE' : '';\n\n    $sqlResult = self::$db->doquery($query, false);\n\n    while ($row = db_fetch($sqlResult)) {\n      $result[$row[$id_field]] = $row;\n      if ($fetch) {\n        break;\n      }\n    }\n\n    if ($no_return) {\n      return true;\n    } else {\n      return $fetch ? (is_array($result) ? reset($result) : false) : $result;\n    }\n  }\n\n  public static function db_upd_record_by_id($location_type, $record_id, $set) {\n    if (!($record_id = idval($record_id)) || !($set = trim($set))) {\n      return false;\n    }\n\n    $location_info = &static::$location_info[$location_type];\n    $id_field      = $location_info[P_ID];\n    $table_name    = $location_info[P_TABLE_NAME];\n    if ($result = self::$db->doquery(\"UPDATE {{{$table_name}}} SET {$set} WHERE `{$id_field}` = {$record_id}\", false)) // TODO Как-то вернуть может быть LIMIT 1 ?\n    {\n      if (static::$db->db_affected_rows()) {\n        // Обновляем данные только если ряд был затронут\n        DBStaticUnit::cache_clear();\n      }\n    }\n\n    return $result;\n  }\n\n  public static function db_upd_record_list($location_type, $condition, $set) {\n    if (!($set = trim($set))) {\n      return false;\n    }\n\n    $condition  = trim($condition);\n    $table_name = static::$location_info[$location_type][P_TABLE_NAME];\n\n    if ($result = self::$db->doquery(\"UPDATE {{{$table_name}}} SET \" . $set . ($condition ? ' WHERE ' . $condition : ''))) {\n\n      if (static::$db->db_affected_rows()) { // Обновляем данные только если ряд был затронут\n        // Поскольку нам неизвестно, что и как обновилось - сбрасываем кэш этого типа полностью\n        DBStaticUnit::cache_clear();\n      }\n    }\n\n    return $result;\n  }\n\n  public static function db_ins_record($location_type, $set) {\n    $set        = trim($set);\n    $table_name = static::$location_info[$location_type][P_TABLE_NAME];\n    if ($result = self::$db->doquery(\"INSERT INTO `{{{$table_name}}}` SET {$set}\")) {\n      if (static::$db->db_affected_rows()) // Обновляем данные только если ряд был затронут\n      {\n        $record_id = SN::$db->db_insert_id();\n        // Вытаскиваем запись целиком, потому что в $set могли быть \"данные по умолчанию\"\n        $result = static::db_get_record_by_id($location_type, $record_id);\n        // Очищаем второстепенные кэши - потому что вставленная запись могла повлиять на результаты запросов или локация или еще чего\n        DBStaticUnit::cache_clear();\n      }\n    }\n\n    return $result;\n  }\n\n  public static function db_del_record_by_id($location_type, $safe_record_id) {\n    if (!($safe_record_id = idval($safe_record_id))) {\n      return false;\n    }\n\n    $location_info = &static::$location_info[$location_type];\n    $id_field      = $location_info[P_ID];\n    $table_name    = $location_info[P_TABLE_NAME];\n    if ($result = self::$db->doquery(\"DELETE FROM `{{{$table_name}}}` WHERE `{$id_field}` = {$safe_record_id}\")) {\n      if (static::$db->db_affected_rows()) // Обновляем данные только если ряд был затронут\n      {\n        DBStaticUnit::cache_clear();\n      }\n    }\n\n    return $result;\n  }\n\n  public static function db_del_record_list($location_type, $condition) {\n    if (!($condition = trim($condition))) {\n      return false;\n    }\n\n    $table_name = static::$location_info[$location_type][P_TABLE_NAME];\n\n    if ($result = self::$db->doquery(\"DELETE FROM `{{{$table_name}}}` WHERE {$condition}\")) {\n      if (static::$db->db_affected_rows()) // Обновляем данные только если ряд был затронут\n      {\n        // Обнуление кэша, потому что непонятно, что поменялось\n        DBStaticUnit::cache_clear();\n      }\n    }\n\n    return $result;\n  }\n\n\n  /*\n   * С $for_update === true эта функция должна вызываться только из транзакции! Все соответствующие записи в users и planets должны быть уже блокированы!\n   *\n   * $que_type\n   *   !$que_type - все очереди\n   *   QUE_XXX - конкретная очередь по планете\n   * $user_id - ID пользователя\n   * $planet_id\n   *   $que_type == QUE_RESEARCH - игнорируется\n   *   null - обработка очередей планет не производится\n   *   false/0 - обрабатываются очереди всех планет по $user_id\n   *   (integer) - обрабатываются локальные очереди для планеты. Нужно, например, в обработчике флотов\n   *   иначе - $que_type для указанной планеты\n   * $for_update - true == нужно блокировать записи\n   *\n   * TODO Работа при !$user_id\n   * TODO Переформатировать вывод данных, что бы можно было возвращать данные по всем планетам и юзерам в одном запросе: добавить под-массивы 'que', 'planets', 'players'\n   *\n   */\n  /** @noinspection PhpUnusedParameterInspection */\n  public static function db_que_list_by_type_location($user_id, $planet_id = null, $que_type = false, $for_update = false) {\n    if (!$user_id) {\n      pdump(debug_backtrace());\n      die('No user_id for que_get_que()');\n    }\n\n    $ques = array();\n\n    $query = array();\n\n    if ($user_id = idval($user_id)) {\n      $query[] = \"`que_player_id` = {$user_id}\";\n    }\n\n    if ($que_type == QUE_RESEARCH || $planet_id === null) {\n      $query[] = \"`que_planet_id` IS NULL\";\n    } elseif ($planet_id) {\n      $query[] = \"(`que_planet_id` = {$planet_id}\" . ($que_type ? '' : ' OR que_planet_id IS NULL') . \")\";\n    }\n    if ($que_type) {\n      $query[] = \"`que_type` = {$que_type}\";\n    }\n\n    $ques['items'] = static::db_get_record_list(LOC_QUE, implode(' AND ', $query));\n\n    return que_recalculate($ques);\n  }\n\n\n  public static function loadFileSettings() {\n    $dbsettings = array();\n\n    require(SN_CONFIG_PATH);\n    //self::$db_prefix = $dbsettings['prefix'];\n    self::$cache_prefix = !empty($dbsettings['cache_prefix']) ? $dbsettings['cache_prefix'] : $dbsettings['prefix'];\n    self::$db_name      = $dbsettings['name'];\n    /** @noinspection SpellCheckingInspection */\n    self::$sn_secret_word = $dbsettings['secretword'];\n\n    self::services();\n\n    unset($dbsettings);\n  }\n\n  public static function init_global_objects() {\n    global $sn_cache, $config, $debug;\n\n    $debug    = self::$debug = self::$gc->debug;\n    self::$db = self::$gc->db;\n    self::$db->sn_db_connect();\n\n    self::$user_options = new userOptions(0);\n\n    // Initializing global `cache` object\n    $sn_cache = static::$cache = self::$gc->cache;\n    $tables   = SN::$db->schema()->getSnTables();\n    empty($tables) && die('DB error - cannot find any table. Halting...');\n\n    // Initializing global `config` object\n    $config = self::$config = self::$gc->config;\n\n    // Initializing statics\n    Vector::_staticInit(self::$config);\n\n    // After init callbacks\n    foreach (static::$afterInit as $callback) {\n      if (is_callable($callback)) {\n        $callback();\n      }\n    }\n  }\n\n  /**\n   * @param int    $newStatus\n   * @param string $newMessage\n   *\n   * @return int\n   * @noinspection PhpUnusedParameterInspection\n   */\n  public static function gameDisable($newStatus = GAME_DISABLE_REASON, $newMessage = '') {\n    /** @noinspection PhpCastIsUnnecessaryInspection */\n    $old_server_status = intval(self::$config->pass()->game_disable);\n\n    self::$config->pass()->game_disable = $newStatus;\n\n    return $old_server_status;\n  }\n\n//  public static function gameEnable() {\n//    self::$config->pass()->game_disable = GAME_DISABLE_NONE;\n//  }\n\n  /**\n   * Is game disabled?\n   *\n   * @return bool\n   */\n  public static function gameIsDisabled() {\n    return self::$config->pass()->game_disable != GAME_DISABLE_NONE;\n  }\n\n\n  /**\n   * @return GlobalContainer\n   */\n  public static function services() {\n    if (empty(self::$gc)) {\n      self::$gc = new GlobalContainer(array(\n        'cachePrefix' => self::$cache_prefix,\n      ));\n    }\n\n    return self::$gc;\n  }\n\n}\n"
  },
  {
    "path": "classes/SkinInterface.php",
    "content": "<?php\n\n/**\n * Created by Gorlum 23.02.2017 13:10\n */\ninterface SkinInterface {\n\n  /**\n   * Get skin name\n   *\n   * @return string\n   */\n  public function getName();\n\n  /**\n   * Возвращает строку для вывода в компилированном темплейте PTL\n   *\n   * @param PTLTag $ptlTag\n   *\n   * @return string\n   */\n  public function imageFromPTLTag($ptlTag);\n\n  /**\n   * Compiles image string from string\n   *\n   * @param string   $stringTag\n   * @param template $template\n   *\n   * @return string\n   */\n  public function imageFromStringTag($stringTag, $template = null);\n\n}\n"
  },
  {
    "path": "classes/SkinModel.php",
    "content": "<?php\n\nuse Core\\GlobalContainer;\n\n/**\n * Created by Gorlum 23.02.2017 12:20\n */\nclass SkinModel {\n  const NO_IMAGE_ID = '_no_image';\n  const NO_IMAGE_PATH = '/design/images/_no_image.png';\n\n  /**\n   * @var GlobalContainer $gc\n   */\n  protected $gc;\n\n  /**\n   * @var SkinInterface[] $skins\n   */\n  // TODO - lazy loading\n  protected $skins;\n\n  /**\n   * @var SkinInterface $activeSkin\n   */\n  protected $activeSkin;\n\n\n  // TODO - remove\n  public function init() {\n  }\n\n\n  /**\n   * SkinModel constructor.\n   *\n   * @param GlobalContainer $gc\n   */\n  public function __construct(GlobalContainer $gc) {\n    $this->gc    = $gc;\n    $this->skins = array();\n\n    // Берем текущий скин\n    $this->activeSkin = $this->getSkin(SN::$gc->theUser->getSkinPath());\n  }\n\n  /**\n   * Returns skin with skin name. Loads it - if it is required\n   *\n   * @param string $skinName\n   *\n   * @return SkinInterface\n   */\n  public function getSkin($skinName) {\n    $skinName = $this->sanitizeSkinName($skinName);\n\n    if (empty($this->skins[$skinName])) {\n      // Прогружаем текущий скин\n      $this->skins[$skinName] = $this->loadSkin($skinName);\n    }\n\n    return $this->skins[$skinName];\n  }\n\n  /**\n   * @param string        $image_tag\n   * @param template|null $template\n   *\n   * @return string\n   */\n  public function getImageCurrent($image_tag, $template = null) {\n    return $this->getImageFrom($this->activeSkin->getName(), $image_tag, $template);\n  }\n\n  /**\n   * @param string        $image_tag\n   * @param template|null $template\n   *\n   * @return bool\n   */\n  public function isImageFileExists($image_tag, $template = null) {\n    $largeUrl = $this->getImageCurrent($image_tag, $template);\n    if (strpos($largeUrl, SKIN_IMAGE_MISSED_FILE_PATH) !== false) {\n      // Image not found in directory\n      $result = false;\n    } else {\n      // Image found in directory. But what for actual file?\n      $imageRelativePath = substr($largeUrl, strlen(SN_ROOT_VIRTUAL));\n      $result            = file_exists(SN_ROOT_PHYSICAL . $imageRelativePath);\n    }\n\n    return $result;\n  }\n\n  public function getImageFrom($skinName, $image_tag, $template) {\n    return $this->getSkin($skinName)->imageFromStringTag($image_tag, $template);\n  }\n\n  /**\n   * Switches current skin\n   *\n   * @param $skinName\n   */\n  public function switchActive($skinName) {\n    $this->activeSkin = $this->getSkin($skinName);\n  }\n\n  /**\n   * Loads skin\n   *\n   * @param string $skinName\n   *\n   * @return SkinInterface\n   */\n  protected function loadSkin($skinName) {\n    $skinClass = $this->gc->skinEntityClass;\n\n    $skin = new $skinClass($skinName, $this);\n\n//    $skin->load();\n\n    return $skin;\n  }\n\n\n  /**\n   * @param string $skinName\n   *\n   * @return string\n   */\n  protected function sanitizeSkinName($skinName) {\n    strpos($skinName, 'skins/') !== false ? $skinName = substr($skinName, 6) : false;\n    strpos($skinName, '/') !== false ? $skinName = str_replace('/', '', $skinName) : false;\n\n    return is_string($skinName) ? $skinName : '';\n  }\n\n}\n"
  },
  {
    "path": "classes/SkinV2.php",
    "content": "<?php\n\n/**\n * User: Gorlum\n * Date: 23.10.2015\n * Time: 19:54\n */\n/*\n\nINI-файл:\n  Спецпараметры начинаются с _:\n      _inherit - брать отсутствующие картинки из другого скина\n  Изображения:\n      eisenplanet01 = \"planeten/eisenplanet01.jpg\" - путь относительно локального скина\n      В качестве ID изображения можно указывать путь:\n        img/galaxie.gif = \"img/galaxie.gif\"\n\nВызов в темплейте\n   {I_<id>|парам1|парам2|...} - {I_abort|html}\n   {I_<путь к картинке от корня скина>|парам1|парам2|...} - {I_img/e.jpg|html}\n   {I_<путь к картинке от корня движка>|парам1|парам2|...} - {I_/img/e.jpg|html}\n   {I_[<имя переменной в темплейте>]} - будет подставлено имя соответствующей переменной в момент выполнения. Поддерживаются:\n       - Корневые значения, например {I_[UNIT_ID]}\n       - Значения в блоках, например {I_[production.ID]}\n       - Корневые значения DEFINE, например {I_[$PLANET_GOVERNOR_ID]}\n   Параметры вывода:\n      html - отрендерить обрамление HTML-тэгом IMG: <img src=\"\" />\n*/\n\n/**\n * Класс skin отвечает за работу скинов. В настоящее время - за маппинг {I_xxx} тэгов в HTTP-путь к файлу с картинкой\n *\n * Возможности:\n * - Поддержка конфигурации в файле skin.ini\n * - Работа через PTL тэги {I_xxx}\n * - Поддержка опций рендеринга через {I_xxx|param...}\n * - Поддержка абсолютных и относительных путей в skin.ini (абсоютный путь начинается с '/' - '/design/images/_no_image.png')\n *    - Относительные пути ресолвятся относительно корня скина - т.е. папки, где лежит skin.ini\n * - Подстановка значений переменных из класса template через {I_xxx[yyy]}:\n *    - Глобальные переменные - {I_xxx[UNIT_ID]}\n *    - Назначенные переменные - {I_xxx[$UNIT_ID]}\n *    - Переменные в блоках - {I_xxx[block.VAR]}\n * - Возможность указать в image-tag прямой путь - {I_/design/images/_no_image.png} - как абсолютный так и относительный\n * - Наследование скинов любой глубины вложенности (опция _inherit в skin.ini)\n * - Подстановка картинок из родителя при отсутствии данных в skin.ini или физическом отутствии файла\n * - Заглушка _NO_IMAGE при отсутствии картинки (опция _no_image в skin.ini)\n * - Автоматическая поддержка WebP для браузеров, что поддерживают WebP с фоллбэком на обычный формат\n */\nclass SkinV2 implements SkinInterface {\n  const PARAM_HTML = 'html';\n  const PARAM_HTML_HEIGHT = 'height';\n  const PARAM_HTML_WIDTH = 'width';\n  // Use skin for image\n  const PARAM_SKIN = 'skin';\n  const WEBP_SUFFIX = '_webp';\n\n  /**\n   * @var string $iniFileName\n   */\n  protected $iniFileName = 'skin.ini';\n\n  /**\n   * @var SkinModel $model\n   */\n  protected $model;\n\n\n  /**\n   * Флаг инициализации статического объекта\n   *\n   * @var bool\n   */\n  protected static $is_init = false;\n  /**\n   * Список скинов\n   * TODO Переделать под контейнер\n   *\n   * @var self[] $skin_list\n   */\n  protected static $skin_list = array();\n  /**\n   * Текущий скин\n   *\n   * @var self|null\n   */\n  protected static $active = null;\n\n  /**\n   * HTTP-путь к файлам скина относительно корня движка\n   *\n   * @var string\n   */\n  protected $root_http_relative = '';\n  /**\n   * Абсолютный физический путь к директории скина\n   *\n   * @var string\n   */\n  protected $root_physical_absolute = '';\n  /**\n   * Родительский скин\n   *\n   * @var SkinInterface|null\n   */\n  protected $parent = null;\n  /**\n   * Конфигурация скина - читается из INI-файла\n   *\n   * @var array\n   */\n  protected $config = array();\n  /**\n   * Сортированный список поддерживаемых параметров\n   *\n   * @var string[] $allowedParams\n   */\n  protected $allowedParams = array(\n    self::PARAM_HTML        => '',\n    // Will be dumped for all tags which have |html\n    self::PARAM_HTML_HEIGHT => self::PARAM_HTML,\n    self::PARAM_HTML_WIDTH  => self::PARAM_HTML,\n\n    self::PARAM_SKIN => '',\n  );\n  /**\n   * Список полностью отрендеренных путей\n   *\n   * @var string[] $container\n   */\n  protected $container = array();\n  /**\n   * Название скина\n   *\n   * @var string $name\n   */\n  protected $name = '';\n\n  /**\n   * Cached value of no image string\n   *\n   * @var string $noImage\n   */\n  protected $noImage;\n\n  /*\n\n  Класс будет хранить инфу о скинах и их наследовании в привязке к темплейту\n\n  Должно быть статик-хранилище, которое будет хранить между экземплярами класса инфу о других скинах - для наследования\n\n  Должен быть метод парсинга конфигурации скина\n\n  Должен быть статик-метод, который будет вызываться из PTL для парсинга I_xxx тэгов\n\n  Иконки перекрываются загрузкой нестандартных иконок, если чо\n\n  Бэкграунд - с ним надо что-то порешать. Например - не использовать. Или тоже перекрывать в CSS\n    Типа, сделать пустой скин.цсс для ЭпикБлю, основные цвета прописать в _template.css, а в остальных просто перекрывать\n\n  */\n\n  /**\n   * Точка входа\n   *\n   * @param string   $image_tag\n   * @param template $template\n   *\n   * @return string\n   */\n  public static function image_url($image_tag, $template) {\n    return SN::$gc->skinModel->getImageCurrent($image_tag, $template);\n  }\n\n  /**\n   * skin constructor.\n   *\n   * @param mixed|null|string $skinName\n   * @param SkinModel         $skinModel\n   */\n  public function __construct($skinName = DEFAULT_SKINPATH, $skinModel) {\n    $this->model = $skinModel;\n    $this->name  = $skinName;\n\n    $this->root_http_relative     = 'skins/' . $this->name . '/'; // Пока стоит base=\"\" в body SN_ROOT_VIRTUAL - не нужен\n    $this->root_physical_absolute = SN_ROOT_PHYSICAL . $this->root_http_relative;\n    // Искать скин среди пользовательских - когда будет конструктор скинов\n    // Может не быть файла конфигурации - тогда используется всё \"по дефаулту\". Т.е. поданная строка - это именно имя файла\n\n    $this->loadIniFile();\n    $this->setParentFromConfig();\n\n    // Пытаемся скомпилировать _no_image заранее\n    $model       = $this->model;\n    $noImageID   = $model::NO_IMAGE_ID;\n    $noImagePath = $model::NO_IMAGE_PATH;\n\n    // Заглушка на самый крайний случай - когда скин является корневым и у него нет _no_image\n    if (empty($this->config[$noImageID]) && !$this->parent) {\n      // Если нет парента - берем хардкод\n      // Используем стандартный файл из движка\n      $this->container[$noImageID] = $this->compile_try_path($noImageID, $noImagePath);\n      // Проверка, что файл - отсутствует. Если да - это повреждение движка\n      // TODO - throw exception\n      empty($this->container[$noImageID]) ? die('Game file missing: ' . $noImagePath) : false;\n    } else {\n      $this->container[$noImageID] = $this->imageFromPTLTag(\n        new PTLTag(SKIN_IMAGE_MISSED_FIELD, null, $this->allowedParams)\n      );\n    }\n\n    $this->noImage = $this->container[$noImageID];\n\n    return $this;\n  }\n\n  /**\n   * @inheritdoc\n   */\n  public function getName() {\n    return $this->name;\n  }\n\n  /**\n   * @inheritdoc\n   */\n  public function imageFromStringTag($stringTag, $template = null) {\n    return $this->imageFromPTLTag(new PTLTag($stringTag, $template, $this->allowedParams));\n  }\n\n  /**\n   * Возвращает строку для вывода в компилированном темплейте PTL\n   *\n   * @param PTLTag $ptlTag\n   *\n   * @return string\n   */\n  public function imageFromPTLTag($ptlTag) {\n    // Проверяем наличие ключа RIT в хранилища. В нём не может быть несуществующих файлов по построению\n    $cacheKey = $ptlTag->getCacheKey();\n    if (!empty($this->container[$cacheKey])) {\n      return $this->container[$cacheKey];\n    }\n\n    // Шорткат\n    $imageId = $ptlTag->resolved;\n\n    $this->tryWebp($imageId);\n\n    // Нет ключа RIT в контейнере - обсчёт пути для RIT из конфигурации\n    empty($this->container[$imageId]) && !empty($this->config[$imageId])\n      ? $this->compile_try_path($imageId, $this->config[$imageId])\n      : false;\n\n    // Всё еще пусто? Может у нас не image ID, а просто путь к файлу?\n    empty($this->container[$imageId]) ? $this->compile_try_path($imageId, $imageId) : false;\n\n    // Нет - image ID не является путём к файлу. Пора обратиться к предкам за помощью...\n    // Пытаемся вытащить путь из родителя и применить к нему свои параметры\n    // Тащим по ID изображения, а не по ТЭГУ - мало ли что там делает с путём родитель и как преобразовывает его в строку?\n    if (empty($this->container[$imageId]) && !empty($this->parent)) {\n      $this->container[$imageId] = $this->parent->imageFromPTLTag(new PTLTag($imageId, $ptlTag->template, $this->allowedParams));\n    }\n\n    // Если у родителя нет картинки - он вернет пустую строку. Тогда нам надо использовать заглушку - свою или родительскую\n    empty($this->container[$imageId]) ? $this->container[$imageId] = $this->noImage : false;\n\n    return !empty($this->container[$imageId]) ? $this->apply_params($ptlTag) : '';\n  }\n\n  /**\n   * Проверка физического наличия файла с картинкой\n   *\n   * @param string $image_id\n   * @param string $file_path\n   *\n   * @return string\n   */\n  protected function compile_try_path($image_id, $file_path) {\n    $relative_path = strpos($file_path, '/') !== 0\n      ? $this->root_http_relative . $file_path\n      // Если первый символ пути '/' - значит это путь от HTTP-корня\n      // Откусываем символ и пользуем остальное в качестве пути\n      : substr($file_path, 1);\n\n    return is_file(SN_ROOT_PHYSICAL . $relative_path) ? $this->container[$image_id] = SN_ROOT_VIRTUAL . $relative_path : '';\n  }\n\n  /**\n   * @param PTLTag $ptlTag\n   *\n   * @return string\n   */\n  protected function apply_params(PTLTag $ptlTag) {\n    if (!is_object($ptlTag) || empty($ptlTag->params) || !is_array($ptlTag->params)) {\n      return $this->container[$ptlTag->resolved];\n    }\n\n    $params       = $ptlTag->params;\n    $image_string = $this->container[$ptlTag->resolved];\n\n    // Здесь автоматически произойдёт упорядочивание параметров\n\n    // Параметр 'skin' - использовать изображение из другого скина\n    if (array_key_exists(self::PARAM_SKIN, $params)) {\n      if ($params[self::PARAM_SKIN] == $this->name) {\n        // If skin - is this skin - then removing this param from list\n        $ptlTag->removeParam(self::PARAM_SKIN);\n      } else {\n        $skin         = $this->model->getSkin($params[self::PARAM_SKIN]);\n        $image_string = $skin->imageFromStringTag($ptlTag->resolved, $ptlTag->template);\n      }\n    }\n\n    // Параметр 'html' - выводить изображение в виде HTML-тэга\n    if (array_key_exists(self::PARAM_HTML, $params)) {\n      $htmlParams   = '';\n      $paramsNoHtml = $params;\n      unset($paramsNoHtml[self::PARAM_HTML]);\n      // Just dump other params\n      foreach ($paramsNoHtml as $name => $data) {\n        if ($this->allowedParams[$name] != self::PARAM_HTML) {\n          continue;\n        }\n\n        $htmlParams .= ' ' . $name . '=' . $data;\n      }\n\n      $image_string = \"<img src=\\\"{$image_string}\\\" {$htmlParams} />\";\n    }\n\n    return $this->container[$ptlTag->getCacheKey()] = $image_string;\n  }\n\n  /**\n   * Loads skin configuration\n   */\n  protected function loadIniFile() {\n    // Проверка на корректность и существование пути\n    if (!is_file($this->root_physical_absolute . $this->iniFileName)) {\n      return;\n    }\n\n    // Пытаемся распарсить файл\n    // По секциям? images и config? Что бы не копировать конфигурацию? Или просто unset(__inherit) а затем заново записать\n    $aConfig = parse_ini_file($this->root_physical_absolute . $this->iniFileName);\n    if (empty($aConfig)) {\n      return;\n    }\n\n    $this->config = $aConfig;\n  }\n\n  protected function setParentFromConfig() {\n    // Проверка на _inherit\n    if (empty($this->config['_inherit'])) {\n      return;\n    }\n\n    $parentName = $this->config['_inherit'];\n    // Если скин наследует себя...\n    if ($parentName == $this->name) {\n      // TODO - определять более сложные случаи циклических ссылок в _inherit\n      // TODO - throw exception\n      die('\">circular skin inheritance!');\n    }\n\n    $this->parent = $this->model->getSkin($parentName);\n  }\n\n  /**\n   * @param string $imageId Internal Image ID to try\n   */\n  private function tryWebp($imageId) {\n    if (!is_object(SN::$gc->theUser) || !SN::$gc->theUser->isWebpSupported()) {\n      return;\n    }\n    if (!empty($this->container[$imageId])) {\n      // Something already there - nothing to do\n      return;\n    }\n\n    $webpImageId = $imageId . self::WEBP_SUFFIX;\n    if (empty($this->config[$webpImageId])) {\n      // No WebP alternative - nothing to do\n      // We WILL NOT check for parent if there is no WebP alternative!\n      return;\n    }\n\n    // Trying to use WebP variant as original image\n    $this->compile_try_path($imageId, $this->config[$webpImageId]);\n\n    // Ready or not - we're out of here\n  }\n\n}\n"
  },
  {
    "path": "classes/SnTemplate.php",
    "content": "<?php\n\nuse Common\\Traits\\TSingleton;\nuse Core\\GlobalContainer;\nuse Fleet\\DbFleetStatic;\nuse Note\\Note;\nuse \\Pages\\PageTutorial;\nuse Planet\\DBStaticPlanet;\nuse Player\\playerTimeDiff;\nuse Template\\TemplateMeta;\n\n/**\n * Template manager\n */\nclass SnTemplate {\n\n  use TSingleton;\n\n  const TPL_HTML = '.tpl.html';\n  /**\n   * Partial path to templates root\n   */\n  const SN_TEMPLATES_PARTIAL_PATH = 'design/templates/';\n  const SN_TEMPLATE_NAME_DEFAULT = 'OpenGame';\n  const P_CONTENT = '__RENDERED_CONTENT';\n\n  /**\n   * @param template|string $template\n   */\n  public static function displayP($template) {\n    if (is_object($template)) {\n      if (empty($template->parsed)) {\n        self::parsetemplate($template);\n      }\n\n      foreach ($template->files as $section => $filename) {\n        $template->display($section);\n      }\n    } else {\n      print($template);\n    }\n  }\n\n  /**\n   * @param template|string $template\n   * @param array|bool      $array\n   *\n   * @return mixed\n   */\n  public static function parsetemplate($template, $array = false) {\n    if (is_object($template)) {\n      return self::templateObjectParse($template, $array);\n    } else {\n      $search[]  = '#\\{L_([a-z0-9\\-_]*?)\\[([a-z0-9\\-_]*?)\\]\\}#Ssie';\n      $replace[] = '((isset($lang[\\'\\1\\'][\\'\\2\\'])) ? $lang[\\'\\1\\'][\\'\\2\\'] : \\'{L_\\1[\\2]}\\');';\n\n      $search[]  = '#\\{L_([a-z0-9\\-_]*?)\\}#Ssie';\n      $replace[] = '((isset($lang[\\'\\1\\'])) ? $lang[\\'\\1\\'] : \\'{L_\\1}\\');';\n\n      $search[]  = '#\\{([a-z0-9\\-_]*?)\\}#Ssie';\n      $replace[] = '((isset($array[\\'\\1\\'])) ? $array[\\'\\1\\'] : \\'{\\1}\\');';\n\n      return preg_replace($search, $replace, $template);\n    }\n  }\n\n  /**\n   * @param template   $template\n   * @param array|bool $array\n   *\n   * @return mixed\n   */\n  public static function templateObjectParse($template, $array = false) {\n    global $user;\n\n    if (!empty($array) && is_array($array)) {\n      foreach ($array as $key => $data) {\n        $template->assign_var($key, $data);\n      }\n    }\n\n    $template->assign_vars(array(\n      'SN_TIME_NOW'      => SN_TIME_NOW,\n      'SN_TEMPLATE_NAME' => SnTemplate::getPlayerTemplateName(),\n      'USER_AUTHLEVEL'   => isset($user['authlevel']) ? $user['authlevel'] : -1,\n      'SN_GOOGLE'        => SN_GOOGLE,\n    ));\n\n    $template->parsed = true;\n\n    return $template;\n  }\n\n  /**\n   * @param array $menu\n   * @param array $extra\n   */\n  public static function tpl_menu_merge_extra(&$menu, &$extra) {\n    if (!is_array($extra) || !is_array($menu) || empty($menu)) {\n      return;\n    }\n\n    foreach ($extra as $menu_item_id => $menu_item) {\n      if (empty($menu_item['LOCATION'])) {\n        $menu[$menu_item_id] = $menu_item;\n        continue;\n      }\n\n      $item_location = $menu_item['LOCATION'];\n      unset($menu_item['LOCATION']);\n\n      $is_positioned = $item_location[0];\n      if ($is_positioned == '+' || $is_positioned == '-') {\n        $item_location = substr($item_location, 1);\n      } else {\n        $is_positioned = '';\n      }\n\n      if ($item_location) {\n        $menu_keys       = array_keys($menu);\n        $insert_position = array_search($item_location, $menu_keys);\n        if ($insert_position === false) {\n          $insert_position = count($menu) - 1;\n          $is_positioned   = '+';\n          $item_location   = '';\n        }\n      } else {\n        $insert_position = $is_positioned == '-' ? 0 : count($menu);\n      }\n\n      $insert_position     += $is_positioned == '+' ? 1 : 0;\n      $spliced             = array_splice($menu, $insert_position, count($menu) - $insert_position);\n      $menu[$menu_item_id] = $menu_item;\n\n      if (!$is_positioned && $item_location) {\n        unset($spliced[$item_location]);\n      }\n      $menu = array_merge($menu, $spliced);\n    }\n\n    $extra = array();\n  }\n\n  /**\n   * @param array $menu\n   *\n   * @return array\n   */\n  public static function tpl_menu_adminize($menu) {\n    !is_array($menu) ? $menu = [] : false;\n\n    foreach ($menu as &$menuItem) {\n      if (!isset($menuItem[MENU_FIELD_AUTH_LEVEL])) {\n        $menuItem[MENU_FIELD_AUTH_LEVEL] = AUTH_LEVEL_ADMINISTRATOR;\n      }\n    }\n\n    return $menu;\n  }\n\n  /**\n   * @param array    $sn_menu\n   * @param template $template\n   */\n  public static function tpl_menu_assign_to_template(&$sn_menu, &$template) {\n    global $lang;\n\n    if (empty($sn_menu) || !is_array($sn_menu)) {\n      return;\n    }\n\n    foreach ($sn_menu as $menu_item_id => $menu_item) {\n      if (!$menu_item) {\n        continue;\n      }\n\n      if (is_string($menu_item_id)) {\n        $menu_item['ID'] = $menu_item_id;\n      }\n\n      if ($menu_item['TYPE'] == 'lang') {\n        $lang_string = &$lang;\n        if (preg_match('#(\\w+)(?:\\[(\\w+)\\])?(?:\\[(\\w+)\\])?(?:\\[(\\w+)\\])?(?:\\[(\\w+)\\])?#', $menu_item['ITEM'], $matches) && count($matches) > 1) {\n          for ($i = 1; $i < count($matches); $i++) {\n            if (defined($matches[$i])) {\n              $matches[$i] = constant($matches[$i]);\n            }\n            $lang_string = &$lang_string[$matches[$i]];\n          }\n        }\n        $menu_item['ITEM'] = $lang_string && is_string($lang_string) ? $lang_string : \"{L_{$menu_item['ITEM']}}\";\n      }\n\n      $menu_item['ALT']   = htmlentities($menu_item['ALT']);\n      $menu_item['TITLE'] = htmlentities($menu_item['TITLE']);\n\n      if (!empty($menu_item['ICON'])) {\n        if (is_string($menu_item['ICON'])) {\n          $menu_item['ICON_PATH'] = $menu_item['ICON'];\n        } else {\n          $menu_item['ICON'] = $menu_item_id;\n        }\n      }\n\n      $template->assign_block_vars('menu', $menu_item);\n    }\n  }\n\n  /**\n   * @param template $template\n   *\n   * @return template\n   */\n  public static function tpl_render_menu($template) {\n    global $user, $lang, $template_result, $sn_menu_admin_extra, $sn_menu_admin, $sn_menu, $sn_menu_extra;\n\n    lng_include('admin');\n\n    $template->assign_vars(array(\n      'USER_AUTHLEVEL'      => $user['authlevel'],\n      'USER_AUTHLEVEL_NAME' => $lang['user_level'][$user['authlevel']],\n      'PAYMENT'             => SN::$gc->modules->countModulesInGroup('payment'),\n      'MENU_START_HIDE'     => !empty($_COOKIE[SN_COOKIE . '_menu_hidden']) || SN_GOOGLE,\n    ));\n\n    if (isset($template_result['MENU_CUSTOMIZE'])) {\n      $template->assign_vars(array(\n        'PLAYER_OPTION_MENU_SHOW_ON_BUTTON'   => SN::$user_options[PLAYER_OPTION_MENU_SHOW_ON_BUTTON],\n        'PLAYER_OPTION_MENU_HIDE_ON_BUTTON'   => SN::$user_options[PLAYER_OPTION_MENU_HIDE_ON_BUTTON],\n        'PLAYER_OPTION_MENU_HIDE_ON_LEAVE'    => SN::$user_options[PLAYER_OPTION_MENU_HIDE_ON_LEAVE],\n        'PLAYER_OPTION_MENU_UNPIN_ABSOLUTE'   => SN::$user_options[PLAYER_OPTION_MENU_UNPIN_ABSOLUTE],\n        'PLAYER_OPTION_MENU_ITEMS_AS_BUTTONS' => SN::$user_options[PLAYER_OPTION_MENU_ITEMS_AS_BUTTONS],\n        'PLAYER_OPTION_MENU_WHITE_TEXT'       => SN::$user_options[PLAYER_OPTION_MENU_WHITE_TEXT],\n        'PLAYER_OPTION_MENU_OLD'              => SN::$user_options[PLAYER_OPTION_MENU_OLD],\n        'PLAYER_OPTION_MENU_HIDE_SHOW_BUTTON' => empty($_COOKIE[SN_COOKIE . '_menu_hidden']) && !SN_GOOGLE\n          ? SN::$user_options[PLAYER_OPTION_MENU_HIDE_SHOW_BUTTON] : 1,\n      ));\n    }\n\n    if (defined('IN_ADMIN') && IN_ADMIN === true && !empty($user['authlevel']) && $user['authlevel'] > 0) {\n      SnTemplate::tpl_menu_merge_extra($sn_menu_admin, $sn_menu_admin_extra);\n      $sn_menu_admin = SnTemplate::tpl_menu_adminize($sn_menu_admin);\n      SnTemplate::tpl_menu_assign_to_template($sn_menu_admin, $template);\n    } else {\n      SnTemplate::tpl_menu_merge_extra($sn_menu, $sn_menu_extra);\n      SnTemplate::tpl_menu_assign_to_template($sn_menu, $template);\n    }\n\n    return $template;\n  }\n\n  /**\n   * @param template[] $page\n   * @param array      $template_result\n   */\n  public static function renderFooter($page, $template_result) {\n    $templateFooter = self::gettemplate('_page/_99_footer', true);\n\n    $templateFooter->assign_vars([\n      'SN_TIME_NOW'      => SN_TIME_NOW,\n      'SN_VERSION'       => SN_VERSION,\n      'ADMIN_EMAIL'      => SN::$config->game_adminEmail,\n      'CURRENT_YEAR'     => date('Y', SN_TIME_NOW),\n      'DB_PATCH_VERSION' => dbPatchGetCurrent(),\n    ]);\n\n    SnTemplate::displayP($templateFooter);\n  }\n\n  /**\n   * @param $page\n   * @param $title\n   * @param $template_result\n   * @param $inLoginLogout\n   * @param $user\n   * @param $config\n   * @param $lang\n   * @param $planetrow\n   */\n  public static function renderHeader($page, $title, &$template_result, $inLoginLogout, &$user, $config, $lang, $planetrow, $renderedContent) {\n    if (SN::$headerRendered) {\n      return;\n    }\n\n    ob_end_flush();\n\n    ob_start();\n//  pdump(microtime(true) - SN_TIME_MICRO, 'Header render started');\n    $isDisplayTopNav = true;\n    $isDisplayMenu   = true;\n\n    isset($template_result['GLOBAL_DISPLAY_MENU']) ? $isDisplayMenu = $template_result['GLOBAL_DISPLAY_MENU'] : false;\n    isset($template_result['GLOBAL_DISPLAY_NAVBAR']) ? $isDisplayTopNav = $template_result['GLOBAL_DISPLAY_NAVBAR'] : false;\n\n    // TODO: DEPRECATED! Use $template_result to turn menu and navbar or ond off!\n    if (is_object($page)) {\n      isset($page->_rootref['MENU']) ? $isDisplayMenu = $page->_rootref['MENU'] : false;\n      isset($page->_rootref['NAVBAR']) ? $isDisplayTopNav = $page->_rootref['NAVBAR'] : false;\n    }\n\n    $inAdmin         = defined('IN_ADMIN') && IN_ADMIN === true;\n    $isDisplayMenu   = ($isDisplayMenu || $inAdmin) && !isset($_COOKIE['menu_disable']);\n    $isDisplayTopNav = $isDisplayTopNav && !$inAdmin;\n\n    if ($inLoginLogout || empty($user['id']) || !is_numeric($user['id'])) {\n      $isDisplayMenu   = false;\n      $isDisplayTopNav = false;\n    }\n\n    $template = self::gettemplate('_page/_00_header', true);\n    $template->assign_vars([\n      'SN_TIME_NOW'      => SN_TIME_NOW,\n      'SN_VERSION'       => SN_VERSION,\n      'ADMIN_EMAIL'      => SN::$config->game_adminEmail,\n      'CURRENT_YEAR'     => date('Y', SN_TIME_NOW),\n      'DB_PATCH_VERSION' => dbPatchGetCurrent(),\n    ]);\n\n    self::renderJavaScript();\n\n    self::renderCss($inLoginLogout);\n\n    $template->assign_vars(array(\n      self::P_CONTENT => $renderedContent,\n\n      'LANG_LANGUAGE'  => $lang['LANG_INFO']['LANG_NAME_ISO2'],\n      'LANG_ENCODING'  => 'utf-8',\n      'LANG_DIRECTION' => $lang['LANG_INFO']['LANG_DIRECTION'],\n\n      'SN_ROOT_VIRTUAL' => SN_ROOT_VIRTUAL,\n\n      'ADV_SEO_META_DESCRIPTION' => $config->adv_seo_meta_description,\n      'ADV_SEO_META_KEYWORDS'    => $config->adv_seo_meta_keywords,\n\n      // WARNING! This can be set by page!\n      // CHANGE CODE TO MAKE IT IMPOSSIBLE!\n      'GLOBAL_META_TAGS'         => isset($page->_rootref['GLOBAL_META_TAGS']) ? $page->_rootref['GLOBAL_META_TAGS'] : '',\n\n      'IN_ADMIN' => $inAdmin ? 1 : '',\n    ));\n\n    $template->assign_vars(array(\n      'GLOBAL_DISPLAY_MENU'   => $isDisplayMenu,\n      'GLOBAL_DISPLAY_NAVBAR' => $isDisplayTopNav,\n\n      'USER_AUTHLEVEL' => intval($user['authlevel']),\n\n      'FONT_SIZE'                        => self::playerFontSize(),\n      'FONT_SIZE_PERCENT_DEFAULT_STRING' => FONT_SIZE_PERCENT_DEFAULT_STRING,\n\n      'SN_TIME_NOW'          => SN_TIME_NOW,\n      'LOGIN_LOGOUT'         => $template_result['LOGIN_LOGOUT'],\n      'GAME_MODE_CSS_PREFIX' => $config->game_mode == GAME_BLITZ ? 'blitz_' : '',\n      'TIME_DIFF_MEASURE'    => playerTimeDiff::timeDiffTemplate(), // Проводить замер только если не выставлен флаг форсированного замера И (иссяк интервал замера ИЛИ замера еще не было)\n\n      'title'              => ($title ? \"{$title} - \" : '') . \"{$lang['sys_server']} {$config->game_name} - {$lang['sys_supernova']}\",\n      'ADV_SEO_JAVASCRIPT' => $config->adv_seo_javascript,\n\n      'SOUND_ENABLED'                        => SN::$user_options[PLAYER_OPTION_SOUND_ENABLED],\n      'PLAYER_OPTION_ANIMATION_DISABLED'     => SN::$user_options[PLAYER_OPTION_ANIMATION_DISABLED],\n      'PLAYER_OPTION_PROGRESS_BARS_DISABLED' => SN::$user_options[PLAYER_OPTION_PROGRESS_BARS_DISABLED],\n\n      'IMPERSONATING'                        => !empty($template_result[F_IMPERSONATE_STATUS]) ? sprintf($lang['sys_impersonated_as'], $user['username'], $template_result[F_IMPERSONATE_OPERATOR]) : '',\n      'PLAYER_OPTION_DESIGN_DISABLE_BORDERS' => SN::$user_options[PLAYER_OPTION_DESIGN_DISABLE_BORDERS],\n\n      'WEBP_SUPPORT_NEED_CHECK' => ($webpSupported = SN::$gc->theUser->isWebpSupported()) === null ? 1 : 0,\n      'WEBP_SUPPORTED'          => $webpSupported ? 1 : 0,\n    ));\n    $template->assign_recursive($template_result);\n\n    if ($isDisplayMenu) {\n      SnTemplate::tpl_render_menu($template);\n    }\n\n    if ($isDisplayTopNav) {\n      SN::$gc->pimp->tpl_render_topnav($user, $planetrow, $template);\n    }\n\n    SnTemplate::displayP($template);\n    ob_end_flush();\n\n    SN::$headerRendered = true;\n\n    ob_start();\n  }\n\n  /**\n   */\n  public static function renderJavaScript() {\n    global $sn_mvc, $sn_page_name, $template_result;\n\n    empty($sn_mvc['javascript']) ? $sn_mvc['javascript'] = ['' => []] : false;\n\n    $standard_js = self::addFileName([\n      'js/lib/jquery',\n      'js/lib/js.cookie',\n      'js/lib/jquery-ui',\n      'js/lib/jquery.ui.touch-punch',\n      'js/lib/ion.sound',\n      'js/sn_global',\n      'js/sn_sound',\n      'js/sn_timer',\n    ], [], '.js');\n\n    $standard_js = self::addFileName(!empty($sn_mvc['javascript_filenames']) ? $sn_mvc['javascript_filenames'] : [], $standard_js, '.js');\n\n    $standard_js = self::cacheFiles($standard_js, '.js');\n\n    // Prepending standard JS files\n    $sn_mvc['javascript'][''] = array_merge($standard_js, $sn_mvc['javascript'][''] ?? []);\n\n    self::renderFileListInclude($template_result, $sn_mvc, $sn_page_name, 'javascript');\n  }\n\n  /**\n   * @param $is_login\n   */\n  public static function renderCss($is_login) {\n    global $sn_mvc, $sn_page_name, $template_result;\n\n    empty($sn_mvc['css']) ? $sn_mvc['css'] = ['' => []] : false;\n\n    $standard_css = [];\n    $standard_css = self::addFileName('design/css/jquery-ui', $standard_css);\n    $standard_css = self::addFileName('design/css/global', $standard_css);\n    $is_login ? $standard_css = self::addFileName('design/css/login', $standard_css) : false;\n    $standard_css = self::addFileName('design/css/menu_icons', $standard_css);\n\n    $standard_css = self::getCurrentTemplate()->cssAddFileName('_template', $standard_css);\n\n    $standard_css = self::addFileName(SN::$gc->theUser->getSkinPath() . 'skin', $standard_css);\n    $standard_css = self::addFileName('design/css/global_override', $standard_css);\n\n    // Trying to add extra CSS to cache file\n    foreach ($sn_mvc['css'][''] as $css => $cork) {\n      $cssOriginal = $css;\n\n      // At first - removing minimization flag from file name\n      if(($rPos = strrpos($css, '.min.css')) !== false) {\n        // 4 - string length of substring to cut `.min`\n        $css = substr_replace($css, '', $rPos, 4);\n      }\n      // Also we don't need `.css` extension\n      if(($rPos = strrpos($css, '.css')) !== false) {\n        // 4 - string length of substring to cut `.css`\n        $css = substr_replace($css, '', $rPos, 4);\n      }\n\n      // Memorizing size of css filename array\n      $cssWas = count($standard_css);\n      // Trying to add newly found filename\n      $standard_css = self::addFileName($css, $standard_css);\n      if(count($standard_css) > $cssWas) {\n        // Removing file from extra CSS list only if everything went well and records was added to list of CSS to cache\n        // Otherwise something went wrong - so we don't touch this\n        unset($sn_mvc['css'][''][$cssOriginal]);\n      }\n    }\n\n    // Trying to cache CSS files\n    $standard_css = self::cacheFiles($standard_css);\n\n    // Prepending standard CSS files\n    $sn_mvc['css'][''] = array_merge($standard_css, $sn_mvc['css']['']);\n\n    self::renderFileListInclude($template_result, $sn_mvc, $sn_page_name, 'css');\n  }\n\n  /**\n   * @param $time\n   * @param $event\n   * @param $msg\n   * @param $prefix\n   * @param $is_decrease\n   * @param $fleet_flying_row\n   * @param $fleet_flying_sorter\n   * @param $fleet_flying_events\n   * @param $fleet_event_count\n   */\n  public static function tpl_topnav_event_build_helper($time, $event, $msg, $prefix, $is_decrease, $fleet_flying_row, &$fleet_flying_sorter, &$fleet_flying_events, &$fleet_event_count) {\n    $fleet_flying_sorter[$fleet_event_count] = $time;\n    $fleet_flying_events[$fleet_event_count] = array(\n      'ROW'              => $fleet_flying_row,\n      'FLEET_ID'         => $fleet_flying_row['fleet_id'],\n      'EVENT'            => $event,\n      'COORDINATES'      => uni_render_coordinates($fleet_flying_row, $prefix),\n      'COORDINATES_TYPE' => $fleet_flying_row[\"{$prefix}type\"],\n      'TEXT'             => \"{$msg}\",\n      'DECREASE'         => $is_decrease,\n    );\n    $fleet_event_count++;\n  }\n\n  /**\n   * @param template $template\n   * @param array    $fleet_flying_list\n   * @param string   $type\n   */\n  public static function tpl_topnav_event_build(&$template, $fleet_flying_list, $type = 'fleet') {\n    if (empty($fleet_flying_list)) {\n      return;\n    }\n\n    global $lang;\n\n    $fleet_event_count   = 0;\n    $fleet_flying_sorter = array();\n    $fleet_flying_events = array();\n    foreach ($fleet_flying_list as &$fleet_flying_row) {\n      $will_return = true;\n      if ($fleet_flying_row['fleet_mess'] == 0) {\n        // cut fleets on Hold and Expedition\n        if ($fleet_flying_row['fleet_start_time'] >= SN_TIME_NOW) {\n          $fleet_flying_row['fleet_mission'] == MT_RELOCATE ? $will_return = false : false;\n          SnTemplate::tpl_topnav_event_build_helper($fleet_flying_row['fleet_start_time'], EVENT_FLEET_ARRIVE, $lang['sys_event_arrive'], 'fleet_end_', !$will_return, $fleet_flying_row, $fleet_flying_sorter, $fleet_flying_events, $fleet_event_count);\n        }\n        if ($fleet_flying_row['fleet_end_stay']) {\n          SnTemplate::tpl_topnav_event_build_helper($fleet_flying_row['fleet_end_stay'], EVENT_FLEET_STAY, $lang['sys_event_stay'], 'fleet_end_', false, $fleet_flying_row, $fleet_flying_sorter, $fleet_flying_events, $fleet_event_count);\n        }\n      }\n      if ($will_return) {\n        SnTemplate::tpl_topnav_event_build_helper($fleet_flying_row['fleet_end_time'], EVENT_FLEET_RETURN, $lang['sys_event_return'], 'fleet_start_', true, $fleet_flying_row, $fleet_flying_sorter, $fleet_flying_events, $fleet_event_count);\n      }\n    }\n\n    asort($fleet_flying_sorter);\n\n    $fleet_flying_count = count($fleet_flying_list);\n    foreach ($fleet_flying_sorter as $fleet_event_id => $fleet_time) {\n      $fleet_event = &$fleet_flying_events[$fleet_event_id];\n      $template->assign_block_vars(\"flying_{$type}s\", array(\n        'TIME' => max(0, $fleet_time - SN_TIME_NOW),\n        'TEXT' => $fleet_flying_count,\n        'HINT' => date(FMT_DATE_TIME, $fleet_time + SN_CLIENT_TIME_DIFF) . \" - {$lang['sys_fleet']} {$fleet_event['TEXT']} {$fleet_event['COORDINATES']} {$lang['sys_planet_type_sh'][$fleet_event['COORDINATES_TYPE']]} {$lang['type_mission'][$fleet_event['ROW']['fleet_mission']]}\",\n      ));\n      $fleet_event['DECREASE'] ? $fleet_flying_count-- : false;\n    }\n  }\n\n  /**\n   * @return mixed|string\n   */\n  public static function playerFontSize() {\n    $font_size = !empty($_COOKIE[SN_COOKIE_F]) ? $_COOKIE[SN_COOKIE_F] : SN::$user_options[PLAYER_OPTION_BASE_FONT_SIZE];\n    if (strpos($font_size, '%') !== false) {\n      // Размер шрифта в процентах\n      $font_size = min(max(floatval($font_size), FONT_SIZE_PERCENT_MIN), FONT_SIZE_PERCENT_MAX) . '%';\n\n      return $font_size;\n    } elseif (strpos($font_size, 'px') !== false) {\n      // Размер шрифта в пикселях\n      $font_size = min(max(floatval($font_size), FONT_SIZE_PIXELS_MIN), FONT_SIZE_PIXELS_MAX) . 'px';\n\n      return $font_size;\n    } else {\n      // Не мышонка, не лягушка...\n      $font_size = FONT_SIZE_PERCENT_DEFAULT_STRING;\n\n      return $font_size;\n    }\n  }\n\n  /**\n   * Checks if minified/full-size file variant exists - and adds it if any\n   *\n   * @param string|string[] $fileNames\n   * @param array  $anArray\n   *\n   * @return array\n   */\n  public static function addFileName($fileNames, $anArray, $ext = '.css') {\n    if(!is_array($fileNames)) {\n      $fileNames = [$fileNames];\n    }\n\n    foreach($fileNames as $fileName) {\n      if (file_exists(SN_ROOT_PHYSICAL . $fileName . '.min' . $ext)) {\n        $anArray[$fileName . '.min' . $ext] = '';\n      } elseif (file_exists(SN_ROOT_PHYSICAL . $fileName . '.css' . $ext)) {\n        $anArray[$fileName . $ext] = '';\n      }\n    }\n\n    return $anArray;\n  }\n\n  /**\n   * @param array   $template_result\n   * @param array[] $sn_mvc\n   * @param string  $sn_page_name\n   * @param string  $fileType - 'css' or 'javascript'\n   */\n  public static function renderFileListInclude(&$template_result, &$sn_mvc, $sn_page_name, $fileType) {\n    if (empty($sn_mvc[$fileType])) {\n      return;\n    }\n\n    foreach ($sn_mvc[$fileType] as $page_name => $script_list) {\n      if (empty($page_name) || $page_name == $sn_page_name) {\n        foreach ($script_list as $filename => $content) {\n          $template_result['.'][$fileType][] = array(\n            'FILE'    => $filename,\n            'CONTENT' => $content,\n          );\n        }\n      }\n    }\n  }\n\n  /**\n   * @param $template\n   * @param $user\n   */\n  public static function tpl_navbar_render_notes(&$template, &$user) {\n    $notes_query = doquery(\"SELECT * FROM {{notes}} WHERE `owner` = {$user['id']} AND `sticky` = 1 ORDER BY priority DESC, time DESC\");\n    while ($note_row = db_fetch($notes_query)) {\n      Note::note_assign($template, $note_row);\n    }\n  }\n\n  /**\n   * @param             $template\n   * @param             $user\n   * @param classConfig $config\n   */\n  public static function tpl_navbar_render_news(&$template, &$user, $config) {\n    if ($config->game_news_overview) {\n      $user_last_read_safe     = intval($user['news_lastread']);\n      $newsSql                 = \"AND UNIX_TIMESTAMP(`tsTimeStamp`) >= {$user_last_read_safe} \";\n      $newsOverviewShowSeconds = intval($config->game_news_overview_show);\n      if ($newsOverviewShowSeconds) {\n        $newsSql .= \"AND `tsTimeStamp` >= DATE_SUB(NOW(), INTERVAL {$newsOverviewShowSeconds} SECOND) \";\n      }\n      nws_render($user, $template, $newsSql, $config->game_news_overview);\n    }\n  }\n\n  /**\n   * @param array  $sn_mvc\n   * @param string $blockName\n   *\n   * @return array|false\n   */\n  public static function render_button_block(&$sn_mvc, $blockName) {\n    $result = false;\n\n    if (!empty($sn_mvc[$blockName]) && is_array($sn_mvc[$blockName])) {\n      foreach ($sn_mvc[$blockName] as $navbar_button_image => $navbar_button_url) {\n        $result[] = array(\n          'IMAGE'        => $navbar_button_image,\n          'URL_RELATIVE' => $navbar_button_url,\n        );\n      }\n\n      $result = array(\n        '.' => array(\n          $blockName =>\n            $result\n        ),\n      );\n    }\n\n    return $result;\n  }\n\n  /**\n   * @param array    $sn_mvc\n   * @param template $template\n   */\n  public static function tpl_navbar_extra_buttons(&$sn_mvc, $template) {\n    ($block = SnTemplate::render_button_block($sn_mvc, 'navbar_prefix_button')) ? $template->assign_recursive($block) : false;\n    ($block = SnTemplate::render_button_block($sn_mvc, 'navbar_main_button')) ? $template->assign_recursive($block) : false;\n  }\n\n  /**\n   * @param template|string $template\n   *\n   * @return false|string|null\n   */\n  public static function templateRenderToHtml($template) {\n    $output = null;\n\n    ob_start();\n    SnTemplate::displayP($template);\n    $output = ob_get_contents();\n    ob_end_clean();\n\n    return $output;\n  }\n\n  /**\n   * @param template $template\n   */\n  public static function tpl_login_lang(&$template) {\n    global $language;\n\n    $url_params = array();\n\n    $language ? $url_params[] = \"lang={$language}\" : false;\n\n    ($id_ref = sys_get_param_id('id_ref')) ? $url_params[] = \"id_ref={$id_ref}\" : false;\n\n    $template->assign_vars($q = array(\n      'LANG'     => $language ? $language : '',\n      'referral' => $id_ref ? '&id_ref=' . $id_ref : '',\n\n      'REQUEST_PARAMS' => !empty($url_params) ? '?' . implode('&', $url_params) : '',// \"?lang={$language}\" . ($id_ref ? \"&id_ref={$id_ref}\" : ''),\n      'FILENAME'       => basename($_SERVER['PHP_SELF']),\n    ));\n\n    foreach (lng_get_list() as $lng_id => $lng_data) {\n      if (isset($lng_data['LANG_VARIANTS']) && is_array($lng_data['LANG_VARIANTS'])) {\n        foreach ($lng_data['LANG_VARIANTS'] as $lang_variant) {\n          $lng_data1 = $lng_data;\n          $lng_data1 = array_merge($lng_data1, $lang_variant);\n          $template->assign_block_vars('language', $lng_data1);\n        }\n      } else {\n        $template->assign_block_vars('language', $lng_data);\n      }\n    }\n  }\n\n  /**\n   * @param template $template\n   * @param string   $blockName\n   * @param mixed    $values\n   * @param string   $keyName   - Name for key name\n   * @param string   $valueName - Name for value name\n   */\n  public static function tpl_assign_select(&$template, $blockName, $values, $keyName = 'KEY', $valueName = 'VALUE') {\n    !is_array($values) ? $values = array($values => $values) : false;\n\n    foreach ($values as $key => $value) {\n      $template->assign_block_vars($blockName, array(\n        $keyName   => HelperString::htmlSafe($key),\n        $valueName => HelperString::htmlSafe($value),\n      ));\n    }\n  }\n\n  /**\n   * Renders unit bonus from unit data\n   *\n   * @param array $unitInfo\n   *\n   * @return string\n   */\n  public static function tpl_render_unit_bonus_data($unitInfo) {\n    $strBonus = self::tplAddPlus($unitInfo[P_BONUS_VALUE]);\n    switch ($unitInfo[P_BONUS_TYPE]) {\n      case BONUS_PERCENT:\n        $strBonus = \"{$strBonus}% \";\n      break;\n\n      case BONUS_ABILITY:\n        $strBonus = '';\n      break;\n\n      case BONUS_ADD:\n      default:\n      break;\n    }\n\n    return $strBonus;\n  }\n\n  /**\n   * Converts number to string then adds \"+\" sign for positive AND ZERO numbers\n   *\n   * @param float $value\n   *\n   * @return string\n   */\n  public static function tplAddPlus($value) {\n    return ($value >= 0 ? '+' : '') . $value;\n  }\n\n  /**\n   * Convert number to prettified string then adds \"+\" sign for positive AND ZERO numbers\n   *\n   * @param float $value\n   *\n   * @return string\n   */\n  public static function tplPrettyPlus($value) {\n    return ($value >= 0 ? '+' : '') . HelperString::numberFloorAndFormat($value);\n  }\n\n  /**\n   * Add message to result box\n   *\n   * If $template specified - message would be added to template supplied. Otherwise - to $template_result\n   *\n   * @param string $message\n   * @param int    $status\n   * @param null   $template\n   */\n  public static function tplAddResult($message, $status = ERR_NONE, $template = null) {\n    global $template_result;\n\n    $block = [\n      'STATUS'  => $status,\n      'MESSAGE' => $message,\n    ];\n\n    if ($template instanceof template) {\n      $template->assign_block_vars('result', $block);\n    } else {\n      $template_result['.']['result'][] = $block;\n    }\n  }\n\n  /**\n   * @param string    $message\n   * @param string    $title\n   * @param string    $redirectTo\n   * @param int       $timeout\n   * @param bool|true $showHeader\n   */\n  public static function messageBox($message, $title = '', $redirectTo = '', $timeout = 5, $showHeader = true) {\n    global $lang, $template_result;\n\n    if (empty($title)) {\n      $title = $lang['sys_error'];\n    }\n\n    $template = self::gettemplate('message_body', true);\n\n    $template_result['GLOBAL_DISPLAY_NAVBAR'] = $showHeader;\n\n    $template->assign_vars(array(\n//    'GLOBAL_DISPLAY_NAVBAR' => $showHeader,\n\n      'TITLE'       => $title,\n      'MESSAGE'     => $message,\n      'REDIRECT_TO' => $redirectTo,\n      'TIMEOUT'     => $timeout,\n    ));\n\n    self::display($template, $title);\n  }\n\n  /**\n   * Admin message box\n   *\n   * @param        $message\n   * @param string $title\n   * @param string $redirectTo\n   * @param int    $timeout\n   *\n   * @see SnTemplate::messageBox()\n   */\n  public static function messageBoxAdmin($message, $title = '', $redirectTo = '', $timeout = 5) {\n    SnTemplate::messageBox($message, $title, $redirectTo, $timeout, false);\n  }\n\n  public static function messageBoxAdminAccessDenied($level = AUTH_LEVEL_ADMINISTRATOR) {\n    global $user, $lang;\n\n    if ($user['authlevel'] < $level) {\n      SnTemplate::messageBoxAdmin($lang['adm_err_denied'], $lang['admin_title_access_denied'], SN_ROOT_VIRTUAL . 'overview.php');\n    }\n  }\n\n  /**\n   * @param          $prevUser\n   * @param array    $user\n   * @param array    $planetrow\n   * @param template $template\n   *\n   * @return array\n   */\n  public static function sn_tpl_render_topnav(&$prevUser, $user, $planetrow, $template) {\n    global $lang, $config, $sn_mvc;\n\n    // This call was not first one... Using results from previous call\n    if (!empty($prevUser['username'])) {\n      $user = $prevUser;\n    }\n\n    if (!is_array($user)) {\n      return $user;\n    }\n\n    $GET_mode = sys_get_param_str('mode');\n\n    $ThisUsersPlanets = DBStaticPlanet::db_planet_list_sorted($user);\n    foreach ($ThisUsersPlanets as $CurPlanet) {\n      if ($CurPlanet['destruyed']) {\n        continue;\n      }\n\n      $fleet_listx = flt_get_fleets_to_planet($CurPlanet);\n      if ($CurPlanet['planet_type'] == PT_MOON) {\n        $parentPlanet = DBStaticPlanet::db_planet_by_id($CurPlanet['parent_planet']);\n      } else {\n        $parentPlanet = $CurPlanet;\n      }\n\n      $template->assign_block_vars('topnav_planets', [\n        'ID'          => $CurPlanet['id'],\n        'NAME'        => $CurPlanet['name'],\n        'TYPE'        => $CurPlanet['planet_type'],\n        'TYPE_TEXT'   => $lang['sys_planet_type_sh'][$CurPlanet['planet_type']],\n        'IS_CAPITAL'  => $parentPlanet['id'] == $user['id_planet'],\n        'IS_MOON'     => $CurPlanet['planet_type'] == PT_MOON,\n        'PLIMAGE'     => $CurPlanet['image'],\n        'FLEET_ENEMY' => $fleet_listx['enemy']['count'],\n        'COORDS'      => uni_render_coordinates($CurPlanet),\n        'SELECTED'    => $CurPlanet['id'] == $user['current_planet'] ? ' selected' : '',\n      ]);\n    }\n\n    $fleet_flying_list = DbFleetStatic::tpl_get_fleets_flying($user);\n    SnTemplate::tpl_topnav_event_build($template, $fleet_flying_list[0]);\n    SnTemplate::tpl_topnav_event_build($template, $fleet_flying_list[MT_EXPLORE], 'expedition');\n\n    que_tpl_parse($template, QUE_STRUCTURES, $user, $planetrow, null, true);\n    que_tpl_parse($template, QUE_RESEARCH, $user, array(), null, !SN::$user_options[PLAYER_OPTION_NAVBAR_RESEARCH_WIDE]);\n    que_tpl_parse($template, SUBQUE_FLEET, $user, $planetrow, null, true);\n    que_tpl_parse($template, SUBQUE_DEFENSE, $user, $planetrow, null, true);\n\n    SnTemplate::tpl_navbar_extra_buttons($sn_mvc, $template);\n    SnTemplate::tpl_navbar_render_news($template, $user, $config);\n    SnTemplate::tpl_navbar_render_notes($template, $user);\n    $tutorial_enabled = PageTutorial::renderNavBar($template);\n\n\n    $premium_lvl = mrc_get_level($user, false, UNIT_PREMIUM, true, true);\n\n    $str_date_format   = \"%3$02d %2$0s %1$04d {$lang['top_of_year']} %4$02d:%5$02d:%6$02d\";\n    $time_now_parsed   = getdate(SN_TIME_NOW);\n    $time_local_parsed = getdate(defined('SN_CLIENT_TIME_LOCAL') ? SN_CLIENT_TIME_LOCAL : SN_TIME_NOW);\n\n    $template->assign_vars(array(\n      'QUE_ID'   => QUE_RESEARCH,\n      'QUE_HTML' => 'topnav',\n\n      'RESEARCH_ONGOING' => (boolean)$user['que'],\n\n      'TIME_TEXT'       => sprintf($str_date_format, $time_now_parsed['year'], $lang['months'][$time_now_parsed['mon']], $time_now_parsed['mday'],\n        $time_now_parsed['hours'], $time_now_parsed['minutes'], $time_now_parsed['seconds']\n      ),\n      'TIME_TEXT_LOCAL' => sprintf($str_date_format, $time_local_parsed['year'], $lang['months'][$time_local_parsed['mon']], $time_local_parsed['mday'],\n        $time_local_parsed['hours'], $time_local_parsed['minutes'], $time_local_parsed['seconds']\n      ),\n\n      'GAME_BLITZ_REGISTER'             => $config->game_blitz_register,\n      'GAME_BLITZ_REGISTER_TEXT'        => $lang['sys_blitz_registration_mode_list'][$config->game_blitz_register],\n      'BLITZ_REGISTER_OPEN'             => $config->game_blitz_register == BLITZ_REGISTER_OPEN,\n      'BLITZ_REGISTER_CLOSED'           => $config->game_blitz_register == BLITZ_REGISTER_CLOSED,\n      'BLITZ_REGISTER_SHOW_LOGIN'       => $config->game_blitz_register == BLITZ_REGISTER_SHOW_LOGIN,\n      'BLITZ_REGISTER_DISCLOSURE_NAMES' => $config->game_blitz_register == BLITZ_REGISTER_DISCLOSURE_NAMES,\n      'GAME_BLITZ'                      => $config->game_mode == GAME_BLITZ,\n\n      'USERS_ONLINE'  => $config->var_online_user_count,\n      'USERS_TOTAL'   => $config->users_amount,\n      'USER_RANK'     => $user['total_rank'],\n      'USER_NICK'     => $user['username'],\n      'USER_AVATAR'   => $user['avatar'],\n      'USER_AVATARID' => $user['id'],\n      'USER_PREMIUM'  => $premium_lvl,\n      'USER_RACE'     => $user[P_RACE],\n\n      'TOPNAV_CURRENT_PLANET'       => $user['current_planet'],\n      'TOPNAV_CURRENT_PLANET_NAME'  => uni_render_planet_full($planetrow), // htmlspecialchars($planetrow['name']),\n      'TOPNAV_CURRENT_PLANET_IMAGE' => $planetrow['image'],\n      'TOPNAV_COLONIES_CURRENT'     => get_player_current_colonies($user),\n      'TOPNAV_COLONIES_MAX'         => get_player_max_colonies($user),\n      'NAVBAR_MODE'                 => $GET_mode,\n\n      'TOPNAV_DARK_MATTER'            => mrc_get_level($user, '', RES_DARK_MATTER),\n      'TOPNAV_DARK_MATTER_TEXT'       => HelperString::numberFloorAndFormat(mrc_get_level($user, '', RES_DARK_MATTER)),\n      'TOPNAV_DARK_MATTER_PLAIN'      => mrc_get_level($user, '', RES_DARK_MATTER, false, true),\n      'TOPNAV_DARK_MATTER_PLAIN_TEXT' => HelperString::numberFloorAndFormat(mrc_get_level($user, '', RES_DARK_MATTER, false, true)),\n      'TOPNAV_METAMATTER'             => mrc_get_level($user, '', RES_METAMATTER),\n      'TOPNAV_METAMATTER_TEXT'        => HelperString::numberFloorAndFormat(mrc_get_level($user, '', RES_METAMATTER)),\n\n      // TODO ГРЯЗНЫЙ ХАК!!!\n      'TOPNAV_PAYMENT'                => SN::$gc->modules->countModulesInGroup('payment') && !SN_GOOGLE,\n\n      'TOPNAV_MESSAGES_ADMIN'    => $user['msg_admin'],\n      'TOPNAV_MESSAGES_PLAYER'   => $user['mnl_joueur'],\n      'TOPNAV_MESSAGES_ALLIANCE' => $user['mnl_alliance'],\n      'TOPNAV_MESSAGES_ATTACK'   => $user['mnl_attaque'],\n      'TOPNAV_MESSAGES_ALL'      => $user['new_message'],\n\n      'TOPNAV_FLEETS_FLYING'      => empty($fleet_flying_list[0]) ? 0 : count($fleet_flying_list[0]),\n      'TOPNAV_FLEETS_TOTAL'       => GetMaxFleets($user),\n      'TOPNAV_EXPEDITIONS_FLYING' => empty($fleet_flying_list[MT_EXPLORE]) ? 0 : count($fleet_flying_list[MT_EXPLORE]),\n      'TOPNAV_EXPEDITIONS_TOTAL'  => get_player_max_expeditons($user),\n\n      'TOPNAV_QUEST_COMPLETE'    => get_quest_amount_complete($user['id']),\n      'TOPNAV_QUEST_IN_PROGRESS' => get_quest_amount_in_progress($user['id']),\n\n      'GAME_NEWS_OVERVIEW'       => $config->game_news_overview,\n      'GAME_RESEARCH_DISABLED'   => defined('GAME_RESEARCH_DISABLED') && GAME_RESEARCH_DISABLED,\n      'GAME_DEFENSE_DISABLED'    => defined('GAME_DEFENSE_DISABLED') && GAME_DEFENSE_DISABLED,\n      'GAME_STRUCTURES_DISABLED' => defined('GAME_STRUCTURES_DISABLED') && GAME_STRUCTURES_DISABLED,\n      'GAME_HANGAR_DISABLED'     => defined('GAME_HANGAR_DISABLED') && GAME_HANGAR_DISABLED,\n\n      'PLAYER_OPTION_NAVBAR_PLANET_VERTICAL'        => SN::$user_options[PLAYER_OPTION_NAVBAR_PLANET_VERTICAL],\n      'PLAYER_OPTION_NAVBAR_PLANET_OLD'             => SN::$user_options[PLAYER_OPTION_NAVBAR_PLANET_OLD],\n      'PLAYER_OPTION_NAVBAR_PLANET_DISABLE_STORAGE' => SN::$user_options[PLAYER_OPTION_NAVBAR_PLANET_DISABLE_STORAGE],\n      'PLAYER_OPTION_NAVBAR_DISABLE_RESEARCH'       => SN::$user_options[PLAYER_OPTION_NAVBAR_DISABLE_RESEARCH],\n      'PLAYER_OPTION_NAVBAR_DISABLE_PLANET'         => SN::$user_options[PLAYER_OPTION_NAVBAR_DISABLE_PLANET],\n      'PLAYER_OPTION_NAVBAR_DISABLE_HANGAR'         => SN::$user_options[PLAYER_OPTION_NAVBAR_DISABLE_HANGAR],\n      'PLAYER_OPTION_NAVBAR_DISABLE_DEFENSE'        => SN::$user_options[PLAYER_OPTION_NAVBAR_DISABLE_DEFENSE],\n      'PLAYER_OPTION_NAVBAR_DISABLE_FLYING_FLEETS'  => SN::$user_options[PLAYER_OPTION_NAVBAR_DISABLE_FLYING_FLEETS],\n      'PLAYER_OPTION_NAVBAR_DISABLE_EXPEDITIONS'    => SN::$user_options[PLAYER_OPTION_NAVBAR_DISABLE_EXPEDITIONS],\n      'PLAYER_OPTION_NAVBAR_DISABLE_QUESTS'         => SN::$user_options[PLAYER_OPTION_NAVBAR_DISABLE_QUESTS],\n      'PLAYER_OPTION_NAVBAR_DISABLE_META_MATTER'    => SN::$user_options[PLAYER_OPTION_NAVBAR_DISABLE_META_MATTER],\n      'PLAYER_OPTION_NAVBAR_RESEARCH_WIDE'          => SN::$user_options[PLAYER_OPTION_NAVBAR_RESEARCH_WIDE],\n\n      'TUTORIAL_ENABLED' => $tutorial_enabled,\n\n      'PT_MOON'        => PT_MOON,\n      'SUBQUE_FLEET'   => SUBQUE_FLEET,\n      'SUBQUE_DEFENSE' => SUBQUE_DEFENSE,\n      'QUE_RESEARCH'   => QUE_RESEARCH,\n      'QUE_STRUCTURES' => QUE_STRUCTURES,\n    ));\n\n    if (\n      (defined('SN_RENDER_NAVBAR_PLANET') && SN_RENDER_NAVBAR_PLANET === true)\n      ||\n      ($user['option_list'][OPT_INTERFACE]['opt_int_navbar_resource_force'] && (!defined('SN_RENDER_NAVBAR_PLANET') || SN_RENDER_NAVBAR_PLANET !== false))\n    ) {\n      tpl_set_resource_info($template, $planetrow);\n      $template->assign_vars(array(\n        'SN_RENDER_NAVBAR_PLANET' => true,\n        'SN_NAVBAR_HIDE_FLEETS'   => true,\n      ));\n    }\n\n    return $user;\n  }\n\n  /**\n   * @param array|string  $files\n   * @param template|null $template\n   * @param string|null   $template_path - path to templates root\n   *\n   * @return template\n   */\n  public static function gettemplate($files, $template = null, $template_path = null, $fallBackPath = '') {\n    global $sn_mvc, $sn_page_name;\n\n    $template = self::getCurrentTemplate()->getTemplate($template, $template_path, $fallBackPath);\n\n    // TODO ГРЯЗНЫЙ ХАК! Это нужно, что бы по возможности перезаписать инфу из языковых пакетов модулей там, где она была перезаписана раньше инфой из основного пакета. Почему?\n    //  - сначала грузятся модули и их языковые пакеты\n    //  - затем по ходу дела ОСНОВНОЙ языковой пакет может перезаписать данные из МОДУЛЬНОГО языкового пакета\n    // Поэтому и нужен этот грязный хак\n    // В норме же - страницы заявляют сами, какие им пакеты нужны. Так что сначала всегда должны грузится основные языковые пакеты, а уже ПОВЕРХ них - пакеты модулей\n    !empty($sn_mvc['i18n']['']) ? lng_load_i18n($sn_mvc['i18n']['']) : false;\n    $sn_page_name ? lng_load_i18n($sn_mvc['i18n'][$sn_page_name]) : false;\n\n    if (empty($files)) {\n      // Make sure that all empty files will translate to empty array\n      $files = [];\n    } elseif (is_string($files)) {\n      // If we have single filename - making array from it\n      $files = [basename($files) => $files];\n    } elseif (!is_array($files)) {\n      // And final touch - all other non-string and non-array inputs converted to empty array\n      $files = [];\n    }\n\n    foreach ($files as &$filename) {\n      $filename = $filename . self::TPL_HTML;\n    }\n\n    $template->set_filenames($files);\n\n    return $template;\n  }\n\n  /**\n   * @param template|string $page\n   * @param string          $title\n   *\n   * @return mixed\n   */\n  public static function display($page, $title = '') {\n    SN::$gSomethingWasRendered = true;\n\n    if (!defined('SN_TIME_RENDER_START')) {\n      define('SN_TIME_RENDER_START', microtime(true));\n    }\n\n//  return sn_function_call('display', array($page, $title));\n//}\n//\n///**\n// * @param template|string $page\n// * @param string          $title\n// */\n//function sn_display($page, $title = '') {\n    global $debug, $user, $planetrow, $config, $lang, $template_result, $sn_mvc;\n\n    !empty($sn_mvc['view']['']) and execute_hooks($sn_mvc['view'][''], $page, 'view', '');\n\n    $exitStatus                      = true;\n    $template_result['LOGIN_LOGOUT'] = $inLoginLogout = defined('LOGIN_LOGOUT') && LOGIN_LOGOUT === true;\n\n    if (is_object($page)) {\n      isset($page->_rootref['PAGE_TITLE']) && empty($title) ? $title = $page->_rootref['PAGE_TITLE'] : false;\n      !$title && !empty($page->_rootref['PAGE_HEADER']) ? $title = $page->_rootref['PAGE_HEADER'] : false;\n      !isset($page->_rootref['PAGE_HEADER']) && $title ? $page->assign_var('PAGE_HEADER', $title) : false;\n    }\n\n    $isRenderGlobal = is_object($page) && isset($template_result['GLOBAL_DISPLAY_HEADER']) ? $template_result['GLOBAL_DISPLAY_HEADER'] : true;\n\n    if (self::getCurrentTemplate()->isRenderWhole()) {\n      ob_start();\n    } else {\n      // Global header\n      if ($isRenderGlobal) {\n        SnTemplate::renderHeader($page, $title, $template_result, $inLoginLogout, $user, $config, $lang, $planetrow, null);\n      }\n    }\n\n    // Page content\n    !is_array($page) ? $page = array($page) : false;\n    $result_added = false;\n    foreach ($page as $page_item) {\n      /**\n       * @var template $page_item\n       */\n      if (\n        !$result_added\n        && is_object($page_item)\n        && (\n          isset($page_item->_tpldata['result'])\n          ||\n          !empty($template_result['.']['result'])\n        )\n      ) {\n        $resultTemplate = SnTemplate::gettemplate('_result_message');\n\n        $resultTemplate->_tpldata = $page_item->_tpldata;\n\n        // Checking that no duplicates would be merged from template_result to template itself\n        $filtered = [];\n        if (!empty($template_result['.']['result']) && is_array($template_result['.']['result'])) {\n          foreach ($template_result['.']['result'] as $message) {\n            if (empty($message['MESSAGE'])) {\n              continue;\n            }\n\n            foreach ($resultTemplate->_tpldata['result'] as $tplData) {\n              if (empty($tplData['MESSAGE'])) {\n                continue;\n              }\n\n              if ($tplData['MESSAGE'] == $message['MESSAGE']) {\n                continue 2;\n              }\n            }\n\n            $filtered['.']['result'][] = $message;\n          }\n        }\n        $resultTemplate->assign_recursive($filtered);\n\n        SnTemplate::displayP($resultTemplate);\n        $result_added = true;\n      }\n\n      SnTemplate::displayP($page_item);\n    }\n\n    if (is_array($template_result[TEMPLATE_EXTRA_ARRAY]) && !empty($template_result[TEMPLATE_EXTRA_ARRAY])) {\n      foreach ($template_result[TEMPLATE_EXTRA_ARRAY] as $extraName => $extraTemplate) {\n        /**\n         * @var template $extraTemplate\n         */\n        SnTemplate::displayP($extraTemplate);\n      }\n    }\n\n    if (\n      is_object($page[0])\n      &&\n      (\n        // Checking if hiding page hint flag is present\n        empty($template_result['PAGE_HINT_HIDE'])\n        &&\n        empty($page[0]->_rootref['PAGE_HINT_HIDE'])\n      )\n      &&\n      (\n        isset($page[0]->_tpldata['page_hint'])\n        ||\n        isset($page[0]->_rootref['PAGE_HINT'])\n        ||\n        !empty($template_result['.']['page_hint'])\n        ||\n        !empty($template_result['PAGE_HINT'])\n      )\n    ) {\n      $resultTemplate = self::gettemplate('page_hint');\n\n      $resultTemplate->_tpldata = &$page[0]->_tpldata;\n      $resultTemplate->_rootref = &$page[0]->_rootref;\n      $resultTemplate->assign_recursive($template_result);\n\n      SnTemplate::displayP($resultTemplate);\n    }\n\n    if (self::getCurrentTemplate()->isRenderWhole()) {\n      $renderedContent = ob_get_clean();\n      // Global header\n      if ($isRenderGlobal) {\n        SnTemplate::renderHeader($page, $title, $template_result, $inLoginLogout, $user, $config, $lang, $planetrow, $renderedContent);\n      } else {\n        echo $renderedContent;\n      }\n    }\n\n    // Flushing all opened buffers\n    while (@ob_end_flush()) {\n      ;\n    }\n\n\n    // Global footer\n    if ($isRenderGlobal) {\n      SnTemplate::renderFooter($page, $template_result);\n    }\n\n    $user['authlevel'] >= 3 && $config->debug ? $debug->echo_log() : false;;\n\n    SN::$db->db_disconnect();\n\n    $exitStatus and die($exitStatus === true ? 0 : $exitStatus);\n\n    return $exitStatus;\n  }\n\n  /**\n   * @var GlobalContainer|null $gc\n   */\n  protected $gc = null;\n\n  /**\n   * @var TemplateMeta[] $templates\n   */\n  protected $templates = [];\n\n  public function __construct($gc = null) {\n    $this->gc = empty($gc) ? SN::$gc : $gc;\n  }\n\n  public function registerTemplate($templateName) {\n    if (empty($this->templates[$templateName])) {\n      $this->templates[$templateName] = new TemplateMeta($this, $templateName);\n    }\n\n    return $this->templates[$templateName];\n  }\n\n\n  /**\n   * Дефолтное имя темплейта на сервере\n   *\n   * @return string\n   */\n  public static function getServerDefaultTemplateName() {\n    return SN::$config->game_default_template ? SN::$config->game_default_template : self::SN_TEMPLATE_NAME_DEFAULT;\n  }\n\n  /**\n   * Имя темплейта у игрока\n   *\n   * @return string\n   */\n  public static function getPlayerTemplateName() {\n    return SN::$gc->theUser->getTemplateName();\n  }\n\n  /**\n   * Относительный путь к сконфигурированному темплейту\n   * 'design/templates/(имя_темплейта)\n   *\n   * @return string\n   * @deprecated\n   */\n  public static function pathRelativeToCurrentTemplate() {\n    return self::SN_TEMPLATES_PARTIAL_PATH . self::getPlayerTemplateName();\n  }\n\n  /**\n   * @return TemplateMeta\n   */\n  public static function getCurrentTemplate() {\n    $templateName = SnTemplate::getPlayerTemplateName();\n    $tMeta        = static::me()->registerTemplate($templateName);\n\n    return $tMeta;\n  }\n\n  /**\n   * @param array  $fileList\n   * @param string $extension\n   *\n   * @return array|string[]\n   */\n  protected static function cacheFiles($fileList, $extension = '.css') {\n    // Calculating file key which include all used filenames and modification time for them\n    $key          = '';\n    $filesToMerge = [];\n    foreach ($fileList as $fileName => $aContent) {\n      // If content empty - index is a filename\n      if (empty($aContent)) {\n        // Checking file modification time. Also we will check if file exists\n        if ($fileMTime = filemtime(SN_ROOT_PHYSICAL . $fileName)) {\n          // Generating key\n          $key .= \"$fileName:\" . date(FMT_DATE_TIME_SQL, $fileMTime) . \"\\n\";\n          // Adding filename to list of merge\n          $filesToMerge[$fileName] = \"$fileName:\" . date(FMT_DATE_TIME_SQL, $fileMTime);\n        }\n      }\n    }\n    // Name we will be using to address this file\n    $cacheFileName = 'design/cache/' . md5($key) . $extension;\n    // If no file exists - trying to create it\n    if (!file_exists($fullCacheFileName = SN_ROOT_PHYSICAL . $cacheFileName)) {\n      // Caching several files into one\n      $contentToCache = '';\n      foreach ($filesToMerge as $fileName => $temp) {\n        $fileExists = file_exists($fName = SN_ROOT_PHYSICAL . $fileName);\n        $notEmpty   = !empty($content = file_get_contents($fName));\n        if ($fileExists && $notEmpty) {\n          // Adding content of cached file\n          $contentToCache .= $content . \"\\n\";\n          // Marking that file was read OK - for future debug purposes if any\n          $filesToMerge[$fileName] .= \" - OK\\n\";\n        } else {\n          $filesToMerge[$fileName] .= \" - ERROR: fileExists = `$fileExists`, notEmpty = `$notEmpty`!\\n\";\n        }\n      }\n      // If we have something to cache...\n      if (!empty($contentToCache)) {\n        // /* */ works as comment for both JS and CSS\n        file_put_contents($fullCacheFileName, \"/* Generated content. Safe to delete\\n\" . implode('', $filesToMerge) . \"*/\\n\\n\" . $contentToCache);\n      }\n    }\n\n    // If CSS cache exists for current combination of files\n    if (file_exists($fullCacheFileName)) {\n      // Removing from list all CSS files that already in cache\n      foreach ($filesToMerge as $fileName => $temp) {\n        unset($fileList[$fileName]);\n      }\n      // Adding to list cache file\n      $fileList = [$cacheFileName => ''] + $fileList;\n    }\n\n    return $fileList;\n  }\n\n}\n"
  },
  {
    "path": "classes/StatCalculator.php",
    "content": "<?php\n/**\n * Created by Gorlum 24.09.2017 18:09\n */\n\nuse DBAL\\db_mysql;\nuse Fleet\\DbFleetStatic;\nuse Planet\\DBStaticPlanet;\nuse Que\\DBStaticQue;\nuse Unit\\DBStaticUnit;\n\n/**\n * Class StatCalculator\n *\n * Updates player's stats\n */\nclass StatCalculator {\n\n  /**\n   * @var int\n   */\n  public static $memoryStart = 0;\n  /**\n   * @var float\n   */\n  public static $timeLastOperation = 0.0;\n\n  public static function sta_set_time_limit($sta_update_msg = 'updating something', $next_step = true) {\n    global $config, $debug, $sta_update_step;\n\n    $value = $config->stats_minimal_interval ? $config->stats_minimal_interval : STATS_RUN_INTERVAL_MINIMUM;\n    set_time_limit($value);\n    $config->pass()->var_stat_update_end = time() + $value;\n\n    $sta_update_msg = SN::$db->db_escape($sta_update_msg);\n\n    if ($next_step) {\n      $sta_update_step++;\n    }\n\n    $nowMicro = microtime(true);\n    $sta_update_msg = \"Update in progress. Step {$sta_update_step}/14: {$sta_update_msg}.\\r\\nMemory usage: \"\n      . number_format(memory_get_usage(true) - static::$memoryStart)\n      . \"\\r\\nPrevious operation time: \" . number_format($nowMicro - static::$timeLastOperation, 5);\n\n    $config->pass()->var_stat_update_msg = $sta_update_msg;\n    if ($next_step) {\n      static::$timeLastOperation = $nowMicro;\n      $debug->warning($sta_update_msg, 'Stat update', LOG_INFO_STAT_PROCESS);\n    }\n  }\n\n  public static function sys_stat_calculate_flush(&$data, $force = false) {\n    if (count($data) < 25 && !$force) {\n      return;\n    }\n\n    if (!empty($data)) {\n      doquery('REPLACE INTO `{{statpoints}}`\n      (`id_owner`, `id_ally`, `stat_type`, `stat_code`, `tech_points`, `tech_count`, `build_points`, `build_count`,\n       `defs_points`, `defs_count`, `fleet_points`, `fleet_count`, `res_points`, `res_count`, `total_points`,\n       `total_count`, `stat_date`) VALUES ' . implode(',', $data)\n      );\n    }\n\n    $data = array();\n  }\n\n\n  public static function sys_stat_calculate() {\n    global $config, $sta_update_step;\n\n    ini_set('memory_limit', $config->stats_php_memory ? $config->stats_php_memory : '1G');\n\n    static::$memoryStart = memory_get_usage(true);\n    static::$timeLastOperation = microtime(true);\n\n    $user_skip_list = sys_stat_get_user_skip_list();\n\n    // $sn_groups_resources_loot = sn_get_groups('resources_loot');\n    $rate[RES_METAL] = $config->rpg_exchange_metal;\n    $rate[RES_CRYSTAL] = $config->rpg_exchange_crystal / $config->rpg_exchange_metal;\n    $rate[RES_DEUTERIUM] = $config->rpg_exchange_deuterium / $config->rpg_exchange_metal;\n    $rate[RES_DARK_MATTER] = $config->rpg_exchange_darkMatter / $config->rpg_exchange_metal;\n\n    $sta_update_step = -1;\n\n    static::sta_set_time_limit('starting update');\n    $counts = $points = $unit_cost_cache = $user_allies = array();\n\n\n    static::sta_set_time_limit('calculating players stats');\n\n    db_mysql::db_transaction_start();\n    $i = 0;\n    // Блокируем всех пользователей\n    db_mysql::db_lock_tables('users');\n    $user_list = db_user_list('', true, 'id, dark_matter, metal, crystal, deuterium, user_as_ally, ally_id');\n    $row_num = count($user_list);\n    // while($player = db_fetch($query))\n    foreach ($user_list as $player) {\n      if ($i++ % 100 == 0) {\n        static::sta_set_time_limit(\"calculating players stats (player {$i}/{$row_num})\", false);\n      }\n      if (array_key_exists($user_id = $player['id'], $user_skip_list)) {\n        continue;\n      }\n\n      $resources =\n        $player['metal'] * $rate[RES_METAL]\n        + $player['crystal'] * $rate[RES_CRYSTAL]\n        + $player['deuterium'] * $rate[RES_DEUTERIUM]\n        + $player['dark_matter'] * $rate[RES_DARK_MATTER];\n      ;\n      $counts[$user_id][UNIT_RESOURCES] += $resources;\n      // $points[$user_id][UNIT_RESOURCES] += $resources;\n\n      // А здесь мы фильтруем пользователей по $user_skip_list - далее не нужно этого делать, потому что\n      if (!isset($user_skip_list[$user_id])) {\n        $user_allies[$user_id] = $player['ally_id'];\n      }\n    }\n    unset($user_list);\n    DBStaticUnit::cache_clear();\n\n    static::sta_set_time_limit('calculating planets stats');\n    $i = 0;\n    $query = DBStaticPlanet::db_planet_list_resources_by_owner();\n    $row_num = SN::$db->db_num_rows($query);\n    while ($planet = db_fetch($query)) {\n      if ($i++ % 100 == 0) {\n        static::sta_set_time_limit(\"calculating planets stats (planet {$i}/{$row_num})\", false);\n      }\n      if (array_key_exists($user_id = $planet['id_owner'], $user_skip_list)) {\n        continue;\n      }\n\n      $resources =\n        $planet['metal'] * $rate[RES_METAL] +\n        $planet['crystal'] * $rate[RES_CRYSTAL] +\n        $planet['deuterium'] * $rate[RES_DEUTERIUM];\n      $counts[$user_id][UNIT_RESOURCES] += $resources;\n    }\n\n    // Calculation of Fleet-In-Flight\n    static::sta_set_time_limit('calculating flying fleets stats');\n    $i = 0;\n    $query = DbFleetStatic::db_fleet_list_query_all_stat();\n    $row_num = SN::$db->db_num_rows($query);\n    while ($fleet_row = db_fetch($query)) {\n      if ($i++ % 100 == 0) {\n        static::sta_set_time_limit(\"calculating flying fleets stats (fleet {$i}/{$row_num})\", false);\n      }\n      if (array_key_exists($user_id = $fleet_row['fleet_owner'], $user_skip_list)) {\n        continue;\n      }\n\n      $fleet = sys_unit_str2arr($fleet_row['fleet_array']);\n      foreach ($fleet as $unit_id => $unit_amount) {\n        $counts[$user_id][UNIT_SHIPS] += $unit_amount;\n\n        if (!isset($unit_cost_cache[$unit_id][0])) {\n          $unit_cost_cache[$unit_id][0] = get_unit_param($unit_id, P_COST);\n        }\n        $unit_cost_data = &$unit_cost_cache[$unit_id][0];\n        $points[$user_id][UNIT_SHIPS] += (\n            $unit_cost_data[RES_METAL] * $rate[RES_METAL] +\n            $unit_cost_data[RES_CRYSTAL] * $rate[RES_CRYSTAL] +\n            $unit_cost_data[RES_DEUTERIUM] * $rate[RES_DEUTERIUM]\n          ) * $unit_amount;\n      }\n      $resources =\n        $fleet_row['fleet_resource_metal'] * $rate[RES_METAL] +\n        $fleet_row['fleet_resource_crystal'] * $rate[RES_CRYSTAL] +\n        $fleet_row['fleet_resource_deuterium'] * $rate[RES_DEUTERIUM];\n\n      $counts[$user_id][UNIT_RESOURCES] += $resources;\n    }\n\n    static::sta_set_time_limit('calculating ques stats');\n    $i = 0;\n    $query = DBStaticQue::db_que_list_stat();\n    $row_num = SN::$db->db_num_rows($query);\n    while ($que_item = db_fetch($query)) {\n      if ($i++ % 100 == 0) {\n        static::sta_set_time_limit(\"calculating ques stats (que item {$i}/{$row_num})\", false);\n      }\n      if (array_key_exists($user_id = $que_item['que_player_id'], $user_skip_list)) {\n        continue;\n      }\n      $que_unit_amount = $que_item['que_unit_amount'];\n      $que_item = sys_unit_str2arr($que_item['que_unit_price']);\n      $resources = (\n          $que_item[RES_METAL] * $rate[RES_METAL] +\n          $que_item[RES_CRYSTAL] * $rate[RES_CRYSTAL] +\n          $que_item[RES_DEUTERIUM] * $rate[RES_DEUTERIUM]\n        ) * $que_unit_amount;\n      $counts[$user_id][UNIT_RESOURCES] += $resources;\n    }\n\n    static::sta_set_time_limit('calculating unit stats');\n    $i = 0;\n    $query = DBStaticUnit::db_unit_list_stat_calculate();\n    $row_num = SN::$db->db_num_rows($query);\n    while ($unit = db_fetch($query)) {\n      if ($i++ % 100 == 0) {\n        static::sta_set_time_limit(\"calculating unit stats (unit {$i}/{$row_num})\", false);\n      }\n      if (array_key_exists($user_id = $unit['unit_player_id'], $user_skip_list)) {\n        continue;\n      }\n\n      $counts[$user_id][$unit['unit_type']] += $unit['unit_level'] * $unit['unit_amount'];\n      $total_cost = eco_get_total_cost($unit['unit_snid'], $unit['unit_level']);\n      $points[$user_id][$unit['unit_type']] += (isset($total_cost['total']) ? $total_cost['total'] : 0) * $unit['unit_amount'];\n    }\n\n    static::sta_set_time_limit('archiving old statistic');\n    // Statistic rotation\n    // doquery(\"DELETE FROM {{statpoints}} WHERE `stat_code` >= 14;\");\n    doquery(\"DELETE FROM `{{statpoints}}` WHERE `stat_date` < UNIX_TIMESTAMP(DATE_SUB(NOW(), INTERVAL {$config->stats_history_days} DAY));\");\n    doquery(\"UPDATE `{{statpoints}}` SET `stat_code` = `stat_code` + 1;\");\n\n    static::sta_set_time_limit('posting new user stats to DB');\n    $data = array();\n    foreach ($user_allies as $user_id => $ally_id) {\n      // $counts[UNIT_RESOURCES] дублирует $points[UNIT_RESOURCES], поэтому $points не заполняем, а берем $counts и делим на 1000\n      $points[$user_id][UNIT_RESOURCES] = $counts[$user_id][UNIT_RESOURCES] / 1000;\n      $points[$user_id] = array_map('floor', $points[$user_id]);\n      $counts[$user_id] = array_map('floor', $counts[$user_id]);\n\n      $ally_id = $ally_id ? $ally_id : 'NULL';\n      $user_defence_points = 0 + $points[$user_id][UNIT_DEFENCE] + $points[$user_id][UNIT_DEF_MISSILES];\n      $user_defence_counts = 0 + $counts[$user_id][UNIT_DEFENCE] + $counts[$user_id][UNIT_DEF_MISSILES];\n      $user_points = array_sum($points[$user_id]);\n      $user_counts = array_sum($counts[$user_id]);\n\n      !isset($points[$user_id][UNIT_TECHNOLOGIES]) ? $points[$user_id][UNIT_TECHNOLOGIES] = 0 : false;\n      !isset($counts[$user_id][UNIT_TECHNOLOGIES]) ? $counts[$user_id][UNIT_TECHNOLOGIES] = 0 : false;\n      !isset($points[$user_id][UNIT_STRUCTURES]) ? $points[$user_id][UNIT_STRUCTURES] = 0 : false;\n      !isset($counts[$user_id][UNIT_STRUCTURES]) ? $counts[$user_id][UNIT_STRUCTURES] = 0 : false;\n      !isset($points[$user_id][UNIT_SHIPS]) ? $points[$user_id][UNIT_SHIPS] = 0 : false;\n      !isset($counts[$user_id][UNIT_SHIPS]) ? $counts[$user_id][UNIT_SHIPS] = 0 : false;\n      !isset($points[$user_id][UNIT_RESOURCES]) ? $points[$user_id][UNIT_RESOURCES] = 0 : false;\n      !isset($counts[$user_id][UNIT_RESOURCES]) ? $counts[$user_id][UNIT_RESOURCES] = 0 : false;\n\n      $data[] = \"({$user_id},{$ally_id},1,1,'{$points[$user_id][UNIT_TECHNOLOGIES]}','{$counts[$user_id][UNIT_TECHNOLOGIES]}',\" .\n        \"'{$points[$user_id][UNIT_STRUCTURES]}','{$counts[$user_id][UNIT_STRUCTURES]}','{$user_defence_points}','{$user_defence_counts}',\" .\n        \"'{$points[$user_id][UNIT_SHIPS]}','{$counts[$user_id][UNIT_SHIPS]}','{$points[$user_id][UNIT_RESOURCES]}','{$counts[$user_id][UNIT_RESOURCES]}',\" .\n        \"{$user_points},{$user_counts},\" . SN_TIME_NOW . \")\";\n\n      static::sys_stat_calculate_flush($data);\n    }\n    static::sys_stat_calculate_flush($data, true);\n\n\n    // Updating Allie's stats\n    static::sta_set_time_limit('posting new Alliance stats to DB');\n    doquery(\n      \"INSERT INTO `{{statpoints}}`\n      (`tech_points`, `tech_count`, `build_points`, `build_count`, `defs_points`, `defs_count`,\n        `fleet_points`, `fleet_count`, `res_points`, `res_count`, `total_points`, `total_count`,\n        `stat_date`, `id_owner`, `id_ally`, `stat_type`, `stat_code`,\n        `tech_old_rank`, `build_old_rank`, `defs_old_rank`, `fleet_old_rank`, `res_old_rank`, `total_old_rank`\n      )\n      SELECT\n        SUM(u.`tech_points`)+aus.`tech_points`, SUM(u.`tech_count`)+aus.`tech_count`, SUM(u.`build_points`)+aus.`build_points`, SUM(u.`build_count`)+aus.`build_count`,\n        SUM(u.`defs_points`)+aus.`defs_points`, SUM(u.`defs_count`)+aus.`defs_count`, SUM(u.`fleet_points`)+aus.`fleet_points`, SUM(u.`fleet_count`)+aus.`fleet_count`,\n        SUM(u.`res_points`)+aus.`res_points`, SUM(u.`res_count`)+aus.`res_count`, SUM(u.`total_points`)+aus.`total_points`, SUM(u.`total_count`)+aus.`total_count`,\n        \" . SN_TIME_NOW . \", NULL, u.`id_ally`, 2, 1,\n        a.tech_rank, a.build_rank, a.defs_rank, a.fleet_rank, a.res_rank, a.total_rank\n      FROM `{{statpoints}}` AS u\n        JOIN `{{alliance}}` AS al ON al.id = u.id_ally\n        LEFT JOIN `{{statpoints}}` AS aus ON aus.id_owner = al.ally_user_id AND aus.stat_type = 1 AND aus.stat_code = 1\n        LEFT JOIN `{{statpoints}}` AS a ON a.id_ally = u.id_ally AND a.stat_code = 2 AND a.stat_type = 2\n      WHERE u.`stat_type` = 1 AND u.stat_code = 1 AND u.id_ally<>0\n      GROUP BY u.`id_ally`\"\n    );\n\n    // Удаляем больше не нужные записи о достижении игрока-альянса\n    db_stat_list_delete_ally_player();\n\n    // Some variables we need to update ranks\n    $qryResetRowNum = 'SET @rownum=0;';\n    $qryFormat = 'UPDATE `{{statpoints}}` SET `%1$s_rank` = (SELECT @rownum:=@rownum+1) WHERE `stat_type` = \"%2$d\" AND `stat_code` = 1 ORDER BY `%1$s_points` DESC, `id_owner` ASC, `id_ally` ASC;';\n\n    $rankNames = array('tech', 'build', 'defs', 'fleet', 'res', 'total');\n\n    // Updating player's ranks\n    static::sta_set_time_limit(\"updating ranks for players\");\n    foreach ($rankNames as $rankName) {\n      static::sta_set_time_limit(\"updating player rank '{$rankName}'\", false);\n      doquery($qryResetRowNum);\n      doquery(sprintf($qryFormat, $rankName, 1));\n    }\n\n    static::sta_set_time_limit(\"updating ranks for Alliances\");\n    // --- Updating Allie's ranks\n    foreach ($rankNames as $rankName) {\n      static::sta_set_time_limit(\"updating Alliances rank '{$rankName}'\", false);\n      doquery($qryResetRowNum);\n      doquery(sprintf($qryFormat, $rankName, 2));\n    }\n\n    static::sta_set_time_limit('setting previous user stats from archive');\n    doquery(\n    \"UPDATE `{{statpoints}}` AS new\n      LEFT JOIN `{{statpoints}}` AS old ON old.id_owner = new.id_owner AND old.stat_code = 2 AND old.stat_type = new.stat_type\n    SET\n      new.tech_old_rank = old.tech_rank,\n      new.build_old_rank = old.build_rank,\n      new.defs_old_rank  = old.defs_rank ,\n      new.fleet_old_rank = old.fleet_rank,\n      new.res_old_rank = old.res_rank,\n      new.total_old_rank = old.total_rank\n    WHERE\n      new.stat_type = 1 AND new.stat_code = 1;\");\n\n    static::sta_set_time_limit('setting previous allies stats from archive');\n    doquery(\n      \"UPDATE `{{statpoints}}` AS new\n      LEFT JOIN `{{statpoints}}` AS old ON old.id_ally = new.id_ally AND old.stat_code = 2 AND old.stat_type = new.stat_type\n    SET\n      new.tech_old_rank = old.tech_rank,\n      new.build_old_rank = old.build_rank,\n      new.defs_old_rank  = old.defs_rank ,\n      new.fleet_old_rank = old.fleet_rank,\n      new.res_old_rank = old.res_rank,\n      new.total_old_rank = old.total_rank\n    WHERE\n      new.stat_type = 2 AND new.stat_code = 1;\");\n\n    static::sta_set_time_limit('updating players current rank and points');\n    doquery(\"UPDATE `{{users}}` AS u JOIN `{{statpoints}}` AS sp ON sp.id_owner = u.id AND sp.stat_code = 1 AND sp.stat_type = 1 SET u.total_rank = sp.total_rank, u.total_points = sp.total_points WHERE user_as_ally IS NULL;\");\n\n    static::sta_set_time_limit('updating Allys current rank and points');\n    doquery(\"UPDATE `{{alliance}}` AS a JOIN `{{statpoints}}` AS sp ON sp.id_ally = a.id AND sp.stat_code = 1 AND sp.stat_type = 2 SET a.total_rank = sp.total_rank, a.total_points = sp.total_points;\");\n\n    // Counting real user count and updating values\n    dbUpdateUsersCount(db_user_count());\n\n    db_mysql::db_transaction_commit();\n  }\n\n}\n"
  },
  {
    "path": "classes/StatUpdateLauncher.php",
    "content": "<?php\n/**\n * Created by Gorlum 24.09.2017 17:15\n */\n\nuse DBAL\\db_mysql;\nuse Fleet\\DbFleetStatic;\n\n/**\n * Class StatUpdateLauncher\n *\n * Part of the scheduling process. Response for Stat Updater launch\n *\n */\nclass StatUpdateLauncher {\n\n  public static function unlock() {\n    if (\n      SN::$config->game_disable != GAME_DISABLE_STAT || SN_TIME_NOW - strtotime(SN::$config->pass()->var_stat_update_end) <= STATS_RUN_INTERVAL_MINIMUM\n    ) {\n      return;\n    }\n    $next_run = date(FMT_DATE_TIME_SQL, sys_schedule_get_prev_run(SN::$config->stats_schedule, SN::$config->var_stat_update, true));\n    SN::$config->pass()->game_disable = GAME_DISABLE_NONE;\n    SN::$config->pass()->var_stat_update = SN_TIME_SQL;\n    SN::$config->pass()->var_stat_update_next = $next_run;\n    SN::$config->pass()->var_stat_update_end = SN_TIME_SQL;\n    SN::$debug->warning('Stat worked too long - watchdog unlocked', 'Stat WARNING');\n  }\n\n\n  public static function scheduler_process() {\n    global $user, $lang;\n\n    $config = SN::$config;\n    $debug = SN::$debug;\n\n    $is_admin_request = false;\n\n    $ts_var_stat_update = strtotime($config->pass()->var_stat_update);\n    $ts_scheduled_update = sys_schedule_get_prev_run($config->pass()->stats_schedule, $config->var_stat_update);\n\n    if (sys_get_param_int('admin_update')) {\n      define('USER_LEVEL', isset($user['authlevel']) ? $user['authlevel'] : -1);\n      if (USER_LEVEL > 0) {\n        $is_admin_request = true;\n        $ts_scheduled_update = SN_TIME_NOW;\n      }\n    }\n\n    if ($ts_scheduled_update > $ts_var_stat_update) {\n      lng_include('admin');\n      db_mysql::db_transaction_start();\n      $ts_var_stat_update_end = strtotime($config->pass()->var_stat_update_end);\n      if (SN_TIME_NOW > $ts_var_stat_update_end) {\n        $old_server_status = $config->pass()->game_disable;\n        $config->pass()->game_disable = GAME_DISABLE_STAT;\n\n        $statMinimalInterval = intval($config->pass()->stats_minimal_interval);\n        $config->pass()->var_stat_update_end= date(FMT_DATE_TIME_SQL, SN_TIME_NOW + ($statMinimalInterval ? $statMinimalInterval : STATS_RUN_INTERVAL_MINIMUM));\n        $config->pass()->var_stat_update_msg = 'Update started';\n        db_mysql::db_transaction_commit();\n\n        $msg = $is_admin_request ? 'admin request' : 'scheduler';\n        $next_run = date(FMT_DATE_TIME_SQL, sys_schedule_get_prev_run($config->stats_schedule, $config->pass()->var_stat_update, true));\n        $msg = \"Running stat updates: {$msg}. Config->var_stat_update = \" . $config->var_stat_update .\n          ', $ts_scheduled_update = ' . date(FMT_DATE_TIME_SQL, $ts_scheduled_update) .\n          ', next_stat_update = ' . $next_run;\n        $debug->warning($msg, 'Stat update', LOG_INFO_STAT_PROCESS);\n        $total_time = microtime(true);\n\n        StatCalculator::sys_stat_calculate();\n\n        DbFleetStatic::db_fleet_acs_purge();\n\n        $total_time = microtime(true) - $total_time;\n        $msg = \"Stat update complete in {$total_time} seconds.\";\n        $debug->warning($msg, 'Stat update', LOG_INFO_STAT_PROCESS);\n\n        $msg = \"{$lang['adm_done']}: {$total_time} {$lang['sys_sec']}.\"; // . date(FMT_DATE_TIME, $ts_scheduled_update) . ' ' . date(FMT_DATE_TIME, $config->var_stat_update);\n\n        // TODO: Analyze maintenance result. Add record to log if error. Add record to log if OK\n        $maintenance_result = sys_maintenance();\n\n        $config->pass()->var_stat_update = SN_TIME_SQL;\n        $config->pass()->var_stat_update_msg = $msg;\n        $config->pass()->var_stat_update_next = $next_run;\n        $config->pass()->var_stat_update_admin_forced = SN_TIME_SQL;\n        $config->pass()->var_stat_update_end = SN_TIME_SQL;\n\n        $config->pass()->game_disable = $old_server_status;\n      } elseif ($ts_scheduled_update > $ts_var_stat_update) {\n        $timeout = strtotime($config->pass()->var_stat_update_end) - SN_TIME_NOW;\n        $msg = $config->pass()->var_stat_update_msg;\n        $msg = \"{$msg} ETA {$timeout} seconds. Please wait...\";\n      }\n      db_mysql::db_transaction_rollback();\n    } elseif ($is_admin_request) {\n      $msg = 'Stat is up to date';\n    }\n\n    return $msg;\n  }\n\n}\n"
  },
  {
    "path": "classes/Storage.php",
    "content": "<?php\n\n/**\n * Created by Gorlum 10.02.2017 0:28\n */\n\nuse Core\\GlobalContainer;\n\n/**\n * Class Storage\n * @deprecated\n */\nclass Storage {\n\n  /**\n   * Storage constructor.\n   *\n   * @param GlobalContainer $gc\n   */\n  public function __construct(GlobalContainer $gc) {\n  }\n\n  /**\n   * @param TextRecordDescription $recordDescription\n   * @param string|int            $id\n   *\n   * @return array|null\n   */\n  public function loadById($recordDescription, $id) {\n    $dbq = new \\DBAL\\DbQuery($recordDescription->db);\n    $dbq\n      ->setTable($recordDescription->table)\n      ->setOneRow()\n      ->setWhereArray(array($recordDescription->indexFieldName => $id));\n    return $recordDescription->db->dbqSelectAndFetch($dbq);\n  }\n\n}\n"
  },
  {
    "path": "classes/Template/TemplateMeta.php",
    "content": "<?php\n/**\n * Created by Gorlum 03.09.2019 0:16\n */\n\nnamespace Template;\n\nuse SnTemplate;\nuse template;\n\n/**\n * Meta template - should work as interface between game and any rendering engine\n *\n * @package Template\n */\nclass TemplateMeta {\n  const INI_FILE_NAME = '_template.ini';\n  const CONFIG_RENDER_WHOLE = '_renderWhole';\n  /**\n   * Конфигурация скина - читается из INI-файла\n   *\n   * @var array $config\n   */\n  protected $config = [];\n\n  /**\n   * @var string\n   */\n  protected $name = '';\n  /**\n   * Full path to template root\n   *\n   * @var string $pathFull\n   */\n  protected $pathFull = '';\n  /**\n   * Relative path from any template\n   *\n   * @var string $pathRelative\n   */\n  protected $pathRelative = '';\n\n  /**\n   * @var self|null $parent\n   */\n  protected $parent = null;\n\n  /**\n   * @var SnTemplate|null $manager\n   */\n  protected $manager = null;\n\n  /**\n   * TemplateMeta constructor.\n   *\n   * @param SnTemplate $manager\n   * @param            $templateName\n   * @param string     $templatePath Path to template. Can be absolute (starting with '/', should also include template name if any) or relative to game root. Empty means \"autodetect\"\n   */\n  public function __construct($manager, $templateName, $templatePath = '') {\n    $this->name    = $templateName;\n    $this->manager = $manager;\n\n    if (empty($templatePath) || !is_string($templatePath)) {\n      $this->pathFull = SN_ROOT_PHYSICAL . SnTemplate::SN_TEMPLATES_PARTIAL_PATH . $templateName . '/';\n    } else {\n      if (substr($templatePath, -1) !== '/') {\n        $templatePath .= '/';\n      }\n      if (substr($templatePath, 0, 1) === '/') {\n        // Absolute path\n        $this->pathFull = $templatePath;\n      } else {\n        // Game root relative path\n        $this->pathFull = SN_ROOT_PHYSICAL . $templatePath . $templateName . '/';\n      }\n    }\n\n    $this->loadIniFile();\n    $this->setParentFromConfig();\n\n  }\n\n  /**\n   * Loads skin configuration\n   */\n  protected function loadIniFile() {\n    // Проверка на корректность и существование пути\n    if (!is_file($this->pathFull . static::INI_FILE_NAME)) {\n      return;\n    }\n\n    // Пытаемся распарсить файл\n    // По секциям? images и config? Что бы не копировать конфигурацию? Или просто unset(__inherit) а затем заново записать\n    $aConfig = parse_ini_file($this->pathFull . static::INI_FILE_NAME);\n    if (empty($aConfig)) {\n      return;\n    }\n\n    $this->config = $aConfig;\n  }\n\n  protected function setParentFromConfig() {\n    // Проверка на _inherit\n    if (empty($this->config['_inherit'])) {\n      return;\n    }\n\n    $parentName = $this->config['_inherit'];\n    // Если скин наследует себя...\n    if ($parentName === $this->name) {\n      // TODO - определять более сложные случаи циклических ссылок в _inherit\n      // TODO - throw exception\n      die('\">circular skin inheritance!');\n    }\n\n    $this->parent = $this->manager->registerTemplate($parentName);\n  }\n\n  /**\n   * @param template $template\n   * @param string   $template_path\n   * @param string   $fallBackPath\n   *\n   * @return template\n   */\n  public function getTemplate($template, $template_path, $fallBackPath = '') {\n    if (!is_object($template)) {\n      $template = new template(SN_ROOT_PHYSICAL);\n    }\n\n    if (!$template_path || !is_string($template_path)) {\n      $template_path = SN_ROOT_PHYSICAL . SnTemplate::SN_TEMPLATES_PARTIAL_PATH;\n    }\n\n//    var_dump($fallBackPath);\n//    var_dump($template);\n\n    if(empty($fallBackPath)) {\n      if (!$this->parent || empty($fallbackName = $this->parent->getName()) || !$this->isTemplateExists()) {\n        // If no parent template - then using default template as fallback one\n        $fallbackName = SnTemplate::SN_TEMPLATE_NAME_DEFAULT;\n      }\n      $fallBackPath = $template_path . $fallbackName . '/';\n    }\n\n//    var_dump($fallBackPath);\n\n    $template->set_custom_template(\"{$template_path}{$this->name}/\", $this->name, $fallBackPath);\n\n    return $template;\n  }\n\n\n  public function getName() {\n    return $this->name;\n  }\n\n  public function getPathFull() {\n    return $this->pathFull;\n  }\n\n  public function cssAddFileName($cssFileName, array $standard_css) {\n    if ($this->parent) {\n      $standard_css = $this->parent->cssAddFileName($cssFileName, $standard_css);\n    } elseif (!$this->isTemplateExists()) {\n      // If template dir does not exists - falling back to default CSS file\n      $standard_css = SnTemplate::addFileName(SnTemplate::SN_TEMPLATES_PARTIAL_PATH . SnTemplate::SN_TEMPLATE_NAME_DEFAULT . '/' . $cssFileName, $standard_css);\n    }\n\n    $standard_css = SnTemplate::addFileName(SnTemplate::SN_TEMPLATES_PARTIAL_PATH . $this->name . '/' . $cssFileName, $standard_css);\n\n    return $standard_css;\n  }\n\n  /**\n   * Does template files physically exists on disk?\n   * Only full path to template checked\n   *\n   * @return bool\n   */\n  public function isTemplateExists() {\n    return file_exists($this->pathFull);\n  }\n\n  /**\n   * Is template rendered at once - not header/footer separately?\n   *\n   * @return bool\n   */\n  public function isRenderWhole() {\n    return !empty($this->config[self::CONFIG_RENDER_WHOLE]);\n  }\n\n}\n"
  },
  {
    "path": "classes/TextEntity.php",
    "content": "<?php\n\n/**\n * Created by Gorlum 07.02.2017 10:20\n */\n\n/**\n * Class TextEntity\n *\n * Represents text in DB\n *\n * @property int    $id\n * @property int    $parent\n * @property int    $context\n * @property int    $prev\n * @property int    $next\n * @property int    $next_alt\n * @property string $title\n * @property string $content\n *\n */\nclass TextEntity extends \\Common\\ContainerPlus {\n  const _class = 'TextEntity';\n\n  /**\n   * @var TextModel $model\n   */\n  protected $model;\n\n  /**\n   * @inheritdoc\n   */\n  public function __construct(array $values = array()) {\n    parent::__construct($values);\n\n    // TODO\n    $this->model = new TextModel(SN::$gc);\n  }\n\n  /**\n   * @return array\n   */\n  public function toArray() {\n    $tutorial = array();\n    foreach($this->keys() as $key) {\n      $tutorial[$key] = $this->$key;\n    }\n\n    return $tutorial;\n  }\n\n  /**\n   * Return HTML-ized array of elements (if any)\n   *\n   * @param int $encodeOptions - HTML_ENCODE_xxx constants\n   *\n   * @return array\n   */\n  public function toArrayHtml($encodeOptions = HTML_ENCODE_MULTILINE) {\n    $result = $this->toArray();\n\n    if (!empty($result)) {\n      $result['title'] = HelperString::htmlEncode($result['title'], $encodeOptions);\n      $result['content'] = HelperString::htmlEncode($result['content'], $encodeOptions);\n    }\n\n    return $result;\n  }\n\n  /**\n   * Return HTML-ized array of elements (if any)\n   *\n   * @param int $encodeOptions - HTML_ENCODE_xxx constants\n   *\n   * @return array\n   */\n  public function toArrayParsedBBC($encodeOptions = HTML_ENCODE_MULTILINE) {\n    $result = $this->toArray();\n\n    if (!empty($result)) {\n      $result['title'] = SN::$gc->bbCodeParser->expandBbCode($result['title'], AUTH_LEVEL_SYSTEM, $encodeOptions);\n      $result['content'] = SN::$gc->bbCodeParser->expandBbCode($result['content'], AUTH_LEVEL_SYSTEM, $encodeOptions);\n    }\n\n    return $result;\n  }\n\n//  public function get($textId) {\n//    $this->clear();\n//    $this->id = $textId;\n//\n//    return $this->repository->get($this);\n//  }\n//\n//  public function next($textId) {\n//    $current = new static();\n//    $static->get($textId);\n//\n//    if ($static->next) {\n//      $next = new static();\n//      $next->get($static->next);\n//      if ($next->isEmpty() && $current->alternative) {\n//\n//      }\n//    }\n//\n//\n//  }\n//\n//\n\n}\n"
  },
  {
    "path": "classes/TextModel.php",
    "content": "<?php\n\nuse Core\\GlobalContainer;\nuse Core\\Repository;\n\n/**\n * Created by Gorlum 09.02.2017 23:27\n */\nclass TextModel {\n\n  /**\n   * @var Repository $repository\n   */\n  protected $repository;\n\n  /**\n   * @var Storage $storage\n   */\n  protected $storage;\n\n  /**\n   * @var TextRecordDescription $textRecordDescription\n   */\n  protected $textRecordDescription;\n\n  protected $entityClass = '\\TextEntity';\n\n  /**\n   * TextModel constructor.\n   *\n   * @param GlobalContainer $gc\n   */\n  public function __construct(GlobalContainer $gc) {\n    $this->textRecordDescription = new TextRecordDescription();\n    $this->repository = $gc->repository;\n    $this->storage = $gc->storage;\n  }\n\n\n  /**\n   * Gets text entity by id\n   *\n   * @param int|string $textId\n   *\n   * @return TextEntity\n   */\n  public function getById($textId) {\n    $entity = $this->repository->getById($this, $textId);\n\n    return $entity;\n  }\n\n  /**\n   * Load from Storage\n   *\n   * Operates with arrays from different tables\n   *\n   * @param int|string $textId\n   *\n   * @return TextEntity\n   */\n  public function loadById($textId) {\n    $array = $this->storage->loadById($this->textRecordDescription, $textId);\n\n    /**\n     * @var TextEntity $text\n     */\n    $text = new $this->entityClass();\n    if (is_array($array) && !empty($array)) {\n      $this->parseArray($text, $array);\n    }\n\n    return $text;\n  }\n\n\n  /**\n   * @param TextEntity $entity\n   * @param array      $array\n   */\n  public function parseArray($entity, $array) {\n    // Basic parsing\n    foreach ($array as $key => $value) {\n      $entity->$key = $value;\n    }\n  }\n\n\n  /**\n   * Get next text in chain with resolving alternate next\n   *\n   * @param int|string $textId\n   *\n   * @return TextEntity\n   */\n  public function next($textId) {\n    $currentText = $this->getById($textId);\n    if (!$currentText->isEmpty() && $currentText->next) {\n      $next = $this->getById($currentText->next);\n      if ($next->isEmpty() && $currentText->next_alt) {\n        $next = $this->getById($currentText->next_alt);\n      }\n    }\n\n    if(!isset($next)) {\n      $next = new TextEntity();\n    }\n\n    return $next;\n  }\n\n  /**\n   * @param int|string $textId\n   *\n   * @return TextEntity\n   */\n  public function prev($textId) {\n    $currentText = $this->getById($textId);\n    if (!$currentText->isEmpty() && $currentText->prev) {\n      $prev = $this->getById($currentText->prev);\n    }\n\n    if(!isset($prev)) {\n      $prev = new TextEntity();\n    }\n\n    return $prev;\n  }\n\n}\n"
  },
  {
    "path": "classes/TextRecordDescription.php",
    "content": "<?php\n\n/**\n * Created by Gorlum 10.02.2017 0:39\n */\n\nuse DBAL\\db_mysql;\n\n/**\n * Class TextRecordDescription\n *\n * Describe storage's record attributes\n */\nclass TextRecordDescription {\n  public $table = 'text';\n  public $indexFieldName = 'id';\n  /**\n   * @var db_mysql $db\n   */\n  public $db;\n\n  /**\n   * TextRecordDescription constructor.\n   *\n   * @param db_mysql|null $db\n   */\n  public function __construct($db = null) {\n    $this->db = empty($db) ? SN::$gc->db : $db;\n  }\n\n}\n"
  },
  {
    "path": "classes/TheUser.php",
    "content": "<?php\n\n/**\n * Created by Gorlum 27.02.2017 16:22\n */\n\n/**\n * Class User\n *\n * Dummy object for player class\n *\n */\nclass TheUser {\n\n  /**\n   * @var \\Core\\GlobalContainer $gc\n   */\n  protected $gc;\n\n  /**\n   * TheUser constructor.\n   *\n   * @param \\Core\\GlobalContainer $gc\n   */\n  public function __construct($gc) {\n    $this->gc = $gc;\n  }\n\n  /**\n   * @return string\n   */\n  public function getSkinName() {\n    global $user;\n\n    $skinName = !empty($user['skin']) ? $user['skin'] : DEFAULT_SKIN_NAME;\n\n    return $skinName;\n  }\n\n  /**\n   * @return string\n   */\n  public function getSkinPath() {\n    return 'skins/' . $this->getSkinName() . '/';\n  }\n\n  /**\n   * Set skin name\n   *\n   * @param string $skinName\n   */\n  public function setSkinName($skinName) {\n    global $user;\n\n    $user['skin'] = $skinName;\n  }\n\n  /**\n   * Get skin name\n   *\n   * @return string\n   */\n  public function getTemplateName() {\n    global $user;\n\n    return !empty($user['template']) ? $user['template'] : SnTemplate::getServerDefaultTemplateName();\n  }\n\n  /**\n   * @return bool|null null - not set, true|false - WebP supported or not\n   */\n  public function isWebpSupported() {\n    return !isset($_COOKIE[SN_COOKIE_WEBP]) ? null : !empty($_COOKIE[SN_COOKIE_WEBP]);\n  }\n\n  public function setWebpSupport($isSupported) {\n    sn_setcookie(SN_COOKIE_WEBP, empty($isSupported) ? 0 : 1);\n  }\n\n}\n"
  },
  {
    "path": "classes/Timer.php",
    "content": "<?php\n/**\n * Created by Gorlum 05.08.2018 8:05\n */\n\nclass Timer {\n  /**\n   * @var array[] $times [\n   *                        'time' => (float)microtime(true),\n   *                        'location' => (string)filename&line,\n   *                        'message' => (string)attachedMessage\n   *                     ]\n   */\n  private static $times = [];\n\n  public static function init() {\n    if (empty(static::$times)) {\n      static::$times[] = ['time' => microtime(true)];\n    }\n  }\n\n  /**\n   * @param string $message Message to attach to timestamp\n   */\n  public static function mark($message = '') {\n    static::init();\n\n    foreach (debug_backtrace() as $trace) {\n      if (empty($trace['class']) || $trace['class'] != static::class) {\n        break;\n      }\n\n      $realCall = $trace;\n    }\n\n    static::$times[] = [\n      'time'     => microtime(true),\n      'location' => $realCall['file'] . '@' . $realCall['line'],\n      'message'  => $message,\n    ];\n  }\n\n\n  /**\n   * @param string $message\n   *\n   * @param bool   $fromStart\n   *\n   * @return string\n   */\n  public static function elapsed($message = '', $fromStart = false) {\n    $prevTime = $fromStart ? static::first()['time'] : static::last()['time'];\n\n    static::mark($message);\n\n    empty($prevTime) ? $prevTime = static::first()['time'] : false;\n\n    return number_format(static::last()['time'] - $prevTime, 6) . 's';\n  }\n\n  public static function msg($message, $fromStart = false) {\n    print\n      $message .\n      ($fromStart ? ' FROM START ' : ' in ') .\n      static::elapsed($message, $fromStart) .\n      ' ---- ' . static::last()['location'] .\n      \"\\n<br />\";\n  }\n\n  /**\n   * @return mixed\n   */\n  public static function last() {\n    return end(static::$times);\n  }\n\n  /**\n   * @return mixed\n   */\n  public static function first() {\n    return reset(static::$times);\n  }\n\n  public static function getLog() {\n    return static::$times;\n  }\n\n}\n"
  },
  {
    "path": "classes/Tools.php",
    "content": "<?php\n\n/**\n * Created by Gorlum 01.03.2017 13:58\n */\n\n/**\n * Common set of system-wide tools\n *\n * Should be broken to several service classes\n */\nclass Tools {\n\n  /**\n   * Return number style based by percent of sample\n   *\n   * Color coding (.style - color - ranges):\n   *   .white - white - value == 0 && sample <= 0 - handles division by zero\n   *   .ok - green - value <= 50%\n   *   .info - blue - 50% < value <= 75%\n   *   .notice - yellow - 75% < value <= 90%\n   *   .warning - orange - 90% < value <= 100%\n   *   .error - red -  value > 100%\n   *\n   * @param float $maximum\n   * @param float $value\n   *\n   * @return string - style for number\n   */\n  public static function fillPercentStyle($maximum, $value) {\n    switch (true) {\n      case $maximum == 0 && $value == 0:\n        $result = 'zero_number';\n      break;\n      case $value > $maximum:\n        $result = 'error';\n      break;\n      case $value == $maximum:\n        $result = 'warning';\n      break;\n      case $maximum == 0:\n        $result = 'zero_number';\n      break;\n\n      case ($percent = $value / $maximum) > 0.9:\n        $result = 'warning';\n      break;\n      case $percent > 0.75:\n        $result = 'notice';\n      break;\n      case $percent > 0.50:\n        $result = 'info';\n      break;\n      default:\n        $result = 'ok';\n      break;\n    }\n\n    return $result;\n  }\n\n  /**\n   * Ues Tools::fillPercentStyle to span-tag\n   *\n   * @param $value\n   * @param $sample\n   *\n   * @return string\n   * @see Tools::fillPercentStyle\n   */\n  public static function numberPercentSpan($value, $sample) {\n    return \"<span class=\\\"\" . self::fillPercentStyle($value, $sample) . \"\\\">\" . HelperString::numberFloorAndFormat($value) . \"</span>\";\n  }\n\n}\n"
  },
  {
    "path": "classes/Ube/Ube4_1/Ube4_1Calc.php",
    "content": "<?php\n/**\n * Created by Gorlum 13.02.2018 7:53\n */\n\nnamespace Ube\\Ube4_1;\n\nuse Universe\\Universe;\n\nclass Ube4_1Calc {\n  const BONUS_LIST = [\n    UBE_ATTACK => UBE_ATTACK,\n    UBE_ARMOR  => UBE_ARMOR,\n    UBE_SHIELD => UBE_SHIELD,\n  ];\n\n  const HP_DESTRUCTION_LIMIT_PERCENT = 75; // How much ship structure should be damaged for instant destruction chance, %\n  const DEFENSE_GIVEBACK_PERCENT = 75; // How much defence unit could be restored after destruction\n  const DEFENSE_GIVEBACK_MIN_PERCENT = 80; // Minimum percent of defence to give back\n  const DEFENSE_GIVEBACK_MAX_PERCENT = 120; // Maximum percent of defence to give back\n  const DEBRIS_FROM_SHIPS_MIN_PERCENT = 20; // Minimum amount of debris from destroyed ships\n  const DEBRIS_FROM_SHIPS_MAX_PERCENT = 40; // Maximum amount of debris from destroyed ships\n  const DEBRIS_FROM_CARGO_MIN_PERCENT = 30; // Minimum amount of debris dropped from cargo bays of destroyed ships\n  const DEBRIS_FROM_CARGO_MAX_PERCENT = 70; // Maximum amount of debris dropped from cargo bays of destroyed ships\n\n\n  public function __construct() {\n  }\n\n  public function sn_ube_combat(&$combat_data) {\n    // TODO: Сделать атаку по типам,  когда они будут\n\n    $combat_data[UBE_TIME_SPENT] = microtime(true);\n    $this->sn_ube_combat_round_prepare($combat_data, 0);\n\n    for ($round = 1; $round <= 10; $round++) {\n      // Готовим данные для раунда\n      $this->sn_ube_combat_round_prepare($combat_data, $round);\n\n      // Проводим раунд\n      $this->sn_ube_combat_round_crossfire_fleet($combat_data, $round);\n\n      // Анализируем итоги текущего раунда и готовим данные для следующего\n      if ($this->sn_ube_combat_round_analyze($combat_data, $round) != UBE_COMBAT_RESULT_DRAW) {\n        break;\n      }\n    }\n    $combat_data[UBE_TIME_SPENT] = microtime(true) - $combat_data[UBE_TIME_SPENT];\n\n    // Делать это всегда - нам нужны результаты боя: луна->обломки->количество осташихся юнитов\n    $this->sn_ube_combat_analyze($combat_data);\n  }\n\n// ------------------------------------------------------------------------------------------------\n// Вычисление дополнительной информации для расчета раунда\n  protected function sn_ube_combat_round_prepare(&$combat_data, $round) {\n    $isSimulatorStatic = $combat_data[UBE_OPTIONS][UBE_SIMULATOR_STATIC];\n\n    $round_data = &$combat_data[UBE_ROUNDS][$round];\n    foreach ($round_data[UBE_FLEETS] as $fleet_id => &$fleet_data) {\n      // Кэшируем переменные для легкого доступа к подмассивам\n      $fleet_info = &$combat_data[UBE_FLEETS][$fleet_id];\n      $fleet_data[UBE_FLEET_INFO] = &$fleet_info;\n      $fleet_type = $fleet_info[UBE_FLEET_TYPE];\n\n      foreach ($fleet_data[UBE_COUNT] as $unit_id => $unit_count) {\n        if ($unit_count <= 0) {\n          continue;\n        }\n\n        // TODO:  Добавить процент регенерации щитов\n\n        // Для не-симулятора - рандомизируем каждый раунд значения атаки и щитов\n        $fleet_data[UBE_ATTACK_BASE][$unit_id] = floor($fleet_info[UBE_ATTACK][$unit_id] * ($isSimulatorStatic ? 1 : mt_rand(80, 120) / 100));\n        $fleet_data[UBE_SHIELD_BASE][$unit_id] = floor($fleet_info[UBE_SHIELD][$unit_id] * ($isSimulatorStatic ? 1 : mt_rand(80, 120) / 100));\n        $fleet_data[UBE_ARMOR_BASE][$unit_id] = floor($fleet_info[UBE_ARMOR][$unit_id]);// * ($is_simulator ? 1 : mt_rand(80, 120) / 100));\n\n        $fleet_data[UBE_ATTACK][$unit_id] = $fleet_data[UBE_ATTACK_BASE][$unit_id] * $unit_count;\n        $fleet_data[UBE_SHIELD][$unit_id] = $fleet_data[UBE_SHIELD_BASE][$unit_id] * $unit_count;\n        $fleet_data[UBE_SHIELD_REST][$unit_id] = $fleet_data[UBE_SHIELD_BASE][$unit_id];\n      }\n\n      // Суммируем данные по флоту\n      foreach (static::BONUS_LIST as $bonus_id) {\n        $round_data[$fleet_type][$bonus_id][$fleet_id] += is_array($fleet_data[$bonus_id]) ? array_sum($fleet_data[$bonus_id]) : 0;\n      }\n    }\n\n    // Суммируем данные по атакующим и защитникам\n    foreach (static::BONUS_LIST as $bonus_id) {\n      $round_data[UBE_TOTAL][UBE_DEFENDERS][$bonus_id] = array_sum($round_data[UBE_DEFENDERS][$bonus_id]);\n      $round_data[UBE_TOTAL][UBE_ATTACKERS][$bonus_id] = array_sum($round_data[UBE_ATTACKERS][$bonus_id]);\n    }\n\n    // Высчитываем долю атаки, приходящейся на юнит равную отношению брони юнита к общей броне - крупные цели атакуют чаще\n    foreach ($round_data[UBE_FLEETS] as &$fleet_data) {\n      $fleet_type = $fleet_data[UBE_FLEET_INFO][UBE_FLEET_TYPE];\n      foreach ($fleet_data[UBE_COUNT] as $unit_id => $unit_count) {\n        $fleet_data[UBE_DAMAGE_PERCENT][$unit_id] = $fleet_data[UBE_ARMOR][$unit_id] / $round_data[UBE_TOTAL][$fleet_type][UBE_ARMOR];\n      }\n    }\n  }\n\n// ------------------------------------------------------------------------------------------------\n// Разбирает данные боя для генерации отчета\n  protected function sn_ube_combat_analyze(&$combat_data) {\n    global $config;\n\n    $isSimulatorStatic = $combat_data[UBE_OPTIONS][UBE_SIMULATOR_STATIC];\n    $combat_data[UBE_OPTIONS][UBE_EXCHANGE] = array(RES_METAL => $config->rpg_exchange_metal);\n\n    $exchange = &$combat_data[UBE_OPTIONS][UBE_EXCHANGE];\n    foreach (array(RES_CRYSTAL => 'rpg_exchange_crystal', RES_DEUTERIUM => 'rpg_exchange_deuterium', RES_DARK_MATTER => 'rpg_exchange_darkMatter') as $resource_id => $resource_name) {\n      $exchange[$resource_id] = $config->$resource_name * $exchange[RES_METAL];\n    }\n\n    // Переменные для быстрого доступа к подмассивам\n    $outcome = &$combat_data[UBE_OUTCOME];\n    $fleets_info = &$combat_data[UBE_FLEETS];\n    $last_round_data = &$combat_data[UBE_ROUNDS][count($combat_data[UBE_ROUNDS]) - 1];\n\n    $outcome[UBE_DEBRIS] = array();\n\n    // Генерируем результат боя\n    foreach ($fleets_info as $fleet_id => &$fleet_info) {\n      $fleet_type = $fleet_info[UBE_FLEET_TYPE];\n      // Инициализируем массив результатов для флота\n      $outcome[UBE_FLEETS][$fleet_id] = array(UBE_UNITS_LOST => array());\n      $outcome[$fleet_type][UBE_FLEETS][$fleet_id] = &$outcome[UBE_FLEETS][$fleet_id];\n\n      // Переменные для быстрого доступа к подмассивам\n      $fleet_outcome = &$outcome[UBE_FLEETS][$fleet_id];\n      $fleet_data = &$last_round_data[UBE_FLEETS][$fleet_id];\n\n      foreach ($fleet_info[UBE_COUNT] as $unit_id => $unit_count) {\n        // Вычисляем сколько юнитов осталось и сколько потеряно\n        $units_left = $fleet_data[UBE_COUNT][$unit_id];\n\n        // Восстановление обороны - 75% от уничтоженной\n        if ($fleet_info[UBE_TYPE][$unit_id] == UNIT_DEFENCE) {\n          $units_giveback = $this->defenceGiveBack($unit_count, $units_left, $isSimulatorStatic);\n\n          $units_left += $units_giveback;\n          $fleet_outcome[UBE_DEFENCE_RESTORE][$unit_id] = $units_giveback;\n        }\n\n        // TODO: Сбор металла/кристалла от обороны\n\n        $units_lost = $unit_count - $units_left;\n\n        // Вычисляем емкость трюмов оставшихся кораблей\n        $outcome[$fleet_type][UBE_CAPACITY][$fleet_id] += $fleet_info[UBE_CAPACITY][$unit_id] * $units_left;\n\n        // Вычисляем потери в ресурсах\n        if ($units_lost) {\n          $fleet_outcome[UBE_UNITS_LOST][$unit_id] = $units_lost;\n\n          foreach ($fleet_info[UBE_PRICE] as $resource_id => $unit_prices) {\n            if (!$unit_prices[$unit_id]) {\n              continue;\n            }\n\n            // ...чистыми\n            $resources_lost = $units_lost * $unit_prices[$unit_id];\n            $fleet_outcome[UBE_RESOURCES_LOST][$resource_id] += $resources_lost;\n\n            // Если это корабль - прибавляем потери к обломкам на орбите\n            if ($fleet_info[UBE_TYPE][$unit_id] == UNIT_SHIPS) {\n              $debrisFraction = (\n                $isSimulatorStatic\n                  ? (\n                    static::DEBRIS_FROM_SHIPS_MIN_PERCENT +\n                    static::DEBRIS_FROM_SHIPS_MAX_PERCENT) / 2\n                  : mt_rand(\n                  static::DEBRIS_FROM_SHIPS_MIN_PERCENT,\n                  static::DEBRIS_FROM_SHIPS_MAX_PERCENT)\n                ) / 100;\n              $outcome[UBE_DEBRIS][$resource_id] += floor($resources_lost * $debrisFraction);\n            }\n\n            // ...в металле\n            $resources_lost_in_metal = $resources_lost * $exchange[$resource_id];\n            $fleet_outcome[UBE_RESOURCES_LOST_IN_METAL][RES_METAL] += $resources_lost_in_metal;\n          }\n        }\n      }\n\n      // На планете ($fleet_id = 0) ресурсы в космос не выбрасываются\n      if ($fleet_id == 0) {\n        continue;\n      }\n\n      // Количество ресурсов флота\n      $fleet_total_resources = empty($fleet_info[UBE_RESOURCES]) ? 0 : array_sum($fleet_info[UBE_RESOURCES]);\n      // Если на борту нет ресурсов - зачем нам все это?\n      if ($fleet_total_resources == 0) {\n        continue;\n      }\n\n      // Емкость трюмов флота\n      $fleet_capacity = $outcome[$fleet_type][UBE_CAPACITY][$fleet_id];\n      // Если емкость трюмов меньше количество ресурсов - часть ресов выбрасываем нахуй\n      if ($fleet_capacity < $fleet_total_resources) {\n        $left_percent = $fleet_capacity / $fleet_total_resources; // Сколько ресурсов будет оставлено\n        foreach ($fleet_info[UBE_RESOURCES] as $resource_id => $resource_amount) {\n          // Не просчитываем ресурсы, которых нет на борту кораблей флота\n          if (!$resource_amount) {\n            continue;\n          }\n\n          // TODO Восстанавливаем ошибку округления - придумать нормальный алгоритм - вроде round() должно быть достаточно. Проверить\n          $fleet_outcome[UBE_RESOURCES][$resource_id] = round($left_percent * $resource_amount);\n          $resource_dropped = $resource_amount - $fleet_outcome[UBE_RESOURCES][$resource_id];\n          $fleet_outcome[UBE_CARGO_DROPPED][$resource_id] = $resource_dropped;\n\n          $cargoDroppedFraction = (\n            $isSimulatorStatic\n              ? (static::DEBRIS_FROM_CARGO_MIN_PERCENT +\n                static::DEBRIS_FROM_CARGO_MAX_PERCENT) / 2\n              : mt_rand(\n              static::DEBRIS_FROM_CARGO_MIN_PERCENT,\n              static::DEBRIS_FROM_CARGO_MAX_PERCENT)\n            ) / 100;\n          $outcome[UBE_DEBRIS][$resource_id] += round($resource_dropped * $cargoDroppedFraction); // TODO: Configurize\n          $fleet_outcome[UBE_RESOURCES_LOST_IN_METAL][RES_METAL] += $resource_dropped * $exchange[$resource_id];\n        }\n        $fleet_total_resources = array_sum($fleet_outcome[UBE_RESOURCES]);\n      }\n\n      $outcome[$fleet_type][UBE_CAPACITY][$fleet_id] = $fleet_capacity - $fleet_total_resources;\n    }\n\n    $outcome[UBE_COMBAT_RESULT] = !isset($last_round_data[UBE_OUTCOME]) || $last_round_data[UBE_OUTCOME] == UBE_COMBAT_RESULT_DRAW_END ? UBE_COMBAT_RESULT_DRAW : $last_round_data[UBE_OUTCOME];\n    // SFR - Small Fleet Reconnaissance ака РМФ\n    $outcome[UBE_SFR] = count($combat_data[UBE_ROUNDS]) == 2 && $outcome[UBE_COMBAT_RESULT] == UBE_COMBAT_RESULT_LOSS;\n\n    if (!$combat_data[UBE_OPTIONS][UBE_LOADED]) {\n      if ($combat_data[UBE_OPTIONS][UBE_MOON_WAS]) {\n        $outcome[UBE_MOON] = UBE_MOON_WAS;\n      } else {\n        $this->sn_ube_combat_analyze_moon($outcome, $isSimulatorStatic);\n      }\n\n      // Лутаем ресурсы - если аттакер выиграл\n      if ($outcome[UBE_COMBAT_RESULT] == UBE_COMBAT_RESULT_WIN) {\n        $this->sn_ube_combat_analyze_loot($combat_data);\n        if ($combat_data[UBE_OPTIONS][UBE_MOON_WAS] && $combat_data[UBE_OPTIONS][UBE_MISSION_TYPE] == MT_DESTROY) {\n          $this->sn_ube_combat_analyze_moon_destroy($combat_data);\n        }\n      }\n    }\n\n  }\n\n// ------------------------------------------------------------------------------------------------\n  protected function sn_ube_combat_analyze_moon(&$outcome, $is_simulator) {\n    $outcome[UBE_DEBRIS_TOTAL] = 0;\n    foreach ([RES_METAL, RES_CRYSTAL] as $resource_id) {\n      $outcome[UBE_DEBRIS_TOTAL] += $outcome[UBE_DEBRIS][$resource_id];\n    }\n\n    $outcome[UBE_DEBRIS_ORIGINAL] = $outcome[UBE_DEBRIS];\n\n    if ($outcome[UBE_DEBRIS_TOTAL]) {\n      if ($outcome[UBE_MOON_CHANCE] = Universe::moonCalcChanceFromDebris($outcome[UBE_DEBRIS_TOTAL])) {\n        $outcome[UBE_MOON_SIZE] = $is_simulator\n          // On simulator moon always will be average size\n          ? round(max(1, $outcome[UBE_MOON_CHANCE] / 2) * 150 + 2000)\n          : Universe::moonRollSize($outcome[UBE_DEBRIS_TOTAL]);\n        if ($outcome[UBE_MOON_SIZE]) {\n          // Got moon\n          $outcome[UBE_MOON] = UBE_MOON_CREATE_SUCCESS;\n\n          if ($outcome[UBE_DEBRIS_TOTAL] <= Universe::moonMaxDebris()) {\n            $outcome[UBE_DEBRIS_TOTAL] = 0;\n            $outcome[UBE_DEBRIS] = [];\n          } else {\n            $moon_debris_spent = Universe::moonMaxDebris();\n            $moon_debris_left_percent = ($outcome[UBE_DEBRIS_TOTAL] - $moon_debris_spent) / $outcome[UBE_DEBRIS_TOTAL];\n\n            $outcome[UBE_DEBRIS_TOTAL] = 0;\n            foreach ([RES_METAL, RES_CRYSTAL] as $resource_id) {\n              $outcome[UBE_DEBRIS][$resource_id] = floor($outcome[UBE_DEBRIS][$resource_id] * $moon_debris_left_percent);\n              $outcome[UBE_DEBRIS_TOTAL] += $outcome[UBE_DEBRIS][$resource_id];\n            }\n          }\n        } else {\n          $outcome[UBE_MOON] = UBE_MOON_CREATE_FAILED;\n        }\n      }\n    } else {\n      $outcome[UBE_MOON] = UBE_MOON_NONE;\n    }\n  }\n\n// ------------------------------------------------------------------------------------------------\n  protected function sn_ube_combat_analyze_moon_destroy(&$combat_data) {\n    // TODO: $is_simulator\n    $reapers = 0;\n    foreach ($combat_data[UBE_ROUNDS][count($combat_data[UBE_ROUNDS]) - 1][UBE_FLEETS] as $fleet_data) {\n      if ($fleet_data[UBE_FLEET_INFO][UBE_FLEET_TYPE] == UBE_ATTACKERS) {\n        foreach ($fleet_data[UBE_COUNT] as $unit_id => $unit_count) {\n          // TODO: Работа по группам - группа \"Уничтожители лун\"\n          $reapers += ($unit_id == SHIP_HUGE_DEATH_STAR) ? $unit_count : 0;\n        }\n      }\n    }\n\n    $moon_size = $combat_data[UBE_OUTCOME][UBE_PLANET][PLANET_SIZE];\n    if ($reapers) {\n      $random = mt_rand(1, 100);\n      $combat_data[UBE_OUTCOME][UBE_MOON_DESTROY_CHANCE] = max(1, min(99, round((100 - sqrt($moon_size)) * sqrt($reapers))));\n      $combat_data[UBE_OUTCOME][UBE_MOON_REAPERS_DIE_CHANCE] = round(sqrt($moon_size) / 2 + sqrt($reapers));\n      $combat_data[UBE_OUTCOME][UBE_MOON] = $random <= $combat_data[UBE_OUTCOME][UBE_MOON_DESTROY_CHANCE] ? UBE_MOON_DESTROY_SUCCESS : UBE_MOON_DESTROY_FAILED;\n      $random = mt_rand(1, 100);\n      $combat_data[UBE_OUTCOME][UBE_MOON_REAPERS] = $random <= $combat_data[UBE_OUTCOME][UBE_MOON_REAPERS_DIE_CHANCE] ? UBE_MOON_REAPERS_DIED : UBE_MOON_REAPERS_RETURNED;\n    } else {\n      $combat_data[UBE_OUTCOME][UBE_MOON_REAPERS] = UBE_MOON_REAPERS_NONE;\n    }\n  }\n\n// ------------------------------------------------------------------------------------------------\n  protected function sn_ube_combat_analyze_loot(&$combat_data) {\n    $exchange = &$combat_data[UBE_OPTIONS][UBE_EXCHANGE];\n    $planet_resource_list = &$combat_data[UBE_FLEETS][0][UBE_RESOURCES];\n    $outcome = &$combat_data[UBE_OUTCOME];\n\n    $planet_looted_in_metal = 0;\n    $planet_resource_looted = array();\n    $planet_resource_total = is_array($planet_resource_list) ? array_sum($planet_resource_list) : 0;\n    if ($planet_resource_total && ($total_capacity = array_sum($outcome[UBE_ATTACKERS][UBE_CAPACITY]))) {\n      // Можно вывести только половину ресурсов, но не больше, чем общая вместимость флотов атакующих\n      $planet_lootable = min($planet_resource_total / 2, $total_capacity);\n      // Вычисляем процент вывоза. Каждого ресурса будет вывезено в одинаковых пропорциях\n      $planet_lootable_percent = $planet_lootable / $planet_resource_total;\n\n      // Вычисляем какой процент общей емкости трюмов атакующих будет задействован\n      $total_lootable = min($planet_lootable, $total_capacity);\n\n      // Вычисляем сколько ресурсов вывезено\n      foreach ($outcome[UBE_ATTACKERS][UBE_CAPACITY] as $fleet_id => $fleet_capacity) {\n        $looted_in_metal = 0;\n        $fleet_loot_data = array();\n        foreach ($planet_resource_list as $resource_id => $resource_amount) {\n          // TODO Восстанавливаем ошибку округления - придумать нормальный алгоритм - вроде round() должно быть достаточно. Проверить\n          $fleet_lootable_percent = $fleet_capacity / $total_capacity;\n          $looted = round($resource_amount * $planet_lootable_percent * $fleet_lootable_percent);\n          $fleet_loot_data[$resource_id] = -$looted;\n          $planet_resource_looted[$resource_id] += $looted;\n          $looted_in_metal -= $looted * $exchange[$resource_id];\n        }\n        $outcome[UBE_FLEETS][$fleet_id][UBE_RESOURCES_LOOTED] = $fleet_loot_data;\n        $outcome[UBE_FLEETS][$fleet_id][UBE_RESOURCES_LOST_IN_METAL][RES_METAL] += $looted_in_metal;\n        $planet_looted_in_metal += $looted_in_metal;\n      }\n    }\n\n    $outcome[UBE_FLEETS][0][UBE_RESOURCES_LOST_IN_METAL][RES_METAL] -= $planet_looted_in_metal;\n    $outcome[UBE_FLEETS][0][UBE_RESOURCES_LOOTED] = $planet_resource_looted;\n  }\n\n\n// ------------------------------------------------------------------------------------------------\n// Анализирует результаты раунда и генерирует данные для следующего раунда\n  protected function sn_ube_combat_round_analyze(&$combat_data, $round) {\n    $round_data = &$combat_data[UBE_ROUNDS][$round];\n    $round_data[UBE_OUTCOME] = UBE_COMBAT_RESULT_DRAW;\n\n    $outcome = array();\n    $next_round_fleet = array();\n    foreach ($round_data[UBE_FLEETS] as $fleet_id => &$fleet_data) {\n      if (array_sum($fleet_data[UBE_COUNT]) <= 0) {\n        continue;\n      }\n\n      foreach ($fleet_data[UBE_COUNT] as $unit_id => $unit_count) {\n        if ($unit_count <= 0) {\n          continue;\n        }\n        $next_round_fleet[$fleet_id][UBE_COUNT][$unit_id] = $unit_count;\n        $next_round_fleet[$fleet_id][UBE_ARMOR][$unit_id] = $fleet_data[UBE_ARMOR][$unit_id];\n        $next_round_fleet[$fleet_id][UBE_ARMOR_REST][$unit_id] = $fleet_data[UBE_ARMOR_REST][$unit_id];\n        $outcome[$fleet_data[UBE_FLEET_INFO][UBE_FLEET_TYPE]] = 1;\n      }\n    }\n\n    // Проверяем - если кого-то не осталось или не осталось обоих - заканчиваем цикл\n    if (count($outcome) == 0 || $round == 10) {\n      $round_data[UBE_OUTCOME] = UBE_COMBAT_RESULT_DRAW_END;\n    } elseif (count($outcome) == 1) {\n      $round_data[UBE_OUTCOME] = isset($outcome[UBE_ATTACKERS]) ? UBE_COMBAT_RESULT_WIN : UBE_COMBAT_RESULT_LOSS;\n    } elseif (count($outcome) == 2) {\n      if ($round < 10) {\n        $combat_data[UBE_ROUNDS][$round + 1][UBE_FLEETS] = $next_round_fleet;\n      }\n    }\n\n    return ($round_data[UBE_OUTCOME]);\n  }\n\n\n// ------------------------------------------------------------------------------------------------\n// Рассчитывает результат столкновения флотов ака раунд\n  protected function sn_ube_combat_round_crossfire_fleet(&$combat_data, $round) {\n    if (BE_DEBUG === true) {\n      // sn_ube_combat_helper_round_header($round);\n    }\n\n    $round_data = &$combat_data[UBE_ROUNDS][$round];\n    // Проводим бой. Сталкиваем каждый корабль атакующего с каждым кораблем атакуемого\n    foreach ($round_data[UBE_ATTACKERS][UBE_ATTACK] as $attack_fleet_id => $temp) {\n      $attack_fleet_data = &$round_data[UBE_FLEETS][$attack_fleet_id];\n      foreach ($round_data[UBE_DEFENDERS][UBE_ATTACK] as $defend_fleet_id => $temp2) {\n        $defend_fleet_data = &$round_data[UBE_FLEETS][$defend_fleet_id];\n\n        foreach ($attack_fleet_data[UBE_COUNT] as $attack_unit_id => $attack_unit_count) {\n          // if($attack_unit_count <= 0) continue; // TODO: Это пока нельзя включать - вот если будут \"боевые порядки юнитов...\"\n          foreach ($defend_fleet_data[UBE_COUNT] as $defend_unit_id => $defend_unit_count) {\n            $this->sn_ube_combat_round_crossfire_unit2($attack_fleet_data, $defend_fleet_data, $attack_unit_id, $defend_unit_id, $combat_data[UBE_OPTIONS]);\n            $this->sn_ube_combat_round_crossfire_unit2($defend_fleet_data, $attack_fleet_data, $defend_unit_id, $attack_unit_id, $combat_data[UBE_OPTIONS]);\n          }\n        }\n      }\n    }\n\n    if (BE_DEBUG === true) {\n      // sn_ube_combat_helper_round_footer();\n    }\n  }\n\n\n  // ------------------------------------------------------------------------------------------------\n  // Рассчитывает результат столкновения двух юнитов ака ход\n  protected function sn_ube_combat_round_crossfire_unit2(&$attack_fleet_data, &$defend_fleet_data, $attack_unit_id, $defend_unit_id, &$combat_options) {\n    if ($defend_fleet_data[UBE_COUNT][$defend_unit_id] <= 0) {\n      return;\n    }\n\n    // Вычисляем прямой дамадж от атакующего юнита с учетом размера атакуемого\n    $direct_damage = floor($attack_fleet_data[UBE_ATTACK][$attack_unit_id] * $defend_fleet_data[UBE_DAMAGE_PERCENT][$defend_unit_id]);\n\n    // Применяем амплифай, если есть\n    $amplify = $attack_fleet_data[UBE_FLEET_INFO][UBE_AMPLIFY][$attack_unit_id][$defend_unit_id];\n    $amplify = $amplify ? $amplify : 1;\n    $amplified_damage = floor($direct_damage * $amplify);\n\n    // Проверяем - не взорвался ли текущий раненный юнит\n    $this->sn_ube_combat_round_crossfire_unit_damage_current($defend_fleet_data, $defend_unit_id, $amplified_damage, $units_lost, $units_boomed, $combat_options);\n\n    $defend_unit_base_defence = $defend_fleet_data[UBE_SHIELD_BASE][$defend_unit_id] + $defend_fleet_data[UBE_ARMOR_BASE][$defend_unit_id];\n\n    // todo Добавить взрывы от полуповрежденных юнитов - т.е. заранее вычислить из убитых юнитов еще количество убитых умножить на вероятность от структуры\n\n    // Вычисляем, сколько юнитов взорвалось полностью\n    $units_lost_full = floor($amplified_damage / $defend_unit_base_defence);\n    // Уменьшаем дамадж на ту же сумму\n    $amplified_damage -= $units_lost_full * $defend_unit_base_defence;\n    // Вычисляем, сколько юнитов осталось\n    $defend_fleet_data[UBE_COUNT][$defend_unit_id] = max(0, $defend_fleet_data[UBE_COUNT][$defend_unit_id] - $units_lost_full);\n    // Уменьшаем броню подразделения на броню потерянных юнитов\n    $defend_fleet_data[UBE_ARMOR][$defend_unit_id] -= $units_lost_full * $defend_fleet_data[UBE_ARMOR_BASE][$defend_unit_id];\n    $defend_fleet_data[UBE_SHIELD][$defend_unit_id] -= $units_lost_full * $defend_fleet_data[UBE_SHIELD_BASE][$defend_unit_id];\n\n    // Проверяем - не взорвался ли текущий юнит\n    $this->sn_ube_combat_round_crossfire_unit_damage_current($defend_fleet_data, $defend_unit_id, $amplified_damage, $units_lost, $units_boomed, $combat_options);\n  }\n\n  /**\n   * @param $defend_fleet_data\n   * @param $defend_unit_id\n   * @param $amplified_damage\n   * @param $units_lost\n   * @param $units_boomed\n   * @param $combat_options\n   *\n   * @return bool\n   */\n  protected function sn_ube_combat_round_crossfire_unit_damage_current(&$defend_fleet_data, $defend_unit_id, &$amplified_damage, &$units_lost, &$units_boomed, &$combat_options) {\n    $unit_is_lost = false;\n\n    $units_boomed = $units_boomed ? $units_boomed : 0;\n    $units_lost = $units_lost ? $units_lost : 0;\n\n    if ($defend_fleet_data[UBE_COUNT][$defend_unit_id] > 0 && $amplified_damage) {\n      $damage_to_shield = min($amplified_damage, $defend_fleet_data[UBE_SHIELD_REST][$defend_unit_id]);\n      $amplified_damage -= $damage_to_shield;\n      $defend_fleet_data[UBE_SHIELD_REST][$defend_unit_id] -= $damage_to_shield;\n\n      $damage_to_armor = min($amplified_damage, $defend_fleet_data[UBE_ARMOR_REST][$defend_unit_id]);\n      $amplified_damage -= $damage_to_armor;\n      $defend_fleet_data[UBE_ARMOR_REST][$defend_unit_id] -= $damage_to_armor;\n\n      // Если брони не осталось - юнит потерян\n      if ($defend_fleet_data[UBE_ARMOR_REST][$defend_unit_id] <= 0) {\n        $unit_is_lost = true;\n      } elseif ($defend_fleet_data[UBE_SHIELD_REST][$defend_unit_id] <= 0) {\n        // Если броня осталось, но не осталось щитов - прошел дамадж по броне и надо проверить - не взорвался ли корабль\n        $last_unit_hp = $defend_fleet_data[UBE_ARMOR_REST][$defend_unit_id];\n        $last_unit_percent = $last_unit_hp / $defend_fleet_data[UBE_ARMOR_BASE][$defend_unit_id] * 100;\n\n        $random = $combat_options[UBE_SIMULATOR_STATIC] ? static::HP_DESTRUCTION_LIMIT_PERCENT / 2 : mt_rand(0, 100);\n        if ($last_unit_percent <= static::HP_DESTRUCTION_LIMIT_PERCENT && $last_unit_percent <= $random) {\n          $unit_is_lost = true;\n          $units_boomed++;\n          $damage_to_armor += $defend_fleet_data[UBE_ARMOR_REST][$defend_unit_id];\n          $defend_fleet_data[UBE_UNITS_BOOM][$defend_unit_id]++;\n          $defend_fleet_data[UBE_ARMOR_REST][$defend_unit_id] = 0;\n        }\n      }\n\n      $defend_fleet_data[UBE_ARMOR][$defend_unit_id] -= $damage_to_armor;\n      $defend_fleet_data[UBE_SHIELD][$defend_unit_id] -= $damage_to_shield;\n\n      if ($unit_is_lost) {\n        $units_lost++;\n        $defend_fleet_data[UBE_COUNT][$defend_unit_id]--;\n        if ($defend_fleet_data[UBE_COUNT][$defend_unit_id]) {\n          $defend_fleet_data[UBE_ARMOR_REST][$defend_unit_id] = $defend_fleet_data[UBE_ARMOR_BASE][$defend_unit_id];\n          $defend_fleet_data[UBE_SHIELD_REST][$defend_unit_id] = $defend_fleet_data[UBE_SHIELD_BASE][$defend_unit_id];\n        }\n      }\n    }\n\n    return $unit_is_lost;\n  }\n\n  /**\n   * @param $unit_count\n   * @param $units_left\n   * @param $isSimulator\n   *\n   * @return int\n   */\n  protected function defenceGiveBack($unit_count, $units_left, $isSimulator) {\n    $units_lost = $unit_count - $units_left;\n    if ($isSimulator) {\n      // for simulation just return 75% of loss\n      $units_giveback = round($units_lost * static::DEFENSE_GIVEBACK_PERCENT / 100);\n    } else {\n      if ($unit_count > 10) {\n        // if there were more then 10 defense elements - mass-calculating giveback\n\n        // Calculating random part of return - it would be a decimal\n        $random = mt_rand(static::DEFENSE_GIVEBACK_MIN_PERCENT * 1000, static::DEFENSE_GIVEBACK_MAX_PERCENT * 1000) / (100 * 1000); // Trick to get random with high precision\n        // Limiting max return to 100% - in case if we messed with min/max chance and/or giveback\n        $random = min($random * static::DEFENSE_GIVEBACK_PERCENT, 100);\n        $units_giveback = round($units_lost * $random / 100);\n      } else {\n        // if there were less then 10 defense elements - calculating giveback per element\n        $units_giveback = 0;\n        for ($i = 1; $i <= $units_lost; $i++) {\n          if (mt_rand(1, 100) <= static::DEFENSE_GIVEBACK_PERCENT) {\n            $units_giveback++;\n          }\n        }\n      }\n    }\n\n    return $units_giveback;\n  }\n\n}\n"
  },
  {
    "path": "classes/Ube/Ube4_1/Ube4_1Prepare.php",
    "content": "<?php\n/**\n * Created by Gorlum 13.02.2018 8:17\n */\n\nnamespace Ube\\Ube4_1;\n\nuse Fleet\\FleetDispatchEvent;\nuse Planet\\DBStaticPlanet;\n\nclass Ube4_1Prepare {\n  const CONVERT_TECHS = [\n    TECH_WEAPON => UBE_ATTACK,\n    TECH_ARMOR  => UBE_ARMOR,\n    TECH_SHIELD => UBE_SHIELD,\n  ];\n  const CONVERT_UNIT_PARAMS = [\n    UBE_ATTACK => 'attack',\n    UBE_ARMOR  => 'armor',\n    UBE_SHIELD => 'shield',\n  ];\n\n  /**\n   * Заполняет начальные данные по данным миссии\n   *\n   * @param ?FleetDispatchEvent $fleetEvent\n   *\n   * @return array\n   */\n  public function prepareFromMissionArray($fleetEvent, $fleet_list_on_hold, $acs_fleet_list) {\n    /*\n    UBE_OPTIONS[UBE_LOADED]\n    UBE_OPTIONS[UBE_SIMULATOR_STATIC]\n    UBE_OPTIONS[UBE_EXCHANGE]\n    UBE_OPTIONS[UBE_MOON_WAS]\n    */\n\n    // We refreshed fleet and planet before - so nothing to do here\n    $destination_planet = $fleetEvent->dstPlanetRow;\n\n    $combat_data = [\n      UBE_TIME           => $fleetEvent->eventTimeStamp,\n      UBE_OBJ_PREPARATOR => $this,\n    ];\n    // TODO: Не допускать атаки игроком своих же флотов - т.е. холд против атаки\n    // Готовим инфу по атакуемой планете\n    $this->ube_attack_prepare_planet($combat_data, $destination_planet);\n\n    // Готовим инфу по удержанию\n    foreach ($fleet_list_on_hold as $fleet) {\n      $this->ube_attack_prepare_fleet($combat_data, $fleet, false);\n    }\n\n    // Готовим инфу по атакующим\n    foreach ($acs_fleet_list as $fleet) {\n      $this->ube_attack_prepare_fleet($combat_data, $fleet, true);\n    }\n\n    // Готовим опции\n    $combat_data[UBE_OPTIONS][UBE_MOON_WAS] = $destination_planet['planet_type'] == PT_MOON || is_array(DBStaticPlanet::db_planet_by_parent($destination_planet['id'], true, '`id`'));\n    $combat_data[UBE_OPTIONS][UBE_MISSION_TYPE] = $fleetEvent->missionId;\n    global $config;\n    $combat_data[UBE_OPTIONS][UBE_METHOD] = $config->game_ube_method ? $config->game_ube_method : 0;\n\n    $this->sn_ube_combat_prepare_first_round($combat_data);\n\n    return $combat_data;\n  }\n\n  // ------------------------------------------------------------------------------------------------\n  // Заполняет данные по игроку\n  protected function ube_attack_prepare_player(&$combat_data, $player_id, $is_attacker) {\n    if (!isset($combat_data[UBE_PLAYERS][$player_id])) {\n      $combat_data[UBE_PLAYERS][$player_id] = [\n        UBE_ATTACKER => $is_attacker,\n      ];\n      $player_info = &$combat_data[UBE_PLAYERS][$player_id];\n\n      $player_data = db_user_by_id($player_id, true);\n      $player_info[UBE_NAME] = $player_data['username'];\n      $player_info[UBE_AUTH_LEVEL] = $player_data['authlevel'];\n      $combat_data[UBE_OPTIONS][UBE_COMBAT_ADMIN] = $combat_data[UBE_OPTIONS][UBE_COMBAT_ADMIN] || $player_data['authlevel']; // Участвует ли админ в бою?\n      $player_info[UBE_PLAYER_DATA] = $player_data;\n\n      $admiral_bonus = mrc_get_level($player_data, false, MRC_ADMIRAL) * get_unit_param(MRC_ADMIRAL, P_BONUS_VALUE) / 100;\n      foreach (static::CONVERT_TECHS as $unit_id => $ube_id) {\n        $player_info[UBE_BONUSES][$ube_id] += mrc_get_level($player_data, [], $unit_id) * get_unit_param($unit_id, P_BONUS_VALUE) / 100 + $admiral_bonus;\n      }\n    } else {\n      $combat_data[UBE_PLAYERS][$player_id][UBE_ATTACKER] = $combat_data[UBE_PLAYERS][$player_id][UBE_ATTACKER] || $is_attacker;\n    }\n  }\n\n\n  /**\n   * Заполняет данные по флоту\n   *\n   * Через жопу для сохранения обратной совместимости\n   *\n   * @param $combat_data\n   * @param $fleet\n   * @param $is_attacker\n   *\n   * @deprecated\n   */\n  public function ube_attack_prepare_fleet(&$combat_data, &$fleet, $is_attacker) {\n    $fleet_owner_id = $fleet['fleet_owner'];\n    $fleet_id = $fleet['fleet_id'];\n\n    $this->ube_attack_prepare_player($combat_data, $fleet_owner_id, $is_attacker);\n\n    $fleet_data = sys_unit_str2arr($fleet['fleet_array']);\n\n    $combat_data[UBE_FLEETS][$fleet_id][UBE_OWNER] = $fleet_owner_id;\n    $fleet_info = &$combat_data[UBE_FLEETS][$fleet_id];\n    $fleet_info[UBE_FLEET_GROUP] = $fleet['fleet_group'];\n    foreach ($fleet_data as $unit_id => $unit_count) {\n      if (!$unit_count) {\n        continue;\n      }\n\n      $unit_type = get_unit_param($unit_id, P_UNIT_TYPE);\n      if ($unit_type == UNIT_SHIPS || $unit_type == UNIT_DEFENCE) {\n        $fleet_info[UBE_COUNT][$unit_id] = $unit_count;\n      }\n    }\n\n    $fleet_info[UBE_RESOURCES] = array(\n      RES_METAL     => $fleet['fleet_resource_metal'],\n      RES_CRYSTAL   => $fleet['fleet_resource_crystal'],\n      RES_DEUTERIUM => $fleet['fleet_resource_deuterium'],\n    );\n\n    $fleet_info[UBE_PLANET] = array(\n      // TODO: Брать имя и кэшировать ИД и имя планеты?\n      PLANET_GALAXY => $fleet['fleet_start_galaxy'],\n      PLANET_SYSTEM => $fleet['fleet_start_system'],\n      PLANET_PLANET => $fleet['fleet_start_planet'],\n      PLANET_TYPE   => $fleet['fleet_start_type'],\n    );\n\n    // Calling other functions in call-chain\n    // TODO - THIS IS WRONG! YOU SHOULD NEVER DO THIS! THIS IS DIRTY HACK!\n    sn_function_call('ube_attack_prepare_fleet', array(&$combat_data, &$fleet, $is_attacker));\n  }\n\n  /**\n   * Заполняет данные по планете\n   *\n   * @param $combat_data\n   * @param $planet\n   */\n  protected function ube_attack_prepare_planet(&$combat_data, &$planet) {\n    $player_id = $planet['id_owner'];\n\n    $this->ube_attack_prepare_player($combat_data, $player_id, false);\n\n    $player = &$combat_data[UBE_PLAYERS][$player_id][UBE_PLAYER_DATA];\n\n    $combat_data[UBE_FLEETS][0] = array(UBE_OWNER => $player_id);\n    $fleet_info = &$combat_data[UBE_FLEETS][0];\n\n    foreach (sn_get_groups('combat') as $unit_id) {\n      if ($unit_count = mrc_get_level($player, $planet, $unit_id)) {\n        $fleet_info[UBE_COUNT][$unit_id] = $unit_count;\n      }\n    }\n\n    foreach (sn_get_groups('resources_loot') as $resource_id) {\n      $fleet_info[UBE_RESOURCES][$resource_id] = floor(mrc_get_level($player, $planet, $resource_id));\n    }\n\n    if ($fortifier_level = mrc_get_level($player, $planet, MRC_FORTIFIER)) {\n      $fortifier_bonus = $fortifier_level * get_unit_param(MRC_FORTIFIER, P_BONUS_VALUE) / 100;\n      foreach (Ube4_1Calc::BONUS_LIST as $ube_id) {\n        $fleet_info[UBE_BONUSES][$ube_id] += $fortifier_bonus;\n      }\n    }\n\n    $combat_data[UBE_OUTCOME][UBE_PLANET] = $fleet_info[UBE_PLANET] = array(\n      PLANET_ID     => $planet['id'],\n      PLANET_NAME   => $planet['name'],\n      PLANET_GALAXY => $planet['galaxy'],\n      PLANET_SYSTEM => $planet['system'],\n      PLANET_PLANET => $planet['planet'],\n      PLANET_TYPE   => $planet['planet_type'],\n      PLANET_SIZE   => $planet['diameter'],\n    );\n\n    $combat_data[UBE_OPTIONS][UBE_DEFENDER_ACTIVE] = $player['onlinetime'] >= $combat_data[UBE_TIME] - PLAYER_INACTIVE_TIMEOUT;\n  }\n\n\n  public function sn_ube_simulator_fleet_converter($sym_attacker, $sym_defender) {\n    $combat_data = [\n      UBE_OPTIONS => [\n        UBE_SIMULATOR        => true,\n        UBE_SIMULATOR_STATIC => sys_get_param_int('simulator'),\n        UBE_MISSION_TYPE     => MT_ATTACK,\n      ],\n\n      UBE_PLAYERS => [],\n\n      UBE_FLEETS => [],\n\n      UBE_OBJ_PREPARATOR => $this,\n    ];\n\n    $this->sn_ube_simulator_fill_side($combat_data, $sym_defender, false);\n    $this->sn_ube_simulator_fill_side($combat_data, $sym_attacker, true);\n\n    $this->sn_ube_combat_prepare_first_round($combat_data);\n\n    return $combat_data;\n  }\n\n  // ------------------------------------------------------------------------------------------------\n  // Преобразовывает данные симулятора в данные для расчета боя\n  protected function sn_ube_simulator_fill_side(&$combat_data, $side_info, $attacker, $player_id = -1) {\n    $player_id = $player_id == -1 ? count($combat_data[UBE_PLAYERS]) : $player_id;\n\n    foreach ($side_info as $fleet_data) {\n      $combat_data[UBE_PLAYERS][$player_id][UBE_NAME] = $attacker ? 'Attacker' : 'Defender';\n      $combat_data[UBE_PLAYERS][$player_id][UBE_ATTACKER] = $attacker;\n\n      $combat_data[UBE_FLEETS][$player_id][UBE_OWNER] = $player_id;\n      foreach ($fleet_data as $unit_id => $unit_count) {\n        if (!$unit_count) {\n          continue;\n        }\n\n        $unit_type = get_unit_param($unit_id, P_UNIT_TYPE);\n\n        if ($unit_type == UNIT_SHIPS || $unit_type == UNIT_DEFENCE) {\n          $combat_data[UBE_FLEETS][$player_id][UBE_COUNT][$unit_id] = $unit_count;\n        } elseif ($unit_type == UNIT_RESOURCES) {\n          $combat_data[UBE_FLEETS][$player_id][UBE_RESOURCES][$unit_id] = $unit_count;\n        } elseif ($unit_type == UNIT_TECHNOLOGIES) {\n          $combat_data[UBE_PLAYERS][$player_id][UBE_BONUSES][static::CONVERT_TECHS[$unit_id]] += $unit_count * get_unit_param($unit_id, P_BONUS_VALUE) / 100;\n        } elseif ($unit_type == UNIT_GOVERNORS) {\n          if ($unit_id == MRC_FORTIFIER) {\n            foreach (static::CONVERT_TECHS as $ube_id) {\n              $combat_data[UBE_FLEETS][$player_id][UBE_BONUSES][$ube_id] += $unit_count * get_unit_param($unit_id, P_BONUS_VALUE) / 100;\n            }\n          }\n        } elseif ($unit_type == UNIT_MERCENARIES) {\n          if ($unit_id == MRC_ADMIRAL) {\n            foreach (static::CONVERT_TECHS as $ube_id) {\n              $combat_data[UBE_PLAYERS][$player_id][UBE_BONUSES][$ube_id] += $unit_count * get_unit_param($unit_id, P_BONUS_VALUE) / 100;\n            }\n          }\n        }\n      }\n    }\n  }\n\n  // ------------------------------------------------------------------------------------------------\n  protected function sn_ube_combat_prepare_first_round(&$combat_data) {\n    // Готовим информацию для первого раунда - проводим все нужные вычисления из исходных данных\n    $first_round_data = array();\n    foreach ($combat_data[UBE_FLEETS] as $fleet_id => &$fleet_info) {\n      $fleet_info[UBE_COUNT] = is_array($fleet_info[UBE_COUNT]) ? $fleet_info[UBE_COUNT] : array();\n      $player_data = &$combat_data[UBE_PLAYERS][$fleet_info[UBE_OWNER]];\n      $fleet_info[UBE_FLEET_TYPE] = $player_data[UBE_ATTACKER] ? UBE_ATTACKERS : UBE_DEFENDERS;\n\n      foreach (Ube4_1Calc::BONUS_LIST as $bonus_id => $bonus_value) {\n        // Вычисляем бонус игрока\n        $bonus_value = isset($player_data[UBE_BONUSES][$bonus_id]) ? $player_data[UBE_BONUSES][$bonus_id] : 0;\n        // Добавляем к бонусам флота бонусы игрока\n        $fleet_info[UBE_BONUSES][$bonus_id] += $bonus_value;\n      }\n\n      $first_round_data[$fleet_id][UBE_COUNT] = $fleet_info[UBE_PRICE] = array();\n      foreach ($fleet_info[UBE_COUNT] as $unit_id => $unit_count) {\n        if ($unit_count <= 0) {\n          continue;\n        }\n\n        $unit_info = get_unit_param($unit_id);\n        // Заполняем информацию о кораблях в информации флота\n        foreach (Ube4_1Calc::BONUS_LIST as $bonus_id => $bonus_value) {\n          $fleet_info[$bonus_id][$unit_id] = floor($unit_info[static::CONVERT_UNIT_PARAMS[$bonus_id]] * (1 + $fleet_info[UBE_BONUSES][$bonus_id]));\n        }\n        $fleet_info[UBE_AMPLIFY][$unit_id] = $unit_info[P_AMPLIFY];\n        // TODO: Переделать через get_ship_data()\n        $fleet_info[UBE_CAPACITY][$unit_id] = $unit_info[P_CAPACITY];\n        $fleet_info[UBE_TYPE][$unit_id] = $unit_info[P_UNIT_TYPE];\n        // TODO: Переделать через список ресурсов\n        $fleet_info[UBE_PRICE][RES_METAL]    [$unit_id] = $unit_info[P_COST][RES_METAL];\n        $fleet_info[UBE_PRICE][RES_CRYSTAL]  [$unit_id] = $unit_info[P_COST][RES_CRYSTAL];\n        $fleet_info[UBE_PRICE][RES_DEUTERIUM][$unit_id] = $unit_info[P_COST][RES_DEUTERIUM];\n        $fleet_info[UBE_PRICE][RES_DARK_MATTER][$unit_id] = $unit_info[P_COST][RES_DARK_MATTER];\n\n        // Копируем её в информацию о первом раунде\n        $first_round_data[$fleet_id][UBE_ARMOR][$unit_id] = $fleet_info[UBE_ARMOR][$unit_id] * $unit_count;\n        $first_round_data[$fleet_id][UBE_COUNT][$unit_id] = $unit_count;\n        $first_round_data[$fleet_id][UBE_ARMOR_REST][$unit_id] = $fleet_info[UBE_ARMOR][$unit_id];\n        $first_round_data[$fleet_id][UBE_SHIELD_REST][$unit_id] = $fleet_info[UBE_SHIELD][$unit_id];\n      }\n    }\n    $combat_data[UBE_ROUNDS][0][UBE_FLEETS] = $first_round_data;\n    $combat_data[UBE_ROUNDS][1][UBE_FLEETS] = $first_round_data;\n  }\n\n}\n"
  },
  {
    "path": "classes/Unit/DBStaticUnit.php",
    "content": "<?php\n\n/** @noinspection SqlResolve */\n\nnamespace Unit;\n\nuse Exception;\nuse mysqli_result;\nuse Planet\\Planet;\nuse SN;\n\nclass DBStaticUnit {\n\n  public static function db_unit_time_restrictions($date = SN_TIME_NOW) {\n    $date = is_numeric($date) ? \"FROM_UNIXTIME({$date})\" : \"'{$date}'\";\n\n    return\n      \"(unit_time_start IS NULL OR unit_time_start <= {$date}) AND\n    (unit_time_finish IS NULL OR unit_time_finish = '1970-01-01 03:00:00' OR unit_time_finish >= {$date})\";\n  }\n\n  /**\n   * Used by `unit_captain`\n   *\n   * @param int $unit_id\n   *\n   * @return array|bool|mysqli_result|null\n   */\n  public static function db_unit_by_id($unit_id) {\n    return doquery(\"SELECT * FROM `{{unit}}` WHERE `unit_id` = \" . idval($unit_id), true);\n  }\n\n  /**\n   * @param int $user_id\n   * @param int $location_type\n   * @param int $location_id\n   * @param int $unit_snid\n   *\n   * @return array|false|mixed|null\n   */\n  public static function db_unit_by_location($user_id, $location_type, $location_id, $unit_snid = 0) {\n    // apply time restrictions ????\n    $allUnits = DBStaticUnit::db_get_unit_list_by_location($location_type, $location_id);\n\n    $unit_snid = intval($unit_snid);\n\n    $resultOld = $unit_snid ? (isset($allUnits[$unit_snid]) ? $allUnits[$unit_snid] : null ) : $allUnits;\n\n    return $resultOld;\n  }\n\n  public static $locator = array(); // Кэширует соответствия между расположением объектов - в частности юнитов\n\n  /**\n   * @param $location_type\n   * @param $location_id\n   *\n   * @return array|false\n   */\n  public static function db_get_unit_list_by_location($location_type, $location_id) {\n    if (!($location_type = idval($location_type)) || !($location_id = idval($location_id))) {\n      return false;\n    }\n\n    if (!isset(DBStaticUnit::$locator[$location_type][$location_id])) {\n      $got_data = SN::db_get_record_list(LOC_UNIT, \"unit_location_type = {$location_type} AND unit_location_id = {$location_id} AND \" . DBStaticUnit::db_unit_time_restrictions());\n      if (is_array($got_data)) {\n        foreach ($got_data as $unitRow) {\n          DBStaticUnit::$locator[$unitRow['unit_location_type']][$unitRow['unit_location_id']][$unitRow['unit_snid']] = $unitRow;\n        }\n      }\n    }\n\n    $result = false;\n\n    if (is_array(DBStaticUnit::$locator[$location_type][$location_id])) {\n      foreach (DBStaticUnit::$locator[$location_type][$location_id] as $key => $value) {\n        $result[$key] = $value;\n      }\n    }\n\n    return $result;\n  }\n\n  public static function cache_clear() {\n    DBStaticUnit::$locator = [];\n  }\n\n  public static function db_unit_count_by_user_and_type_and_snid($user_id, $unit_type = 0, $unit_snid = 0) {\n    $query  = doquery(\n      \"SELECT unit_snid, sum(unit_level) as `qty`  FROM {{unit}} WHERE `unit_player_id` = {$user_id} \" .\n      ($unit_type ? \"AND `unit_type` = {$unit_type} \" : '') .\n      ($unit_snid ? \"AND `unit_snid` = {$unit_snid} \" : '') .\n      'GROUP BY `unit_snid`'\n    );\n    $result = array();\n    while ($row = db_fetch($query)) {\n      $result[$row['unit_snid']] = $row;\n    }\n\n    return $result;\n  }\n\n// Used by UNIT_CAPTAIN module TODO\n  public static function db_unit_in_fleet_by_user($user_id, $location_id, $unit_snid, $for_update) {\n    return doquery(\n      \"SELECT *\n    FROM {{fleets}} AS f\n      JOIN {{unit}} AS u ON u.`unit_location_id` = f.fleet_id\n    WHERE\n      f.fleet_owner = {$user_id} AND\n      (f.fleet_start_planet_id = {$location_id} OR f.fleet_end_planet_id = {$location_id})\n      AND u.unit_snid = {$unit_snid} AND u.`unit_location_type` = \" . LOC_FLEET . \" AND \" . self::db_unit_time_restrictions() .\n      \" LIMIT 1\" .\n      ($for_update ? ' FOR UPDATE' : '')\n      , true);\n  }\n\n\n  public static function db_unit_list_laboratories($user_id) {\n    return doquery(\"SELECT DISTINCT unit_location_id AS `id`\n    FROM {{unit}}\n    WHERE unit_player_id = {$user_id} AND unit_location_type = \" . LOC_PLANET . \" AND unit_level > 0 AND unit_snid IN (\" . STRUC_LABORATORY . \", \" . STRUC_LABORATORY_NANO . \");\");\n  }\n\n  public static function db_unit_set_by_id($unit_record_id, $set) {\n    return SN::db_upd_record_by_id(LOC_UNIT, $unit_record_id, $set);\n  }\n\n  /**\n   * @param string $set\n   *\n   * @return array|bool|false|mysqli_result|null\n   */\n  public static function db_unit_set_insert($set) {\n    return SN::db_ins_record(LOC_UNIT, $set);\n  }\n\n  public static function db_unit_list_delete($user_id, $unit_location_type, $unit_location_id = 0, $unit_snid = 0) {\n    return SN::db_del_record_list(LOC_UNIT,\n      \"`unit_location_type` = {$unit_location_type}\" .\n      ($unit_location_id = idval($unit_location_id) ? \" AND `unit_location_id` = {$unit_location_id}\" : '') .\n      ($user_id = idval($user_id) ? \" AND `unit_player_id` = {$user_id}\" : '') .\n      ($unit_snid = idval($unit_snid) ? \" AND `unit_snid` = {$unit_snid}\" : ''));\n  }\n\n  public static function db_unit_list_stat_calculate() {\n    return doquery(\n      \"SELECT unit_player_id, unit_type, unit_snid, unit_level, count(*) AS unit_amount\n    FROM `{{unit}}`\n    WHERE unit_level > 0 AND \" . self::db_unit_time_restrictions() .\n      \" GROUP BY unit_player_id, unit_type, unit_snid, unit_level\"\n    );\n  }\n\n\n  public static function db_unit_change_owner($location_type, $location_id, $new_owner_id) {\n    doquery(\"UPDATE {{unit}} SET `unit_player_id` = {$new_owner_id} WHERE `unit_location_type` = {$location_type} AND `unit_location_id` = {$location_id}\");\n  }\n\n\n  public static function db_unit_list_admin_delete_mercenaries_finished() {\n    /** @noinspection SqlWithoutWhere */\n    return doquery(\"DELETE FROM {{unit}} WHERE unit_time_finish IS NOT NULL AND unit_time_finish < FROM_UNIXTIME(\" . SN_TIME_NOW . \") AND unit_type = \" . UNIT_MERCENARIES);\n  }\n\n  public static function db_unit_list_admin_set_mercenaries_expire_time($default_length) {\n    return doquery(\n      \"UPDATE {{unit}}\n    SET\n      unit_time_start = FROM_UNIXTIME(\" . SN_TIME_NOW . \"),\n      unit_time_finish = FROM_UNIXTIME(\" . (SN_TIME_NOW + $default_length) . \")\n    WHERE unit_type = \" . UNIT_MERCENARIES\n    );\n  }\n\n  /**\n   * Adjust unit amount\n   *\n   * @param int   $playerId\n   * @param int   $planetId\n   * @param int   $unitSnId\n   * @param float $amount\n   * @param int   $reason\n   *\n   * @return bool\n   *\n   * @throws Exception\n   */\n  public static function dbChangeUnit($playerId, $planetId, $unitSnId, $amount, $reason = RPG_NONE) {\n    $result = false;\n\n    // TODO - Lock user\n    $userArray = db_user_by_id($playerId);\n\n    if ($unitSnId == RES_DARK_MATTER) {\n      // Add dark matter to user\n      $result = boolval(rpg_points_change($playerId, $reason, $amount));\n    } elseif (in_array($unitSnId, sn_get_groups(UNIT_RESOURCES_STR_LOOT))) {\n      // Add resources to user's capital\n      if ($userArray['user_as_ally'] == 1) {\n        // TODO - If ally - adding resources to user record\n      } else {\n        // Adding resources to planet\n        $planet = new Planet();\n        $planet->dbLoadRecord($planetId);\n        $planet->changeResource($unitSnId, $amount);\n        $planet->save();\n\n        $result = true;\n      }\n    } elseif (in_array($unitSnId, sn_get_groups(UNIT_ARTIFACTS_STR))) {\n      // Add artifacts to player\n      $result = self::dbAdd($playerId, 0, $unitSnId, $amount);\n    } elseif (!empty($planetId) && in_array($unitSnId, sn_get_groups([UNIT_STRUCTURES_STR, UNIT_SHIPS_STR, UNIT_DEFENCE_STR, ]))) {\n      // Add fleet or defense to user's capital\n      $result = self::dbAdd($playerId, $planetId, $unitSnId, $amount);\n    }\n\n    return $result;\n  }\n\n\n  /**\n   * Add unit to player/planet\n   *\n   * Supports units. DOES NOT support resources\n   *\n   * DOES NOT autodetect location\n   *\n   * @param int   $playerId\n   * @param int   $planetId 0 - for player units\n   * @param int   $unitSnId\n   * @param float $amount\n   *\n   * @return bool\n   */\n  protected static function dbAdd($playerId, $planetId, $unitSnId, $amount) {\n    if (!in_array($unitSnId, sn_get_groups([UNIT_SHIPS_STR, UNIT_DEFENCE_STR, UNIT_ARTIFACTS_STR, UNIT_STRUCTURES_STR,]))) {\n      return false;\n    }\n\n    if ($planetId == 0) {\n      $locationType = LOC_USER;\n      $locationId   = $playerId;\n    } else {\n      $locationType = LOC_PLANET;\n      $locationId   = $planetId;\n    }\n\n    $fields = [\n      'unit_player_id'     => $playerId,\n      'unit_location_type' => $locationType,\n      'unit_location_id'   => $locationId,\n      'unit_snid'          => $unitSnId,\n    ];\n    if (!($unitRecord = RecordUnit::findFirst($fields))) {\n      if ($amount < 0) {\n        return false;\n      }\n\n      // New unit\n//      $unitRecord = RecordUnit::build([\n//        'unit_player_id'     => $playerId,\n//        'unit_location_type' => $locationType,\n//        'unit_location_id'   => $locationId,\n//        'unit_snid'          => $unitSnId,\n//        'unit_type'          => get_unit_param($unitSnId, P_UNIT_TYPE),\n//        'unit_level'         => $level,\n//      ]);\n\n      $fields     += [\n        'unit_type'  => get_unit_param($unitSnId, P_UNIT_TYPE),\n        'unit_level' => $amount,\n      ];\n      $unitRecord = RecordUnit::build($fields);\n\n      return $unitRecord->insert();\n    } else {\n      if ($unitRecord->unit_level + $amount < 0) {\n        // TODO - Log error or throw Exception\n        return false;\n      }\n\n      $unitRecord->inc()->unit_level = $amount;\n\n      return $unitRecord->update();\n    }\n  }\n\n  public static function dbUserAdd($playerId, $unitSnId, $amount) {\n//    if (!($unitRecord = RecordUnit::findFirst([\n//      'unit_player_id'     => $playerId,\n//      'unit_location_type' => LOC_USER,\n//      'unit_location_id'   => $playerId,\n//      'unit_snid'          => $unitSnId,\n//    ]))) {\n//      if ($level < 0) {\n//        return false;\n//      }\n//\n//      // New unit\n//      $unitRecord = RecordUnit::build([\n//        'unit_player_id'     => $playerId,\n//        'unit_location_type' => LOC_USER,\n//        'unit_location_id'   => $playerId,\n//        'unit_type'          => get_unit_param($unitSnId, P_UNIT_TYPE),\n//        'unit_snid'          => $unitSnId,\n//        'unit_level'         => $level,\n//      ]);\n//\n////      var_dump($unitRecord);die();\n//\n//      return $unitRecord->insert();\n//    } else {\n//      if ($unitRecord->unit_level + $level < 0) {\n//        // TODO - Log error or throw Exception\n//        return false;\n//      }\n//\n//      $unitRecord->inc()->unit_level = $level;\n//\n//      return $unitRecord->update();\n//    }\n    return self::dbAdd($playerId, 0, $unitSnId, $amount);\n  }\n\n}\n"
  },
  {
    "path": "classes/Unit/Governor.php",
    "content": "<?php\n\n/**\n * Created by Gorlum 08.01.2018 13:23\n */\n\nnamespace Unit;\n\nuse DBAL\\db_mysql;\nuse \\SN;\nuse Planet\\Planet;\n\nclass Governor extends Unit {\n//  protected $type = UNIT_GOVERNOR_PRIMARY;\n//  protected $typeIdField = 'PLANET_GOVERNOR_ID';\n//  protected $typeLevelField = 'PLANET_GOVERNOR_LEVEL';\n\n  protected $snId = 0;\n  protected $level = 0;\n\n  /**\n   * @var RecordUnit $unit\n   */\n  protected $unit;\n\n  /**\n   * @var Planet $planet\n   */\n  protected $planet;\n\n  /**\n   * Governor constructor.\n   */\n  public function __construct() {\n    $this->reset();\n  }\n\n  /**\n   * @param Planet $planet\n   */\n  public function setPlanet($planet) {\n    $this->reset();\n\n    $this->planet = $planet;\n    $this->getExternalData();\n  }\n\n  /**\n   * @param int $hireId - Hire unit SN ID\n   */\n  public function hire($hireId) {\n    if (!in_array($hireId, sn_get_groups('governors'))) {\n      return;\n    }\n\n    if ($hireId == $this->getSnId() && $this->getMaxLevel() && $this->getMaxLevel() >= $this->getLevel()) {\n      return;\n    }\n\n    db_mysql::db_transaction_start();\n    $user = db_user_by_id($this->planet->id_owner, true);\n//    $this->planetRow = Planet\\DBStaticPlanet::db_planet_by_id($this->planet->id, true);\n//    $build_data = eco_get_build_data($user, $this->planetRow, $hireId, $this->getId() == $hireId ? $this->getLevel() : 0);\n    $this->planet->dbLoadRecord($this->planet->id);\n\n    $build_data = eco_get_build_data($user, $this->planet->asArray(), $hireId, $this->getSnId() == $hireId ? $this->getLevel() : 0);\n    if (\n      $build_data['CAN'][BUILD_CREATE]\n      &&\n      mrc_get_level($user, [], RES_DARK_MATTER) >= $build_data[BUILD_CREATE][RES_DARK_MATTER]\n      &&\n      rpg_points_change(\n        $user['id'],\n        RPG_GOVERNOR,\n        -$build_data[BUILD_CREATE][RES_DARK_MATTER],\n        sprintf(SN::$lang['ov_governor_purchase'],\n          SN::$lang['tech'][$hireId],\n          $hireId,\n          $this->level,\n          uni_render_planet_object_full($this->planet, false, true)\n        )\n      )\n    ) {\n      $this->addLevel($hireId);\n      $this->planet->update();\n    }\n    db_mysql::db_transaction_commit();\n  }\n\n\n  /**\n   * @return int\n   */\n  public function getSnId() {\n    return $this->snId;\n  }\n\n  /**\n   * @return int\n   */\n  public function getLevel() {\n    return $this->level;\n  }\n\n  /**\n   * @return int\n   */\n  public function getMaxLevel() {\n    $snId =  $this->getSnId();\n    return !empty($snId) ? get_unit_param($snId, P_MAX_STACK) : 0;\n  }\n\n  /**\n   * @param $hireId\n   */\n  protected function addLevel($hireId) {\n    if ($this->getSnId() == $hireId) {\n      $this->level++;\n    } else {\n      $this->level = 1;\n    }\n\n    $this->snId = $hireId;\n\n    $this->setExternalData();\n  }\n\n\n  protected function reset() {\n    unset($this->unit);\n    $this->planet = null;\n\n    $this->snId = 0;\n    $this->level = 0;\n  }\n\n  /**\n   * Sets data on external sources from internal properties\n   */\n  protected function setExternalData() {\n    $this->planet->PLANET_GOVERNOR_ID = $this->getSnId();\n    $this->planet->PLANET_GOVERNOR_LEVEL = $this->level;\n  }\n\n  /**\n   * Loads some data from external sources\n   */\n  protected function getExternalData() {\n    $this->snId = !empty($this->planet->PLANET_GOVERNOR_ID) ? intval($this->planet->PLANET_GOVERNOR_ID) : 0;\n    $this->level = !empty($this->planet->PLANET_GOVERNOR_LEVEL) ? intval($this->planet->PLANET_GOVERNOR_LEVEL) : 0;\n  }\n\n}\n"
  },
  {
    "path": "classes/Unit/RecordUnit.php",
    "content": "<?php\n/**\n * Created by Gorlum 04.10.2017 6:56\n */\n\nnamespace Unit;\n\nuse DBAL\\ActiveRecord;\n\n/**\n * Class RecordUnit\n * @package Unit\n *\n * @property int|string $unit_player_id     - bigint   -\n * @property int        $unit_location_type - tinyint  -\n * @property int|string $unit_location_id   - bigint   -\n * @property int        $unit_type          - bigint   -\n * @property int        $unit_snid          - bigint   -\n * @property int|string $unit_level         - decimal  -\n * @property string     $unit_time_start    - datetime -\n * @property string     $unit_time_finish   - datetime -\n *\n */\nclass RecordUnit extends ActiveRecord {\n  protected static $_primaryIndexField = 'unit_id';\n\n}\n"
  },
  {
    "path": "classes/Unit/Unit.php",
    "content": "<?php\n/**\n * Created by Gorlum 25.01.2018 7:20\n */\n\nnamespace Unit;\n\n\nclass Unit {\n\n}\n"
  },
  {
    "path": "classes/Universe/Universe.php",
    "content": "<?php\n/**\n * Created by Gorlum 13.02.2018 12:45\n */\n\nnamespace Universe;\n\nuse \\Fleet\\RecordFleet;\n\nclass Universe {\n\n  const MOON_MIN_SIZE = 1100;\n  const MOON_MAX_SIZE = 8999;\n\n  const MOON_DEBRIS_MIN = 1000000;    // Minimum amount of debris to span a moon\n  const MOON_CHANCE_MIN_PERCENT = 1;  // Minimum chance to span a moon\n  const MOON_CHANCE_MAX_PERCENT = 30; // Maximum chance to span a moon\n\n  /**\n   * Calc moon chance from debris\n   *\n   * @param float $debrisTotal\n   *\n   * @return float\n   */\n  public static function moonCalcChanceFromDebris($debrisTotal) {\n    $moon_chance = $debrisTotal / static::moonPercentCostInDebris();\n\n    $moon_chance = $moon_chance < static::MOON_CHANCE_MIN_PERCENT ? 0 : $moon_chance;\n    $moon_chance = $moon_chance > static::MOON_CHANCE_MAX_PERCENT ? static::MOON_CHANCE_MAX_PERCENT : $moon_chance;\n\n    return $moon_chance;\n  }\n\n  /**\n   * Roll moon size from rolled fracture\n   *\n   * @param float $rolledChance\n   *\n   * @return int\n   */\n  protected static function moonRollSizeSecondary($rolledChance) {\n    $minSize = max(static::MOON_MIN_SIZE, $rolledChance * 100 + 1000);\n    $maxSize = min(static::MOON_MAX_SIZE, $rolledChance * 200 + 2999);\n\n    return mt_rand($minSize, $maxSize);\n  }\n\n  /**\n   * Roll moon size from debris amount\n   *\n   * @param int|float $debrisTotal\n   *\n   * @return int\n   */\n  public static function moonRollSize($debrisTotal) {\n    $roll = mt_rand(1, 100);\n    if ($roll <= static::moonCalcChanceFromDebris($debrisTotal)) {\n      $moonSize = Universe::moonRollSizeSecondary($roll);\n    } else {\n      $moonSize = 0;\n    }\n\n    return $moonSize;\n  }\n\n\n  /**\n   * Real cost of 1% moon creation size in debris\n   *\n   * @return float|int\n   */\n  public static function moonPercentCostInDebris() {\n    return game_resource_multiplier(true) * static::MOON_DEBRIS_MIN;\n  }\n\n  /**\n   * Max debris cost for max sized moon\n   *\n   * @return float|int\n   */\n  public static function moonMaxDebris() {\n    return static::MOON_CHANCE_MAX_PERCENT * static::moonPercentCostInDebris();\n  }\n\n  /**\n   * Random moon size within allowed limits\n   *\n   * @return int\n   */\n  public static function moonSizeRandom() {\n    return mt_rand(static::MOON_MIN_SIZE, static::MOON_MAX_SIZE);\n  }\n\n\n  /**\n   * Return fleets heading to specified location\n   *\n   * @param int|null $galaxy\n   * @param int|null $system\n   * @param int|null $planet\n   * @param bool     $fromHold - Should be fleets on active Hold mission returned too\n   */\n  public static function fleetsReturn($galaxy = null, $system = null, $planet = null, $type = null, $fromHold = true) {\n    $filter = [];\n    $galaxy ? $filter['fleet_end_galaxy'] = $galaxy : false;\n    $galaxy ? $filter['fleet_end_system'] = $system : false;\n    $galaxy ? $filter['fleet_end_planet'] = $planet : false;\n    $galaxy ? $filter['fleet_end_type'] = $type : false;\n    $fleetsResult = RecordFleet::findAll($filter);\n    foreach ($fleetsResult as $fleetRecord) {\n      if ($fleetRecord->fleet_mess == FLEET_STATUS_RETURNING) {\n        continue;\n      }\n\n      if ($fleetRecord->fleet_mission == MT_HOLD) {\n        if (!$fromHold) {\n          continue;\n        }\n\n        // Changing end time\n        $fleetRecord->fleet_end_stay = SN_TIME_NOW;\n      }\n\n      $fleetRecord->fleet_mess = FLEET_STATUS_RETURNING;\n\n      $fleetRecord->update();\n    }\n  }\n\n}\n"
  },
  {
    "path": "classes/Universe.php",
    "content": "<?php\n\n/**\n * Created by Gorlum 25.03.2023 21:11\n */\nclass Universe {\n\n  /**\n   * @param $from\n   * @param $to\n   *\n   * @return float|int\n   */\n  public static function distance($from, $to) {\n    if ($from['galaxy'] != $to['galaxy']) {\n      $distance = abs($from['galaxy'] - $to['galaxy']) * self::getBaseGalaxyDistance();\n    } elseif ($from['system'] != $to['system']) {\n      $distance = abs($from['system'] - $to['system']) * 5 * 19 + 2700;\n    } elseif ($from['planet'] != $to['planet']) {\n      $distance = abs($from['planet'] - $to['planet']) * 5 + 1000;\n    } else {\n      $distance = 5;\n    }\n\n    return $distance;\n  }\n\n  public static function getBaseGalaxyDistance() {\n    return SN::$config->uni_galaxy_distance ? SN::$config->uni_galaxy_distance : UNIVERSE_GALAXY_DISTANCE;\n  }\n\n  /**\n   * Get fleet flying speed aka... hmph... fleet flying speed\n   *\n   * @param bool $plain\n   *\n   * @return float|int\n   */\n  public static function flt_server_flight_speed_multiplier($plain = false) {\n    return getValueFromStorage(UNIT_SERVER_SPEED_FLEET, $plain);\n  }\n\n}\n"
  },
  {
    "path": "classes/Validators.php",
    "content": "<?php\n\n/**\n * Class Validators\n */\n// TODO - перенести сюда все базовые валидаторы\nclass Validators {\n\n  /**\n   * @param mixed &$value\n   *\n   * @return bool\n   */\n  public static function isNotEmptyByRef(&$value) {\n    return !empty($value);\n  }\n\n  /**\n   * @param mixed $value\n   *\n   * @return bool\n   */\n  public static function isNotEmpty($value) {\n    return self::isNotEmptyByRef($value);\n  }\n\n}\n"
  },
  {
    "path": "classes/auth_abstract.php",
    "content": "<?php\n\nuse DBAL\\db_mysql;\nuse Modules\\sn_module;\n\n/**\n * User: Gorlum\n * Date: 10.10.2015\n * Time: 6:05\n */\nclass auth_abstract extends sn_module {\n  public $manifest = [];\n\n  public $provider_id = ACCOUNT_PROVIDER_NONE;\n\n  protected $features = array();\n\n  /**\n   * @var Account\n   */\n  public $account = null;\n\n  /**\n   * Статус входа аккаунта в игру\n   *\n   * @var int\n   */\n  public $account_login_status = LOGIN_UNDEFINED;\n  public $account_login_message = '';\n\n  /**\n   * @var db_mysql $db\n   */\n  // TODO Should be PROTECTED\n  public $db;\n\n  /**\n   * @param string $filename\n   */\n  public function __construct($filename = __FILE__) {\n    if ($this->provider_id == ACCOUNT_PROVIDER_NONE) {\n      die('У всех провайдеров должен быть $provider_id!');\n    }\n\n    parent::__construct($filename);\n  }\n\n  /**\n   * @return int\n   */\n  public function login() {\n    return $this->account_login_status;\n  }\n\n  /**\n   *\n   */\n  public function logout() {\n  }\n\n  /**\n   * Меняет пароль у аккаунта с проверкой старого пароля\n   *\n   * @param      $old_password_unsafe\n   * @param      $new_password_unsafe\n   * @param null $salt_unsafe\n   *\n   * @return array|bool|resource\n   */\n  public function password_change($old_password_unsafe, $new_password_unsafe, $salt_unsafe = null) {\n    return\n      $this->is_feature_supported(AUTH_FEATURE_PASSWORD_CHANGE)\n      && is_object($this->account)\n      && $this->account->password_change($old_password_unsafe, $new_password_unsafe, $salt_unsafe);\n  }\n\n  /**\n   * @param $account_to_impersonate\n   */\n  public function impersonate($account_to_impersonate) {\n  }\n\n  /**\n   * Меняет пароль на пристыкованном аккаунте\n   *\n   * @param $password_unsafe\n   *\n   * @return bool\n   */\n  public function password_check($password_unsafe) {\n    return\n      $this->is_feature_supported(AUTH_FEATURE_HAS_PASSWORD)\n      && is_object($this->account)\n      && $this->account->password_check($password_unsafe);\n  }\n\n  /**\n   * Проверка на поддержку фичи\n   *\n   * @param $feature\n   *\n   * @return bool\n   */\n  public function is_feature_supported($feature) {\n    return !empty($this->features[$feature]);\n  }\n\n  /**\n   * Функция предлогает имя игрока (`users`) по данным аккаунта\n   *\n   * @return string\n   */\n  public function player_name_suggest() {\n    $name = '';\n    if (is_object($this->account) && !empty($this->account->account_email)) {\n      list($name) = explode('@', $this->account->account_email);\n    }\n\n    empty($name) && is_object($this->account) && !empty($this->account->account_name) ? $name = $this->account->account_name : false;\n\n    return $name;\n  }\n\n}\n"
  },
  {
    "path": "classes/auth_local.php",
    "content": "<?php\n\nuse DBAL\\db_mysql;\n\n/**\n * Class auth_local\n */\n// Расширяет Modules\\sn_module, потому что его потомки так же являются модулями\nclass auth_local extends auth_abstract {\n  public $versionCommitted = '#46d0#';\n\n  public $manifest = [\n    'package'   => 'auth',\n    'name'      => 'local',\n    'version'   => '0a0',\n    'copyright' => 'Project \"SuperNova.WS\" #46d0# copyright © 2009-2015 Gorlum',\n\n    self::M_LOAD_ORDER => MODULE_LOAD_ORDER_AUTH_LOCAL,\n\n    'config_path' => SN_ROOT_PHYSICAL,\n  ];\n\n  public $provider_id = ACCOUNT_PROVIDER_LOCAL;\n\n  /**\n   * Флаг входа в игру\n   *\n   * @var bool\n   */\n  protected $is_login = false;\n  /**\n   * Флаг регистрации\n   *\n   * @var bool\n   */\n  protected $is_register = false;\n  /**\n   * Флаг запроса кода на сброс пароля\n   *\n   * @var bool\n   */\n  protected $is_password_reset = false;\n  /**\n   * Флаг запроса на сброс пароля по коду\n   *\n   * @var bool\n   */\n  protected $is_password_reset_confirm = false;\n  /**\n   * Нужно ли запоминать креденшиался при выходе из браузера\n   *\n   * @var bool\n   */\n  protected $remember_me = 1;\n\n  /**\n   * @var Confirmation\n   */\n  protected $confirmation = null;\n\n\n  protected $features = array(\n    AUTH_FEATURE_EMAIL_CHANGE    => AUTH_FEATURE_EMAIL_CHANGE,\n    AUTH_FEATURE_PASSWORD_RESET  => AUTH_FEATURE_PASSWORD_RESET,\n    AUTH_FEATURE_PASSWORD_CHANGE => AUTH_FEATURE_PASSWORD_CHANGE,\n    AUTH_FEATURE_HAS_PASSWORD    => AUTH_FEATURE_HAS_PASSWORD,\n  );\n\n  /**\n   * @var string $input_login_unsafe\n   */\n  protected $input_login_unsafe = '';\n  protected $input_login_password_raw = '';\n  protected $input_login_password_raw_repeat = '';\n  protected $input_email_unsafe = '';\n  protected $input_language_unsafe = '';\n  protected $input_language_safe = '';\n\n  protected $domain = null;\n  protected $sn_root_path = SN_ROOT_RELATIVE;\n  protected $cookie_name = SN_COOKIE;\n  protected $cookie_name_impersonate = SN_COOKIE_I;\n  protected $secret_word = '';\n\n  /**\n   * @param string $filename\n   */\n  public function __construct($filename = __FILE__) {\n    parent::__construct($filename);\n\n    $this->prepare();\n\n    $this->active = false;\n    if (!empty($this->config) && is_array($this->config['db'])) {\n      // БД, отличная от стандартной\n      $this->db = new db_mysql();\n\n      $this->db->sn_db_connect($this->config['db']);\n      if ($this->active = $this->db->connected) {\n        $this->provider_id = ACCOUNT_PROVIDER_CENTRAL;\n\n        $this->domain       = $this->config['domain'];\n        $this->sn_root_path = $this->config['sn_root_path'];\n        $this->cookie_name  = $this->config['cookie_name'];\n        $this->secret_word  = $this->config['secretword'];\n        // TODO Проверить наличие всех нужных таблиц\n      } else {\n        unset($this->db);\n      }\n    }\n\n    // Fallback to local DB\n    if (!$this->active) {\n      $this->db = SN::$db;\n\n      $this->provider_id = ACCOUNT_PROVIDER_LOCAL;\n\n      $this->domain       = null;\n      $this->sn_root_path = SN_ROOT_RELATIVE;\n      $this->cookie_name  = SN_COOKIE;\n      $this->secret_word  = SN::$sn_secret_word;\n\n      $this->active = true;\n    }\n\n    $this->cookie_name_impersonate = $this->cookie_name . AUTH_COOKIE_IMPERSONATE_SUFFIX;\n\n    $this->account      = new Account($this->db);\n    $this->confirmation = new Confirmation($this->db);\n  }\n\n  /**\n   * Попытка залогиниться с использованием метода $method\n   *\n   * @param string $method_name\n   *\n   * @version 4.5\n   *\n   */\n  public function login() {\n    // TODO Проверяем поддерживаемость метода\n    // TODO Пытаемся залогиниться\n    $this->password_reset_send_code();\n    $this->password_reset_confirm();\n    $this->register();\n    $this->login_username();\n    $this->login_cookie();\n\n    // $this->is_impersonating = $this->account_login_status == LOGIN_SUCCESS && !empty($_COOKIE[$this->cookie_name_impersonate]);\n\n    return $this->account_login_status;\n  }\n\n  public function logout() {\n    $this->cookie_clear();\n  }\n\n  /**\n   * Меняет пароль у аккаунта с проверкой старого пароля\n   *\n   * @param      $old_password_unsafe\n   * @param      $new_password_unsafe\n   * @param null $salt_unsafe\n   *\n   * @return array|bool|resource\n   * @throws Exception\n   */\n  public function password_change($old_password_unsafe, $new_password_unsafe, $salt_unsafe = null) {\n    $result = parent::password_change($old_password_unsafe, $new_password_unsafe, $salt_unsafe);\n    if ($result) {\n      $this->cookie_set();\n    }\n\n    return $result;\n  }\n\n  public function impersonate($account_to_impersonate) {\n    $this->cookie_set($account_to_impersonate);\n  }\n\n  /**\n   * @param Account $account\n   */\n  public function login_with_account($account) {\n    $this->account = $account;\n    $this->cookie_set();\n\n    return $this->login_cookie();\n  }\n\n\n  /**\n   * Отсылает письмо с кодом подтверждения для сброса пароля\n   *\n   * @return int|string\n   */\n  protected function password_reset_send_code() {\n    global $lang, $config;\n\n    if (!$this->is_password_reset) {\n      return $this->account_login_status;\n    }\n\n    // Проверяем поддержку сброса пароля\n    if (!$this->is_feature_supported(AUTH_FEATURE_PASSWORD_RESET)) {\n      return $this->account_login_status;\n    }\n\n    try {\n      $email_unsafe = $this->input_email_unsafe;\n\n      unset($this->account);\n      $this->account = new Account($this->db);\n\n      if (!$this->account->db_get_by_email($email_unsafe)) {\n        throw new Exception(PASSWORD_RESTORE_ERROR_EMAIL_NOT_EXISTS, ERR_ERROR);\n        // return $this->account_login_status;\n      }\n\n      $account_translation = PlayerToAccountTranslate::db_translate_get_users_from_account_list($this->provider_id, $this->account->account_id); // OK 4.5\n      $user_list           = db_user_list_by_id(array_keys($account_translation));\n\n      // TODO - Проверять уровень доступа аккаунта!\n      // Аккаунты с АУТЛЕВЕЛ больше 0 - НЕ СБРАСЫВАЮТ ПАРОЛИ!\n      foreach ($user_list as $user_id => $user_data) {\n        if ($user_data['authlevel'] > AUTH_LEVEL_REGISTERED) {\n          throw new Exception(PASSWORD_RESTORE_ERROR_ADMIN_ACCOUNT, ERR_ERROR);\n        }\n      }\n\n      $confirmation = $this->confirmation->db_confirmation_get_latest_by_type_and_email(CONFIRM_PASSWORD_RESET, $email_unsafe); // OK 4.5\n      if (isset($confirmation['create_time']) && SN_TIME_NOW - strtotime($confirmation['create_time']) < PERIOD_MINUTE_10) {\n        throw new Exception(PASSWORD_RESTORE_ERROR_TOO_OFTEN, ERR_ERROR);\n      }\n\n      // Удаляем предыдущие записи продтверждения сброса пароля\n      !empty($confirmation['id']) or $this->confirmation->db_confirmation_delete_by_type_and_email(CONFIRM_PASSWORD_RESET, $email_unsafe); // OK 4.5\n\n      db_mysql::db_transaction_start();\n      $confirm_code_unsafe = $this->confirmation->db_confirmation_get_unique_code_by_type_and_email(CONFIRM_PASSWORD_RESET, $email_unsafe); // OK 4.5\n      db_mysql::db_transaction_commit();\n\n      if (!is_email($email_unsafe)) {\n        SN::$debug->error(\"Email is invalid: '{$email_unsafe}'\", 'Invalid email for password restoration');\n      }\n\n      @$result = mymail($email_unsafe,\n        sprintf($lang['log_lost_email_title'], $config->game_name),\n        sprintf($lang['log_lost_email_code'], SN_ROOT_VIRTUAL . 'login.php', $confirm_code_unsafe, date(FMT_DATE_TIME, SN_TIME_NOW + AUTH_PASSWORD_RESET_CONFIRMATION_EXPIRE), $config->game_name)\n      );\n\n      $result = $result ? PASSWORD_RESTORE_SUCCESS_CODE_SENT : PASSWORD_RESTORE_ERROR_SENDING;\n    } catch (Exception $e) {\n      db_mysql::db_transaction_rollback();\n      $result = $e->getMessage();\n    }\n\n    return $this->account_login_status = $result;\n  }\n\n  /**\n   * Сброс пароля по введенному коду подтверждения\n   *\n   * @return int|string\n   */\n  protected function password_reset_confirm() {\n    global $lang, $config;\n\n    if (!$this->is_password_reset_confirm) {\n      return $this->account_login_status;\n    }\n\n    if ($this->account_login_status != LOGIN_UNDEFINED) {\n      return $this->account_login_status;\n    }\n\n    // Проверяем поддержку сброса пароля\n    if (!$this->is_feature_supported(AUTH_FEATURE_PASSWORD_RESET)) {\n      return $this->account_login_status;\n    }\n\n    try {\n      $code_unsafe = sys_get_param_str_unsafe('password_reset_code');\n      if (empty($code_unsafe)) {\n        throw new Exception(PASSWORD_RESTORE_ERROR_CODE_EMPTY, ERR_ERROR);\n      }\n\n      db_mysql::db_transaction_start();\n      $confirmation = $this->confirmation->db_confirmation_get_by_type_and_code(CONFIRM_PASSWORD_RESET, $code_unsafe); // OK 4.5\n\n      if (empty($confirmation)) {\n        throw new Exception(PASSWORD_RESTORE_ERROR_CODE_WRONG, ERR_ERROR);\n      }\n\n      if (SN_TIME_NOW - strtotime($confirmation['create_time']) > AUTH_PASSWORD_RESET_CONFIRMATION_EXPIRE) {\n        throw new Exception(PASSWORD_RESTORE_ERROR_CODE_TOO_OLD, ERR_ERROR);\n      }\n\n      unset($this->account);\n      $this->account = new Account($this->db);\n\n      if (!$this->account->db_get_by_email($confirmation['email'])) {\n        throw new Exception(PASSWORD_RESTORE_ERROR_CODE_OK_BUT_NO_ACCOUNT_FOR_EMAIL, ERR_ERROR);\n      }\n\n      $new_password_unsafe = $this->make_random_password();\n      $salt_unsafe         = $this->password_salt_generate();\n      if (!$this->account->db_set_password($new_password_unsafe, $salt_unsafe)) {\n        // Ошибка смены пароля\n        throw new Exception(AUTH_ERROR_INTERNAL_PASSWORD_CHANGE_ON_RESTORE, ERR_ERROR);\n      }\n\n      $this->account_login_status = LOGIN_UNDEFINED;\n      $this->remember_me          = 1;\n      $this->cookie_set();\n      $this->login_cookie();\n\n      if ($this->account_login_status == LOGIN_SUCCESS) {\n        // TODO - НЕ ОБЯЗАТЕЛЬНО ОТПРАВЛЯТЬ ЧЕРЕЗ ЕМЕЙЛ! ЕСЛИ ЭТО ФЕЙСБУЧЕК ИЛИ ВКШЕЧКА - МОЖНО ЧЕРЕЗ ЛС ПИСАТЬ!!\n        $message_header = sprintf($lang['log_lost_email_title'], $config->game_name);\n        $message        = sprintf($lang['log_lost_email_pass'], $config->game_name, $this->account->account_name, $new_password_unsafe);\n        @$operation_result = mymail($confirmation['email'], $message_header, htmlspecialchars($message));\n\n        $users_translated = PlayerToAccountTranslate::db_translate_get_users_from_account_list($this->provider_id, $this->account->account_id); // OK 4.5\n        if (!empty($users_translated)) {\n          // Отправляем в лички письмо о сбросе пароля\n\n          // ПО ОПРЕДЕЛЕНИЮ в $users_translated только\n          //    - аккаунты, поддерживающие сброс пароля\n          //    - список аккаунтов, имеющих тот же емейл, что указан в Подтверждении\n          //    - игроки, привязанные только к этим аккаунтам\n          // Значит им всем сразу скопом можно отправлять сообщения\n          $message = sprintf($lang['sys_password_reset_message_body'], $new_password_unsafe);\n          $message = HelperString::nl2br($message) . '<br><br>';\n          // msg_send_simple_message($found_provider->data[F_USER_ID], 0, SN_TIME_NOW, MSG_TYPE_ADMIN, $lang['sys_administration'], $lang['sys_login_register_message_title'], $message);\n\n          foreach ($users_translated as $user_id => $providers_list) {\n            msg_send_simple_message($user_id, 0, SN_TIME_NOW, MSG_TYPE_ADMIN, $lang['sys_administration'], $lang['sys_login_register_message_title'], $message);\n          }\n        } else {\n          // Фигня - может быть и пустой, если у нас есть только аккаунт, но нет пользователей\n          // throw new Exception(AUTH_PASSWORD_RESET_INSIDE_ERROR_NO_ACCOUNT_FOR_CONFIRMATION, ERR_ERROR);\n        }\n      }\n\n      $this->confirmation->db_confirmation_delete_by_type_and_email(CONFIRM_PASSWORD_RESET, $confirmation['email']); // OK 4.5\n\n      db_mysql::db_transaction_commit();\n\n      sys_redirect('overview.php');\n    } catch (Exception $e) {\n      db_mysql::db_transaction_rollback();\n      $this->account_login_status = $e->getMessage();\n    }\n\n    return $this->account_login_status;\n  }\n\n  /**\n   * Функция инициализирует данные провайдера - разворачивает куки, берет данные итд\n   */\n  protected function prepare() {\n    $this->input_login_unsafe = sys_get_param_str_unsafe('username', sys_get_param_str_unsafe('email')); // TODO переделать эту порнографию\n\n    $this->is_login                  = sys_get_param('login') ? true : false;\n    $this->is_register               = sys_get_param('register') ? true : false;\n    $this->is_password_reset         = sys_get_param('password_reset') ? true : false;\n    $this->is_password_reset_confirm = sys_get_param('password_reset_confirm') ? true : false;\n\n    $this->remember_me                     = intval(sys_get_param_int('rememberme') || $this->is_register);\n    $this->input_login_password_raw        = sys_get_param('password');\n    $this->input_login_password_raw_repeat = sys_get_param('password_repeat');\n    $this->input_email_unsafe              = sys_get_param_str_unsafe('email');\n    $this->input_language_unsafe           = sys_get_param_str_unsafe('lang', DEFAULT_LANG);\n    $this->input_language_safe             = sys_get_param_str('lang', DEFAULT_LANG);\n\n  }\n\n  /**\n   * Пытается зарегестрировать пользователя по введенным данным\n   * @return mixed\n   * @version 4.5\n   *\n   */\n  protected function register() {\n    // TODO РЕГИСТРАЦИЯ ВСЕГДА ДОЛЖНА ЛОГИНИТЬ ПОЛЬЗОВАТЕЛЯ!\n    $this->flog('Регистрация: начинаем. Провайдер ' . $this->provider_id);\n\n    if (!$this->is_register) {\n      $this->flog('Регистрация: не выставлен флаг регистрации - пропускаем');\n    } else {\n      try {\n\n        $this->register_validate_input();\n\n        db_mysql::db_transaction_start();\n        $this->account->db_get_by_name_or_email($this->input_login_unsafe, $this->input_email_unsafe);\n        if ($this->account->is_exists) {\n          if ($this->account->account_email == $this->input_email_unsafe) {\n            throw new Exception(REGISTER_ERROR_EMAIL_EXISTS, ERR_ERROR);\n          } else {\n            throw new Exception(REGISTER_ERROR_ACCOUNT_NAME_EXISTS, ERR_ERROR);\n          }\n        }\n\n        // Проблемы с созданием аккаунта - вызовут эксершн и обработается catch()\n        $this->account->db_create(\n          $this->input_login_unsafe,\n          $this->input_login_password_raw,\n          $this->input_email_unsafe,\n          $this->input_language_unsafe\n        );\n\n        // Устанавливать не надо - мы дальше пойдем по workflow\n        $this->account_login_status = LOGIN_SUCCESS;\n        $this->cookie_set();\n\n        // А вот это пока не нужно. Трансляцией аккаунтов в юзеров и созданием новых юзеров для новозашедших аккаунтов занимается Auth\n        // $this->register_account();\n        db_mysql::db_transaction_commit();\n      } catch (Exception $e) {\n        db_mysql::db_transaction_rollback();\n        $this->account_login_status == LOGIN_UNDEFINED ? $this->account_login_status = $e->getMessage() : false;\n      }\n    }\n\n    return $this->account_login_status;\n  }\n\n  /**\n   * Пытается залогинить пользователя по куке\n   * @return int Результат попытки\n   * @version 4.5\n   *\n   */\n  protected function login_cookie() {\n    if ($this->account_login_status != LOGIN_UNDEFINED) {\n      return $this->account_login_status;\n    }\n\n    if ($this->account->cookieLogin($rememberMe)) {\n      $this->account_login_status = LOGIN_SUCCESS;\n      $this->remember_me          = intval($rememberMe);\n    }\n\n    return $this->account_login_status;\n  }\n\n  /**\n   * Пытается залогинить пользователя по имени аккаунта и паролю\n   * @return mixed\n   * @version 4.5\n   *\n   */\n  protected function login_username() {\n    // TODO - Логин по старым именам\n    try {\n      if (!$this->is_login) {\n        $this->flog('Логин: не выставлен флаг входа в игру - это не логин');\n        throw new Exception(LOGIN_UNDEFINED, ERR_ERROR);\n      }\n\n      // TODO Пустое имя аккаунта\n      if (!$this->input_login_unsafe) {\n        throw new Exception(LOGIN_UNDEFINED, ERR_ERROR);\n      }\n\n      $this->login_validate_input();\n\n      if (!$this->account->db_get_by_name($this->input_login_unsafe) && !$this->account->db_get_by_email($this->input_login_unsafe)) {\n        throw new Exception(LOGIN_ERROR_USERNAME, ERR_ERROR);\n      }\n\n      if (!$this->account->password_check($this->input_login_password_raw)) {\n        throw new Exception(LOGIN_ERROR_PASSWORD, ERR_ERROR);\n      }\n\n      $this->cookie_set();\n      $this->account_login_status = LOGIN_SUCCESS;\n    } catch (Exception $e) {\n      $this->account_login_status == LOGIN_UNDEFINED ? $this->account_login_status = $e->getMessage() : false;\n    }\n\n    return $this->account_login_status;\n  }\n\n  /**\n   * Устанавливает куку аккаунта по данным $this->data[F_ACCOUNT]\n   *\n   * @param Account|null $account_to_impersonate\n   *\n   * @return bool\n   * @throws Exception\n   *\n   */\n  // TODO - должен устанавливать куку исходя из пользователя, что бы пользователь мог логинится\n  // TODO - или ставить мультикуку - хотя нахуя, спрашивается\n  protected function cookie_set($account_to_impersonate = null) {\n    $this_account = is_object($account_to_impersonate) ? $account_to_impersonate : $this->account;\n\n    if (!is_object($this_account) || !$this_account->is_exists) {\n      throw new Exception(LOGIN_ERROR_NO_ACCOUNT_FOR_COOKIE_SET, ERR_ERROR);\n    }\n\n    if (is_object($account_to_impersonate) && $account_to_impersonate->is_exists) {\n      sn_setcookie($this->cookie_name_impersonate, $_COOKIE[$this->cookie_name], SN_TIME_NOW + PERIOD_YEAR, $this->sn_root_path, $this->domain);\n    }\n\n    $this->flog(\"cookie_set() - Устанавливаем куку\");\n\n    return $this_account->cookieSet($this->remember_me, $this->domain);\n  }\n\n  /**\n   * Очищает куку аккаунта - совсем или восстанавливая куку текущего имперсонатора\n   */\n  protected function cookie_clear() {\n    $this->account->cookieClear($this->domain);\n  }\n\n\n  // ХЕЛПЕРЫ ===========================================================================================================\n\n  /**\n   * Проверяет введенные данные логина на корректность\n   *\n   * @throws Exception\n   */\n  protected function login_validate_input() {\n    // Проверяем, что бы в начале и конце не было пустых символов\n    // TODO - при копировании Эксель -> Опера - в конце образуются пустые места. Это не должно быть проблемой! Вынести проверку пароля в регистрацию!\n    if ($this->input_login_password_raw != trim($this->input_login_password_raw)) {\n      throw new Exception(LOGIN_ERROR_PASSWORD_TRIMMED, ERR_ERROR);\n    }\n    if (!$this->input_login_password_raw) {\n      throw new Exception(LOGIN_ERROR_PASSWORD_EMPTY, ERR_ERROR);\n    }\n  }\n\n  /**\n   * Проверяет данные для регистрации на корректность\n   *\n   * @throws Exception\n   */\n  protected function register_validate_input() {\n    // То, что не подходит для логина - не подходит и для регистрации\n    $this->login_validate_input();\n\n    // Если нет имени пользователя - NO GO!\n    if (!$this->input_login_unsafe) {\n      throw new Exception(LOGIN_ERROR_USERNAME_EMPTY, ERR_ERROR);\n    }\n    // Если логин имеет запрещенные символы - NO GO!\n    if (strpbrk($this->input_login_unsafe, LOGIN_REGISTER_CHARACTERS_PROHIBITED)) {\n      throw new Exception(LOGIN_ERROR_USERNAME_RESTRICTED_CHARACTERS, ERR_ERROR);\n    }\n    // Если логин меньше минимальной длины - NO GO!\n    if (strlen($this->input_login_unsafe) < LOGIN_LENGTH_MIN) {\n      throw new Exception(REGISTER_ERROR_USERNAME_SHORT, ERR_ERROR);\n    }\n    // Если пароль меньше минимальной длины - NO GO!\n    if (strlen($this->input_login_password_raw) < PASSWORD_LENGTH_MIN) {\n      throw new Exception(REGISTER_ERROR_PASSWORD_INSECURE, ERR_ERROR);\n    }\n    // Если пароль имеет пробельные символы в начале или конце - NO GO!\n    if ($this->input_login_password_raw != trim($this->input_login_password_raw)) {\n      throw new Exception(LOGIN_ERROR_PASSWORD_TRIMMED, ERR_ERROR);\n    }\n    // Если пароль не совпадает с подтверждением - NO GO! То, что у пароля нет пробельных символов в начале/конце - мы уже проверили выше\n    //Если они есть у повтора - значит пароль и повтор не совпадут\n    if ($this->input_login_password_raw <> $this->input_login_password_raw_repeat) {\n      throw new Exception(REGISTER_ERROR_PASSWORD_DIFFERENT, ERR_ERROR);\n    }\n    // Если нет емейла - NO GO!\n    // TODO - регистрация без емейла\n    if (!$this->input_email_unsafe) {\n      throw new Exception(REGISTER_ERROR_EMAIL_EMPTY, ERR_ERROR);\n    }\n    // Если емейл не является емейлом - NO GO!\n    if (!is_email($this->input_email_unsafe)) {\n      throw new Exception(REGISTER_ERROR_EMAIL_WRONG, ERR_ERROR);\n    }\n  }\n\n\n  protected function password_encode($password, $salt) {\n    return core_auth::password_encode($password, $salt);\n  }\n\n  protected function password_salt_generate() {\n    return core_auth::password_salt_generate();\n  }\n\n  /**\n   * Генерирует случайный пароль\n   *\n   * @return string\n   */\n  protected function make_random_password() {\n    return core_auth::make_random_password();\n  }\n\n  protected function flog($message, $die = false) {\n    if (!defined('DEBUG_AUTH') || !DEBUG_AUTH) {\n      return;\n    }\n    list($called, $caller) = debug_backtrace(false);\n\n    $caller_name =\n      ((get_called_class()) ? get_called_class() : (!empty($caller['class']) ? $caller['class'] : '')) .\n      (!empty($caller['type']) ? $caller['type'] : '') .\n      (!empty($caller['function']) ? $caller['function'] : '') .\n      (!empty($called['line']) ? ':' . $called['line'] : '');\n\n    $_SERVER['SERVER_NAME'] == 'localhost' ? print(\"<div class='debug'>$message - $caller_name\\r\\n</div>\") : false;\n\n    SN::log_file(\"$message - $caller_name\");\n    if ($die) {\n      $die && die(\"<div class='negative'>СТОП! Функция {$caller_name} при вызове в \" . get_called_class() . \" (располагается в \" . get_class() . \"). СООБЩИТЕ АДМИНИСТРАЦИИ!</div>\");\n    }\n  }\n\n}\n"
  },
  {
    "path": "classes/classCache.php",
    "content": "<?php\n/**\n *\n * @package supernova\n * @version #43b0#\n * @copyright (c) 2009-2017 Gorlum for http://supernova.ws\n * @license http://opensource.org/licenses/gpl-license.php GNU Public License\n *\n */\n\n/**\n *\n * Basic cacher class that handles different cache engines\n * It's pretty smart to handle one cache instance for all application instances (if there is PHP-cacher installed)\n * Currently supported only XCache and no-cache (array)\n * With no-cache some advanced features would be unaccessible\n * Cacher works not only with single values. It's also support multidimensional arrays\n * Currently support is a bit limited - for example there is no \"walk\" function. However basic array abilities supported\n * You should NEVER operate with arrays inside of cacher and should ALWAYS use wrap-up functions\n *\n * @property bool  _INITIALIZED\n * @property array lng_stat_usage - Array for locale strings usage statistics\n * @property array tables\n *\n * @package supernova\n */\nclass classCache implements ArrayAccess {\n  /**\n   * CACHER_NOT_INIT - not initialized\n   */\n  const CACHER_NOT_INIT = -1;\n  /**\n   * CACHER_NO_CACHE - no cache - array() used\n   */\n  const CACHER_NO_CACHE = 0;\n  /**\n   * CACHER_XCACHE   - xCache\n   */\n  const CACHER_XCACHE = 1;\n\n  /**\n   * @var int $mode - cacher mode\n   */\n  protected static $mode = self::CACHER_NOT_INIT;\n  /**\n   * @var array $data - Cacher data\n   */\n  protected static $data;\n  /**\n   * @var string $prefix - Cacher prefix\n   */\n  protected $prefix;\n\n  /**\n   * @var $cacheObject static - Singleton object\n   */\n  protected static $cacheObject;\n\n  public function __construct($prefIn = 'CACHE_', $init_mode = false) {\n    if (!($init_mode === false || $init_mode === self::CACHER_NO_CACHE || ($init_mode === self::CACHER_XCACHE && extension_loaded('xcache')))) {\n      throw new UnexpectedValueException('Wrong work mode or current mode does not supported on your server');\n    }\n\n    $this->prefix = $prefIn;\n    if (extension_loaded('xcache') && ($init_mode === self::CACHER_XCACHE || $init_mode === false)) {\n      if (self::$mode === self::CACHER_NOT_INIT) {\n        self::$mode = self::CACHER_XCACHE;\n      }\n    } else {\n      if (self::$mode === self::CACHER_NOT_INIT) {\n        self::$mode = self::CACHER_NO_CACHE;\n        if (!self::$data) {\n          self::$data = array();\n        }\n      }\n    }\n  }\n\n  public static function getInstance($prefIn = 'CACHE_', $table_name = '') {\n    if (!isset(self::$cacheObject)) {\n      $className = get_class();\n      self::$cacheObject = new $className($prefIn);\n    }\n\n    return self::$cacheObject;\n  }\n\n  public final function __clone() {\n    // You NEVER need to copy cacher object or siblings\n    throw new BadMethodCallException('Clone is not allowed');\n  }\n\n  /**\n   * @return int\n   */\n  public function getMode() {\n    return self::$mode;\n  }\n\n  /**\n   * @return string\n   */\n  public function getPrefix() {\n    return $this->prefix;\n  }\n\n  /**\n   * @param $prefix\n   */\n  public function setPrefix($prefix) {\n    $this->prefix = $prefix;\n  }\n\n  // -------------------------------------------------------------------------\n  // Here comes low-level functions - those that directly works with cacher engines\n  // -------------------------------------------------------------------------\n  public function __set($name, $value) {\n    switch (self::$mode) {\n      case self::CACHER_NO_CACHE:\n        self::$data[$this->prefix . $name] = $value;\n      break;\n\n      case self::CACHER_XCACHE:\n        xcache_set($this->prefix . $name, $value);\n      break;\n    }\n  }\n\n  public function __get($name) {\n    switch (self::$mode) {\n      case self::CACHER_NO_CACHE:\n        return array_key_exists($this->prefix . $name, self::$data) ? self::$data[$this->prefix . $name] : null;\n      break;\n\n      case self::CACHER_XCACHE:\n        return xcache_get($this->prefix . $name);\n      break;\n    }\n\n    return null;\n  }\n\n  public function __isset($name) {\n    switch (self::$mode) {\n      case self::CACHER_NO_CACHE:\n        return isset(self::$data[$this->prefix . $name]);\n      break;\n\n      case self::CACHER_XCACHE:\n        return xcache_isset($this->prefix . $name) && ($this->__get($name) !== null);\n      break;\n    }\n\n    return false;\n  }\n\n  public function __unset($name) {\n    switch (self::$mode) {\n      case self::CACHER_NO_CACHE:\n        unset(self::$data[$this->prefix . $name]);\n      break;\n\n      case self::CACHER_XCACHE:\n        xcache_unset($this->prefix . $name);\n      break;\n    }\n  }\n\n  public function unset_by_prefix($prefix_unset = '') {\n    static $array_clear;\n    !$array_clear ? $array_clear = function (&$v, $k, $p) {\n      strpos($k, $p) === 0 ? $v = null : false;\n    } : false;\n\n    switch (self::$mode) {\n      case self::CACHER_NO_CACHE:\n//        array_walk(self::$data, create_function('&$v,$k,$p', 'if(strpos($k, $p) === 0)$v = NULL;'), $this->prefix.$prefix_unset);\n        array_walk(self::$data, $array_clear, $this->prefix . $prefix_unset);\n\n        return true;\n      break;\n\n      case self::CACHER_XCACHE:\n        if (!function_exists('xcache_unset_by_prefix')) {\n          return false;\n        }\n\n        set_time_limit(300); // TODO - Optimize\n        $result = xcache_unset_by_prefix($this->prefix . $prefix_unset);\n        set_time_limit(30); // TODO - Optimize\n\n        return $result;\n      break;\n    }\n\n    return true;\n  }\n  // -------------------------------------------------------------------------\n  // End of low-level functions\n  // -------------------------------------------------------------------------\n\n  protected function make_element_name($args, $diff = 0) {\n    $num_args = count($args);\n\n    if ($num_args < 1) {\n      return false;\n    }\n\n    $name = '';\n    $aName = array();\n    for ($i = 0; $i <= $num_args - 1 - $diff; $i++) {\n      $name .= \"[{$args[$i]}]\";\n      array_unshift($aName, $name);\n    }\n\n    return $aName;\n  }\n\n  public function array_set() {\n    $args = func_get_args();\n    $name = $this->make_element_name($args, 1);\n\n    if (!$name) {\n      return null;\n    }\n\n    if ($this->$name[0] === null) {\n      for ($i = count($name) - 1; $i > 0; $i--) {\n        $cName = \"{$name[$i]}_COUNT\";\n        $cName1 = \"{$name[$i-1]}_COUNT\";\n        if ($this->$cName1 == null || $i == 1) {\n          $this->$cName++;\n        }\n      }\n    }\n\n    $this->$name[0] = $args[count($args) - 1];\n\n    return true;\n  }\n\n  public function array_get() {\n    $name = $this->make_element_name(func_get_args());\n    if (!$name) {\n      return null;\n    }\n\n    return $this->$name[0];\n  }\n\n  public function array_count() {\n    $name = $this->make_element_name(func_get_args());\n    if (!$name) {\n      return 0;\n    }\n    $cName = \"{$name[0]}_COUNT\";\n    $retVal = $this->$cName;\n    if (!$retVal) {\n      $retVal = null;\n    }\n\n    return $retVal;\n  }\n\n  public function array_unset() {\n    $name = $this->make_element_name(func_get_args());\n\n    if (!$name) {\n      return false;\n    }\n    $this->unset_by_prefix($name[0]);\n\n    $count = count($name);\n    for ($i = 1; $i < $count; $i++) {\n      $cName = \"{$name[$i]}_COUNT\";\n      $cName1 = \"{$name[$i-1]}_COUNT\";\n\n      if ($i == 1 || $this->$cName1 === null) {\n        $this->$cName--;\n        if ($this->$cName <= 0) {\n          unset($this->$cName);\n        }\n      }\n    }\n\n    return true;\n  }\n\n  public function dumpData() {\n    switch (self::$mode) {\n      case self::CACHER_NO_CACHE:\n        return dump(self::$data, $this->prefix);\n      break;\n\n      default:\n        return false;\n      break;\n    }\n  }\n\n  public function reset() {\n    $this->unset_by_prefix();\n\n    $this->_INITIALIZED = false;\n  }\n\n  public function init($reInit = false) {\n    $this->_INITIALIZED = true;\n  }\n\n  public function isInitialized() {\n    return $this->_INITIALIZED;\n  }\n\n  /**\n   * Whether a offset exists\n   * @link http://php.net/manual/en/arrayaccess.offsetexists.php\n   *\n   * @param mixed $offset <p>\n   * An offset to check for.\n   * </p>\n   *\n   * @return boolean true on success or false on failure.\n   * </p>\n   * <p>\n   * The return value will be casted to boolean if non-boolean was returned.\n   * @since 5.0.0\n   */\n  public function offsetExists($offset) {\n    return $this->__isset($offset);\n  }\n\n  /**\n   * Offset to retrieve\n   * @link http://php.net/manual/en/arrayaccess.offsetget.php\n   *\n   * @param mixed $offset <p>\n   * The offset to retrieve.\n   * </p>\n   *\n   * @return mixed Can return all value types.\n   * @since 5.0.0\n   */\n  public function offsetGet($offset) {\n    return $this->__get($offset);\n  }\n\n  /**\n   * Offset to set\n   * @link http://php.net/manual/en/arrayaccess.offsetset.php\n   *\n   * @param mixed $offset <p>\n   * The offset to assign the value to.\n   * </p>\n   * @param mixed $value <p>\n   * The value to set.\n   * </p>\n   *\n   * @return void\n   * @since 5.0.0\n   */\n  public function offsetSet($offset, $value) {\n    $this->__set($offset, $value);\n  }\n\n  /**\n   * Offset to unset\n   * @link http://php.net/manual/en/arrayaccess.offsetunset.php\n   *\n   * @param mixed $offset <p>\n   * The offset to unset.\n   * </p>\n   *\n   * @return void\n   * @since 5.0.0\n   */\n  public function offsetUnset($offset) {\n    $this->__unset($offset);\n  }\n\n}\n"
  },
  {
    "path": "classes/classConfig.php",
    "content": "<?php\n\n/** @noinspection PhpMissingFieldTypeInspection */\n/** @noinspection PhpMissingParamTypeInspection */\n/** @noinspection PhpMissingReturnTypeInspection */\n/** @noinspection PhpUnnecessaryCurlyVarSyntaxInspection */\n\n/**\n * Created by Gorlum 29.10.2016 10:16\n */\n\n\n/**\n *\n * This class is used to handle server configuration\n *\n * @package supernova\n *\n * @property string $db_prefix                     - REMOVE! Just for compatibility!\n *\n * @property int    $debug\n *\n * @property bool   $DEBUG_SQL_FILE_LOG            => 0 // Полный дамп запросов в рил-тайме. Подойдет любое значение\n * @property bool   $DEBUG_SQL_ERROR               => 0 // Выводить в сообщении об ошибке так же полный дамп запросов за сессию. Подойдет любое значение. Подразумевает `DEBUG_SQL_COMMENT`\n * @property bool   $DEBUG_SQL_COMMENT_LONG        => 0 // Добавлять SQL запрос длинные комментарии. Подойдет любое значение. Подразумевает `DEBUG_SQL_COMMENT`\n * @property bool   $DEBUG_SQL_COMMENT             => 0 // Добавлять комментарии прямо в SQL запрос. Подойдет любое значение\n *\n * @property string $db_version\n * @property int    $db_manual_lock_enabled\n *\n * @property string $admin_http_key                => '', // Use this key to access some admin functionality via HTTP - say, in script\n *\n * @property string $ali_bonus_members             => 10, // Minimum alliance size to start using bonus\n *\n * @property string $auth_vkontakte_app_id\n * @property string $auth_vkontakte_app_key\n * @property string     $auth_vkontakte_token\n * @property int        $auth_vkontakte_token_expire\n *\n * @property int        $BuildLabWhileRun              => 0, // Allow to build lab/Nanolab while tech researching AND allowing research tech while lab/Nanolab\n *\n * @property string     $COOKIE_NAME                   => 'SuperNova'\n *\n * @property int        $empire_mercenary_base_period  => PERIOD_MONTH, // Base hire period for price calculations\n * @property int        $empire_mercenary_temporary    => 0, // Temporary empire-wide mercenaries\n *\n * @property float      $fleet_update_dispatch_time    => 3           // (float), seconds Maximum seconds fleet dispatch can run, Default 3s\n * @property int        $fleet_update_interval         => 4 second    // how often fleet dispatch worker should run\n * @property int        $fleet_update_max_run_time     => 30,         // (int), seconds. How long fleet dispatch worker can run. Should be 1 second or more\n * @property int        $fleet_update_last             => SN_TIME_NOW // unixtime - when fleet was updated last\n * @property int        $fleet_update_lock             => ''          // SQL time when lock was acquired\n *\n * @property string     $game_adminEmail               => 'root@localhost',    // Admin email to show to users\n *\n * @property string     $game_default_language         => 'ru'\n * @property string     $game_default_skin             => 'skins/EpicBlue/'\n * @property string     $game_default_template         => 'OpenGame'\n *\n * @property int        $game_installed                => 0 - is game installed\n * @property int        $game_disable                  => GAME_DISABLE_INSTALL - Current game status - see GAME_DISABLE_xxx\n * @property string     $game_disable_reason           => 'SuperNova is in maintenance mode! Please return later!' - Status for custom disable reason\n *\n * @property int        $game_maxGalaxy                => 5\n * @property int        $game_maxSystem                => 199\n * @property int        $game_maxPlanet                => 15\n *\n * @property string     $game_name                     Server's name as it would be seen through game\n *\n * @property string     $game_watchlist\n *\n * @property int        $metal_basic_income            => 40,\n * @property int        $crystal_basic_income          => 20,\n * @property int        $deuterium_basic_income        => 0,\n * @property int        $energy_basic_income           => 0,\n *\n * @property int        $game_news_actual              How long announcement would be marked as \"New\". In seconds. Default - 3 days PERIOD_DAY_3\n * @property int        $game_news_overview            How much last news to show in Overview page. Default - 3\n * @property int        $game_news_overview_show       How long news will be shown in Overview page in seconds. Default - 2 weeks. 0 - show all\n *\n * @property int        $game_noob_factor              => 5    // Multiplier to divide \"stronger\" and \"weaker\" users\n * @property int        $game_noob_points              => 5000 // Below this point user treated as noob. 0 to disable\n *\n * @property string     $int_format_date               => 'd.m.Y' // Date default format\n * @property string     $int_format_time               => 'H:i:s' // Time default format\n *\n * @property int        $menu_server_name_disabled     => 0\n * @property int        $menu_launch_date_disabled     => 0\n * @property int        $menu_server_logo              => MENU_SERVER_LOGO_DEFAULT\n * @property int        $menu_server_logo_disabled     => 0\n *\n * @property string     $payment_currency_default      => 'USD',\n * @property float      $payment_currency_exchange_dm_ => 20000,\n * @property float      $payment_currency_exchange_mm_ => 20000,\n * @property float      $payment_currency_exchange_eur => 0.90,\n * @property float      $payment_currency_exchange_rub => 60,\n * @property float      $payment_currency_exchange_uah => 30,\n * @property float      $payment_currency_exchange_usd => 1,\n * @property float      $payment_currency_exchange_wmb => 18000,\n * @property float      $payment_currency_exchange_wme => 0.9,\n * @property float      $payment_currency_exchange_wmr => 60,\n * @property float      $payment_currency_exchange_wmu => 30,\n * @property float      $payment_currency_exchange_wmz => 1,\n *\n * @property int        $tutorial_first_item           ID of first item of tutorial\n *\n * @property string $url_faq                 URL of FAQ root\n * @property string $url_purchase_metamatter URL to purchase MM for servers w/o payment modules\n * @property string $url_forum               '',\n * @property string $url_rules               '',\n *\n * @property int        $users_amount                  => 1,                // Total users count\n * @property int        $game_users_online_timeout     => PERIOD_MINUTE_15, // How long user should consider ONLINE for online counter (seconds)\n * @property int        $game_users_update_online      => 30,               // How often user online should be refreshed (seconds)\n * @property int        $var_online_user_time          => 0,                // When last time user online was refreshed (Unix timestamp)\n * @property int        $var_online_user_count         => 0,                // Last calculated online user count\n * @property int        $server_log_online             => 0,                // Log online user count\n *\n * @property int        $quest_total                   => 0, // Total number of quests\n *\n * @property float      $resource_multiplier           => 1, // aka Mining speed\n * @property float      $game_speed                    => 1, // Game speed aka Building/Research speed\n * @property float      $fleet_speed                   => 1, // Fleet speed\n * @property float      $game_speed_expedition         => 1, // Game expedition rate aka Game expedition speed\n *\n * @property int        $tpl_minifier                  => 0, // Template minifier\n * @property int        $tpl_allow_php                 => 0, // PTL allow INCLUDEPHP and PHP tags\n *\n * @property int        $uni_galaxy_distance           => 20000, // Distance between galaxies\n *\n * @property int        $user_birthday_celebrate       => 0, // When last time celebrations (i.e. gift-giving) was made\n * @property int        $user_birthday_gift            => 0, // User birthday gift - DM points\n * @property int        $user_birthday_range           => PERIOD_MONTH, // How far in past can be user birthday for giving him gift\n *\n * ----- Player settings\n * @property int        $player_metamatter_immortal    => 200000, // MM amount to reward account with Immortal status\n *\n * @property int        $game_user_changename          => 2, // Is user allowed to change name after registration?\n * @property int        $game_user_changename_cost     => 100000, // Change name cost for paid changename\n *\n * @property int        $user_vacation_disable         => 0, // Disable vacation mode for players\n * @property int        $player_vacation_time          => PERIOD_WEEK, // Minimal vacation length in seconds\n * @property int        $player_vacation_timeout       => PERIOD_WEEK, // Timeout after leaving vacation to start new one in seconds\n *\n * @property string     $player_levels                 => '', // JSON-encoded array of [(int)level => (float)maxPointsForLevel]\n * @property string     $player_levels_calculated      => '2000-01-01 00:00:00', // Date and time when player level was calculated last\n *\n * @property int        $player_delete_time            => 3888000, //\n *\n *\n *\n * ----- Planet settings\n * @property int        $LastSettedGalaxyPos           => 1,\n * @property int        $LastSettedPlanetPos           => 1,\n * @property int        $LastSettedSystemPos           => 1,\n *\n * @property int        $eco_planet_starting_crystal   => 500,\n * @property int        $eco_planet_starting_deuterium => 0,\n * @property int        $eco_planet_starting_metal     => 500,\n * @property int        $eco_planet_storage_crystal    => 500000,\n * @property int        $eco_planet_storage_deuterium  => 500000,\n * @property int        $eco_planet_storage_metal      => 500000,\n *\n * @property int        $planet_capital_cost           => 25000, // Cost in DM to move Capital to current planet\n * @property float      $planet_capital_mining_rate    => 2.0,   // Capital Mining rates\n * @property float      $planet_capital_building_rate  => 2.0,   // Capital Building rates\n * @property int        $planet_teleport_cost          => 50000, // Cost of planet teleportation\n * @property int        $planet_teleport_timeout       => 86400, // Timeout for next teleportation\n *\n * @property string     $server_updater_check_auto     => 0, // Server auto-check version\n * @property int        $server_updater_check_last     => 0, // Server last check time\n * @property int        $server_updater_check_period   => PERIOD_DAY, // Server auto-check period\n * @property int        $server_updater_check_result   => SNC_VER_NEVER, // Server last check result\n * @property int|string $server_updater_id             => 0, // Server ID on update server\n * @property string     $server_updater_key            => '', // Server key on update server\n *\n * @property int        $stats_hide_admins             => 1,  // Hide admins accounts from stat and stat of admins\n * @property string     $stats_hide_player_list        => '', // Comma separated list of player IDs which stat to hide. Used for bots, for example\n * @property int        $stats_hide_pm_link            => 0,  // Hide PM link from stat screen\n * @property int        $stats_history_days            => 14, // За сколько дней хранить статистику в базе\n * @property string     $stats_minimal_interval        => STATS_RUN_INTERVAL_MINIMUM -  Minimal interval between stat runs in seconds. Default - 600s aka 10 minutes\n * @property string     $stats_schedule                => '04:00:00' - Schedule for running stat updates - see readme.txt\n * @property string     $stats_php_memory              => ???????????????\n *\n * @property int        $upd_lock_time                 => Update lock time\n *\n * @property string     $server_cypher                 => Internally generated cypher for in-server communications\n *\n *\n * @property string            $var_db_update                 => '0' - SQL_DATE_TIME\n * @property string            $var_db_update_end             => '0' - SQL_DATE_TIME\n *\n * @property string            $var_stat_update               => '0' - SQL_DATE_TIME - when stat update was started\n * @property string            $var_stat_update_end           => '0' - SQL_DATE_TIME - ?????????\n * @property string            $var_stat_update_admin_forced  => '0' - SQL_DATE_TIME - Last time when update was triggered from admin console\n * @property string            $var_stat_update_next          => ''  - SQL_DATE_TIME - Next time when stat update scheduled to run\n * @property string            $var_stat_update_msg           => 'Update never started' - Last stat update message\n *\n * @property string            $var_news_last                 => 0, // Last news post time\n *\n * @property int    $allow_buffing           => 0, // Disable buffing check for TRANSPORT missions\n * @property int    $ally_help_weak          => 0, // Allow strong players to HOLD on weak co-ally planets\n * @property int    $game_email_pm           => 0, // Is allowed forwarding messages from PM to e-mail?\n * @property int    $rpg_exchange_metal      => 1,\n * @property int    $rpg_exchange_crystal    => 2,\n * @property int    $rpg_exchange_deuterium  => 4,\n * @property int    $rpg_exchange_darkMatter => 400,\n * @property int    $initial_fields          => 163,\n * @property int    $chat_refresh_rate       => 5, // in seconds. Chat AJAX refresh rate\n * @property int    $chat_timeout            => 900, // in seconds. Default = 15 min\n * @property int    $game_counter            => 0,  // Does built-in page hit counter is on?\n * @property string $geoip_whois_url         => '',\n * @property int    $advGoogleLeftMenuIsOn   => 0,\n * @property string $advGoogleLeftMenuCode   => '(Place here code for banner)',\n * @property int    $uni_price_galaxy        => 10000,\n * @property int    $uni_price_system        => 1000,\n * @property int               $fleet_bashing_attacks   => 3,      // Max amount of attack per wave - 3 by default\n * @property int               $fleet_bashing_interval  => 1800,   // Maximum interval between attacks when they still count as one wave - 30m by default\n * @property int               $fleet_bashing_scope     => 86400,  // Interval on which bashing waves counts - 24h by default\n * @property int               $fleet_bashing_war_delay => 43200,  // Delay before start bashing after declaring war to alliance - 12h by default\n * @property int               $fleet_bashing_waves     => 3,      // Max amount of waves per day - 3 by default\n * @property int               $player_max_colonies     => -1, // Max player planet count (NOT including main planet)\n * @property int               $eco_scale_storage       => 1,\n * @property int               $game_mode               => 0,           // 0 - SuperNova, 1 - oGame\n * @property int $festival_gather_log_enabled => 0, // Is Festival Gather log enabled?\n * @property int $festival_gather_autobalance  => 0, // Is Festival Gather auto-balancer enabled?\n *\n *\n * @property string            $cache_prefix Temporary for updater\n *\n * @property mixed|string|null $rpg_cost_exchange => 1000, // Exchange allows resource trade between players\n */\nclass classConfig extends classPersistent {\n  const DATE_TYPE_UNIX = 0;\n  const DATE_TYPE_SQL_STRING = 1;\n\n  const FLEET_UPDATE_RUN_LOCK = 'fleet_update_run_lock';\n  const FLEET_UPDATE_MAX_RUN_TIME = 'fleet_update_max_run_time';\n\n  /**\n   * Internal cypher string for server/server communication\n   *\n   * @var string $cypher\n   */\n  protected $cypher = '';\n\n  protected $defaults = array(\n    'geoip_whois_url' => '',\n\n    'db_manual_lock_enabled' => 0,\n\n    'admin_http_key'  => '',\n\n    // Debug\n    'DEBUG_SQL_FILE_LOG'     => 0, // Полный дамп запросов в отдельный файл. Подойдет любое значение\n    'DEBUG_SQL_ERROR'        => 0, // Выводить в сообщении об ошибке так же полный дамп запросов за сессию. Подойдет любое значение. Подразумевает `DEBUG_SQL_COMMENT`\n    'DEBUG_SQL_COMMENT_LONG' => 0, // Добавлять SQL запрос длинные комментарии. Подойдет любое значение. Подразумевает `DEBUG_SQL_COMMENT`\n    'DEBUG_SQL_COMMENT'      => 0, // Добавлять комментарии прямо в SQL запрос. Подойдет любое значение\n\n    // SEO meta\n    'adv_conversion_code_payment'  => '',\n    'adv_conversion_code_register' => '',\n    'adv_seo_meta_description'     => '',\n    'adv_seo_meta_keywords'        => '',\n    'adv_seo_javascript'           => '',\n\n    // Advert banner\n    'advGoogleLeftMenuIsOn'        => 0,\n    'advGoogleLeftMenuCode'        => '(Place here code for banner)',\n\n    // Alliance bonus calculations\n    'ali_bonus_algorithm'          => 0,  // Bonus calculation algorithm\n    'ali_bonus_brackets'           => 10, // Brackets count for ALI_BONUS_BY_RANK\n    'ali_bonus_brackets_divisor'   => 10,// Bonus divisor for ALI_BONUS_BY_RANK\n    'ali_bonus_divisor'            => 10000000, // Rank divisor for ALI_BONUS_BY_POINTS\n    'ali_bonus_members'            => 10, // Minimum alliance size to start using bonus\n\n    'allow_buffing'  => 0, // Disable buffing check for TRANSPORT missions\n    'ally_help_weak' => 0, // Allow strong players to HOLD on weak co-ally planets\n\n    'auth_vkontakte_app_id'       => '',\n    'auth_vkontakte_app_key'      => '',\n    'auth_vkontakte_token'        => '',\n    'auth_vkontakte_token_expire' => '2000-01-01',\n\n    // User avatar and alliance logo\n    'avatar_max_height'           => 128, // Maximum height\n    'avatar_max_width'            => 128, // Maximum width\n\n    'BuildLabWhileRun'         => 0, // Allow to build lab/Nanolab while tech researching AND allowing research tech while lab/Nanolab\n\n    // Chat settings\n    // Nick highlighting\n    'chat_highlight_developer' => '<span class=\\\"nick_developer\\\">$1</span>', // Developer nick\n    'chat_highlight_admin'     => '<span class=\\\"nick_admin\\\">$1</span>', // Admin nick\n    'chat_highlight_moderator' => '<span class=\\\"nick_moderator\\\">$1</span>', // Moderator nick\n    'chat_highlight_operator'  => '<span class=\\\"nick_operator\\\">$1</span>', // Operator nick\n    'chat_highlight_premium'   => '<span class=\\\"nick_premium\\\">$1</span>', // Premium nick\n    // Other chat settings\n    'chat_refresh_rate'        => 5, // in seconds. Chat AJAX refresh rate\n    'chat_timeout'             => 900, // in seconds. Default = 15 min\n\n    'COOKIE_NAME' => 'SuperNova',\n    'debug'       => 0,\n    'Defs_Cdr'    => 30,\n\n    'eco_planet_starting_crystal'   => 500,\n    'eco_planet_starting_deuterium' => 0,\n    'eco_planet_starting_metal'     => 500,\n    'eco_planet_storage_crystal'    => 500000,\n    'eco_planet_storage_deuterium'  => 500000,\n    'eco_planet_storage_metal'      => 500000,\n\n    'eco_scale_storage'            => 1,\n    'eco_stockman_fleet'           => '', // Black Market - Starting amount of s/h ship merchant to sell\n    'eco_stockman_fleet_populate'  => 1,  // Populate empty Stockman fleet with ships or not\n    'empire_mercenary_base_period' => PERIOD_MONTH, // Base\n    'empire_mercenary_temporary'   => 0, // Temporary empire-wide mercenaries\n\n    // Planet basic income\n    'metal_basic_income'           => 40,\n    'crystal_basic_income'         => 20,\n    'deuterium_basic_income'       => 0,\n    'energy_basic_income'          => 0,\n\n    'festival_gather_log_enabled'  => 0, // Is Festival Gather log enabled?\n    'festival_gather_autobalance'  => 0, // Is Festival Gather auto-balancer enabled?\n\n    // Bashing protection settings\n    'fleet_bashing_attacks'        => 3,      // Max amount of attack per wave - 3 by default\n    'fleet_bashing_interval'       => 1800,   // Maximum interval between attacks when they still count as one wave - 30m by default\n    'fleet_bashing_scope'          => 86400,  // Interval on which bashing waves counts - 24h by default\n    'fleet_bashing_war_delay'      => 43200,  // Delay before start bashing after declaring war to alliance - 12h by default\n    'fleet_bashing_waves'          => 3,      // Max amount of waves per day - 3 by default\n\n    'Fleet_Cdr'   => 30,\n    'fleet_speed' => 1,\n\n    'fleet_update_dispatch_time'    => 3,  // (float) Maximum seconds fleet dispatch can run, Default 3s\n    'fleet_update_interval'         => 4,  // How often fleet dispatch worker should run\n    self::FLEET_UPDATE_MAX_RUN_TIME => 30, // (int) seconds. How long fleet dispatch worker can run. Should be 1 second or more\n    'fleet_update_lock'             => '', // SQL time when lock was acquired\n\n    'game_adminEmail'       => 'root@localhost',    // Admin email to show to users\n    'game_counter'          => 0,  // Does built-in page hit counter is on?\n    // Defaults\n    'game_default_language' => 'ru',\n    'game_default_skin'     => 'skins/EpicBlue/',\n    'game_default_template' => 'OpenGame',\n\n    'game_disable'        => GAME_DISABLE_INSTALL,\n    'game_disable_reason' => 'SuperNova is in maintenance mode! Please return later!',\n    'game_email_pm'       => 0, // Is allowed forwarding messages from PM to e-mail?\n    // Universe size\n    'game_maxGalaxy'      => 5,\n    'game_maxSystem'      => 199,\n    'game_maxPlanet'      => 15,\n    // Game global settings\n    'game_mode'           => 0,           // 0 - SuperNova, 1 - oGame\n    'game_name'           => 'SuperNova', // Server name (would be on banners and on top of left menu)\n\n    'game_news_actual'        => PERIOD_DAY_3, // How long announcement would be marked as \"New\". In seconds. Default - 3 days\n    'game_news_overview'      => 3,    // How much last news to show in Overview page\n    'game_news_overview_show' => PERIOD_WEEK_2,    // How long news will be shown in Overview page in seconds. Default - 2 weeks\n    // Noob protection\n    'game_noob_factor'        => 5,    // Multiplier to divide \"stronger\" and \"weaker\" users\n    'game_noob_points'        => 5000, // Below this point user treated as noob. 0 to disable\n\n    'game_multiaccount_enabled' => 0, // 1 - allow interactions for players with same IP (multiaccount)\n\n    'game_speed'            => 1, // Game speed\n    'game_speed_expedition' => 1, // Game expedition speed\n\n    'game_user_changename'      => 2, // Is user allowed to change name after registration?\n    'game_user_changename_cost' => 100000, // Change name cost for paid changename\n\n    'initial_fields'            => 163,\n\n    // Interface - UserBanner\n    'int_banner_background'     => 'design/images/banner.png',\n    'int_banner_fontInfo'       => 'terminator.ttf',\n    'int_banner_fontRaids'      => 'klmnfp2005.ttf',\n    'int_banner_fontUniverse'   => 'cristal.ttf',\n    'int_banner_showInOverview' => 1,\n    'int_banner_URL'            => '/banner.php?type=banner',\n\n    'int_format_date'            => 'd.m.Y', // Date default format\n    'int_format_time'            => 'H:i:s', // Time default format\n\n    // Interface - UserBar\n    'int_userbar_background'     => 'design/images/userbar.png',\n    'int_userbar_font'           => 'arialbd.ttf',\n    'int_userbar_showInOverview' => 1,\n    'int_userbar_URL'            => '/banner.php?type=userbar',\n\n    'LastSettedGalaxyPos' => 1,\n    'LastSettedPlanetPos' => 1,\n    'LastSettedSystemPos' => 1,\n\n    'locale_cache_disable' => 0, // Disable locale caching\n\n    'menu_server_name_disabled' => 0,\n    'menu_launch_date_disabled' => 0,\n    'menu_server_logo'          => MENU_SERVER_LOGO_DEFAULT,\n    'menu_server_logo_disabled' => 0,\n\n    'payment_currency_default'      => 'USD',\n    'payment_currency_exchange_dm_' => 20000,\n    'payment_currency_exchange_mm_' => 20000,\n    'payment_currency_exchange_eur' => 0.90,\n    'payment_currency_exchange_rub' => 60,\n    'payment_currency_exchange_uah' => 30,\n    'payment_currency_exchange_usd' => 1,\n    'payment_currency_exchange_wmb' => 18000,\n    'payment_currency_exchange_wme' => 0.9,\n    'payment_currency_exchange_wmr' => 60,\n    'payment_currency_exchange_wmu' => 30,\n    'payment_currency_exchange_wmz' => 1,\n    'payment_currency_exchange_pln' => 3.86,\n\n    'payment_lot_price' => 1,     // Lot's price in default currency\n    'payment_lot_size'  => 2500,  // Lot size. Also service as minimum amount of DM that could be bought with one transaction\n\n    'planet_capital_cost'          => 25000, // Cost in DM to move Capital to current planet\n    'planet_capital_mining_rate'   => 2.0,   // Capital Mining rates\n    'planet_capital_building_rate' => 2.0,   // Capital Building rates\n    'planet_teleport_cost'         => 50000, // Cost of planet teleportation\n    'planet_teleport_timeout'      => 86400, // Timeout for next teleportation\n\n    'player_delete_time'  => 3888000, //\n    'player_max_colonies' => -1, // Max player planet count (NOT including main planet)\n\n    'user_vacation_disable'   => 0, // Disable vacation mode for players\n    'player_vacation_time'    => PERIOD_WEEK, //\n    'player_vacation_timeout' => PERIOD_WEEK, //\n\n    'player_metamatter_immortal' => 200000, // MM amount to reward account with Immortal status\n\n    'player_levels'            => '', // JSON-encoded array of [(int)level => (float)maxPointsForLevel]\n    'player_levels_calculated' => '2000-01-01 00:00:00', // Date and time when player level was calculated last\n\n    // Quests\n    'quest_total'              => 0, // Total number of quests\n\n    'resource_multiplier'     => 1,\n\n    //Role play system\n    'rpg_bonus_divisor'       => 10,    // Amount of DM referral should get for partner have 1 DM bonus\n    'rpg_bonus_minimum'       => 10000, // Minimum DM amount for starting paying bonuses to affiliate\n\n    // Black Market - General\n    'rpg_cost_banker'         => 1000, // Banker can hold some resources\n    'rpg_cost_exchange'       => 1000, // Exchange allows resource trade between players\n    'rpg_cost_info'           => 10000, // Infotrader sells valuable information about users, alliances and universe\n    'rpg_cost_pawnshop'       => 1000, // You can get loan in pawnshop\n    'rpg_cost_scraper'        => 1000, // Scrapper buys ship for fraction of cost\n    'rpg_cost_stockman'       => 1000, // Stockman resells ship that was scrapped\n    'rpg_cost_trader'         => 1000, // Trader trades between resources\n\n    // Black Market - Resource exchange rates\n    'rpg_exchange_metal'      => 1,\n    'rpg_exchange_crystal'    => 2,\n    'rpg_exchange_deuterium'  => 4,\n    'rpg_exchange_darkMatter' => 400,\n\n    'rpg_flt_explore'      => 1000, // DM reward for finding Supernova in expedition\n\n    // Black Market - Scraper rates for ship pre resource\n    'rpg_scrape_crystal'   => 0.50,\n    'rpg_scrape_deuterium' => 0.25,\n    'rpg_scrape_metal'     => 0.75,\n\n    'security_ban_extra'               => '',\n    'security_write_full_url_disabled' => 1, // Disables writing full URLs to counter table\n\n    'server_que_length_hangar'     => '5', //\n    'server_que_length_research'   => '1', //\n    'server_que_length_structures' => '5', //\n\n    'server_start_date' => '', //\n\n    'server_updater_check_auto'   => 0, // Server auto-check version\n    'server_updater_check_last'   => 0, // Server last check time\n    'server_updater_check_period' => PERIOD_DAY, // Server auto-check period\n    'server_updater_check_result' => SNC_VER_NEVER, // Server last check result\n    'server_updater_id'           => 0, // Server ID on update server\n    'server_updater_key'          => '', // Server key on update server\n\n    'stats_history_days'     => 14, // За сколько дней хранить статистику в базе\n    'stats_hide_admins'      => 1,\n    'stats_hide_player_list' => '',\n    'stats_hide_pm_link'     => 0,\n    'stats_minimal_interval' => STATS_RUN_INTERVAL_MINIMUM, // Minimal stats interval\n    'stats_schedule'         => '04:00:00',\n    'stats_php_memory'       => '1G',\n\n    'tpl_minifier'  => 0, // Template minifier\n    'tpl_allow_php' => 0, // PTL allow INCLUDEPHP and PHP tags\n\n    'tutorial_first_item' => 1,\n\n    'uni_galaxy_distance' => UNIVERSE_GALAXY_DISTANCE, // 20000 by default\n    'uni_price_galaxy'    => 10000,\n    'uni_price_system'    => 1000,\n\n    'upd_lock_time' => 300, // How long update will lock table. Also update increment time when it requires\n\n    'url_faq'                 => '',\n    'url_forum'               => '',\n    'url_purchase_metamatter' => '',\n    'url_rules'               => '',\n\n    'users_amount'              => 1,                // Total users count\n    'game_users_online_timeout' => PERIOD_MINUTE_15, // Seconds, How long user should consider ONLINE for online counter\n    'game_users_update_online'  => 30,               // How often user online should be refreshed (seconds)\n    'var_online_user_time'      => 0,                // When last time user online was refreshed\n    'var_online_user_count'     => 0,                // Last calculated online user count\n    'server_log_online'         => 0,                // Log online user count\n\n    'user_birthday_celebrate' => 0, // When last time celebrations (i.e. gift-giving) was made - timestamp\n    'user_birthday_gift'      => 0, // User birthday gift - Dark Matter points\n    'user_birthday_range'     => PERIOD_MONTH, // How far in past can be user birthday for giving him gift - seconds\n\n\n    'var_db_update'     => 0, // Time of last DB update\n    'var_db_update_end' => 0, // Time when last DB update should end. Need to prevent duplicate update\n\n    'var_news_last'       => 0, // Last news post time\n\n    // Statistic\n    'var_stat_update'     => 0,\n    'var_stat_update_end' => 0,\n    'var_stat_update_msg' => 'Update never started',\n\n  );\n\n  protected $notEmptyFields = [\n    'upd_lock_time' => 'upd_lock_time',\n  ];\n\n  public function __construct($gamePrefix = 'sn_') {\n    parent::__construct($gamePrefix, 'config');\n  }\n\n  public static function getInstance($gamePrefix = 'sn_', $table_name = 'config') {\n    if (!isset(self::$cacheObject)) {\n      $className         = get_class();\n      self::$cacheObject = new $className($gamePrefix, $table_name);\n    }\n\n    return self::$cacheObject;\n  }\n\n  /**\n   * @param int|string $date Date ether as Unix timestamp or mySQL timestamp\n   * @param int        $as   Output format WATCHDOG_TIME_UNIX | WATCHDOG_TIME_SQL\n   *\n   * @return false|int|string Will return 0 on invalid string with WATCHDOG_TIME_UNIX and FALSE on invalid value with WATCHDOG_TIME_UNIX\n   * @see FMT_DATE_TIME_SQL\n   */\n  public function dateConvert($date, $as) {\n    if ($as === self::DATE_TYPE_UNIX && !is_numeric($date)) {\n      // It is not a TIMESTAMP - maybe it's SQL timestamp or other date-related string? Trying to convert to UNIX\n      $date = intval(strtotime($date, SN_TIME_NOW));\n    } elseif ($as === self::DATE_TYPE_SQL_STRING && (!is_string($date) || is_numeric($date))) {\n      $date = date(FMT_DATE_TIME_SQL, $date);\n    }\n\n    return $date;\n  }\n\n  /**\n   * Will write to DB date as specified format\n   *\n   * @param string     $name Config field name\n   * @param int|string $date Date ether as Unix timestamp or mySQL timestamp\n   * @param int        $as   Format of field in config table WATCHDOG_TIME_UNIX | WATCHDOG_TIME_SQL\n   *\n   * @return classConfig\n   * @see dateConvert()\n   */\n  public function dateWrite($name, $date, $as = self::DATE_TYPE_SQL_STRING) {\n    $this->pass()[$name] = $this->dateConvert($date, $as);\n\n    return $this;\n  }\n\n  /**\n   * Will read from DB date and convert it to specified format\n   *\n   * @param string $name Config field name\n   * @param int    $as   Output format WATCHDOG_TIME_UNIX | WATCHDOG_TIME_SQL\n   *\n   * @return false|int|string\n   * @see dateConvert()\n   */\n  public function dateRead($name, $as) {\n    return $this->dateConvert($this->pass()[$name], $as);\n  }\n\n  public function getCypher() {\n    $db = SN::$gc->db;\n\n    if (empty($this->cypher)) {\n      $db->transactionStart();\n      $cypher = $this->pass()->server_cypher;\n      if (empty($cypher)) {\n        $cypher = md5(sys_random_string(32));\n\n        $this->pass()->server_cypher = $cypher;\n\n        $db->transactionCommit();\n      } else {\n        $db->transactionRollback();\n      }\n      $this->cypher = $cypher;\n    }\n\n    return $this->cypher;\n  }\n\n}\n"
  },
  {
    "path": "classes/classLocale.php",
    "content": "<?php\n\nclass classLocale implements ArrayAccess {\n  public $container = array();\n  public $lang_list = null;\n  public $active = null;\n\n  public $enable_stat_usage = false;\n  protected $stat_usage = array();\n  protected $stat_usage_new = array();\n\n  /**\n   * Порядок проверки языков\n   *\n   * @var array $fallback\n   */\n  protected $fallback = array();\n\n  /**\n   * @var classCache $cache\n   */\n  protected $cache = null;\n  protected $cache_prefix = 'lng_';\n  protected $cache_prefix_lang = '';\n\n  public function __construct($enable_stat_usage = false) {\n    SN::log_file('locale.__constructor: Starting', 1);\n\n    $this->container = array();\n\n    if (SN::$cache->getMode() != classCache::CACHER_NO_CACHE && !SN::$config->locale_cache_disable) {\n      $this->cache = SN::$cache;\n      SN::log_file('locale.__constructor: Cache is present');\n//$this->cache->unset_by_prefix($this->cache_prefix); // TODO - remove? 'cause debug!\n    }\n\n    if ($enable_stat_usage && empty($this->stat_usage)) {\n      $this->enable_stat_usage = $enable_stat_usage;\n      $this->usage_stat_load();\n      // TODO shutdown function\n      register_shutdown_function(array($this, 'usage_stat_save'));\n    }\n\n    SN::log_file(\"locale.__constructor: Switching language to default\");\n    $this->lng_switch(DEFAULT_LANG);\n\n    SN::log_file(\"locale.__constructor: Complete - EXIT\", -1);\n  }\n\n  /**\n   * Фоллбэк для строки на другие локали\n   *\n   * @param array|string $offset\n   */\n  protected function locale_string_fallback($offset) {\n    global $locale_cache_statistic;\n    // Фоллбэк вызывается только если мы не нашли нужную строку в массиве...\n    $fallback = $this->fallback;\n    // ...поэтому $offset в активном языке заведомо нет\n    unset($fallback[$this->active]);\n\n    // Проходим по оставшимся локалям\n    foreach ($fallback as $try_language) {\n      // Если нет такой строки - пытаемся вытащить из кэша\n      if (!isset($this->container[$try_language][$offset]) && $this->cache) {\n        $this->container[$try_language][$offset] = $this->cache->__get($this->cache_prefix . $try_language . '_' . $offset);\n        // Записываем результат работы кэша\n        $locale_cache_statistic['queries']++;\n        isset($this->container[$try_language][$offset]) ? $locale_cache_statistic['hits']++ : $locale_cache_statistic['misses']++;\n        !isset($this->container[$try_language][$offset]) ? $locale_cache_statistic['missed_str'][] = $this->cache_prefix . $try_language . '_' . $offset : false;\n      }\n\n      // Если мы как-то где-то нашли строку...\n      if (isset($this->container[$try_language][$offset])) {\n        // ...значит она получена в результате фоллбэка и записываем её в кэш и контейнер\n        $this[$offset] = $this->container[$try_language][$offset];\n        $locale_cache_statistic['fallbacks']++;\n        break;\n      }\n    }\n  }\n\n  public function offsetSet($offset, $value) {\n    if (is_null($offset)) {\n      $this->container[$this->active][] = $value;\n    } else {\n      $this->container[$this->active][$offset] = $value;\n      if ($this->cache) {\n        $this->cache->__set($this->cache_prefix_lang . $offset, $value);\n      }\n    }\n  }\n\n  public function offsetExists($offset) {\n    // Шорткат если у нас уже есть строка в памяти PHP\n    if (!isset($this->container[$this->active][$offset])) {\n      if (!$this->cache || !($this->container[$this->active][$offset] = $this->cache->__get($this->cache_prefix_lang . $offset))) {\n        // Если нету такой строки - делаем фоллбэк\n        $this->locale_string_fallback($offset);\n      }\n\n      return isset($this->container[$this->active][$offset]);\n    } else {\n      return true;\n    }\n  }\n\n  public function offsetUnset($offset) {\n    unset($this->container[$this->active][$offset]);\n  }\n\n  public function offsetGet($offset) {\n    $value = $this->offsetExists($offset) ? $this->container[$this->active][$offset] : null;\n    if ($this->enable_stat_usage) {\n      $this->usage_stat_log($offset, $value);\n    }\n\n    return $value;\n  }\n\n\n  public function merge($array) {\n    $this->container[$this->active] = is_array($this->container[$this->active]) ? $this->container[$this->active] : array();\n    // $this->container[$this->active] = array_merge($this->container[$this->active], $array);\n    $this->container[$this->active] = array_replace_recursive($this->container[$this->active], $array);\n  }\n\n\n  public function usage_stat_load() {\n    global $sn_cache;\n\n    $this->stat_usage = $sn_cache->lng_stat_usage = array(); // TODO for debug\n    if (empty($this->stat_usage)) {\n      $query = doquery(\"SELECT * FROM {{lng_usage_stat}}\");\n      while ($row = db_fetch($query)) {\n        $this->stat_usage[$row['lang_code'] . ':' . $row['string_id'] . ':' . $row['file'] . ':' . $row['line']] = $row['is_empty'];\n      }\n    }\n  }\n\n  public function usage_stat_save() {\n    if (!empty($this->stat_usage_new)) {\n      global $sn_cache;\n      $sn_cache->lng_stat_usage = $this->stat_usage;\n      doquery(\"SELECT 1 FROM {{lng_usage_stat}} LIMIT 1\");\n      foreach ($this->stat_usage_new as &$value) {\n        foreach ($value as &$value2) {\n          $value2 = '\"' . SN::$db->db_escape($value2) . '\"';\n        }\n        $value = '(' . implode(',', $value) . ')';\n      }\n      doquery(\"REPLACE INTO {{lng_usage_stat}} (lang_code,string_id,`file`,line,is_empty,locale) VALUES \" . implode(',', $this->stat_usage_new));\n    }\n  }\n\n  public function usage_stat_log(&$offset, &$value) {\n    $trace = debug_backtrace();\n    unset($trace[0]);\n    unset($trace[1]['object']);\n\n    $file = str_replace('\\\\', '/', substr($trace[1]['file'], strlen(SN_ROOT_PHYSICAL) - 1));\n\n    $string_id = $this->active . ':' . $offset . ':' . $file . ':' . $trace[1]['line'];\n    if (!isset($this->stat_usage[$string_id]) || $this->stat_usage[$string_id] != $empty) {\n      $this->stat_usage[$string_id] = empty($value);\n      $this->stat_usage_new[]       = array(\n        'lang_code' => $this->active,\n        'string_id' => $offset,\n        'file'      => $file,\n        'line'      => $trace[1]['line'],\n        'is_empty'  => intval(empty($value)),\n        'locale'    => '' . $value,\n      );\n    }\n  }\n\n\n  protected function lng_try_filepath($path, $file_path_relative) {\n    $file_path = SN_ROOT_PHYSICAL . ($path && file_exists(SN_ROOT_PHYSICAL . $path . $file_path_relative) ? $path : '') . $file_path_relative;\n\n    return file_exists($file_path) ? $file_path : false;\n  }\n\n  protected function make_fallback($language = '') {\n    global $user;\n\n    $this->fallback = array();\n    $language ? $this->fallback[$language] = $language : false; // Desired language\n    $this->active ? $this->fallback[$this->active] = $this->active : false; // Active language\n    // TODO - account_language\n    !empty($user['lang']) ? $this->fallback[$user['lang']] = $user['lang'] : false; // Player language\n    $this->fallback[DEFAULT_LANG] = DEFAULT_LANG; // Server default language\n    $this->fallback['ru']         = 'ru'; // Russian\n    $this->fallback['en']         = 'en'; // English\n  }\n\n  public function lng_include($filename, $path = '', $ext = '.mo.php') {\n    global $language;\n\n    SN::log_file(\"locale.include: Loading data from domain '{$filename}'\", 1);\n\n    $cache_file_key = $this->cache_prefix_lang . '__' . $filename;\n\n    // Подключен ли внешний кэш?\n    if ($this->cache) {\n      // Загружен ли уже данный файл?\n      $cache_file_status = $this->cache->__get($cache_file_key);\n      SN::log_file(\"locale.include: Cache - '{$filename}' has key '{$cache_file_key}' and is \" . ($cache_file_status ? 'already loaded - EXIT' : 'EMPTY'), $cache_file_status ? -1 : 0);\n      if ($cache_file_status) {\n        // Если да - повторять загрузку нет смысла\n        return null;\n      }\n    }\n\n    // У нас нет внешнего кэша или в кэш не загружена данная локализация текущего файла\n\n    $ext          = $ext ? $ext : '.mo.php';\n    $filename_ext = \"{$filename}{$ext}\";\n\n    $this->make_fallback($language);\n\n    $file_path = '';\n    foreach ($this->fallback as $lang_try) {\n      if (!$lang_try /* || isset($language_tried[$lang_try]) */) {\n        continue;\n      }\n\n      if ($file_path = $this->lng_try_filepath($path, \"language/{$lang_try}/{$filename_ext}\")) {\n        break;\n      }\n\n      if ($file_path = $this->lng_try_filepath($path, \"language/{$filename}_{$lang_try}{$ext}\")) {\n        break;\n      }\n\n      $file_path = '';\n    }\n\n    if ($file_path) {\n      include($file_path);\n\n      if (!empty($a_lang_array)) {\n        $this->merge($a_lang_array);\n\n        // Загрузка данных из файла в кэш\n        if ($this->cache) {\n          SN::log_file(\"Locale: loading '{$filename}' into cache\");\n          foreach ($a_lang_array as $key => $value) {\n            $value_cache_key = $this->cache_prefix_lang . $key;\n            if ($this->cache->__isset($value_cache_key)) {\n              if (is_array($value)) {\n                $alt_value = $this->cache->__get($value_cache_key);\n                $value     = array_replace_recursive($alt_value, $value);\n              }\n            }\n            $this->cache->__set($this->cache_prefix_lang . $key, $value);\n          }\n        }\n      }\n\n      if ($this->cache) {\n        $this->cache->__set($cache_file_key, true);\n      }\n\n      unset($a_lang_array);\n    }\n\n    SN::log_file(\"locale.include: Complete - EXIT\", -1);\n\n    return null;\n  }\n\n  public function lng_load_i18n($i18n) {\n    if (!isset($i18n)) {\n      return;\n    }\n\n    foreach ($i18n as $i18n_data) {\n      if (is_string($i18n_data)) {\n        $this->lng_include($i18n_data);\n      } elseif (is_array($i18n_data)) {\n        $this->lng_include($i18n_data['file'], $i18n_data['path']);\n      }\n    }\n\n    return null;\n  }\n\n  public function lng_switch($language_new) {\n    global $language, $user, $sn_mvc;\n\n    SN::log_file(\"locale.switch: Request for switch to '{$language_new}'\", 1);\n\n    $language_new = str_replace(array('?', '&', 'lang='), '', $language_new);\n    $language_new = $language_new ? $language_new : (!empty($user['lang']) ? $user['lang'] : DEFAULT_LANG);\n\n    SN::log_file(\"locale.switch: Trying to switch language to '{$language_new}'\");\n\n//    if ($language_new == $this->active) {\n//      SN::log_file(\"locale.switch: New language '{$language_new}' is equal to current language '{$this->active}' - EXIT\", -1);\n//\n//      return false;\n//    }\n\n    $this->active            = $language = $language_new;\n    $this->cache_prefix_lang = $this->cache_prefix . $this->active . '_';\n\n    $this['LANG_INFO'] = $this->lng_get_info($this->active);\n    $this->make_fallback($this->active);\n\n    if ($this->cache) {\n      $cache_lang_init_status = $this->cache->__get($this->cache_prefix_lang . '__INIT');\n      SN::log_file(\"locale.switch: Cache for '{$this->active}' prefixed '{$this->cache_prefix_lang}' is \" . ($cache_lang_init_status ? 'already loaded. Doing nothing - EXIT' : 'EMPTY'), $cache_lang_init_status ? -1 : 0);\n      if ($cache_lang_init_status) {\n        return false;\n      }\n\n      // Чистим текущие локализации из кэша. Достаточно почистить только флаги инициализации языкового кэша и загрузки файлов - они начинаются с '__'\n      SN::log_file(\"locale.switch: Cache - invalidating data\");\n      $this->cache->unset_by_prefix($this->cache_prefix_lang . '__');\n    }\n\n    $this->lng_include('system');\n//    $this->lng_include('menu');\n    $this->lng_include('tech');\n    $this->lng_include('payment');\n    // Loading global language files\n    $this->lng_load_i18n($sn_mvc['i18n']['']);\n\n    if ($this->cache) {\n      SN::log_file(\"locale.switch: Cache - setting flag \" . $this->cache_prefix_lang . '__INIT');\n      $this->cache->__set($this->cache_prefix_lang . '__INIT', true);\n    }\n\n    SN::log_file(\"locale.switch: Complete - EXIT\");\n\n    return true;\n  }\n\n\n  public function lng_get_info($entry) {\n    $file_name = SN_ROOT_PHYSICAL . 'language/' . $entry . '/language.mo.php';\n    $lang_info = array();\n    if (file_exists($file_name)) {\n      include($file_name);\n    }\n\n    return ($lang_info);\n  }\n\n  public function lng_get_list() {\n    if (empty($this->lang_list)) {\n      $this->lang_list = array();\n\n      $path = SN_ROOT_PHYSICAL . 'language/';\n      $dir  = dir($path);\n      while (false !== ($entry = $dir->read())) {\n        if (is_dir($path . $entry) && $entry[0] != '.') {\n          $lang_info = $this->lng_get_info($entry);\n          if ($lang_info['LANG_NAME_ISO2'] == $entry) {\n            $this->lang_list[$lang_info['LANG_NAME_ISO2']] = $lang_info;\n          }\n        }\n      }\n      $dir->close();\n    }\n\n    return $this->lang_list;\n  }\n}\n"
  },
  {
    "path": "classes/classPersistent.php",
    "content": "<?php\n/**\n * Created by Gorlum 29.10.2016 10:16\n */\n\n/**\n *\n * Persistent is extension of class cacher and can save itself to DB\n * It's most usefull to hold basic structures as configuration, variables etc\n * Persistent pretty smart to handle one-level tables structures a-la \"variable_name\"+\"variable_value\"\n * Look supernova.sql to learn more\n * Also this class can holds default values for variables\n *\n * @package supernova\n *\n */\nclass classPersistent extends classCache {\n  protected $table_name;\n  protected $sql_index_field;\n  protected $sql_value_field;\n\n  protected $defaults = array();\n\n  /**\n   * List of fields which should have not empty values\n   *\n   * @var string[] $notEmptyFields [(str)fieldName => (str)fieldName, ...]\n   */\n  protected $notEmptyFields = [];\n\n  /**\n   * @var bool $force\n   */\n  protected $force = false;\n\n  public function __construct($gamePrefix = 'sn_', $table_name = 'table') {\n    parent::__construct(\"{$gamePrefix}{$table_name}_\");\n    $this->table_name = $table_name;\n\n    $this->sql_index_field = \"{$table_name}_name\";\n    $this->sql_value_field = \"{$table_name}_value\";\n\n    if(!$this->_DB_LOADED) {\n      $this->db_loadAll();\n    }\n  }\n\n  public static function getInstance($gamePrefix = 'sn_', $table_name = '') {\n    if (!isset(self::$cacheObject)) {\n      $className = get_class();\n      self::$cacheObject = new $className($gamePrefix, $table_name);\n    }\n    return self::$cacheObject;\n  }\n\n  /**\n   * @param string $index\n   *\n   * @return string|null\n   */\n  public function db_loadItem($index) {\n    $result = null;\n    if($index) {\n      $index_safe = SN::$db->db_escape($index);\n      $queryResult = doquery(\"SELECT `{$this->sql_value_field}` FROM `{{{$this->table_name}}}` WHERE `{$this->sql_index_field}` = '{$index_safe}' FOR UPDATE\", true);\n      if(is_array($queryResult) && !empty($queryResult)) {\n        $this->$index = $result = $queryResult[$this->sql_value_field];\n      }\n    }\n\n    return $result;\n  }\n\n  public function db_loadAll() {\n    $this->loadDefaults();\n\n    $query = doquery(\"SELECT * FROM {{{$this->table_name}}} FOR UPDATE;\");\n    while($row = db_fetch($query)) {\n      $this[$row[$this->sql_index_field]] = $row[$this->sql_value_field];\n    }\n\n    $this->_DB_LOADED = true;\n  }\n\n  public function loadDefaults() {\n    foreach($this->defaults as $defName => $defValue) {\n      $this->$defName = $defValue;\n    }\n  }\n\n  public function db_saveAll() {\n    $this->db_saveItem(array_combine(array_keys($this->defaults), array_fill(0, count($this->defaults), null)));\n  }\n\n  public function db_saveItem($item_list, $value = NULL) {\n    if(empty($item_list)) {\n      return;\n    }\n\n    !is_array($item_list) ? $item_list = array($item_list => $value) : false;\n\n    // Сначала записываем данные в базу - что бы поймать все блокировки\n    $qry = array();\n    foreach($item_list as $item_name => $item_value) {\n      if($item_name) {\n        $item_value = SN::$db->db_escape($item_value === NULL ? $this->$item_name : $item_value);\n        $item_name = SN::$db->db_escape($item_name);\n        $qry[] = \"('{$item_name}', '{$item_value}')\";\n      }\n    }\n    doquery(\"REPLACE INTO `{{\" . $this->table_name . \"}}` (`{$this->sql_index_field}`, `{$this->sql_value_field}`) VALUES \" . implode(',', $qry) . \";\");\n\n    // И только после взятия блокировок - меняем значения в кэше\n    foreach($item_list as $item_name => $item_value) {\n      if($item_name && $item_value !== null) {\n        $this->__set($item_name, $item_value);\n      }\n    }\n  }\n\n  /**\n   * Instructs cache to pass next operation to DB - whether it read or write\n   *\n   * This allows more transparency when accessing variables. So\n   *    $this->db_loadItem('variable_name')\n   * converts to\n   *    $this->pass()->variable_name\n   * Latest makes IDE aware of operation with variables and makes navigation and code refactoring (i.e. variable renaming) much easier\n   * Same work with saving items directly to DB:\n   *    $this->db_saveItem('variable_name', $value)\n   * becomes\n   *    $this->pass()->variable_name = $value;\n   *\n   * @return $this\n   */\n  public function pass() {\n    $this->force = true;\n\n    return $this;\n  }\n\n  public function __get($name) {\n    if($this->force) {\n      $this->force = false;\n      $value = $this->db_loadItem($name);\n    } else {\n      $value = parent::__get($name);\n    }\n\n    if(isset($this->notEmptyFields[$name]) && empty($value) && isset($this->defaults[$name])) {\n      $value = $this->defaults[$name];\n    }\n\n    return $value;\n  }\n\n  public function __set($name, $value) {\n    if($this->force) {\n      $this->force = false;\n      $this->db_saveItem($name, $value);\n    }\n\n    parent::__set($name, $value);\n  }\n\n  public function __unset($name) {\n    doquery('DELETE FROM `{{config}}` WHERE `config_name` = \"' . SN::$db->db_escape($name) . '\"');\n\n    parent::__unset($name);\n  }\n\n}\n"
  },
  {
    "path": "classes/core_auth.php",
    "content": "<?php\n/** @noinspection PhpUnnecessaryCurlyVarSyntaxInspection */\n/** @noinspection PhpDeprecationInspection */\n\n// define(\"DEBUG_AUTH\", true);\nuse DBAL\\db_mysql;\nuse Modules\\sn_module;\n\n/**\n * Статический над-класс, который обеспечивает интерфейс авторизации для остального кода\n *\n * User: Gorlum\n * Date: 21.04.2015\n * Time: 3:51\n *\n * version #46d0#\n */\nclass core_auth extends sn_module {\n  public $versionCommitted = '#46d0#';\n\n  public $manifest = [\n    'package' => 'core',\n    'name' => 'auth',\n    'version' => '0a0',\n    'copyright' => 'Project \"SuperNova.WS\" #46d0# copyright © 2009-2025 Gorlum',\n\n    self::M_LOAD_ORDER => MODULE_LOAD_ORDER_CORE_AUTH,\n\n    'mvc' => [\n      'pages' => [\n        'player_register' => 'classes/core_auth'\n      ],\n\n      'model' => [\n        'player_register' => [\n          /** @see core_auth::player_register_model() */\n          'callable' => 'player_register_model',\n        ],\n      ],\n\n      'view' => [\n        'player_register' => [\n          /** @see core_auth::player_register_view() */\n          'callable' => 'player_register_view',\n        ],\n      ],\n    ],\n  ];\n\n  /**\n   * БД из которой читать данные\n   *\n   * @var db_mysql $db\n   */\n  static $db;\n  /**\n   * Информация об устройстве\n   *\n   * @var RequestInfo\n   */\n  static $device;\n  /**\n   * Аккаунт ????\n   *\n   * @var Account\n   */\n  public $account = null;\n  /**\n   * Запись текущего игрока из `users`\n   *\n   * @var null\n   */\n  static $user = null;\n\n  /**\n   * Основной провайдер\n   *\n   * @var auth_local\n   */\n  public static $main_provider = null;\n\n  /**\n   * Статус инициализации\n   *\n   * @var bool\n   */\n  // protected static $is_init = false;\n  /**\n   * Список провайдеров\n   *\n   * @var auth_local[]\n   */\n  protected $providers = array();\n\n  /**\n   * Глобальный статус входа в игру\n   *\n   * @var int\n   */\n  static $login_status = LOGIN_UNDEFINED;\n  static $login_message = '';\n\n  /**\n   * Имя, предлагаемое пользователю в качестве имени игрока\n   *\n   * @var string\n   */\n  protected $player_suggested_name = '';\n\n  /**\n   * Список полностью авторизированных аккаунтов с LOGIN_SUCCESS\n   *\n   * @var auth_local[]\n   */\n  protected $providers_authorised = array();\n  /** @var auth_local[] $provider_error_list Статусы всех провайдеров */\n  protected $provider_error_list = array();\n//  /** @var string[] */\n//  protected $provider_error_messages = array();\n  /** @var array $accessible_user_row_list Список юзеров (user_row - записей из `user`), доступных всем авторизированным аккаунтам */\n  protected $accessible_user_row_list = array();\n\n  protected $user_id_to_provider = array();\n\n  /** @var bool $is_impersonating Флаг имперсонации */\n  protected $is_impersonating = false;\n  protected $impersonator_username = '';\n\n  /** @var bool $is_player_register Флаг регистрации пользователя */\n  protected $is_player_register = false;\n  /** @var int $register_status */\n  protected $register_status = LOGIN_UNDEFINED;\n\n  /**\n   * Максимальный локальный уровень авторизации игрока\n   *\n   * @var int\n   */\n  public $auth_level_max_local = AUTH_LEVEL_ANONYMOUS;\n\n  /**\n   * @var int\n   */\n  public $partner_id = 0;\n\n  /**\n   * @var string\n   */\n  protected $server_name = '';\n\n  /**\n   * @param string $filename\n   *\n   * @throws Exception\n   */\n  // TODO - OK 4.7\n  public function __construct($filename = __FILE__) {\n    parent::__construct($filename);\n\n    // В этой точке все модули уже прогружены и инициализированы по 1 экземпляру\n    self::$db = SN::$db;\n\n    self::$device             = new RequestInfo();\n    $this->is_player_register = (bool)sys_get_param('player_register');\n    $this->partner_id         = sys_get_param_int('id_ref', sys_get_param_int('partner_id'));\n    $this->server_name        = sys_get_param_str_unsafe('server_name', SN_ROOT_VIRTUAL);\n\n    self::$main_provider = new auth_local();\n    SN::$gc->modules->registerModule(core_auth::$main_provider->manifest['name'], core_auth::$main_provider);\n  }\n\n  // TODO - OK v4.7\n  public function player_register_model() {\n    // TODO ВСЕГДА ПРЕДЛАГАТЬ РЕГАТЬ ИГРОКА ИЛИ ПОДКЛЮЧИТЬ ИМЕЮЩЕГОСЯ!\n\n    // TODO в auth_local делать проверку БД на существование имени игрока в локальной БД - что бы избежать лишнего шага (см.выше)\n    // TODO Хотя тут может получиться вечный цикл - ПОДУМАТЬ\n    // TODO Тут же можно пробовать провести попытку слияния аккаунтов - хотя это и очень небезопасно\n\n    if (sys_get_param('login_player_register_logout')) {\n      $this->logout();\n    }\n\n    $original_suggest = '';\n    // Смотрим - есть ли у нас данные от пользователя\n    if (($player_name_submitted = sys_get_param('submit_player_name'))) {\n      // Попытка регистрации нового игрока из данных, введенных пользователем\n      $this->player_suggested_name = sys_get_param_str_unsafe('player_suggested_name');\n    } else {\n      foreach ($this->providers_authorised as $provider) {\n        if ($this->player_suggested_name = $provider->player_name_suggest()) { // OK 4.5\n          $original_suggest = $provider->player_name_suggest();\n          break;\n        }\n      }\n    }\n\n    // Если у нас провайдеры не дают имени и пользователь не дал свой вариант - это у нас первый логин в игру\n    if (!$this->player_suggested_name) {\n      $max_user_id = db_player_get_max_id(); // 4.5\n      // TODO - предлагать имя игрока по локали\n\n      // Проверить наличие такого имени в истории имён\n      do {\n        db_mysql::db_transaction_rollback();\n        $this->player_suggested_name = 'Emperor ' . mt_rand($max_user_id + 1, $max_user_id + 1000);\n        db_mysql::db_transaction_start();\n      } while (db_player_name_exists($this->player_suggested_name));\n\n    }\n\n    if ($player_name_submitted) {\n      $this->register_player_db_create($this->player_suggested_name); // OK 4.5\n      if ($this->register_status == LOGIN_SUCCESS) {\n        sys_redirect(SN_ROOT_VIRTUAL . 'overview.php');\n      } /** @noinspection PhpStatementHasEmptyBodyInspection */\n      elseif ($this->register_status == REGISTER_ERROR_PLAYER_NAME_EXISTS && $original_suggest == $this->player_suggested_name) {\n        // self::$player_suggested_name .= ' ' . $this->account->account_id;\n      }\n//      if(self::$login_status != LOGIN_SUCCESS) {\n//        // TODO Ошибка при регистрации нового игрока под текущим именем\n//      }\n    }\n\n  }\n\n  // TODO - OK v4.7\n  public function player_register_view($template = null) {\n    global $template_result, $lang;\n\n    define('LOGIN_LOGOUT', true);\n\n    $template_result[F_PLAYER_REGISTER_MESSAGE] =\n      isset($template_result[F_PLAYER_REGISTER_MESSAGE]) && $template_result[F_PLAYER_REGISTER_MESSAGE]\n        ? $template_result[F_PLAYER_REGISTER_MESSAGE]\n        : ($this->register_status != LOGIN_UNDEFINED\n        ? $lang['sys_login_messages'][$this->register_status]\n        : false\n      );\n\n    if ($this->register_status == LOGIN_ERROR_USERNAME_RESTRICTED_CHARACTERS) {\n      $prohibited_characters                      = array_map(function ($value) {\n        return \"'\" . htmlentities($value, ENT_QUOTES, 'UTF-8') . \"'\";\n      }, str_split(LOGIN_REGISTER_CHARACTERS_PROHIBITED));\n      $template_result[F_PLAYER_REGISTER_MESSAGE] .= implode(', ', $prohibited_characters);\n    }\n\n    $template_result = array_merge($template_result, array(\n      'NAVBAR'                  => false,\n      'PLAYER_SUGGESTED_NAME'   => sys_safe_output($this->player_suggested_name),\n      'PARTNER_ID'              => sys_safe_output($this->partner_id),\n      'SERVER_NAME'             => sys_safe_output($this->server_name),\n      'PLAYER_REGISTER_STATUS'  => $this->register_status,\n      'PLAYER_REGISTER_MESSAGE' => $template_result[F_PLAYER_REGISTER_MESSAGE],\n      'LOGIN_UNDEFINED'         => LOGIN_UNDEFINED,\n    ));\n\n    return SnTemplate::gettemplate('login_player_register', $template);\n  }\n\n  /**\n   * Функция пытается залогиниться по всем известным провайдерам\n   *\n   * @return array|void\n   */\n  // TODO - OK v4.5\n  public function login() {\n    global $lang;\n\n    // !self::$is_init ? self::init() : false;\n\n    if (!SN::$gc->modules->countModulesInGroup('auth')) {\n      die('{Не обнаружено ни одного провайдера авторизации в core_auth::login()!}');\n    }\n\n    if (!empty($_POST)) {\n      self::flog(dump($_POST, '$_POST'));\n    }\n    if (!empty($_GET)) {\n      self::flog(dump($_GET, '$_GET'));\n    }\n    if (!empty($_COOKIE)) {\n      self::flog(dump($_COOKIE, '$_COOKIE'));\n    }\n\n    $this->auth_reset(); // OK v4.5\n\n    $this->providers = array();\n    foreach (SN::$gc->modules->getModulesInGroup('auth', true) as $module) {\n      /** @var auth_abstract $module */\n      $this->providers[$module->provider_id] = $module;\n    }\n\n    // pdump($this->providers);\n    foreach ($this->providers as $provider_id => $provider) {\n      $login_status = $provider->login(); // OK v4.5\n      self::flog(($provider->manifest['name'] . '->' . 'login_try - ') . (empty($provider->account->account_id) ? $lang['sys_login_messages'][$provider->account_login_status] : dump($provider)));\n      /** @noinspection PhpConditionCheckedByNextConditionInspection */\n      if ($login_status == LOGIN_SUCCESS && is_object($provider->account) && $provider->account instanceof Account && $provider->account->account_id) {\n        $this->providers_authorised[$provider_id] = &$this->providers[$provider_id];\n\n        $this->user_id_to_provider = array_replace_recursive(\n          $this->user_id_to_provider,\n          // static::db_translate_get_users_from_account_list($provider_id, $provider->account->account_id) // OK 4.5\n          PlayerToAccountTranslate::db_translate_get_users_from_account_list($provider_id, $provider->account->account_id) // OK 4.5\n        );\n      } elseif ($login_status != LOGIN_UNDEFINED) {\n        $this->provider_error_list[$provider_id] = $login_status;\n      }\n    }\n\n    if (empty($this->providers_authorised)) {\n      // Ни один аккаунт не авторизирован\n      // Проверяем - есть ли у нас ошибки в аккаунтах?\n      if (!empty($this->provider_error_list)) {\n        // Если есть - выводим их\n        self::$login_status = reset($this->provider_error_list);\n        $providerError      = $this->providers[key($this->provider_error_list)]->account_login_message;\n\n        if (!empty($providerError)) {\n          self::$login_message = $providerError;\n        }\n      }\n      // Иначе - это первый запуск страницы. ИЛИ СПЕЦИАЛЬНОЕ ДЕЙСТВИЕ!\n      // ...которые по факты должны обрабатываться в рамках provider->login()\n    } else {\n      // Есть хотя бы один авторизированный аккаунт\n      $temp          = reset($this->providers_authorised);\n      $this->account = $temp->account;\n\n      $this->get_accessible_user_list();\n      // В self::$accessible_user_row_list - список доступных игроков для данных аккаунтов с соответствующими записями из таблицы `users`\n\n      // Остались ли у нас в списке доступные игроки?\n      if (empty($this->accessible_user_row_list)) {\n        // Нет ни одного игрока ни на одном авторизированном аккаунте\n        // Надо регистрировать нового игрока\n\n        // Сейчас происходит процесс регистрации игрока?\n        if (!$this->is_player_register) {\n          // Нет - отправляем на процесс регистрации\n          $partner_id = sys_get_param_int('id_ref', sys_get_param_int('partner_id'));\n          sys_redirect(SN_ROOT_VIRTUAL . 'index.php?page=player_register&player_register=1' . ($partner_id ? '&id_ref=' . $partner_id : ''));\n        }\n      } else {\n        // Да, есть доступные игроки, которые так же прописаны в базе\n        $this->get_active_user(); // 4.5\n\n        if ($this->is_impersonating = !empty($_COOKIE[SN_COOKIE_U_I]) ? $_COOKIE[SN_COOKIE_U_I] : 0) {\n          $a_user                      = db_user_by_id($this->is_impersonating);\n          $this->impersonator_username = $a_user['username'];\n        }\n\n\n        //Прописываем текущего игрока на все авторизированные аккаунты\n        // TODO - ИЛИ ВСЕХ ИГРОКОВ??\n        if (empty($this->is_impersonating)) {\n          foreach ($this->providers_authorised as $provider_id => $provider) {\n            if (empty($this->user_id_to_provider[self::$user['id']][$provider_id])) {\n              // self::db_translate_register_user($provider_id, $provider->account->account_id, self::$user['id']);\n              PlayerToAccountTranslate::db_translate_register_user($provider_id, $provider->account->account_id, self::$user['id']);\n              $this->user_id_to_provider[self::$user['id']][$provider_id][$provider->account->account_id] = true;\n            }\n          }\n        }\n      }\n    }\n\n    if (empty(self::$user['id'])) {\n      self::cookie_set(''); // OK 4.5\n    } elseif (self::$user['id'] != $_COOKIE[SN_COOKIE_U]) {\n      self::cookie_set(self::$user['id']); // OK 4.5\n    }\n\n    return $this->make_return_array();\n  }\n\n  /**\n   * Логаут игрока и всех аккаунтов\n   *\n   * @param bool|string $redirect          Нужно ли сделать перенаправление после логаута\n   *                                       <p><b>false</b> - не перенаправлять</p>\n   *                                       <p><i><b>true</b></i> - перенаправить на главную страницу</p>\n   *                                       <p><b>string</b> - перенаправить на указанный URL</p>\n   */\n  // OK v4.7\n  public function logout($redirect = true) {\n    if (!empty($_COOKIE[SN_COOKIE_U_I])) {\n      self::cookie_set($_COOKIE[SN_COOKIE_U_I]);\n      self::cookie_set(0, true);\n      self::$main_provider->logout();\n    } else {\n      foreach ($this->providers as $provider) {\n        $provider->logout();\n      }\n\n      self::cookie_set(0);\n    }\n\n    if ($redirect === true) {\n      sys_redirect(SN_ROOT_RELATIVE . (empty($_COOKIE[SN_COOKIE_U]) ? 'login.php' : 'admin/overview.php'));\n    } elseif ($redirect !== false) {\n      sys_redirect($redirect);\n    }\n  }\n\n  /**\n   * Имперсонация\n   *\n   * @param $user_selected\n   */\n  public function impersonate($user_selected) {\n    if ($_COOKIE[SN_COOKIE_U_I]) {\n      die('You already impersonating someone. Go back to living other\\'s life! Or clear your cookies and try again'); // TODO: Log it\n    }\n\n    if ($this->auth_level_max_local < AUTH_LEVEL_ADMINISTRATOR) {\n      die('You can\\'t impersonate - too low level'); // TODO: Log it\n    }\n\n    if ($this->auth_level_max_local <= $user_selected['authlevel']) {\n      die('You can\\'t impersonate this account - level is greater or equal to yours'); // TODO: Log it\n    }\n\n    $account_translate      = PlayerToAccountTranslate::db_translate_get_account_by_user_id($user_selected['id'], self::$main_provider->provider_id);\n    $account_translate      = reset($account_translate[$user_selected['id']][self::$main_provider->provider_id]);\n    $account_to_impersonate = new Account(self::$main_provider->db);\n    $account_to_impersonate->db_get_by_id($account_translate['provider_account_id']);\n    if (!$account_to_impersonate->is_exists) {\n      die('Какая-то ошибка - не могу найти аккаунт для имперсонации'); // TODO: Log it\n    }\n    self::$main_provider->impersonate($account_to_impersonate);\n\n    self::cookie_set($_COOKIE[SN_COOKIE_U], true, 0);\n\n    // TODO - Имперсонация - только на одну сессию\n    self::cookie_set($user_selected['id']);\n\n    // sec_set_cookie_by_user($user_selected, 0);\n    sys_redirect(SN_ROOT_RELATIVE);\n  }\n\n  /**\n   * Проверяет пароль на совпадение с текущими паролями\n   *\n   * @param $password_unsafe\n   *\n   * @return bool\n   */\n  // OK v4.6\n  // TODO - ПЕРЕДЕЛАТЬ!\n  public function password_check($password_unsafe) {\n    $result = false;\n\n    if (empty($this->providers_authorised)) {\n      // TODO - такого быть не может!\n      self::flog(\"password_check: Не найдено ни одного авторизированного провайдера в self::\\$providers_authorised\", true);\n    } else {\n      foreach ($this->providers_authorised as $provider) {\n        if ($provider->is_feature_supported(AUTH_FEATURE_HAS_PASSWORD)) {\n          $result = $result || $provider->password_check($password_unsafe);\n        }\n      }\n    }\n\n    return $result;\n  }\n\n  /**\n   * Меняет старый пароль на новый\n   *\n   * @param $old_password_unsafe\n   * @param $new_password_unsafe\n   *\n   * @return bool\n   * @throws Exception\n   */\n  // OK v4.6\n  public function password_change($old_password_unsafe, $new_password_unsafe) {\n    global $lang;\n\n    if (empty($this->providers_authorised)) {\n      // TODO - такого быть не может!\n      self::flog(\"Не найдено ни одного авторизированного провайдера в self::\\$providers_authorised\", true);\n\n      return false;\n    }\n\n    // TODO - Проверять пароль на корректность\n\n    // TODO - Не менять (?) пароль у аккаунтов, к которым пристёгнут хоть один игрок с AUTH_LEVEL > 0\n\n    $salt_unsafe = self::password_salt_generate();\n\n    $providers_changed_password = array();\n    foreach ($this->providers_authorised as $provider_id => $provider) {\n      if (\n        !$provider->is_feature_supported(AUTH_FEATURE_PASSWORD_CHANGE)\n        || !$provider->password_change($old_password_unsafe, $new_password_unsafe, $salt_unsafe)\n      ) {\n        continue;\n      }\n\n      // Узнаем список игроков, которые прикреплены к этому аккаунту\n      // $account_translation = self::db_translate_get_users_from_account_list($provider_id, $provider->account->account_id);\n      $account_translation = PlayerToAccountTranslate::db_translate_get_users_from_account_list($provider_id, $provider->account->account_id);\n\n      // Рассылаем уведомления о смене пароля в ЛС\n      foreach ($account_translation as $user_id => $provider_info) {\n        // TODO - Указывать тип аккаунта, на котором сменён пароль\n        msg_send_simple_message($user_id, 0, SN_TIME_NOW, MSG_TYPE_ADMIN,\n          $lang['sys_administration'], $lang['sys_login_register_message_title'],\n          sprintf($lang['sys_login_register_message_body'], $provider->account->account_name, $new_password_unsafe), false //true\n        );\n      }\n      $providers_changed_password[$provider_id] = $provider;\n    }\n\n    // TODO - отсылать уведомление на емейл\n\n    return !empty($providers_changed_password);\n  }\n\n\n\n\n  /**\n   * Сбрасывает значения полей\n   */\n  // OK v4.5\n  protected function auth_reset() {\n    self::$login_status             = LOGIN_UNDEFINED;\n    $this->player_suggested_name    = '';\n    $this->account                  = null;\n    self::$user                     = null;\n    $this->providers_authorised     = array(); // Все аккаунты, которые успешно залогинились\n    $this->provider_error_list      = array(); // Статусы всех аккаунтов\n    $this->accessible_user_row_list = array();\n    $this->user_id_to_provider      = array();\n  }\n\n  /**\n   * Функция пытается создать игрока в БД, делая все нужные проверки\n   *\n   * @param $player_name_unsafe\n   */\n  // OK v4\n  protected function register_player_db_create($player_name_unsafe) {\n    try {\n      // Проверить корректность имени\n      $this->register_player_name_validate($player_name_unsafe);\n\n      db_mysql::db_transaction_start();\n      // Проверить наличие такого имени в истории имён\n\n      if (db_player_name_exists($player_name_unsafe)) {\n        throw new Exception(REGISTER_ERROR_PLAYER_NAME_EXISTS, ERR_ERROR);\n      }\n\n      // Узнаем язык и емейл игрока\n      $player_language = '';\n      $player_email    = '';\n      // TODO - порнография - работа должна происходить над списком аккаунтов, а не только на одном аккаунте...\n      foreach ($this->providers_authorised as $provider) {\n        if (!$player_language && $provider->account->account_language) {\n          $player_language = $provider->account->account_language;\n        }\n        if (!$player_email && $provider->account->account_email) {\n          $player_email = $provider->account->account_email;\n        }\n      }\n      $player_language = sys_get_param_str('lang') ? sys_get_param_str('lang') : $player_language;\n      $player_language = $player_language ?: DEFAULT_LANG;\n\n      // TODO - дописать exceptions в процедуре создания игрока\n      self::$user = player_create($player_name_unsafe, $player_email, array(\n        'partner_id'   => sys_get_param_int('id_ref', sys_get_param_int('partner_id')),\n        'language_iso' => static::$db->db_escape($player_language),\n        // 'password_encoded_unsafe' => $this->data[F_ACCOUNT]['account_password'],\n        // 'salt' => $this->data[F_ACCOUNT]['account_salt'],\n      ));\n      // Зарегистрировать на него аккаунты из self::$accounts_authorised\n      $a_user = self::$user;\n      foreach ($this->providers_authorised as $provider) {\n        // TODO - порнография. Должен быть отдельный класс трансляторов - в т.ч. и кэширующий транслятор\n        // TODO - ну и работа должна происходить над списком аккаунтов, а не только на одном аккаунте...\n        // self::db_translate_register_user($provider->provider_id, $provider->account->account_id, $a_user['id']);\n        PlayerToAccountTranslate::db_translate_register_user($provider->provider_id, $provider->account->account_id, $a_user['id']);\n\n      }\n      // Установить куку игрока\n      self::cookie_set(self::$user['id']);\n\n      db_mysql::db_transaction_commit();\n      $this->register_status = LOGIN_SUCCESS;\n    } catch (Exception $e) {\n      db_mysql::db_transaction_rollback();\n\n      // Если старое имя занято\n      self::$user = null;\n      if ($this->register_status == LOGIN_UNDEFINED) {\n        $this->register_status = $e->getMessage();\n      }\n    }\n  }\n\n\n  /**\n   * Проверяет доступ авторизированных аккаунтов к заявленным в трансляции юзерам\n   * @version 4.5\n   */\n  // OK v4.5\n  protected function get_accessible_user_list() {\n    // Пробиваем все ИД игроков по базе - есть ли вообще такие записи\n    // Вообще-то это не особо нужно - у нас по определению стоят ограничения\n    // Зато так мы узнаем максимальный authlevel, проверим права имперсонейта и вытащим все записи юзеров\n    foreach ($this->user_id_to_provider as $user_id => $cork) {\n      $user = db_user_by_id($user_id);\n      // Если записи игрока в БД не существует?\n      if (empty($user['id'])) {\n        // Удаляем этого и переходим к следующему\n        unset($this->user_id_to_provider[$user_id]);\n        // Де-регистрируем игрока из таблицы трансляции игроков\n        PlayerToAccountTranslate::db_translate_unregister_user($user_id);\n      } else {\n        $this->accessible_user_row_list[$user['id']] = $user;\n        $this->auth_level_max_local                  = max($this->auth_level_max_local, $user['authlevel']);\n      }\n      unset($user);\n    }\n  }\n\n  /**\n   * Выбирает активного игрока из куки или из списка доступных игроков\n   *\n   * @version 4.5\n   */\n  // OK v4.5\n  protected function get_active_user() {\n    // Проверяем куку \"текущего игрока\" из браузера\n    if (\n      // Кука не пустая\n      ($_COOKIE[SN_COOKIE_U] = trim($_COOKIE[SN_COOKIE_U])) && !empty($_COOKIE[SN_COOKIE_U])\n      // И в куке находится ID\n      && is_id($_COOKIE[SN_COOKIE_U])\n      // И у аккаунтов есть права на данного игрока\n      && (\n        // Есть прямые права из `account_translate`\n        !empty($this->accessible_user_row_list[$_COOKIE[SN_COOKIE_U]])\n        // Или есть доступ через имперсонейт\n        || (\n          // Максимальные права всех доступных записей игроков - не ниже администраторских\n          $this->auth_level_max_local >= AUTH_LEVEL_ADMINISTRATOR\n          // И права игрока, в которого пытаются зайти - меньше текущих максимальных прав\n          && $this->accessible_user_row_list[$_COOKIE[SN_COOKIE_U]]['authlevel'] < $this->auth_level_max_local\n        )\n      )\n    ) {\n      // Берем текущим юзером юзера с ИД из куки\n      self::$user = db_user_by_id($_COOKIE[SN_COOKIE_U]);\n    }\n\n    // В куке нет валидного ИД записи игрока, доступной с текущих аккаунтов\n    if (empty(self::$user['id'])) {\n      // Берем первого из доступных\n      // TODO - default_user\n      self::$user = reset($this->accessible_user_row_list);\n    }\n  }\n\n\n\n  /**\n   * Генерирует набор данных для возврата в основной код\n   * @noinspection SpellCheckingInspection\n   */\n  // OK v4.5\n  protected function make_return_array() {\n    global $config;\n\n    $user_id = !empty(self::$user['id']) ? self::$user['id'] : 0;\n    // if(!empty($user_id) && !$user_impersonator) {\n    // $user_id не может быть пустым из-за ключей в таблице SPE\n    // self::db_security_entry_insert();\n    self::$device->db_security_entry_insert($user_id);\n\n    $result = array();\n\n    if ($user_id && empty($this->is_impersonating)) {\n      // self::db_counter_insert();\n      self::$device->db_counter_insert($user_id);\n\n      $user = &self::$user;\n\n      sys_user_options_unpack($user);\n\n      if ($user['banaday'] && $user['banaday'] <= SN_TIME_NOW) {\n        $user['banaday']  = 0;\n        $user['vacation'] = SN_TIME_NOW;\n      }\n\n      /** @noinspection SpellCheckingInspection */\n      $user['user_lastip'] = self::$device->ip_v4_string;// $ip['ip'];\n      $user['user_proxy']  = self::$device->ip_v4_proxy_chain; //$ip['proxy_chain'];\n\n      $result[F_BANNED_STATUS]   = $user['banaday'];\n      $result[F_VACATION_STATUS] = $user['vacation'];\n\n      $proxy_safe = static::$db->db_escape(self::$device->ip_v4_proxy_chain);\n\n      doquery(\"LOCK TABLES {{users}} WRITE;\");\n      /** @noinspection SpellCheckingInspection */\n      db_user_set_by_id($user['id'], \"`onlinetime` = \" . SN_TIME_NOW . \",\n      `banaday` = \" . static::$db->db_escape($user['banaday']) . \", `vacation` = \" . static::$db->db_escape($user['vacation']) . \",\n      `user_lastip` = '\" . static::$db->db_escape($user['user_lastip']) . \"', `user_last_proxy` = '{$proxy_safe}', `user_last_browser_id` = \" . self::$device->browser_id\n      );\n      doquery(\"UNLOCK TABLES;\");\n    }\n\n    if ($extra = $config->security_ban_extra) {\n      $extra = explode(',', $extra);\n      array_walk($extra, 'trim');\n      in_array(self::$device->device_id, $extra) and die();\n    }\n\n    if (self::$login_message) {\n      $result[F_LOGIN_MESSAGE] = self::$login_message;\n    }\n\n    $result[F_LOGIN_STATUS]           = self::$login_status = empty($this->providers_authorised) ? self::$login_status : LOGIN_SUCCESS;\n    $result[F_PLAYER_REGISTER_STATUS] = $this->register_status;\n    $result[F_USER]                   = self::$user;\n\n    // $result[AUTH_LEVEL] = isset(self::$user['authlevel']) ? self::$user['authlevel'] : AUTH_LEVEL_ANONYMOUS;\n    $result[AUTH_LEVEL] = $this->auth_level_max_local;\n\n    $result[F_IMPERSONATE_STATUS]   = $this->is_impersonating;\n    $result[F_IMPERSONATE_OPERATOR] = $this->impersonator_username;\n    // TODO\n//    self::$hidden[F_IMPERSONATE_OPERATOR] = $found_provider->data[F_IMPERSONATE_OPERATOR];\n\n    //TODO Сол и Парол тоже вкинуть в хидден\n    $result[F_ACCOUNTS_AUTHORISED] = $this->providers_authorised;\n\n    return $result;\n  }\n\n\n  // ХЕЛПЕРЫ ===========================================================================================================\n  /**\n   * Функция проверяет корректность имени игрока при регистрации\n   *\n   * @param $player_name_unsafe\n   *\n   * @throws Exception\n   */\n  // OK v4\n  // TODO - вынести в отдельный хелпер\n  protected function register_player_name_validate($player_name_unsafe) {\n    // TODO - переделать под RAW-строки\n    // Если имя игрока пустое - NO GO!\n    if (trim($player_name_unsafe) == '') {\n      throw new Exception(REGISTER_ERROR_PLAYER_NAME_EMPTY, ERR_ERROR);\n    }\n    // Проверяем, что бы в начале и конце не было пустых символов\n    if ($player_name_unsafe != trim($player_name_unsafe)) {\n      throw new Exception(REGISTER_ERROR_PLAYER_NAME_TRIMMED, ERR_ERROR);\n    }\n    // Если логин имеет запрещенные символы - NO GO!\n    if (strpbrk($player_name_unsafe, LOGIN_REGISTER_CHARACTERS_PROHIBITED)) {\n      // TODO - выдавать в сообщение об ошибке список запрещенных символов\n      // TODO - заранее извещать игрока, какие символы являются запрещенными\n      throw new Exception(REGISTER_ERROR_PLAYER_NAME_RESTRICTED_CHARACTERS, ERR_ERROR);\n    }\n    // Если логин меньше минимальной длины - NO GO!\n    if (strlen($player_name_unsafe) < LOGIN_LENGTH_MIN) {\n      // TODO - выдавать в сообщение об ошибке минимальную длину имени игрока\n      // TODO - заранее извещать игрока, какая минимальная и максимальная длина имени\n      throw new Exception(REGISTER_ERROR_PLAYER_NAME_SHORT, ERR_ERROR);\n    }\n\n    // TODO проверка на максимальную длину имени игрока\n  }\n\n//  /**\n//   * Генерирует случайный код для сброса пароля\n//   *\n//   * @return string\n//   */\n//  // OK v4\n//  public static function make_password_reset_code() {\n//    return sys_random_string(LOGIN_PASSWORD_RESET_CONFIRMATION_LENGTH, SN_SYS_SEC_CHARS_CONFIRMATION);\n//  }\n  /**\n   * Генерирует случайный пароль\n   *\n   * @return string\n   */\n  // OK v4\n  public static function make_random_password() {\n    return sys_random_string(LOGIN_PASSWORD_RESET_CONFIRMATION_LENGTH, SN_SYS_SEC_CHARS_CONFIRMATION);\n  }\n  /**\n   * Просаливает пароль\n   *\n   * @param $password\n   * @param $salt\n   *\n   * @return string\n   */\n  // OK v4\n  public static function password_encode($password, $salt) {\n    return md5($password . $salt);\n  }\n  /**\n   * Генерирует соль\n   *\n   * @return string\n   */\n  // OK v4\n  public static function password_salt_generate() {\n    // НЕ ПЕРЕКРЫВАТЬ\n    // TODO ВКЛЮЧИТЬ ГЕНЕРАЦИЮ СОЛИ !!!\n    return ''; // sys_random_string(16);\n  }\n\n  // OK v4.5\n  // TODO - REMEMBER_ME\n  protected static function cookie_set($value, $impersonate = false, $period = null) {\n    sn_setcookie($impersonate ? SN_COOKIE_U_I : SN_COOKIE_U, $value, $period === null ? SN_TIME_NOW + PERIOD_YEAR : $period);\n  }\n\n  protected static function flog($message, $die = false) {\n    if (!defined('DEBUG_AUTH') || !DEBUG_AUTH) {\n      return;\n    }\n    list($called, $caller) = debug_backtrace(false);\n    $caller_name =\n      (!empty($caller['class']) ? $caller['class'] : '') .\n      (!empty($caller['type']) ? $caller['type'] : '') .\n      (!empty($caller['function']) ? $caller['function'] : '') .\n      (!empty($called['line']) ? ':' . $called['line'] : '');\n\n    if ($_SERVER['SERVER_NAME'] == 'localhost') {\n      print(\"<div class='debug'>$message - $caller_name\\r\\n</div>\");\n    }\n\n    SN::log_file(\"$message - $caller_name\");\n    if ($die) {\n      $die && die(\"<div class='negative'>СТОП! Функция {$caller_name} при вызове в \" . get_called_class() . \" (располагается в \" . get_class() . \"). СООБЩИТЕ АДМИНИСТРАЦИИ!</div>\");\n    }\n  }\n\n}\n"
  },
  {
    "path": "classes/debug.php",
    "content": "<?php /** @noinspection PhpUnnecessaryCurlyVarSyntaxInspection */\n\n/*\n * debug.php ::  Clase Debug, maneja reporte de eventos\n *\n * V4.0 copyright 2010-2011 by Gorlum for http://supernova.ws\n *  [!] Merged `errors` to `logs`\n *  [+] Now debugger can work with database detached. All messages would be dumped to page\n *  [+] Now `logs` has both human-readable and machine-readable fields\n *\n * V3.0 copyright 2010 by Gorlum for http://supernova.ws\n *  [+] Full rewrtie & optimize\n *  [*] Now there is fallback procedure if no link to db detected\n *\n * V2.0 copyright 2010 by Gorlum for http://supernova.ws\n *  [*] Now error also contains backtrace - to see exact way problem comes\n *  [*] New method 'warning' sends message to dedicated SQL-table for non-errors\n *\n * V1.0 Created by Perberos. All rights reversed (C) 2006\n *\n *  Experiment code!!!\n *\n * vamos a experimentar >:)\n * le veo futuro a las classes, ayudaria mucho a tener un codigo mas ordenado...\n * que esperabas!!! soy newbie!!! D':<\n*/\n\nuse DBAL\\db_mysql;\n\nif (!defined('INSIDE')) {\n  die(\"attemp hacking\");\n}\n\nclass debug {\n  protected $log;\n  protected $numqueries;\n  protected $log_array;\n\n  public function log_file($message, $ident_change = 0) {\n    if (!defined('SN_DEBUG_LOG') || !SN_DEBUG_LOG) {\n      return;\n    }\n\n    static $ident = 0;\n    static $logFileName;\n\n    if (!$logFileName) {\n      $logFileName = SN_ROOT_PHYSICAL . '/.logs/supernova.log';\n      file_put_contents($logFileName, \"\\r\\n\\r\\n\", FILE_APPEND);\n    }\n    if ($ident_change < 0) {\n      $ident += $ident_change * 2;\n    }\n    file_put_contents($logFileName, date(FMT_DATE_TIME_SQL, time()) . str_repeat(' ', $ident + 1) . $message . \"\\r\\n\", FILE_APPEND);\n    if ($ident_change > 0) {\n      $ident += $ident_change * 2;\n    }\n  }\n\n  public function log_sql($message, $ident_change = 0) {\n    static $ident = 0;\n    static $logFileName;\n    static $mt_rand;\n\n    if (!$mt_rand) {\n      $mt_rand = mt_rand();\n    }\n\n    if (!$logFileName) {\n      $dbName      = SN::$db->dbName;\n      $logFileName = SN_ROOT_PHYSICAL . \"/.logs/{$dbName}.mysql.\" . date('Y-m-d-H-i-s.') . sprintf(\"%06d\", gettimeofday()[\"usec\"]) . \".log\";\n    }\n    if ($ident_change < 0) {\n      $ident += $ident_change * 2;\n    }\n    file_put_contents($logFileName, str_repeat(' ', $ident) . $message . \"\\n\\n\", FILE_APPEND);\n    if ($ident_change > 0) {\n      $ident += $ident_change * 2;\n    }\n  }\n\n  public function __construct() {\n    $this->log        = '';\n    $this->numqueries = 0;\n  }\n\n  function add($mes) {\n    $this->log .= $mes;\n    $this->numqueries++;\n  }\n\n  function add_to_array($mes) {\n    $this->log_array[] = $mes;\n  }\n\n  function echo_log() {\n    echo '<br><table><tr><td class=k colspan=4><a href=\"' . SN_ROOT_PHYSICAL . \"admin/settings.php\\\">Debug Log</a>:</td></tr>{$this->log}</table>\";\n    die();\n  }\n\n  public function compact_backtrace($backtrace, $long_comment = false, $onlyLastX = null) {\n    static $exclude_functions = array(\n      // Caller not needed\n      'comment_query',\n      // Excluding includes/requires\n      'include', 'include_once', 'require_once', 'require',\n      // Excluding query calls/DB wrap functions\n      'doquery',\n      'db_get_record_list', // 'db_user_by_id', 'db_get_user_by_id',\n      'doSelect', 'doSelectFetch',\n      'db_query_update',\n      'selectValue',\n      // classPersistent\n      '__get', 'db_loadItem', '__set', 'db_saveItem',\n      // Constructors ?! Why not...\n      '__construct',\n      // Hook handlers\n      'sn_function_call',\n      // Chat\n      'db_chat_player_list_online',\n      // Transaction-related functions\n      'db_transaction_commit', 'transactionCommit', 'transactionStart', 'db_transaction_start', 'db_transaction_rollback', 'transactionRollback',\n    );\n\n    $raw      = [];\n    $filtered = [];\n    foreach ($backtrace as $a_trace) {\n      $function =\n        (!empty($a_trace['type'])\n          ? ($a_trace['type'] == '->'\n            ? \"({$a_trace['class']})\" . get_class($a_trace['object'])\n            : $a_trace['class']\n          ) . $a_trace['type']\n          : ''\n        ) . $a_trace['function'] . '()';\n\n      $file = str_replace(SN_ROOT_PHYSICAL, '', str_replace('\\\\', '/', !empty($a_trace['file']) ? $a_trace['file'] : ''));\n\n      $line  = !empty($a_trace['line']) ? '@' . $a_trace['line'] : '';\n      $raw[] = \"{$function} - '{$file}'{$line}\";\n\n      if (!in_array($a_trace['function'], $exclude_functions)) {\n        $filtered[] = &$raw[count($raw) - 1];\n      }\n\n      if (!$long_comment) {\n        break;\n      }\n    }\n\n    $raw      = array_reverse($raw);\n    $filtered = array_reverse($filtered);\n\n    if ($onlyLastX) {\n      $raw      = array_slice($raw, -$onlyLastX);\n      $filtered = array_slice($filtered, -$onlyLastX);\n    }\n\n    return [$raw, $filtered,];\n  }\n\n  /**\n   * @param array  $backtrace\n   * @param string $sql\n   *\n   * @return string\n   */\n  public function comment_query($backtrace, $sql) {\n    list($raw, $filtered,) = $this->compact_backtrace($backtrace, defined('DEBUG_SQL_COMMENT_LONG'));\n\n    $sql_commented = [\n      \"/* \",\n      'date' => date(\"s.\") . sprintf(\"%06d\", gettimeofday()[\"usec\"]),\n      !empty($filtered) ? \"\\n\" . implode(\"\\n\", $filtered) . \" \\n\" : '',\n      \"*/\\n\",\n      // SQL itself with removed double spaces\n      preg_replace(\"/\\s+/\", ' ', $sql),\n    ];\n\n    if (defined('DEBUG_SQL_FILE_LOG') && !empty(DEBUG_SQL_FILE_LOG)) {\n      $this->log_sql(implode('', $sql_commented));\n    }\n\n//      $sql_commented = '/* ' . implode(\"<br />\", $sql_comment) . '<br /> */ ' . preg_replace(\"/\\s+/\", ' ', $sql);\n//      $isSelect = strpos(strtoupper($query), 'SELECT') !== false ? 'true' : 'false';\n    $sql_commented['date'] = date('Y-m-d H:i:') . $sql_commented['date'];\n\n//      if(strpos($sql_comment, 'compact_backtrace') === false) {\n//        $transaction_id = SN::db_transaction_check(false) ? SN::$transaction_id : SN::$transaction_id++;\n//        $result[] = \"tID {$transaction_id}\";\n//      }\n\n    if (defined('DEBUG_SQL_ERROR')) {\n      array_unshift($raw, preg_replace(\"/\\s+/\", ' ', $sql));\n      array_unshift($raw, $sql_commented['date']);\n      $this->add_to_array($raw);\n    }\n\n    $sql = implode('', $sql_commented);\n\n    return $sql;\n  }\n\n\n  function dump($dump = false, $force_base = false, $deadlock = false) {\n    if ($dump === false) {\n      return [];\n    }\n\n    $error_backtrace = array();\n    $base_dump       = false;\n\n    if ($force_base === true) {\n      $base_dump = true;\n    }\n\n    if ($dump === true) {\n      $base_dump = true;\n    } else {\n      if (!is_array($dump)) {\n        $dump = array('var' => $dump);\n      }\n\n      foreach ($dump as $dump_var_name => $dump_var) {\n        if ($dump_var_name == 'base_dump') {\n          $base_dump = $dump_var;\n        } else {\n          $error_backtrace[$dump_var_name] = $dump_var;\n        }\n      }\n    }\n\n    if ($deadlock && ($q = SN::$db->mysql_get_innodb_status())) {\n      $error_backtrace['deadlock'] = explode(\"\\n\", $q['Status']);\n      foreach ($error_backtrace['cSN_data'] as &$location) {\n        foreach ($location as $location_id => &$location_data) //          $location_data = $location_id;\n        {\n          $location_data = isset($location_data['username']) ? $location_data['username'] :\n            (isset($location_data['name']) ? $location_data['name'] : $location_id);\n        }\n      }\n    }\n\n    if ($base_dump) {\n      if (!is_array($this->log_array) || empty($this->log_array)) {\n        $this->log_array = [];\n      } else {\n        foreach ($this->log_array as $log) {\n          $error_backtrace['queries'][] = $log;\n        }\n      }\n\n      $error_backtrace['backtrace'] = debug_backtrace();\n      unset($error_backtrace['backtrace'][1]);\n      unset($error_backtrace['backtrace'][0]);\n\n      // Converting object instances to object names\n\n      foreach ($error_backtrace['backtrace'] as &$backtrace) {\n        if (!empty($backtrace['object']) && is_object($backtrace['object'])) {\n          $backtrace['object'] = get_class($backtrace['object']);\n        }\n\n        if (empty($backtrace['args'])) {\n          continue;\n        }\n\n        // Doing same conversion for backtrace params\n        foreach ($backtrace['args'] as &$arg) {\n          if (is_object($arg)) {\n            $arg = 'object::' . get_class($arg);\n          }\n        }\n      }\n\n      // $error_backtrace['query_log'] = \"\\r\\n\\r\\nQuery log\\r\\n<table><tr><th>Number</th><th>Query</th><th>Page</th><th>Table</th><th>Rows</th></tr>{$this->log}</table>\\r\\n\";\n      $error_backtrace['$_GET']     = $_GET;\n      $error_backtrace['$_POST']    = $_POST;\n      $error_backtrace['$_REQUEST'] = $_REQUEST;\n      $error_backtrace['$_COOKIE']  = $_COOKIE;\n      $error_backtrace['$_SESSION'] = empty($_SESSION) ? [] : $_SESSION;\n      $error_backtrace['$_SERVER']  = $_SERVER;\n      global $user, $planetrow;\n      $error_backtrace['user']      = $user;\n      $error_backtrace['planetrow'] = $planetrow;\n    }\n\n    return $error_backtrace;\n  }\n\n  function error_fatal($die_message, $details = 'There is a fatal error on page') {\n    // TODO - Записывать детали ошибки в лог-файл\n    die($die_message);\n  }\n\n  public function error($message = 'There is a error on page', $title = 'Internal Error', $httpCode = 500, $dump = true) {\n    global $config, $sys_stop_log_hit, $lang, $sys_log_disabled, $user;\n\n    if (empty(SN::$db->connected)) {\n      // TODO - писать ошибку в файл\n      die('SQL server currently unavailable. Please contact Administration...');\n    }\n\n    db_mysql::db_transaction_rollback();\n\n    if (SN::$config->debug == 1) {\n      /** @noinspection HtmlDeprecatedTag */\n      /** @noinspection XmlDeprecatedElement */\n      /** @noinspection HtmlDeprecatedAttribute */\n      echo \"<h2>{$title}</h2><br><font color=red>\" . htmlspecialchars($message) . \"</font><br><hr>\";\n      echo \"<table>{$this->log}</table>\";\n    }\n\n    $fatal_error = 'Fatal error: cannot write to `logs` table. Please contact Administration...';\n\n    $error_text      = SN::$db->db_escape($message);\n    $error_backtrace = $this->dump($dump, true, strpos($message, 'Deadlock') !== false);\n\n    if (!$sys_log_disabled) {\n      $this->_writeLogMessage($httpCode, $user, $title, $message, $error_backtrace, $fatal_error);\n\n      $message = \"Пожалуйста, свяжитесь с админом, если ошибка повторится. Ошибка №: <b>\" . SN::$db->db_insert_id() . \"</b>\";\n\n      $sys_stop_log_hit = true;\n      $sys_log_disabled = true;\n      !function_exists('messageBox') ? die($message) : SnTemplate::messageBox($message, 'Ошибка', '', 0, false);\n    } else {\n//        // TODO Здесь надо писать в файло\n      ob_start();\n      print(\"<hr>User ID {$user['id']} raised error code {$httpCode} titled '{$title}' with text '{$error_text}' on page {$_SERVER['SCRIPT_NAME']}\");\n\n      foreach ($error_backtrace as $name => $value) {\n        print('<hr>');\n        pdump($value, $name);\n      }\n      ob_end_flush();\n      die();\n    }\n  }\n\n  function warning($message, $title = 'System Message', $httpCode = 300, $dump = false) {\n    global $user, $lang, $sys_log_disabled;\n\n    if (empty(SN::$db->connected)) {\n      // TODO - писать ошибку в файл\n      die('SQL server currently unavailable. Please contact Administration...');\n    }\n\n    $fatal_error = 'Fatal error: cannot write to `logs` table. Please contact Administration...';\n\n    $error_backtrace = $this->dump($dump, false);\n\n    if (empty($sys_log_disabled)) {\n      $this->_writeLogMessage($httpCode, $user, $title, $message, $error_backtrace, $fatal_error);\n    } else {\n//        // TODO Здесь надо писать в файло\n      $id = !empty($user['id']) ? $user['id'] : 0;\n      print(\"<hr>User ID {$id} made log entry with code {$httpCode} titled '{$title}' with text '{$message}' on page {$_SERVER['SCRIPT_NAME']}\");\n    }\n  }\n\n  /**\n   * @param       $httpCode\n   * @param       $user\n   * @param       $title\n   * @param       $message\n   * @param array $error_backtrace\n   * @param       $fatal_error\n   *\n   * @return void\n   */\n  function _writeLogMessage($httpCode, $user, $title, $message, array $error_backtrace, $fatal_error) {\n    /** @noinspection SqlResolve */\n    $query = \"INSERT INTO `{{logs}}` SET\n        `log_time` = '\" . time() . \"', `log_code` = '\" . SN::$db->db_escape($httpCode) . \"', \" .\n      \"`log_sender` = '\" . (!empty($user['id']) ? SN::$db->db_escape($user['id']) : 0) . \"', \" .\n      \"`log_username` = '\" . SN::$db->db_escape(!empty($user['user_name']) ? $user['user_name'] : '') . \"', \" .\n      \"`log_title` = '\" . SN::$db->db_escape($title) . \"',  `log_text` = '\" . SN::$db->db_escape($message) . \"', \" .\n      \"`log_page` = '\" . SN::$db->db_escape(strpos($_SERVER['SCRIPT_NAME'], SN_ROOT_RELATIVE) === false ? $_SERVER['SCRIPT_NAME'] : substr($_SERVER['SCRIPT_NAME'], strlen(SN_ROOT_RELATIVE))) . \"'\" . \", \" .\n      \"`log_dump` = '\" . ($error_backtrace ? SN::$db->db_escape(json_encode($error_backtrace)) : '') . \"'\" . \";\";\n\n    doquery($query, '', false, true) or die($fatal_error . SN::$db->db_error());\n  }\n}\n\n// Copyright (c) 2009-2010 Gorlum for http://supernova.ws\n// Dump variables nicer then var_dump()\n\nfunction dump($value, $varname = null, $level = 0, $dumper = '') {\n  if (isset($varname)) {\n    $varname .= \" = \";\n  }\n\n  if ($level == -1) {\n    $trans[' ']  = '&there4;';\n    $trans[\"\\t\"] = '&rArr;';\n    $trans[\"\\n\"] = '&para;;';\n    $trans[\"\\r\"] = '&lArr;';\n    $trans[\"\\0\"] = '&oplus;';\n\n    return strtr(htmlspecialchars($value), $trans);\n  }\n  if ($level == 0) {\n//    $dumper = '<pre>' . mt_rand(10, 99) . '|' . $varname;\n    $dumper = mt_rand(10, 99) . '|' . $varname;\n  }\n\n  $type   = gettype($value);\n  $dumper .= $type;\n\n  if ($type == 'string') {\n    $dumper .= '(' . strlen($value) . ')';\n    $value  = dump($value, '', -1);\n  } elseif ($type == 'boolean') {\n    $value = ($value ? 'true' : 'false');\n  } elseif ($type == 'object') {\n    $props  = get_class_vars(get_class($value));\n    $dumper .= '(' . count($props) . ') <u>' . get_class($value) . '</u>';\n    foreach ($props as $key => $val) {\n      $dumper .= \"\\n\" . str_repeat(\"\\t\", $level + 1) . $key . ' => ';\n      $dumper .= dump($value->$key, '', $level + 1);\n    }\n    $value = '';\n  } elseif ($type == 'array') {\n    $dumper .= '(' . count($value) . ')';\n    foreach ($value as $key => $val) {\n      $dumper .= \"\\n\" . str_repeat(\"\\t\", $level + 1) . dump($key, '', -1) . ' => ';\n      $dumper .= dump($val, '', $level + 1);\n    }\n    $value = '';\n  }\n  $dumper .= \" <b>$value</b>\";\n//  if($level == 0) {\n//    $dumper .= '</pre>';\n//  }\n\n  return $dumper;\n}\n\nfunction debug($value, $varname = null) {\n  pdump($value, $varname);\n}\n\nfunction pr($prePrint = false) {\n  if ($prePrint) {\n    print(\"<br>\");\n  }\n  print(mt_rand() . \"<br>\");\n}\n\nfunction pc($prePrint = false) {\n  global $_PRINT_COUNT_VALUE;\n  $_PRINT_COUNT_VALUE++;\n\n  if ($prePrint) {\n    print(\"<br>\");\n  }\n  print($_PRINT_COUNT_VALUE . \"<br>\");\n}\n\nfunction prep($message) {\n  print('<pre>' . $message . '</pre>');\n}\n\nfunction backtrace_no_arg() {\n  $trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);\n  array_shift($trace);\n\n  return $trace;\n}\n"
  },
  {
    "path": "classes/sn_module_payment.php",
    "content": "<?php\n\nuse DBAL\\db_mysql;\nuse Modules\\sn_module;\nuse Payment\\PaymentMethods;\n\n/**\n * User: Gorlum\n * Date: 21.04.2015\n * Time: 3:49\n */\nabstract class sn_module_payment extends sn_module {\n  const DO_NOT_REDIRECT = 'DO_NOT_REDIRECT';\n  const FIELD_SUM = 'SUM';\n  const FIELD_CURRENCY = 'CURRENCY';\n\n  public $versionCommitted = '#46d0#';\n\n  public $active = false;\n\n  public static $bonus_table = [\n    2000    => 0,\n    // 5000 => 0,\n    10000   => 0,\n    20000   => 0,\n    50000   => 0.02,\n    100000  => 0.05,\n    200000  => 0.07,\n    300000  => 0.10,\n    400000  => 0.15,\n    500000  => 0.20,\n    800000  => 0.25,\n    1000000 => 0.30,\n    1500000 => 0.40,\n    2000000 => 0.50,\n    3000000 => 0.60,\n    5000000 => 0.70,\n  ];\n\n  /**\n   * @var Account $account\n   */\n  public $account = null;\n\n  /**\n   * @var db_mysql $db\n   */\n  public $db = null;\n\n  /**\n   * @var int\n   */\n  public $request_payment_id = 0;\n  /**\n   * Идентификатор сервера, на который производится оплата\n   *\n   * @var string $request_server_id\n   */\n  public $request_server_id = '';\n  /**\n   * Идентификатор платящего пользователя\n   *\n   * @var int\n   */\n  public $request_account_id = 0;\n  /**\n   * @var int\n   */\n  // public $request_mm_amount = 0;\n  /**\n   * @var float\n   */\n  // public $request_money_out = 0.0;\n\n  /**\n   * Внутренний идентификатор платежа\n   *\n   * @var int\n   */\n  public $payment_id = 0;\n  public $payment_status = PAYMENT_STATUS_NONE;\n  public $payment_provider_id = ACCOUNT_PROVIDER_NONE;\n  public $payment_account_id = 0;\n  public $payment_account_name = '';\n  public $payment_user_id = 0;\n  public $payment_user_name = '';\n  public $payment_amount = 0;\n  public $payment_currency = '';\n  public $payment_dark_matter_paid = 0;\n  public $payment_dark_matter_gained = 0;\n  public $payment_date = SN_TIME_SQL;\n  public $payment_comment = '';\n  public $payment_module_name = '';\n\n  public $payment_external_id = '';\n  public $payment_external_date = '';\n  public $payment_external_lots = 0;\n  public $payment_external_amount = 0;\n  public $payment_external_currency = '';\n\n  public $payment_method = null;\n\n  public $payment_test = 0;\n\n  public $is_exists = false;\n  public $is_loaded = false;\n\n  protected $description_generated = array();\n\n  protected $debug = false;\n\n  protected $payment_params = array(\n//    'server_id' => 'shp_server', // Должен быть server_id\n//    'account_id' => 'shp_id', // Должен быть user_id\n//    'payment_id' => 'InvId', // Должен быть внутренний payment_id\n//    'payment_dark_matter_gained' => 'shp_dm', // TODO - Реально - dark_matter_gained! Что бы учитывались акции!\n//    'payment_external_money' => 'OutSum', // Количество денег \"к оплате\" от СН\n//    'test' => 'shp_z_test', // Тестовый статус аккаунта\n//    'payment_external_id' => '', // ИД платежа в платёжной системе\n//    'payment_external_currency' => 'payment_currency', // Валюта платежа в платёжной системе\n  );\n\n  protected $result_translations = array(\n    // Универсальный ответ на неизвестную ошибку\n    SN_PAYMENT_REQUEST_UNDEFINED_ERROR => SN_PAYMENT_REQUEST_UNDEFINED_ERROR,\n    // Утвердительный ответ\n    SN_PAYMENT_REQUEST_OK              => SN_PAYMENT_REQUEST_OK,\n  );\n\n  /**\n   * sn_module_payment constructor.\n   *\n   * @param string $filename\n   *\n   * @throws Exception\n   */\n  public function __construct($filename = __FILE__) {\n    parent::__construct($filename);\n\n    if (!empty($this->config['debug'])) {\n      $this->debug = true;\n    }\n  }\n\n  /**\n   * @param array $data\n   */\n  public function debug($data) {\n    if (!$this->debug) {\n      return;\n    }\n\n    file_put_contents(SN_ROOT_PHYSICAL . '_' . get_called_class() . '_debug.txt', $data, FILE_APPEND);\n  }\n\n  /**\n   * Компилирует запрос к платёжной системе\n   *\n   * @param $request\n   *\n   * @throws Exception\n   */\n  public function compile_request($request, $payment_method_selected) {\n    global $config, $lang, $user;\n\n//    if (!(SN::$auth->account instanceof Account)) {\n//      // TODO - throw new Exception($lang['pay_msg_mm_request_amount_invalid'], SN_PAYMENT_REQUEST_ERROR_UNIT_AMOUNT);\n//    }\n    $this->account = SN::$auth->account;\n\n    $this->db = $this->account->db;\n\n    $this->payment_provider_id  = core_auth::$main_provider->provider_id;\n    $this->payment_account_id   = $this->account->account_id;\n    $this->payment_account_name = $this->account->account_name;\n    $this->payment_user_id      = $user['id'];\n    $this->payment_user_name    = $user['username'];\n\n    // TODO - минимальное количество ММ к оплате\n    $this->payment_dark_matter_paid   = $request['metamatter'];\n    $this->payment_dark_matter_gained = self::bonus_calculate($this->payment_dark_matter_paid, true);\n\n    $this->payment_currency = $config->payment_currency_default;\n    $this->payment_amount   = self::currency_convert($this->payment_dark_matter_paid, 'MM_', $this->payment_currency);\n\n    $this->payment_method = $payment_method_selected;\n    if (empty($this->payment_external_currency)) {\n      $this->payment_external_currency = $this->getMethodCurrency($this->payment_method);\n    }\n    if (empty($this->payment_external_currency) && !empty($this->config['currency'])) {\n      $this->payment_external_currency = $this->config['currency'];\n    }\n    if (empty($this->payment_external_currency)) {\n      throw new Exception($lang['pay_error_internal_no_external_currency_set'], SN_PAYMENT_ERROR_INTERNAL_NO_EXTERNAL_CURRENCY_SET);\n    }\n\n    $this->payment_external_amount = self::currency_convert($this->payment_dark_matter_paid, 'MM_', $this->payment_external_currency);\n    if ($this->payment_external_amount < 0.01) {\n      throw new Exception($lang['pay_msg_mm_request_amount_invalid'], SN_PAYMENT_REQUEST_ERROR_UNIT_AMOUNT);\n    }\n\n    $this->payment_test = !empty($this->config['test']);\n\n    $this->generate_description();\n\n    $this->db_insert();\n    if (!$this->is_exists) {\n      throw new Exception($lang['pay_msg_request_error_db_payment_create'], SN_PAYMENT_REQUEST_DB_ERROR_PAYMENT_CREATE);\n    }\n  }\n\n  /**\n   * @param array $options\n   *\n   * @return array\n   * @throws Exception\n   */\n  protected function payment_request_process($options = []) {\n    global $lang, $config;\n\n    if (!$this->active) {\n      throw new Exception($lang['pay_msg_module_disabled'], SN_MODULE_DISABLED);\n    }\n\n    // Если есть payment_id - загружаем под него данные\n    if (!empty($this->payment_params['payment_id'])) {\n      $this->request_payment_id = sys_get_param_id($this->payment_params['payment_id']);\n      if (!$this->request_payment_id) {\n        throw new Exception($lang['pay_msg_request_payment_id_invalid'], SN_PAYMENT_REQUEST_INTERNAL_ID_WRONG);\n      }\n\n      if (!$this->db_get_by_id($this->request_payment_id)) {\n        throw new Exception($lang['pay_msg_request_payment_id_invalid'], SN_PAYMENT_REQUEST_INTERNAL_ID_WRONG);\n      }\n\n      // Проверяем - был ли этот платеж обработан?\n      // TODO - Статусы бывают разные. Нужен спецфлаг payment_processed\n      if ($this->payment_status != PAYMENT_STATUS_NONE && empty($options[self::DO_NOT_REDIRECT])) {\n        db_mysql::db_transaction_rollback();\n        sys_redirect(SN_ROOT_VIRTUAL . 'metamatter.php?payment_id=' . $this->payment_id);\n        die();\n      }\n    }\n\n    // Пытаемся получить из запроса ИД аккаунта\n    $request_account_id = !empty($this->payment_params['account_id']) ? sys_get_param_id($this->payment_params['account_id']) : 0;\n    // Если в запросе нет ИД аккаунта - пытаемся использовать payment_account_id\n    if (empty($request_account_id) && !empty($this->payment_account_id)) {\n      $request_account_id = $this->payment_account_id;\n    }\n    // Если теперь у нас нету ИД аккаунта ни в запросе, ни в записи таблицы - можно паниковать\n    if (empty($request_account_id)) {\n      // TODO - аккаунт\n      throw new Exception($lang['pay_msg_request_user_invalid'], $this->retranslate_error(SN_PAYMENT_REQUEST_USER_NOT_FOUND, $options));\n    }\n    // Если нет записи в таблице - тогда берем payment_account_id из запроса\n    if (empty($this->payment_account_id)) {\n      $this->payment_account_id = $request_account_id;\n    }\n    // Если у нас отличаются ИД аккаунта в запросе и ИД аккаунта в записи - тоже можно паниковать\n    if ($this->payment_account_id != $request_account_id) {\n      // TODO - Поменять сообщение об ошибке\n      throw new Exception($lang['pay_msg_request_user_invalid'], $this->retranslate_error(SN_PAYMENT_REQUEST_USER_NOT_FOUND, $options));\n    }\n    // Проверяем существование аккаунта с данным ИД\n    if (!$this->account->db_get_by_id($this->payment_account_id)) {\n      throw new Exception($lang['pay_msg_request_user_invalid'] . ' ID ' . $this->payment_account_id, $this->retranslate_error(SN_PAYMENT_REQUEST_USER_NOT_FOUND, $options));\n    }\n\n    // TODO Проверка на сервер_ид - как бы и не нужна, наверное?\n    if (!empty($this->payment_params['server_id'])) {\n      $this->request_server_id = sys_get_param_str($this->payment_params['server_id']);\n      if (SN_ROOT_VIRTUAL != $this->request_server_id) {\n        throw new Exception($lang['pay_msg_request_server_wrong'] . \" {$this->request_server_id} вместо \" . SN_ROOT_VIRTUAL, SN_PAYMENT_REQUEST_SERVER_WRONG);\n      }\n    }\n\n    // Сверка количества оплаченной ММ с учётом бонусов\n    if (!empty($this->payment_params['payment_dark_matter_gained'])) {\n      $request_mm_amount = sys_get_param_id($this->payment_params['payment_dark_matter_gained']);\n      if ($request_mm_amount != $this->payment_dark_matter_gained && $this->is_loaded) {\n        throw new Exception($lang['pay_msg_mm_request_amount_invalid'] . \" пришло {$request_mm_amount} ММ вместо {$this->payment_dark_matter_gained} ММ\", SN_PAYMENT_REQUEST_MM_AMOUNT_INVALID);\n      }\n      empty($this->payment_dark_matter_gained) ? $this->payment_dark_matter_gained = $request_mm_amount : false;\n    }\n//    if (empty($this->payment_dark_matter_paid)) {\n//      // TODO - обратный расчёт из gained\n//    }\n\n    // Проверка наличия внешнего ИД платежа\n    if (!empty($this->payment_params['payment_external_id'])) {\n      $request_payment_external_id = sys_get_param_str($this->payment_params['payment_external_id']);\n      if (empty($request_payment_external_id)) {\n        throw new exception($lang['pay_msg_request_payment_id_invalid'], SN_PAYMENT_REQUEST_EXTERNAL_ID_WRONG);\n      } elseif (!empty($this->payment_external_id) && $this->payment_external_id != $request_payment_external_id) {\n        // TODO - Может быть поменять сообщение\n        throw new exception($lang['pay_msg_request_payment_id_invalid'], SN_PAYMENT_REQUEST_EXTERNAL_ID_WRONG);\n      }\n      $this->payment_external_id = $request_payment_external_id;\n    }\n    // Сверка суммы, запрошенной СН к оплате\n    if (!empty($this->payment_params['payment_external_money'])) {\n      $request_money_out = sys_get_param_float($this->payment_params['payment_external_money']);\n      if ($request_money_out != $this->payment_external_amount && $this->is_loaded) {\n        throw new Exception($lang['pay_msg_request_payment_amount_invalid'] . \" пришло {$request_money_out} денег вместо {$this->payment_external_amount} денег\", SN_PAYMENT_REQUEST_CURRENCY_AMOUNT_INVALID);\n      }\n      empty($this->payment_external_amount) ? $this->payment_external_amount = $request_money_out : false;\n    }\n    // Заполняем поле валюты платёжной системы\n    if (!empty($this->payment_params['payment_external_currency'])) {\n      $this->payment_external_currency = sys_get_param_str($this->payment_params['payment_external_currency']);\n      if (empty($this->payment_external_currency)) {\n        // TODO - поменять сообщение\n        throw new Exception($lang['pay_msg_request_payment_amount_invalid'] . \" {$this->payment_external_currency}\", SN_PAYMENT_REQUEST_CURRENCY_AMOUNT_INVALID);\n      }\n    }\n    if (empty($this->payment_external_currency)) {\n      $this->payment_external_currency = $this->config['currency'];\n    }\n\n    // Заполнение внутренней суммы и валюты из внешних данных\n    if (empty($this->payment_currency)) {\n      $this->payment_currency = $config->payment_currency_default;\n    }\n    if (empty($this->payment_amount) && !empty($this->payment_external_currency)) {\n      $this->payment_amount = self::currency_convert($this->payment_external_amount, $this->payment_external_currency, $this->payment_currency);\n    }\n\n    // TODO - Тестовый режим\n    if (!empty($this->payment_params['test'])) {\n      $this->payment_test = $this->config['test'] || sys_get_param_int($this->payment_params['test']);\n    }\n\n    $this->generate_description();\n  }\n\n  /**\n   * Точка входа для коллбэка системы платежей - вызывается из <class_name>_response.php\n   *\n   * @return array\n   */\n  // OK 4.8\n  // TODO - Здесь должно происходить разделение на resultURL, successURL, failURL\n  public function payment_request_response() {\n    global $debug;\n\n    $this->db      = core_auth::$main_provider->db;\n    $this->account = new Account($this->db);\n\n    // TODO - REPLACE WITH INNATE CALL!\n    db_mysql::db_transaction_start();\n    try {\n      $response = $this->payment_request_process();\n    } catch (Exception $e) {\n      $response['result']  = $e->getCode();\n      $response['message'] = $e->getMessage();\n\n      $this->debug([\n        \"\\n\",\n        \"Kinda Error!\\n\",\n        '$response => ', var_export($response, true),\n        \"\\n\",\n      ]);\n    }\n\n    if ($response['result'] == SN_PAYMENT_REQUEST_OK) {\n      db_mysql::db_transaction_commit();\n      $debug->warning('Результат операции: код ' . $response['result'] . ' сообщение \"' . $response['message'] . '\"', 'Успешный платёж', LOG_INFO_PAYMENT);\n    } else {\n      db_mysql::db_transaction_rollback();\n      $debug->warning('Результат операции: код ' . $response['result'] . ' сообщение \"' . $response['message'] . '\"', 'Ошибка платежа', LOG_INFO_PAYMENT, true);\n    }\n\n    // Переводим код результата из СН в код платежной системы\n    if (is_array($this->result_translations) && !empty($this->result_translations)) {\n      $response['result'] = isset($this->result_translations[$response['result']]) ? $this->result_translations[$response['result']] : $this->result_translations[SN_PAYMENT_REQUEST_UNDEFINED_ERROR];\n    }\n\n    return $response;\n  }\n\n  /**\n   *\n   *\n   * @param string  $payment_method_selected\n   * @param string  $player_currency\n   * @param integer $metamatter\n   *\n   * @return array [self::FIELD_SUM => (float){sum_to_pay}, self::FIELD_CURRENCY => (str){currency_code}]. Currency code is optional. Empty if no data\n   */\n  public function getPrice($payment_method_selected, $player_currency, $metamatter) {\n    return [];\n  }\n  // Function converts money values between currencies\n\n  /**\n   * Внутриигровая конвертация валют\n   *\n   * @param        $value\n   * @param string $currency_from\n   * @param string $currency_to\n   * @param int    $round\n   *\n   * @return float|int\n   */\n  public static function currency_convert($value, $currency_from = '', $currency_to = '', $round = 2) {\n//    global $config;\n\n    $currency_from = strtolower($currency_from);\n    $currency_to   = strtolower($currency_to);\n\n    if ($currency_from != $currency_to) {\n//      $config_currency_from_name = 'payment_currency_exchange_' . $currency_from;\n//      $config_currency_to_name = 'payment_currency_exchange_' . $currency_to;\n\n//      $exchange_from = floatval($currency_from == 'mm_' ? get_mm_cost() : $config->$config_currency_from_name);\n//      $exchange_to = floatval($currency_to == 'mm_' ? get_mm_cost() : $config->$config_currency_to_name);\n\n      $exchange_from = get_exchange_rate($currency_from);\n      $exchange_to   = get_exchange_rate($currency_to);\n\n      $value = $exchange_from ? $value / $exchange_from * $exchange_to * pow(10, $round) : 0;\n      $value = ceil($value) / pow(10, $round);\n    }\n\n    return $value;\n  }\n\n  // Function calculates bonused DM amount for bulk purchase and ($direct = false) vice versa\n\n  /**\n   * Рассчёт бонуса ММ\n   *\n   * @param            $dark_matter\n   * @param bool|true  $direct\n   * @param bool|false $return_bonus\n   *\n   * @return float|int\n   */\n  public static function bonus_calculate($dark_matter, $direct = true, $return_bonus = false) {\n    $bonus           = 0;\n    $dark_matter_new = $dark_matter;\n    if (!empty(self::$bonus_table) && $dark_matter >= self::$bonus_table[0]) {\n      if ($direct) {\n        foreach (self::$bonus_table as $dm_for_bonus => $multiplier) {\n          if ($dm_for_bonus <= $dark_matter) {\n            $dark_matter_new = $dark_matter * (1 + $multiplier);\n            $bonus           = $multiplier;\n          } else {\n            break;\n          }\n        }\n      } else {\n        foreach (self::$bonus_table as $dm_for_bonus => $multiplier) {\n          $temp = $dm_for_bonus * (1 + $multiplier);\n          if ($dark_matter >= $temp) {\n            $dark_matter_new = round($dark_matter / (1 + $multiplier));\n            $bonus           = $multiplier;\n          } else {\n            break;\n          }\n        }\n      }\n    }\n\n    return $return_bonus ? $bonus : $dark_matter_new;\n  }\n\n  // Дополнительная ре-трансляция адреса, если в каком-то случае платежная система ожидает нелогичный ответ\n  // Пример: иксолла при неправильно заданном пользователе в ордере ожидает НЕПРАВИЛЬНЫЙ_ОРДЕР, а не НЕПРАВИЛЬНЫЙ_ПОЛЬЗОВАТЕЛЬ\n  protected function retranslate_error($error_code, $options = array()) {\n    return isset($options['retranslate_error'][$error_code]) ? $options['retranslate_error'][$error_code] : $error_code;\n  }\n\n\n  protected function db_insert() {\n    $this->payment_test = !empty($this->config['test']) || $this->payment_test;\n\n    $payment = array(\n      'payment_module_name' => $this->manifest['name'],\n\n      'payment_status' => $this->payment_status,\n      // 'payment_date' => $this->payment_date, // Не нужно\n\n      'payment_provider_id'  => $this->payment_provider_id,\n      'payment_account_id'   => $this->payment_account_id,\n      'payment_account_name' => $this->payment_account_name,\n      'payment_user_id'      => $this->payment_user_id,\n      'payment_user_name'    => $this->payment_user_name,\n\n      'payment_dark_matter_paid'   => $this->payment_dark_matter_paid,\n      'payment_dark_matter_gained' => $this->payment_dark_matter_gained,\n\n      'payment_amount'   => $this->payment_amount,\n      'payment_currency' => $this->payment_currency,\n\n      'payment_external_id'       => $this->payment_external_id, // TODO\n      'payment_external_amount'   => $this->payment_external_amount,\n      'payment_external_currency' => $this->payment_external_currency,\n      'payment_external_date'     => $this->payment_external_date, // TODO\n\n      'payment_test' => $this->payment_test ? 1 : 0, // Boolean -> int\n\n      'payment_comment' => $this->description_generated[PAYMENT_DESCRIPTION_MAX],\n\n      'payment_external_lots' => $this->payment_dark_matter_paid / get_mm_cost(),\n\n      'payment_method_id' => $this->payment_method,\n    );\n\n    $replace = false;\n    if ($this->payment_id) {\n      $payment['payment_id'] = $this->payment_id;\n      $replace               = true;\n    }\n\n    $query = array();\n    foreach ($payment as $key => $value) {\n      if ($value === null) {\n        $value = 'NULL';\n      } else {\n        $value = is_string($value) ? '\"' . SN::$db->db_escape($value) . '\"' : $value;\n      }\n      $query[] = \"`{$key}` = {$value}\";\n    }\n\n    $this->db->doquery(($replace ? 'REPLACE' : 'INSERT') . ' INTO `{{payment}}` SET ' . implode(',', $query) . ';');\n\n    return $this->db_get_by_id($this->db->db_insert_id());\n  }\n\n  /**\n   * Get currency that module support for method\n   *\n   * @param $paymentMethod\n   *\n   * @return string\n   */\n  public function getMethodCurrency($paymentMethod) {\n    // Generally each module can support range of currencies and override this method\n    // This is just a plug to route requests by default\n    return PaymentMethods::getDefaultCurrency($paymentMethod);\n  }\n\n\n  /**\n   * @throws Exception\n   */\n  protected function payment_adjust_mm_new() {\n    if (!$this->payment_test) {\n      // Not a test payment. Adding DM to account\n      $this->account = new Account($this->db);\n      $this->account->db_get_by_id($this->payment_account_id);\n      $result = $this->account->metamatter_change(RPG_PURCHASE, $this->payment_dark_matter_gained, $this->payment_comment);\n      if (!$result) {\n        throw new Exception('Ошибка начисления ММ', SN_METAMATTER_ERROR_ADJUST);\n      }\n    }\n  }\n\n  /**\n   * @param $payment\n   *\n   * @throws exception\n   */\n  function payment_cancel(/** @noinspection PhpUnusedParameterInspection */ &$payment) {\n    die('{НЕ РАБОТАЕТ! СООБЩИТЕ АДМИНИСТРАЦИИ!}');\n    global $lang;\n\n    if (!isset($payment['payment_status'])) {\n      throw new exception($lang['pay_msg_request_payment_not_found'], SN_PAYMENT_REQUEST_ORDER_NOT_FOUND);\n    }\n\n    if ($payment['payment_status'] == PAYMENT_STATUS_COMPLETE) {\n      $safe_comment = SN::$db->db_escape($payment['payment_comment'] = $lang['pay_msg_request_payment_cancelled'] . ' ' . $payment['payment_comment']);\n\n      if (!$payment['payment_test']) {\n        $result = $this->account->metamatter_change(RPG_PURCHASE_CANCEL, -$payment['payment_dark_matter_gained'], $payment['payment_comment']);\n        if (!$result) {\n          throw new exception('Ошибка начисления ММ', SN_METAMATTER_ERROR_ADJUST);\n        }\n      }\n      $payment['payment_status'] = PAYMENT_STATUS_CANCELED;\n      doquery(\"UPDATE {{payment}} SET payment_status = {$payment['payment_status']}, payment_comment = '{$safe_comment}' WHERE payment_id = {$payment['payment_id']};\");\n      throw new exception($lang['pay_msg_request_payment_cancel_complete'], SN_PAYMENT_REQUEST_OK);\n    } elseif ($payment['payment_status'] == PAYMENT_STATUS_CANCELED) {\n      throw new exception($lang['pay_msg_request_payment_cancelled_already'], SN_PAYMENT_REQUEST_OK);\n    } elseif ($payment['payment_status'] == PAYMENT_STATUS_NONE) {\n      throw new exception($lang['pay_msg_request_payment_cancel_not_complete'], SN_PAYMENT_REQUEST_PAYMENT_NOT_COMPLETE);\n    }\n  }\n\n\n  protected function db_get_by_id($payment_id_unsafe) {\n    $payment_id_internal_safe = $this->db->db_escape($payment_id_unsafe);\n    $payment                  = $this->db->doQueryAndFetch(\n      \"SELECT * \n      FROM {{payment}} \n      WHERE \n        `payment_module_name` = '{$this->manifest['name']}' \n        AND `payment_id` = '{$payment_id_internal_safe}' \n        LIMIT 1 FOR UPDATE;\"\n    );\n\n    return $this->db_assign_payment($payment);\n  }\n\n  /**\n   * @throws Exception\n   */\n  protected function db_complete_payment() {\n    // TODO - поле payment_processed\n    if ($this->payment_status == PAYMENT_STATUS_NONE) {\n      if (!defined('PAYMENT_EXPIRE_TIME') || PAYMENT_EXPIRE_TIME == 0 || empty($this->payment_date) || strtotime($this->payment_date) + PAYMENT_EXPIRE_TIME <= SN_TIME_NOW) {\n        $this->payment_adjust_mm_new();\n        $this->payment_status = PAYMENT_STATUS_COMPLETE;\n      } else {\n        $this->payment_status = PAYMENT_STATUS_EXPIRED;\n      }\n\n      $this->db_insert();\n    }\n  }\n\n  protected function payment_reset() {\n    $this->payment_id     = 0;\n    $this->payment_status = PAYMENT_STATUS_NONE;\n\n    $this->payment_provider_id  = ACCOUNT_PROVIDER_NONE;\n    $this->payment_account_id   = 0;\n    $this->payment_account_name = '';\n    $this->payment_user_id      = 0;\n    $this->payment_user_name    = '';\n\n    $this->payment_amount   = 0;\n    $this->payment_currency = '';\n\n    $this->payment_dark_matter_paid   = 0;\n    $this->payment_dark_matter_gained = 0;\n    $this->payment_date               = SN_TIME_SQL;\n    $this->payment_comment            = '';\n    $this->payment_module_name        = '';\n\n    $this->payment_external_id       = '';\n    $this->payment_external_date     = '';\n    $this->payment_external_lots     = 0;\n    $this->payment_external_amount   = 0;\n    $this->payment_external_currency = '';\n\n    $this->payment_test = 0;\n\n    $this->is_exists = false;\n    $this->is_loaded = false;\n\n    $this->description_generated = array();\n  }\n\n  protected function db_assign_payment($payment = null) {\n    $this->payment_reset();\n\n    if (is_array($payment) && isset($payment['payment_id'])) {\n      $this->payment_id     = $payment['payment_id'];\n      $this->payment_status = $payment['payment_status'];\n      $this->payment_date   = $payment['payment_date'];\n\n      $this->payment_provider_id  = $payment['payment_provider_id'];\n      $this->payment_account_id   = $payment['payment_account_id'];\n      $this->payment_account_name = $payment['payment_account_name'];\n      $this->payment_user_id      = $payment['payment_user_id'];\n      $this->payment_user_name    = $payment['payment_user_name'];\n\n      $this->payment_amount   = $payment['payment_amount'];\n      $this->payment_currency = $payment['payment_currency'];\n\n      $this->payment_dark_matter_paid   = $payment['payment_dark_matter_paid'];\n      $this->payment_dark_matter_gained = $payment['payment_dark_matter_gained'];\n\n      $this->payment_comment     = $payment['payment_comment'];\n      $this->payment_module_name = $payment['payment_module_name'];\n\n      $this->payment_external_id       = $payment['payment_external_id'];\n      $this->payment_external_date     = $payment['payment_external_date'];\n      $this->payment_external_lots     = $payment['payment_external_lots'];\n      $this->payment_external_amount   = $payment['payment_external_amount'];\n      $this->payment_external_currency = $payment['payment_external_currency'];\n\n      $this->payment_test = $payment['payment_test'];\n\n      $this->is_exists = true;\n      $this->is_loaded = true;\n\n      $this->generate_description();\n\n      return true;\n    } else {\n      return false;\n    }\n  }\n\n  protected function generate_description() {\n    $mmShort = SN::$lang['sys_metamatter_sh'];\n    // TODO - системная локализация\n    $this->description_generated = array(\n      PAYMENT_DESCRIPTION_50 => substr(\n        ($this->payment_test ? \"T!\" : '') .\n        \"{$this->payment_dark_matter_gained} {$mmShort} {$this->account->account_name}\",\n        0,\n        PAYMENT_DESCRIPTION_50\n      ),\n      PAYMENT_DESCRIPTION_100 => substr(\"{$this->payment_dark_matter_gained} {$mmShort} аккаунт [{$this->account->account_name}] ID {$this->account->account_id} на \" . SN_ROOT_VIRTUAL, 0, PAYMENT_DESCRIPTION_100),\n      PAYMENT_DESCRIPTION_250 => substr(\"Оплата {$this->payment_dark_matter_gained} {$mmShort} для аккаунта [{$this->payment_user_name}] ID {$this->payment_user_id} на сервере \" . SN_ROOT_VIRTUAL, 0, PAYMENT_DESCRIPTION_250),\n      PAYMENT_DESCRIPTION_MAX => ($this->payment_test ? \"ТЕСТОВЫЙ ПЛАТЕЖ! \" : '') .\n        \"Платеж от аккаунта '{$this->payment_account_name}' ID {$this->payment_account_id} игрока '{$this->payment_user_name}' ID {$this->payment_user_id} на сервере \" . SN_ROOT_VIRTUAL .\n        \" сумма {$this->payment_amount} {$this->payment_currency} за {$this->payment_dark_matter_paid} {$mmShort} (начислено {$this->payment_dark_matter_gained} {$mmShort})\" .\n        \" через '{$this->manifest['name']}' сумма {$this->payment_external_amount} {$this->payment_external_currency}\",\n    );\n  }\n\n\n  /**\n   * Does this module support payment method?\n   *\n   * @param $payment_method\n   *\n   * @return bool\n   */\n  public function isMethodSupported($payment_method) {\n    return array_key_exists($payment_method, $this->manifest['payment_method']);\n  }\n\n  /**\n   * Get details about payment method\n   *\n   * @param int $payment_method\n   *\n   * @return mixed|null\n   */\n  public function getMethodDetails($payment_method) {\n    return $this->isMethodSupported($payment_method) ? $this->manifest['payment_method'][$payment_method] : null;\n  }\n\n  public function getMethodList() {\n    return $this->manifest['payment_method'];\n  }\n\n  /**\n   * Get payment method external ID used by payment provider\n   *\n   * @param $payment_method\n   *\n   * @return mixed|null\n   */\n  public function getMethodExternalId($payment_method) {\n    return $this->getMethodDetails($payment_method);\n  }\n\n}\n"
  },
  {
    "path": "classes/template.php",
    "content": "<?php\n/**\n*\n* @package phpBB3\n* @version $Id$\n* @copyright (c) 2005 phpBB Group, sections (c) 2001 ispi of Lincoln Inc\n* @license http://opensource.org/licenses/gpl-license.php GNU Public License\n*\n* Modified by Gorlum to work within http://supernova.ws\n*\n*/\n\n/**\n* @ignore\n*/\nif (!defined('INSIDE'))\n{\n  exit;\n}\n\n/**\n* Base Template class.\n* @package phpBB3\n*/\nclass template\n{\n  /** variable that holds all the data we'll be substituting into\n  * the compiled templates. Takes form:\n  * --> $this->_tpldata[block][iteration#][child][iteration#][child2][iteration#][variablename] == value\n  * if it's a root-level variable, it'll be like this:\n  * --> $this->_tpldata[.][0][varname] == value\n  */\n  var $_tpldata = array('.' => array(0 => array()));\n  var $_rootref;\n//  var $_block_counter = array();\n  var $_block_value = array();\n\n  // Root dir and hash of filenames for each template handle.\n  var $root = '';\n  var $cachepath = '';\n  var $files = array();\n  var $filename = array();\n  var $files_inherit = array();\n  var $files_template = array();\n  var $inherit_root = '';\n  var $orig_tpl_storedb;\n  var $orig_tpl_inherits_id;\n\n  // this will hash handle names to the compiled/uncompiled code for that handle.\n  var $compiled_code = array();\n\n  /**\n   * Is template already parsed with SN code?\n   *\n   * @var bool $parsed\n   */\n  var $parsed = false;\n\n  /**\n   * @var template_compile|null $compiler\n   */\n  var $compiler = null;\n\n  /**\n   * Physical root for template search\n   *\n   * @var string $rootPhysical\n   */\n  public $rootPhysical = '';\n\n  /**\n   * template constructor.\n   *\n   * @param string $rootPhysical - physical location of game root\n   */\n  public function __construct($rootPhysical = SN_ROOT_PHYSICAL) {\n    $this->rootPhysical = $rootPhysical;\n    $this->compiler = new template_compile($this);\n  }\n\n  /**\n  * Set template location\n  * @access public\n  */\n  function set_template()\n  {\n    global $user;\n\n    if (file_exists($this->rootPhysical. 'styles/' . $user->theme['template_path'] . '/template'))\n    {\n      $this->root = $this->rootPhysical . 'styles/' . $user->theme['template_path'] . '/template';\n      $this->cachepath = $this->rootPhysical . 'cache/tpl_' . str_replace('_', '-', $user->theme['template_path']) . '_';\n\n      if ($this->orig_tpl_storedb === null)\n      {\n        $this->orig_tpl_storedb = $user->theme['template_storedb'];\n      }\n\n      if ($this->orig_tpl_inherits_id === null)\n      {\n        $this->orig_tpl_inherits_id = $user->theme['template_inherits_id'];\n      }\n\n      $user->theme['template_storedb'] = $this->orig_tpl_storedb;\n      $user->theme['template_inherits_id'] = $this->orig_tpl_inherits_id;\n\n      if ($user->theme['template_inherits_id'])\n      {\n        $this->inherit_root = $this->rootPhysical . 'styles/' . $user->theme['template_inherit_path'] . '/template';\n      }\n    }\n    else\n    {\n      trigger_error('Template path could not be found: styles/' . $user->theme['template_path'] . '/template', E_USER_ERROR);\n    }\n\n    $this->_rootref = &$this->_tpldata['.'][0];\n\n    return true;\n  }\n\n  /**\n  * Set custom template location (able to use directory outside of phpBB)\n  * @access public\n  */\n  function set_custom_template($template_path, $template_name, $fallback_template_path = false)\n  {\n    global $user;\n\n    // Make sure $template_path has no ending slash\n    if (substr($template_path, -1) == '/')\n    {\n      $template_path = substr($template_path, 0, -1);\n    }\n\n    $this->root = $template_path;\n    $this->cachepath = $this->rootPhysical . 'cache/ctpl_' . str_replace('_', '-', $template_name) . '_';\n\n    if ($fallback_template_path !== false)\n    {\n      if (substr($fallback_template_path, -1) == '/')\n      {\n        $fallback_template_path = substr($fallback_template_path, 0, -1);\n      }\n\n      $this->inherit_root = $fallback_template_path;\n      $this->orig_tpl_inherits_id = true;\n    }\n    else\n    {\n      $this->orig_tpl_inherits_id = false;\n    }\n\n    // the database does not store the path or name of a custom template\n    // so there is no way we can properly store custom templates there\n    $this->orig_tpl_storedb = false;\n\n    $this->_rootref = &$this->_tpldata['.'][0];\n\n    return true;\n  }\n\n  /**\n  * Sets the template filenames for handles. $filename_array\n  * should be a hash of handle => filename pairs.\n  * @access public\n  */\n  function set_filenames($filename_array)\n  {\n    if (!is_array($filename_array))\n    {\n      return false;\n    }\n    foreach ($filename_array as $handle => $filename)\n    {\n      if (empty($filename))\n      {\n        trigger_error(\"template->set_filenames: Empty filename specified for $handle\", E_USER_ERROR);\n      }\n\n      $this->filename[$handle] = $filename;\n      $this->files[$handle] = $this->root . '/' . $filename;\n\n      if ($this->inherit_root)\n      {\n        $this->files_inherit[$handle] = $this->inherit_root . '/' . $filename;\n      }\n    }\n\n    return true;\n  }\n\n  /**\n  * Destroy template data set\n  * @access public\n  */\n  function destroy()\n  {\n    $this->_tpldata = array('.' => array(0 => array()));\n    $this->_rootref = &$this->_tpldata['.'][0];\n  }\n\n  /**\n  * Reset/empty complete block\n  * @access public\n  */\n  function destroy_block_vars($blockname)\n  {\n    if (strpos($blockname, '.') !== false)\n    {\n      // Nested block.\n      $blocks = explode('.', $blockname);\n      $blockcount = sizeof($blocks) - 1;\n\n      $str = &$this->_tpldata;\n      for ($i = 0; $i < $blockcount; $i++)\n      {\n        $str = &$str[$blocks[$i]];\n        $str = &$str[sizeof($str) - 1];\n      }\n\n      unset($str[$blocks[$blockcount]]);\n    }\n    else\n    {\n      // Top-level block.\n      unset($this->_tpldata[$blockname]);\n    }\n\n    return true;\n  }\n\n  /**\n  * Display handle\n  * @access public\n  */\n  function display($handle, $include_once = true)\n  {\n    if (defined('IN_ERROR_HANDLER'))\n    {\n      $is_enotice = error_reporting();\n      if ((E_NOTICE & $is_enotice) == E_NOTICE)\n      {\n        error_reporting(error_reporting() ^ E_NOTICE);\n      }\n    }\n\n    if ($filename = $this->_tpl_load($handle))\n    {\n      ($include_once) ? include_once($filename) : include($filename);\n    }\n    else\n    {\n      $this->evaluate($this->compiled_code[$handle]);\n    }\n\n    return true;\n  }\n\n  /**\n  * Display the handle and assign the output to a template variable or return the compiled result.\n  * @access public\n  */\n  function assign_display($handle, $template_var = '', $return_content = true, $include_once = false)\n  {\n    ob_start();\n    $this->display($handle, $include_once);\n    $contents = ob_get_clean();\n\n    if ($return_content)\n    {\n      return $contents;\n    }\n\n    $this->assign_var($template_var, $contents);\n\n    return true;\n  }\n\n  /**\n  * Load a compiled template if possible, if not, recompile it\n  * @access private\n  */\n  function _tpl_load(&$handle)\n  {\n    global $user, $config;\n\n    if (!isset($this->filename[$handle]))\n    {\n      trigger_error(\"template->_tpl_load(): No file specified for handle $handle\", E_USER_ERROR);\n    }\n\n    // reload these settings to have the values they had when this object was initialised\n    // using set_template or set_custom_template, they might otherwise have been overwritten\n    // by other template class instances in between.\n    //$user->theme['template_storedb'] = $this->orig_tpl_storedb;\n    //$user->theme['template_inherits_id'] = $this->orig_tpl_inherits_id;\n\n    $filename = $this->cachepath . str_replace('/', '.', $this->filename[$handle]) . DOT_PHP_EX;\n    //$this->files_template[$handle] = (isset($user->theme['template_id'])) ? $user->theme['template_id'] : 0;\n\n    $recompile = false;\n    if (!file_exists($filename) || @filesize($filename) === 0)\n    {\n      $recompile = true;\n    }\n    else if ($config->load_tplcompile)\n    {\n      // No way around it: we need to check inheritance here\n      if ($user->theme['template_inherits_id'] && !file_exists($this->files[$handle]))\n      {\n        $this->files[$handle] = $this->files_inherit[$handle];\n        $this->files_template[$handle] = $user->theme['template_inherits_id'];\n      }\n      $recompile = (@filemtime($filename) < filemtime($this->files[$handle])) ? true : false;\n    }\n\n    // Recompile page if the original template is newer, otherwise load the compiled version\n    if (!$recompile)\n    {\n      return $filename;\n    }\n\n    global $db;\n\n    // Inheritance - we point to another template file for this one. Equality is also used for store_db\n    if (isset($user->theme['template_inherits_id']) && $user->theme['template_inherits_id'] && !file_exists($this->files[$handle]))\n    {\n      $this->files[$handle] = $this->files_inherit[$handle];\n      $this->files_template[$handle] = $user->theme['template_inherits_id'];\n    }\n\n    $compile = $this->compiler;\n\n    // If we don't have a file assigned to this handle, die.\n    if (!isset($this->files[$handle]))\n    {\n      trigger_error(\"template->_tpl_load(): No file specified for handle $handle\", E_USER_ERROR);\n    }\n\n    // Just compile if no user object is present (happens within the installer)\n    if (!$user)\n    {\n      $compile->_tpl_load_file($handle);\n      return false;\n    }\n\n    if (isset($user->theme['template_storedb']) && $user->theme['template_storedb'])\n    {\n      $rows = array();\n      $ids = array();\n      // Inheritance\n      if (isset($user->theme['template_inherits_id']) && $user->theme['template_inherits_id'])\n      {\n        $ids[] = $user->theme['template_inherits_id'];\n      }\n      $ids[] = $user->theme['template_id'];\n\n      foreach ($ids as $id)\n      {\n        $sql = 'SELECT *\n        FROM ' . STYLES_TEMPLATE_DATA_TABLE . '\n        WHERE template_id = ' . $id . \"\n          AND (template_filename = '\" . $db->sql_escape($this->filename[$handle]) . \"'\n            OR template_included \" . $db->sql_like_expression($db->any_char . $this->filename[$handle] . ':' . $db->any_char) . ')';\n\n        $result = $db->sql_query($sql);\n        while ($row = $db->sql_fetchrow($result))\n        {\n          $rows[$row['template_filename']] = $row;\n        }\n        $db->sql_freeresult($result);\n      }\n\n      if (sizeof($rows))\n      {\n        foreach ($rows as $row)\n        {\n          $file = $this->root . '/' . $row['template_filename'];\n          $force_reload = false;\n          if ($row['template_id'] != $user->theme['template_id'])\n          {\n            // make sure that we are not overlooking a file not in the db yet\n            if (isset($user->theme['template_inherits_id']) && $user->theme['template_inherits_id'] && !file_exists($file))\n            {\n              $file = $this->inherit_root . '/' . $row['template_filename'];\n              $this->files[$row['template_filename']] = $file;\n              $this->files_inherit[$row['template_filename']] = $file;\n              $this->files_template[$row['template_filename']] = $user->theme['template_inherits_id'];\n            }\n            else if (isset($user->theme['template_inherits_id']) && $user->theme['template_inherits_id'])\n            {\n              // Ok, we have a situation. There is a file in the subtemplate, but nothing in the DB. We have to fix that.\n              $force_reload = true;\n              $this->files_template[$row['template_filename']] = $user->theme['template_inherits_id'];\n            }\n          }\n          else\n          {\n            $this->files_template[$row['template_filename']] = $user->theme['template_id'];\n          }\n\n          if ($force_reload || $row['template_mtime'] < filemtime($file))\n          {\n            if ($row['template_filename'] == $this->filename[$handle])\n            {\n              $compile->_tpl_load_file($handle, true);\n            }\n            else\n            {\n              $this->files[$row['template_filename']] = $file;\n              $this->filename[$row['template_filename']] = $row['template_filename'];\n              $compile->_tpl_load_file($row['template_filename'], true);\n              unset($this->compiled_code[$row['template_filename']]);\n              unset($this->files[$row['template_filename']]);\n              unset($this->filename[$row['template_filename']]);\n            }\n          }\n\n          if ($row['template_filename'] == $this->filename[$handle])\n          {\n            $this->compiled_code[$handle] = $compile->compile(trim($row['template_data']));\n            $compile->compile_write($handle, $this->compiled_code[$handle]);\n          }\n          else\n          {\n            // Only bother compiling if it doesn't already exist\n            if (!file_exists($this->cachepath . str_replace('/', '.', $row['template_filename']) . DOT_PHP_EX))\n            {\n              $this->filename[$row['template_filename']] = $row['template_filename'];\n              $compile->compile_write($row['template_filename'], $compile->compile(trim($row['template_data'])));\n              unset($this->filename[$row['template_filename']]);\n            }\n          }\n        }\n      }\n      else\n      {\n        $file = $this->root . '/' . $row['template_filename'];\n\n        if (isset($user->theme['template_inherits_id']) && $user->theme['template_inherits_id'] && !file_exists($file))\n        {\n          $file = $this->inherit_root . '/' . $row['template_filename'];\n          $this->files[$row['template_filename']] = $file;\n          $this->files_inherit[$row['template_filename']] = $file;\n          $this->files_template[$row['template_filename']] = $user->theme['template_inherits_id'];\n        }\n        // Try to load from filesystem and instruct to insert into the styles table...\n        $compile->_tpl_load_file($handle, true);\n        return false;\n      }\n\n      return false;\n    }\n\n    $compile->_tpl_load_file($handle);\n    return false;\n  }\n\n  /**\n  * Assign key variable pairs from an array\n  * @access public\n  */\n  function assign_vars($vararray)\n  {\n    foreach ($vararray as $key => $val)\n    {\n      $this->_rootref[$key] = $val;\n    }\n\n    return true;\n  }\n\n  /**\n  * Assign a single variable to a single key\n  * @access public\n  */\n  function assign_var($varname, $varval)\n  {\n    $this->_rootref[$varname] = $varval;\n\n    return true;\n  }\n\n  /**\n  * Assign key variable pairs from an array to a specified block\n  * @access public\n  */\n  function assign_block_vars($blockname, $vararray)\n  {\n    if (strpos($blockname, '.') !== false)\n    {\n      // Nested block.\n      $blocks = explode('.', $blockname);\n      $blockcount = sizeof($blocks) - 1;\n\n      $str = &$this->_tpldata;\n      for ($i = 0; $i < $blockcount; $i++)\n      {\n        $str = &$str[$blocks[$i]];\n        $str = &$str[sizeof($str) - 1];\n      }\n\n      $s_row_count = isset($str[$blocks[$blockcount]]) ? sizeof($str[$blocks[$blockcount]]) : 0;\n      $vararray['S_ROW_COUNT'] = $s_row_count;\n\n      // Assign S_FIRST_ROW\n      if (!$s_row_count)\n      {\n        $vararray['S_FIRST_ROW'] = true;\n      }\n\n      // Now the tricky part, we always assign S_LAST_ROW and remove the entry before\n      // This is much more clever than going through the complete template data on display (phew)\n      $vararray['S_LAST_ROW'] = true;\n      if ($s_row_count > 0)\n      {\n        unset($str[$blocks[$blockcount]][($s_row_count - 1)]['S_LAST_ROW']);\n      }\n\n      // Now we add the block that we're actually assigning to.\n      // We're adding a new iteration to this block with the given\n      // variable assignments.\n      $str[$blocks[$blockcount]][] = $vararray;\n    }\n    else\n    {\n      // Top-level block.\n      $s_row_count = (isset($this->_tpldata[$blockname])) ? sizeof($this->_tpldata[$blockname]) : 0;\n      $vararray['S_ROW_COUNT'] = $s_row_count;\n\n      // Assign S_FIRST_ROW\n      if (!$s_row_count)\n      {\n        $vararray['S_FIRST_ROW'] = true;\n      }\n\n      // We always assign S_LAST_ROW and remove the entry before\n      $vararray['S_LAST_ROW'] = true;\n      if ($s_row_count > 0)\n      {\n        unset($this->_tpldata[$blockname][($s_row_count - 1)]['S_LAST_ROW']);\n      }\n\n      // Add a new iteration to this block with the variable assignments we were given.\n      $this->_tpldata[$blockname][] = $vararray;\n    }\n\n    return true;\n  }\n\n  /**\n  * Change already assigned key variable pair (one-dimensional - single loop entry)\n  *\n  * An example of how to use this function:\n  * {@example alter_block_array.php}\n  *\n  * @param  string  $blockname  the blockname, for example 'loop'\n  * @param  array $vararray the var array to insert/add or merge\n  * @param  mixed $key    Key to search for\n  *\n  * array: KEY => VALUE [the key/value pair to search for within the loop to determine the correct position]\n  *\n  * int: Position [the position to change or insert at directly given]\n  *\n  * If key is false the position is set to 0\n  * If key is true the position is set to the last entry\n  *\n  * @param  string  $mode   Mode to execute (valid modes are 'insert' and 'change')\n  *\n  * If insert, the vararray is inserted at the given position (position counting from zero).\n  * If change, the current block gets merged with the vararray (resulting in new key/value pairs be added and existing keys be replaced by the new value).\n  *\n  * Since counting begins by zero, inserting at the last position will result in this array: array(vararray, last positioned array)\n  * and inserting at position 1 will result in this array: array(first positioned array, vararray, following vars)\n  *\n  * @return bool false on error, true on success\n  * @access public\n  */\n  function alter_block_array($blockname, $vararray, $key = false, $mode = 'insert')\n  {\n    if (strpos($blockname, '.') !== false)\n    {\n      // Nested blocks are not supported\n      return false;\n    }\n\n    // Change key to zero (change first position) if false and to last position if true\n    if ($key === false || $key === true)\n    {\n      $key = ($key === false) ? 0 : sizeof($this->_tpldata[$blockname]);\n    }\n\n    // Get correct position if array given\n    if (is_array($key))\n    {\n      // Search array to get correct position\n      list($search_key, $search_value) = @each($key);\n\n      $key = NULL;\n      foreach ($this->_tpldata[$blockname] as $i => $val_ary)\n      {\n        if ($val_ary[$search_key] === $search_value)\n        {\n          $key = $i;\n          break;\n        }\n      }\n\n      // key/value pair not found\n      if ($key === NULL)\n      {\n        return false;\n      }\n    }\n\n    // Insert Block\n    if ($mode == 'insert')\n    {\n      // Make sure we are not exceeding the last iteration\n      if ($key >= sizeof($this->_tpldata[$blockname]))\n      {\n        $key = sizeof($this->_tpldata[$blockname]);\n        unset($this->_tpldata[$blockname][($key - 1)]['S_LAST_ROW']);\n        $vararray['S_LAST_ROW'] = true;\n      }\n      else if ($key === 0)\n      {\n        unset($this->_tpldata[$blockname][0]['S_FIRST_ROW']);\n        $vararray['S_FIRST_ROW'] = true;\n      }\n\n      // Re-position template blocks\n      for ($i = sizeof($this->_tpldata[$blockname]); $i > $key; $i--)\n      {\n        $this->_tpldata[$blockname][$i] = $this->_tpldata[$blockname][$i-1];\n        $this->_tpldata[$blockname][$i]['S_ROW_COUNT'] = $i;\n      }\n\n      // Insert vararray at given position\n      $vararray['S_ROW_COUNT'] = $key;\n      $this->_tpldata[$blockname][$key] = $vararray;\n\n      return true;\n    }\n\n    // Which block to change?\n    if ($mode == 'change')\n    {\n      if ($key == sizeof($this->_tpldata[$blockname]))\n      {\n        $key--;\n      }\n\n      $this->_tpldata[$blockname][$key] = array_merge($this->_tpldata[$blockname][$key], $vararray);\n      return true;\n    }\n\n    return false;\n  }\n\n  /**\n  * Include a separate template\n  * @access private\n  */\n  function _tpl_include($filename, $include = true)\n  {\n    // This is used to access global vars\n    // global $lang, $config, $user; // Not needed!\n\n    $handle = $filename;\n    $this->filename[$handle] = $filename;\n    $this->files[$handle] = $this->root . '/' . $filename;\n    if ($this->inherit_root)\n    {\n      $this->files_inherit[$handle] = $this->inherit_root . '/' . $filename;\n    }\n\n    $filename = $this->_tpl_load($handle);\n\n    if ($include)\n    {\n\n      if ($filename)\n      {\n        include($filename);\n        return;\n      }\n      $this->evaluate($this->compiled_code[$handle]);\n    }\n  }\n\n  /**\n  * Include a php-file\n  * @access private\n  */\n  function _php_include($filename)\n  {\n    $file = $this->rootPhysical . $filename;\n\n    if (!file_exists($file))\n    {\n      // trigger_error cannot be used here, as the output already started\n      echo 'template->_php_include(): File ' . htmlspecialchars($file) . ' does not exist or is empty';\n      return;\n    }\n    include($file);\n  }\n\n  /**\n  * Assign key variable pairs from an array with block support\n  * @access public\n  */\n  function assign_recursive($values, $name = '')\n  {\n    if(isset($values['.']))\n    {\n      $values_extra = $values['.'];\n      unset($values['.']);\n    }\n\n    if(!$name)\n    {\n      $this->assign_vars($values);\n    }\n    else\n    {\n      $this->assign_block_vars($name, $values);\n    }\n\n    if(isset($values_extra))\n    {\n      foreach($values_extra as $sub_array_name => $sub_array)\n      {\n        $new_name = $name . ($name ? '.' : '') . $sub_array_name;\n        foreach($sub_array as $sub_element)\n        {\n          $this->assign_recursive($sub_element, $new_name);\n        }\n      }\n    }\n  }\n\n  /**\n   * This function will be called from compiled template to re-render variables - i.e. allow late binding of values aka accessing variable value by it's name in template var\n   *\n   * @param string $stringTag\n   *\n   * @return mixed|string\n   */\n  public function reRender($stringTag) {\n    $tplTag = new PTLTag($stringTag, $this);\n    $result = $tplTag->resolved;\n    $this->compiler->compile_var_tags($result);\n    $this->evaluate($result);\n\n    return $result;\n  }\n\n  protected function evaluate($code) {\n    // This is used to access global vars\n    // global $lang, $config, $user; // Not needed\n\n    eval(' ?>' . $code . '<?php ');\n  }\n\n}\n"
  },
  {
    "path": "classes/template_compile.php",
    "content": "<?php\n/**\n*\n* @package phpBB3\n* @version $Id$\n* @copyright (c) 2005 phpBB Group, sections (c) 2001 ispi of Lincoln Inc\n* @license http://opensource.org/licenses/gpl-license.php GNU Public License\n*\n* Modified by Gorlum to work within http://supernova.ws\n*\n*/\n\n/**\n* @ignore\n*/\nif (!defined('INSIDE'))\n{\n  exit;\n}\n\n/**\n* Extension of template class - Functions needed for compiling templates only.\n*\n* psoTFX, phpBB Development Team - Completion of file caching, decompilation\n* routines and implementation of conditionals/keywords and associated changes\n*\n* The interface was inspired by PHPLib templates,  and the template file (formats are\n* quite similar)\n*\n* The keyword/conditional implementation is currently based on sections of code from\n* the Smarty templating engine (c) 2001 ispi of Lincoln, Inc. which is released\n* (on its own and in whole) under the LGPL. Section 3 of the LGPL states that any code\n* derived from an LGPL application may be relicenced under the GPL, this applies\n* to this source\n*\n* DEFINE directive inspired by a request by Cyberalien\n*\n* @package phpBB3\n*/\nclass template_compile\n{\n  var $template;\n\n  // Various storage arrays\n  var $block_names = array();\n  var $block_else_level = array();\n\n  /**\n   * template_compile constructor.\n   *\n   * @param template $template\n   */\n  public function __construct($template) {\n    $this->template = $template;\n  }\n\n  /**\n  * Load template source from file\n  * @access private\n  */\n  function _tpl_load_file($handle, $store_in_db = false)\n  {\n    // Try and open template for read\n    if (!file_exists($this->template->files[$handle]))\n    {\n      if (!file_exists($this->template->files_inherit[$handle]))\n      {\n//        pdump($handle);\n////        global $debug;\n//        var_dump((debug_backtrace()));\n//        die();\n\n        SN::$gc->debug->warning(\"template->_tpl_load_file(): File {$this->template->files[$handle]} does not exist or is empty\");\n\n        return;\n        trigger_error(\"template->_tpl_load_file(): File {$this->template->files[$handle]} does not exist or is empty\", E_USER_ERROR);\n      }\n      else\n      {\n        $this->template->files[$handle] = $this->template->files_inherit[$handle];\n      }\n    }\n\n    $html = $this->minify(@file_get_contents($this->template->files[$handle]));\n\n    $this->template->compiled_code[$handle] = $this->compile(trim($html));\n\n    // Actually compile the code now.\n    $this->compile_write($handle, $this->template->compiled_code[$handle]);\n\n    // Store in database if required...\n    if ($store_in_db)\n    {\n//      global $db, $user;\n//\n//      $sql_ary = array(\n//        'template_id'     => $this->template->files_template[$handle],\n//        'template_filename'   => $this->template->filename[$handle],\n//        'template_included'   => '',\n//        'template_mtime'    => time(),\n//        'template_data'     => trim(@file_get_contents($this->template->files[$handle])),\n//      );\n//\n//      $sql = 'INSERT INTO ' . STYLES_TEMPLATE_DATA_TABLE . ' ' . $db->sql_build_array('INSERT', $sql_ary);\n//      $db->sql_query($sql);\n    }\n  }\n\n  /**\n  * Remove any PHP tags that do not belong, these regular expressions are derived from\n  * the ones that exist in zend_language_scanner.l\n  * @access private\n  */\n  function remove_php_tags(&$code)\n  {\n    // This matches the information gathered from the internal PHP lexer\n    $match = array(\n      '#<([\\?%])=?.*?\\1>#s',\n      '#<script\\s+language\\s*=\\s*([\"\\']?)php\\1\\s*>.*?</script\\s*>#s',\n      '#<\\?php(?:\\r\\n?|[ \\n\\t]).*?\\?>#s'\n    );\n\n    $code = preg_replace($match, '', $code);\n  }\n\n  /**\n  * The all seeing all doing compile method. Parts are inspired by or directly from Smarty\n  * @access private\n  */\n  function compile($code, $no_echo = false, $echo_var = '')\n  {\n    if ($echo_var)\n    {\n      global $$echo_var;\n    }\n\n    // Remove any \"loose\" php ... we want to give admins the ability\n    // to switch on/off PHP for a given template. Allowing unchecked\n    // php is a no-no. There is a potential issue here in that non-php\n    // content may be removed ... however designers should use entities\n    // if they wish to display < and >\n    $this->remove_php_tags($code);\n\n    // Pull out all block/statement level elements and separate plain text\n    preg_match_all('#<!-- PHP -->(.*?)<!-- ENDPHP -->#s', $code, $matches);\n    $php_blocks = $matches[1];\n    $code = preg_replace('#<!-- PHP -->.*?<!-- ENDPHP -->#s', '<!-- PHP -->', $code);\n\n    preg_match_all('#<!-- INCLUDE (\\{\\$?[A-Z0-9\\-_]+\\}|[a-zA-Z0-9\\_\\-\\+\\./]+) -->#', $code, $matches);\n    $include_blocks = $matches[1];\n    if($include_blocks)\n    {\n      foreach($include_blocks as &$included_file)\n      {\n        $included_file .= '.tpl.html';\n      }\n    }\n    $code = preg_replace('#<!-- INCLUDE (?:\\{\\$?[A-Z0-9\\-_]+\\}|[a-zA-Z0-9\\_\\-\\+\\./]+) -->#', '<!-- INCLUDE -->', $code);\n\n    preg_match_all('#<!-- INCLUDEPHP ([a-zA-Z0-9\\_\\-\\+\\./]+) -->#', $code, $matches);\n    $includephp_blocks = $matches[1];\n    $code = preg_replace('#<!-- INCLUDEPHP [a-zA-Z0-9\\_\\-\\+\\./]+ -->#', '<!-- INCLUDEPHP -->', $code);\n\n    preg_match_all('#<!-- ([^<].*?) (.*?)? ?-->#', $code, $blocks, PREG_SET_ORDER);\n\n    $text_blocks = preg_split('#<!-- [^<].*? (?:.*?)? ?-->#', $code);\n\n    for ($i = 0, $j = sizeof($text_blocks); $i < $j; $i++)\n    {\n      $this->compile_var_tags($text_blocks[$i]);\n    }\n    $compile_blocks = array();\n\n    for ($curr_tb = 0, $tb_size = sizeof($blocks); $curr_tb < $tb_size; $curr_tb++)\n    {\n      $block_val = &$blocks[$curr_tb];\n\n      switch ($block_val[1])\n      {\n        case 'BEGIN':\n          $this->block_else_level[] = false;\n          $compile_blocks[] = '<?php ' . $this->compile_tag_block($block_val[2]) . ' ?>';\n        break;\n\n        case 'BEGINELSE':\n          $this->block_else_level[sizeof($this->block_else_level) - 1] = true;\n          $compile_blocks[] = '<?php }} else { ?>';\n        break;\n\n        case 'END':\n          array_pop($this->block_names);\n          $compile_blocks[] = '<?php ' . ((array_pop($this->block_else_level)) ? '}' : '}}') . ' ?>';\n        break;\n\n        case 'IF':\n          $compile_blocks[] = '<?php ' . $this->compile_tag_if($block_val[2], false) . ' ?>';\n        break;\n\n        case 'ELSE':\n          $compile_blocks[] = '<?php } else { ?>';\n        break;\n\n        case 'ELSEIF':\n          $compile_blocks[] = '<?php ' . $this->compile_tag_if($block_val[2], true) . ' ?>';\n        break;\n\n        case 'ENDIF':\n          $compile_blocks[] = '<?php } ?>';\n        break;\n\n        case 'DEFINE':\n          $compile_blocks[] = '<?php ' . $this->compile_tag_define($block_val[2], true) . ' ?>';\n        break;\n\n        case 'UNDEFINE':\n          $compile_blocks[] = '<?php ' . $this->compile_tag_define($block_val[2], false) . ' ?>';\n        break;\n\n        case 'INCLUDE':\n          $temp = array_shift($include_blocks);\n\n          // Dynamic includes\n          // Cheap match rather than a full blown regexp, we already know\n          // the format of the input so just use string manipulation.\n          if ($temp[0] == '{')\n          {\n            $file = false;\n\n            if ($temp[1] == '$')\n            {\n              $var = substr($temp, 2, -1);\n              //$file = $this->template->_tpldata['DEFINE']['.'][$var];\n              $temp = \"\\$this->_tpldata['DEFINE']['.']['$var']\";\n            }\n            else\n            {\n              $var = substr($temp, 1, -1);\n              //$file = $this->template->_rootref[$var];\n              $temp = \"\\$this->_rootref['$var']\";\n            }\n          }\n          else\n          {\n            $file = $temp;\n          }\n\n          $compile_blocks[] = '<?php ' . $this->compile_tag_include($temp) . ' ?>';\n\n          // No point in checking variable includes\n          if ($file)\n          {\n            $this->template->_tpl_include($file, false);\n          }\n        break;\n\n        case 'INCLUDEPHP':\n          $compile_blocks[] = (SN::$config->tpl_allow_php) ? '<?php ' . $this->compile_tag_include_php(array_shift($includephp_blocks)) . ' ?>' : '';\n        break;\n\n        case 'PHP':\n          $compile_blocks[] = (SN::$config->tpl_allow_php) ? '<?php ' . array_shift($php_blocks) . ' ?>' : '';\n        break;\n\n        default:\n          $this->compile_var_tags($block_val[0]);\n          $trim_check = trim($block_val[0]);\n          $compile_blocks[] = (!$no_echo) ? ((!empty($trim_check)) ? $block_val[0] : '') : ((!empty($trim_check)) ? $block_val[0] : '');\n        break;\n      }\n    }\n\n    $template_php = '';\n    for ($i = 0, $size = sizeof($text_blocks); $i < $size; $i++)\n    {\n      $trim_check_text = trim($text_blocks[$i]);\n      $template_php .= (!$no_echo) ? (($trim_check_text != '') ? $text_blocks[$i] : '') . ((isset($compile_blocks[$i])) ? $compile_blocks[$i] : '') : (($trim_check_text != '') ? $text_blocks[$i] : '') . ((isset($compile_blocks[$i])) ? $compile_blocks[$i] : '');\n    }\n\n    // Remove unused opening/closing tags\n    $template_php = str_replace(' ?><?php ', ' ', $template_php);\n\n    // Now add a newline after each php closing tag which already has a newline\n    // PHP itself strips a newline if a closing tag is used (this is documented behaviour) and it is mostly not intended by style authors to remove newlines\n    $template_php = preg_replace('#\\?\\>([\\r\\n])#', '?>\\1\\1', $template_php);\n\n    // There will be a number of occasions where we switch into and out of\n    // PHP mode instantaneously. Rather than \"burden\" the parser with this\n    // we'll strip out such occurences, minimising such switching\n    if ($no_echo)\n    {\n      return \"\\$$echo_var .= '\" . $template_php . \"'\";\n    }\n\n    return $template_php;\n  }\n\n  /**\n  * Compile variables\n  * @access private\n  */\n  function compile_var_tags(&$text_blocks)\n  {\n    // including $lang variable\n    // global $lang, $config; // NOT NEDEED - $lang now is global!\n\n    // change template varrefs into PHP varrefs\n    $varrefs = array();\n\n    // This one will handle varrefs WITH namespaces\n    preg_match_all('#\\{((?:[a-z0-9\\-_]+\\.)+)(\\$)?([A-Z0-9\\-_]+)(?:(\\|.+?)*)?\\}#', $text_blocks, $varrefs, PREG_SET_ORDER);\n\n    foreach ($varrefs as $var_val)\n    {\n      $namespace = $var_val[1];\n      $varname = $var_val[3];\n      $new = $this->generate_block_varref($namespace, $varname, $var_val[2]);\n\n      if(!empty($var_val[4])) {\n        $new = \\Ptl\\PtlVariableDecorator::decorate($var_val[0], $new, $this->template);\n      }\n\n      $new = \"<?php echo $new; ?>\";\n\n      $text_blocks = str_replace($var_val[0], $new, $text_blocks);\n    }\n\n    // This will handle the remaining root-level varrefs\n\n    // Prefix R_ means \"render this block again\". Only one level of rendering supported to avoid circular references\n    if (strpos($text_blocks, '{R_') !== false) {\n      $text_blocks = preg_replace(/** @lang RegExp */'#\\{R_([a-zA-Z0-9\\-_\\.\\$\\[\\]]+)\\}#', /** @lang PHP */'<?php $this->reRender(\\'\\\\1\\'); ?>', $text_blocks);\n    }\n\n    // transform vars prefixed by I_ into skin-specific images with context\n    if (strpos($text_blocks, '{I_') !== false && is_callable(array('SkinV2', 'image_url'))) {\n      $text_blocks = preg_replace(/** @lang RegExp */'#\\{I_(.+?)\\}#', /** @lang PHP */'<?php echo SkinV2::image_url(\\'\\\\1\\', $this); ?>', $text_blocks);\n    }\n\n    // transform vars prefixed by C_ into global config value\n    if (strpos($text_blocks, '{C_') !== false)\n    {\n      $text_blocks = preg_replace(/** @lang RegExp */'#\\{C_([a-zA-Z0-9\\-_]+)\\[([a-zA-Z0-9\\-_]*?)\\]\\}#', /** @lang PHP */'<?php echo ((isset($this->_rootref[\\'C_\\\\1\\'][\\'\\\\2\\'])) ? $this->_rootref[\\'C_\\\\1\\'][\\'\\\\2\\'] : ((isset(SN::$config[\\'\\\\1\\'][\\'\\\\2\\'])) ? SN::$config[\\'\\\\1\\'][\\'\\\\2\\'] : \\'{ \\\\1[\\\\2] }\\')); ?>', $text_blocks);\n      $text_blocks = preg_replace(/** @lang RegExp */'#\\{C_([a-zA-Z0-9\\-_]+)\\}#', /** @lang PHP */'<?php echo ((isset($this->_rootref[\\'C_\\\\1\\'])) ? $this->_rootref[\\'C_\\\\1\\'] : ((isset(SN::$config[\\'\\\\1\\'])) ? SN::$config[\\'\\\\1\\'] : \\'{ C_\\\\1 }\\')); ?>', $text_blocks);\n    }\n    // transform vars prefixed by D_ into global defined constant\n    if (strpos($text_blocks, '{D_') !== false)\n    {\n      $text_blocks = preg_replace(/** @lang RegExp */'#\\{D_([a-zA-Z0-9\\-_]+)\\}#', /** @lang PHP */'<?php echo ((isset($this->_rootref[\\'D_\\\\1\\'])) ? $this->_rootref[\\'D_\\\\1\\'] : ((defined(\\'\\\\1\\')) ? \\\\1 : \\'{ D_\\\\1 }\\')); ?>', $text_blocks);\n    }\n    // transform vars prefixed by L_ into their language variable pendant if nothing is set within the tpldata array\n    if (strpos($text_blocks, '{L_') !== false)\n    {\n      $text_blocks = preg_replace(/** @lang RegExp */'#\\{L_([a-zA-Z0-9\\-_]+)\\[D_([a-zA-Z0-9\\-_]*?)\\]\\}#', /** @lang PHP */'<?php echo ((isset($this->_rootref[\\'L_\\\\1\\'][\\\\2])) ? $this->_rootref[\\'L_\\\\1\\'][\\\\2] : ((isset(SN::$lang[\\'\\\\1\\'][\\\\2])) ? SN::$lang[\\'\\\\1\\'][\\\\2] : \\'{ \\\\1[\\\\2] }\\')); ?>', $text_blocks);\n      $text_blocks = preg_replace(/** @lang RegExp */'#\\{L_([a-zA-Z0-9\\-_]+)\\[([a-zA-Z0-9\\-_]*?)\\]\\}#', /** @lang PHP */'<?php echo ((isset($this->_rootref[\\'L_\\\\1\\'][\\'\\\\2\\'])) ? $this->_rootref[\\'L_\\\\1\\'][\\'\\\\2\\'] : ((isset(SN::$lang[\\'\\\\1\\'][\\'\\\\2\\'])) ? SN::$lang[\\'\\\\1\\'][\\'\\\\2\\'] : \\'{ \\\\1[\\\\2] }\\')); ?>', $text_blocks);\n      $text_blocks = preg_replace(/** @lang RegExp */'#\\{L_([a-zA-Z0-9\\-_]+)\\}#', /** @lang PHP */'<?php echo ((isset($this->_rootref[\\'L_\\\\1\\'])) ? $this->_rootref[\\'L_\\\\1\\'] : ((isset(SN::$lang[\\'\\\\1\\'])) ? SN::$lang[\\'\\\\1\\'] : \\'{ L_\\\\1 }\\')); ?>', $text_blocks);\n    }\n\n    // Handle addslashed language variables prefixed with LA_\n    // If a template variable already exist, it will be used in favor of it...\n    if (strpos($text_blocks, '{LA_') !== false)\n    {\n      $text_blocks = preg_replace(/** @lang RegExp */'#\\{LA_([a-zA-Z0-9\\-_]+)\\}#', /** @lang PHP */'<?php echo ((isset($this->_rootref[\\'LA_\\\\1\\'])) ? $this->_rootref[\\'LA_\\\\1\\'] : ((isset($this->_rootref[\\'L_\\\\1\\'])) ? addslashes($this->_rootref[\\'L_\\\\1\\']) : ((isset(SN::$lang[\\'\\\\1\\'])) ? addslashes(SN::$lang[\\'\\\\1\\']) : \\'{ LA_\\\\1 }\\'))); ?>', $text_blocks);\n    }\n\n    // Handle remaining varrefs\n    $text_blocks = preg_replace(/** @lang RegExp */'#\\{([a-zA-Z0-9\\-_]+)\\}#', /** @lang PHP */'<?php echo (isset($this->_rootref[\\'\\\\1\\'])) ? $this->_rootref[\\'\\\\1\\'] : \\'\\'; ?>', $text_blocks);\n    $text_blocks = preg_replace(/** @lang RegExp */'#\\{\\$([a-zA-Z0-9\\-_]+)\\}#', /** @lang PHP */'<?php echo (isset($this->_tpldata[\\'DEFINE\\'][\\'.\\'][\\'\\\\1\\'])) ? $this->_tpldata[\\'DEFINE\\'][\\'.\\'][\\'\\\\1\\'] : \\'\\'; ?>', $text_blocks);\n\n    return;\n  }\n\n  /**\n  * Compile blocks\n  * @access private\n  */\n  function compile_tag_block($tag_args)\n  {\n    $no_nesting = false;\n\n    // Is the designer wanting to call another loop in a loop?\n    if (strpos($tag_args, '!') === 0)\n    {\n      // Count the number if ! occurrences (not allowed in vars)\n      $no_nesting = substr_count($tag_args, '!');\n      $tag_args = substr($tag_args, $no_nesting);\n    }\n\n    // Allow for control of looping (indexes start from zero):\n    // foo(2)    : Will start the loop on the 3rd entry\n    // foo(-2)   : Will start the loop two entries from the end\n    // foo(3,4)  : Will start the loop on the fourth entry and end it on the fifth\n    // foo(3,-4) : Will start the loop on the fourth entry and end it four from last\n    if (preg_match('#^([^()]*)\\(([\\-\\d]+)(?:,([\\-\\d]+))?\\)$#', $tag_args, $match))\n    {\n      $tag_args = $match[1];\n\n      if ($match[2] < 0)\n      {\n        $loop_start = '($_' . $tag_args . '_count ' . $match[2] . ' < 0 ? 0 : $_' . $tag_args . '_count ' . $match[2] . ')';\n      }\n      else\n      {\n        $loop_start = '($_' . $tag_args . '_count < ' . $match[2] . ' ? $_' . $tag_args . '_count : ' . $match[2] . ')';\n      }\n\n      if (strlen($match[3]) < 1 || $match[3] == -1)\n      {\n        $loop_end = '$_' . $tag_args . '_count';\n      }\n      else if ($match[3] >= 0)\n      {\n        $loop_end = '(' . ($match[3] + 1) . ' > $_' . $tag_args . '_count ? $_' . $tag_args . '_count : ' . ($match[3] + 1) . ')';\n      }\n      else //if ($match[3] < -1)\n      {\n        $loop_end = '$_' . $tag_args . '_count' . ($match[3] + 1);\n      }\n    }\n    else\n    {\n      $loop_start = 0;\n      $loop_end = '$_' . $tag_args . '_count';\n    }\n\n    $tag_template_php = '';\n    array_push($this->block_names, $tag_args);\n\n    if ($no_nesting !== false)\n    {\n      // We need to implode $no_nesting times from the end...\n      $block = array_slice($this->block_names, -$no_nesting);\n    }\n    else\n    {\n      $block = $this->block_names;\n    }\n\n    if (sizeof($block) < 2)\n    {\n      // Block is not nested.\n      $tag_template_php = '$_' . $tag_args . \"_count = (isset(\\$this->_tpldata['$tag_args'])) ? sizeof(\\$this->_tpldata['$tag_args']) : 0;\";\n      $varref = \"\\$this->_tpldata['$tag_args']\";\n    }\n    else\n    {\n      // This block is nested.\n      // Generate a namespace string for this block.\n      $namespace = implode('.', $block);\n\n      // Get a reference to the data array for this block that depends on the\n      // current indices of all parent blocks.\n      $varref = $this->generate_block_data_ref($namespace, false);\n\n      // Create the for loop code to iterate over this block.\n      $tag_template_php = '$_' . $tag_args . '_count = (isset(' . $varref . ')) ? sizeof(' . $varref . ') : 0;';\n    }\n\n    $tag_template_php .= 'if ($_' . $tag_args . '_count) {';\n\n    /**\n    * The following uses foreach for iteration instead of a for loop, foreach is faster but requires PHP to make a copy of the contents of the array which uses more memory\n    * <code>\n    * if (!$offset)\n    * {\n    *   $tag_template_php .= 'foreach (' . $varref . ' as $_' . $tag_args . '_i => $_' . $tag_args . '_val){';\n    * }\n    * </code>\n    */\n\n    $tag_template_php .= 'for ($_' . $tag_args . '_i = ' . $loop_start . '; $_' . $tag_args . '_i < ' . $loop_end . '; ++$_' . $tag_args . '_i){';\n//    $tag_template_php .= '$this->_block_counter[\"'. $tag_args . '\"] = $_' . $tag_args . '_i;';\n    $tag_template_php .= '$_'. $tag_args . '_val = &' . $varref . '[$_'. $tag_args. '_i];';\n    $tag_template_php .= '$this->_block_value[\"'. $tag_args . '\"] = &' . $varref . '[$_'. $tag_args. '_i];';\n\n    return $tag_template_php;\n  }\n\n  /**\n  * Compile IF tags - much of this is from Smarty with\n  * some adaptions for our block level methods\n  * @access private\n  */\n  function compile_tag_if($tag_args, $elseif)\n  {\n    // Tokenize args for 'if' tag.\n    preg_match_all('/(?:\n      \"[^\"\\\\\\\\]*(?:\\\\\\\\.[^\"\\\\\\\\]*)*\"         |\n      \\'[^\\'\\\\\\\\]*(?:\\\\\\\\.[^\\'\\\\\\\\]*)*\\'     |\n      [(),]                                  |\n      [^\\s(),]+)/x', $tag_args, $match);\n\n    $tokens = $match[0];\n    $is_arg_stack = array();\n\n    for ($i = 0, $size = sizeof($tokens); $i < $size; $i++)\n    {\n      $token = &$tokens[$i];\n\n      switch ($token)\n      {\n        case '!==':\n        case '===':\n        case '<<':\n        case '>>':\n        case '|':\n        case '^':\n        case '&':\n        case '~':\n        case ')':\n        case ',':\n        case '+':\n        case '-':\n        case '*':\n        case '/':\n        case '@':\n        break;\n\n        case '==':\n        case 'eq':\n          $token = '==';\n        break;\n\n        case '!=':\n        case '<>':\n        case 'ne':\n        case 'neq':\n          $token = '!=';\n        break;\n\n        case '<':\n        case 'lt':\n          $token = '<';\n        break;\n\n        case '<=':\n        case 'le':\n        case 'lte':\n          $token = '<=';\n        break;\n\n        case '>':\n        case 'gt':\n          $token = '>';\n        break;\n\n        case '>=':\n        case 'ge':\n        case 'gte':\n          $token = '>=';\n        break;\n\n        case '&&':\n        case 'and':\n          $token = '&&';\n        break;\n\n        case '||':\n        case 'or':\n          $token = '||';\n        break;\n\n        case '!':\n        case 'not':\n          $token = '!';\n        break;\n\n        case '%':\n        case 'mod':\n          $token = '%';\n        break;\n\n        case '(':\n          array_push($is_arg_stack, $i);\n        break;\n\n        case 'is':\n          $is_arg_start = ($tokens[$i-1] == ')') ? array_pop($is_arg_stack) : $i-1;\n          $is_arg = implode(' ', array_slice($tokens, $is_arg_start, $i - $is_arg_start));\n\n          $new_tokens = $this->_parse_is_expr($is_arg, array_slice($tokens, $i+1));\n\n          array_splice($tokens, $is_arg_start, sizeof($tokens), $new_tokens);\n\n          $i = $is_arg_start;\n\n        // no break\n\n        default:\n          if (preg_match('#^((?:[a-z0-9\\-_]+\\.)+)?(\\$)?(?=[A-Za-z])([A-Za-z0-9\\-_]+)#s', $token, $varrefs))\n          {\n            $token = (!empty($varrefs[1])) ? $this->generate_block_data_ref(substr($varrefs[1], 0, -1), true, $varrefs[2]) . '[\\'' . $varrefs[3] . '\\']' : (($varrefs[2]) ? '$this->_tpldata[\\'DEFINE\\'][\\'.\\'][\\'' . $varrefs[3] . '\\']' : '$this->_rootref[\\'' . $varrefs[3] . '\\']');\n          }\n          else if (preg_match('#^\\.((?:[a-z0-9\\-_]+\\.?)+)$#s', $token, $varrefs))\n          {\n            // Allow checking if loops are set with .loopname\n            // It is also possible to check the loop count by doing <!-- IF .loopname > 1 --> for example\n            $blocks = explode('.', $varrefs[1]);\n\n            // If the block is nested, we have a reference that we can grab.\n            // If the block is not nested, we just go and grab the block from _tpldata\n            if (sizeof($blocks) > 1)\n            {\n              $block = array_pop($blocks);\n              $namespace = implode('.', $blocks);\n              $varref = $this->generate_block_data_ref($namespace, true);\n\n              // Add the block reference for the last child.\n              $varref .= \"['\" . $block . \"']\";\n            }\n            else\n            {\n              $varref = '$this->_tpldata';\n\n              // Add the block reference for the last child.\n              $varref .= \"['\" . $blocks[0] . \"']\";\n            }\n            $token = \"(empty($varref) ? 0 : sizeof($varref))\";\n          }\n          else if (!empty($token))\n          {\n            $token = '(' . $token . ')';\n          }\n\n        break;\n      }\n    }\n\n    // If there are no valid tokens left or only control/compare characters left, we do skip this statement\n    if (!sizeof($tokens) || str_replace(array(' ', '=', '!', '<', '>', '&', '|', '%', '(', ')'), '', implode('', $tokens)) == '')\n    {\n      $tokens = array('false');\n    }\n    return (($elseif) ? '} else if (' : 'if (') . (implode(' ', $tokens) . ') { ');\n  }\n\n  /**\n  * Compile DEFINE tags\n  * @access private\n  */\n  function compile_tag_define($tag_args, $op)\n  {\n    preg_match('#^((?:[a-z0-9\\-_]+\\.)+)?\\$(?=[A-Z])([A-Z0-9_\\-]*)(?: = (\\'?)([^\\']*)(\\'?))?$#', $tag_args, $match);\n\n    if (empty($match[2]) || (!isset($match[4]) && $op))\n    {\n      return '';\n    }\n\n    if (!$op)\n    {\n      return 'unset(' . (($match[1]) ? $this->generate_block_data_ref(substr($match[1], 0, -1), true, true) . '[\\'' . $match[2] . '\\']' : '$this->_tpldata[\\'DEFINE\\'][\\'.\\'][\\'' . $match[2] . '\\']') . ');';\n    }\n\n    // Are we a string?\n    if ($match[3] && $match[5])\n    {\n      $match[4] = str_replace(array('\\\\\\'', '\\\\\\\\', '\\''), array('\\'', '\\\\', '\\\\\\''), $match[4]);\n\n      // Compile reference, we allow template variables in defines...\n      $match[4] = $this->compile($match[4]);\n\n      // Now replace the php code\n      $match[4] = \"'\" . str_replace(array('<?php echo ', '; ?>'), array(\"' . \", \" . '\"), $match[4]) . \"'\";\n    }\n    else\n    {\n      preg_match('#true|false|\\.#i', $match[4], $type);\n\n      switch (strtolower($type[0]))\n      {\n        case 'true':\n        case 'false':\n          $match[4] = strtoupper($match[4]);\n        break;\n\n        case '.':\n          $match[4] = doubleval($match[4]);\n        break;\n\n        default:\n          $match[4] = intval($match[4]);\n        break;\n      }\n    }\n\n    return (($match[1]) ? $this->generate_block_data_ref(substr($match[1], 0, -1), true, true) . '[\\'' . $match[2] . '\\']' : '$this->_tpldata[\\'DEFINE\\'][\\'.\\'][\\'' . $match[2] . '\\']') . ' = ' . $match[4] . ';';\n  }\n\n  /**\n  * Compile INCLUDE tag\n  * @access private\n  */\n  function compile_tag_include($tag_args)\n  {\n    // Process dynamic includes\n    if ($tag_args[0] == '$')\n    {\n      return \"if (isset($tag_args)) { \\$this->_tpl_include($tag_args); }\";\n    }\n\n    return \"\\$this->_tpl_include('$tag_args');\";\n  }\n\n  /**\n  * Compile INCLUDE_PHP tag\n  * @access private\n  */\n  function compile_tag_include_php($tag_args)\n  {\n    return \"\\$this->_php_include('$tag_args');\";\n  }\n\n  /**\n  * parse expression\n  * This is from Smarty\n  * @access private\n  */\n  function _parse_is_expr($is_arg, $tokens)\n  {\n    $expr_end = 0;\n    $negate_expr = false;\n\n    if (($first_token = array_shift($tokens)) == 'not')\n    {\n      $negate_expr = true;\n      $expr_type = array_shift($tokens);\n    }\n    else\n    {\n      $expr_type = $first_token;\n    }\n\n    switch ($expr_type)\n    {\n      case 'even':\n        if (@$tokens[$expr_end] == 'by')\n        {\n          $expr_end++;\n          $expr_arg = $tokens[$expr_end++];\n          $expr = \"!(($is_arg / $expr_arg) % $expr_arg)\";\n        }\n        else\n        {\n          $expr = \"!($is_arg & 1)\";\n        }\n      break;\n\n      case 'odd':\n        if (@$tokens[$expr_end] == 'by')\n        {\n          $expr_end++;\n          $expr_arg = $tokens[$expr_end++];\n          $expr = \"(($is_arg / $expr_arg) % $expr_arg)\";\n        }\n        else\n        {\n          $expr = \"($is_arg & 1)\";\n        }\n      break;\n\n      case 'div':\n        if (@$tokens[$expr_end] == 'by')\n        {\n          $expr_end++;\n          $expr_arg = $tokens[$expr_end++];\n          $expr = \"!($is_arg % $expr_arg)\";\n        }\n      break;\n    }\n\n    if ($negate_expr)\n    {\n      $expr = \"!($expr)\";\n    }\n\n    array_splice($tokens, 0, $expr_end, $expr);\n\n    return $tokens;\n  }\n\n  /**\n   * Generates a reference to the given variable inside the given (possibly nested)\n   * block namespace. This is a string of the form:\n   * ' . $this->_tpldata['parent'][$_parent_i]['$child1'][$_child1_i]['$child2'][$_child2_i]...['varname'] . '\n   * It's ready to be inserted into an \"echo\" line in one of the templates.\n   * NOTE: expects a trailing \".\" on the namespace.\n   *\n   * @param string $namespace\n   * @param string $varname\n   * @param bool   $defop\n   *\n   * @return string\n   */\n  private function generate_block_varref($namespace, $varname, $defop = false)\n  {\n    // Strip the trailing period.\n    $namespace = substr($namespace, 0, -1);\n\n    // Get a reference to the data block for this namespace.\n    $varref = $this->generate_block_data_ref($namespace, true, $defop);\n    // Prepend the necessary code to stick this in an echo line.\n\n    // Append the variable reference.\n    $varref .= \"['$varname']\";\n\n    return $varref;\n  }\n\n  /**\n  * Generates a reference to the array of data values for the given\n  * (possibly nested) block namespace. This is a string of the form:\n  * $this->_tpldata['parent'][$_parent_i]['$child1'][$_child1_i]['$child2'][$_child2_i]...['$childN']\n  *\n  * If $include_last_iterator is true, then [$_childN_i] will be appended to the form shown above.\n  * NOTE: does not expect a trailing \".\" on the blockname.\n  * @access private\n  */\n  function generate_block_data_ref($blockname, $include_last_iterator, $defop = false)\n  {\n    // Get an array of the blocks involved.\n    $blocks = explode('.', $blockname);\n    $blockcount = sizeof($blocks) - 1;\n\n    // DEFINE is not an element of any referenced variable, we must use _tpldata to access it\n    if ($defop)\n    {\n      $varref = '$this->_tpldata[\\'DEFINE\\']';\n      // Build up the string with everything but the last child.\n      for ($i = 0; $i < $blockcount; $i++)\n      {\n        $varref .= \"['\" . $blocks[$i] . \"'][\\$_\" . $blocks[$i] . '_i]';\n      }\n      // Add the block reference for the last child.\n      $varref .= \"['\" . $blocks[$blockcount] . \"']\";\n      // Add the iterator for the last child if requried.\n      if ($include_last_iterator)\n      {\n        $varref .= '[$_' . $blocks[$blockcount] . '_i]';\n      }\n      return $varref;\n    }\n    else if ($include_last_iterator)\n    {\n      return '$_'. $blocks[$blockcount] . '_val';\n    }\n    else\n    {\n      return '$_'. $blocks[$blockcount - 1] . '_val[\\''. $blocks[$blockcount]. '\\']';\n    }\n  }\n\n  /**\n  * Write compiled file to cache directory\n  * @access private\n  */\n  function compile_write($handle, $data)\n  {\n    $filename = $this->template->cachepath . str_replace('/', '.', $this->template->filename[$handle]) . DOT_PHP_EX;\n\n    $data = \"<?php if (!defined('INSIDE')) exit;\" . ((strpos($data, '<?php') === 0) ? substr($data, 5) : ' ?>' . $data);\n\n    if ($fp = @fopen($filename, 'wb'))\n    {\n      @flock($fp, LOCK_EX);\n      @fwrite ($fp, $data);\n      @flock($fp, LOCK_UN);\n      @fclose($fp);\n\n      //phpbb_chmod($filename, CHMOD_READ | CHMOD_WRITE);\n      chmod($filename, 0710);\n    }\n\n    return;\n  }\n\n  // Gorlum's minifier BOF\n  /**\n  * Minifies template w/i PHP code by removing extra spaces\n  * @access private\n  */\n  function minify($html)\n  {\n    if(!SN::$config->tpl_minifier)\n    {\n      return $html;\n    }\n\n    // TODO: Match <code> and <pre> too - in separate arrays\n    preg_match_all('/(<script[^>]*?>.*?<\\/script>)/si', $html, $pre);\n    $html = preg_replace('/(<script[^>]*?>.*?<\\/script>)/si', '#pre#', $html);\n    //$html = preg_replace('#<!-[^\\[].+->#', '', $html);\n    //$html = preg_replace('/[\\r\\n\\t]+/', ' ', $html);\n    $html = preg_replace('/>[\\s]*</', '><', $html); // Strip spacechars between tags\n    $html = preg_replace('/[\\s]+/', ' ', $html); // Replace several spacechars with one space\n    if(!empty($pre[0]))\n    {\n      foreach($pre[0] as $tag)\n      {\n        $tag = preg_replace('/^\\ *\\/\\/[^\\<]*?$/m', ' ', $tag); // Strips comments - except those that contains HTML comment inside\n        $tag = preg_replace('/[\\ \\t]{2,}/', ' ', $tag); // Replace several spaces by one\n        $tag = preg_replace('/\\s{2,}/', \"\\r\\n\", $tag); // Replace several linefeeds by one\n        $html = preg_replace('/#pre#/', $tag, $html,1);\n      }\n    }\n\n    return $html;\n  }\n  // Gorlum's minifier EOF\n\n}\n"
  },
  {
    "path": "common.php",
    "content": "<?php\n/*\n * common.php\n *\n * Common init file\n *\n * @version 1.1 Security checks by Gorlum for http://supernova.ws\n */\n\nuse DBAL\\db_mysql;\n\nrequire_once('includes/init.php');\n\nglobal $debug, $template_result, $user, $lang, $planetrow;\n\n// Напоминание для Администрации, что игра отключена\nif($template_result[F_GAME_DISABLE]) {\n  echo '<div class=\"global_admin_warning\">', $template_result[F_GAME_DISABLE_REASON], '</div>';\n}\nunset($disable_reason);\n\n\nif(defined('IN_ADMIN') && IN_ADMIN === true) {\n  lng_include('admin');\n} elseif(SN::$sys_user_logged_in) {\n  sys_user_vacation($user);\n\n  $planet_id = SetSelectedPlanet($user);\n\n  // TODO НЕ НУЖНО АЛЬЯНС КАЖДЫЙ РАЗ ОБНОВЛЯТЬ!!!\n  if($user['ally_id']) {\n    db_mysql::db_transaction_start();\n    \\Alliance\\Alliance::sn_ali_fill_user_ally($user);\n    if(!$user['ally']['player']['id']) {\n      // sn_sys_logout(false, true);\n      // core_auth::logout(false);\n      SN::$auth->logout(false);\n      $debug->error(\"User ID {$user['id']} has ally ID {$user['ally_id']} but no ally info\", 'User record error', 502);\n    }\n    // TODO UNCOMMENT\n    que_process($user['ally']['player']);\n    db_user_set_by_id($user['ally']['player']['id'], '`onlinetime` = ' . SN_TIME_NOW);\n    db_mysql::db_transaction_commit();\n  }\n\n\n  // TODO - в режиме эмуляции, на самом деле!\n  db_mysql::db_transaction_start();\n  $global_data = sys_o_get_updated($user['id'], $planet_id, SN_TIME_NOW);\n  db_mysql::db_transaction_commit();\n\n  $planetrow = $global_data['planet'];\n  if(!($planetrow && isset($planetrow['id']) && $planetrow['id'])) {\n    // sn_sys_logout(false, true);\n    // core_auth::logout(false);\n    SN::$auth->logout(false);\n    $debug->error(\"User ID {$user['id']} has no current planet and no homeworld\", 'User record error', 502);\n  }\n\n  $que = $global_data['que'];\n}\n\nrequire_once('includes/vars_menu.php');\n\nsys_user_options_unpack($user);\n\nglobal $sn_page_name, $sn_mvc;\nif(!empty($sn_mvc['pages'][INITIAL_PAGE][PAGE_OPTION_EARLY_HEADER])) {\n  $title = !empty($sn_mvc['pages'][INITIAL_PAGE][PAGE_OPTION_TITLE]) ? $sn_mvc['pages'][INITIAL_PAGE][PAGE_OPTION_TITLE] : '';\n  SnTemplate::renderHeader($page, $title, $template_result, false, $user, SN::$config, $lang, $planetrow);\n}\n"
  },
  {
    "path": "composer.json",
    "content": "{\n  \"config\": {\n    \"platform\": {\n      \"php\": \"7.4\"\n    },\n    \"allow-plugins\": {\n      \"php-http/discovery\": true\n    }\n  },\n  \"require\": {\n    \"ext-json\": \"*\",\n    \"ext-mysqli\": \"*\",\n    \"ext-curl\": \"*\",\n    \"ext-iconv\": \"*\",\n    \"ext-gd\": \"*\",\n    \"stil/gif-endec\": \"^0.2.0\"\n  },\n  \"require-dev\": {\n    \"ext-json\": \"*\",\n    \"ext-mysqli\": \"*\",\n    \"ext-curl\": \"*\",\n    \"ext-iconv\": \"*\",\n    \"ext-gd\": \"*\",\n    \"phpunit/phpunit\": \"5.*\",\n    \"mailgun/mailgun-php\": \"^3.0\",\n    \"symfony/http-client\": \"^5.4\",\n    \"nyholm/psr7\": \"^1.8\",\n    \"psr/log\": \"^1.1\"\n  }\n}\n"
  },
  {
    "path": "dark_matter.php",
    "content": "<?php\n\ninclude_once('common.' . substr(strrchr(__FILE__, '.'), 1));\n\nif(SN::$gc->modules->countModulesInGroup('payment') && !SN_GOOGLE) {\n  sys_redirect('metamatter.php');\n}\n\n$template = SnTemplate::gettemplate('dark_matter', true);\n\nlng_include('infos');\n$template->assign_vars(array(\n  'URL_DARK_MATTER' => SN::$config->url_dark_matter,\n  'DARK_MATTER_DESCRIPTION' => $lang['info'][RES_DARK_MATTER]['description'],\n\n  'PAYMENT_AVAILABLE' => SN::$gc->modules->countModulesInGroup('payment') && !SN_GOOGLE,\n\n  'PAGE_HEADER' =>$lang['sys_dark_matter'],\n));\n\nSnTemplate::display($template, $lang['sys_dark_matter']);\n"
  },
  {
    "path": "design/css/blitz_login_background.css",
    "content": "/* Smartphones (portrait) ----------- */\n@media only screen\nand (max-width : 320px) {\n  /* Styles */\n  body\n  {\n    background-image: none;\n  }\n}\n\n/* iPads (portrait and landscape) ----------- */\n@media only screen\nand (min-device-width : 768px)\nand (max-device-width : 1024px) {\n  /* Styles */\n  body\n  {\n    background-image: none;\n  }\n}\n\n/* iPads (landscape) ----------- */\n@media only screen\nand (min-device-width : 768px)\nand (max-device-width : 1024px)\nand (orientation : landscape) {\n  /* Styles */\n  body\n  {\n    background-image: none;\n  }\n}\n\n/* iPads (portrait) ----------- */\n@media only screen\nand (min-device-width : 768px)\nand (max-device-width : 1024px)\nand (orientation : portrait) {\n  /* Styles */\n  body\n  {\n    background-image: none;\n  }\n}\n\n/* Desktops and laptops medium-res */\n@media only screen\nand (min-width : 760px) {\n  /* Styles */\n  body\n  {\n      background-image: none;\n  }\n}\n\n/* Desktops and laptops ----------- */\n@media only screen\nand (min-width : 1224px) {\n  /* Styles */\n  body \n  {\n      background-image: url(../images/blitz_bg-unauthorized.jpg);\n      background-repeat: no-repeat;\n      background-size: 100% auto ;\n    background-position: bottom 0px right 0px;\n  }\n}\n\n/* Large screens ----------- */\n@media only screen\nand (min-width : 1824px) {\n  /* Styles */\n  body \n  {\n      background-image: url(../images/blitz_bg-unauthorized.jpg);\n      background-repeat: no-repeat;\n      background-size: 100% auto ;\n    background-position: bottom 0px right 0px;\n  }\n}\n"
  },
  {
    "path": "design/css/global-ie.css",
    "content": ".icon_alpha, .alpha75, .a75 /* Partially transparent background for over-planet-image icons */\n{\n  /* For IE 5.5 - 7. 0.75*255 = 191.25 ~ 0xC0 */\n  filter:progid:DXImageTransform.Microsoft.gradient(startColorstr=#C0000000, endColorstr=#C0000000);\n  /* For IE 8*/\n  -ms-filter: \"progid:DXImageTransform.Microsoft.gradient(startColorstr=#C0000000, endColorstr=#C0000000)\";\n}\n\n.alpha50\n{\n  /* For IE 5.5 - 7. 0.50*255 = 128 ~ 0x80 */\n  filter:progid:DXImageTransform.Microsoft.gradient(startColorstr=#80000000, endColorstr=#80000000);\n  /* For IE 8*/\n  -ms-filter: \"progid:DXImageTransform.Microsoft.gradient(startColorstr=#80000000, endColorstr=#80000000)\";\n}\n\n.alpha25\n{\n  /* For IE 5.5 - 7. 0.25*255 = 64 ~ 0x40 */\n  filter:progid:DXImageTransform.Microsoft.gradient(startColorstr=#40000000, endColorstr=#40000000);\n  /* For IE 8*/\n  -ms-filter: \"progid:DXImageTransform.Microsoft.gradient(startColorstr=#40000000, endColorstr=#40000000)\";\n}\n\n#top_avat\n{\n  margin-top:-36px;\n}\n"
  },
  {
    "path": "design/css/global.css",
    "content": "/* Boilerplate */\n\n/* Rid of annoying Chrome on Android FontBoositng featurebug */\nbody, body * {\n  max-height: 1000000em;\n}\n\nhtml, body {\n  height: 100%;\n  margin: 0;\n  padding: 0;\n  border: 0;\n}\n\nform, iframe {\n  margin: 0;\n  padding: 0;\n  border: 0;\n}\n\ntextarea {\n  resize: none;\n}\n\n/* System classes */\n.fl { /* Float left */\n  float: left;\n}\n\n.fr { /* Float right */\n  float: right;\n}\n\n.c_l { text-align: left; }\n.c_r { text-align: right; }\n.c_c { text-align: center; }\n.c_j { text-align: justify; }\n\n.hide { /* Hide */\n  display: none;\n}\n\n.strong { /* strong tag emulation */\n  font-weight: bold;\n}\n\n.debug {\n  text-align: left;\n  background-color: #111111;\n  color: #0A0;\n  font-family: Courier, monospace !important;\n  padding: 0.1em 0;\n  font-weight: 800;\n  font-size: 1em;\n}\n.benchmark {\n  color: white;\n  background-color: #111111;\n  font-size: 0.8em;\n  text-align: left;\n  font-weight: bold;\n  line-height: 1em;\n}\n/* Debug borders */\n.db1, .db2, .db3, .db4 {\n  border: 0.1em solid;\n}\n.db1 {\n  border-color: red;\n}\n.db2 {\n  border-color: yellow;\n}\n.db3 {\n  border-color: green;\n}\n.db4 {\n  border-color: blue;\n}\n\n\n.icon-info-cover {\n  display: inline-block;\n\n  width: 100%; /* 1.454545454545em; */\n  height: 100%; /* 1.454545454545em; */\n\n  background-image: url(../images/icon_help_32x32.png);\n  background-size: cover;\n}\n\n.icons {\n  width: 16px; /* 1.454545454545em; */\n  height: 16px; /* 1.454545454545em; */\n}\n/* Icon file */\n.icons { background-image: url(../images/icons.png); }\n.icon-plus {\n  background-position: 0 0;\n  display: inline-block; vertical-align: middle;\n}\n.icon-minus {\n  background-position: -16px 0;\n  display: inline-block; vertical-align: middle;\n}\n.icon-info {\n  background-position: -32px 0;\n  display: inline-block; vertical-align: middle;\n}\n.icon-cancel {\n  background-position: -48px 0;\n  display: inline-block; vertical-align: middle;\n}\n.icon-gather {\n  background-position: -64px 0;\n  display: inline-block; vertical-align: middle;\n}\n\n.icon_plus:after, .icon_minus:after, .icon_info:after, .icon_gather:after {\n  content: \" \";\n\n  display: inline-block;\n\n  position: absolute;\n  top:0;\n  left:0;\n\n  width: 100%;\n  height:100%;\n\n  background-size: contain;\n  background-image: url(\"../images/icon_plus.png\");\n}\n.icon_minus:after {\n  background-image: url(\"../images/icon_minus.png\");\n}\n.icon_info:after {\n  background-image: url(\"../images/icon_info.png\");\n}\n.icon_gather:after {\n  background-image: url(\"../images/icon_gather.png\");\n}\n\n.i_icon_mail, .i_icon_mail_userlist {\n  background-image: url(../images/icon_mail.gif);\n  background-size: contain;\n  width: 16px;\n  height: 16px;\n  display: inline-block;\n}\n.i_icon_delete_userlist {\n  background-image: url(../images/r1.png);\n  background-size: contain;\n  width: 16px;\n  height: 16px;\n  display: inline-block;\n}\n.i_icon_impers_userlist {\n  background-image: url(../images/icon_impersonate.gif);\n  background-size: contain;\n  width: 16px;\n  height: 16px;\n  display: inline-block;\n}\n.icon-info_empire {\n  background-position: -32px 0;\n  display: inline-block; vertical-align: middle;\n}\n\n/* Partially transparent background for over-planet-image icons */\n.icon_alpha, .alpha75, .a75 {\n  background: rgb(0, 0, 0); /* Fallback for web browsers that doesn't support RGBa */\n  background: rgba(0, 0, 0, 0.75); /* RGBa with 0.75 opacity */\n}\n.alpha50, .a50 {\n  background: rgb(0, 0, 0);\n  background: rgba(0, 0, 0, 0.50);\n}\n.alpha25, .a25 {\n  background: rgb(0, 0, 0);\n  background: rgba(0, 0, 0, 0.25);\n}\n\n/* Full element opacity with background */\n.opacity75, .o75 {\n  opacity: 0.75;\n  filter: alpha(opacity=75); /* For IE8 and earlier */\n}\n.opacity50, .o50 {\n  opacity: 0.50;\n  filter: alpha(opacity=50); /* For IE8 and earlier */\n}\n.opacity25, .o25 {\n  opacity: 0.25;\n  filter: alpha(opacity=25); /* For IE8 and earlier */\n}\n\n.percent33 {\n  width: 33%;\n  height: 33%;\n}\n.percent50 {\n  width: 50%;\n  height: 50%;\n}\n.percent66 {\n  width: 66%;\n  height: 66%;\n}\n.percent75 {\n  width: 75%;\n  height: 75%;\n}\n.percent100 {\n  width: 100%;\n  height: 100%;\n}\n\n.w100 {\n  width: 100%;\n}\n\n.h100 {\n  height: 100%;\n}\n\n.h2em {\n  height: 2em;\n}\n\n/* Absolute Positioning */\n\n/* Fill container - usually images */\n.posFill {\n  position: absolute;\n  left: 0;\n  top: 0;\n\n  width: 100%;\n  height: 100%;\n}\n/* Top of container, full width, centered */\n.posT {\n  position: absolute;\n  left: 0;\n  top: 0;\n\n  width: 100%;\n\n  text-align: center;\n  vertical-align: middle;\n}\n\n.posLT {\n  position: absolute;\n  left: 0;\n  top: 0;\n}\n\n.posRT {\n  position: absolute;\n  right: 0;\n  top: 0;\n}\n\n/* Bottom of container, full width, centered */\n.posB {\n  position: absolute;\n  left: 0;\n  bottom: 0;\n\n  width: 100%;\n\n  text-align: center;\n  vertical-align: middle;\n}\n\n.posLB {\n  position: absolute;\n  left: 0;\n  bottom: 0;\n}\n\n.posRB {\n  position: absolute;\n  right: 0;\n  bottom: 0;\n}\n\n/* Containers ======================================================================================================= */\n/* Container Flex - Base Flex Container */\n/* One-line vertically centered, space distributed non-wrapping  */\n.contF, .contFJ, .contFS, .contFC, .contFE {\n  display: flex;\n\n  justify-content: space-around;\n  align-content: space-around;\n\n  align-items: center;\n}\n\n/* Container Flex Justify */\n/* Replacement for float-left + float-right: Flex, non-wrapping, one-line vertically centered, item justified  */\n.contFJ {\n  text-align: left;\n  justify-content: space-between;\n}\n.contFS {\n  text-align: left;\n  justify-content: flex-start;\n}\n.contFE {\n  text-align: right;\n  justify-content: flex-end;\n}\n/* CONTainer Flex Center*/\n.contFC {\n  justify-content: center;\n  align-items: center;\n}\n\n.flexW {\n  flex-wrap: wrap;\n}\n\n/*.FW {*/\n  /*flex-wrap: wrap;*/\n/*}*/\n\n\nbody.blitz_ {\n  background-image: none;\n  background-color: black;\n  background-repeat: no-repeat;\n  background-size: 100% auto ;\n  background-position: bottom 0 right 0;\n}\n\n@media only screen and (max-width: 479px) {\n}\n\n@media only screen and (min-width: 480px) {\n  body.blitz_ {\n    background-image: url(../images/background_blitz_0500.jpg);\n  }\n}\n\n@media only screen and (min-width: 580px) {\n  body.blitz_ {\n    background-image: url(../images/background_blitz_0600.jpg);\n  }\n}\n\n/* Desktops and laptops medium-res */\n@media only screen and (min-width: 760px) {\n  body.blitz_ {\n    background-image: url(../images/background_blitz_0800.jpg);\n  }\n}\n\n/* Desktops and laptops ----------- */\n@media only screen and (min-width: 950px) {\n  body.blitz_ {\n    background-image: url(../images/background_blitz_1024.jpg);\n  }\n}\n\n@media only screen and (min-width: 1224px) {\n  body.blitz_ {\n    background-image: url(../images/background_blitz_1280.jpg);\n  }\n}\n\n/* Large screens ----------- */\n@media only screen and (min-width: 1550px) {\n  body.blitz_ {\n    background-image: url(../images/background_blitz_1600.jpg);\n  }\n}\n\n@media only screen and (min-width: 1824px) {\n  body.blitz_ {\n    background-image: url(../images/background_blitz_1920.jpg);\n  }\n}\n\n.image_close {\n  width: 1.454545em;\n  height: 1.454545em;\n}\n\n.underline {\n  text-decoration: underline;\n}\n\n.strike {\n  text-decoration: line-through;\n}\n\n.no_select {\n  -webkit-touch-callout: none; /* iOS Safari */\n  -webkit-user-select: none; /* Safari */\n  -moz-user-select: none; /* Firefox */\n  -ms-user-select: none; /* Internet Explorer/Edge */\n  user-select: none; /* Non-prefixed version, currently supported by Chrome and Opera */\n}\n\n/* General wrapper class  */\n.flexible_wrapper\n{\n  display: flex;\n  flex-wrap: nowrap;\n  white-space: nowrap;\n\n  flex-basis: 0.1%;\n  flex-grow: 1;\n  flex-shrink: 1;\n}\n/* Add 'wrap' class to break points  */\n.flexible_wrapper.wrap\n{\n  flex-wrap: wrap;\n}\n\n.xdebug-var-dump {\n  text-align: left;\n  font-size: 1.5rem;\n  background-color: #222;\n}\n\ntr.highlight:hover {\n  color: lightblue;\n  text-decoration: underline;\n}\n\ntr.highlight.notice:hover {\n   color: #d3ffe6;\n}\n\n.b-red {\n  border: 1px solid red;\n}\n.b-green {\n  border: 1px solid green;\n}\n.b-blue {\n  border: 1px solid blue;\n}\n.b-cyan {\n  border: 1px solid cyan;\n}\n.b-magenta {\n  border: 1px solid magenta;\n}\n.b-yellow {\n  border: 1px solid yellow;\n}\n.b-black {\n  border: 1px solid black;\n}\n.b-white {\n  border: 1px solid white;\n}\n.b-grey {\n  border: 1px solid grey;\n}\n.b-orange {\n  border: 1px solid orange;\n}\n\n\n/* ---------- FIX for Google Chrome v65 */\n#__content_cell\n{\n  overflow-x: auto;\n  overflow-y: hidden;\n}\n"
  },
  {
    "path": "design/css/index.html",
    "content": ""
  },
  {
    "path": "design/css/jquery-ui.css",
    "content": "/*! jQuery UI - v1.11.4 - 2017-02-16\n* http://jqueryui.com\n* Includes: core.css, draggable.css, resizable.css, sortable.css, accordion.css, button.css, dialog.css, menu.css, progressbar.css, slider.css, tabs.css, tooltip.css, theme.css\n* To view and modify this theme, visit http://jqueryui.com/themeroller/?ffDefault=Tahoma%2C%20sans-serif&fsDefault=11px&fwDefault=bold&cornerRadius=4px&bgColorHeader=%23dfeafd&bgTextureHeader=flat&borderColorHeader=%23415680&fcHeader=%2313233E&iconColorHeader=%2313233E&bgColorContent=%23344566&bgTextureContent=flat&borderColorContent=%23415680&fcContent=%23e6ebfb&iconColorContent=%23e6ebfb&bgColorDefault=%23222D42&bgTextureDefault=flat&borderColorDefault=%23415680&fcDefault=%23e6ebfb&iconColorDefault=%23e6ebfb&bgColorHover=%23415680&bgTextureHover=flat&borderColorHover=%23415680&fcHover=%23e6ebfb&iconColorHover=%23e6ebfb&bgColorActive=%23415680&bgTextureActive=flat&borderColorActive=%23415780&fcActive=%23e6ebfb&iconColorActive=%23e6ebfb&bgColorHighlight=%23415680&bgTextureHighlight=flat&borderColorHighlight=%23415780&fcHighlight=%23ecc504&iconColorHighlight=%232e83ff&bgColorError=%23415680&bgTextureError=flat&borderColorError=%23ff0000&fcError=%23ff0000&iconColorError=%23ff0000&bgColorOverlay=%23aaaaaa&bgTextureOverlay=flat&bgImgOpacityOverlay=0&opacityOverlay=30&bgColorShadow=%23aaaaaa&bgTextureShadow=flat&bgImgOpacityShadow=0&opacityShadow=30&thicknessShadow=8px&offsetTopShadow=-8px&offsetLeftShadow=-8px&cornerRadiusShadow=8px&bgImgOpacityHeader=100&bgImgOpacityContent=75&bgImgOpacityDefault=75&bgImgOpacityHover=25&bgImgOpacityActive=65&bgImgOpacityHighlight=100&bgImgOpacityError=95\n* Copyright jQuery Foundation and other contributors; Licensed MIT */\n\n/* Layout helpers\n----------------------------------*/\n.ui-helper-hidden {\n\tdisplay: none;\n}\n.ui-helper-hidden-accessible {\n\tborder: 0;\n\tclip: rect(0 0 0 0);\n\theight: 1px;\n\tmargin: -1px;\n\toverflow: hidden;\n\tpadding: 0;\n\tposition: absolute;\n\twidth: 1px;\n}\n.ui-helper-reset {\n\tmargin: 0;\n\tpadding: 0;\n\tborder: 0;\n\toutline: 0;\n\tline-height: 1.3;\n\ttext-decoration: none;\n\tfont-size: 100%;\n\tlist-style: none;\n}\n.ui-helper-clearfix:before,\n.ui-helper-clearfix:after {\n\tcontent: \"\";\n\tdisplay: table;\n\tborder-collapse: collapse;\n}\n.ui-helper-clearfix:after {\n\tclear: both;\n}\n.ui-helper-clearfix {\n\tmin-height: 0; /* support: IE7 */\n}\n.ui-helper-zfix {\n\twidth: 100%;\n\theight: 100%;\n\ttop: 0;\n\tleft: 0;\n\tposition: absolute;\n\topacity: 0;\n\tfilter:Alpha(Opacity=0); /* support: IE8 */\n}\n\n.ui-front {\n\tz-index: 100;\n}\n\n\n/* Interaction Cues\n----------------------------------*/\n.ui-state-disabled {\n\tcursor: default !important;\n}\n\n\n/* Icons\n----------------------------------*/\n\n/* states and images */\n.ui-icon {\n\tdisplay: block;\n\ttext-indent: -99999px;\n\toverflow: hidden;\n\tbackground-repeat: no-repeat;\n}\n\n\n/* Misc visuals\n----------------------------------*/\n\n/* Overlays */\n.ui-widget-overlay {\n\tposition: fixed;\n\ttop: 0;\n\tleft: 0;\n\twidth: 100%;\n\theight: 100%;\n}\n.ui-draggable-handle {\n\t-ms-touch-action: none;\n\ttouch-action: none;\n}\n.ui-resizable {\n\tposition: relative;\n}\n.ui-resizable-handle {\n\tposition: absolute;\n\tfont-size: 0.1px;\n\tdisplay: block;\n\t-ms-touch-action: none;\n\ttouch-action: none;\n}\n.ui-resizable-disabled .ui-resizable-handle,\n.ui-resizable-autohide .ui-resizable-handle {\n\tdisplay: none;\n}\n.ui-resizable-n {\n\tcursor: n-resize;\n\theight: 7px;\n\twidth: 100%;\n\ttop: -5px;\n\tleft: 0;\n}\n.ui-resizable-s {\n\tcursor: s-resize;\n\theight: 7px;\n\twidth: 100%;\n\tbottom: -5px;\n\tleft: 0;\n}\n.ui-resizable-e {\n\tcursor: e-resize;\n\twidth: 7px;\n\tright: -5px;\n\ttop: 0;\n\theight: 100%;\n}\n.ui-resizable-w {\n\tcursor: w-resize;\n\twidth: 7px;\n\tleft: -5px;\n\ttop: 0;\n\theight: 100%;\n}\n.ui-resizable-se {\n\tcursor: se-resize;\n\twidth: 12px;\n\theight: 12px;\n\tright: 1px;\n\tbottom: 1px;\n}\n.ui-resizable-sw {\n\tcursor: sw-resize;\n\twidth: 9px;\n\theight: 9px;\n\tleft: -5px;\n\tbottom: -5px;\n}\n.ui-resizable-nw {\n\tcursor: nw-resize;\n\twidth: 9px;\n\theight: 9px;\n\tleft: -5px;\n\ttop: -5px;\n}\n.ui-resizable-ne {\n\tcursor: ne-resize;\n\twidth: 9px;\n\theight: 9px;\n\tright: -5px;\n\ttop: -5px;\n}\n.ui-sortable-handle {\n\t-ms-touch-action: none;\n\ttouch-action: none;\n}\n.ui-accordion .ui-accordion-header {\n\tdisplay: block;\n\tcursor: pointer;\n\tposition: relative;\n\tmargin: 2px 0 0 0;\n\tpadding: .5em .5em .5em .7em;\n\tmin-height: 0; /* support: IE7 */\n\tfont-size: 100%;\n}\n.ui-accordion .ui-accordion-icons {\n\tpadding-left: 2.2em;\n}\n.ui-accordion .ui-accordion-icons .ui-accordion-icons {\n\tpadding-left: 2.2em;\n}\n.ui-accordion .ui-accordion-header .ui-accordion-header-icon {\n\tposition: absolute;\n\tleft: .5em;\n\ttop: 50%;\n\tmargin-top: -8px;\n}\n.ui-accordion .ui-accordion-content {\n\tpadding: 1em 2.2em;\n\tborder-top: 0;\n\toverflow: auto;\n}\n.ui-button {\n\tdisplay: inline-block;\n\tposition: relative;\n\tpadding: 0;\n\tline-height: normal;\n\tmargin-right: .1em;\n\tcursor: pointer;\n\tvertical-align: middle;\n\ttext-align: center;\n\toverflow: visible; /* removes extra width in IE */\n}\n.ui-button,\n.ui-button:link,\n.ui-button:visited,\n.ui-button:hover,\n.ui-button:active {\n\ttext-decoration: none;\n}\n/* to make room for the icon, a width needs to be set here */\n.ui-button-icon-only {\n\twidth: 2.2em;\n}\n/* button elements seem to need a little more width */\nbutton.ui-button-icon-only {\n\twidth: 2.4em;\n}\n.ui-button-icons-only {\n\twidth: 3.4em;\n}\nbutton.ui-button-icons-only {\n\twidth: 3.7em;\n}\n\n/* button text element */\n.ui-button .ui-button-text {\n\tdisplay: block;\n\tline-height: normal;\n}\n.ui-button-text-only .ui-button-text {\n\tpadding: .4em 1em;\n}\n.ui-button-icon-only .ui-button-text,\n.ui-button-icons-only .ui-button-text {\n\tpadding: .4em;\n\ttext-indent: -9999999px;\n}\n.ui-button-text-icon-primary .ui-button-text,\n.ui-button-text-icons .ui-button-text {\n\tpadding: .4em 1em .4em 2.1em;\n}\n.ui-button-text-icon-secondary .ui-button-text,\n.ui-button-text-icons .ui-button-text {\n\tpadding: .4em 2.1em .4em 1em;\n}\n.ui-button-text-icons .ui-button-text {\n\tpadding-left: 2.1em;\n\tpadding-right: 2.1em;\n}\n/* no icon support for input elements, provide padding by default */\ninput.ui-button {\n\tpadding: .4em 1em;\n}\n\n/* button icon element(s) */\n.ui-button-icon-only .ui-icon,\n.ui-button-text-icon-primary .ui-icon,\n.ui-button-text-icon-secondary .ui-icon,\n.ui-button-text-icons .ui-icon,\n.ui-button-icons-only .ui-icon {\n\tposition: absolute;\n\ttop: 50%;\n\tmargin-top: -8px;\n}\n.ui-button-icon-only .ui-icon {\n\tleft: 50%;\n\tmargin-left: -8px;\n}\n.ui-button-text-icon-primary .ui-button-icon-primary,\n.ui-button-text-icons .ui-button-icon-primary,\n.ui-button-icons-only .ui-button-icon-primary {\n\tleft: .5em;\n}\n.ui-button-text-icon-secondary .ui-button-icon-secondary,\n.ui-button-text-icons .ui-button-icon-secondary,\n.ui-button-icons-only .ui-button-icon-secondary {\n\tright: .5em;\n}\n\n/* button sets */\n.ui-buttonset {\n\tmargin-right: 7px;\n}\n.ui-buttonset .ui-button {\n\tmargin-left: 0;\n\tmargin-right: -.3em;\n}\n\n/* workarounds */\n/* reset extra padding in Firefox, see h5bp.com/l */\ninput.ui-button::-moz-focus-inner,\nbutton.ui-button::-moz-focus-inner {\n\tborder: 0;\n\tpadding: 0;\n}\n.ui-dialog {\n\toverflow: hidden;\n\tposition: absolute;\n\ttop: 0;\n\tleft: 0;\n\tpadding: .2em;\n\toutline: 0;\n}\n.ui-dialog .ui-dialog-titlebar {\n\tpadding: .4em 1em;\n\tposition: relative;\n}\n.ui-dialog .ui-dialog-title {\n\tfloat: left;\n\tmargin: .1em 0;\n\twhite-space: nowrap;\n\twidth: 90%;\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n}\n.ui-dialog .ui-dialog-titlebar-close {\n\tposition: absolute;\n\tright: .3em;\n\ttop: 50%;\n\twidth: 20px;\n\tmargin: -10px 0 0 0;\n\tpadding: 1px;\n\theight: 20px;\n}\n.ui-dialog .ui-dialog-content {\n\tposition: relative;\n\tborder: 0;\n\tpadding: .5em 1em;\n\tbackground: none;\n\toverflow: auto;\n}\n.ui-dialog .ui-dialog-buttonpane {\n\ttext-align: left;\n\tborder-width: 1px 0 0 0;\n\tbackground-image: none;\n\tmargin-top: .5em;\n\tpadding: .3em 1em .5em .4em;\n}\n.ui-dialog .ui-dialog-buttonpane .ui-dialog-buttonset {\n\tfloat: right;\n}\n.ui-dialog .ui-dialog-buttonpane button {\n\tmargin: .5em .4em .5em 0;\n\tcursor: pointer;\n}\n.ui-dialog .ui-resizable-se {\n\twidth: 12px;\n\theight: 12px;\n\tright: -5px;\n\tbottom: -5px;\n\tbackground-position: 16px 16px;\n}\n.ui-draggable .ui-dialog-titlebar {\n\tcursor: move;\n}\n.ui-menu {\n\tlist-style: none;\n\tpadding: 0;\n\tmargin: 0;\n\tdisplay: block;\n\toutline: none;\n}\n.ui-menu .ui-menu {\n\tposition: absolute;\n}\n.ui-menu .ui-menu-item {\n\tposition: relative;\n\tmargin: 0;\n\tpadding: 3px 1em 3px .4em;\n\tcursor: pointer;\n\tmin-height: 0; /* support: IE7 */\n\t/* support: IE10, see #8844 */\n\tlist-style-image: url(\"data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7\");\n}\n.ui-menu .ui-menu-divider {\n\tmargin: 5px 0;\n\theight: 0;\n\tfont-size: 0;\n\tline-height: 0;\n\tborder-width: 1px 0 0 0;\n}\n.ui-menu .ui-state-focus,\n.ui-menu .ui-state-active {\n\tmargin: -1px;\n}\n\n/* icon support */\n.ui-menu-icons {\n\tposition: relative;\n}\n.ui-menu-icons .ui-menu-item {\n\tpadding-left: 2em;\n}\n\n/* left-aligned */\n.ui-menu .ui-icon {\n\tposition: absolute;\n\ttop: 0;\n\tbottom: 0;\n\tleft: .2em;\n\tmargin: auto 0;\n}\n\n/* right-aligned */\n.ui-menu .ui-menu-icon {\n\tleft: auto;\n\tright: 0;\n}\n.ui-progressbar {\n\theight: 2em;\n\ttext-align: left;\n\toverflow: hidden;\n}\n.ui-progressbar .ui-progressbar-value {\n\tmargin: -1px;\n\theight: 100%;\n}\n.ui-progressbar .ui-progressbar-overlay {\n\tbackground: url(\"data:image/gif;base64,R0lGODlhKAAoAIABAAAAAP///yH/C05FVFNDQVBFMi4wAwEAAAAh+QQJAQABACwAAAAAKAAoAAACkYwNqXrdC52DS06a7MFZI+4FHBCKoDeWKXqymPqGqxvJrXZbMx7Ttc+w9XgU2FB3lOyQRWET2IFGiU9m1frDVpxZZc6bfHwv4c1YXP6k1Vdy292Fb6UkuvFtXpvWSzA+HycXJHUXiGYIiMg2R6W459gnWGfHNdjIqDWVqemH2ekpObkpOlppWUqZiqr6edqqWQAAIfkECQEAAQAsAAAAACgAKAAAApSMgZnGfaqcg1E2uuzDmmHUBR8Qil95hiPKqWn3aqtLsS18y7G1SzNeowWBENtQd+T1JktP05nzPTdJZlR6vUxNWWjV+vUWhWNkWFwxl9VpZRedYcflIOLafaa28XdsH/ynlcc1uPVDZxQIR0K25+cICCmoqCe5mGhZOfeYSUh5yJcJyrkZWWpaR8doJ2o4NYq62lAAACH5BAkBAAEALAAAAAAoACgAAAKVDI4Yy22ZnINRNqosw0Bv7i1gyHUkFj7oSaWlu3ovC8GxNso5fluz3qLVhBVeT/Lz7ZTHyxL5dDalQWPVOsQWtRnuwXaFTj9jVVh8pma9JjZ4zYSj5ZOyma7uuolffh+IR5aW97cHuBUXKGKXlKjn+DiHWMcYJah4N0lYCMlJOXipGRr5qdgoSTrqWSq6WFl2ypoaUAAAIfkECQEAAQAsAAAAACgAKAAAApaEb6HLgd/iO7FNWtcFWe+ufODGjRfoiJ2akShbueb0wtI50zm02pbvwfWEMWBQ1zKGlLIhskiEPm9R6vRXxV4ZzWT2yHOGpWMyorblKlNp8HmHEb/lCXjcW7bmtXP8Xt229OVWR1fod2eWqNfHuMjXCPkIGNileOiImVmCOEmoSfn3yXlJWmoHGhqp6ilYuWYpmTqKUgAAIfkECQEAAQAsAAAAACgAKAAAApiEH6kb58biQ3FNWtMFWW3eNVcojuFGfqnZqSebuS06w5V80/X02pKe8zFwP6EFWOT1lDFk8rGERh1TTNOocQ61Hm4Xm2VexUHpzjymViHrFbiELsefVrn6XKfnt2Q9G/+Xdie499XHd2g4h7ioOGhXGJboGAnXSBnoBwKYyfioubZJ2Hn0RuRZaflZOil56Zp6iioKSXpUAAAh+QQJAQABACwAAAAAKAAoAAACkoQRqRvnxuI7kU1a1UU5bd5tnSeOZXhmn5lWK3qNTWvRdQxP8qvaC+/yaYQzXO7BMvaUEmJRd3TsiMAgswmNYrSgZdYrTX6tSHGZO73ezuAw2uxuQ+BbeZfMxsexY35+/Qe4J1inV0g4x3WHuMhIl2jXOKT2Q+VU5fgoSUI52VfZyfkJGkha6jmY+aaYdirq+lQAACH5BAkBAAEALAAAAAAoACgAAAKWBIKpYe0L3YNKToqswUlvznigd4wiR4KhZrKt9Upqip61i9E3vMvxRdHlbEFiEXfk9YARYxOZZD6VQ2pUunBmtRXo1Lf8hMVVcNl8JafV38aM2/Fu5V16Bn63r6xt97j09+MXSFi4BniGFae3hzbH9+hYBzkpuUh5aZmHuanZOZgIuvbGiNeomCnaxxap2upaCZsq+1kAACH5BAkBAAEALAAAAAAoACgAAAKXjI8By5zf4kOxTVrXNVlv1X0d8IGZGKLnNpYtm8Lr9cqVeuOSvfOW79D9aDHizNhDJidFZhNydEahOaDH6nomtJjp1tutKoNWkvA6JqfRVLHU/QUfau9l2x7G54d1fl995xcIGAdXqMfBNadoYrhH+Mg2KBlpVpbluCiXmMnZ2Sh4GBqJ+ckIOqqJ6LmKSllZmsoq6wpQAAAh+QQJAQABACwAAAAAKAAoAAAClYx/oLvoxuJDkU1a1YUZbJ59nSd2ZXhWqbRa2/gF8Gu2DY3iqs7yrq+xBYEkYvFSM8aSSObE+ZgRl1BHFZNr7pRCavZ5BW2142hY3AN/zWtsmf12p9XxxFl2lpLn1rseztfXZjdIWIf2s5dItwjYKBgo9yg5pHgzJXTEeGlZuenpyPmpGQoKOWkYmSpaSnqKileI2FAAACH5BAkBAAEALAAAAAAoACgAAAKVjB+gu+jG4kORTVrVhRlsnn2dJ3ZleFaptFrb+CXmO9OozeL5VfP99HvAWhpiUdcwkpBH3825AwYdU8xTqlLGhtCosArKMpvfa1mMRae9VvWZfeB2XfPkeLmm18lUcBj+p5dnN8jXZ3YIGEhYuOUn45aoCDkp16hl5IjYJvjWKcnoGQpqyPlpOhr3aElaqrq56Bq7VAAAOw==\");\n\theight: 100%;\n\tfilter: alpha(opacity=25); /* support: IE8 */\n\topacity: 0.25;\n}\n.ui-progressbar-indeterminate .ui-progressbar-value {\n\tbackground-image: none;\n}\n.ui-slider {\n\tposition: relative;\n\ttext-align: left;\n}\n.ui-slider .ui-slider-handle {\n\tposition: absolute;\n\tz-index: 2;\n\twidth: 1.2em;\n\theight: 1.2em;\n\tcursor: default;\n\t-ms-touch-action: none;\n\ttouch-action: none;\n}\n.ui-slider .ui-slider-range {\n\tposition: absolute;\n\tz-index: 1;\n\tfont-size: .7em;\n\tdisplay: block;\n\tborder: 0;\n\tbackground-position: 0 0;\n}\n\n/* support: IE8 - See #6727 */\n.ui-slider.ui-state-disabled .ui-slider-handle,\n.ui-slider.ui-state-disabled .ui-slider-range {\n\tfilter: inherit;\n}\n\n.ui-slider-horizontal {\n\theight: .8em;\n}\n.ui-slider-horizontal .ui-slider-handle {\n\ttop: -.3em;\n\tmargin-left: -.6em;\n}\n.ui-slider-horizontal .ui-slider-range {\n\ttop: 0;\n\theight: 100%;\n}\n.ui-slider-horizontal .ui-slider-range-min {\n\tleft: 0;\n}\n.ui-slider-horizontal .ui-slider-range-max {\n\tright: 0;\n}\n\n.ui-slider-vertical {\n\twidth: .8em;\n\theight: 100px;\n}\n.ui-slider-vertical .ui-slider-handle {\n\tleft: -.3em;\n\tmargin-left: 0;\n\tmargin-bottom: -.6em;\n}\n.ui-slider-vertical .ui-slider-range {\n\tleft: 0;\n\twidth: 100%;\n}\n.ui-slider-vertical .ui-slider-range-min {\n\tbottom: 0;\n}\n.ui-slider-vertical .ui-slider-range-max {\n\ttop: 0;\n}\n.ui-tabs {\n\tposition: relative;/* position: relative prevents IE scroll bug (element with position: relative inside container with overflow: auto appear as \"fixed\") */\n\tpadding: .2em;\n}\n.ui-tabs .ui-tabs-nav {\n\tmargin: 0;\n\tpadding: .2em .2em 0;\n}\n.ui-tabs .ui-tabs-nav li {\n\tlist-style: none;\n\tfloat: left;\n\tposition: relative;\n\ttop: 0;\n\tmargin: 1px .2em 0 0;\n\tborder-bottom-width: 0;\n\tpadding: 0;\n\twhite-space: nowrap;\n}\n.ui-tabs .ui-tabs-nav .ui-tabs-anchor {\n\tfloat: left;\n\tpadding: .5em 1em;\n\ttext-decoration: none;\n}\n.ui-tabs .ui-tabs-nav li.ui-tabs-active {\n\tmargin-bottom: -1px;\n\tpadding-bottom: 1px;\n}\n.ui-tabs .ui-tabs-nav li.ui-tabs-active .ui-tabs-anchor,\n.ui-tabs .ui-tabs-nav li.ui-state-disabled .ui-tabs-anchor,\n.ui-tabs .ui-tabs-nav li.ui-tabs-loading .ui-tabs-anchor {\n\tcursor: text;\n}\n.ui-tabs-collapsible .ui-tabs-nav li.ui-tabs-active .ui-tabs-anchor {\n\tcursor: pointer;\n}\n.ui-tabs .ui-tabs-panel {\n\tdisplay: block;\n\tborder-width: 0;\n\tpadding: 1em 1.4em;\n\tbackground: none;\n}\n.ui-tooltip {\n\tpadding: 8px;\n\tposition: absolute;\n\tz-index: 9999;\n\tmax-width: 300px;\n\t-webkit-box-shadow: 0 0 5px #aaa;\n\tbox-shadow: 0 0 5px #aaa;\n}\nbody .ui-tooltip {\n\tborder-width: 2px;\n}\n\n/* Component containers\n----------------------------------*/\n.ui-widget {\n\tfont-family: Tahoma, sans-serif;\n\tfont-size: 11px;\n}\n.ui-widget .ui-widget {\n\tfont-size: 1em;\n}\n.ui-widget input,\n.ui-widget select,\n.ui-widget textarea,\n.ui-widget button {\n\tfont-family: Tahoma, sans-serif;\n\tfont-size: 1em;\n}\n.ui-widget-content {\n\tborder: 1px solid #415680;\n\tbackground: #344566;\n\tcolor: #e6ebfb;\n}\n.ui-widget-content a {\n\tcolor: #e6ebfb;\n}\n.ui-widget-header {\n\tborder: 1px solid #415680;\n\tbackground: #dfeafd;\n\tcolor: #13233E;\n\tfont-weight: bold;\n}\n.ui-widget-header a {\n\tcolor: #13233E;\n}\n\n/* Interaction states\n----------------------------------*/\n.ui-state-default,\n.ui-widget-content .ui-state-default,\n.ui-widget-header .ui-state-default {\n\tborder: 1px solid #415680;\n\tbackground: #222D42;\n\tfont-weight: bold;\n\tcolor: #e6ebfb;\n}\n.ui-state-default a,\n.ui-state-default a:link,\n.ui-state-default a:visited {\n\tcolor: #e6ebfb;\n\ttext-decoration: none;\n}\n.ui-state-hover,\n.ui-widget-content .ui-state-hover,\n.ui-widget-header .ui-state-hover,\n.ui-state-focus,\n.ui-widget-content .ui-state-focus,\n.ui-widget-header .ui-state-focus {\n\tborder: 1px solid #415680;\n\tbackground: #415680;\n\tfont-weight: bold;\n\tcolor: #e6ebfb;\n}\n.ui-state-hover a,\n.ui-state-hover a:hover,\n.ui-state-hover a:link,\n.ui-state-hover a:visited,\n.ui-state-focus a,\n.ui-state-focus a:hover,\n.ui-state-focus a:link,\n.ui-state-focus a:visited {\n\tcolor: #e6ebfb;\n\ttext-decoration: none;\n}\n.ui-state-active,\n.ui-widget-content .ui-state-active,\n.ui-widget-header .ui-state-active {\n\tborder: 1px solid #415780;\n\tbackground: #415680;\n\tfont-weight: bold;\n\tcolor: #e6ebfb;\n}\n.ui-state-active a,\n.ui-state-active a:link,\n.ui-state-active a:visited {\n\tcolor: #e6ebfb;\n\ttext-decoration: none;\n}\n\n/* Interaction Cues\n----------------------------------*/\n.ui-state-highlight,\n.ui-widget-content .ui-state-highlight,\n.ui-widget-header .ui-state-highlight {\n\tborder: 1px solid #415780;\n\tbackground: #415680;\n\tcolor: #ecc504;\n}\n.ui-state-highlight a,\n.ui-widget-content .ui-state-highlight a,\n.ui-widget-header .ui-state-highlight a {\n\tcolor: #ecc504;\n}\n.ui-state-error,\n.ui-widget-content .ui-state-error,\n.ui-widget-header .ui-state-error {\n\tborder: 1px solid #ff0000;\n\tbackground: #415680;\n\tcolor: #ff0000;\n}\n.ui-state-error a,\n.ui-widget-content .ui-state-error a,\n.ui-widget-header .ui-state-error a {\n\tcolor: #ff0000;\n}\n.ui-state-error-text,\n.ui-widget-content .ui-state-error-text,\n.ui-widget-header .ui-state-error-text {\n\tcolor: #ff0000;\n}\n.ui-priority-primary,\n.ui-widget-content .ui-priority-primary,\n.ui-widget-header .ui-priority-primary {\n\tfont-weight: bold;\n}\n.ui-priority-secondary,\n.ui-widget-content .ui-priority-secondary,\n.ui-widget-header .ui-priority-secondary {\n\topacity: .7;\n\tfilter:Alpha(Opacity=70); /* support: IE8 */\n\tfont-weight: normal;\n}\n.ui-state-disabled,\n.ui-widget-content .ui-state-disabled,\n.ui-widget-header .ui-state-disabled {\n\topacity: .35;\n\tfilter:Alpha(Opacity=35); /* support: IE8 */\n\tbackground-image: none;\n}\n.ui-state-disabled .ui-icon {\n\tfilter:Alpha(Opacity=35); /* support: IE8 - See #6059 */\n}\n\n/* Icons\n----------------------------------*/\n\n/* states and images */\n.ui-icon {\n\twidth: 16px;\n\theight: 16px;\n}\n.ui-icon,\n.ui-widget-content .ui-icon {\n\tbackground-image: url(\"images/ui-icons_e6ebfb_256x240.png\");\n}\n.ui-widget-header .ui-icon {\n\tbackground-image: url(\"images/ui-icons_13233E_256x240.png\");\n}\n.ui-state-default .ui-icon {\n\tbackground-image: url(\"images/ui-icons_e6ebfb_256x240.png\");\n}\n.ui-state-hover .ui-icon,\n.ui-state-focus .ui-icon {\n\tbackground-image: url(\"images/ui-icons_e6ebfb_256x240.png\");\n}\n.ui-state-active .ui-icon {\n\tbackground-image: url(\"images/ui-icons_e6ebfb_256x240.png\");\n}\n.ui-state-highlight .ui-icon {\n\tbackground-image: url(\"images/ui-icons_2e83ff_256x240.png\");\n}\n.ui-state-error .ui-icon,\n.ui-state-error-text .ui-icon {\n\tbackground-image: url(\"images/ui-icons_ff0000_256x240.png\");\n}\n\n/* positioning */\n.ui-icon-blank { background-position: 16px 16px; }\n.ui-icon-carat-1-n { background-position: 0 0; }\n.ui-icon-carat-1-ne { background-position: -16px 0; }\n.ui-icon-carat-1-e { background-position: -32px 0; }\n.ui-icon-carat-1-se { background-position: -48px 0; }\n.ui-icon-carat-1-s { background-position: -64px 0; }\n.ui-icon-carat-1-sw { background-position: -80px 0; }\n.ui-icon-carat-1-w { background-position: -96px 0; }\n.ui-icon-carat-1-nw { background-position: -112px 0; }\n.ui-icon-carat-2-n-s { background-position: -128px 0; }\n.ui-icon-carat-2-e-w { background-position: -144px 0; }\n.ui-icon-triangle-1-n { background-position: 0 -16px; }\n.ui-icon-triangle-1-ne { background-position: -16px -16px; }\n.ui-icon-triangle-1-e { background-position: -32px -16px; }\n.ui-icon-triangle-1-se { background-position: -48px -16px; }\n.ui-icon-triangle-1-s { background-position: -64px -16px; }\n.ui-icon-triangle-1-sw { background-position: -80px -16px; }\n.ui-icon-triangle-1-w { background-position: -96px -16px; }\n.ui-icon-triangle-1-nw { background-position: -112px -16px; }\n.ui-icon-triangle-2-n-s { background-position: -128px -16px; }\n.ui-icon-triangle-2-e-w { background-position: -144px -16px; }\n.ui-icon-arrow-1-n { background-position: 0 -32px; }\n.ui-icon-arrow-1-ne { background-position: -16px -32px; }\n.ui-icon-arrow-1-e { background-position: -32px -32px; }\n.ui-icon-arrow-1-se { background-position: -48px -32px; }\n.ui-icon-arrow-1-s { background-position: -64px -32px; }\n.ui-icon-arrow-1-sw { background-position: -80px -32px; }\n.ui-icon-arrow-1-w { background-position: -96px -32px; }\n.ui-icon-arrow-1-nw { background-position: -112px -32px; }\n.ui-icon-arrow-2-n-s { background-position: -128px -32px; }\n.ui-icon-arrow-2-ne-sw { background-position: -144px -32px; }\n.ui-icon-arrow-2-e-w { background-position: -160px -32px; }\n.ui-icon-arrow-2-se-nw { background-position: -176px -32px; }\n.ui-icon-arrowstop-1-n { background-position: -192px -32px; }\n.ui-icon-arrowstop-1-e { background-position: -208px -32px; }\n.ui-icon-arrowstop-1-s { background-position: -224px -32px; }\n.ui-icon-arrowstop-1-w { background-position: -240px -32px; }\n.ui-icon-arrowthick-1-n { background-position: 0 -48px; }\n.ui-icon-arrowthick-1-ne { background-position: -16px -48px; }\n.ui-icon-arrowthick-1-e { background-position: -32px -48px; }\n.ui-icon-arrowthick-1-se { background-position: -48px -48px; }\n.ui-icon-arrowthick-1-s { background-position: -64px -48px; }\n.ui-icon-arrowthick-1-sw { background-position: -80px -48px; }\n.ui-icon-arrowthick-1-w { background-position: -96px -48px; }\n.ui-icon-arrowthick-1-nw { background-position: -112px -48px; }\n.ui-icon-arrowthick-2-n-s { background-position: -128px -48px; }\n.ui-icon-arrowthick-2-ne-sw { background-position: -144px -48px; }\n.ui-icon-arrowthick-2-e-w { background-position: -160px -48px; }\n.ui-icon-arrowthick-2-se-nw { background-position: -176px -48px; }\n.ui-icon-arrowthickstop-1-n { background-position: -192px -48px; }\n.ui-icon-arrowthickstop-1-e { background-position: -208px -48px; }\n.ui-icon-arrowthickstop-1-s { background-position: -224px -48px; }\n.ui-icon-arrowthickstop-1-w { background-position: -240px -48px; }\n.ui-icon-arrowreturnthick-1-w { background-position: 0 -64px; }\n.ui-icon-arrowreturnthick-1-n { background-position: -16px -64px; }\n.ui-icon-arrowreturnthick-1-e { background-position: -32px -64px; }\n.ui-icon-arrowreturnthick-1-s { background-position: -48px -64px; }\n.ui-icon-arrowreturn-1-w { background-position: -64px -64px; }\n.ui-icon-arrowreturn-1-n { background-position: -80px -64px; }\n.ui-icon-arrowreturn-1-e { background-position: -96px -64px; }\n.ui-icon-arrowreturn-1-s { background-position: -112px -64px; }\n.ui-icon-arrowrefresh-1-w { background-position: -128px -64px; }\n.ui-icon-arrowrefresh-1-n { background-position: -144px -64px; }\n.ui-icon-arrowrefresh-1-e { background-position: -160px -64px; }\n.ui-icon-arrowrefresh-1-s { background-position: -176px -64px; }\n.ui-icon-arrow-4 { background-position: 0 -80px; }\n.ui-icon-arrow-4-diag { background-position: -16px -80px; }\n.ui-icon-extlink { background-position: -32px -80px; }\n.ui-icon-newwin { background-position: -48px -80px; }\n.ui-icon-refresh { background-position: -64px -80px; }\n.ui-icon-shuffle { background-position: -80px -80px; }\n.ui-icon-transfer-e-w { background-position: -96px -80px; }\n.ui-icon-transferthick-e-w { background-position: -112px -80px; }\n.ui-icon-folder-collapsed { background-position: 0 -96px; }\n.ui-icon-folder-open { background-position: -16px -96px; }\n.ui-icon-document { background-position: -32px -96px; }\n.ui-icon-document-b { background-position: -48px -96px; }\n.ui-icon-note { background-position: -64px -96px; }\n.ui-icon-mail-closed { background-position: -80px -96px; }\n.ui-icon-mail-open { background-position: -96px -96px; }\n.ui-icon-suitcase { background-position: -112px -96px; }\n.ui-icon-comment { background-position: -128px -96px; }\n.ui-icon-person { background-position: -144px -96px; }\n.ui-icon-print { background-position: -160px -96px; }\n.ui-icon-trash { background-position: -176px -96px; }\n.ui-icon-locked { background-position: -192px -96px; }\n.ui-icon-unlocked { background-position: -208px -96px; }\n.ui-icon-bookmark { background-position: -224px -96px; }\n.ui-icon-tag { background-position: -240px -96px; }\n.ui-icon-home { background-position: 0 -112px; }\n.ui-icon-flag { background-position: -16px -112px; }\n.ui-icon-calendar { background-position: -32px -112px; }\n.ui-icon-cart { background-position: -48px -112px; }\n.ui-icon-pencil { background-position: -64px -112px; }\n.ui-icon-clock { background-position: -80px -112px; }\n.ui-icon-disk { background-position: -96px -112px; }\n.ui-icon-calculator { background-position: -112px -112px; }\n.ui-icon-zoomin { background-position: -128px -112px; }\n.ui-icon-zoomout { background-position: -144px -112px; }\n.ui-icon-search { background-position: -160px -112px; }\n.ui-icon-wrench { background-position: -176px -112px; }\n.ui-icon-gear { background-position: -192px -112px; }\n.ui-icon-heart { background-position: -208px -112px; }\n.ui-icon-star { background-position: -224px -112px; }\n.ui-icon-link { background-position: -240px -112px; }\n.ui-icon-cancel { background-position: 0 -128px; }\n.ui-icon-plus { background-position: -16px -128px; }\n.ui-icon-plusthick { background-position: -32px -128px; }\n.ui-icon-minus { background-position: -48px -128px; }\n.ui-icon-minusthick { background-position: -64px -128px; }\n.ui-icon-close { background-position: -80px -128px; }\n.ui-icon-closethick { background-position: -96px -128px; }\n.ui-icon-key { background-position: -112px -128px; }\n.ui-icon-lightbulb { background-position: -128px -128px; }\n.ui-icon-scissors { background-position: -144px -128px; }\n.ui-icon-clipboard { background-position: -160px -128px; }\n.ui-icon-copy { background-position: -176px -128px; }\n.ui-icon-contact { background-position: -192px -128px; }\n.ui-icon-image { background-position: -208px -128px; }\n.ui-icon-video { background-position: -224px -128px; }\n.ui-icon-script { background-position: -240px -128px; }\n.ui-icon-alert { background-position: 0 -144px; }\n.ui-icon-info { background-position: -16px -144px; }\n.ui-icon-notice { background-position: -32px -144px; }\n.ui-icon-help { background-position: -48px -144px; }\n.ui-icon-check { background-position: -64px -144px; }\n.ui-icon-bullet { background-position: -80px -144px; }\n.ui-icon-radio-on { background-position: -96px -144px; }\n.ui-icon-radio-off { background-position: -112px -144px; }\n.ui-icon-pin-w { background-position: -128px -144px; }\n.ui-icon-pin-s { background-position: -144px -144px; }\n.ui-icon-play { background-position: 0 -160px; }\n.ui-icon-pause { background-position: -16px -160px; }\n.ui-icon-seek-next { background-position: -32px -160px; }\n.ui-icon-seek-prev { background-position: -48px -160px; }\n.ui-icon-seek-end { background-position: -64px -160px; }\n.ui-icon-seek-start { background-position: -80px -160px; }\n/* ui-icon-seek-first is deprecated, use ui-icon-seek-start instead */\n.ui-icon-seek-first { background-position: -80px -160px; }\n.ui-icon-stop { background-position: -96px -160px; }\n.ui-icon-eject { background-position: -112px -160px; }\n.ui-icon-volume-off { background-position: -128px -160px; }\n.ui-icon-volume-on { background-position: -144px -160px; }\n.ui-icon-power { background-position: 0 -176px; }\n.ui-icon-signal-diag { background-position: -16px -176px; }\n.ui-icon-signal { background-position: -32px -176px; }\n.ui-icon-battery-0 { background-position: -48px -176px; }\n.ui-icon-battery-1 { background-position: -64px -176px; }\n.ui-icon-battery-2 { background-position: -80px -176px; }\n.ui-icon-battery-3 { background-position: -96px -176px; }\n.ui-icon-circle-plus { background-position: 0 -192px; }\n.ui-icon-circle-minus { background-position: -16px -192px; }\n.ui-icon-circle-close { background-position: -32px -192px; }\n.ui-icon-circle-triangle-e { background-position: -48px -192px; }\n.ui-icon-circle-triangle-s { background-position: -64px -192px; }\n.ui-icon-circle-triangle-w { background-position: -80px -192px; }\n.ui-icon-circle-triangle-n { background-position: -96px -192px; }\n.ui-icon-circle-arrow-e { background-position: -112px -192px; }\n.ui-icon-circle-arrow-s { background-position: -128px -192px; }\n.ui-icon-circle-arrow-w { background-position: -144px -192px; }\n.ui-icon-circle-arrow-n { background-position: -160px -192px; }\n.ui-icon-circle-zoomin { background-position: -176px -192px; }\n.ui-icon-circle-zoomout { background-position: -192px -192px; }\n.ui-icon-circle-check { background-position: -208px -192px; }\n.ui-icon-circlesmall-plus { background-position: 0 -208px; }\n.ui-icon-circlesmall-minus { background-position: -16px -208px; }\n.ui-icon-circlesmall-close { background-position: -32px -208px; }\n.ui-icon-squaresmall-plus { background-position: -48px -208px; }\n.ui-icon-squaresmall-minus { background-position: -64px -208px; }\n.ui-icon-squaresmall-close { background-position: -80px -208px; }\n.ui-icon-grip-dotted-vertical { background-position: 0 -224px; }\n.ui-icon-grip-dotted-horizontal { background-position: -16px -224px; }\n.ui-icon-grip-solid-vertical { background-position: -32px -224px; }\n.ui-icon-grip-solid-horizontal { background-position: -48px -224px; }\n.ui-icon-gripsmall-diagonal-se { background-position: -64px -224px; }\n.ui-icon-grip-diagonal-se { background-position: -80px -224px; }\n\n\n/* Misc visuals\n----------------------------------*/\n\n/* Corner radius */\n.ui-corner-all,\n.ui-corner-top,\n.ui-corner-left,\n.ui-corner-tl {\n\tborder-top-left-radius: 4px;\n}\n.ui-corner-all,\n.ui-corner-top,\n.ui-corner-right,\n.ui-corner-tr {\n\tborder-top-right-radius: 4px;\n}\n.ui-corner-all,\n.ui-corner-bottom,\n.ui-corner-left,\n.ui-corner-bl {\n\tborder-bottom-left-radius: 4px;\n}\n.ui-corner-all,\n.ui-corner-bottom,\n.ui-corner-right,\n.ui-corner-br {\n\tborder-bottom-right-radius: 4px;\n}\n\n/* Overlays */\n.ui-widget-overlay {\n\tbackground: #aaaaaa;\n\topacity: .3;\n\tfilter: Alpha(Opacity=30); /* support: IE8 */\n}\n.ui-widget-shadow {\n\tmargin: -8px 0 0 -8px;\n\tpadding: 8px;\n\tbackground: #aaaaaa;\n\topacity: .3;\n\tfilter: Alpha(Opacity=30); /* support: IE8 */\n\tborder-radius: 8px;\n}\n"
  },
  {
    "path": "design/css/login.css",
    "content": "/* OK =============================================================================================================== */\nbody.blitz.body-auth {\n  background-image: none;\n  /*background-color: black;*/\n  background-repeat: no-repeat;\n  /*background-size: auto 100%;*/\n  background-size: cover;\n  background-position: bottom 0 right 0;\n}\n\nbody.body-auth {\n  /*background-color: black;*/\n  background-image: none;\n}\n\n@media only screen and (max-width: 479px) {\n}\n\n@media only screen and (min-width: 480px) {\n  body.body-auth {\n    background-image: url(../images/background_0500_login.jpg);\n  }\n\n  body.blitz.body-auth {\n    background-image: url(../images/background_blitz_login_0500.jpg);\n  }\n}\n\n@media only screen and (min-width: 580px) {\n  body.body-auth {\n    background-image: url(../images/background_0600_login.jpg);\n  }\n\n  body.blitz.body-auth {\n    background-image: url(../images/background_blitz_login_0600.jpg);\n  }\n}\n\n/* Desktops and laptops medium-res */\n@media only screen and (min-width: 760px) {\n  body {\n    background-image: url(../images/background_0800_login.jpg);\n  }\n\n  body.blitz.body-auth {\n    background-image: url(../images/background_blitz_login_0800.jpg);\n  }\n}\n\n/* Desktops and laptops ----------- */\n@media only screen and (min-width: 950px) {\n  body.body-auth {\n    background-image: url(../images/background_1024_login.jpg);\n  }\n\n  body.blitz.body-auth {\n    background-image: url(../images/background_blitz_login_1024.jpg);\n  }\n}\n\n@media only screen and (min-width: 1224px) {\n  body.body-auth {\n    background-image: url(../images/background_1280_login.jpg);\n  }\n\n  body.blitz.body-auth {\n    background-image: url(../images/background_blitz_login_1280.jpg);\n  }\n}\n\n/* Large screens ----------- */\n@media only screen and (min-width: 1550px) {\n  body.body-auth {\n    background-image: url(../images/background_1600_login.jpg);\n  }\n\n  body.blitz.body-auth {\n    background-image: url(../images/background_blitz_login_1600.jpg);\n  }\n}\n\n@media only screen and (min-width: 1824px) {\n  body.body-auth {\n    background-image: url(../images/background_1920_login.jpg);\n  }\n\n  body.blitz.body-auth {\n    background-image: url(../images/background_blitz_login_1920.jpg);\n  }\n}\n\n\n@media screen\n{\n  body, form, table, input, select\n  {\n    font-size: 1.2em;\n  }\n\n  #log_skipper\n  {\n    height: 16em;\n  }\n\n  #log_title\n  {\n    font-size: 200%;\n  }\n\n  #log_description\n  {\n    margin: 1.5em;\n  }\n\n  #log_register {\n    margin: 1.5em;\n  }\n\n  #log_menu {\n    padding: 1em 0;\n  }\n}\n\nbody {\n  text-align: center;\n  font-family: Arial, Helvetica, sans-serif;\n}\n\n#login_menu .menu_line a {\n  color: #e6ebfb;\n}\n\n\n#log_main, #copyright\n{\n  background-repeat: no-repeat;\n\n  position: relative;\n  border: 0.1em solid #FFFFFF;\n\n  width: 450px;\n  margin: auto;\n\n  filter               : alpha(opacity=90);\n  -moz-opacity         : 0.9;\n  -khtml-opacity       : 0.9;\n  opacity              : 0.9;\n}\n\n#log_main {\n  color: #e6ebfb;\n  background-color : #000000;\n\n  display: flex;\n  flex-direction: column;\n  align-items: center;\n}\n\n#copyright, #copyright #supernova_ws\n{\n  padding: 0;\n\n  color: #0a1c25;\n  background-color     : #FFFFFF;\n\n  font-size: 0.75em;\n  font-weight: bold;\n\n}\n\n\n#log_error {\n  font-size: 160%;\n  color: red;\n}\n\n#login_container {\n  width: 33em;\n  font-size: 1em;\n  display: none;\n  margin-top: 1em;\n}\n\n#player_register {\n  width: 33em;\n  font-size: 1em;\n  margin: 1em 0;\n}\n\n#player_register_logout {\n  margin: 1em 0;\n}\n\n#login_container .ui-tabs-nav {\n  text-align: center;\n}\n\n\n#login_container ol li.ui-tabs-selected a {\n/*  background: #344566 url(\"../../skins/EpicBlue/images/ui-bg_highlight-hard_75_344566_1x100.png\") repeat-x 50% 50%;*/\n/*  background: #344566 url(\"../../skins/EpicBlue/images/ui-bg_inset-soft_95_13233e_1x100.png\") repeat-x 50% 50%;*/\n  /*background: #344566 url(\"images/ui-bg_inset-soft_95_13233e_1x100.png\") repeat-x 50% 50%;*/\n  background: #344566;\n}\n\n#login_menu {\n  padding: 1em 0;\n  border-top: 0.1em solid white;\n  width: 100%;\n}\n\n.menu_line :not(:last-child):after\n{\n  content: ' :: ';\n}\n"
  },
  {
    "path": "design/css/login_background.css",
    "content": "/* Smartphones (portrait) ----------- */\n@media only screen\nand (max-width : 320px) {\n  /* Styles */\n  body\n  {\n    background-image: none;\n  }\n}\n\n/* iPads (portrait and landscape) ----------- */\n@media only screen\nand (min-device-width : 768px)\nand (max-device-width : 1024px) {\n  /* Styles */\n  body\n  {\n    background-image: none;\n  }\n}\n\n/* iPads (landscape) ----------- */\n@media only screen\nand (min-device-width : 768px)\nand (max-device-width : 1024px)\nand (orientation : landscape) {\n  /* Styles */\n  body\n  {\n    background-image: none;\n  }\n}\n\n/* iPads (portrait) ----------- */\n@media only screen\nand (min-device-width : 768px)\nand (max-device-width : 1024px)\nand (orientation : portrait) {\n  /* Styles */\n  body\n  {\n    background-image: none;\n  }\n}\n\n/* Desktops and laptops medium-res */\n@media only screen\nand (min-width : 760px) {\n  /* Styles */\n  body\n  {\n      background-image: none;\n  }\n}\n\n/* Desktops and laptops ----------- */\n@media only screen\nand (min-width : 1224px) {\n  /* Styles */\n  body \n  {\n      background-image: url(../images/bg-unauthorized.jpg);\n      background-repeat: no-repeat;\n  }\n}\n\n/* Large screens ----------- */\n@media only screen\nand (min-width : 1824px) {\n  /* Styles */\n  body \n  {\n      background-image: url(../images/bg-unauthorized.jpg);\n      background-repeat: no-repeat;\n  }\n}\n"
  },
  {
    "path": "design/css/menu_icons.css",
    "content": ".menu_icons {background-image: url('/design/images/menu_icons.png');transform-origin: top left;}\n#icon_menu_documentation{background-position: -0px -0px;zoom: calc(14/32);width: 32px;height: 32px;}\n#icon_menu_info_best_battles{background-position: -32px -0px;zoom: calc(14/16);width: 16px;height: 16px;}\n#icon_menu_planet_fleets{background-position: -48px -0px;zoom: calc(14/16);width: 16px;height: 16px;}\n#icon_menu_empire_universe{background-position: -64px -0px;zoom: calc(14/16);width: 16px;height: 16px;}\n#icon_menu_planet_shipyard{background-position: -80px -0px;zoom: calc(14/16);width: 16px;height: 16px;}\n#icon_menu_empire_market{background-position: -96px -0px;zoom: calc(14/16);width: 16px;height: 16px;}\n#icon_menu_empire_fleets{background-position: -112px -0px;zoom: calc(14/16);width: 16px;height: 16px;}\n#icon_menu_ally_overview{background-position: -0px -32px;zoom: calc(14/16);width: 16px;height: 16px;}\n#icon_menu_batch_operations{background-position: -16px -32px;zoom: calc(14/16);width: 16px;height: 16px;}\n#icon_menu_utils_notes{background-position: -32px -32px;zoom: calc(14/12);width: 12px;height: 12px;}\n#icon_menu_planet_structures{background-position: -44px -32px;zoom: calc(14/12);width: 12px;height: 12px;}\n#icon_menu_planet_defense{background-position: -56px -32px;zoom: calc(14/12);width: 12px;height: 12px;}\n#icon_menu_utils_shortcuts{background-position: -68px -32px;zoom: calc(14/12);width: 12px;height: 12px;}\n#icon_menu_planet_overview{background-position: -80px -32px;zoom: calc(14/12);width: 12px;height: 12px;}\n#icon_menu_planet_resources{background-position: -0px -48px;zoom: calc(14/12);width: 12px;height: 12px;}\n#icon_menu_premium{background-position: -12px -48px;zoom: calc(14/12);width: 12px;height: 12px;}\n#icon_menu_utils_buddies{background-position: -24px -48px;zoom: calc(14/12);width: 12px;height: 12px;}\n#icon_menu_races{background-position: -36px -48px;zoom: calc(14/12);width: 12px;height: 12px;}\n#icon_menu_radio{background-position: -48px -48px;zoom: calc(14/12);width: 12px;height: 12px;}\n#icon_menu_utils_search{background-position: -60px -48px;zoom: calc(14/12);width: 12px;height: 12px;}\n#icon_menu_info_stats{background-position: -72px -48px;zoom: calc(14/12);width: 12px;height: 12px;}\n#icon_menu_rules{background-position: -0px -60px;zoom: calc(14/12);width: 12px;height: 12px;}\n#icon_menu_shop{background-position: -12px -60px;zoom: calc(14/12);width: 12px;height: 12px;}\n#icon_menu_utils_reports{background-position: -24px -60px;zoom: calc(14/12);width: 12px;height: 12px;}\n#icon_menu_news{background-position: -36px -60px;zoom: calc(14/12);width: 12px;height: 12px;}\n#icon_menu_affiliates{background-position: -48px -60px;zoom: calc(14/12);width: 12px;height: 12px;}\n#icon_menu_info_server{background-position: -60px -60px;zoom: calc(14/12);width: 12px;height: 12px;}\n#icon_menu_empire_mercenaries{background-position: -72px -60px;zoom: calc(14/12);width: 12px;height: 12px;}\n#icon_menu_ally_chat{background-position: -0px -72px;zoom: calc(14/12);width: 12px;height: 12px;}\n#icon_menu_captain{background-position: -12px -72px;zoom: calc(14/12);width: 12px;height: 12px;}\n#icon_menu_comm_chat{background-position: -24px -72px;zoom: calc(14/12);width: 12px;height: 12px;}\n#icon_menu_comm_forum{background-position: -36px -72px;zoom: calc(14/12);width: 12px;height: 12px;}\n#icon_menu_comm_messages{background-position: -48px -72px;zoom: calc(14/12);width: 12px;height: 12px;}\n#icon_menu_empire_artifacts{background-position: -60px -72px;zoom: calc(14/12);width: 12px;height: 12px;}\n#icon_menu_empire_emperor{background-position: -72px -72px;zoom: calc(14/12);width: 12px;height: 12px;}\n#icon_menu_empire_overview{background-position: -0px -84px;zoom: calc(14/12);width: 12px;height: 12px;}\n#icon_menu_info_research{background-position: -12px -84px;zoom: calc(14/12);width: 12px;height: 12px;}\n#icon_menu_empire_quests{background-position: -24px -84px;zoom: calc(14/12);width: 12px;height: 12px;}\n#icon_menu_empire_schematics{background-position: -36px -84px;zoom: calc(14/12);width: 12px;height: 12px;}\n#icon_menu_empire_techtree{background-position: -48px -84px;zoom: calc(14/12);width: 12px;height: 12px;}\n#icon_menu_faq{background-position: -60px -84px;zoom: calc(14/12);width: 12px;height: 12px;}\n#icon_menu_info_admins{background-position: -72px -84px;zoom: calc(14/12);width: 12px;height: 12px;}\n#icon_menu_info_ban{background-position: -0px -96px;zoom: calc(14/12);width: 12px;height: 12px;}\n#icon_menu_ally{background-position: -12px -96px;zoom: calc(14/12);width: 12px;height: 12px;}\n#icon_menu_info_records{background-position: -24px -96px;zoom: calc(14/12);width: 12px;height: 12px;}\n#icon_menu_utils_simulator{background-position: -36px -96px;zoom: calc(14/12);width: 12px;height: 12px;}\n"
  },
  {
    "path": "design/images/index.html",
    "content": ""
  },
  {
    "path": "design/images/lang/index.html",
    "content": ""
  },
  {
    "path": "design/images/smileys/index.html",
    "content": ""
  },
  {
    "path": "design/templates/OpenGame/.htaccess",
    "content": "# Disable index list\nOptions -Indexes\n\n<Files ~ \"\\.html$\">\n  order allow,deny\n  deny from all\n</Files>\n\n# Deny access to all dotted fields and folders\nRedirectMatch 404 /\\..*$\n"
  },
  {
    "path": "design/templates/OpenGame/_ajax.tpl.html",
    "content": "{AJAX_RENDERED}"
  },
  {
    "path": "design/templates/OpenGame/_number_color_maximum.tpl.html",
    "content": "<!-- INCLUDE _number_percent_class --><span class=\"{$T_VALUE_CLASS}\">{$T_MAXIMUM}</span>"
  },
  {
    "path": "design/templates/OpenGame/_number_color_percent.tpl.html",
    "content": "<!-- IF $NUMBER <= 0 -->\n  <!-- DEFINE $FIELD_COLOR = 'negative' -->\n<!-- ELSEIF $NUMBER < 50 -->\n  <!-- DEFINE $FIELD_COLOR = 'warning' -->\n<!-- ELSEIF $NUMBER < 80 -->\n  <!-- DEFINE $FIELD_COLOR = 'neutral' -->\n<!-- ELSEIF $NUMBER < 100 -->\n  <!-- DEFINE $FIELD_COLOR = 'positive' -->\n<!-- ELSE -->\n  <!-- DEFINE $FIELD_COLOR = 'info' -->\n<!-- ENDIF -->\n"
  },
  {
    "path": "design/templates/OpenGame/_number_color_planet_fill_bg.tpl.html",
    "content": "<!-- IF $PLANET_FILL_PERCENT < 50 -->\n  <!-- DEFINE $PLANET_FILL_STYLE_BG = 'positive_bg' -->\n<!-- ELSEIF $PLANET_FILL_PERCENT < 80 -->\n  <!-- DEFINE $PLANET_FILL_STYLE_BG = 'neutral_bg' -->\n<!-- ELSEIF $PLANET_FILL_PERCENT < 100 -->\n  <!-- DEFINE $PLANET_FILL_STYLE_BG = 'warning_bg' -->\n<!-- ELSE -->\n  <!-- DEFINE $PLANET_FILL_STYLE_BG = 'negative_bg' -->\n<!-- ENDIF -->\n"
  },
  {
    "path": "design/templates/OpenGame/_number_color_production_bg.tpl.html",
    "content": "<!-- IF $PRODUCTION_PERCENT <= 0 -->\n  <!-- DEFINE $PRODUCTION_PERCENT_STYLE_BG = 'negative_bg' -->\n<!-- ELSEIF $PRODUCTION_PERCENT < 50 -->\n  <!-- DEFINE $PRODUCTION_PERCENT_STYLE_BG = 'warning_bg' -->\n<!-- ELSEIF $PRODUCTION_PERCENT < 80 -->\n  <!-- DEFINE $PRODUCTION_PERCENT_STYLE_BG = 'neutral_bg' -->\n<!-- ELSEIF $PRODUCTION_PERCENT < 100 -->\n  <!-- DEFINE $PRODUCTION_PERCENT_STYLE_BG = 'positive_bg' -->\n<!-- ELSE -->\n  <!-- DEFINE $PRODUCTION_PERCENT_STYLE_BG = 'info_bg' -->\n<!-- ENDIF -->\n"
  },
  {
    "path": "design/templates/OpenGame/_number_color_value.tpl.html",
    "content": "<!-- INCLUDE _number_percent_class --><span class=\"{$T_VALUE_CLASS}\">{$T_VALUE}</span>"
  },
  {
    "path": "design/templates/OpenGame/_number_percent_class.tpl.html",
    "content": "<!-- IF ! $T_MAXIMUM -->\n<!-- DEFINE $T_MAXIMUM = 0 -->\n<!-- ENDIF -->\n<!-- IF ! $T_VALUE -->\n<!-- DEFINE $T_VALUE = 0 -->\n<!-- ENDIF -->\n<!-- IF ! $T_MAXIMUM && ! $T_VALUE -->\n<!-- DEFINE $T_VALUE_CLASS = 'zero_number' -->\n<!-- ELSEIF $T_VALUE > $T_MAXIMUM -->\n<!-- DEFINE $T_VALUE_CLASS = 'error' -->\n<!-- ELSEIF $T_VALUE == $T_MAXIMUM -->\n<!-- DEFINE $T_VALUE_CLASS = 'warning' -->\n<!-- ELSEIF ! $T_MAXIMUM  -->\n<!-- DEFINE $T_VALUE_CLASS = 'zero_number' -->\n<!-- ELSEIF $T_VALUE / $T_MAXIMUM  > 0.9 -->\n<!-- DEFINE $T_VALUE_CLASS = 'warning' -->\n<!-- ELSEIF $T_VALUE / $T_MAXIMUM  > 0.75 -->\n<!-- DEFINE $T_VALUE_CLASS = 'notice' -->\n<!-- ELSEIF $T_VALUE / $T_MAXIMUM  > 0.50 -->\n<!-- DEFINE $T_VALUE_CLASS = 'info' -->\n<!-- ELSE -->\n<!-- DEFINE $T_VALUE_CLASS = 'ok' -->\n<!-- ENDIF -->"
  },
  {
    "path": "design/templates/OpenGame/_page/_00_header.tpl.html",
    "content": "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">\n<html xmlns=\"http://www.w3.org/1999/xhtml\" dir=\"{LANG_DIRECTION}\" lang=\"{LANG_LANGUAGE}\" xml:lang=\"{LANG_LANGUAGE}\">\n  <head>\n    <base href=\"{D_SN_ROOT_VIRTUAL}\" />\n\n    <title>{title}</title>\n\n    <!-- INCLUDE _page/_01_head -->\n  </head>\n\n  <!-- IF PLAYER_OPTION_DESIGN_DISABLE_BORDERS -->\n    <!-- DEFINE $PLAYER_OPTION_DESIGN_DISABLE_BORDERS = 'no_border_image' -->\n  <!-- ENDIF -->\n\n  <!-- IF LOGIN_LOGOUT -->\n    <!-- DEFINE $BODY_CLASS_AUTH = 'body-auth' -->\n  <!-- ENDIF -->\n\n  <body class=\"style {$PLAYER_OPTION_DESIGN_DISABLE_BORDERS} {GAME_MODE_CSS_PREFIX} {$BODY_CLASS_AUTH} {SN_TEMPLATE_NAME}\">\n    <!-- INCLUDE _page/_40_js -->\n\n    <!-- IF IMPERSONATING -->\n      <div style=\"text-align: center\" class=\"warning\">{IMPERSONATING}</div>\n    <!-- ENDIF -->\n\n    <div id=\"__page\">\n      <table class=\"markup\" width=\"100%\">\n        <tr>\n          <!-- IF .menu && GLOBAL_DISPLAY_MENU -->\n            <td align=\"left\" valign=\"top\">\n              <!-- INCLUDE menu -->\n            </td>\n          <!-- ENDIF -->\n        <td align=\"center\" valign=\"top\" width=\"100%\" id=\"__content_cell\">\n\n          <!-- DEFINE $NAVBAR_VERTICAL = 0 -->\n\n          <!-- IF $NAVBAR_VERTICAL -->\n            <!-- DEFINE $NAVBAR_VERTICAL_CLASS = 'navbar_v' -->\n          <!-- ENDIF -->\n\n          <!-- IF GLOBAL_DISPLAY_NAVBAR -->\n          <div id=\"__navbar_wrapper\" class=\"{$NAVBAR_VERTICAL_CLASS}\">\n            <!-- INCLUDE _page/navbar -->\n          </div>\n          <!-- ENDIF -->\n\n          <!-- INCLUDE _page/navbar_additions -->\n\n          <div id=\"__content\">\n            {__RENDERED_CONTENT}\n          </div> <!-- __content -->\n\n          <div id=\"copyright\">\n            Project &quot;<a href=\"http://supernova.ws\"><span id=\"supernova_ws\">Sup<span>erNo</span>va<span>.W</span>S</span></a>&quot; Rel<span>ease {D_SN_RELEASE} V</span>{D_SN_VERSION}-{DB_PATCH_VERSION} &copy; 20<span>09-{CURRENT_YEAR} Go</span>rlum<br>\n            In<span>spired by X</span>Nova Ra<span>geRe<span>pac</span>k v2</span>26\n          </div>\n        </td></tr></table> <!-- <table class=\"markup\" width=\"100%\"> -->\n    </div><!-- __page -->\n\n    <div id=\"debug\" style=\"text-align: left\"></div>\n    <script type=\"text/javascript\">\n      document_ready();\n      jQuery(document).ready(function () {\n        jQuery('.benchmark').append(', JS run: ' + ((new Date().valueOf() - timeBrowser.valueOf()) / 1000) + 's');\n      });\n    </script>\n  </body>\n</html>\n"
  },
  {
    "path": "design/templates/OpenGame/_page/_01_head.tpl.html",
    "content": "<!--<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />-->\n<!--<meta name=\"viewport\" content=\"width=device-width, target-densitydpi=device-dpi, initial-scale=0.6675\" />-->\n<!--, target-densitydpi=device-dpi-->\n\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n<meta name=\"HandheldFriendly\" content=\"True\">\n\n<!--<meta name=\"MobileOptimized\" content=\"800\">-->\n\n<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\" />\n<meta http-equiv=\"Cache-Control\" content=\"no-cache\" />\n<!-- IF ADV_SEO_META_KEYWORDS --><meta name=\"keywords\" content=\"{ADV_SEO_META_KEYWORDS}\" /><!-- ENDIF -->\n<!-- IF ADV_SEO_META_DESCRIPTION --><meta name=\"description\" content=\"{ADV_SEO_META_DESCRIPTION}\" /><!-- ENDIF -->\n\n{GLOBAL_META_TAGS}\n\n<link rel=\"shortcut icon\" href=\"{D_SN_ROOT_VIRTUAL}favicon.ico?{C_var_db_update}\" />\n\n<!--[if IE]>\n<link rel=\"stylesheet\" type=\"text/css\" href=\"{D_SN_ROOT_VIRTUAL}design/css/global-ie.min.css?{C_var_db_update}\" />\n<![endif]-->\n\n\n<!-- BEGIN css -->\n  <!-- IF css.CONTENT -->\n    <style type=\"text/css\">{css.CONTENT}</style>\n  <!-- ELSEIF css.FILE -->\n    <link rel=\"stylesheet\" type=\"text/css\" href=\"{D_SN_ROOT_VIRTUAL}{css.FILE}?{C_var_db_update}\" />\n  <!-- ENDIF -->\n<!-- END css -->\n\n<!-- IF FONT_SIZE != FONT_SIZE_PERCENT_DEFAULT_STRING -->\n  <style type=\"text/css\">\n    html {font-size: {FONT_SIZE} !important;}\n  </style>\n<!-- ENDIF -->\n"
  },
  {
    "path": "design/templates/OpenGame/_page/_40_js.tpl.html",
    "content": "<!-- JS block _40_js.tpl.html ====================================================================================== -->\n<script type=\"text/javascript\">\n  // Этот скрипт находится здесь, потому что он должен быть выполнен максимально быстро\n  var timeBrowser = new Date();\n</script>\n\n<!-- IF ADV_SEO_JAVASCRIPT -->\n  {ADV_SEO_JAVASCRIPT}\n<!-- ENDIF -->\n\n<!-- JS file include block ----------------------------------------------------------------------------------------- -->\n<!-- BEGIN js -->\n  <script type=\"text/javascript\" src=\"{D_SN_ROOT_VIRTUAL}{js.FILE}?{C_var_db_update}\"></script>\n<!-- END js -->\n\n<!-- BEGIN javascript -->\n  <!-- IF javascript.CONTENT -->\n    <script type=\"text/javascript\">{javascript.CONTENT}</script>\n  <!-- ELSEIF javascript.FILE -->\n    <script type=\"text/javascript\" src=\"{D_SN_ROOT_VIRTUAL}{javascript.FILE}?{C_var_db_update}\"></script>\n  <!-- ENDIF -->\n<!-- END javascript -->\n\n<!-- JS data block ------------------------------------------------------------------------------------------------- -->\n<script type=\"text/javascript\">\n  var SN_SOUND_ENABLED = parseInt('{SOUND_ENABLED}') ? parseInt('{SOUND_ENABLED}') : 0;\n  var PLAYER_OPTION_ANIMATION_DISABLED = parseInt('{PLAYER_OPTION_ANIMATION_DISABLED}') ? parseInt('{PLAYER_OPTION_ANIMATION_DISABLED}') : 0;\n  var PLAYER_OPTION_PROGRESS_BARS_DISABLED = parseInt('{PLAYER_OPTION_PROGRESS_BARS_DISABLED}') ? parseInt('{PLAYER_OPTION_PROGRESS_BARS_DISABLED}') : 0;\n  var SN_ROOT_VIRTUAL = '{D_SN_ROOT_VIRTUAL}';\n  var sn_path_prefix = '{D_SN_ROOT_VIRTUAL}';\n\n  jQuery.fx.off = PLAYER_OPTION_ANIMATION_DISABLED;\n\n  // Корректировка по времени запроса - если доступно\n  // var timeBrowser = window.performance ? timeBrowser = new Date(window.performance.timing.requestStart) : new Date();\n  // var timeBrowser = window.performance ? timeBrowser = new Date(window.performance.timing.responseStart) : new Date();\n\n  // С сервера ВСЕГДА должна передаваться РАЗНИЦА во времени - что бы корректно работали скрипты при навигации вперед-назад по страницам в браузерах!\n  var timeDiff = parseInt('{D_SN_CLIENT_TIME_DIFF}') ? parseInt('{D_SN_CLIENT_TIME_DIFF}') : false;\n\n  /*global SN_TIME_NOW:true*/\n  /*eslint no-undef: \"error\"*/\n  var SN_TIME_NOW = parseInt('{D_SN_TIME_NOW}') ? parseInt('{D_SN_TIME_NOW}') : (round(timeBrowser.valueOf() / 1000));\n\n  var timeTimerStart = timeBrowser;\n\n  if(parseInt('{TIME_DIFF_MEASURE}') || timeDiff === false) {\n    timeDiff = 0;\n    jQuery.post(\"time_probe.php\", {'timeBrowser': timeBrowser.valueOf(), 'utc_offset': -timeBrowser.getTimezoneOffset() * 60, 'client_gmt': timeBrowser.toUTCString()}, function(data) {\n      timeDiff = parseInt(data);\n    });\n  }\n\n  var SN_GOOGLE = parseInt('{SN_GOOGLE}') ? 1 : 0;\n\n  /*global snFont:true*/\n  /*eslint no-undef: \"error\"*/\n  var snFont = {\n    min: Math.floatVal('{D_FONT_SIZE_PERCENT_MIN}'),\n    max: Math.floatVal('{D_FONT_SIZE_PERCENT_MAX}'),\n    step: Math.floatVal('{D_FONT_SIZE_PERCENT_STEP}'),\n    sizeDefaultPercent: Math.floatVal('{D_FONT_SIZE_PERCENT_DEFAULT}'),\n    size: Math.floatVal('{FONT_SIZE}')\n  };\n\n  $.extend(language, {\n    sys_confirm: '{LA_sys_confirm}',\n    sys_cancel: '{LA_sys_cancel}',\n    sys_confirm_action: '{LA_sys_confirm_action}',\n    sys_confirm_action_title: '{LA_sys_confirm_action_title}',\n  });\n\n  var WEBP_SUPPORTED = Math.intVal('{WEBP_SUPPORTED}');\n  var WEBP_SUPPORT_NEED_CHECK = Math.intVal('{WEBP_SUPPORT_NEED_CHECK}');\n\n  if(WEBP_SUPPORT_NEED_CHECK) {\n    hasWebP().then(function() {\n      jQuery.post(\"time_probe.php\", {'webpSupport': WEBP_SUPPORTED = 1,});\n    }, function() {\n      jQuery.post(\"time_probe.php\", {'webpSupport': WEBP_SUPPORTED = 0,});\n    });\n  }\n  // var HIGHSPOT_GATHER = \"{HIGHSPOT_GATHER}\";\n</script>\n\n<!-- IF HALLOWEEN_CODE -->\n<script type=\"text/javascript\">\n  window.HALLOWEEN_CODE = \"{HALLOWEEN_CODE}\";\n  window.HALLOWEEN_IMAGE = \"{HALLOWEEN_IMAGE}\";\n</script>\n<span class=\"halloween\"></span>\n<!-- ENDIF -->\n<!-- JS block _40_js.tpl.html ====================================================================================== -->\n"
  },
  {
    "path": "design/templates/OpenGame/_page/_99_footer.tpl.html",
    "content": ""
  },
  {
    "path": "design/templates/OpenGame/_page/navbar.tpl.html",
    "content": "<!-- NAVBAR START -->\n\n<!-- DEFINE $NAVBAR_RESOURCES_RENDER = '{SN_RENDER_NAVBAR_PLANET}' -->\n<!-- DEFINE $NAVBAR_RESOURCES_OLD = '{PLAYER_OPTION_NAVBAR_PLANET_OLD}' -->\n<!-- DEFINE $NAVBAR_RESOURCES_VERTICAL = '{PLAYER_OPTION_NAVBAR_PLANET_VERTICAL}' -->\n<!-- DEFINE $NAVBAR_RESOURCES_HIDE_FLEETS = '{SN_NAVBAR_HIDE_FLEETS}' -->\n<!-- DEFINE $NAVBAR_RESOURCES_ALLY = '{SN_IN_ALLY}' -->\n<!-- DEFINE $NAVBAR_RESOURCES_HIDE_STORAGE = '{PLAYER_OPTION_NAVBAR_PLANET_DISABLE_STORAGE}' -->\n\n<!-- IF $NAVBAR_VERTICAL -->\n  <!--DEFINE $NAVBAR_VERTICAL_CLASS = 'navbar_v' -->\n  <!-- DEFINE $NAVBAR_RESOURCES_VERTICAL = 1 -->\n  <!-- DEFINE $NAVBAR_RESOURCES_OLD = 0 -->\n<!-- ENDIF -->\n\n<!-- IF $NAVBAR_RESOURCES_VERTICAL -->\n  <!-- DEFINE $NAVBAR_RESOURCES_CLASS = 'resbar_v' -->\n<!-- ELSE -->\n  <!-- DEFINE $NAVBAR_RESOURCES_CLASS = '' -->\n<!-- ENDIF -->\n\n<!-- IF TUTORIAL_ENABLED -->\n  <!-- INCLUDE tutorial -->\n<!-- ENDIF -->\n\n\n<script type=\"text/javascript\">\n  PLAYER_OPTION_NAVBAR_PLANET_VERTICAL = Math.intVal('{PLAYER_OPTION_NAVBAR_PLANET_VERTICAL}');\n</script>\n\n<div id=\"navbar_resource_flex_tooltip_pattern\">\n  <table class=\"no_border_image\">\n    <tr class=\"c_c no_select\">\n      <th colspan=\"2\">&#123;0&#125;</th>\n    </tr>\n    <tr>\n      <td>{L_tech_storage}</td>\n      <td class=\"c_r\">&#123;1&#125;</td>\n    </tr>\n    <tr>\n      <td>{L_tech_storage_max}</td>\n      <td class=\"c_r\">&#123;2&#125;</td>\n    </tr>\n    <tr>\n      <td>{L_tech_fullness}</td>\n      <td class=\"c_r\">&#123;3&#125;%</td>\n    </tr>\n  </table>\n</div>\n\n<div id=\"navbar_resource_flex_tooltip_pattern_energy\">\n  <table class=\"no_border_image\">\n    <tr class=\"c_c no_select\">\n      <th colspan=\"2\">&#123;0&#125;</th>\n    </tr>\n    <tr>\n      <td>{L_tech_storage_energy}</td>\n      <td class=\"c_r\">&#123;1&#125;</td>\n    </tr>\n    <tr>\n      <td>{L_tech_storage_energy_max}</td>\n      <td class=\"c_r\">&#123;2&#125;</td>\n    </tr>\n    <tr>\n      <td>{L_tech_storage_energy_fullness}</td>\n      <td class=\"c_r\">&#123;3&#125;%</td>\n    </tr>\n  </table>\n</div>\n\n<!-- DEFINE $IN_NAVBAR = 1 -->\n<div id=\"navbar_container\" class=\"{$NAVBAR_RESOURCES_CLASS} {$NAVBAR_VERTICAL_CLASS}\">\n  <div class=\"header c_c border_image_small {$NAVBAR_RESOURCES_CLASS} {$NAVBAR_VERTICAL_CLASS}\" id=\"navbar\">\n    <div id=\"navbar_main\">\n      <!-- IF .navbar_prefix_button -->\n      <div class=\"contF a50 flexW cell\">\n        <!-- BEGIN navbar_prefix_button -->\n        <div class=\"navbar-festival_button\">\n          <a class=\"w100 h100\" href=\"{navbar_prefix_button.URL_RELATIVE}\" >\n            <img class=\"w100 h100\" src=\"{navbar_prefix_button.IMAGE}\" />\n          </a>\n        </div>\n        <!-- END navbar_prefix_button -->\n      </div>\n      <!-- ENDIF -->\n\n      <!-- IF GAME_BLITZ_REGISTER && ! GAME_BLITZ -->\n      <div>\n        <button onclick=\"sn_redirect('{D_SN_ROOT_VIRTUAL}blitz_register.php');\" id=\"navbar-blitz-button\">\n          {L_sys_blitz_global_button}:\n            <span class=\"<!-- IF BLITZ_REGISTER_OPEN -->positive<!-- ELSEIF BLITZ_REGISTER_CLOSED -->negative<!-- ELSEIF BLITZ_REGISTER_SHOW_LOGIN -->zero<!-- ELSEIF BLITZ_REGISTER_DISCLOSURE_NAMES --><!-- ENDIF -->\">\n              {GAME_BLITZ_REGISTER_TEXT} ({C_game_blitz_register_users})\n            </span>\n        </button>\n      </div>\n      <!-- ENDIF -->\n\n      <div id=\"navbar_planet_and_time\">\n        <div class=\"contF flexW header\" id=\"navbar_planet_wrapper\">\n          <!-- INCLUDE _page/navbar_planet_select -->\n\n          <div id=\"navbar_player_count\">{L_top_online}&nbsp;[<span class=\"js_global_users_online\">{USERS_ONLINE}</span>/<span class=\"js_global_users_total\">{USERS_TOTAL}</span>]</div>\n        </div>\n\n      <div class=\"contF flexW cell\" id=\"navbar-container-time\">\n        <div class=\"navbar-container-time-item c_c\">\n          <div class=\"ok\"><span id=\"navbar_time_local_title\">{L_time_local}: </span><span id=\"top_time\">{TIME_TEXT_LOCAL}</span></div>\n          <div id=\"navbar_time_server\"><span id=\"navbar_time_server_title\">{L_time_server}: </span><span id=\"top_time_server\">{TIME_TEXT}</span></div>\n        </div>\n\n        <div class=\"contF\" id=\"navbar_font_wrapper\">\n          <button id=\"font_minus\">{L_navbar_font} -</button>\n          <button id=\"font_normal\">{L_navbar_font_normal}</button>\n          <button id=\"font_plus\">{L_navbar_font} +</button>\n        </div>\n      </div>\n      </div>\n\n      <div class=\"header\" id=\"navbar-container-button\">\n        <!-- INCLUDE _page/navbar_buttons -->\n      </div>\n    </div>\n\n    <!-- IF $NAVBAR_RESOURCES_RENDER && ! $NAVBAR_RESOURCES_OLD -->\n      <!-- INCLUDE _page/navbar_resources -->\n    <!-- ENDIF -->\n  </div>\n\n  <!-- IF $NAVBAR_RESOURCES_RENDER && $NAVBAR_RESOURCES_OLD-->\n    <!-- DEFINE $NAVBAR_OLD_OUT = 1 -->\n    <!-- INCLUDE _page/navbar_resources -->\n    <!-- DEFINE $NAVBAR_OLD_OUT = 0 -->\n  <!-- ENDIF -->\n</div>\n<!-- DEFINE $IN_NAVBAR = 0 -->\n\n<script type=\"text/javascript\">\n  NAVBAR_MODE = Math.intVal('{NAVBAR_MODE}');\n\n  sn_timers.unshift({\n    'id': 'top_time_server',\n    'type': TIMER_CLOCK_REALTIME,\n    'options': {\n      'format': 3,\n      'delta': -timeDiff\n    }\n  });\n\n  sn_timers.unshift({\n    'id': 'top_time',\n    'type': TIMER_CLOCK_REALTIME,\n    'options': {\n      'format': 3,\n      'delta': 0\n    }\n  });\n\n  <!-- IF .flying_fleets -->\n  sn_timers.unshift({\n    'id': 'topnav_fleet_counter',\n    'type': TIMER_EVENT_QUE,\n    'options': {\n      'msg_done': '0',\n      'changed': true,\n      'que': [<!-- BEGIN flying_fleets -->\n        [parseInt('{flying_fleets.TIME}'), '{flying_fleets.TEXT}', '{flying_fleets.HINT}'],\n      <!-- END flying_fleets -->]\n    }\n  });\n  <!-- ENDIF -->\n\n  <!-- IF .flying_expeditions -->\n  sn_timers.unshift({\n    'id': 'topnav_expedition_counter',\n    'type': TIMER_EVENT_QUE,\n    'options': {\n      'msg_done': '0',\n      'changed': true,\n      'que': [<!-- BEGIN flying_expeditions -->\n        [parseInt('{flying_expeditions.TIME}'), '{flying_expeditions.TEXT}', '{flying_expeditions.HINT}'],\n      <!-- END flying_expeditions -->]\n    }\n  });\n  <!-- ENDIF -->\n</script>\n"
  },
  {
    "path": "design/templates/OpenGame/_page/navbar_additions.tpl.html",
    "content": "<div id=\"__navbar_additions\">\n\n<div id=\"navbar_refresh\" class=\"button_pseudo\" onclick=\"document.location.reload(true);\">{L_topnav_refresh_page}</div>\n\n<script type=\"text/javascript\">\n  sn_inframe ? $('#navbar_refresh').css('display', 'block') : false;\n</script>\n\n<!-- IF .announces && GAME_NEWS_OVERVIEW -->\n  <!-- INCLUDE navbar_news -->\n<!-- ENDIF -->\n\n<!-- IF .note -->\n  <!-- INCLUDE navbar_notes -->\n<!-- ENDIF -->\n</div>"
  },
  {
    "path": "design/templates/OpenGame/_page/navbar_buttons.tpl.html",
    "content": "<!-- IF ! GAME_RESEARCH_DISABLED && ! PLAYER_OPTION_NAVBAR_DISABLE_RESEARCH -->\n  <!-- DEFINE $QUE_ID = '{QUE_RESEARCH}' -->\n\n  <!-- IF PLAYER_OPTION_NAVBAR_RESEARCH_WIDE -->\n    <!-- DEFINE $NAVBAR_QUE_WIDE = 'wide' -->\n    <!-- DEFINE $NAVBAR_QUE_CLASS = 'posLT sprite_navbar_buttons navbar_research_button_wide' -->\n    <!-- DEFINE $NAVBAR_QUE_NAME = '{L_sys_que_research}' -->\n  <!-- ELSE -->\n    <!-- DEFINE $NAVBAR_QUE_CLASS = 'posLT sprite_navbar_buttons navbar_research_button' -->\n    <!-- DEFINE $NAVBAR_QUE_NAME = '{L_sys_que_research_short}' -->\n  <!-- ENDIF -->\n  <!-- DEFINE $NAVBAR_QUE_HINT = '{L_Research}' -->\n  <!-- DEFINE $NAVBAR_QUE_URL = 'buildings.php?mode={D_QUE_RESEARCH}' -->\n\n  <!-- INCLUDE navbar_button_queued -->\n<!-- ENDIF -->\n\n<!-- DEFINE $NAVBAR_QUE_WIDE = '' -->\n\n<!-- IF ! PLAYER_OPTION_NAVBAR_DISABLE_PLANET -->\n  <!-- DEFINE $QUE_ID = '{QUE_STRUCTURES}' -->\n\n  <!-- DEFINE $NAVBAR_QUE_CLASS = '' -->\n  <!-- DEFINE $NAVBAR_QUE_NAME = '{L_sys_que_structures}' -->\n  <!-- DEFINE $NAVBAR_QUE_IMAGE = '{I_[TOPNAV_CURRENT_PLANET_IMAGE]}' -->\n  <!-- DEFINE $NAVBAR_QUE_HINT = '{TOPNAV_CURRENT_PLANET_NAME}' -->\n  <!-- DEFINE $NAVBAR_QUE_URL = 'overview.php' -->\n\n  <!-- INCLUDE navbar_button_queued -->\n<!-- ENDIF -->\n\n<!-- DEFINE $NAVBAR_QUE_IMAGE = '' -->\n\n<!-- IF ! PLAYER_OPTION_NAVBAR_DISABLE_HANGAR && ! GAME_HANGAR_DISABLED -->\n  <!-- DEFINE $QUE_ID = '{SUBQUE_FLEET}' -->\n\n  <!-- DEFINE $NAVBAR_QUE_CLASS = 'posLT sprite_navbar_buttons navbar_hangar_button' -->\n  <!-- DEFINE $NAVBAR_QUE_NAME = '{L_sys_que_hangar}' -->\n  <!-- DEFINE $NAVBAR_QUE_HINT = '{L_Shipyard}' -->\n  <!-- DEFINE $NAVBAR_QUE_URL = 'buildings.php?mode={D_SUBQUE_FLEET}' -->\n\n  <!-- INCLUDE navbar_button_queued -->\n<!-- ENDIF -->\n\n<!-- IF ! PLAYER_OPTION_NAVBAR_DISABLE_EXPEDITIONS -->\n<div class=\"navbar_button\" alt=\"{L_sys_expeditions}\" title=\"{L_sys_expeditions}\" go_url=\"fleet.php\">\n  <span class=\"posLT sprite_navbar_buttons navbar_expedition_button\"></span>\n    <span id=\"topnav_expedition_counter_total\" class=\"a50 posRB w100\">\n      <span id='topnav_expedition_counter'>{TOPNAV_EXPEDITIONS_FLYING}</span>/{TOPNAV_EXPEDITIONS_TOTAL}\n    </span>\n    <span class=\"posLT w100 a50\">\n      {L_navbar_button_expeditions_short}\n    </span>\n</div>\n<!-- ENDIF -->\n\n<!-- IF ! PLAYER_OPTION_NAVBAR_DISABLE_FLYING_FLEETS -->\n<div class=\"navbar_button\" alt=\"{L_sys_fleets}\" title=\"{L_sys_fleets}\" go_url=\"flying_fleets.php\">\n  <span class=\"posLT sprite_navbar_buttons navbar_fleet_own_button\"></span>\n    <span id=\"topnav_fleet_counter_total\" class=\"a50 posRB w100\">\n      <span id='topnav_fleet_counter'>{TOPNAV_FLEETS_FLYING}</span>/{TOPNAV_FLEETS_TOTAL}\n    </span>\n    <span class=\"posLT w100 a50\">{L_navbar_button_fleets}</span>\n</div>\n<!-- ENDIF -->\n\n<!-- IF ! PLAYER_OPTION_NAVBAR_DISABLE_QUESTS && ! GAME_QUESTS_DISABLED -->\n<div class=\"navbar_button\" alt=\"{L_qst_quests}\" title=\"{L_qst_quests}\" go_url=\"quest.php\">\n  <span class=\"posLT sprite_navbar_buttons navbar_quest_button\"></span>\n    <span class=\"a50 posRB w100\">\n      <span class=\"notice\">{TOPNAV_QUEST_IN_PROGRESS}</span>/<span class=\"success\">{TOPNAV_QUEST_COMPLETE}</span>/{C_quest_total}\n    </span>\n    <span class=\"posLT w100 a50\">{L_navbar_button_quests}</span>\n</div>\n<!-- ENDIF -->\n\n<div class=\"navbar_button\" alt=\"{L_Message}\" title=\"{L_Message}\" go_url=\"messages.php\">\n  <span class=\"posLT sprite_navbar_buttons navbar_mail_button\"></span>\n  <span href=\"messages.php\" alt=\"{L_msg_class[D_MSG_TYPE_NEW]}\" title=\"{L_msg_class[D_MSG_TYPE_NEW]}\">\n    <div class=\"posRB\">\n      <!-- IF TOPNAV_MESSAGES_ALL --><span class=\"blink\" duration=\"2000\">[{TOPNAV_MESSAGES_ALL}]</span><!-- ELSE -->0<!-- ENDIF -->\n    </div>\n  </span>\n  <!-- IF TOPNAV_MESSAGES_PLAYER -->\n  <a href=\"messages.php?mode=show&message_class={D_MSG_TYPE_PLAYER}\" alt=\"{L_msg_class[D_MSG_TYPE_PLAYER]}\" title=\"{L_msg_class[D_MSG_TYPE_PLAYER]}\">\n    <span class=\"posLT\"><span class=\"mnl_joueur\">[{TOPNAV_MESSAGES_PLAYER}]</span></span>\n  </a>\n  <!-- ENDIF -->\n  <!-- IF TOPNAV_MESSAGES_ADMIN -->\n  <a href=\"messages.php?mode=show&message_class={D_MSG_TYPE_ADMIN}\" alt=\"{L_msg_class[D_MSG_TYPE_ADMIN]}\" title=\"{L_msg_class[D_MSG_TYPE_ADMIN]}\">\n    <span class=\"posLB\"><span class=\"msg_admin\">[{TOPNAV_MESSAGES_ADMIN}]</span></span>\n  </a>\n  <!-- ENDIF -->\n  <!-- IF TOPNAV_MESSAGES_ALLIANCE && ! TOPNAV_ALLY -->\n  <a href=\"messages.php?mode=show&message_class={D_MSG_TYPE_ALLIANCE}\" alt=\"{L_msg_class[D_MSG_TYPE_ALLIANCE]}\" title=\"{L_msg_class[D_MSG_TYPE_ALLIANCE]}\">\n    <span class=\"posRT\"><span class=\"mnl_alliance\">[{TOPNAV_MESSAGES_ALLIANCE}]</span></span>\n  </a>\n  <!-- ENDIF -->\n</div>\n\n<div class=\"navbar_button\" alt=\"{L_sys_dark_matter}\" title=\"{L_sys_dark_matter}\" go_url=\"dark_matter.php\">\n  <span class=\"posLT sprite_navbar_buttons navbar_dark_matter_button\"></span>\n    <!-- IF TOPNAV_METAMATTER -->\n    <span id=\"navbar_button_dm_mm\" class=\"a50 metamatter posRB\">{TOPNAV_METAMATTER_TEXT}</span>\n    <span id=\"navbar_button_dm_dm\" class=\"a50 dark_matter posRB js_dark_matter_plain\">{TOPNAV_DARK_MATTER_PLAIN_TEXT}</span>\n    <span id=\"navbar_button_dm_plus\" class=\"a50 ok posLB\">+</span>\n    <!-- DEFINE $NAVBAR_DEBIT_LINE_CLASS = 'navbar_button_dm_debit_line' -->\n    <!-- ENDIF -->\n    <span class=\"a50 ok posRB {$NAVBAR_DEBIT_LINE_CLASS} js_dark_matter_full\">{TOPNAV_DARK_MATTER_TEXT}</span>\n    <span class=\"posLT w100 a50\">{L_sys_dark_matter_sh}</span>\n</div>\n\n<!-- IF ! PLAYER_OPTION_NAVBAR_DISABLE_META_MATTER && TOPNAV_PAYMENT -->\n<div class=\"navbar_button\" alt=\"{L_sys_metamatte}\" title=\"{L_sys_metamatter}\" go_url=\"metamatter.php\">\n  <span class=\"posLT sprite_navbar_buttons navbar_metamatter_button\"></span>\n    <span class=\"a50 posRB metamatter\">{TOPNAV_METAMATTER_TEXT}</span>\n    <span class=\"posLT w100 a50\">{L_sys_metamatter_sh}</span>\n</div>\n<!-- ENDIF -->\n\n<!-- BEGIN navbar_main_button -->\n<div class=\"navbar-festival_button\">\n  <a class=\"w100 h100\" href=\"{navbar_main_button.URL_RELATIVE}\" >\n    <img class=\"w100 h100\" src=\"{navbar_main_button.IMAGE}\" />\n  </a>\n</div>\n<!-- END navbar_main_button -->\n"
  },
  {
    "path": "design/templates/OpenGame/_page/navbar_planet_select.tpl.html",
    "content": "  <select size=\"1\" onchange=\"changePlanet(this);\" id=\"navbar_planet_select\" class=\"js_navbar_planet_select\">\n    <!-- BEGIN topnav_planets -->\n    <option {topnav_planets.SELECTED} value=\"{topnav_planets.ID}\">\n      <!-- IF topnav_planets.IS_CAPITAL -->&#9813;<!-- ENDIF -->\n      <!-- IF topnav_planets.IS_MOON -->&#9789;<!-- ENDIF -->\n      {topnav_planets.COORDS}&nbsp;{topnav_planets.TYPE_TEXT}&nbsp;{topnav_planets.NAME}\n    </option>\n    <!-- END topnav_planets -->\n  </select>\n\n"
  },
  {
    "path": "design/templates/OpenGame/_page/navbar_resources.tpl.html",
    "content": "<!-- navbar_resources BOF -->\n\n<!-- IF $IN_NAVBAR -->\n  <!-- IF ! $NAVBAR_RESOURCES_OLD -->\n    <!-- INCLUDE _page/navbar_resources_flex -->\n  <!-- ELSE -->\n    <div>\n    <!-- IF $NAVBAR_RESOURCES_VERTICAL -->\n      <!-- INCLUDE _page/navbar_resources_vertical -->\n    <!-- ELSE -->\n      <!-- INCLUDE _page/navbar_resources_horizontal -->\n    <!-- ENDIF -->\n    </div>\n  <!-- ENDIF -->\n<!-- ELSE -->\n  <!-- INCLUDE _page/navbar_resources_horizontal -->\n<!-- ENDIF -->\n\n\n<script type=\"text/javascript\">\n  $.extend(language, {\n    sys_metal: '{LA_sys_metal}',\n    sys_crystal: '{LA_sys_crystal}',\n    sys_deuterium: '{LA_sys_deuterium}',\n    sys_energy: '{LA_sys_energy}'\n  });\n\n  navbarResources = {\n    \"metal\": {\n      start_value: Math.floatVal('{PLANET_METAL}'),\n      per_second: Math.floatVal('{PLANET_METAL_PERHOUR}' / 3600),\n      max_value: Math.floatVal('{PLANET_METAL_MAX}'),\n      round: Math.intVal('{RESOURCE_ROUNDING}'),\n      resourceNameLongId: 'sys_metal'\n    },\n    \"crystal\": {\n      start_value: Math.floatVal('{PLANET_CRYSTAL}'),\n      per_second: Math.floatVal('{PLANET_CRYSTAL_PERHOUR}' / 3600),\n      max_value: Math.floatVal('{PLANET_CRYSTAL_MAX}'),\n      round: Math.intVal('{RESOURCE_ROUNDING}'),\n      resourceNameLongId: 'sys_crystal'\n    },\n    \"deuterium\": {\n      start_value: Math.floatVal('{PLANET_DEUTERIUM}'),\n      per_second: Math.floatVal('{PLANET_DEUTERIUM_PERHOUR}' / 3600),\n      max_value: Math.floatVal('{PLANET_DEUTERIUM_MAX}'),\n      round: Math.intVal('{RESOURCE_ROUNDING}'),\n      resourceNameLongId: 'sys_deuterium'\n    },\n    \"energy\": {\n      used_value: Math.floatVal('{PLANET_ENERGY}'),\n      start_value: Math.floatVal('{ENERGY_BALANCE_NUMBER}'),\n      max_value: Math.floatVal('{ENERGY_MAX_NUMBER}'),\n      resourceNameLongId: 'sys_energy'\n    }\n  };\n\n  sn_timers.unshift({className: 'top_metal_v2', type: TIMER_COUNTER, options: navbarResources.metal});\n  sn_timers.unshift({className: 'top_crystal_v2', type: TIMER_COUNTER, options: navbarResources.crystal});\n  sn_timers.unshift({className: 'top_deuterium_v2', type: TIMER_COUNTER, options: navbarResources.deuterium});\n</script>\n\n<!-- navbar_resources EOF -->\n"
  },
  {
    "path": "design/templates/OpenGame/_page/navbar_resources_flex.tpl.html",
    "content": "<!-- IF $NAVBAR_RESOURCES_ALLY -->\n<!-- DEFINE $NAVBAR_RESOURCES_HIDE_STORAGE = 1 -->\n<!-- DEFINE $NAVBAR_RESOURCES_HIDE_FLEETS = 1 -->\n<!-- ENDIF -->\n\n<!--suppress HtmlUnknownTarget -->\n<div id=\"navbar_resources_flex\" class=\"navbar_resources_flex header no_select {$NAVBAR_RESOURCES_CLASS}\">\n  <div class=\"navbar_resources_flex_title\">\n    <!-- IF $NAVBAR_RESOURCES_ALLY -->\n    {L_sys_alliance}\n    <!-- ELSE -->\n    {L_sys_resources}\n    <!-- ENDIF -->\n  </div>\n\n  <div class=\"navbar_resbar_flex_wrap2\">\n    <div class=\"navbar_resbar_flex_wrap1\">\n      <div class=\"cell navbar_resources_flex_resource\" data-resource=\"metal\">\n        <div class=\"navbar_resource_image\"><img class=\"image_close\" src=\"design/images/icon_metal.png\" alt=\"{L_sys_metal}\"/></div>\n\n        <div class=\"navbar_resource_numbers\">\n          <div class=\"top_metal_v2\">\n            {PLANET_METAL_TEXT}\n          </div>\n\n          <!-- IF ! $NAVBAR_RESOURCES_HIDE_STORAGE -->\n          <div>\n            <!-- DEFINE $T_MAXIMUM = '{PLANET_METAL_MAX}' -->\n            <!-- DEFINE $T_VALUE = '{PLANET_METAL}' -->\n            <!-- INCLUDE _number_percent_class -->\n            <span class=\"{$T_VALUE_CLASS}\">{PLANET_METAL_MAX_NO_COLOR}</span>\n          </div>\n          <!-- ENDIF -->\n\n          <!-- IF ! $NAVBAR_RESOURCES_HIDE_FLEETS -->\n          <div>{PLANET_METAL_FLEET_TEXT}</div>\n          <!-- ENDIF -->\n        </div>\n      </div>\n\n      <div class=\"cell navbar_resources_flex_resource\" data-resource=\"crystal\">\n        <div class=\"navbar_resource_image\"><img class=\"image_close\" src=\"design/images/icon_crystal.png\" alt=\"{L_sys_crystal}\"/></div>\n\n        <div class=\"navbar_resource_numbers\">\n          <div class=\"top_crystal_v2\">\n            {PLANET_CRYSTAL_TEXT}\n          </div>\n\n          <!-- IF ! $NAVBAR_RESOURCES_HIDE_STORAGE -->\n          <div>\n            <!-- DEFINE $T_MAXIMUM = '{PLANET_CRYSTAL_MAX}' -->\n            <!-- DEFINE $T_VALUE = '{PLANET_CRYSTAL}' -->\n            <!-- INCLUDE _number_percent_class -->\n            <span class=\"{$T_VALUE_CLASS}\">{PLANET_CRYSTAL_MAX_NO_COLOR}</span>\n          </div>\n          <!-- ENDIF -->\n\n          <!-- IF ! $NAVBAR_RESOURCES_HIDE_FLEETS -->\n          <div>{PLANET_CRYSTAL_FLEET_TEXT}</div>\n          <!-- ENDIF -->\n        </div>\n      </div>\n    </div>\n\n    <div class=\"navbar_resbar_flex_wrap1\">\n      <div class=\"cell navbar_resources_flex_resource\" data-resource=\"deuterium\">\n        <div class=\"navbar_resource_image\"><img class=\"image_close\" src=\"design/images/icon_deuterium.png\" alt=\"{L_sys_deuterium}\"/></div>\n\n        <div class=\"navbar_resource_numbers\">\n          <div class=\"top_deuterium_v2\">\n            {PLANET_DEUTERIUM_TEXT}\n          </div>\n\n          <!-- IF ! $NAVBAR_RESOURCES_HIDE_STORAGE -->\n          <div>\n            <!-- DEFINE $T_MAXIMUM = '{PLANET_DEUTERIUM_MAX}' -->\n            <!-- DEFINE $T_VALUE = '{PLANET_DEUTERIUM}' -->\n            <!-- INCLUDE _number_percent_class -->\n            <span class=\"{$T_VALUE_CLASS}\">{PLANET_DEUTERIUM_MAX_NO_COLOR}</span>\n          </div>\n          <!-- ENDIF -->\n\n          <!-- IF ! $NAVBAR_RESOURCES_HIDE_FLEETS -->\n          <div>{PLANET_DEUTERIUM_FLEET_TEXT}</div>\n          <!-- ENDIF -->\n        </div>\n\n      </div>\n\n      <!-- IF ! $NAVBAR_RESOURCES_ALLY -->\n      <div class=\"cell navbar_resources_flex_resource\" data-resource=\"energy\">\n        <div class=\"navbar_resource_image\"><img class=\"image_close\" src=\"design/images/icon_energy.png\" alt=\"{L_sys_energy}\"/></div>\n\n        <div class=\"navbar_resource_numbers\">\n          <div>{ENERGY_BALANCE}</div>\n\n          <!-- IF ! $NAVBAR_RESOURCES_HIDE_STORAGE -->\n          <div>\n            <!-- DEFINE $T_MAXIMUM = '{ENERGY_MAX_NUMBER}' -->\n            <!-- DEFINE $T_VALUE = '{PLANET_ENERGY}' -->\n            <!-- INCLUDE _number_percent_class -->\n            <span class=\"{$T_VALUE_CLASS}\">{ENERGY_MAX_NUMBER_TEXT_NO_COLOR}</span>\n          </div>\n          <!-- ENDIF -->\n        </div>\n      </div>\n      <!-- ELSE -->\n      <div class=\"cell navbar_resources_flex_resource\">\n        <div class=\"navbar_resource_image\"><img class=\"image_close\" src=\"design/images/icon_dark_matter.png\" alt=\"{L_sys_dark_matter}\"/></div>\n\n        <div class=\"navbar_resource_numbers\">\n          <div id='top_dark_matter'>\n            <!-- IF DARK_MATTER_TEXT -->{DARK_MATTER_TEXT}<!-- ELSE -->0<!-- ENDIF -->\n          </div>\n        </div>\n      </div>\n      <!-- ENDIF -->\n    </div>\n  </div>\n</div>\n"
  },
  {
    "path": "design/templates/OpenGame/_page/navbar_resources_horizontal.tpl.html",
    "content": "<table class=\"border_image_small\" id=\"navbar_resources\">\n<tr class=\"c_l\">\n  <th nowrap><!-- IF $NAVBAR_RESOURCES_ALLY -->{L_sys_alliance}<!-- ELSE -->{L_sys_resources}<!-- ENDIF --></th>\n  <th data-resource=\"metal\">{L_sys_metal}</th>\n  <th data-resource=\"crystal\">{L_sys_crystal}</th>\n  <th data-resource=\"deuterium\">{L_sys_deuterium}</th>\n  <!-- IF $NAVBAR_RESOURCES_ALLY -->\n  <th>{L_sys_dark_matter}</th>\n  <!-- ELSE -->\n  <th data-resource=\"energy\">{L_sys_energy}</th>\n  <!-- ENDIF -->\n</tr>\n\n<tr class=\"c_r\">\n  <th class=\"c_l\" nowrap>{L_tech_storage}</th>\n  <td class='top_metal_v2' data-resource=\"metal\">{PLANET_METAL_TEXT}</td>\n  <td class='top_crystal_v2' data-resource=\"crystal\">{PLANET_CRYSTAL_TEXT}</td>\n  <td class='top_deuterium_v2' data-resource=\"deuterium\">{PLANET_DEUTERIUM_TEXT}</td>\n  <!-- IF $NAVBAR_RESOURCES_ALLY -->\n  <td id='top_dark_matter'>{DARK_MATTER_TEXT}</td>\n  <!-- ELSE -->\n  <td data-resource=\"energy\">{ENERGY_BALANCE}</td>\n  <!-- ENDIF -->\n</tr>\n\n<!-- IF ! $NAVBAR_RESOURCES_ALLY && (! $NAVBAR_RESOURCES_HIDE_STORAGE || ! $IN_NAVBAR) -->\n<tr class=\"c_r\">\n  <th class=\"c_l\" nowrap>{L_tech_storage_max}</th>\n  <td data-resource=\"metal\">{PLANET_METAL_MAX_TEXT}</td>\n  <td data-resource=\"crystal\">{PLANET_CRYSTAL_MAX_TEXT}</td>\n  <td data-resource=\"deuterium\">{PLANET_DEUTERIUM_MAX_TEXT}</td>\n  <td data-resource=\"energy\">{ENERGY_MAX}</td>\n</tr>\n<!-- ENDIF -->\n\n<!-- IF ! $NAVBAR_RESOURCES_HIDE_FLEETS -->\n<tr class=\"c_r\">\n  <th class=\"c_l\" nowrap>{L_sys_fleet_and}</th>\n  <td data-resource=\"metal\">{PLANET_METAL_FLEET_TEXT}</td>\n  <td data-resource=\"crystal\">{PLANET_CRYSTAL_FLEET_TEXT}</td>\n  <td data-resource=\"deuterium\">{PLANET_DEUTERIUM_FLEET_TEXT}</td>\n  <td>-</td>\n</tr>\n<!-- ENDIF -->\n</table>\n"
  },
  {
    "path": "design/templates/OpenGame/_page/navbar_resources_vertical.tpl.html",
    "content": "<table class=\"border_image_small\" id=\"navbar_resources\">\n<tr class=\"c_l\">\n  <th nowrap><!-- IF $NAVBAR_RESOURCES_ALLY -->{L_sys_alliance}<!-- ELSE -->{L_sys_resources}<!-- ENDIF --></th>\n  <th class=\"c_l\" nowrap>{L_tech_storage}</th>\n  <!-- IF ! $NAVBAR_RESOURCES_ALLY && ! $NAVBAR_RESOURCES_HIDE_STORAGE -->\n  <th class=\"c_l\" nowrap>{L_tech_storage_max}</th>\n  <!-- ENDIF -->\n  <!-- IF ! $NAVBAR_RESOURCES_HIDE_FLEETS -->\n  <th class=\"c_l\" nowrap>{L_sys_fleet_and}</th>\n  <!-- ENDIF -->\n</tr>\n\n<tr class=\"c_r\" data-resource=\"metal\">\n  <th class=\"c_l\">{L_sys_metal}</th>\n  <td class=\"top_metal_v2\">{PLANET_METAL_TEXT}</td>\n  <!-- IF ! $NAVBAR_RESOURCES_ALLY && ! $NAVBAR_RESOURCES_HIDE_STORAGE -->\n  <td>{PLANET_METAL_MAX_TEXT}</td>\n  <!-- ENDIF -->\n  <!-- IF ! $NAVBAR_RESOURCES_HIDE_FLEETS -->\n  <td>{PLANET_METAL_FLEET_TEXT}</td>\n  <!-- ENDIF -->\n</tr>\n\n<tr class=\"c_r\" data-resource=\"crystal\">\n  <th class=\"c_l\">{L_sys_crystal}</th>\n  <td class=\"top_crystal_v2\">{PLANET_CRYSTAL_TEXT}</td>\n  <!-- IF ! $NAVBAR_RESOURCES_ALLY && ! $NAVBAR_RESOURCES_HIDE_STORAGE -->\n  <td>{PLANET_CRYSTAL_MAX_TEXT}</td>\n  <!-- ENDIF -->\n  <!-- IF ! $NAVBAR_RESOURCES_HIDE_FLEETS -->\n  <td>{PLANET_CRYSTAL_FLEET_TEXT}</td>\n  <!-- ENDIF -->\n</tr>\n\n<tr class=\"c_r\" data-resource=\"deuterium\">\n  <th class=\"c_l\">{L_sys_deuterium}</th>\n  <td id='top_deuterium_v2'>{PLANET_DEUTERIUM_TEXT}</td>\n  <!-- IF ! $NAVBAR_RESOURCES_ALLY && ! $NAVBAR_RESOURCES_HIDE_STORAGE -->\n  <td>{PLANET_DEUTERIUM_MAX_TEXT}</td>\n  <!-- ENDIF -->\n  <!-- IF ! $NAVBAR_RESOURCES_HIDE_FLEETS -->\n  <td>{PLANET_DEUTERIUM_FLEET_TEXT}</td>\n  <!-- ENDIF -->\n</tr>\n\n<!-- IF $NAVBAR_RESOURCES_ALLY -->\n<tr class=\"c_r\">\n  <th class=\"c_l\">{L_sys_dark_matter}</th>\n  <td id='top_dark_matter'>{DARK_MATTER_TEXT}</td>\n\n  <!-- IF ! $NAVBAR_RESOURCES_HIDE_FLEETS -->\n  <td>-</td>\n  <!-- ENDIF -->\n</tr>\n<!-- ELSE -->\n<tr class=\"c_r\" data-resource=\"energy\">\n  <th class=\"c_l\">{L_sys_energy}</th>\n  <td>{ENERGY_BALANCE}</td>\n  <!-- IF ! $NAVBAR_RESOURCES_HIDE_STORAGE -->\n  <td>{ENERGY_MAX}</td>\n  <!-- ENDIF -->\n\n  <!-- IF ! $NAVBAR_RESOURCES_HIDE_FLEETS -->\n  <td>-</td>\n  <!-- ENDIF -->\n</tr>\n<!-- ENDIF -->\n</table>\n"
  },
  {
    "path": "design/templates/OpenGame/_paging.tpl.html",
    "content": "<!-- IF .paging -->\n<div class=\"paging contFJ\">\n  <!-- BEGIN paging -->\n  <div>\n    <!-- IF paging.HREF -->\n    <a href=\"{PAGING_ROOT}{paging.PAGE_NUM}\" class=\"link {paging.STYLE}\">{paging.TEXT}</a>\n    <!-- ELSE -->\n    <span class=\"{paging.STYLE}\">{paging.TEXT}</span>\n    <!-- ENDIF -->\n  </div>\n  <!-- END paging -->\n</div>\n<!-- ENDIF -->"
  },
  {
    "path": "design/templates/OpenGame/_redirect.tpl.html",
    "content": "<script type=\"text/javascript\">\n  document.location.assign(\"{URL}\");\n</script>\n"
  },
  {
    "path": "design/templates/OpenGame/_result_message.tpl.html",
    "content": "<!-- Preventing double include -->\n<!-- IF ! $PAGE_RESULT_INCLUDED -->\n<!-- DEFINE $PAGE_RESULT_INCLUDED = 1 -->\n\n<!-- IF ! $RESULT_TABLE_EXISTS -->\n  <!-- BEGIN result -->\n    <!-- IF result.MESSAGE -->\n      <!-- IF ! $RESULT_TABLE_EXISTS -->\n        <!-- DEFINE $RESULT_TABLE_EXISTS = 'true' -->\n        <div class=\"border_image_small\" id=\"global_status_window\">\n          <div class=\"header\">{L_sys_result_operation}</div>\n          <div class=\"cell\">\n            <ul id=\"global_status_list\">\n      <!-- ENDIF -->\n        <!-- IF result.STATUS == 0 -->\n          <!-- DEFINE $RESULT_CLASS = 'ok' -->\n        <!-- ELSEIF result.STATUS == 1 -->\n          <!-- DEFINE $RESULT_CLASS = 'warning' -->\n        <!-- ELSEIF result.STATUS == 8 -->\n          <!-- DEFINE $RESULT_CLASS = 'notice' -->\n        <!-- ELSE -->\n          <!-- DEFINE $RESULT_CLASS = 'error' -->\n        <!-- ENDIF -->\n            <li class=\"{$RESULT_CLASS}\">{result.MESSAGE}</li>\n    <!-- ENDIF -->\n  <!-- END result -->\n\n  <!-- IF $RESULT_TABLE_EXISTS -->\n            </ul>\n          </div>\n        </div>\n  <!-- ENDIF -->\n<!-- ENDIF -->\n\n<!-- ENDIF -->\n"
  },
  {
    "path": "design/templates/OpenGame/_table.tpl.html",
    "content": "{NAME}\n\n<!-- IF .rows -->\n<table>\n  <!-- BEGIN rows -->\n\n  <!-- IF rows.CLASS -->\n  <!-- DEFINE $ROW_CLASS = '{rows.CLASS}' -->\n  <!-- ELSE -->\n  <!--DEFINE $ROW_CLASS = 'c_c' -->\n  <!-- ENDIF -->\n\n  <tr class=\"{$ROW_CLASS}\">\n    <!-- IF rows.NAME -->\n\n    <!-- IF rows.NAME_CLASS -->\n    <!-- DEFINE $ROW_NAME_CLASS = '{rows.NAME_CLASS}' -->\n    <!-- ELSE -->\n    <!-- DEFINE $ROW_NAME_CLASS = 'c_c' -->\n    <!-- ENDIF -->\n\n    <th class=\"{$ROW_NAME_CLASS}\">\n      {rows.NAME}\n    </th>\n    <!-- ENDIF -->\n\n\n    <!-- BEGIN cells -->\n\n\n\n    <!-- IF cells.CLASS -->\n    <!-- DEFINE $CELL_CLASS = '{cells.CLASS}' -->\n    <!-- ELSE -->\n    <!-- DEFINE $CELL_CLASS = '' -->\n    <!-- ENDIF -->\n\n    <!-- IF cells.NOWRAP -->\n    <!-- DEFINE $CELL_NOWRAP = 'nowrap' -->\n    <!-- ELSE -->\n    <!-- DEFINE $CELL_NOWRAP = '' -->\n    <!-- ENDIF -->\n\n    <!-- IF rows.HEADER || cells.HEADER -->\n    <th class=\"{$CELL_CLASS}\" {$CELL_NOWRAP}>\n    <!-- ELSE -->\n    <td class=\"{$CELL_CLASS}\" {$CELL_NOWRAP}>\n    <!-- ENDIF -->\n\n\n      {cells.CONTENT}\n\n\n\n    <!-- IF ! rows.HEADER && ! cells.HEADER -->\n    </td>\n    <!-- ELSE -->\n    </th>\n    <!-- ENDIF -->\n\n\n\n    <!-- END cells -->\n  </tr>\n  <!-- END rows -->\n</table>\n<!-- ENDIF -->\n"
  },
  {
    "path": "design/templates/OpenGame/_template.css",
    "content": "@media only screen and (min-device-width : 320px) and (max-device-width : 480px) {  }\n/* Smartphones (landscape) ----------- */\n@media only screen and (min-width : 321px) {  }\n/* Smartphones (portrait) ----------- */\n@media only screen and (max-width : 320px) {  }\n/* iPads (portrait and landscape) ----------- */\n@media only screen and (min-device-width : 768px) and (max-device-width : 1024px) {  }\n/* iPads (landscape) ----------- */\n@media only screen and (min-device-width : 768px) and (max-device-width : 1024px) and (orientation : landscape) {  }\n/* iPads (portrait) ----------- */\n@media only screen and (min-device-width : 768px) and (max-device-width : 1024px) and (orientation : portrait) {  }\n/* Desktops and laptops medium-res */\n@media only screen and (min-width : 760px) {  }\n/* Desktops and laptops ----------- */\n@media only screen and (min-width : 1224px) {  }\n/* Large screens ----------- */\n@media only screen and (min-width : 1824px) {  }\n/* iPhone 4+ ----------- */\n@media only screen and (-webkit-min-device-pixel-ratio : 1.5), only screen and (min-device-pixel-ratio : 1.5) {  }\n\n\n.c_c, th.c_c, td.c_c, tr.c_c td, tr.c_c th, .cell_center, tr.cell_center td, tr.cell_center th, table.c_c tr,\ntable > tbody > tr > th.c_c, table > tbody > tr > td.c_c\n{\n  text-align: center;\n}\n\n.c_l, th.c_l, td.c_l, tr.c_l td, tr.c_l th, .cell_left, tr.cell_left td, tr.cell_left th, td.c, table.c_l tr,\ntable > tbody > tr > th.c_l, table > tbody > tr > td.c_l\n{\n  text-align: left;\n}\n\n.c_r, th.c_r, td.c_r, tr.c_r td, tr.c_r th, .cell_right, tr.cell_right td, tr.cell_right th, table.c_r tr,\ntable > tbody > tr > th.c_r, table > tbody > tr > td.c_r\n{\n  text-align: right;\n}\n\n/* TEMPLATE BOILERPLATE ********************************************************************************************* */\nhtml {\n  font-size: 68.75%;\n  /*font-size: 100%;*/\n}\n\nbody {\n  font-family: Tahoma, sans-serif;\n  font-size: 1em;\n  font-weight: bold;\n  line-height: 1.1;\n\n  background-attachment: fixed;\n  background-repeat: no-repeat;\n  background-position: top;\n  background-size: cover;\n}\n\n/* #__page = #__menu + #__navbar_wrapper */\n#__page {\n  display: flex;\n}\n#__menu {\n}\n/* #__navbar_wrapper = #navbar_container + #__content_wrapper*/\n#__navbar_wrapper {\n  flex-basis: 100%;\n  flex-shrink: 1;\n  flex-grow: 1;\n}\n/* #__content_wrapper = #__navbar_additions + #__content */\n#__content_wrapper {\n  /* for all that matters*/\n  flex-basis: 100%;\n  text-align: center;\n}\n#__navbar_additions {\n}\n/* #__content = {page rendered here} + #copyright */\n#__content {\n}\n#copyright {\n}\n\n\nimg {\n  border: 0;\n}\n\na {\n  text-decoration: none;\n  font: inherit;\n  color: inherit;\n}\n\nul { /* Unmarked list */\n  margin: 0;\n  padding-left: 1.8em;\n  text-align: left;\n}\nli {\n  padding-top: 0.3em;\n}\n\nh1 {\n  font-size: 2em;\n  border-bottom: none; /* 0 #344566 solid */\n}\n\ninput, select, textarea {\n  font: inherit;\n  color: inherit;\n\n  padding-left: 0.3em;\n  padding-right: 0.3em;\n\n  border: 0.1em solid;\n  -o-border-radius: 0.4em;\n  -moz-border-radius: 0.4em;\n  -webkit-border-radius: 0.4em;\n  -khtml-border-radius: 0.4em;\n  border-radius: 0.4em;\n}\ninput.button {\n  padding: 0.1em 0.3em;\n}\ntextarea {\n  width: 92%;\n  padding: 0;\n}\ntextarea.ui-button {\n  padding: 0.4em 1em;\n\n  border: 0.1em solid;\n}\n\n/* Table ======================================================================================================== */\ntable {\n  font: inherit;\n  color: inherit;\n\n  margin-left: auto;\n  margin-right: auto;\n}\n\nth, td {\n  padding: 0.3em;\n\n  border: 0.1em solid;\n}\n\ntd.l {\n  vertical-align: top;\n  padding: 0.3em;\n}\n\ntable table {\n  border: 0;\n}\n\n.noborder, .noborder tr, .noborder th {\n  border: 0;\n}\n\n.markup, .markup > tbody > tr, .markup > tbody > tr > th, .markup > tbody > tr > td {\n  background: transparent;\n  padding: 0;\n  margin: 0;\n  border: 0;\n}\n\n/* jQuery-UI patches ********************************************************************************************* */\n\n/* jQuery-UI Dialogs ================================================================================================ */\n/* Hack: Setting no background and border for dialogs */\nbody .ui-dialog {\n  background: transparent;\n  border: none;\n  padding: 0.2em; /* Original */\n}\n/* Turning off headers in dialogs */\n.ui-dialog .ui-dialog-titlebar {\n  display: none;\n}\n/* Turning off resize grapplers */\n.ui-dialog .ui-resizable-se {\n  display: none;\n}\n/* Setting no-padding to content inside dialog */\n.ui-dialog .ui-dialog-content {\n  padding: 0;\n}\n.ui-dialog .ui-dialog-buttonpane {\n  padding: 0.3em 1em 0.5em 0.4em;  /* Original */\n}\n\n.ui-dialog .ui-dialog-titlebar {\n  margin-bottom: 0.2em;\n}\n.ui-dialog .ui-dialog-buttonpane {\n  margin-top: 0.2em;\n}\n\n.ui-button,\n.ui-button .ui-button-text\n{\n  line-height: inherit;\n}\n.ui-textfield {\n  font: inherit;\n  color: inherit;\n  outline: none;\n}\ninput[type=button].ui-textfield, input[type=submit].ui-textfield {\n  text-align: inherit;\n}\ninput[type=text].ui-textfield, input[type=password].ui-textfield, input[type=file].ui-textfield, textarea.ui-textfield {\n  text-align: left;\n  background-image: none;\n  cursor: text;\n}\n\ninput[type=checkbox] {\n  -webkit-appearance: none;\n  -moz-appearance: none;\n  appearance: none;\n\n  width: 1.8181818181818em;\n  height: 1.8181818181818em;\n\n  border-radius: 0.4em;\n}\n\n/*  Patching this theme for ui-slider */\n/*.style .ui-slider a:link { width: 1.2em; height: 1.2em; }*/\n/*.style .ui-slider a:hover { width: 1.2em; height: 1.2em; }*/\n\n.ui-button-text-only .ui-button-text {\n  padding: 0.6em 1em;\n}\n\ninput.ui-button {\n  padding: 0.6em 1em;\n}\n\n/* jQuery-UI slider patches ========================================================================================= */\n.ui-slider { /* Embedding slider to page */\n  font-size: 125%;\n  margin: 0.75em;\n  padding-right: 4.4em;\n  width: auto;\n  height: 1em;\n}\n.ui-slider .ui-slider-handle { /* Increasing slider a bit */\n  width: 1.4em;\n  height: 1.4em;\n}\n.ui-state-active.ui-slider-handle {\n  box-shadow: none;\n  margin-top: 0;\n  margin-right: 0;\n  margin-bottom: 0;\n}\n\n/* Pseudo button  ********************************************************************************************* */\n/* Main pseudo button magic*/\n.ui-button,\n.button_pseudo\n{\n  box-shadow: 0.3em 0.3em rgba(0,0,0,0.750);\n  margin: 0.5em;\n}\n\n.ui-state-active,\n.button_pseudo_pressed\n{\n  box-shadow: none;\n  margin: 0.8em 0.2em 0.2em 0.8em;\n}\n\n.ui-tabs.ui-widget-content {\n  background-color: transparent;\n  background-image: none;\n}\n\n.button_pseudo {\n  cursor: pointer;\n  border: 0.1em solid;\n}\n\n.button_pseudo_pressed {\n}\n\n/* Text pseudo button */\n.button_pseudo:not(img) {\n  border-radius: 0.4em;\n  display: inline-block;\n  text-align: center;\n  vertical-align: middle;\n  /*padding: 0.475em 1em 0.475em 1em;*/\n  padding: 0.6em 1em;\n}\n\n/* Image pseudo button */\nimg.button_pseudo {\n  filter: alpha(Opacity=50);\n  opacity: 0.5;\n}\nimg.button_pseudo:hover {\n  filter: alpha(Opacity=75);\n  opacity: 0.75;\n}\nimg.button_pseudo.button_pseudo_pressed {\n  filter: alpha(Opacity=100);\n  opacity: 1;\n}\n\ninput[type=text].coordinate {\n  max-width: 3em;\n  padding: 0.363636363636em;\n}\n\n\n\n/* Page layout  ********************************************************************************************* */\n\n/* Left menu ======================================================================================================== */\n#left_menu {\n  margin: 0.5em 1em 0 0.5em;\n  text-align: left;\n  z-index: 999;\n  /*font-weight: bold;*/\n  width: 13em; /* 100% */\n}\n#left_menu.MENU_BIG_BUTTONS {\n  font-size: 150%;\n}\n#left_menu.MENU_ABSOLUTE {\n  position: absolute;\n}\n#left_menu.MENU_START_HIDDEN {\n  display: none;\n}\n#left_menu.MENU_WHITE_BUTTONS td a {\n  color: white !important;\n}\n\n#left_menu td, #left_menu th {\n  min-height: 1.5em;\n  padding: 0.25em 0.5em 0.25em 0.5em;\n  border: none;\n}\n#left_menu td {\n  padding-left: 1em;\n}\n#left_menu.MENU_NARROW_CELLS td, #left_menu.MENU_NARROW_CELLS th {\n  padding: 0 0.5em;\n  border: 0.1em solid;\n  border-top: none;\n}\n\n#left_menu th {\n  font-size: 1.1em;\n  background-repeat: repeat-x;\n  text-align: center;\n}\n#left_menu th a {\n  width: 80%;\n}\n#left_menu td a {\n  width: 83%;\n}\n#left_menu.MENU_NARROW_CELLS th a {\n  width: 100%;\n}\n#left_menu.MENU_NARROW_CELLS td a {\n  width: 100%;\n}\n\n\n\n#left_menu > tbody > tr > td span, #left_menu > tbody > tr > th span {\n  display: inline-block;\n  vertical-align: top;\n}\n\n#left_menu th a:not(.lm_with_image) {\n  min-height: 1.4em;\n}\n#left_menu a:not(.lm_with_image) {\n  padding: 0.2em 1em;\n  border: 0.1em solid;\n  border-radius: 0.3em;\n\n  height: 100%;\n  display: inline-block;\n}\n#left_menu.MENU_NARROW_CELLS a:not(.lm_with_image) {\n  padding: 0;\n  border: none;\n\n  height: 100%;\n  display: inline-block;\n}\n\n#left_menu_show {\n  z-index: 9999;\n}\n#left_menu_show.MENU_SHOW_HIDE_FIXED {\n  position: fixed;\n}\n#left_menu_show.MENU_SHOW_HIDE_HIDDEN {\n  display: none;\n}\n\n#menu_show_hidden {\n  cursor: pointer;\n}\n\n.menu_visible {\n  width: 1.5em;\n  margin-left: 0.5em;\n  height: 100%;\n  position: absolute;\n  top: 0;\n  right: 0;\n\n  cursor: pointer;\n  z-index: 9999;\n}\n\n.menu_drag {\n  margin-right: 0.5em;\n  width: 1.5em;\n  height: 100%;\n  position: absolute;\n  cursor: pointer;\n  left: 0;\n  background-size: contain !important;\n}\n\n#menu_news .fresh {\n  display: inline;\n  float: right;\n}\n\n.i_icon_visible {\n  background-image: url(\"/design/images/icon_eye_visible.png\");\n  background-size: contain;\n  background-repeat: no-repeat;\n}\n\n.i_icon_invisible {\n  background-image: url(\"/design/images/icon_eye_invisible.png\");\n  background-size: contain;\n  background-repeat: no-repeat;\n}\n\n.menu_icon {\n  margin-right: 0.5em;\n  height: 1.3em;\n  width: 1.3em;\n  /*border: none;*/\n}\n\n.menu_icons { /* New menu icon*/\n  margin-right: 0.5em;\n}\n\n\n\n.menu_text_t, .menu_text_b {\n  text-align: center;\n}\n\n#menu_premium .link_b, #menu_premium .menu_icon {\n  float: left;\n}\n\n#menu_admin_server_time, #menu_admin_version_info {\n  text-align: center;\n}\n\n#menu_premium_level {\n  display: inline;\n  float: right;\n}\n\n#global_status_window {\n  max-width: 50em;\n  margin: 0.5em;\n  display: block;\n}\n\n#global_status_list {\n  text-align: justify;\n  padding: 0.5em;\n}\n\n#global_status_list > li {\n  margin-left: 1.5em;\n}\n\n/* NavBar ======================================================================================================== */\n\n#navbar {\n  margin: auto;\n  background-color: transparent;\n  background-image: none;\n  margin-top: 0.5em;\n  margin-bottom: 0.5em;\n}\n\n.navbar-festival_button {\n  display: inline-block;\n\n  margin: 0.5em;\n  width: 5.818181818182em;\n  height: 5.818181818182em;\n}\n\n.navbar-festival_button:hover > a,\n.navbar-festival_button > a:hover {\n  background-color: transparent;\n}\n\n#navbar-blitz-button {\n  margin: 0.5em 0;\n}\n\n#navbar_planet_select {\n  margin: 0.5em 0.5em;\n  flex-grow: 10;\n  font-size: 125%;\n  max-width: 20em;\n}\n\n#navbar_player_count {\n  margin: 0.5em 0.5em;\n  font-size: 125%;\n}\n\n#navbar-container-time-item {\n  flex-grow: 10;\n}\n\n#navbar-container-button {\n  display: flex;\n  flex-wrap: wrap;\n  justify-content: space-around;\n}\n.navbar-container-button-que-empty {\n  position: absolute;\n  left: 0;\n  top: 33.3%;\n\n  width: 100%;\n}\n\n#navbar_resources {\n  margin: 0.5em;\n}\n\n.navbar_button {\n  display: inline-block;\n\n  position: relative;\n\n  width: 5.818181818182em;\n  height: 5.818181818182em;\n\n  margin: 0.25em;\n\n  text-align: center;\n}\n.navbar_button.wide {\n  width: 11.63636363636em;\n}\n\n.navbar_button_image {\n  position: absolute;\n  left: 0;\n  top: 0;\n\n  width: 100%;\n}\n.navbar_button_que {\n  position: absolute;\n\n  display: flex;\n  flex-direction: column;\n  justify-content: space-between;\n\n  width: 100%;\n  height: 100%;\n}\n\n.navbar_button_que_current_unit {\n  flex-grow: 1;\n\n  display: flex;\n  align-items: center;\n  justify-content: center;\n}\n\n.sn-dialog-confirm {\n  padding: 1em;\n}\n\n#navbar_button_dm_mm.posRB {\n  /*position: absolute;*/\n  /*right: 0;*/\n  bottom: 2.3em;\n}\n\n#navbar_button_dm_dm.posRB {\n  /*position: absolute;*/\n  /*right: 0;*/\n  bottom: 1.2em;\n}\n\n#navbar_button_dm_plus.posLB {\n  /*position: absolute;*/\n  /*right: 0;*/\n  bottom: 1.75em;\n}\n\n.navbar_button_dm_debit_line {\n  border-top: 2px solid;\n}\n\n#navbar_refresh {\n  display: none;\n  margin: 10px 0;\n  text-align: center;\n}\n\n#note_table {\n  margin: 0.5em auto;\n  max-width: 70em;\n}\n\n#note_main_table {\n  max-width: 70em;\n}\n\n/* Page Body ======================================================================================================== */\n#page_body {\n  margin-left: 14em;\n  padding-left: 1em;\n  padding-right: 1em;\n  width: auto;\n}\n\n\n/* Copyright ======================================================================================================== */\n#copyright {\n  text-align: center;\n}\n#copyright #supernova_ws {\n  font-weight: normal;\n}\n#copyright, #copyright #supernova_ws {\n  padding-top: 1em;\n}\n\n\n/* Service Classes  ********************************************************************************************* */\n.link {\n  text-decoration: underline;\n  font-weight: bold;\n  cursor: pointer;\n}\n\n.link_b {\n  border-bottom: 0.1em solid;\n  font-weight: bold;\n  cursor: pointer;\n}\n\n.link_action {\n  border-bottom: 0.1em dashed;\n  font-weight: bold;\n  cursor: pointer;\n}\n\n.link_external {\n  border-bottom: 0.3em double;\n  font-weight: bold;\n  cursor: pointer;\n}\n\n.pointer {\n  cursor: pointer;\n}\n\n/* TODO DEPRECATED  ======================================================================================================== */\n.left {\n  position: absolute;\n  left: 0;\n}\n.right {\n  position: absolute;\n  right: 0;\n}\n.bottom {\n  position: absolute;\n  bottom: 0;\n}\n.left_top\n{\n  position: absolute;\n  left: 0;\n  top: 0;\n}\n.left_bottom\n{\n  position: absolute;\n  bottom: 0;\n  left: 0;\n}\n.right_top {\n  position: absolute;\n  right: 0;\n  top: 0;\n}\n.right_bottom {\n  position: absolute;\n  right: 0;\n  bottom: 0;\n}\n.icon_25per_left_1 {\n  position: absolute;\n  top: 0;\n  left: 0;\n  width: 25%;\n  height: 25%\n}\n.icon_25per_left_2 {\n  position: absolute;\n  top: 25%;\n  left: 0;\n  width: 25%;\n  height: 25%\n}\n.icon_25per_left_3 {\n  position: absolute;\n  top: 50%;\n  left: 0;\n  width: 25%;\n  height: 25%;\n}\n.icon_25per_left_4 {\n  position: absolute;\n  top: 75%;\n  left: 0;\n  width: 25%;\n  height: 25%;\n}\n.icon_25per_right_4 {\n  position: absolute;\n  bottom: 0;\n  right: 0;\n  width: 25%;\n  height: 25%;\n}\n.icon_25per_top_2 {\n  position: absolute;\n  top: 0;\n  left: 25%;\n  width: 25%;\n  height: 25%;\n}\n.icon_fill {\n  height: 100%;\n  width: 100%;\n  border-style: none;\n  cursor: pointer;\n}\n.fill_parent {\n  height: 100%;\n  width: 100%;\n}\n.va_m {\n  vertical-align: middle;\n}\n.transparent_cell {\n  background-color: transparent;\n  border: 0;\n}\n\n.global_admin_warning{\n  text-align: center;\n  width: 100%;\n  font-size: 2em;\n  font-weight: bold;\n}\n\n\n/* News and Surveys ================================================================================================= */\n#fresh_news_table {\n  margin-top: 1em;\n}\n\n.news_header {\n  padding: 0.6em 1em;\n  margin: 0.2em;\n}\n.news_fresh, .news_future {\n  font-weight: bold;\n}\n.news_text {\n  text-align: justify;\n  padding: 0.6em 1em;\n}\n.news_link {\n  text-decoration: underline;\n  font-weight: bold;\n  cursor: pointer;\n}\n.news_hide {\n  display: none;\n}\n\n.survey_block {\n  text-align: left;\n  margin: 0 1em;\n}\n.survey_block [survey_id] {\n  width: 93%;\n  text-align: left;\n}\n.survey_header, .survey_voted {\n  padding: 0.6em 1em;\n  border: none;\n}\n.survey_voted {\n  text-align: center;\n}\n.survey_confirm {\n  padding: 0 1em;\n  border: none;\n}\n\n.survey_votes {\n  position: relative;\n}\n.survey_votes_text {\n  position: relative;\n  z-index: 1;\n  padding: 0.6em 10em 0.6em 1em;\n}\n.survey_votes_bar {\n  position: absolute;\n  left: 0;\n  top: 0;\n  height: 100%;\n\n  z-index: 0;\n}\n.survey_votes_number, .survey_votes_percent {\n  position: absolute;\n  right: 1em;\n  top: 0.6em;\n  text-align: right;\n  width: 100%;\n  height: 100%;\n  z-index: 1;\n}\n.survey_votes_number {\n  right: 5em;\n}\n\n.survey_length {\n  padding: 0.6em 1em;\n  border: none;\n}\n\n\n/* Build page ================================================================================================= */\n#build_sort {\n  margin-bottom: 0.5em;\n}\n\n#unit_require li, #unit_grants li {\n  margin: 0;\n  padding: 0;\n}\n\n#unit_info {\n  display: table;\n\n  vertical-align: top;\n  text-align: left;\n\n  width: 100%;\n}\n\n#unit_description_wrapper {\n  display: table-header-group;\n\n  height: 13em;\n  vertical-align: top;\n  margin: 0.5em;\n}\n#unit_description_wrapper_cell {\n  display: table-cell;\n}\n#unit_info_image_outer {\n  vertical-align: top;\n  width: 13em;\n  height: 13em;\n  margin-right: 0.5em;\n  float: left;\n}\n#unit_info_image_wrapper {\n  position: relative;\n  width: 100%;\n  height: 100%;\n}\n#unit_info_image {\n  position: absolute;\n  left: 0;\n  top: 0;\n\n  width: 100%;\n  height: 100%;\n  border: 0;\n}\n#unit_info_name {\n  position: absolute;\n  left: 0;\n  top: 0;\n\n  font-size: 1em;\n  vertical-align: top;\n  text-align: center;\n  width: 100%;\n\n  white-space: normal;\n}\n#unit_info_level {\n  position: absolute;\n  left: 0;\n  bottom: 0;\n\n  font-size: 1em;\n  width: 100%;\n  text-align: center;\n}\n\n#unit_description_wrapper_inner {\n  vertical-align: top;\n  margin-right: 0.5em;\n}\n#unit_info_description {\n  text-align: justify;\n}\n#unit_require_wrapper, #unit_grants_wrapper {\n  font-size: 0.8em;\n}\n#unit_require, #unit_grants {\n  overflow: hidden;\n}\n\n#unit_balance_wrapper {\n  display: table-footer-group;\n\n  vertical-align: top;\n  text-align: center;\n}\n#unit_balance {\n  display: table-cell;\n\n  font-size: 0.8em;\n  height: 13em;\n}\n#unit_balance table {\n  border-spacing: 0.2em;\n}\n#unit_balance td {\n  border-spacing: 0.2em;\n  padding: 0;\n}\n#unit_balance tr > td:not(:first-child) {\n  width: 7em;\n}\n#unit_max {\n  display: none;\n}\n\n\n#form_unit {\n  display: table;\n  width: 100%;\n\n  margin-top: 1em;\n}\n\n#unit_cost_container {\n  display: table-header-group;\n}\n#unit_cost_container > .wrap {\n  display: inline-block;\n}\n#unit_time_div {\n  margin: 1em 0;\n}\n\n#unit_cost_table {\n  border-spacing: 0.5em;\n}\n\n#unit_balance > table {\n  border-spacing: 0.5em;\n}\n\n#unit_balance > table tr:nth-child(odd) td {\n  /*background-color: #233452;*/\n  background-color: rgba(0,0,0,0.5);\n}\n\n\n#unit_create {\n  display: table-row;\n  text-align: center;\n  vertical-align: middle;\n}\n#unit_create_stackable_autoconvert {\n  margin-bottom: 1em;\n}\n#unit_destroy {\n  display: table-footer-group;\n  text-align: center;\n}\n/*#unit_create_button_auto {*/\n  /*margin-left: 5em;*/\n/*}*/\n\n@media only screen and (max-width : 51.9375em) { /* 831px and less*/\n  /*body.no_border_image #unit_table { /!* 3 preview *!/*/\n    /*width: 37em;*/\n  /*}*/\n  /*body:not(.no_border_image) #unit_table { /!* 3 preview *!/*/\n    /*width: 40.3em;*/\n  /*}*/\n  #unit_destroy_resources {\n    margin-top: 1em;\n  }\n  #unit_destroy .unit_destroy {\n    margin-bottom: 1em;\n  }\n}\n@media only screen and (min-width : 52em) { /* 832px */\n  /*body.no_border_image #unit_table { /!* 4 preview *!/*/\n    /*width: 48.5em;*/\n  /*}*/\n  /*body:not(.no_border_image) #unit_table { /!* 4 preview *!/*/\n    /*width: 51.8em;*/\n  /*}*/\n  #unit_create_button_auto {\n    margin-left: 5.1em;\n  }\n  #unit_create_button_auto.ui-state-active {\n    margin-left: 5.4em;\n  }\n  #unit_destroy_resources {\n    margin-top: 1em;\n  }\n  #unit_destroy .unit_destroy {\n    margin-bottom: 1em;\n  }\n\n}\n@media only screen and (min-width : 60em) { /* 960px */\n  /*body.no_border_image #unit_table { /!* 5 preview *!/*/\n    /*width: 60.5em;*/\n  /*}*/\n  /*body:not(.no_border_image) #unit_table { /!* 5 preview *!/*/\n    /*width: 63.8em;*/\n  /*}*/\n  #unit_cost_container.unit_create_stackable {\n    display: table-cell;\n    width: 60%;\n  }\n  #unit_create.unit_create_stackable {\n    display: table-cell;\n    width: 40%;\n  }\n}\n@media only screen and (min-width : 68em) { /* 1088px */\n  /*body.no_border_image #unit_table { /!* 6 preview *!/*/\n    /*width: 72.5em;*/\n  /*}*/\n  /*body:not(.no_border_image) #unit_table { /!* 6 preview *!/*/\n    /*width: 75.8em;*/\n  /*}*/\n\n  #unit_info {\n    display: block;\n  }\n  #unit_description_wrapper {\n    display: block;\n  }\n  #unit_description_wrapper_inner {\n    margin-right: 0;\n  }\n  #unit_description_wrapper_cell {\n    display: block;\n  }\n  #unit_balance_wrapper {\n    display: block;\n    float: right;\n    margin-left: 1em;\n  }\n  #unit_balance {\n    display: inline-block;\n  }\n  #form_unit {\n  }\n  #unit_cost_container {\n    display: table-cell;\n  }\n  #unit_create.unit_create_level {\n    display: table-cell;\n    max-width: 15em;\n  }\n  #unit_create_button_auto {\n  }\n  #unit_create_button_auto {\n    margin-left: 0.1em;\n  }\n  #unit_create_button_auto.ui-state-active {\n    margin-left: 0.4em;\n  }\n  #unit_destroy {\n    display: table-cell;\n    vertical-align: middle;\n  }\n  #unit_destroy_resources {\n    margin-top: 0;\n  }\n  #unit_destroy .unit_destroy {\n    margin-bottom: 0;\n  }\n}\n/*@media only screen and (min-width : 76.5em) { /!* 1224px *!/*/\n  /*body.no_border_image #unit_table { /!* 7 preview *!/*/\n    /*width: 84.5em;*/\n  /*}*/\n  /*body:not(.no_border_image) #unit_table { /!* 7 preview *!/*/\n    /*width: 88em;*/\n  /*}*/\n/*}*/\n\n#off_get_dark_matter {\n  color: #ff8900;\n  font-weight: bold;\n}\n\n/* Fleet send page -------------------------------------------------------------- */\n@media only screen and (max-width : 51.9375em) { /* 831px and less*/\n  #fleet_unit_miniatures_container {\n    max-width: 55em;\n  }\n  body.no_border_image .fleet_2_table { /* Table on Fleet Mission Select*/\n    max-width: 34em;\n  }\n  body:not(.no_border_image) .fleet_2_table { /* Table on Fleet Mission Select*/\n    max-width: 37.3em;\n  }\n}\n@media only screen and (min-width : 52em) { /* 832px */\n  #fleet_unit_miniatures_container {\n    max-width: 66em;\n  }\n  body.no_border_image .fleet_2_table {\n    max-width: 45em;\n  }\n  body:not(.no_border_image) .fleet_2_table {\n    max-width: 48.3em;\n  }\n}\n@media only screen and (min-width : 60em) { /* 960px */\n  #fleet_unit_miniatures_container\n  {\n    max-width: 77em;\n  }\n  body.no_border_image .fleet_2_table { /* Table on Fleet Mission Select*/\n    max-width: 56em;\n  }\n  body:not(.no_border_image) .fleet_2_table { /* Table on Fleet Mission Select*/\n    max-width: 59.3em;\n  }\n}\n@media only screen and (min-width : 68em) { /* 1088px */\n  #fleet_unit_miniatures_container {\n    max-width: 88em;\n  }\n  body.no_border_image .fleet_2_table { /* Table on Fleet Mission Select*/\n    max-width: 67em;\n  }\n  body:not(.no_border_image) .fleet_2_table { /* Table on Fleet Mission Select*/\n    max-width: 70.3em;\n  }\n}\n@media only screen and (min-width : 76.5em) { /* 1224px */\n  #fleet_unit_miniatures_container {\n    max-width: 99em;\n  }\n  body.no_border_image .fleet_2_table { /* Table on Fleet Mission Select*/\n    max-width: 78em;\n  }\n  body:not(.no_border_image) .fleet_2_table { /* Table on Fleet Mission Select*/\n    max-width: 81.3em;\n  }\n}\n@media only screen and (min-width : 82em) { /* 1312px */\n  #fleet_unit_miniatures_container {\n    max-width: 110em;\n  }\n}\n@media only screen and (min-width : 88em) { /* 1408px */\n  #fleet_unit_miniatures_container {\n    max-width: 121em;\n  }\n}\n@media only screen and (min-width : 94em) { /* 1504px */\n  #fleet_unit_miniatures_container {\n    max-width: 132em;\n  }\n}\n@media only screen and (min-width : 100em) { /* 1600px */\n  #fleet_unit_miniatures_container {\n    max-width: 143em;\n  }\n}\n@media only screen and (min-width : 106em) { /* 1696px */\n  #fleet_unit_miniatures_container {\n    max-width: 154em;\n  }\n}\n\n.fleet_mission_button_container {\n  display: inline-block;\n  vertical-align: top;\n  margin: 0.2em;\n  width: 10em;\n  height: 9em;\n}\n\n.fleet_expedition_warning {\n  font-size: 1.5em;\n  margin: 0.5em;\n}\n\n.fleet_expedition_not_enough_fuel {\n  max-width: 30em;\n  font-size: 1.5em;\n  margin: 0.5em;\n  display: none;\n}\n\n#fleet_unit_miniatures_container {\n  display: inline-block;\n  font-size: 0.8em;\n  text-align: center;\n  align-content: center;\n}\n\n.ship_miniature_container\n{\n  /*display: inline-flex;*/\n  /*font-size: 0.8em;*/\n  /*text-align: center;*/\n  /*align-content: center;*/\n  height: 10em;\n  width: 10em;\n  margin: 0.3em 0.3em;\n\n  display: inline-flex;\n  flex-wrap: wrap;\n  align-content: space-between;\n}\n\n.ship_miniature_container.no_slots {\n  /*font-size: 100%;*/\n}\n\n.ship_miniature_image {\n  height: 1em;\n  width: 1em;\n}\n\n.ship_miniature_name,\n.ship_miniature_amount,\n.ship_miniature_capacity,\n.ship_miniature_consumption,\n.ship_miniature_speed,\n.ship_miniature_data\n{\n  width: 100%;\n  flex-shrink: 0;\n}\n\n.ship_miniature_name {\n  color: #12A3EB;\n}\n.ship_miniature_capacity,\n.ship_miniature_consumption,\n.ship_miniature_speed\n{\n  text-align: right;\n}\n\n.ship_miniature_container.old {\n  background: none !important;\n  display: flex;\n  height: auto;\n  /*width: auto;*/\n  /*flex-wrap: nowrap;*/\n}\n\n.ship_miniature_container.old .ship_miniature_amount\n{\n  display: none;\n}\n\n.icon_1em {\n  height: 1em;\n  width: 1em;\n}\n\n\n.unit_info_level,\n.unit_info_name,\n.unit_info_speed_title,\n.unit_info_speed_value,\n.unit_info_consumption_text,\n.unit_info_consumption_value,\n.unit_info_capacity_text,\n.unit_info_capacity_value,\n.unit_info_is_satellite\n{\n  position: absolute;\n  left: 0;\n\n  font-size: 1em;\n  width: 100%;\n  text-align: left;\n}\n.unit_info_level {\n  bottom: 0;\n  text-align: center;\n}\n.unit_info_name {\n  top: 0;\n  text-align: center;\n}\n.unit_info_is_satellite {\n  top: 5em;\n  text-align: center;\n}\n.unit_info_speed_title {\n  top: 2.5em;\n  text-align: left;\n}\n.unit_info_speed_value {\n  top: 3.5em;\n  text-align: right;\n}\n.unit_info_consumption_text {\n  top: 4.5em;\n  text-align: left;\n}\n.unit_info_consumption_value {\n  top: 5.5em;\n  text-align: right;\n}\n.unit_info_capacity_text {\n  top: 6.5em;\n  text-align: left;\n}\n.unit_info_capacity_value {\n  top: 7.5em;\n  text-align: right;\n}\n\n\n.unit_miniatures_wrapper {\n  margin: 0.3em 0.3em;\n  display: inline-block;\n  /*vertical-align: top;*/\n  height: 10em;\n  width: 10em;\n  position: relative;\n  /*margin: 0.2em;*/\n  /*float: left;*/\n}\n\n.mission_button_image {\n  height: 5.818181818182rem;\n  width: 5.818181818182rem;\n}\n\n.flt-mini-mission {\n  display: flex;\n  justify-content: center;\n  align-items: center;\n  font-size: 125%;\n  flex-wrap: wrap;\n  text-align: left;\n}\n.flt-mini-mission div {\n  margin: 0.5em 0;\n  text-align: center;\n}\n.flt-mini-mission-planet {\n  flex-shrink: 0;\n}\n#fleet_mini_mission {\n  padding: 0 1em;\n  flex-shrink: 1;\n}\n\n\n/* Empire overview -------------------------------------------------------------- */\n#empire_overview .prod_header {\n  display: flex;justify-content: space-between;align-items: center;\n}\n\n[go] {\n  cursor: pointer;\n}\n\n[name=save_production] {\n  float: right;\n}\n\ntr[prod_mode] > th:first-child {\n  text-align: left;\n  position: relative;\n}\n\ntr[prod_mode] > td:first-child {\n  text-align: center;\n}\n\ntr[prod_mode] > th:first-child > span  {\n  position: absolute;\n  top: 0.5em;\n}\n\ntr[prod_mode] > :not(:first-child) {\n  text-align: center;\n  position: relative;\n  z-index: 100;\n}\n/* Production builder filler - i.e. production % for mine etc */\ntr[prod_mode] > :not(:first-child) > span[style] {\n  position: absolute;\n  top: 0;\n  left: 0;\n  height: 100%;\n  z-index: 200;\n}\n\ntr[prod_mode] > [ecs]:not(:first-child) {\n/* Standard single-height empire cell */\n  height: 1.181818em;\n}\n/* Double-height empire cell - i.e. for Mines */\ntr[prod_mode] > [ecd]:not(:first-child), tr[prod_mode] > [ecd]:not(:first-child) > span[style] {\n  height: 3.181818em;\n}\ntr[prod_mode] > :not(:first-child) > span:not([style]) { /* Content of empire cell */\n  z-index: 300;\n  position: relative;\n}\n\n\n/* DarkMatter/MetaMatter --------------------------------------------------------------- */\n#payment_metamatter,  #payment_metamatter_info {\n  max-width: 80em;\n}\n\n#payment_metamatter_info {\n  margin-bottom: 1em;\n}\n\n#payment_metamatter .border_image_small {\n  margin-top: 1em;\n}\n\n/* Unit purchase --------------------------------------------------------------- */\n#unit_table {\n  max-width: 100em;\n  /*width: 533px; /* == 48.2em */ /* 46.4em + 0.3em*2(padding)*/\n  /*\n    Opera, FF: 529px\n    Chrome: 531px\n    IE: 533px;\n\n    11.6em - .unit_preview width\n    46.4em - .unit_preview width * 4\n\n    533px/11 = 48.5\n\n  */\n   /*width: 48.5em;*/\n}\n\n/* Unit preview box on build pages */\n.unit_preview {\n  display: inline-block;\n\n  position: relative;\n  height: 11em;\n  width: 11em;\n\n  font-size: inherit;\n\n  cursor: pointer;\n\n  border: 0.3em solid;\n  border-radius: 0.3em;\n}\n.unit_preview .unit_preview_image {\n  position: absolute;\n  left: 0;\n  top: 0;\n\n  width: 100%;\n  height: 100%;\n\n  border: 0;\n  vertical-align: top;\n}\n.unit_preview .level, .unit_preview .count {\n  position: absolute;\n  top: 0;\n\n  height: 1.45rem;\n\n  font-size: 1em;\n\n  text-align: center;\n  white-space: nowrap;\n}\n.unit_preview .level {\n  left: 15%;\n  width: 70%;\n}\n.unit_preview .count {\n  left: 0;\n  width: 100%;\n}\n.unit_preview .name {\n  position: absolute;\n  left: 0;\n  top: 1.45rem;\n\n  width: 100%;\n  height: 2.9rem;\n\n  font-size: 1em;\n  text-align: center;\n  vertical-align: middle;\n}\n.unit_preview .resource_container {\n  position: absolute;\n  left: 0;\n  top: 4.34rem;\n  width: 100%;\n  text-align: left;\n}\n.unit_preview .restrict {\n  position: absolute;\n  left: 0;\n  bottom: 1.4em;\n  width: 100%;\n  /*height: 20%;*/\n\n  font-size: 0.9em;\n  /*line-height: 90%;*/\n}\n.unit_preview .time {\n  position: absolute;\n  right: 15%;\n  bottom: 0;\n\n  height: 1.2em;\n  width: 70%;\n\n  font-size: 1em;\n\n  text-align: right;\n  white-space: nowrap;\n}\n.unit_preview .icon_plus, .unit_preview .icon_minus, .unit_preview .icon_info, .unit_preview .icon_gather {\n  width: 1.46em;\n  height: 1.46em;\n}\n\n#unit_create_button, #unit_create_button_auto {\n  min-width: 17em;\n  min-height: 3.5em;\n  vertical-align: middle;\n}\n#unit_create_button {\n  min-height: 2em;\n}\n\n.unit_create {\n  font-size: 1em;\n}\n\n.ov_planet_fill_bar {\n  position: relative;\n  width: 100%;\n  height: 1.3em;\n  border: 0.1em solid;\n}\n\n/* Admin interface - userlist ------------------------------------------------ */\n#user_list {\n  white-space: nowrap;\n}\n#user_list th {\n  text-align: center;\n}\n#user_list tr td:first-child, #user_list tr td:nth-child(3) {\n  text-align: right;\n}\n#user_list tr td:first-child, #user_list tr td:nth-child(2), #user_list tr td:last-child {\n  cursor: pointer;\n}\n\n/* Chat ************************************************************************************************************* */\n#chat_box {\n  width: 100%;\n  padding-right: 1.25em;\n}\n\n#shoutbox td {\n  display: block;\n}\n#shoutbox tr  {\n  border: none;\n  border-top: 0.1em dashed;\n}\n#shoutbox tr > td:first-child {\n  margin-top: 0.4em;\n}\n#shoutbox tr > td:last-child {\n  margin-bottom: 0.25em;\n  margin-left: 1em;\n}\n\n#chat_message_inputs #send {\n  float: right;\n}\n#chat_smile_point {\n  display: inline-block;\n}\n#chat_msg_wrapper {\n  width: 100%;\n}\n\n/*\n#shoutbox {border: 1px solid red;}\n\n#shoutbox > .chat_a {\n  border: 2px solid green;\n}\n#shoutbox > .chat_a > .chat_a_time {\n  background-color: #5f5f5f;\n}\n#shoutbox > .chat_a > .chat_a_time {\n  background-color: darkred;\n}\n#shoutbox > .chat_a > .chat_a_private, #shoutbox > .chat_a > [js_chat_nick_stripped] {\n  background-color: #9a5500;\n}\n#shoutbox > .chat_a > .chat_a_text {\n  background-color: darkgreen;\n}\n*/\n\n#shoutbox > .chat_a {\n  flex-wrap: wrap;\n  margin-top: 0.4em;\n\n  border-top: 1px solid blue;\n}\n#shoutbox > .chat_a > .chat_a_time {\n  width: 100%;\n}\n#shoutbox > .chat_a > .chat_a_private, #shoutbox > .chat_a > [js_chat_nick_stripped] {\n  padding-left: 0.5em;\n  width: 100%;\n}\n#shoutbox > .chat_a > .chat_a_text {\n  padding-left: 1em;\n}\n#chat_online_dragger {\n  cursor: pointer;\n  text-decoration: underline;\n}\n#chat_online_wrapper, #chat_aux_panel {\n  display: none;\n}\n\n\n@media only screen and (min-width : 52em) { /* 832px */\n  /*#shoutbox {border: 1px solid orange;}*/\n\n  #shoutbox > .chat_a > .chat_a_time {\n    width: auto;\n  }\n  #shoutbox > .chat_a > .chat_a_private, #shoutbox > .chat_a > [js_chat_nick_stripped] {\n    padding-left: 0;\n    width: auto;\n  }\n  #shoutbox > .chat_a > .chat_a_text {\n    padding-left: 1em;\n    width: 100%;\n  }\n}\n@media only screen and (min-width : 60em) { /* 960px */\n  /*#shoutbox {border: 1px solid yellow;}*/\n\n  #chat_online_wrapper, #chat_aux_panel {\n    display: table-cell;\n  }\n\n  #chat_online_dragger {\n    display: none;\n  }\n\n\n\n\n  #chat_message_inputs {\n    text-align: center;\n  }\n  #chat_msg_wrapper {\n    width: auto;\n  }\n  #chat_smile_point {\n    float: left;\n    display: block;\n  }\n}\n@media only screen and (min-width : 64em) { /* 1024px */\n  /*#shoutbox {border: 1px solid green;}*/\n\n  #chat_show_extra {\n    display:none;\n  }\n}\n@media only screen and (min-width : 75em) { /* 1200px */\n  /*#shoutbox {border: 1px solid blue;}*/\n\n  #shoutbox > .chat_a {\n    flex-wrap: nowrap;\n    margin-top: 0;\n\n    border-top: 0;\n  }\n\n  #shoutbox > .chat_a > .chat_a_text {\n    padding-left: 0;\n    width: auto;\n  }\n}\n\n.chat_online, #onlinebox td {\n  border: 0;\n  white-space: nowrap;\n  text-align: left;\n  vertical-align: top;\n  font-weight: bold;\n}\n\n.chat_message {\n  text-align: left;\n  font-weight: normal;\n}\n\n/* Shoutbox */\n#shoutbox {\n  background-color: black;\n\n  overflow: auto;\n\n  text-align: left;\n  vertical-align: text-top;\n\n  margin: 0.5em;\n  height: 30em;\n}\n#shoutbox img {\n  vertical-align: top;\n  margin: 0 0.1em 0 0;\n}\n#shoutbox .chat_private {\n  color: lightblue;\n  font-style: italic;\n}\n#shoutbox span {\n  vertical-align: top;\n}\n/* Chat messages on table */\n#shoutbox > table {\n  background-color: black;\n  border: 0;\n  padding: 0;\n  margin: 0.25em 0;\n  border-collapse: collapse;\n  width: 100%;\n}\n#shoutbox > table > tbody > tr {\n  background-color: black;\n}\n#shoutbox > table > tbody > tr > td {\n  background-color: black;\n  border: 0;\n  padding: 0;\n  text-align: left;\n  vertical-align: top;\n}\n#shoutbox > table > tbody > tr > td:not(:last-child) {\n  white-space: nowrap;\n}\n#shoutbox > table > tbody > tr > td:last-child {\n  width: 100%;\n  padding-left: 0.2em;\n}\n\n/* Chat messages on div */\n/*#shoutbox > div {*/\n  /*margin: 0.2em 0;*/\n/*}*/\n/*#shoutbox > div:after { !* Float clear *!*/\n  /*content: '';*/\n  /*display: block;*/\n  /*clear: both;*/\n/*}*/\n/*#shoutbox > div > div:not(:last-child) { !* All div except message should float left *!*/\n  /*float: left;*/\n/*}*/\n/*#shoutbox > div > div:last-child { !* Message itself *!*/\n  /*overflow: hidden;*/\n/*}*/\n\n/* Nick colors in chat */\n[safe_name], .chat_nick_msg {\n  border-bottom: 0.1em dashed;\n  font-weight: bold;\n  cursor: pointer;\n}\n.player_nick_award, .player_nick_race {\n  cursor: pointer;\n}\n\n/* Input elements - color, smile, text, send button */\n#chat_color_button, #chat_smile_point, #chat_message_inputs #msg, #chat_message_inputs #msg, #chat_message_inputs #send {\n  vertical-align: top;\n  margin: 0 0.5em;\n}\n#chat_color_button {\n  float: left;\n}\n#chat_smile_point {\n  /*float: left;*/\n  /*display: block;*/\n  width: 2.2em;\n  height: 2.2em;\n}\n\n#chat_message_inputs #send {\n  float: right;\n  width: 9em;\n}\n\n#chat_msg_wrapper {\n  display: block;\n  overflow: hidden;\n}\n#chat_msg_wrapper:after {\n  content: '';\n  display: block;\n  clear: both;\n}\n\n#chat_msg_wrapper #msg {\n  width: 100%;\n}\n\n\n#chat_message_inputs {\n  vertical-align: top;\n}\n#chat_color_button div { /* Colored ABCDEFG */\n  display: inline-block;\n}\n\n\n/* Popups - color select, smile select, tooltip and admin command menu */\n#chat_color_select, #chat_message_smiles2, #chat_command_menu, #chat_muted_tooltip {\n  display: none;\n  vertical-align: top;\n  text-align: center;\n  padding: 0.4em;\n  border: 0.1em solid;\n  -moz-border-radius: 0.4em;\n  -webkit-border-radius: 0.4em;\n  -khtml-border-radius: 0.4em;\n  border-radius: 0.4em;\n}\n#chat_color_select {\n  max-width: 26em;\n}\n#chat_message_smiles2 {\n  max-width: 32.5em;\n}\n#chat_command_menu {\n  max-width: 33em;\n}\n#chat_muted_tooltip {\n  max-width: 27em;\n}\n#chat_muted_tooltip img, #chat_command_menu img {\n  vertical-align: top;\n}\n\n/* Popup internal elements - color buttons, smile buttons, command length buttons */\n#chat_color_select div, #chat_message_smiles2 div, #chat_command_menu div:not(:first-child):not(:last-child) {\n  margin: 0.2em;\n  border: 0.1em solid;\n  vertical-align: top;\n  display: inline-block;\n}\n#chat_message_smiles2 div {\n  width: 4.54545em;\n  height: 3.636363636em;\n\n  -webkit-transform-style: preserve-3d;\n  -moz-transform-style: preserve-3d;\n  transform-style: preserve-3d;\n}\n#chat_message_smiles2 div img {\n  /*  display: inline-block;*/\n  position: relative;\n  top: 50%;\n  -ms-transform: translateY(-51%);\n  -webkit-transform: translateY(-51%);\n  -o-transform: translateY(-51%);\n  -moz-transform: translateY(-51%);\n  transform: translateY(-51%);\n}\n\n\n#chat_command_menu div:not(:first-child):not(:last-child) {\n  width: 7.3em;\n}\n#chat_command_menu div:last-child {\n  margin-top: 0.5em;\n}\n\n\n#chat_color_select div:first-child, #chat_color_button div:first-child {\n  color: cyan;\n}\n#chat_color_select div:nth-child(2), #chat_color_button div:nth-child(2) {\n  color: yellow;\n}\n#chat_color_select div:nth-child(3), #chat_color_button div:nth-child(3) {\n  color: green;\n}\n#chat_color_select div:nth-child(4), #chat_color_button div:nth-child(4) {\n  color: pink;\n}\n#chat_color_select div:nth-child(5), #chat_color_button div:nth-child(5) {\n  color: maroon;\n}\n#chat_color_select div:nth-child(6), #chat_color_button div:nth-child(6) {\n  color: orange;\n}\n#chat_color_select div:nth-child(7), #chat_color_button div:nth-child(7) {\n  color: white;\n}\n\n.chat_a {\n  display: flex;\n  justify-content: flex-start;\n  align-content: flex-start;\n  align-items: flex-start;\n\n  text-align: left;\n\n  line-height: 1.4545em;\n}\n[js_chat_nick_stripped] {\n  cursor: pointer;\n  display: flex;\n  align-items: center;\n}\n[js_chat_nick_stripped], .chat_a_time, .chat_a_private {\n  flex-grow: 0;\n  flex-shrink: 0;\n}\n\n\n/* Statistics page ************************************************************************************************************* */\n#stat_cell_min_max {\n  position: relative;\n}\n#stat_cell_min_max > .stat_min, #stat_cell_min_max > .stat_avg, #stat_cell_min_max > .stat_max {\n  text-align: center;\n  width: 100%;\n\n  position: absolute;\n  left: 0;\n}\n#stat_cell_min_max > .stat_max {\n  top: 0;\n}\n#stat_cell_min_max > .stat_avg {\n  top: 45%;\n}\n#stat_cell_min_max > .stat_min {\n  bottom: 0;\n}\n\n.stat_cell, .stat_date_cell {\n  width: 3.63636em;\n}\n\n.stat_cell {\n  height: 18.2em;\n\n  position: relative;\n}\n.stat_cell > .stat_value {\n  text-align: center;\n  width: 100%;\n\n  position: absolute;\n  left: 0;\n  bottom: 1.1em;\n}\n.stat_cell > .stat_delta {\n  text-align: center;\n  width: 100%;\n\n  position: absolute;\n  left: 0;\n  bottom: 0;\n}\n.stat_bar {\n  width: 100%;\n  height: 0;\n\n  position: absolute;\n  left: 0;\n  bottom: 0;\n}\n\n/* Blitz admin page ************************************************************************************************************* */\n.blitz_admin > div {\n  margin-top: 1.8em;\n}\n\n/* Batch operations ************************************************************************************************************* */\n#batch_fleet_table {\n  width: 10em;\n  border: 0;\n  border-spacing: 0.1em; /* cellspacing=\"1\"  */\n}\n#batch_fleet_results {\n  border: 0;\n  border-spacing: 0.1em; /* cellspacing=\"1\"  */\n  margin-bottom: 1em;\n  vertical-align: top;\n}\n#batch_fleet_table > tbody > tr > td, #batch_fleet_table > tbody > tr > th,\n#batch_fleet_results > tbody > tr > td, #batch_fleet_results > tbody > tr > th\n{\n  padding: 0; /* cellpadding=\"0\" */\n}\n\n#batch_fleet_table th, #batch_fleet_table td {\n  vertical-align: middle;\n}\n#batch_fleet_table > tbody > tr:first-child > th {\n  min-width: 11em;\n  vertical-align: top;\n}\n#batch_fleet_table tr:first-child th {\n  text-align: left;\n}\n/* Первая, вторая и третья ячейки заголовка*/\n#batch_fleet_table tr:first-child th:first-child, #batch_fleet_table tr:first-child th:nth-child(2), #batch_fleet_table tr:first-child th:nth-child(3)  {\n  width: 2em;\n  white-space: nowrap;\n  vertical-align: middle;\n}\n#batch_fleet_table tr:first-child th:nth-child(2)  {\n  text-align: center;\n}\n#batch_fleet_table .batch_resource_header th {\n  width: 2em;\n}\n#batch_fleet_table .batch_fleet_unit_row th:first-child {\n  width: 2em;\n  white-space: nowrap;\n  text-align: left;\n}\n#batch_fleet_table .total_row .tw_shrink {\n  width: 2em;\n  white-space: nowrap;\n}\n\n.qualifier_amount, .qualifier_apply {\n  display: none;\n}\n\n.batch_unit_holder {\n  width: 2em;\n  white-space: nowrap;\n}\n.batch_unit_holder > div {\n  text-align: right;\n  margin-left: 2.2em;\n}\n.batch_unit_holder > div.no_checkbox {\n  margin-left: 0.4em;\n}\n.batch_unit_holder > input {\n  float: left;\n}\n\n\n/* Que list ===================================================================================================== */\n.que_item { /* Que item */\n  cursor: pointer;\n\n  display: inline-block;\n\n  border: solid 0.2em transparent;\n  /*border: 0;*/\n\n  position: relative;\n  height: 6em;\n  width: 6em;\n\n  /*margin-right: 0.2em;*/\n  /*margin-bottom: 0.2em;*/\n  margin: 0.2em;\n\n  font-size: 1em;\n}\n.que_unit_picture { /* Unit picture */\n  border: 0;\n\n  position: absolute;\n  left: 0;\n  top: 0;\n\n  width: 100%;\n  height: 100%;\n}\n.que_unit_level {\n  position: absolute;\n  top: 0;\n  left: 0;\n\n  width: 100%;\n  height: 1em;\n\n  font-size: 100%;\n\n  text-align: center;\n}\n.que_unit_slot_bar {\n  position: absolute;\n  left: 0;\n  top: 0;\n\n  height: 1em;\n\n  background-color: rgba(0,0,255,0.75);\n}\n\n.que_slot_time_container {\n  position: relative;\n\n  width: 100%;\n  height: 1em;\n}\n\n.que_unit_time {\n  position: absolute;\n  bottom: 0;\n  left: 0;\n\n  width: 100%;\n  height: 1em;\n\n  font-size: 100%;\n\n  text-align: center;\n}\n.que_item_0 > .que_unit_progress_bar {\n  position: absolute;\n  left: 0;\n  bottom: 0;\n\n  height: 1em;\n  background-color: rgba(0,255,0,0.75);\n}\n.que_unit_time_seconds_0 {\n  position: absolute;\n  bottom: 1em;\n  left: 0;\n  width: 100%;\n  height: 0.5ex;\n  font-size: 100%;\n}\n\n\n.bld_que_container {\n  display: flex;\n  text-align: left;\n  flex-wrap: wrap;\n}\n\n.bld_que_total {\n  /*display: flex;*/\n  /*flex-wrap: wrap;*/\n  /*justify-content: center;*/\n  /*align-content: center;*/\n\n  position: relative;\n\n  width: 11em;\n  /*height: 5.5em;*/\n\n  text-align: center;\n  vertical-align: top;\n}\n.bld_que_total_time_text {\n  /*position: absolute;*/\n  /*left: 0;*/\n  /*top: 0;*/\n  /*height: 50%;*/\n  width: 100%;\n}\n.bld_que_total_bar {\n  width: 100%;\n  position: relative;\n  height: 1em;\n}\n.bld_que_total_progress_bar {\n  position: absolute;\n  left:0;\n  top: 0;\n\n  width: 100%;\n  height: 100%;\n\n  background-color: rgba(197, 201, 9, 0.75);\n}\n.bld_que_total_timer {\n  position: absolute;\n  left: 0;\n  top: 0;\n\n  width: 100%;\n  height: 100%;\n\n  color: red;\n\n  background-color: rgba(0,0,0,0.5);\n}\n.bld_que_total_finish {\n  /*position: absolute;*/\n  /*left: 0;*/\n  /*top: 2em;*/\n\n  color: yellow;\n  /*height: 50%;*/\n  /*width: 100%;*/\n}\n.bld_que_total_item {\n  /*position: relative;*/\n  /*display: block;*/\n  /*color: red;*/\n  /*position: absolute;*/\n  /*left: 0;*/\n  /*bottom: 0;*/\n\n  /*width: inherit;*/\n\n  /*text-align: center;*/\n  /*height: 2.5em;*/\n  /*width: 100%;*/\n}\n\n.bld_que_items {\n  display: inline-block;\n  text-align: left;\n}\n.bld_que_buttons {\n  display: flex;\n  justify-content: space-between;\n\n  width: 100%;\n}\n\n.bld_que_container.vertical {\n  max-width: 11em;\n}\n.bld_que_items.vertical {\n  width: 100%;\n  flex-wrap: wrap;\n  justify-content: center;\n  text-align: center;\n}\n.bld_que_container.vertical .bld_que_buttons {\n  flex-wrap: wrap;\n\n  max-width: 11em;\n}\n\n/* Black market ===================================================================================================== */\n.market_services {\n  max-width: 20em;\n  width: 100%;\n  padding: 0.5em;\n  display: inline-block;\n}\n.market_services div:first-child {\n  text-align: justify;\n  margin-bottom: 1em;\n}\n.market_services .button_pseudo {\n  min-width: 15em;\n}\n#market_trader .button_pseudo {\n  width: 10em;\n}\n\n/* Border magic ===================================================================================================== */\n.no_border_image { /* Just placeholder for phpstorm autocomplete */ }\n.border_image_small { /* Just placeholder for phpstorm autocomplete */ }\n.border_image_large { /* Just placeholder for phpstorm autocomplete */ }\n\nbody:not(.no_border_image) table:not(.markup):not(.noborder):not(.no_border_image):not(.border_image_small),\nbody:not(.no_border_image) div#tab_container,\nbody:not(.no_border_image) div.market_services,\nbody:not(.no_border_image) .border_image_large\n{\n  border: solid transparent;\n  border-radius: 0.5em;\n\n  border-width: 2.181818181818em 1em 2.272727272727em 2.272727272727em;\n\n  -moz-border-image:    url(\"/design/images/border.png\") 24 11 25 25 round round;\n  -o-border-image:      url(\"/design/images/border.png\") 24 11 25 25 round round;\n  -webkit-border-image: url(\"/design/images/border.png\") 24 11 25 25 round round;\n  border-image:         url(\"/design/images/border.png\") 24 11 25 25 round round;\n}\n\nbody:not(.no_border_image) .border_image_small:not(.no_border_image), body:not(.no_border_image) .ui-dialog\n{\n  border: solid transparent;\n  border-radius: 0.5em;\n\n  border-width: 1em 1em 1em 1em;\n\n  -moz-border-image:    url(\"/design/images/border_small.png\") 11 11 11 11 round round;\n  -o-border-image:      url(\"/design/images/border_small.png\") 11 11 11 11 round round;\n  -webkit-border-image: url(\"/design/images/border_small.png\") 11 11 11 11 round round;\n  border-image:         url(\"/design/images/border_small.png\") 11 11 11 11 round round;\n}\n\n\n/* Title/Black magic ===================================================================================================== */\n/*body.no_border_image [table_title] {*/\n[table_title] {\n  position: relative;\n\n  margin-top: 6em;\n}\n\n/*body.no_border_image [table_title]:before {*/\n[table_title]:before {\n  content: attr(table_title);\n\n  font-size: 2em;\n\n  /*display: block;*/\n  width: 100%;\n  /*height: 5em;*/\n\n  /*margin: 5em;*/\n\n  z-index: 0;\n\n  text-align: center;\n\n  position: absolute;\n\n  top: -2em;\n  left: 0;\n}\n\nbody:not(.no_border_image) [table_title] {\n}\n\nbody:not(.no_border_image) [table_title]:before {\n  top: -3em;\n}\n\n\n/* Planet List ===================================================================================================== */\n.element_fill_wrap {\n  position: absolute;\n  left: 0;\n  top: 0;\n  width: 100%;\n  height: 100%\n}\n.planet_list_wrap {\n  position: relative;\n  border: none;\n  width: inherit;\n}\n.planet_list_wrap > .res_bar {\n  position: absolute;\n  top: 0;\n\n  overflow: hidden;\n\n  width: 0.25em;\n  height: 100%;\n}\n\n.planet_list_top_small {\n  width: 7.75em;  /* 87/11 = 7,909090909091 */\n}\n.planet_list_top_small > .planet_list_wrap {\n  height: 7em; /* 75/11 = 6,818181818182 */\n}\n.planet_list_top_small > .planet_list_wrap > .images {\n  height: 100%;\n  width: 7em; /* 75/11 = 6,818181818182 */\n}\n.planet_list_top_small > .planet_list_wrap > .metal_bar {\n  left: 7em; /* 75/11 = 6,818181818182 */\n}\n.planet_list_top_small > .planet_list_wrap > .crystal_bar {\n  left: 7.25em; /* 75/11 = 6,818181818182 */\n}\n.planet_list_top_small > .planet_list_wrap > .deuterium_bar {\n  left: 7.50em; /* 75/11 = 6,818181818182 */\n}\n.planet_list_top_small > .planet_list_wrap > .images > .icon-gather-wrapper {\n  height: 25%;\n  width: 25%;\n  bottom: 0;\n  right: 0;\n  position: absolute;\n}\n\n.planet_list_top_medium {\n  width: 9.75em;\n}\n.planet_list_top_medium > .planet_list_wrap {\n  height: 9em;\n}\n.planet_list_top_medium > .planet_list_wrap > .images {\n  height: 100%;\n  width: 9em;\n}\n.planet_list_top_medium > .planet_list_wrap > .metal_bar {\n  left: 9em;\n}\n.planet_list_top_medium > .planet_list_wrap > .crystal_bar {\n  left: 9.25em;\n}\n.planet_list_top_medium > .planet_list_wrap > .deuterium_bar {\n  left: 9.50em;\n}\n.planet_list_top_medium > .planet_list_wrap > .images > .icon-gather-wrapper {\n  height: 2.25em;\n  width: 2.25em;\n  bottom: 0;\n  right: 0;\n  position: absolute;\n}\n\n.planet_list_fields {\n  position: relative;\n\n  width: 100%;\n  height: 1.1em;\n\n  overflow: hidden;\n\n}\n.planet_list_fields > .numbers {\n  position: absolute;\n  left: 0;\n  top: 0;\n\n  width: 100%;\n  height: 100%;\n\n  font-size: 0.9em;\n  font-weight: bold;\n}\n.planet_list_fields > .bar {\n  position: absolute;\n  left: 0;\n  top: 0;\n\n  /*width: 100%;*/\n  height: 100%;\n\n  /*font-size: 1.1em;*/\n  font-weight: bold;\n}\n\n.planet_list_bar {\n  position: absolute;\n  bottom: 0;\n  left: 0;\n  width: 100%;\n}\n\n.ov_struc_timer {\n  white-space: normal;\n}\n\n.planet_list_moon_attack {\n  position: absolute;\n  top: 15%;\n  left: 15%;\n  width: 70%;\n  height: 70%;\n  cursor: pointer;\n}\n.planet_list_submoon_fields {\n  position: absolute;\n  left: 0;\n  width: 100%;\n  top: 100%;\n  height: 10%;\n  overflow: hidden;\n}\n.planet_list_submoon_filler {\n  position: relative;\n  left: 0;\n  height: 100%;\n}\n.planet_list_submoon_attack {\n  position: absolute;\n  top: 26%;\n  left: 26%;\n  width: 48%;\n  height: 48%;\n}\n\n/* Universe/Galaxy ===================================================================================================== */\n#universe_main img {\n  border: 0;\n  cursor: pointer;\n}\n\n.universe_main > tbody > tr > td, .universe_main > tbody > tr > th {\n  white-space: nowrap;\n  vertical-align: middle;\n}\n.uni_show_planet, .uni_show_debris, .uni_show_user, .uni_show_ally {\n  cursor: pointer;\n}\n.uni_show_planet > div, .uni_show_debris > div { /* DIV holder for old view */\n  position: relative;\n  display: inline-block;\n  width: 2.75em;\n  height: 2.75em;\n  margin: auto;\n}\n.uni_show_planet img:first-child, .uni_show_debris img:first-child { /* First image is planet/debris/moon image */\n  position: absolute;\n  top:0;\n  left: 0;\n  width: 100%;\n  height: 100%;\n}\n.uni_show_debris .uni_debris_percent  { /* % on debris icon - in span inside element */\n  position: absolute;\n  bottom:0;\n  right: 0;\n}\n.uni_own_fleet {\n  position: absolute;\n  bottom:0;\n  left: 0;\n  width: 100%;\n  height: 100%;\n}\n\n.uni_show_user {\n  min-width: 12em;\n}\n\n.uni_scan.uni_planet_row > * {\n  padding-top: 0;\n  padding-bottom: 0;\n}\n\n.uni_scan.button_pseudo  {\n  margin-top: 0;\n  margin-bottom: 0;\n}\n\n.uni_planet_row .uni_object_type { /* Тип объекта - планета/луна/обломки */\n  position: absolute;\n  left: 0;\n  top: 0;\n  width: 100%;\n  height: 1.1em;\n  background-color: rgba(0,0,0,0.5);\n}\n.uni_show_planet_new, .uni_show_debris_new { /* Overwrites styles for new Universe look*/\n  position: relative;\n  display: inline-block;\n  width: 6em;\n  height: 6em;\n  max-width: 6em;\n  margin: 0; /* Overwrites auto margin */\n  border: 0.5em solid transparent;\n}\n.uni_show_planet_new .uni_own_fleet { /* Overwrites size of own fleet icon */\n  width: 50%;\n  height: 50%;\n}\n\n.universe_main_new .uni_show_user, .universe_main .uni_show_ally {\n  display: block;\n}\n\n/*div .ui-dialog-content table.legend_template  {*/\n  /*background-color: transparent;*/\n/*}*/\n\n/*div .ui-dialog-content table.planet_template  {*/\n  /*background-color: transparent;*/\n/*}*/\n.planet_template div {\n  display: block;\n  max-width: 11em;\n}\n.planet_template .uni_popup_planet {\n  width: 7em;\n  height: 7em;\n  position: relative;\n  display: inline-block;\n}\n.planet_template .uni_popup_planet img {\n  position: absolute;\n  left: 0;\n  top: 0;\n\n  width: 100%;\n\n  font-size: 1em;\n}\n.planet_template .uni_popup_planet div {\n  position: absolute;\n  left: 0;\n  bottom: 0;\n\n  width: 100%;\n\n  text-align:center;\n  background-color: rgba(0,0,0,0.50);\n}\n\n/*div .ui-dialog-content table.debris_template  {*/\n  /*background-color: transparent;*/\n/*}*/\n\n.user_template div {\n  display: block;\n  max-width: 20em;\n}\n/*div .ui-dialog-content table.user_template {*/\n  /*background-color: transparent;*/\n/*}*/\n\n.ally_template div {\n  display: block;\n  max-width: 20em;\n}\n/*div .ui-dialog-content table.ally_template {*/\n  /*background-color: transparent;*/\n/*}*/\n\n/* Technology/TechTree page  ===================================================================================================== */\n#tab_tech_tree {\n  font-size: 1em;\n  /*width: 50rem;*/\n  display: none;\n}\n\n#tab_tech_tree .tech_container {\n  display: inline-block;\n  width: 14em;\n  min-height: 11em;\n  margin: 0.5em;\n  padding: 1em;\n\n  vertical-align: top;\n\n  border: 0.1em solid;\n  border-radius: 0.4em;\n}\n\n#tab_tech_tree .tech_image {\n  position: relative;\n\n  width: 11em;\n  height:11em;\n}\n#tab_tech_tree .tech_image img {\n  position: absolute;\n  top: 0;\n  left:0;\n\n  width: 100%;\n  height: 100%;\n}\n#tab_tech_tree .tech_image > span {\n  position: absolute;\n  top: 0;\n  left:0;\n\n  width: 100%;\n  /*height: 4em;*/\n}\n\n#tab_tech_tree .tech_require {\n  text-align: left;\n  font-size: 0.9em;\n}\n\n/* Payment page ===================================================================================================== */\n.payment_mm_describe\n{\n  font-size: 1.8em;\n  text-align: center;\n}\n\n.payment_type_block\n{\n  border: 0.2em solid rgba(255, 255, 255, 0.5);\n  margin: 0.5em 0;\n  text-align: center;\n  padding-bottom: 0.5em;\n}\n\n.payment_type_name\n{\n  font-size: 150%;\n  background-color: rgba(0, 0, 0, 0.5);\n  padding: 0.5em;\n}\n\n.payment_method_block\n{\n  width: 100%;\n  text-align: center;\n\n  display: flex;\n  flex-wrap: wrap;\n  justify-content: center;\n  align-content: space-around;\n  align-items: stretch;\n}\n\n.payment_block_item.ptl_payment_method_hide {\n  display: none;\n}\n\n.payment_block_item {\n  display: flex;\n  flex-wrap: wrap;\n  justify-content: space-around;\n  align-content: space-around;\n  align-items: center;\n  width: 14em;\n  margin: 0.3em;\n}\n\n\n/* Event Halloween ===================================================================================================== */\n.halloween {\n  position: absolute;\n\n  display: none;\n  width: 32px;\n  height: 32px;\n\n  z-index: 10000;\n}\n\n.halloween_result {\n  display: none;\n  width: 32px;\n  height: 32px;\n  /*background-color: white;*/\n\n  position: absolute;\n  z-index: 10001;\n\n  left: 0;\n  top: 0;\n}\n\n/* Planet overview */\n.planet_overview #navbar_resources {\n  border: 0 solid transparent;\n  width: 100%;\n  margin: 0;\n}\n\n.planet_overview-info {\n  width: 100%;\n  margin-bottom: 1em;\n}\n\n.planet_overview {\n  display: flex;\n  /*border: 1px solid green;*/\n  align-items: flex-start;\n}\n\n#planet_overview_double {\n  display: flex;\n  align-content: flex-start;\n  align-items: flex-start;\n  /*border: 1px solid blue;*/\n}\n\n.planet_overview #planet_info_table {\n  margin: 0;\n  width: 522px;\n}\n.planet_overview_list {\n  max-height: 87em;\n  overflow-x: hidden;\n  overflow-y: auto;\n\n  display: flex;\n  flex-wrap: wrap;\n\n  /*border: 1px solid black;*/\n\n  justify-content: space-around;\n}\n.planet_info_overview {\n}\n\n.planet_overview #fleet_list_table {\n  margin: 0 auto;\n}\n\n@media only screen and (max-width: 116em) {\n  div.planet_overview_list\n  {\n    /*border: 1px solid white;*/\n  }\n\n  .planet_overview #fleet_list_table {\n  }\n}\n@media only screen and (max-width: 111em) {\n  div.planet_overview_list\n  {\n    /*border: 1px solid red;*/\n  }\n  #planet_overview_double {\n    flex-wrap: wrap;\n  }\n  .planet_overview #planet_info_table {\n    margin: auto;\n    margin-bottom: 2em;\n  }\n}\n@media only screen and (max-width: 59em) {\n  .planet_overview {\n    flex-wrap: wrap-reverse;\n  }\n  .planet_overview_list {\n    display: flex;\n    flex-wrap: wrap;\n    flex-direction: column;\n\n    overflow-x: auto;\n    overflow-y: hidden;\n\n    max-height: 19em;\n    max-width: 65em;\n\n    width: 100%;\n\n    /*border: 1px solid orange;*/\n  }\n  .planet_info_overview {\n    display: inline-block;\n  }\n  .planet_list_item {\n    /*display: inline-block;*/\n  }\n  .planet_overview #planet_info_table {\n    margin-top: 2em;\n  }\n}\n\n\n/* Planet manage */\n.planet-manage-governor-hire-container {\n  width: 30%;\n  display: inline-block;\n  padding: 1%;\n}\n\n.planet-manage-governor-hire-image-container {\n  width: 100%;\n  display: inline-block;\n  position: relative;\n}\n\n.planet-manage-sectors-container {\n  font-size: 1.5em;\n\n  position: relative;\n\n  width: 100%;\n  height: 1.3em;\n\n  border: 1px solid rgb(153, 153, 255);\n}\n\n.planet-manage-sectors-text {\n  position: absolute;\n  top: 0;\n  left: 0;\n\n  width: 100%;\n  height: 100%;\n\n  text-align: center;\n  vertical-align: middle;\n\n  text-shadow:\n    -1px -1px 0 #000,\n    1px -1px 0 #000,\n    -1px 1px 0 #000,\n    1px 1px 0 #000;\n}\n\n.unit-icon-info-container {\n  position: absolute;\n  right: 0;\n  bottom: 1em;\n\n  width: 4em;\n  height: 4em;\n}\n\n.unit_preview_image_icon {\n  /*position: absolute;*/\n  /*right: 0;*/\n  /*bottom: 1em;*/\n\n  width: 1.45454545em;\n  height: 1.45454545em;\n}\n\n/* TEMPLATE BOILERPLATE ********************************************************************************************* */\n.hint {\n  text-align: justify;\n}\n\n#dialog-sector-buy {\n  text-align: center;\n  display: none;\n  padding: 1em;\n}\n\n\n\n\n/* Message & numbers color coding */\n\n.ok, .success, .positive, button.positive, button.ok, .ui-button.ok {\n  color: lime;\n}\n.info, button.info, .ui-button.info {\n  color: #00B7F3;\n}\n.notice, .zero, .neutral, button.neutral, button.zero {\n  color: yellow;\n}\n.warning, button.warning {\n  color: orange;\n}\n.error, .negative, button.negative, button.error {\n  color: #FF0000; /* red */\n}\n.white, .zero_number {\n  color: white;\n}\ntr.info_bg > td, tr.info_bg > th,\n.info_bg {\n  background-color: #005472;\n}\n.ok_bg, tr.ok_bg > td, tr.ok_bg > th,\n.success_bg, tr.success_bg > td, tr.success_bg > th,\n.positive_bg, tr.positive_bg > td, tr.positive_bg > th, td.positive_bg, th.positive_bg\n, tr.positive_bg > td, tr.positive_bg > th\n, tr.c_r.positive_bg > td, tr.c_r.positive_bg > th /* admin_table_balance */\n {\n  background-color: #004400;\n}\ntr.notice_bg > td, tr.notice_bg > th,\n.notice_bg, .zero_bg, .neutral_bg {\n  /*background-color: #636300 !important;*/\n  background-color: #575700 !important;\n}\ntr.warning_bg > td, tr.warning_bg > th, td.warning_bg, th.warning_bg,\n.warning_bg {\n  background-color: #924200;\n}\ntr.error_bg > td, tr.error_bg > th,\n.error_bg, .negative_bg {\n  background-color: darkred;\n}\n.errormessage a, .success a, .notice a, .warning a, .error a {\n  text-decoration: underline;\n}\n\n/* Resources color coding*/\n.metal {\n  color: darkgrey;\n}\n.crystal {\n  color: cyan;\n}\n.deuterium{\n  color: #B000B0;\n}\n.overall{\n  color: white;\n}\n.metamatter {\n  /*color: #cf95ff;*/\n  color: #CC99FF;\n}\n.dark_matter {\n  color: dodgerblue;\n}\n\n.metal_bg{\n  background-color: darkgrey;\n}\n.crystal_bg{\n  background-color: cyan;\n}\n.deuterium_bg {\n  background-color: #800080;\n}\n.overall_bg {\n  background-color: white;\n}\n\n.nick_developer {\n  /*color: purple;*/\n  color: #9900ff;\n}\n.nick_admin {\n  color: #0066FF;\n}\n.nick_operator {\n  color: red;\n}\n.nick_moderator {\n  color: green;\n}\n.nick_premium {\n  color: gold;\n}\n\n.bonus {\n  color: gold;\n}\n\n.same_alliance, .same_player {\n  color: #33CCFF;\n}\n\n\n/* Universe status styles */\n.allymember {\n  color: lime;\n}\n.myplanet {\n  color: red;\n}\n.noob {\n  color: #a0ffa0;\n}\n.protected {\n  color: yellow;\n}\n.strong {\n  color: #ffa0a0;\n}\n.player_active {\n  font-weight: bold;\n}\n.inactive {\n  font-weight: normal;\n}\n.longinactive {\n  font-weight: normal;\n  font-style: italic;\n}\n.banned {\n  text-decoration: line-through;\n}\n.admin {\n  text-decoration: blink;\n  /*color: purple;*/\n  color: #9900ff;\n}\n.birthday:before {\n  content: url(\"/design/images/birthday.png\");\n}\n.vacation:before { /* Replaces .birthday ! */\n  content: url(\"/design/images/icon_vacation.png\");\n}\n.vacation {\n  color: cyan;\n}\n\n/* Flying fleet & message color coding */\n.flight {\n}\n.return {\n}\n.holding {\n}\n*.ownattack, .mnl_attaque {\n  color: yellow;\n}\n*.ownfederation {\n  color: #33CC00;\n}\n*.owndestroy {\n  color: gold;\n}\n*.ownhold {\n  color: #80a0C0;\n}\n*.ownmissile {\n  color: sienna;\n}\n*.ownespionage, *.mnl_spy, .mnl_spy td {\n  color: orange;\n}\n*.owntransport, .mnl_transport {\n  color: lime;\n}\n*.owndeploy {\n  color: lightgreen;\n}\n*.ownharvest, .mnl_exploit {\n  color: green;\n}\n*.owncolony {\n  color: blue;\n}\n\n.flight .ownattack a, .flight .owncolony a, .flight .owndeploy a, .flight .owndestroy a,\n.flight .ownespionage a, .flight .ownfederation a, .flight .ownharvest a, .flight .ownhold a,\n.flight .ownmissile a, .flight .owntransport a, .flight .ownexpedition a {\n  color: #E6EBFB;\n}\n\n*.attack {\n  color: red;\n}\n*.colony {\n  color: lime;\n}\n*.deploy {\n  color: lime;\n}\n*.destroy {\n  color: gold;\n}\n*.espionage {\n  color: red;\n}\n*.federation {\n  color: #CC0000;\n}\n*.harvest {\n  color: lime;\n}\n*.hold {\n  color: #80a0C0;\n}\n*.missile {\n  color: red;\n}\n*.transport {\n  color: lime;\n}\n\n.flight .attack a, .flight .colony a, .flight .deploy a, .flight .destroy a, .flight .espionage a,\n.flight .federation a, .flight .harvest a, .flight .hold a, .flight .missile a, .flight .transport a {\n  color: #E6EBFB;\n}\n\n.combatreport {\n  color: red;\n}\n\n.espionagereport {\n  color: orange;\n}\n\n.timeremaining {\n  color: orange;\n}\n\n.mnl_outbox, .mnl_outbox * {\n  background-color: #000000;\n}\n.mnl_joueur, .mnl_joueur * {\n  background-color: #114444;\n}\n.mnl_exploit, .mnl_exploit * {\n  /*  background-color: #999999;*/\n}\n*.ownexpedition, .mnl_expedition {\n  color: lightblue;\n}\n.mnl_buildlist, .mnl_buildlist * {\n  color: #000066;\n}\n.mnl_alliance, .mnl_alliance * {\n  background-color: #446666;\n}\n.msg_admin, .msg_admin * {\n  /*color: #9900ff;*/\n  color: #5500aa;\n  background-color: #aaaaaa;\n}\n.new_message, .new_message * {\n  color: #000000;\n  background-color: #ffffff;\n}\n\n.battle_report_link {\n  color: black;\n  background-color: white;\n}\n\n/* Que list ===================================================================================================== */\n.que_item_0 > .que_unit_time {\n  color: lime;\n}\n.que_unit_time_seconds_0 {\n  background-color: green;\n}\n\n/* Note/Bookmark page ===================================================================================================== */\n.note_coordinates {\n  color: #00B7F3;\n}\n\n.note_header {\n  padding: 0.2em;\n  flex-wrap: wrap;\n}\n\n#note_text, #note_title {\n  width: 95%;\n}\n\n/* Emperor page ===================================================================================================== */\n.ov_user_total {\n  color: #FF0000\n}\n.ov_user_rank {\n  color: #FFFF00\n}\n\n\n\n\n\n#tab_admin_settings {\n  font-size: 1em;\n  max-width: 60em;\n  display: none;\n}\n\n#tab_admin_settings > div {\n  text-align: left;\n}\n\n#game_disable_reason {\n  width: 96%;\n  margin-left: 0;\n}\n\n#tab_admin_urls label {\n  flex-shrink: 0;\n}\n\n#tab_admin_urls input {\n  width: 100%;\n  max-width: 40em;\n}\n\n#tab_admin_advertise textarea {\n  width: 100%;\n}\n\n#stats_hide_player_list, #stats_schedule {\n  margin-left: 0;\n  width: 95%;\n}\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n/**\n * Checkator jQuery Plugin\n * A plugin for radio and checkbox elements\n * version 1.1, Dec 20th, 2013\n * by Ingi P. Jacobsen\n */\n/* RESET */\ninput[type=radio],\ninput[type=checkbox] {\n  margin-bottom: 0;\n}\n/* SOURCE ELEMENT (when checkator is enabled on an element) */\n.checkator_source {\n  position: relative;\n  z-index: 2;\n  display: block;\n}\n/* SHARED SETTING */\n.checkator_holder { /* Holder for the new element */\n  display: inline-block;\n  position: relative;\n}\n.checkator_element { /* New element */\n  border: 0.2em solid #abadb3;\n  /*background-color: #fff;*/\n  display: block;\n  position: absolute;\n  top: 0;\n  right: 0;\n  bottom: 0;\n  left: 0;\n  z-index: 1;\n}\n.checkator_source:checked+.checkator_element:after { /* Checked element dot */\n  /*background-color: #7fa92c;*/\n  background-color: lime;\n  display: block;\n  content: ' ';\n  top: 20%;\n  right: 20%;\n  bottom: 20%;\n  left: 20%;\n  position: absolute;\n}\n/*\n.checkator_source:focus+.checkator_element { /!* Focused element *!/\n  border: 0.2em solid #cc0;\n}\n*/\n.checkator_source:hover+.checkator_element { /* Hovered element */\n  /* Bug: There is a bug in chrome preventing this from working correctly */\n  /*background-color: #def;*/\n  /*border: 0.2em solid #79b;*/\n  border: 0.2em solid #dfeafd;\n}\n/* RADIO SETTINGS */\n.checkator_element.radio {\n  border-radius: 50% !important;\n}\n.checkator_element.radio:after {\n  border-radius: 50% !important;\n}\n/* CHECKBOX SETTINGS */\n.checkator_element.checkbox { /* New element */\n  border: 0.2em solid #abadb3;\n}\n\n/* Skinnable items ************************************************************************************************** */\nbody {\n  color: #E6EBFB;\n}\n.ui-dialog-titlebar.ui-widget-header {\n  color: #E6EBFB;\n}\n.ui-widget-content {\n  color: #E6EBFB;\n}\n\ninput[type=checkbox]:checked {\n  background-color: green;\n  color: green;\n}\n\n.ui-slider-range { /* Coloring Slider Range */\n  background-image: none;\n  background: #f6a828;\n}\n\ninput,\nselect,\ntextarea,\ntextarea.ui-button,\nth,\ntd,\n.td,\n.cell,\n.th\n{\n  border-color: #415680;\n  background-color: #344566;\n}\n\n.subheader, tr.subheader > td, tr.subheader > th {\n  background-color: #233452;\n}\n.spy_table tr.subheader > td, .spy_table tr.subheader > th {\n  background-color: #233452;\n}\n.th, .header,  .header > th {\n  background-color:  #132544; /*rgba(0,0,0,0.35);*/\n}\n\ndiv.header {\n  padding: 0.5em 0.5em;\n  font-size: 110%;\n}\n\ndiv.cell {\n  padding: 0.1em 0.5em;\n}\n\n\ninput[type=checkbox] {\n  background-image: none;\n  background-color: #222D42; /*rgba(0,0,0,0.35);*/\n  color: #222D42;\n}\n.ui-button.ui-input-text {\n  background-color: #415680;\n}\n\n/* Setting dialog content background - which is 'transparent' in jQuery-UI */\n.ui-dialog .ui-dialog-content {\n  background-image: none;\n  background-color: #344566;\n}\n\n.button_pseudo {\n  border-color: #415680;\n}\n.button_pseudo:not(img) {\n  background-color: #222D42; /*rgba(0,0,0,0.35);*/\n}\n.button_pseudo:not(img):hover, .button_pseudo_pressed:not(img) {\n  background-color: #415680; /* rgba(65,86,128,1) */\n}\n#left_menu.MENU_NARROW_CELLS td, #left_menu.MENU_NARROW_CELLS th {\n  border-color: #415680;\n}\n#left_menu th {\n  color: goldenrod;/*#63698c;*/\n  background-color: #212c42;\n}\n#left_menu td a:hover, #left_menu th a:hover, #left_menu.MENU_NARROW_CELLS td a:hover, #left_menu.MENU_NARROW_CELLS th a:hover {\n  background-color: #415680;\n}\n#left_menu a:not(.lm_with_image) {\n  background-color: rgba(35, 52, 82, 0.5); /*#233452;*/\n  border-color: #415680;\n}\n\n#left_menu.MENU_NARROW_CELLS a:not(.lm_with_image)\n{\n  background-color: rgba(35, 52, 82, 0); /*#233452;*/\n}\n#left_menu #server_name {\n  color: #00FF00;\n}\n#menu_show_hidden {\n  color: yellow;\n}\n#menu_news .fresh {\n  color: red;\n}\n#menu_admin_exit {\n  color: red;\n}\n#menu_premium {\n  color: gold;\n}\n\n#menu_premium_level {\n  color: gold;\n}\n.global_admin_warning {\n  color:red;\n}\n/* News and Surveys ================================================================================================= */\n.news_header {\n  background-color: #233452;\n}\n.news_date {\n  color: yellow;\n}\n.news_publisher {\n}\n.news_fresh, .news_future {\n  color: red;\n}\n.news_link {\n  color: #ade;\n}\n.survey_header, .survey_voted {\n  background-color: #233452;\n}\n.survey_confirm {\n  background-color: #233452;\n}\n.survey_votes_bar {\n  background-color: darkgreen;\n}\n\n.income_with_fleet {\n  color: #80a0C0;\n}\n\ntd[go]:hover { /* Skin specific */\n  background-color: #415680;\n}\n\n\n/* Unit purchase --------------------------------------------------------------- */\n.unit_preview {\n  border-color: #344566;\n}\n.ov_planet_fill_bar {\n  border-color: rgb(153, 153, 255);\n}\n.unit_border_selected {\n  border-color: #6B90D3;  /* Reserved for unit_preview border color */\n}\n\n/* Chat ************************************************************************************************************* */\n#shoutbox tr  {\n  border-color:  #233452;\n}\n.chat_online, #onlinebox td {\n  background-color: black;\n}\n.chat_message {\n  background-color: black;\n  color:white;\n}\n#chat_msg_wrapper #msg {\n  background-color: black;\n}\n#chat_color_select, #chat_message_smiles2, #chat_command_menu, #chat_muted_tooltip {\n  background-color: #233452;\n}\n/* Batch operations ************************************************************************************************************* */\n.unit_change {\n  color: #80a0C0;\n}\n\n/* Black market ===================================================================================================== */\n.market_services {\n  background-color: #344566;\n}\n\n/* Planet List ===================================================================================================== */\n.planet_list_fields {\n  background-color: #344566;\n}\n.ov_struc_timer {\n  background-color: #344566;\n}\n.planet_list_submoon_fields {\n  background-color: #344566;\n}\n\n/* Technology/TechTree page  ===================================================================================================== */\n#tab_tech_tree .tech_container {\n  border-color: #415680;\n}\n\n.planet-manage-governor-hire-container {\n  border-color: #415680;\n}\n\ntd.error_message {\n  background-color: #415680;\n}\n\n\n\n.ui-dialog-titlebar-show {\n  display: block !important;\n}\n#tutorial_block {\n  /*display: none;*/\n}\n#tutorial_container {\n  padding: 0.5em;\n  max-width: 40em;\n  min-width: 40em;\n  display: inline-block;\n}\n#tutorial_header, .tutorial_dialog_title {\n  padding: 0.5em;\n  font-size: 130%;\n}\n#tutorial_text {\n  padding: 0.5em;\n  text-align: justify;\n}\n#tutorial_text_adviser {\n  float: left;\n}\n#tutorial_text::after {\n  clear: both;\n}\n#tutorial_buttons {\n  padding: 0.5em;\n  text-align: center;\n}\n#tutorial_button_next {\n  margin-left: auto;\n}\n#tutorial_button_window, #tutorial_button_window_off {\n  margin: 0 auto;\n}\n#tutorial_button_close {\n}\n#tutorial_footer {\n  padding: 0.5em;\n  text-align: center;\n}\n\n[go_url] {\n  cursor: pointer;\n}\n\n\n\n\n/* Hiding resource tooltip pattern */\n#navbar_resource_flex_tooltip_pattern, #navbar_resource_flex_tooltip_pattern_energy\n{\n  display: none;\n}\n\n#navbar_container {\n  display: flex;\n  flex-wrap: wrap;\n  flex-direction: column;\n\n  justify-content: center;\n  align-items: center;\n}\n#navbar_container.resbar_v {\n  flex-direction: row;\n\n  align-items: flex-start;\n  justify-content: center;\n\n  text-align: left;\n}\n#navbar.resbar_v {\n  display: flex;\n  flex-wrap: nowrap;\n\n  margin:0;\n}\n\n.navbar_resources_flex {\n  display: flex;\n  flex-wrap: wrap;\n  flex-grow: 1;\n  justify-content: center;\n}\n.navbar_resources_flex.resbar_v {\n  flex-direction: column;\n}\n\n.navbar_resources_flex_title {\n  display: flex;\n  align-items: center;\n}\n\n/* Resbar Vertical -----------------------------------------------------------------------------------------------*/\n.navbar_resources_flex.resbar_v .navbar_resources_flex_title {\n  display: block;\n}\n\n.navbar_resbar_flex_wrap2 {\n  display: flex;\n  flex-grow: 1;\n  flex-wrap: wrap;\n}\n.navbar_resources_flex.resbar_v .navbar_resbar_flex_wrap2 {\n  flex-direction: column;\n  flex-wrap: nowrap;\n}\n\n.navbar_resbar_flex_wrap1 {\n  display: flex;\n  flex-grow: 1;\n  flex-wrap: wrap;\n}\n.navbar_resources_flex.resbar_v .navbar_resbar_flex_wrap1 {\n  flex-direction: column;\n  flex-wrap: nowrap;\n  justify-content: space-around;\n}\n\n.navbar_resources_flex_resource > .navbar_resource_numbers {\n  justify-content: flex-start;\n}\n\n.navbar_resources_flex_resource.cell {\n  cursor: pointer;\n\n  margin: 0 0.2em;\n  /* Overriding .cell padding */\n  padding: 0.1em 0.2em;\n  border-radius: 0.4em;\n\n  border: 1px solid green;\n\n  display: flex;\n  flex-grow: 1;\n  align-items: stretch;\n\n  flex-basis: 0.1%;\n}\n@media all and (-ms-high-contrast: none), (-ms-high-contrast: active) {\n  /* IE10+ CSS styles go here */\n  .navbar_resources_flex_resource.cell {\n    flex-basis: auto;\n  }\n}\n\n.navbar_resources_flex.resbar_v .navbar_resources_flex_resource {\n  flex-grow: 0;\n  margin: 0;\n  flex-basis: auto;\n}\n\n.navbar_resources_flex_resource > div {\n  display: flex;\n  align-items: flex-end !important;\n  justify-content: center;\n  flex-direction: column;\n}\n.navbar_resources_flex_resource > div.navbar_resource_numbers {\n  flex-grow: 1;\n}\n\n\n.quest_list\n{\n  max-width: 70em;\n  display: inline-block;\n}\n\n.quest_filter_wrapper\n{\n  padding: 1em;\n  font-size: 120%;\n}\n\n.quest_admin_wrapper {\n  margin: 0.5em;\n  display: flex;\n  flex-wrap: nowrap;\n  justify-content: space-between;\n}\n\n.quest_admin_buttons\n{\n  margin: 0;\n  flex-shrink: 100;\n  flex-grow: 0.01;\n  flex-basis: 1%;\n}\n\n.quest_wrapper\n{\n  flex-shrink: 1;\n  flex-grow: 1;\n  flex-basis: 1%;\n}\n\n.quest_header.header\n{\n  font-size: 120%;\n  margin: 0.5em;\n  padding: 0.5em;\n}\n\n.quest_name\n{\n\n}\n\n.quest_status\n{\n\n}\n\n.quest_description.cell\n{\n  text-align: justify;\n  margin: 0.5em;\n  padding: 0.5em;\n}\n\n.quest_details\n{\n  flex-wrap: wrap;\n  align-items: stretch;\n}\n\n.quest_details > div\n{\n  flex-grow: 1;\n  margin: 0.5em;\n  padding: 0.5em;\n}\n\n.quest_details ul\n{\n  padding-left: 0;\n}\n\n.quest_details li\n{\n  margin-left: 2em;\n}\n\n\n.admin_message_box {\n  display: inline-block;\n\n  text-align: center;\n\n  max-width: 50em;\n\n  margin: 1em auto;\n}\n\n.admin_message_box .cell, .admin_message_box .header, .admin_message_box .subheader {\n  padding: 0.5em;\n}\n\n.paging {\n  font-size: 125%;\n}\n\n.paging > * {\n  /*border: 1px solid red;*/\n}\n\n.paging .first-last {\n  /*color: green;*/\n}\n.paging .prev-next {\n  /*color: red;*/\n}\n.paging .ellipsis {\n  /*color: orange;*/\n}\n.paging .before-after {\n  /*color: darkblue;*/\n}\n.paging .inactive {\n  color: darkgrey;\n}\n.paging .ellipsis {\n  color: grey;\n}\n.paging .current {\n  color: lightblue;\n  font-size: 125%;\n}\n\n.rank-table {\n  align-items: flex-start;\n  max-width: 74em;\n  width: auto;\n}\n.rank-table > div {\n  max-width: 9em;\n  margin: 0.3em;\n  padding: 0;\n}\n.rank-table .rank, .rank-emperor {\n  width: 9em;\n  height: 9em;\n  display: inline-block;\n}\n.rank-selected {\n  border: 0.3em solid green;\n}\n.rank-selected-not {\n  border: 0.3em solid transparent;\n}\n\n.rank.nick {\n  display: inline-block;\n  height: 1.454545em;\n  width: 1.454545em;\n  background-image: url(\"/design/images/ranks_sprite_small.png\");\n  /*height: 1.5em;*/\n  /*width: 1.5em;*/\n}\n/* Force inline-block for nick's rank in Galaxy user popup */\n.user_template .rank-nick {\n  display: inline-block;\n}\n.rank {\n  background-size: 1000% 600%;\n  background-image: url(\"/design/images/ranks_sprite.png\");\n}\n.rank.rank-0 {background-position:    0  0;}\n.rank.rank-1 {background-position: -200% 0;}\n.rank.rank-2 {background-position: -300% 0;}\n.rank.rank-3 {background-position: -400% 0;}\n.rank.rank-4 {background-position: -600% 0;}\n.rank.rank-5 {background-position: -700% 0;}\n.rank.rank-6 {background-position: -800% 0;}\n\n/*\n!* Текущий расклад *!\n.rank.rank-7  {background-position: -000% -200%;}\n.rank.rank-8  {background-position: -500% -200%;}\n.rank.rank-9  {background-position: -500% -300%;}\n.rank.rank-10 {background-position: -600% -300%;}\n.rank.rank-11 {background-position: -700% -300%;}\n\n!* Ромбики *!\n.rank.rank-7  {background-position: -000% -200%;}\n.rank.rank-8  {background-position: -500% -200%;}\n.rank.rank-9  {background-position: -400% -200%;}\n.rank.rank-10 {background-position: -900% -200%;}\n.rank.rank-11 {background-position: -400% -300%;}\n\n!* Шестиконечная звезда *!\n.rank.rank-7  { background-position: -500% -300%; }\n.rank.rank-8  { background-position: -000% -400%; }\n.rank.rank-9  { background-position: -900% -300%; }\n.rank.rank-10 { background-position: -400% -400%; }\n.rank.rank-11 { background-position: -900% -400%; }\n*/\n\n/* Почти текущий расклад */\n.rank.rank-7  {background-position:     0 -200%;}\n.rank.rank-8  {background-position: -500% -200%;}\n.rank.rank-9  {background-position: -500% -300%;}\n.rank.rank-10 {background-position: -000% -400%;}\n.rank.rank-11 {background-position: -500% -400%;}\n\n.rank.rank-12 {background-position:     0 -500%;}\n.rank.rank-13 {background-position: -100% -500%;}\n.rank.rank-14 {background-position: -200% -500%;}\n.rank.rank-15 {background-position: -400% -500%;}\n.rank.rank-16 {background-position: -500% -500%;}\n.rank.rank-17 {background-position: -600% -500%;}\n.rank.rank-18 {background-position: -700% -500%;}\n.rank.rank-19 {background-position: -800% -500%;}\n.rank.rank-20 {background-position: -900% -500%;}\n\n.js_admin_ban {\n  cursor: pointer;\n}\n.js_admin_whois {\n  /*border-bottom: 0.3em double;*/\n  /*text-align: left;*/\n  font-weight: bold;\n  margin-left: 1em;\n  cursor: pointer;\n}\n\n.options_block_header {\n}\n\n.option_block_option {\n  text-align: left;\n}\n\n#galaxy_form {\n  display: inline-block;\n}\n\n#uni_nav_scan_wrapper {\n  display: inline-block;\n  margin: 0.5em;\n}\n\n.uni_nav_wrapper.header {\n  display: flex;\n  flex-wrap: wrap;\n  justify-content: center;\n\n  padding: 0;\n}\n.uni_nav_wrapper2.header {\n  display: flex;\n  flex-wrap: wrap;\n  justify-content: center;\n\n  padding: 0;\n}\n.uni_nav_child {\n  display: flex;\n  flex-wrap: nowrap;\n  align-items: center;\n  padding: 0 1em;\n}\n\n.uni_show_user > span,\n.uni_show_ally > span\n{\n  text-wrap: none;\n  white-space: nowrap;\n}\n\n\n.ui-icon,\n.ui-widget-content .ui-icon {\n\tbackground-image: url(\"/design/css/images/ui-icons_e6ebfb_256x240.png\");\n}\n.ui-widget-header .ui-icon {\n\tbackground-image: url(\"/design/css/images/ui-icons_13233E_256x240.png\");\n}\n.ui-state-default .ui-icon {\n\tbackground-image: url(\"/design/css/images/ui-icons_e6ebfb_256x240.png\");\n}\n.ui-state-hover .ui-icon,\n.ui-state-focus .ui-icon {\n\tbackground-image: url(\"/design/css/images/ui-icons_e6ebfb_256x240.png\");\n}\n.ui-state-active .ui-icon {\n\tbackground-image: url(\"/design/css/images/ui-icons_e6ebfb_256x240.png\");\n}\n.ui-state-highlight .ui-icon {\n\tbackground-image: url(\"/design/css/images/ui-icons_2e83ff_256x240.png\");\n}\n.ui-state-error .ui-icon,\n.ui-state-error-text .ui-icon {\n\tbackground-image: url(\"/design/css/images/ui-icons_ff0000_256x240.png\");\n}\n\ntable.novapedia .unit_description {\n  display: table-row;\n  position: relative;\n  margin: 0.5em;\n}\ntable.novapedia .unit_image_large {\n  max-width: 50em;\n}\ntable.novapedia .unit_image_small {\n  border: 0;\n  height: 128px;\n  width: 128px;\n  margin-right: 7px;\n}\n"
  },
  {
    "path": "design/templates/OpenGame/_template.ini",
    "content": "; Which template this one inherits\n; Inherited template will be used as source for missing files from current template\n; _inherit = \"OpenGame\"\n\n; Does this template render whole page?\n;\n_renderWhole = 1\n"
  },
  {
    "path": "design/templates/OpenGame/admin/add_moon.tpl.html",
    "content": "<h2>{L_addm_title}</h2>\n\n<form action=\"\" method=\"post\">\n  <input type=\"hidden\" name=\"mode\" value=\"addit\">\n\n  <table width=\"320\" border=\"0\" cellspacing=\"2\" cellpadding=\"0\" style=\"color:#FFFFFF\">\n  <tr>\n    <td class=\"c\" colspan=\"6\">{L_addm_addform}</td>\n  </tr>\n  <tr>\n    <th width=\"150\">{L_addm_playerid}</th>\n    <th width=\"0%\"><input type=\"text\" name=\"user\" size=\"3\"></th>\n  </tr>\n\n  <tr>\n    <th>{L_addm_moonname}</th>\n    <th><input type=\"text\" name=\"name\"></th>\n  </tr>\n\n  <tr>\n    <th colspan=\"2\"><input type=\"submit\" value=\"{L_addm_moondoit}\"></th>\n  </table>\n</form>\n"
  },
  {
    "path": "design/templates/OpenGame/admin/adm_flying_fleets.tpl.html",
    "content": "<!-- INCLUDE fleet_javascript -->\n\n<h2>{L_flt_title}</h2>\n<table><tbody>\n  <tr class=\"c_c\">\n    <th colspan=\"2\" >{L_flt_fleet}</th>\n    <th>{L_flt_mission}<br />{L_flt_here_there}</th>\n    <th colspan=\"3\">{L_flt_departure}</th>\n    <th colspan=\"3\">{L_flt_e_owner}</th>\n    <th rowspan=\"2\">{L_flt_staying}</th>\n  </tr>\n  <tr class=\"c_c\">\n    <th>{L_flt_id}</th>\n    <th>{L_flt_ships}</th>\n    <th>{L_flt_mission}</th>\n    <th>{L_flt_owner}</th>\n    <th>{L_flt_planet}</th>\n    <th>{L_flt_time_return}</th>\n    <th>{L_flt_owner}</th>\n    <th>{L_flt_planet}</th>\n    <th>{L_flt_time_arrive}</th>\n  </tr>\n  <!-- BEGIN fleets -->\n  <tr class=\"c_c\">\n    <td style=\"cursor: pointer;\" onmouseover='fleet_dialog_show(this, {fleets.ID})' onmouseout='popup_hide()'>{fleets.ID}</td>\n    <td style=\"cursor: pointer;\" onmouseover='fleet_dialog_show(this, {fleets.ID})' onmouseout='popup_hide()'>{fleets.AMOUNT}</td>\n    <td>{fleets.MISSION_NAME}<br /><!-- IF fleets.MESSAGE -->{L_flt_here}<!-- ELSE -->{L_flt_there}<!-- ENDIF --></td>\n    <td nowrap><!-- IF fleets.OWNER -->[{fleets.OWNER}] {fleets.OWNER_NAME}<!-- ELSE -->-<!-- ENDIF --></td>\n    <td nowrap class=\"link\">{fleets.START_URL} {fleets.START_TYPE_TEXT_SH}</td>\n    <td>{fleets.START_TIME_TEXT}</td>\n    <td nowrap><!-- IF fleets.TARGET_OWNER_NAME -->[{fleets.TARGET_OWNER}] {fleets.TARGET_OWNER_NAME}<!-- ELSE -->-<!-- ENDIF --></td>\n    <td nowrap class=\"link\">{fleets.END_URL} {fleets.END_TYPE_TEXT_SH}</td>\n    <td>{fleets.END_TIME_TEXT}</td>\n    <td><!-- IF fleets.STAY_TIME_INT -->{fleets.STAY_TIME}<!-- ELSE -->-<!-- ENDIF --></td>\n  </tr>\n  <!-- BEGINELSE -->\n  <tr class=\"c_c\">\n    <td colspan=\"10\">{L_flt_no_fleet}</td>\n  </tr>\n  <!-- END fleets -->\n</tbody></table>\n"
  },
  {
    "path": "design/templates/OpenGame/admin/adm_log_main.tpl.html",
    "content": "<h2>{L_adm_er_ttle}</h2>\n<a class=\"link\" href=\"admin/adm_log_main.php?delete_update_info=1\">{L_adm_log_delete_update_info}</a><br /><br />\n\n<table width=\"600px\">\n  <tr class=\"c_l\">\n    <th width=\"60px\">{L_adm_er_idmsg}</th>\n    <th width=\"120px\">{L_adm_er_time}</th>\n    <th>{L_adm_er_type}</th>\n    <th>{L_adm_er_play}</th>\n    <th>{L_adm_er_page}</th>\n    <th class=\"c_c\" width=\"16px\"><img src=\"design/images/r1.png\"></th>\n  </tr>\n\n  <!-- BEGIN error -->\n    <tr class=\"c_l\">\n      <td><a href=\"admin/adm_log_main.php?detail={error.LOG_ID}\" class=\"link\">{error.LOG_ID}</a></td>\n      <td>{error.LOG_TIMESTAMP}</td>\n      <td>[{error.LOG_CODE}] {error.LOG_TITLE}</td>\n      <td>{L_adm_er_idmsg} {error.LOG_SENDER}<!-- IF error.LOG_USERNAME -->&nbsp;[{error.LOG_USERNAME}]<!-- ENDIF --></td>\n      <td>{error.LOG_PAGE}</td>\n      <td class=\"c_c\"><a href=\"admin/adm_log_main.php?delete={error.LOG_ID}\"><img src=\"design/images/r1.png\"></a></td>\n    </tr>\n\n    <tr><td colspan=\"6\" class=\"c_l error_message\">{error.LOG_TEXT}</td></tr>\n  <!-- END error -->\n\n  <tr>\n    <th class=\"c_l\" colspan=6>\n      {L_adm_er_nbs}&nbsp;{LOG_MESSAGES_VISIBLE}/{LOG_MESSAGES_TOTAL}\n      <!-- <span class=\"fr\">[<a href=\"admin/errors.php?deleteall=yes\">{L_adm_er_clear}</a>]</span> -->\n    </th>\n  </tr>\n</table>\n"
  },
  {
    "path": "design/templates/OpenGame/admin/adm_log_main_detail.tpl.html",
    "content": "<h1>{L_adm_er_ttle}</h1>\n\n<table width=\"auto\" class=\"c_l vars\">\n  <tr class=\"c_l\">\n    <th nowrap>{L_adm_er_idmsg} {log_id}</th>\n    <th nowrap>{L_adm_er_time} {log_timestamp} [{log_time}]</th>\n    <th nowrap>[{log_code}] {log_title}</th>\n    <th nowrap>{L_adm_er_play} <b>ID {log_sender}</b><!-- IF log_username --> [{log_username}]<!-- ENDIF --></th>\n    <th nowrap>{log_page}</th>\n  </tr>\n\n  <tr class=\"c_l\">\n    <th>{L_adm_er_text}</th>\n    <td colspan=\"4\"><div style=\"width: 800px\">{log_text}</div></td>\n  </tr>\n\n  <style type=\"text/css\">\n    .vars pre {\n      text-align: left; background-color: #111111; color: #0A0; font-family: Courier, monospace !important; padding: 0; margin: 0; font-weight: 800; font-size: 14px;\n    }\n    .vars td:first-child {\n      vertical-align: top;\n    }\n    /*.vars td:nth-child(2) {*/\n    /*  !*horiz-align: left;*!*/\n    /*  background-color: black;*/\n    /*}*/\n    .vars td {\n      /*horiz-align: left;*/\n      background-color: black;\n    }\n    .vars td:nth-child(2) table {\n      margin: 0;\n    }\n  </style>\n\n  <!-- BEGIN vars -->\n  <tr><td>{vars.VAR_NAME}</td><td><pre>{vars.VAR_VALUE}</pre></td></tr>\n  <!-- END vars -->\n\n<!--  &lt;!&ndash; IF .vars &ndash;&gt;-->\n<!--  <tr class=\"c_l vars\">-->\n<!--    <th>{L_adm_er_bktr}</th>-->\n<!--    <td colspan=\"4\">-->\n<!--      <div style=\"width: 800px\">-->\n<!--        <table class=\"no_border_image\">-->\n<!--        &lt;!&ndash; BEGIN vars &ndash;&gt;-->\n<!--          <tr><td>{vars.VAR_NAME}</td><td><pre>{vars.VAR_VALUE}</pre></td></tr>-->\n<!--        &lt;!&ndash; END vars &ndash;&gt;-->\n<!--        </table>-->\n<!--      </div>-->\n<!--    </td>-->\n<!--  </tr>-->\n<!--  &lt;!&ndash; ENDIF &ndash;&gt;-->\n\n</table>\n"
  },
  {
    "path": "design/templates/OpenGame/admin/adm_messagelist.tpl.html",
    "content": "<h2>{L_mlst_title}</h2>\n\n<form action=\"\" method=\"post\" id=\"aForm\">\n  <input type=\"hidden\" name=\"msg_del\" id=\"msg_del\" value=\"\" />\n\n  <table border=\"0\" cellspacing=\"1\" cellpadding=\"1\">\n    <tr>\n      <td class=\"c\">{L_mlst_hdr_type}\n        <select name=\"int_type_selected\" onchange=\"submit();\">\n          <!-- BEGIN int_type_selected -->\n          <option value=\"{int_type_selected.VALUE}\"<!-- IF int_type_selected.VALUE == TYPE_SELECTED --> selected<!-- ENDIF -->>{int_type_selected.TEXT}</option>\n          <!-- END int_type_selected -->\n        </select>\n      </td>\n\n      <td class=\"c\">\n        <input type=\"submit\" name=\"page_prev\" value=\"{L_mlst_hdr_prev}\" />\n        {L_mlst_hdr_page}\n\n        <select name=\"int_page_current\" onchange=\"submit();\">\n          <!-- BEGIN page -->\n            <option value=\"{page.NUMBER}\"<!-- IF page.NUMBER == PAGE_CURRENT --> selected<!-- ENDIF -->>{page.NUMBER}</option>\n          <!-- END page -->\n        </select>\n        / {PAGE_MAX}\n        <input type=\"submit\" name=\"page_next\" value=\"{L_mlst_hdr_next}\" />\n      </td>\n    </tr>\n\n    <tr>\n      <td class=\"c\" colspan=\"2\">\n        {L_mlst_hdr_delfrom}\n        <input type=\"text\" name=\"delete_day\" size=\"2\" value=\"DD\" onfocus=\"this.value = this.value == 'DD' ? '' : this.value;\" onblur=\"this.value = this.value ? this.value : 'DD';\" />\n        <input type=\"text\" name=\"delete_month\" size=\"2\" value=\"MM\" onfocus=\"this.value = this.value == 'MM' ? '' : this.value;\" onblur=\"this.value = this.value ? this.value : 'MM';\" />\n        <input type=\"text\" name=\"delete_year\" size=\"4\" value=\"YYYY\" onfocus=\"this.value = this.value == 'YYYY' ? '' : this.value;\" onblur=\"this.value = this.value ? this.value : 'YYYY';\" />\n        <input type=\"submit\" name=\"str_delete_date\" value=\"{L_mlst_bt_deldate}\" />\n      </td>\n    </tr>\n\n    <tr>\n      <td class=\"c\" colspan=\"2\"><input type=\"submit\" name=\"str_delete_selected\" value=\"{L_mlst_bt_delsel}\" /></td>\n    </tr>\n  </table>\n\n  <table width=\"600px\" border=\"0\" cellspacing=\"1\" cellpadding=\"1\">\n    <tr class=\"c_c\">\n      <th>{L_mlst_hdr_action}</th>\n      <th>{L_mlst_hdr_id}</th>\n      <th width=\"121px\" nowrap>{L_mlst_hdr_time}</th>\n      <th>{L_mlst_hdr_from}</th>\n      <th>{L_mlst_hdr_to}</th>\n      <!-- <th class=\"c\" width=\"350\">{L_mlst_hdr_text}</th> -->\n    </tr>\n\n    <!-- BEGIN message -->\n    <tr class=\"c_c\">\n      <td rowspan=\"2\">\n        <input type=\"checkbox\" name=\"selected[]\" value=\"{message.ID}\" /><br/>\n        <img src=\"design/images/r1.png\" onclick=\"$('#msg_del').val({message.ID});$('#aForm').submit();\">\n      </td>\n      <td nowrap>{message.ID}</td>\n      <td nowrap>{message.TIME}</td>\n      <td nowrap>{message.FROM}</td>\n      <td nowrap>{message.OWNER_NAME} [{message.OWNER_ID}]</td>\n    </tr>\n    <tr>\n      <td colspan=\"4\" class=\"c_l error_message\">{message.TEXT}</td>\n    </tr>\n    <!-- BEGINELSE -->\n    <tr>\n      <td colspan=\"5\">{L_mlst_no_messages}</td>\n    </tr>\n    <!-- END message -->\n  </table>\n\n</form>\n"
  },
  {
    "path": "design/templates/OpenGame/admin/adm_metamatter.tpl.html",
    "content": "<h2>{L_adm_mm_title}</h2>\n\n<form action=\"\" method=\"post\">\n  <table width=\"305\">\n    <tbody>\n\n    <tr class=\"c_c\">\n      <th colspan=\"2\">{L_adm_mm_title}</th>\n    </tr>\n    <tr class=\"c_c\">\n      <td>{L_adm_mm_account}</td>\n      <td>\n        <input name=\"accountId\" type=\"text\" value=\"{ACCOUNT_ID}\"/><br/>\n        {L_adm_mm_account_hint}\n      </td>\n    </tr>\n\n\n    <tr class=\"c_c\">\n      <td colspan=\"2\">{L_adm_or_caption}</td>\n    </tr>\n\n    <tr>\n      <th>{L_adm_mm_player}</th>\n      <th>\n        <input name=\"playerId\" type=\"text\" value=\"{PLAYER_ID}\"/><br/>\n        {L_adm_mm_player_hint}\n      </th>\n    </tr>\n\n\n    <tr class=\"c_c\">\n      <td>{L_sys_metamatter}</td>\n      <td><input name=\"points\" type=\"text\" value=\"{POINTS}\"/></td>\n    </tr>\n    <tr class=\"c_c\">\n      <td>{L_adm_reason}</td>\n      <td><input name=\"reason\" type=\"text\" value=\"{REASON}\"/></td>\n    </tr>\n\n\n    <tr class=\"c_c\">\n      <th colspan=\"2\">\n        <!-- IF NEED_CONFIRMATION -->\n        <input type=\"submit\" name=\"confirm_mm_change\" value=\"{L_adm_confirm_do}\"/>\n        <!-- ELSE -->\n        <input type=\"Submit\" value=\"{L_adm_apply}\"/>\n        <!-- ENDIF -->\n      </th>\n    </tr>\n\n    </tbody>\n  </table>\n</form>\n"
  },
  {
    "path": "design/templates/OpenGame/admin/adm_overview.tpl.html",
    "content": "<h2>{L_adm_ov_count}</h2>\n\n<table width=\"600\">\n  <tr class=\"c_c\">\n    <th><a href=\"admin/overview.php?cmd=sort&type=id\">{L_adm_ul_id}</a></th>\n    <th><a href=\"admin/overview.php?cmd=sort&type=username\">{L_adm_ul_name}</a></th>\n    <th><a href=\"admin/overview.php?cmd=sort&type=ally_name\">{L_adm_ov_ally}</a></th>\n    <th><a href=\"admin/overview.php?cmd=sort&type=total_points\">{L_adm_ov_point}</a></th>\n    <th><a href=\"admin/overview.php?cmd=sort&type=onlinetime\">{L_adm_ov_activ}</a></th>\n    <th><img src=\"design/images/icon_mail.gif\" alt=\"{L_adm_ov_altpm}\" title=\"{L_adm_ov_wrtpm}\" border=\"0\"></th>\n  </tr>\n\n  <!-- BEGIN user -->\n  <tr class=\"c_c\">\n    <td class=\"c_r\">{user.ID}</td>\n    <td><a href= # title=\"{user.BROWSER}\">{user.NAME}</a></td>\n    <td>{user.ALLY}</td>\n    <td class=\"c_r\">{user.STAT_POINTS}</td>\n    <td class=\"c_r\">{user.ACTIVITY}</td>\n    <td><a href=\"messages.php?mode=write&id={user.ID}\"><img src=\"design/images/icon_mail.gif\" alt=\"{L_adm_ov_altpm}\" title=\"{L_adm_ov_wrtpm}\" border=\"0\"></a></td>\n  </tr>\n  <!-- END user -->\n\n  <tr>\n    <th class=\"c_c\" colspan=\"6\">{L_adm_ov_count}: {USERS}</th>\n  </tr>\n</table>\n\n<!-- INCLUDE page_hint -->\n"
  },
  {
    "path": "design/templates/OpenGame/admin/adm_planet_list.tpl.html",
    "content": "<h2>{PAGE_TITLE}</h2>\n<table>\n  <tr class=\"c_c\">\n    <th rowspan=\"2\">{L_sys_id}</th>\n    <th rowspan=\"2\">{L_adm_name}</th>\n    <th colspan=\"3\">{L_sys_coordinates}</th>\n    <th rowspan=\"2\">{L_sys_planet_title_short}</th>\n    <!-- IF PARENT_COLUMN -->\n      <th colspan=\"2\">{L_adm_planet_parent}</th>\n    <!-- ENDIF -->\n    <th colspan=\"2\">{L_adm_sys_owner}</th>\n  </tr>\n  <tr class=\"c_c\">\n    <th>{L_sys_galaxy}</th>\n    <th>{L_sys_system}</th>\n    <th>{L_sys_planet}</th>\n    <!-- IF PARENT_COLUMN -->\n      <th>{L_sys_id}</th>\n      <th>{L_adm_name}</th>\n    <!-- ENDIF -->\n    <th>{L_sys_id}</th>\n    <th>{L_sys_user_name_short}</th>\n  </tr>\n  <!-- BEGIN planet -->\n  <tr>\n    <td class=\"c_r\">{planet.ID}</td>\n    <td>{planet.NAME}</td>\n    <td>{planet.GALAXY}</td>\n    <td>{planet.SYSTEM}</td>\n    <td>{planet.PLANET}</td>\n    <td>{planet.PLANET_TYPE_PRINT}</td>\n    <!-- IF PARENT_COLUMN -->\n      <td class=\"c_r\">{planet.PARENT_ID}</td>\n      <td>{planet.PARENT_NAME}</td>\n    <!-- ENDIF -->\n    <td class=\"c_r\">{planet.OWNER_ID}</td>\n    <td>{planet.OWNER}</td>\n  </tr>\n  <!-- END planet -->\n</table>\n"
  },
  {
    "path": "design/templates/OpenGame/admin/admin_ally_all.tpl.html",
    "content": "<style type=\"text/css\">\n  .admin_alliances {\n    cursor: pointer;\n  }\n</style>\n\n<h1>{PAGE_HEADER}</h1>\n\n<!-- DEFINE $COLSPAN = '14' -->\n<table id=\"js_admin_alliance\" class=\"admin_alliances\">\n  <tr class=\"c_c\">\n    <th>ID</th>\n    <th>TAG</th>\n    <th>NAME</th>\n    <th>CREATED</th>\n    <th>OWNER</th>\n    <th>LOGO</th>\n    <th>REQ_LOCK</th>\n    <th>OWNER_RANK_NAME</th>\n    <th>MEMBERS</th>\n    <th>POSITION</th>\n    <th>POINTS</th>\n  </tr>\n\n  <!-- BEGIN ally -->\n  <!-- IF ally.OWNER_ID -->\n  <!-- DEFINE $ALLY_CLASS = '' -->\n  <!-- ELSE -->\n  <!-- DEFINE $ALLY_CLASS = 'notice' -->\n  <!-- ENDIF -->\n\n\n  <tr data-ally-id=\"{ally.ID}\" class=\"highlight {$ALLY_CLASS}\">\n    <td class=\"c_r\">{ally.ID}</td>\n    <td class=\"c_l\">{ally.TAG}</td>\n    <td class=\"c_l\">{ally.NAME}</td>\n    <td>{ally.CREATED_TEXT}</td>\n    <td class=\"c_l\">\n      <!-- IF ally.OWNER_NAME_SAFE -->\n      [{ally.OWNER_ID}] {ally.OWNER_NAME_SAFE}\n      <!-- ELSE -->\n      { N/A }\n      <!-- ENDIF -->\n    </td>\n    <td>{ally.HAVE_LOGO}</td>\n    <td>{ally.REQUESTS_NOT_ALLOWED}</td>\n    <td>{ally.OWNER_RANK_NAME}</td>\n    <td>{ally.MEMBER_COUNT}</td>\n    <td class=\"c_r\">{ally.STAT_POSITION}</td>\n    <td class=\"c_r\">{ally.STAT_POINTS_TEXT}</td>\n  </tr>\n  <!-- BEGINELSE ally -->\n  <tr>\n    <td colspan=\"{$COLSPAN}\">{ НЕТ АЛЬЯНСОВ }</td>\n  </tr>\n  <!-- END ally -->\n\n</table>\n\n<script type=\"text/javascript\">\n  $('#js_admin_alliance').on('click', 'td', function (e) {\n    sn_redirect(\"index.php?page=admin/admin_ally&ally_id=\" + $(this).parent().data('ally-id'));\n  });\n</script>\n"
  },
  {
    "path": "design/templates/OpenGame/admin/admin_ally_one.tpl.html",
    "content": "<script type=\"text/javascript\">\n  var ADMIN_ALLY_ID = Math.intVal('{ID}');\n</script>\n\n<style type=\"text/css\">\n  td[data-member_id]\n  {\n    cursor: pointer;\n  }\n</style>\n\n<h1>{PAGE_HEADER}</h1>\n\n<table class=\"c_l\">\n  <tr>\n    <td>CREATED</td>\n    <td>{CREATED_TEXT} ({CREATED})</td>\n  </tr>\n  <tr>\n    <td>OWNER</td>\n    <td>[{OWNER_ID}] {OWNER_NAME_SAFE}</td>\n  </tr>\n  <tr>\n    <td>DESCRIPTION</td>\n    <td class=\"c_c\">{DESCRIPTION_HTML}</td>\n  </tr>\n  <tr>\n    <td>WEB_PAGE</td>\n    <td>{WEB_PAGE}</td>\n  </tr>\n  <tr>\n    <td>TEXT_INTERNAL</td>\n    <td class=\"c_c\">{TEXT_INTERNAL_HTML}</td>\n  </tr>\n  <tr>\n    <td>HAVE_LOGO</td>\n    <td>{HAVE_LOGO}</td>\n  </tr>\n  <tr>\n    <td>REQUEST_TEMPLATE</td>\n    <td>{REQUEST_TEMPLATE}</td>\n  </tr>\n  <tr>\n    <td>REQUESTS_NOT_ALLOWED</td>\n    <td>{REQUESTS_NOT_ALLOWED}</td>\n  </tr>\n  <tr>\n    <td>OWNER_RANK_NAME</td>\n    <td>{OWNER_RANK_NAME}</td>\n  </tr>\n  <tr>\n    <td>MEMBER_COUNT</td>\n    <td>{MEMBER_COUNT}</td>\n  </tr>\n  <tr>\n    <td>STAT_POSITION</td>\n    <td>{STAT_POSITION}</td>\n  </tr>\n  <tr>\n    <td>STAT_POINTS</td>\n    <td>{STAT_POINTS_TEXT}</td>\n  </tr>\n  <tr>\n    <td>TITLE_LIST_PARSED</td>\n    <td class=\"c_l\">\n      <!-- IF .title -->\n      <table class=\"border_image_small\" align=\"left\">\n        <!-- BEGIN title -->\n        <tr>\n          <td class=\"c_r\">{title.INDEX}</td>\n          <td>{title.NAME_SAFE}</td>\n          <td>{title.RIGHTS_TEXT}</td>\n        </tr>\n        <!-- END title -->\n      </table>\n      <!-- ENDIF -->\n    </td>\n  </tr>\n  <tr>\n    <td>PARENT_PLAYER_ID</td>\n    <td>{PARENT_PLAYER_ID}</td>\n  </tr>\n</table>\n\n<table id=\"js_admin_alliance_members\" class=\"c_l\">\n  <tr class=\"c_c\">\n    <th>OWNER</th>\n    <th>PLAYER</th>\n    <th>TITLE</th>\n    <th>RIGHTS</th>\n    <th>ONLINE</th>\n    <th>VACATION</th>\n    <th>BAN</th>\n  </tr>\n\n  <!-- BEGIN members -->\n\n  <!-- IF ! members.OWNER -->\n    <!-- DEFINE $PLAYER_CLASS = '' -->\n  <!-- ELSEIF SN_TIME_NOW - members.ONLINE > ALLIANCE_HEAD_INACTIVE_TIMEOUT -->\n    <!-- DEFINE $PLAYER_CLASS = 'error' -->\n  <!-- ELSE -->\n    <!-- DEFINE $PLAYER_CLASS = 'ok' -->\n  <!-- ENDIF -->\n  <tr class=\"{$PLAYER_CLASS}\">\n    <td data-member_id=\"{members.PLAYER_ID}\" data-is_owner=\"{members.OWNER}\">\n      <!-- IF members.OWNER -->\n      { ВЛАДЕЛЕЦ }\n      <!-- ELSE -->\n      { ПЕРЕДАТЬ }\n      <!-- ENDIF -->\n    </td>\n    <td>[{members.PLAYER_ID}] {members.PLAYER_NAME}</td>\n    <td>[{members.TITLE_ID}] {members.TITLE}</td>\n    <td>{members.RIGHTS}</td>\n    <td class=\"c_c\">{members.ONLINE_SQL}</td>\n    <td class=\"c_c\">\n      <!-- IF members.VACATION -->\n      {members.VACATION_SQL}\n      <!-- ELSE -->\n      -\n      <!-- ENDIF -->\n    </td>\n    <td class=\"c_c\">\n      <!-- IF members.BAN -->\n      {members.BAN_SQL}\n      <!-- ELSE -->\n      -\n      <!-- ENDIF -->\n    </td>\n  </tr>\n  <!-- END members -->\n\n</table>\n\n<script type=\"text/javascript\">\n  $('#js_admin_alliance_members').on('click', 'td[data-member_id]', function (e) {\n    if (!Math.intVal($(this).data('is_owner'))) {\n      sn_redirect(\"index.php?page=admin/admin_ally&ally_id=\" + ADMIN_ALLY_ID + \"&action=pass&new_owner_id=\" + $(this).data('member_id'));\n    }\n  });\n</script>\n"
  },
  {
    "path": "design/templates/OpenGame/admin/admin_analyze_matter.tpl.html",
    "content": "<h2>{Данные с} {MIN_DATE}</h2>\n\n<table class=\"c_r\">\n  <tr class=\"c_c\">\n    <th rowspan=\"2\">{Статья}</th>\n    <th colspan=\"2\">{ВСЕГО}</th>\n    <th colspan=\"2\">{ТМ}</th>\n    <th colspan=\"2\">{ММ}</th>\n  </tr>\n  <tr class=\"c_c\">\n    <th>{Сумма}</th>\n    <th>{Транзакции}</th>\n    <th>{Сумма}</th>\n    <th>{Транзакции}</th>\n    <th>{Сумма}</th>\n    <th>{Транзакции}</th>\n  </tr>\n  <!-- BEGIN spent -->\n  <tr>\n    <td class=\"c_l\">{spent.CONSTANT} [{spent.REASON}]</td>\n    <td>{spent.TOTAL_AMOUNT_TEXT}</td>\n    <td>{spent.TOTAL_COUNT}</td>\n    <td>{spent.DM_AMOUNT_TEXT}</td>\n    <td>{spent.DM_COUNT}</td>\n    <td>{spent.MM_AMOUNT_TEXT}</td>\n    <td>{spent.MM_COUNT}</td>\n  </tr>\n  <!-- END spent -->\n</table>\n"
  },
  {
    "path": "design/templates/OpenGame/admin/admin_ban.tpl.html",
    "content": "<h2>{L_adm_ban_title}</h2>\n\n<form action=\"\" method=\"post\">\n  <input type=\"hidden\" name=\"mode\" value=\"banit\">\n  <input type=\"hidden\" name=\"action\" value=\"banit\">\n  <table width=\"409\">\n    <tr><td class=\"c\" colspan=\"2\">{L_adm_bn_plto}</td></tr>\n    <tr>\n      <th width=\"129\">{L_adm_bn_name}</th>\n      <th width=\"268\"><input name=\"name\" type=\"text\" size=\"25\" value=\"{name}\" /></th>\n    </tr>\n    <tr>\n      <th>{L_adm_bn_reas}</th>\n      <th><input name=\"why\" type=\"text\" value=\"\" size=\"25\" maxlength=\"50\"></th>\n    </tr>\n    <tr>\n      <th>{L_adm_bn_isvc}</th>\n      <th><input name=\"isVacation\" type=\"checkbox\" checked></th>\n    </tr>\n    <tr><td class=\"c\" colspan=\"2\">{L_adm_bn_time}</td></tr>\n    <tr>\n      <th>{L_adm_bn_days}</th>\n      <th><input name=\"days\" type=\"text\" value=\"3\" size=\"5\" /></th>\n    </tr>\n    <tr>\n      <th>{L_adm_bn_hour}</th>\n      <th><input name=\"hour\" type=\"text\" value=\"0\" size=\"5\" /></th>\n    </tr>\n    <tr>\n      <th>{L_adm_bn_mins}</th>\n      <th><input name=\"mins\" type=\"text\" value=\"0\" size=\"5\" /></th>\n    </tr>\n    <tr>\n      <th>{L_adm_bn_secs}</th>\n      <th><input name=\"secs\" type=\"text\" value=\"0\" size=\"5\" /></th>\n    </tr>\n    <tr><th colspan=\"2\"><input type=\"submit\" value=\"{L_adm_bn_bnbt}\" /></th></tr>\n  </table>\n</form>\n\n<h2>{L_adm_unbn_ttle}</h2>\n\n<form action=\"admin/banned.php\" method=\"post\">\n  <input type=\"hidden\" name=\"mode\" value=\"unbanit\">\n  <input type=\"hidden\" name=\"action\" value=\"unbanit\">\n  <table width=\"409\" style=\"color:#FFFFFF\"><tbody>\n    <tr>\n      <td class=\"c\" colspan=\"2\">{L_adm_unbn_plto}</td>\n    </tr>\n    <tr>\n      <th width=\"129\">{L_adm_unbn_name}</th>\n      <th width=\"268\"><input name=\"name\" maxlength=\"80\" size=\"25\" value=\"{name}\" type=\"text\"></th>\n    </tr>\n    <tr>\n      <th colspan=\"2\"><input value=\"{L_adm_unbn_bnbt}\" type=\"submit\"></th>\n    </tr>\n  </tbody></table>\n</form>\n"
  },
  {
    "path": "design/templates/OpenGame/admin/admin_chat.tpl.html",
    "content": "<h1>{PAGE_HEADER}</h1>\n\n<table width=\"100%\">\n  <tr class=\"c_c\">\n    <th>{L_adm_ch_idmsg}</th>\n    <th>{L_adm_ch_time}</th>\n    <th>{L_adm_ch_play}</th>\n    <th>{L_adm_ch_chat}</th>\n    <th width=\"16px\"><img src=\"design/images/r1.png\"></th>\n  </tr>\n\n  <!-- BEGIN message -->\n  <tr class=\"c_l\">\n    <td>{message.ID}</td>\n    <td>{message.TIMESTAMP}</td>\n    <td>{message.PLAYER_NAME}</td>\n    <td width=\"100%\">{message.MESSAGE}</td>\n    <td><a href=\"admin/admin_chat.php?delete={message.ID}\"><img src=\"design/images/r1.png\"></a>\n    </td>\n  </tr>\n  <!-- END message -->\n\n  <tr class=\"c_c\">\n    <th colspan=\"5\">\n      {msg_num}&nbsp;{L_adm_ch_nbs} [<a href=\"admin/admin_chat.php?deleteall=yes\">{L_adm_ch_clear}</a>]\n    </th>\n  </tr>\n</table>\n"
  },
  {
    "path": "design/templates/OpenGame/admin/admin_darkmatter.tpl.html",
    "content": "<h2>{L_adm_dm_title}</h2>\n\n<form action=\"\" method=\"post\">\n  <table width=\"305\">\n    <tbody>\n    <tr>\n      <td class=\"c\" colspan=\"2\">{L_adm_dm_title}</td>\n    </tr>\n    <tr>\n      <th>{L_adm_dm_user}</th>\n      <th>\n        <input name=\"playerId\" type=\"text\" value=\"{PLAYER_ID}\"/><br/>\n        {L_adm_dm_change_hint}\n      </th>\n    </tr>\n    <tr>\n      <th>{L_dark_matter}</th>\n      <th><input name=\"points\" type=\"text\" value=\"{POINTS}\"/></th>\n    </tr>\n    <tr>\n      <th>{L_adm_reason}</th>\n      <th><input name=\"reason\" type=\"text\" value=\"{REASON}\"/></th>\n    </tr>\n    <tr>\n      <th colspan=\"2\"><input type=\"Submit\" value=\"{L_adm_apply}\"/></th>\n    </tr>\n    </tbody>\n  </table>\n</form>\n"
  },
  {
    "path": "design/templates/OpenGame/admin/admin_locale.tpl.html",
    "content": "<script type=\"text/javascript\">\nvar lang_list = Array();\n\nfunction addRow()\n{\n  var stringName = prompt(\"{L_adm_lng_string_name}\", \"\");\n  if(!stringName)\n  {\n    return;\n  }\n\n  if(stringName[0] != '[')\n  {\n    stringName = '[' + stringName + ']';\n  }\n\n  var table = document.getElementById('localeStrings');\n\n  var rowCount = table.rows.length;\n  var row = table.insertRow(rowCount-1);\n\n  var cell = row.insertCell(0);\n  cell.innerHTML = 'X';\n  cell.style.class=\"c_c\";\n  cell.onclick=\"this.parentElement.parentElement.removeChild(this.parentElement);\";\n\n  cell = row.insertCell(1);\n  cell.innerHTML = stringName;\n  cell.style.class=\"c_l\";\n  cell.style.textAlign = \"left\";\n\n  cellId = 2;\n  for(lang_id in lang_list)\n  {\n    var element = document.createElement(\"input\");\n    element.type = \"text\";\n    element.size = \"30\";\n    element.name = \"lang_new\" + stringName + '[' + lang_id + ']';\n\n    cell = row.insertCell(cellId++);\n    cell.appendChild(element);\n  }\n}\n</script>\n\n<h2 class=\"warning\">{L_adm_lng_warning}</h2>\n<h2>{L_adm_lng_title}</h2>\n<form method=\"post\">\n<!-- IF .domain -->\n    {L_adm_lng_domain} <select name=\"domain\">\n      <!-- BEGIN domain -->\n        <option value=\"{domain.NAME}\">{domain.NAME}</option>\n      <!-- END domain -->\n    </select>\n    <input type=\"submit\">\n<!-- ELSEIF .language -->\n  <h3>{L_adm_lng_domain} {DOMAIN}</h3>\n  <input type=\"hidden\" name=\"domain\" value=\"{DOMAIN}\">\n  <table id=\"localeStrings\">\n   <tr>\n     <th class=\"c_c\">X</th>\n     <th class=\"c_l\">{L_adm_lng_string_name}</th>\n     <!-- BEGIN language -->\n       <script type=\"text/javascript\">\n         lang_list['{language.LANG_NAME_ISO2}'] = '{language.LANG_NAME_ISO2}';\n       </script>\n       <th class=\"c_l\">{language.LANG_NAME_NATIVE}</td>\n     <!-- END language -->\n   </tr>\n  <!-- BEGIN string -->\n    <tr>\n      <td class=\"c_c\" onclick=\"this.parentElement.parentElement.removeChild(this.parentElement);\">X</td>\n      <td class=\"c_l\">{string.NAME}</td>\n      <!-- BEGIN locale -->\n        <td><input type=\"text\" size=30 name=\"lang_new{string.NAME}[{locale.LANG}]\" value=\"{locale.VALUE}\"></td>\n      <!-- END local -->\n    </tr>\n  <!-- END string -->\n  <tr>\n    <th class=\"c_c\" colspan=4>\n      <input class=\"fl\" type=\"button\" value=\"{L_adm_lng_string_add}\" onclick=\"addRow();\">\n      <input class=\"fr\" type=\"submit\" value=\"{L_sys_save}\">\n    </th>\n  </tr>\n  </table>\n<!-- ENDIF -->\n</form>\n"
  },
  {
    "path": "design/templates/OpenGame/admin/admin_mining.tpl.html",
    "content": "<h1>{PAGE_NAME}</h1>\n\n<!-- IF ACTIVE_STATUS == 1 -->\n<!-- DEFINE $ACTIVE_SELECTED_1 = 'checked' -->\n<!-- ELSEIF ACTIVE_STATUS == 2 -->\n<!-- DEFINE $ACTIVE_SELECTED_2 = 'checked' -->\n<!-- ELSEIF ACTIVE_STATUS == 0 -->\n<!-- DEFINE $ACTIVE_SELECTED_0 = 'checked' -->\n<!-- ENDIF -->\n\n<script type=\"text/javascript\">\n  $(document).on('click', \"[name='ACTIVE_STATUS'],[name='SORT_BY']\", function () {\n    $(\"#filterForm\").submit();\n  })\n</script>\n\n<form action=\"\" method=\"get\" id=\"filterForm\">\n  <input type=\"hidden\" name=\"page\" value=\"admin/admin_mining\"/>\n\n  <div>\n    { Players activity }\n    <input type=\"radio\" name=\"ACTIVE_STATUS\" id=\"activeAll\" value=\"0\" {$ACTIVE_SELECTED_0}/>\n    <label for=\"activeAll\"> { activeAll } </label>\n    <input type=\"radio\" name=\"ACTIVE_STATUS\" id=\"activeLong\" value=\"1\" {$ACTIVE_SELECTED_1}/>\n    <label for=\"activeLong\"> { activeLong } </label>\n    <input type=\"radio\" name=\"ACTIVE_STATUS\" id=\"activeShort\" value=\"2\" {$ACTIVE_SELECTED_2}/>\n    <label for=\"activeShort\"> { activeShort } </label>\n  </div>\n\n  <div>\n    { Sort by }\n    <!-- BEGIN sorting -->\n\n    <!-- IF sorting.CHECKED -->\n    <!-- DEFINE $CHECKED = 'checked' -->\n    <!-- ELSE -->\n    <!-- DEFINE $CHECKED = '' -->\n    <!-- ENDIF -->\n    <input type=\"radio\" name=\"SORT_BY\" id=\"{sorting.HTML_ID}\" value=\"{sorting.ID}\" {$CHECKED}/>\n    <label for=\"{sorting.HTML_ID}\"> { {sorting.HTML_NAME} } </label>\n\n    <!-- END sorting -->\n  </div>\n\n</form>\n\n<!-- DEFINE $PAGER_COL_SPAN = '9' -->\n<table>\n  <!-- IF .production && PAGER_MESSAGES -->\n  <tr class=\"c_c\">\n    <td class=\"subheader\" colspan=\"{$PAGER_COL_SPAN}\">\n      {PAGER_MESSAGES}\n    </td>\n  </tr>\n  <!-- ENDIF -->\n\n  <tr class=\"c_l\">\n    <th>'ID'</th>\n    <th>'NAME'</th>\n    <th>'POINTS'</th>\n    <th>'RANK'</th>\n    <th>'TOTAL(metal)'</th>\n    <!--<th>'TOTAL_CALC'</th>-->\n    <th>'METAL'</th>\n    <th>'CRYSTAL'</th>\n    <th>'DEUTERIUM'</th>\n    <th>'PERCENT'</th>\n  </tr>\n\n  <!-- BEGIN production -->\n  <tr class=\"c_r\">\n    <td class=\"c_l\">{production.ID}</td>\n    <td class=\"c_l\">{production.NAME}</td>\n    <td>{production.POINTS|num|format}</td>\n    <td>{production.RANK|num|format}</td>\n    <td>{production.TOTAL|num|format}</td>\n    <!--<td>{production.TOTAL_CALC|num|format}</td>-->\n    <td>{production.METAL|num|format}</td>\n    <td>{production.CRYSTAL|num|format}</td>\n    <td>{production.DEUTERIUM|num|format}</td>\n    <td>{production.PERCENT}</td>\n  </tr>\n  <!-- END production -->\n\n  <!-- IF .production && PAGER_MESSAGES -->\n  <tr class=\"c_c\">\n    <td class=\"subheader\" colspan=\"{$PAGER_COL_SPAN}\">\n      {PAGER_MESSAGES}\n    </td>\n  </tr>\n  <!-- ENDIF -->\n</table>\n"
  },
  {
    "path": "design/templates/OpenGame/admin/admin_modules.tpl.html",
    "content": "<h1>{PAGE_NAME}</h1>\n\n<!-- IF SORT_BY == 1 -->\n<!-- DEFINE $SORT_BY_SELECTED_1 = 'checked' -->\n<!-- ELSEIF SORT_BY == 2 -->\n<!-- DEFINE $SORT_BY_SELECTED_2 = 'checked' -->\n<!-- ELSE -->\n<!-- DEFINE $SORT_BY_SELECTED_0 = 'checked' -->\n<!-- ENDIF -->\n\n<script type=\"text/javascript\">\n  $(document).on('click', \"[name='SORT_BY']\", function () {\n    $(\"#filterForm\").submit();\n  })\n</script>\n\n<form action=\"\" method=\"get\" id=\"filterForm\">\n  <input type=\"hidden\" name=\"page\" value=\"admin/admin_modules\"/>\n\n  <div>\n    <input type=\"radio\" name=\"SORT_BY\" id=\"sortByActive\" value=\"1\" {$SORT_BY_SELECTED_1}/>\n    <label for=\"sortByActive\"> { active } </label>\n    <input type=\"radio\" name=\"SORT_BY\" id=\"sortByName\" value=\"0\" {$SORT_BY_SELECTED_0}/>\n    <label for=\"sortByName\"> { sortByName } </label>\n  </div>\n</form>\n\n\n<table>\n  <tr class=\"c_l\">\n    <th>'PACKAGE'</th>\n    <th>'NAME'</th>\n    <th>'VERSION'</th>\n    <th>'ACTIVE'</th>\n    <th>'INSTALLED'</th>\n  </tr>\n\n  <!-- BEGIN modules -->\n  <!-- IF modules.ACTIVE && modules.INSTALLED -->\n  <!-- DEFINE $CLASS = 'ok' -->\n  <!-- ELSEIF modules.INSTALLED -->\n  <!-- DEFINE $CLASS = 'notice' -->\n  <!-- ELSE -->\n  <!-- DEFINE $CLASS = 'warning' -->\n  <!-- ENDIF -->\n\n  <tr class=\"c_l {$CLASS}\">\n    <td>{modules.PACKAGE}</td>\n    <td>{modules.NAME}</td>\n    <td>{modules.VERSION}</td>\n    <td>\n      <!-- IF modules.ACTIVE -->\n      +\n      <!-- ELSE -->\n      &nbsp;\n      <!-- ENDIF -->\n    </td>\n    <td>\n      <!-- IF modules.INSTALLED -->\n      +\n      <!-- ELSE -->\n      &nbsp;\n      <!-- ENDIF -->\n    </td>\n  </tr>\n  <!-- END modules -->\n\n</table>\n"
  },
  {
    "path": "design/templates/OpenGame/admin/admin_payment.tpl.html",
    "content": "<h2>{PAGE_HEADER}</h2>\n\n<!--<a href=\"index.php?page=admin/admin_payment_stats\" class=\"link\">Статистика</a>-->\n\n<form method=\"GET\" name=\"filter\">\n  <input type=\"hidden\" name=\"page\" value=\"admin/admin_payment\" />\n\n  <!-- IF .flt_stat -->\n  {L_adm_pay_filter_stat_name}\n  <select name=\"flt_stat\" onchange=\"filter.submit();\">\n    <!-- BEGIN flt_stat -->\n    <option value=\"{flt_stat.KEY}\"<!-- IF flt_stat.KEY == FLT_STAT --> selected<!-- ENDIF -->>{flt_stat.VALUE}</option>\n    <!-- END flt_stat -->\n  </select>\n  <!-- ENDIF -->\n\n<!--\n  &lt;!&ndash; IF .flt_currency &ndash;&gt;\n  {L_adm_pay_th_payment_currency}\n  <select name=\"flt_currency\" onchange=\"filter.submit();\">\n    &lt;!&ndash; BEGIN flt_currency &ndash;&gt;\n    <option value=\"{flt_currency.KEY}\"&lt;!&ndash; IF flt_currency.KEY == FLT_CURRENCY &ndash;&gt; selected&lt;!&ndash; ENDIF &ndash;&gt;>{flt_currency.VALUE}</option>\n    &lt;!&ndash; END flt_currency &ndash;&gt;\n  </select>\n  &lt;!&ndash; ENDIF &ndash;&gt;\n-->\n\n\n  <table>\n    <tr>\n      <th class=\"c_c\" colspan=\"3\">\n        {L_adm_pay_th_payer}\n        <!-- IF .payer -->\n        <br />\n        <select name=\"flt_payer\" onchange=\"filter.submit();\">\n          <!-- BEGIN payer -->\n          <option value=\"{payer.KEY}\"<!-- IF payer.KEY == FLT_PAYER --> selected<!-- ENDIF -->>{payer.VALUE}</option>\n          <!-- END payer -->\n        </select>\n        <!-- ENDIF -->\n      </th>\n      <th class=\"c_c\" colspan=\"5\">\n        {L_adm_pay_th_payment}<br />\n        <!-- IF .status -->\n        {L_adm_pay_th_payment_status}\n        <select name=\"flt_status\" onchange=\"filter.submit();\">\n          <!-- BEGIN status -->\n          <option value=\"{status.KEY}\"<!-- IF status.KEY == FLT_STATUS --> selected<!-- ENDIF -->>{status.VALUE}</option>\n          <!-- END status -->\n        </select>\n        <!-- ENDIF -->\n\n        <!-- IF .test -->\n        {L_adm_pay_filter_test[1]}\n        <select name=\"flt_test\" onchange=\"filter.submit();\">\n          <!-- BEGIN test -->\n          <option value=\"{test.KEY}\"<!-- IF test.KEY == FLT_TEST --> selected<!-- ENDIF -->>{test.VALUE}</option>\n          <!-- END test -->\n        </select>\n        <!-- ENDIF -->\n      </th>\n      <th class=\"c_c\" colspan=\"2\">{L_sys_metamatter}</th>\n      <th class=\"c_c\" colspan=\"3\">\n        {L_adm_pay_th_module}\n        <!-- IF .module -->\n        <br />\n        <select name=\"flt_module\" onchange=\"filter.submit();\">\n          <!-- BEGIN module -->\n          <option value=\"{module.KEY}\"<!-- IF module.KEY == FLT_MODULE --> selected<!-- ENDIF -->>{module.VALUE}</option>\n          <!-- END module -->\n        </select>\n        <!-- ENDIF -->\n      </th>\n    </tr>\n\n    <!-- IF .payment && PAGER_PAYMENTS -->\n    <tr class=\"c_c\">\n      <td colspan=\"13\" class=\"subheader\">\n        {PAGER_PAYMENTS}\n      </td>\n    </tr>\n    <!-- ENDIF -->\n\n    <tr>\n      <th class=\"c_c\">{L_adm_pay_th_payer_id}</th>\n      <th class=\"c_c\">{L_adm_pay_th_payer_name}</th>\n      <th class=\"c_c\"><img src=\"design/images/icon_mail.gif\" border=\"0\" /></th>\n      <th class=\"c_c\">{L_adm_pay_th_payment_id}</th>\n      <th class=\"c_c\">{L_adm_pay_th_payment_date}</th>\n      <th class=\"c_c\">{L_adm_pay_th_payment_status}</th>\n      <th class=\"c_c\">{L_adm_pay_th_payment_amount}</th>\n      <th class=\"c_c\">{L_adm_pay_th_payment_currency}</th>\n      <th class=\"c_c\">{L_adm_pay_th_mm_paid}</th>\n      <th class=\"c_c\">{L_adm_pay_th_mm_gained}</th>\n      <th class=\"c_c\">{L_adm_pay_th_module_name}</th>\n      <th class=\"c_c\">{L_adm_pay_th_payment_amount}</th>\n      <th class=\"c_c\">{L_adm_pay_th_payment_currency}</th>\n    </tr>\n\n    <!-- BEGIN payment -->\n    <tr>\n      <td>{payment.PAYMENT_USER_ID}</TD>\n      <td>{payment.PAYMENT_USER_NAME}</TD>\n      <td>\n        <!-- IF payment.PAYMENT_USER_ID -->\n        <a href=\"messages.php?mode=write&id={payment.PAYMENT_USER_ID}\">\n          <img src=\"design/images/icon_mail.gif\" border=\"0\" />\n        </a>\n        <!-- ELSE -->\n        &nbsp;\n        <!-- ENDIF -->\n      </td>\n      <td>{payment.PAYMENT_ID}</td>\n      <td>{payment.PAYMENT_DATE}</TD>\n      <td>\n        <!-- IF payment.PAYMENT_STATUS -->\n        <span class=\"ok\">{L_adm_pay_filter_status[1]}</span>\n        <!-- ELSE -->\n        <span class=\"error\">{L_adm_pay_filter_status[0]}</span>\n        <!-- ENDIF -->\n        <!-- IF payment.PAYMENT_TEST -->\n        <br /><span class=\"warning\">{L_adm_pay_filter_test[1]}</span>\n        <!-- ENDIF -->\n      </td>\n      <td class=\"c_r\">{payment.PAYMENT_AMOUNT}</TD>\n      <td>{payment.PAYMENT_CURRENCY}</TD>\n      <td class=\"c_r\">{payment.PAYMENT_DARK_MATTER_PAID}</TD>\n      <td class=\"c_r\">{payment.PAYMENT_DARK_MATTER_GAINED}</TD>\n      <td>{payment.PAYMENT_MODULE_NAME}</TD>\n      <td class=\"c_r\">{payment.PAYMENT_EXTERNAL_AMOUNT}</TD>\n      <td>{payment.PAYMENT_EXTERNAL_CURRENCY}</TD>\n    </tr>\n    <!-- END payment -->\n\n    <!-- IF .payment && PAGER_PAYMENTS -->\n    <tr class=\"c_c\">\n      <td colspan=\"13\" class=\"subheader\">\n        {PAGER_PAYMENTS}\n      </td>\n    </tr>\n    <!-- ENDIF -->\n\n  </table>\n</form>\n"
  },
  {
    "path": "design/templates/OpenGame/admin/admin_planet_edit.tpl.html",
    "content": "<h2>{L_adm_planet_edit}</h2>\n<form method=\"post\">\n  <input type=\"hidden\" name=\"mode\" value=\"{MODE}\">\n\n  <table width=\"500px\">\n    <tr>\n      <th colspan=\"4\" class=\"c_c\">\n        <div>\n          <div class=\"fl\">{L_adm_planet_id}&nbsp;<!-- IF PLANET_ID --><input type=\"hidden\" name=\"planet_id\" value=\"{PLANET_ID}\">{PLANET_ID} <a href=\"admin/planet_edit.php\">{L_sys_reset}</a><!-- ELSE --><input name=\"planet_id\" value=\"{PLANET_ID}\"><!-- ENDIF --></div>\n          <div class=\"fr\"><input type=\"submit\" name=\"change_data\" value=\"{L_sys_confirm}\"></div>\n        </div>\n      </th>\n    </tr>\n\n    <!-- IF PLANET_ID -->\n    <tr>\n      <td colspan=\"4\" class=\"c_c\">\n        <div>\n          <span class=\"fl\">{PLANET_NAME}</span>\n\n          <span class=\"fr\">\n            <!-- BEGIN page_menu -->\n            <a href=\"admin/planet_edit.php?planet_id={PLANET_ID}&mode={page_menu.ID}\"><span<!-- IF MODE == page_menu.ID --> class=\"positive\"<!-- ENDIF -->>{page_menu.TEXT}</span></a>\n            <!-- END page_menu -->\n          </span>\n        </div>\n      </td>\n    </tr>\n    <!-- ENDIF -->\n\n    <!-- IF .unit -->\n    <tr>\n      <th class=\"c_c\">{L_sys_id}</th>\n      <th class=\"c_c\">{L_adm_name}</th>\n      <th class=\"c_c\">{L_sys_on_planet}</th>\n      <th class=\"c_c\">{L_adm_planet_change}</th>\n    </tr>\n    <!-- ENDIF -->\n\n    <!-- BEGIN unit -->\n      <tr>\n        <!-- IF MODE == '__admin_planet_edit_extra' -->\n        <td class=\"c_l\" colspan=\"2\">{unit.NAME}</td>\n        <td class=\"c_l\">{unit.TEXT}</td>\n        <!-- ELSE -->\n        <td class=\"c_r\">{unit.ID}</td>\n        <td class=\"c_l\">{unit.NAME}</td>\n        <td class=\"c_r\">{unit.TEXT}</td>\n        <!-- ENDIF -->\n        <td width=\"160px\">\n          <!-- IF unit.ID == \"PLANET_GOVERNOR_ID\" -->\n          <select name=\"unit_list[{unit.ID}]\">\n            <!-- BEGIN !governor -->\n            <option value=\"{governor.ID}\"<!-- IF unit.VALUE == governor.ID --> selected<!-- ENDIF -->>{governor.NAME}</option>\n            <!-- END !governor -->\n          </select>\n          <!-- ELSE -->\n          <input name=\"unit_list[{unit.ID}]\" value=\"{unit.VALUE}\">\n          <!-- ENDIF -->\n        </td>\n      </tr>\n    <!-- END unit -->\n  </table>\n</form>\n\n<!-- INCLUDE page_hint -->\n"
  },
  {
    "path": "design/templates/OpenGame/admin/admin_ptl_test.tpl.html",
    "content": "<h1>Test is <span id=\"ptl_global_status\"></span></h1>\n\n<table id=\"ptl_test_table\">\n  <tr class=\"c_c\">\n    <th>{Конструкция}</th>\n    <th>{Ожидание}</th>\n    <th>{Результат}</th>\n    <th>{OK?}</th>\n    <th>{Комментарий}</th>\n  </tr>\n  <tr>\n    <td>Testing JS code</td>\n    <td class=\"ptl_etalon\">SAMPLE</td>\n    <td class=\"ptl_result\">RESULT</td>\n    <td class=\"ptl_status\"></td>\n    <th>Self-test - always should FAIL</th>\n  </tr>\n\n  <tr>\n    <th colspan=\"5\" class=\"c_c\">\n      Self-test - does &#123;R_xxx&#125; directives working. IF ANY OF THIS FAIL - OTHER TEST IS WRONG\n    </th>\n  </tr>\n\n<!--\n  <tr>\n    <td>&#123;VAR&#125;</td>\n    <td class=\"ptl_etalon\">VALUE</td>\n    <td class=\"ptl_result\">{VAR}</td>\n    <td class=\"ptl_status\"></td>\n    <th>Root variable VAR for testing</th>\n  </tr>\n  <tr>\n    <td>&#123;RENDER_VAR&#125;</td>\n    <td class=\"ptl_etalon\">&#123;VAR&#125;</td>\n    <td class=\"ptl_result\">{RENDER_VAR}</td>\n    <td class=\"ptl_status\"></td>\n    <th>Renderer for VAR</th>\n  </tr>\n-->\n\n  <tr>\n    <td>&#123;R_[RENDER_VAR]&#125;</td>\n    <td class=\"ptl_etalon\">VALUE</td>\n    <td class=\"ptl_result\">{R_[RENDER_VAR]}</td>\n    <td class=\"ptl_status\"></td>\n    <th>Rendering VAR</th>\n  </tr>\n\n  <!-- DEFINE $VAR = '$VALUE' -->\n<!--\n  <tr>\n    <td>&#123;$VAR&#125;</td>\n    <td class=\"ptl_etalon\">$VALUE</td>\n    <td class=\"ptl_result\">{$VAR}</td>\n    <td class=\"ptl_status\"></td>\n    <th>Defined variable $VAR for testing</th>\n  </tr>\n  <tr>\n    <td>&#123;RENDER_DEFINED_VAR&#125;</td>\n    <td class=\"ptl_etalon\">&#123;$VAR&#125;</td>\n    <td class=\"ptl_result\">{RENDER_DEFINED_VAR}</td>\n    <td class=\"ptl_status\"></td>\n    <th>Renderer for DEFINE-d $VAR</th>\n  </tr>\n-->\n  <tr>\n    <td>&#123;R_[RENDER_DEFINED_VAR]&#125;</td>\n    <td class=\"ptl_etalon\">$VALUE</td>\n    <td class=\"ptl_result\">{R_[RENDER_DEFINED_VAR]}</td>\n    <td class=\"ptl_status\"></td>\n    <th>Rendering DEFINE-d $VAR</th>\n  </tr>\n\n  <!-- BEGIN render_test_block -->\n<!--\n  <tr>\n    <td>&#123;render_test_block.BLOCK_VAR&#125;</td>\n    <td class=\"ptl_etalon\">&#123;VAR&#125;</td>\n    <td class=\"ptl_result\">{render_test_block.BLOCK_VAR}</td>\n    <td class=\"ptl_status\"></td>\n    <th>What we will render now</th>\n  </tr>\n-->\n  <tr>\n    <td>&#123;R_[render_test_block.BLOCK_VAR]&#125;</td>\n    <td class=\"ptl_etalon\">VALUE</td>\n    <td class=\"ptl_result\">{R_[render_test_block.BLOCK_VAR]}</td>\n    <td class=\"ptl_status\"></td>\n    <th>Rendering result for block var \"{render_test_block.BLOCK_VAR}\"</th>\n  </tr>\n  <!-- END render_test_block -->\n\n  <!-- DEFINE $BLACK = 'black_moon' -->\n  <!-- BEGIN test -->\n  <!-- IF test.HEADER -->\n  <tr>\n    <th colspan=\"5\" class=\"c_c\">\n      {test.HEADER}\n    </th>\n  </tr>\n  <!-- ELSE -->\n  <tr>\n    <td>{test.CONSTRUCTION}</td>\n    <td class=\"ptl_etalon\">{test.EXPECTED}</td>\n    <td class=\"ptl_result\">{R_[test.SAMPLE]}</td>\n    <td class=\"ptl_status\"></td>\n    <th>{test.DESCRIPTION}</th>\n  </tr>\n  <!-- ENDIF -->\n  <!-- END test -->\n\n  <tr>\n    <th colspan=\"5\" class=\"c_c\">\n      Decorators\n    </th>\n  </tr>\n  <!-- INCLUDE admin/tests/_number_color_value -->\n\n</table>\n\n\n<script type=\"text/javascript\">\n  var ptlFailCount = 0;\n\n  function ptlTestResult(element, isOk) {\n    element.text((isOk ? \"\" : \"NOT \") + 'PASSED').css('color', isOk ? 'lightgreen' : 'red');\n  }\n\n  $(\".ptl_etalon\").each(function () {\n    var parentCell = $(this).parent();\n    var isOk = parentCell.find(\".ptl_etalon\").html() === parentCell.find(\".ptl_result\").html();\n    !isOk ? ptlFailCount++ : false;\n    ptlTestResult(parentCell.find(\".ptl_status\"), isOk);\n  });\n\n  ptlTestResult($(\"#ptl_global_status\"), ptlFailCount == 1);\n</script>\n"
  },
  {
    "path": "design/templates/OpenGame/admin/admin_tools.tpl.html",
    "content": "<h1>{PAGE_HEADER}</h1>\n\n<div style=\"display: inline-block;\">\n  <div>\n    <a href=\"admin/tools.php?mode={D_ADM_TOOL_INFO_PHP}\" class=\"button_pseudo w100\">phpinfo()</a>\n  </div>\n  <div>\n    <a href=\"admin/tools.php?mode={D_ADM_TOOL_INFO_SQL}\" class=\"button_pseudo w100\">{L_adm_tool_sql_page_header}</a>\n  </div>\n  <div>\n    <a href=\"admin/tools.php?mode={D_ADM_TOOL_CONFIG_RELOAD}\" class=\"button_pseudo w100\">{L_adm_tools_reloadConfig}</a>\n  </div>\n  <div>\n    <a href=\"admin/tools.php?mode={D_ADM_TOOL_MD5}\" class=\"button_pseudo w100\">{L_adm_tool_md5_header}</a>\n  </div>\n  <div>\n    <a href=\"admin/tools.php?mode={D_ADM_TOOL_FORCE_LAST}\" class=\"button_pseudo w100\">{L_adm_update_repeat}</a>\n  </div>\n  <div>\n    <a href=\"admin/tools.php?mode={D_ADM_TOOL_FORCE_ALL}\" class=\"button_pseudo w100\">{L_adm_update_force}</a>\n  </div>\n  <div>\n    <a href=\"admin/tools.php?mode={D_ADM_PTL_TEST}\" class=\"button_pseudo w100\">{L_adm_ptl_test}</a>\n  </div>\n  <div>\n    <a href=\"admin/tools.php?mode={D_ADM_COUNTER_RECALC}\" class=\"button_pseudo w100\">{L_adm_counter_recalc}</a>\n  </div>\n</div>"
  },
  {
    "path": "design/templates/OpenGame/admin/admin_user.tpl.html",
    "content": "<h2>{PAGE_HEADER}</h2>\n\n<h3><a href=\"admin/admin_user_activity.php?id={USER_ID}\" class=\"link\">Посмотреть активность игрока</a></h3>\n\n<script type=\"text/javascript\">\n  $(document).ready(function () {\n    $(\".tabs\").tabs();\n    $(\"#tab_container\").show();\n\n    // var index = $('.tabs a[href=\"#tab_planet\"]').parent().index();\n    // $(\".tabs\").tabs(\"option\", \"active\", index);\n    // $(\".tabs\").tabs(\"option\", \"active\", 1);\n  });\n</script>\n\n<form action=\"\" method=\"get\">\n  <input type=\"hidden\" name=\"page\" value=\"admin/user_view\"/>\n  <input type=\"hidden\" name=\"uid\" value=\"{USER_ID}\"/>\n\n  <div id=\"tab_container\" style=\"font-size: 1em; width: 50em; display: none;\" class=\"tabs\">\n    <ul>\n      <li><a href=\"#tab_user\">{L_adm_player}</a></li>\n      <li><a href=\"#tab_planet\">{L_adm_planets}</a></li>\n    </ul>\n\n    <div id=\"tab_user\">\n      <table class=\"c_l no_border_image\">\n        <!-- BEGIN block -->\n        <tr>\n          <th class=\"c_l\" colspan=\"2\">\n            {block.TITLE}\n          </th>\n        </tr>\n\n        <!-- BEGIN field -->\n        <tr>\n          <td>{field.NAME}</td>\n          <td>\n            {field.VALUE}\n\n            <!-- IF field.TYPE == 'password' -->\n            <input type=\"text\" name=\"{field.NAME}\"/>\n            <input type=\"submit\" name=\"{field.NAME}_change\" value=\"{ Изменить }\"/>\n            <!-- ELSE -->\n            {field.TYPE}\n            <!-- ENDIF -->\n          </td>\n        </tr>\n        <!-- END field -->\n\n        <!-- END block -->\n      </table>\n    </div>\n\n    <div id=\"tab_planet\">\n      <table class=\"no_border_image\">\n        <tr>\n          <th class=\"c_c\">\n            {L_adm_planets}\n          </th>\n        </tr>\n        <!-- BEGIN planet -->\n        <tr>\n          <td class=\"c_l\">\n            <a href=\"admin/planet_edit.php?planet_id={planet.ID}\" class=\"link\">{planet.NAME_RENDERED}</a>\n          </td>\n        </tr>\n        <!-- END planet -->\n      </table>\n    </div>\n\n\n  </div>\n\n</form>\n"
  },
  {
    "path": "design/templates/OpenGame/admin/admin_user_activity.tpl.html",
    "content": "<!--suppress HtmlUnknownAttribute -->\n<style type=\"text/css\">\n  .activity\n  {\n    display: inline-flex;\n    flex-wrap: wrap;\n    justify-content: flex-start;\n    align-items: center;\n    text-align: left;\n  }\n\n  .activity.header\n  {\n    font-size: 150%;\n  }\n\n  .activity .day\n  {\n    display: flex;\n    justify-content: flex-start;\n    align-items: center;\n    width: 34em;\n  }\n\n  .activity .day > div > div\n  {\n    padding: 0;\n    border: 0;\n    margin: 0;\n    width: 100%;\n    background-color: red\n  }\n\n  /*noinspection CssUnusedSymbol*/\n  .activity .weekend\n  {\n    color: red;\n  }\n\n  .activity .none, .activity .present\n  {\n    display: inline-block;\n    height: 1.5em;\n    width: 1em;\n    background-color: black;\n    border: 1px solid white;\n  }\n\n  .activity .present\n  {\n    background-color: white;\n    border-color: black;\n  }\n</style>\n\n<h1>Активность игрока [{USER_ID}] {USER_NAME}</h1>\n<!-- IF DATE_TO --><h2>с {DATE_TO} по {DATE_FROM}</h2><!-- ENDIF -->\n\n<div class=\"activity header\">\n  <div class=\"none\"></div>&nbsp;- Нет активности&nbsp;\n  <div class=\"present\"></div>&nbsp;- Есть активность (красный - % активности периода)\n</div><br/>\n\n<h3>1 ячейка - 1 час. Навестись мышкой на ячейку - время и длительность (если есть активность)</h3>\n\n{ Records found: } {RECORDS}<br/><br/>\n\n<!-- DEFINE $DAY_OPENED = 0 -->\n<!-- DEFINE $FIRST_DAY = ' style=\"justify-content: flex-end;\"' -->\n\n<div class=\"activity\">\n  <!-- BEGIN hourly -->\n\n  <!-- IF hourly.OPEN_DAY -->\n  <div class=\"day\" {$FIRST_DAY}>\n    <span class='{hourly.DAY_CLASS}'>{hourly.DATE}</span>\n    <!-- DEFINE $FIRST_DAY = '' -->\n    <!-- DEFINE $DAY_OPENED = 1 -->\n    <!-- ENDIF -->\n\n    <!-- IF hourly.MINUTES -->\n    <!-- DEFINE $MINUTES = ' - {hourly.MINUTES}m' -->\n    <!-- ELSE -->\n    <!-- DEFINE $MINUTES = '' -->\n    <!-- ENDIF -->\n\n    <div title=\"{hourly.TIME}{$MINUTES}\" class=\"{hourly.TIME_CLASS}\">\n      <!-- IF hourly.MINUTES -->\n      <div style=\"height: {hourly.LENGTH_PERCENT}%;\">\n\n      </div>\n      <!-- ENDIF -->\n    </div>\n\n    <!-- IF hourly.CLOSE_DAY -->\n  </div>\n  <!-- DEFINE $DAY_OPENED = 0 -->\n  <!-- ENDIF -->\n\n  <!-- BEGINELSE hourly -->\n  <h2>{ Нет активности у указанного игрока }</h2>\n  <!-- END hourly -->\n</div>\n"
  },
  {
    "path": "design/templates/OpenGame/admin/md5enc.tpl.html",
    "content": "<h2>{L_adm_tool_md5_header}</h2>\n<form method=\"post\">\n  <table>\n    <tr>\n      <td class=\"c_l\">{L_sys_password_seed}</td>\n      <td class=\"c_l\">\n        <input type=\"text\" name=\"seed\" value=\"{SEED}\" size=35 />\n      </td>\n    </tr>\n    <tr>\n      <td class=\"c_l\">{L_sys_password_length}</td>\n      <td class=\"c_l\">\n        <input type=\"text\" name=\"length\" value=\"{LENGTH}\">\n        <input type=\"submit\" name=\"ok\" value=\"{L_adm_tool_md5_generate}\" onclick=\"jQuery('#string').val('');\" />\n      </td>\n    </tr>\n    <tr>\n      <td class=\"c_l\">{L_sys_password}</td>\n      <td class=\"c_l\">\n        <input type=\"text\" name=\"string\" id=\"string\" value=\"{STRING}\">\n        <input type=\"submit\" name=\"ok\" value=\"{L_adm_tool_md5_encode}\" />\n      </td>\n    </tr>\n\n    <tr>\n      <td class=\"c_l\">{L_adm_tool_md5_hash}</td>\n      <td><input type=\"text\" name=\"md5w\" value=\"{MD5}\" size=\"32\"></td>\n    </tr>\n  </table>\n</form>"
  },
  {
    "path": "design/templates/OpenGame/admin/planet_compensate.tpl.html",
    "content": "<h1>{L_adm_pl_comp_title}</h1>\n<!-- IF .error -->\n  <br>\n  <table width=\"519\">\n    <tr>\n      <td class=\"c\">{L_sys_error}</td>\n    </tr>\n    <tr><th class=\"c\">\n      <ul>\n        <!-- BEGIN error -->\n          <li>{error.TEXT}</li>\n        <!-- END error -->\n      </ul>\n    </th></tr>\n  </table>\n  <br>\n<!-- ENDIF -->\n\n<form method=\"get\" name=\"f_compensate\">\n  <table>\n    <tr>\n      <th>{L_sys_player}</th>\n      <th><input type=\"text\" name=\"username\" value=\"{username}\"></th>\n    </tr>\n    <tr>\n      <th>{L_adm_pl_comp_src}</th>\n      <th><input type=\"text\" name=\"galaxy_src\" value=\"{galaxy_src}\" size=\"3\" maxlength=\"3\">:<input type=\"text\" name=\"system_src\" value=\"{system_src}\" size=\"3\" maxlength=\"3\">:<input type=\"text\" name=\"planet_src\" value=\"{planet_src}\" size=\"3\" maxlength=\"3\"></th>\n    </tr>\n    <tr>\n      <th>{L_adm_pl_comp_dst}</th>\n      <th><input type=\"text\" name=\"galaxy_dst\" value=\"{galaxy_dst}\" size=\"3\" maxlength=\"3\">:<input type=\"text\" name=\"system_dst\" value=\"{system_dst}\" size=\"3\" maxlength=\"3\">:<input type=\"text\" name=\"planet_dst\" value=\"{planet_dst}\" size=\"3\" maxlength=\"3\"></th>\n    </tr>\n    <tr>\n      <th>{L_adm_pl_comp_bonus}</th>\n      <th><input type=\"text\" name=\"bonus\" value=\"{bonus}\" value=\"1\"></th>\n    </tr>\n    <tr>\n      <th colspan=2>\n        <input type=\"submit\" name=\"btn_check\" value=\"{L_adm_pl_comp_check}\">\n      </tr>\n    </th>\n  </table>\n\n  <!-- IF CHECK -->\n    <br>\n    <table>\n      <tr>\n        <td class=c>{L_sys_planet} [{galaxy_src}:{system_src}:{planet_src}]</td>\n        <td class=c>{L_sys_metal}</td>\n        <td class=c>{L_sys_crystal}</td>\n        <td class=c>{L_sys_deuterium}</td>\n      </tr>\n      <tr align=right>\n        <th align=left>{L_adm_pl_comp_price}</th>\n        <th>{metal_cost}</th>\n        <th>{crystal_cost}</th>\n        <th>{deuterium_cost}</th>\n      </tr>\n      <tr align=right>\n        <th align=left>{L_adm_pl_comp_got} </th>\n        <th>{metal_bonus}</th>\n        <th>{crystal_bonus}</th>\n        <th>{deuterium_bonus}</th>\n      </tr>\n      <tr>\n        <td colspan=4>\n          {L_sys_planet} <b>[{galaxy_src}:{system_src}:{planet_src}]</b> {L_adm_pl_com_of_plr} <b>{username}</b> \n          <!-- IF CHECK == 1 -->\n            {L_adm_pl_comp_will_be}\n          <!-- ENDIF -->\n          {L_adm_pl_comp_destr}<br>\n  \n          {L_adm_pl_comp_recieve}\n          <!-- IF CHECK == 1 -->\n            {L_adm_pl_comp_will_be}\n          <!-- ENDIF -->\n          {L_adm_pl_comp_recieve2} <b>[{galaxy_dst}:{system_dst}:{planet_dst}]</b>.\n        </td>\n      </tr>\n\n      <tr>\n        <td colspan=4 class=\"c\" align=\"center\">\n          <!-- IF CHECK == 1 -->\n          <input type=\"submit\" name=\"btn_confirm\" value=\"{L_adm_pl_comp_confirm}\">\n          <!-- ELSEIF CHECK == 2 -->\n          <font color=\"lime\">{L_adm_pl_comp_done}</font>\n          <!-- ENDIF -->\n        </td>\n      </tr>\n    </table>\n  <!-- ENDIF -->\n</form>"
  },
  {
    "path": "design/templates/OpenGame/admin/reset_body.tpl.html",
    "content": "<br><br>\n<h2>{adm_rz_ttle}</h2>\n<form action=\"admin/XNovaResetUnivers.php\" method=\"post\">\n<input type=\"hidden\" name=\"mode\" value=\"reset\">\n<table width=\"500\">\n<tbody>\n<tr>\n<td class=\"c\" colspan=\"6\">{adm_rz_conf}</td>\n</tr><tr>\n<th colspan=\"2\">{adm_rz_text}</th>\n</tr><tr>\n<th colspan=\"2\"><input type=\"Submit\" value=\"{adm_rz_doit}\" /></th>\n</tbody>\n</tr>\n</table>\n</form>"
  },
  {
    "path": "design/templates/OpenGame/admin/settings.tpl.html",
    "content": "<h1>{L_adm_opt_title}</h1>\n<!-- IF MESSAGE -->\n{MESSAGE}<br>\n<!-- ENDIF -->\n\n<script type=\"text/javascript\">\n  var SNC_MODE_REGISTER = '{D_SNC_MODE_REGISTER}';\n  var SNC_MODE_VERSION_CHECK = '{D_SNC_MODE_VERSION_CHECK}';\n  var SNC_VER_ERROR_CONNECT = '{D_SNC_VER_ERROR_CONNECT}';\n  var SNC_VER_ERROR_SERVER = '{D_SNC_VER_ERROR_SERVER}';\n\n  var lang = Array();\n  <!-- BEGIN ver_response -->\n  lang[{ver_response.ID}] = '{ver_response.NAME}';\n  <!-- END ver_response -->\n</script>\n\n<script type=\"text/javascript\">\n  jQuery(document).on('click', \"#version_check_button\", function () {\n    // send requests\n    jQuery.post(\"ajax_version_check.php\", {mode: SNC_MODE_VERSION_CHECK, ajax: 1},function (data) {\n      // output result\n      console.log(data);\n      data = !data ? SNC_VER_ERROR_CONNECT : (isNaN(data) || lang[data] == undefined ? SNC_VER_ERROR_SERVER : data);\n      $(\"#version_check_result\").html(lang[data]);\n    });\n  });\n\n  jQuery(document).on('click', \"#server_update_register\", function () {\n    // send requests\n    jQuery.post(\"ajax_version_check.php\", {ajax: 1,mode: SNC_MODE_REGISTER,url: SN_ROOT_VIRTUAL}, function (data) {\n      // output result\n      data = !data ? SNC_VER_ERROR_CONNECT : (isNaN(data) || lang[data] == undefined ? SNC_VER_ERROR_SERVER : data);\n      $(\"#register_result\").html(lang[data]);\n    });\n  });\n</script>\n\n<form action=\"\" method=\"post\">\n  <div id=\"tab_admin_settings\" class=\"tabs\">\n    <ul>\n      <li><a href=\"#tab_admin_status\">{L_admin_tab_status}</a></li>\n      <li><a href=\"#tab_admin_game\">{L_admin_tab_game}</a></li>\n      <li><a href=\"#tab_admin_urls\">{L_admin_tab_urls}</a></li>\n      <li><a href=\"#tab_admin_universe\">{L_admin_tab_universe}</a></li>\n      <li><a href=\"#tab_admin_players\">{L_admin_tab_players}</a></li>\n      <li><a href=\"#tab_admin_UBE\">{L_admin_tab_UBE}</a></li>\n      <li><a href=\"#tab_admin_stats\">{L_admin_tab_stats_and_records}</a></li>\n      <!--<li><a href=\"#tab_admin_planets\">{L_admin_tab_planets}</a></li>-->\n      <li><a href=\"#tab_admin_advertise\">{L_admin_tab_advertise}</a></li>\n    </ul>\n\n    <div id=\"tab_admin_status\">\n      <div class=\"header\">{L_adm_opt_game_status}</div>\n      <div class=\"cell\">\n        <label for=\"game_disable\">{L_adm_game_status}</label>\n        <select name=\"game_disable\" id=\"game_disable\">\n          <!-- BEGIN sys_game_disable_reason -->\n          <!-- IF sys_game_disable_reason.ID == GAME_DISABLE -->\n          <!-- DEFINE $GAME_MODE = ' selected' -->\n          <!-- ELSE -->\n          <!-- DEFINE $GAME_MODE = '' -->\n          <!-- ENDIF -->\n          <option value=\"{sys_game_disable_reason.ID}\" {$GAME_MODE}>{sys_game_disable_reason.NAME}\n            <!-- END sys_game_disable_reason -->\n        </select>\n        <br/>\n        <input name=\"game_disable_reason\" id=\"game_disable_reason\" value=\"{C_game_disable_reason}\" type=\"text\" />\n        <!--\n        <textarea name=\"game_disable_reason\" cols=\"50\" rows=\"2\" size=\"50\" >{C_game_disable_reason}</textarea>\n        -->\n      </div>\n\n      <div class=\"header\">\n        {L_adm_opt_ver_check}\n      </div>\n      <div class=\"cell\">\n        <div class=\"c_l\">\n          {L_adm_opt_ver_check_hint}\n        </div>\n        <div class=\"c_c\">\n          <input type=\"button\" value=\"{LA_adm_opt_ver_check_do}\" id=\"version_check_button\">\n        </div>\n\n        <div class=\"c_c\" id=\"version_check_result\">\n          <!-- IF CHECK_DATE -->{L_adm_opt_ver_check_last}&nbsp;{CHECK_DATE}<!-- ENDIF -->\n          <div class=\"{CHECK_CLASS}\">{CHECK_RESULT}</div>\n        </div>\n      </div>\n      <div class=\"cell\">\n        <input name=\"server_updater_check_auto\" id=\"server_updater_check_auto\" type=\"checkbox\" value=\"1\"\n        <!-- IF SERVER_UPDATE_CHECK_AUTO --> checked<!-- ENDIF -->/>&nbsp;<label for=\"server_updater_check_auto\">{L_adm_opt_ver_check_auto}</label><br/>\n        <div>{L_adm_opt_ver_check_auto_hint}</div>\n      </div>\n\n      <div class=\"header\">\n        {L_adm_upd_register}\n      </div>\n      <div class=\"cell c_l\" id=\"register_result\">\n        <!-- IF SERVER_UPDATE_KEY -->\n          {L_adm_upd_register_already}<br/>\n          {L_adm_upd_register_id}&nbsp;<span class=\"ok\">{C_server_updater_id}</span><br/>\n          {L_adm_upd_register_key}&nbsp;<span class=\"ok\">{C_server_updater_key}</span>\n        <!-- ELSE -->\n          <div class=\"c_c\">\n            {L_adm_opt_ver_response[D_SNC_VER_REGISTER_UNREGISTERED]}<br/>\n            <input type=\"button\" value=\"{LA_adm_upd_register_do}\" id=\"server_update_register\">\n          </div>\n          <div>{L_adm_upd_register_hint}</div>\n        <!-- ENDIF -->\n      </div>\n    </div>\n\n    <div id=\"tab_admin_game\">\n      <div class=\"header\">{L_adm_opt_game_settings}</div>\n      <div class=\"cell contFJ\">\n        <label for=\"game_name\" style=\"flex-shrink: 0;\">{L_adm_opt_game_name}</label>\n        <input id=\"game_name\" name=\"game_name\" value=\"{C_game_name}\" type=\"text\" style=\"width: 100%;\" />\n        <div class=\"cell\" style=\"flex-shrink: 0;\">\n          <label for=\"game_mode\">{L_adm_opt_game_mode}</label>\n          <select id=\"game_mode\" name=\"game_mode\"><!-- BEGIN game_modes -->\n            <!-- IF game_modes.ID == game_mode -->\n            <!-- DEFINE $GAME_MODE = 'selected' -->\n            <!-- ELSE -->\n            <!-- DEFINE $GAME_MODE = '' -->\n            <!-- ENDIF -->\n            <option value=\"{game_modes.ID}\" {$GAME_MODE}>{game_modes.NAME}</option>\n            <!-- END game_modes --></select>\n        </div>\n      </div>\n\n      <!--<div class=\"header\">-->\n        <!--{L_adm_opt_game_oth_info}-->\n      <!--</div>-->\n      <div class=\"cell\">\n        <input name=\"user_vacation_disable\" id=\"user_vacation_disable\" type=\"checkbox\" value=\"1\"<!-- IF USER_VACATION_DISABLE --> checked<!-- ENDIF -->/>\n        <label for=\"user_vacation_disable\">{L_adm_opt_vacation_mode}</label>\n      </div>\n      <div class=\"cell\">\n        <input name=\"allow_buffing\" id=\"allow_buffing\" type=\"checkbox\" value=\"1\" <!-- IF ALLOW_BUFFING -->checked<!-- ENDIF --> />\n        <label for=\"allow_buffing\">{L_adm_opt_allow_buffing}</label>\n      </div>\n      <div class=\"cell\">\n        <input name=\"ally_help_weak\" id=\"ally_help_weak\" type=\"checkbox\" value=\"1\" <!-- IF ALLY_HELP_WEAK -->checked<!-- ENDIF --> />\n        <label for=\"ally_help_weak\">{L_adm_opt_ally_help_weak}</label>\n      </div>\n      <div class=\"cell\">\n        <input name=\"game_email_pm\" id=\"game_email_pm\" type=\"checkbox\" value=\"1\" <!-- IF GAME_EMAIL_PM -->checked<!-- ENDIF --> />\n        <label for=\"game_email_pm\">{L_adm_opt_email_pm}</label>\n      </div>\n      <div class=\"cell\">\n        <input name=\"debug\" id=\"debug\" type=\"checkbox\" value=\"1\"<!-- IF GAME_DEBUG --> checked<!-- ENDIF --> />\n        <label for=\"debug\">{L_adm_opt_game_debugmod}</label>\n      </div>\n      <div class=\"cell\">\n        <input name=\"game_counter\" id=\"game_counter\" type=\"checkbox\" value=\"1\"<!-- IF GAME_COUNTER --> checked<!-- ENDIF --> />\n        <label for=\"game_counter\">{L_adm_opt_game_counter}</label>\n      </div>\n      <div class=\"cell\">\n        <input name=\"tpl_minifier\" id=\"tpl_minifier\" type=\"checkbox\" value=\"1\"<!-- IF TPL_MINIFIER --> checked<!-- ENDIF --> />\n        <label for=\"tpl_minifier\">{L_adm_opt_tpl_minifier}</label><br />\n        <label for=\"tpl_minifier\">{L_adm_opt_tpl_minifier_hint}</label>\n      </div>\n\n      <div class=\"header\">{L_adm_opt_empire_mercenary_temporary}</div>\n      <div class=\"cell\">\n        <input name=\"empire_mercenary_temporary\" id=\"empire_mercenary_temporary\" type=\"checkbox\" value=\"1\"<!-- IF EMPIRE_MERCENARY_TEMPORARY --> checked<!-- ENDIF -->/>\n        <label for=\"empire_mercenary_temporary\">{L_adm_opt_empire_mercenary_temporary}</label><br />\n        <label for=\"empire_mercenary_base_period\">{L_adm_opt_empire_mercenary_temporary_base}</label> <input type=\"text\" id=\"empire_mercenary_base_period\" name=\"empire_mercenary_base_period\" value=\"{C_empire_mercenary_base_period}\"><br />\n        {L_adm_opt_empire_mercenary_temporary_hint}\n      </div>\n\n      <div class=\"header c_l\">\n        {L_adm_opt_chat}\n      </div>\n      <div class=\"cell c_l\">\n        <label for=\"chat_timeout\">{L_adm_opt_chat_timeout}</label>\n        <input id=\"chat_timeout\" name=\"chat_timeout\" maxlength=\"80\" size=\"10\" value=\"{C_chat_timeout}\" type=\"text\"> {L_sys_sec} {L_adm_opt_game_zero_disable}\n      </div>\n\n      <!--<div class=\"header c_l\">-->\n        <!--{L_adm_opt_experimental}-->\n      <!--</div>-->\n    </div>\n\n    <div id=\"tab_admin_universe\">\n      <div class=\"header\">\n        {L_adm_opt_speed} {L_adm_opt_game_speed_normal}\n      </div>\n      <div class=\"cell contFJ\">\n        <div>\n          {L_adm_opt_game_gspeed}\n          <input type=\"text\" name=\"game_speed\" size=\"4\" maxlength=\"9\" value=\"{C_game_speed}\" />\n        </div>\n        <div>\n          {L_adm_opt_game_fspeed}\n          <input type=\"text\" name=\"fleet_speed\" size=\"4\" maxlength=\"9\" value=\"{C_fleet_speed}\" />\n        </div>\n        <div>\n          {L_adm_opt_game_pspeed}\n          <input type=\"text\" name=\"resource_multiplier\" size=\"4\" maxlength=\"9\" value=\"{C_resource_multiplier}\" />&nbsp;\n        </div>\n      </div>\n\n\n      <div class=\"header\">\n        {L_adm_opt_exchange}\n      </div>\n      <div class=\"cell contFJ\">\n        <div>\n          {L_sys_metal} <input name=\"rpg_exchange_metal\" maxlength=\"10\" size=\"2\" value=\"{C_rpg_exchange_metal}\" type=\"text\">\n        </div>\n        <div>\n          :\n        </div>\n        <div>\n          {L_sys_crystal} <input name=\"rpg_exchange_crystal\" maxlength=\"10\" size=\"2\" value=\"{C_rpg_exchange_crystal}\" type=\"text\">\n        </div>\n        <div>\n          :\n        </div>\n        <div>\n          {L_sys_deuterium} <input name=\"rpg_exchange_deuterium\" maxlength=\"10\" size=\"2\" value=\"{C_rpg_exchange_deuterium}\" type=\"text\">\n        </div>\n        <div>\n          :\n        </div>\n        <div>\n          {L_sys_dark_matter_sh} <input name=\"rpg_exchange_darkMatter\" maxlength=\"10\" size=\"8\" value=\"{C_rpg_exchange_darkMatter}\" type=\"text\">\n        </div>\n      </div>\n\n      <div class=\"header\">{L_admin_tab_universe_main}</div>\n      <div class=\"cell contFS\">\n        <div>{L_adm_opt_universe_size}</div>\n        <div>\n          <input id=\"game_maxGalaxy\" name=\"game_maxGalaxy\" maxlength=\"5\" size=\"5\" value=\"{C_game_maxGalaxy}\" type=\"text\">\n          <label for=\"game_maxGalaxy\">{L_adm_opt_galaxies}</label>\n        </div>\n        <div>\n          <input id=\"game_maxSystem\" name=\"game_maxSystem\" maxlength=\"5\" size=\"5\" value=\"{C_game_maxSystem}\" type=\"text\">\n          <label for=\"game_maxSystem\">{L_adm_opt_systems}</label>\n        </div>\n        <div>\n          <input id=\"game_maxPlanet\" name=\"game_maxPlanet\" maxlength=\"5\" size=\"5\" value=\"{C_game_maxPlanet}\" type=\"text\">&nbsp;\n          <label for=\"game_maxPlanet\">{L_adm_opt_planets}</label>\n        </div>\n      </div>\n      <div class=\"cell contFJ\">\n        <label for=\"uni_price_galaxy\">{L_adm_uni_price_galaxy}</label>\n        <input id=\"uni_price_galaxy\" name=\"uni_price_galaxy\" maxlength=\"20\" size=\"20\" value=\"{C_uni_price_galaxy}\" type=\"text\"/>\n      </div>\n      <div class=\"cell contFJ\">\n        <label for=\"uni_price_system\">{L_adm_uni_price_system}</label>\n        <input id=\"uni_price_system\" name=\"uni_price_system\" maxlength=\"20\" size=\"20\" value=\"{C_uni_price_system}\" type=\"text\"/>\n      </div>\n\n    <!--</div>-->\n\n    <!--<div id=\"tab_admin_planets\">-->\n      <div class=\"header c_l\">\n        {L_adm_opt_plan_settings}\n      </div>\n\n      <div class=\"cell c_l\">\n        <label for=\"initial_fields\">{L_adm_opt_plan_initial}</label>\n        <input id=\"initial_fields\" name=\"initial_fields\" maxlength=\"10\" size=\"10\" value=\"{C_initial_fields}\" type=\"text\"> {L_adm_opt_sectors}\n      </div>\n\n      <div class=\"cell contFS\">\n        <div>\n          {L_adm_opt_max_colonies} {L_adm_opt_colonies_not_counted}\n        </div>\n        <div>\n          <input name=\"player_max_colonies\" maxlength=\"80\" size=\"10\" value=\"{C_player_max_colonies}\" type=\"text\">\n          {L_adm_opt_colonies_no_restrictions}\n        </div>\n      </div>\n\n      <div class=\"header c_l\">\n        {L_res_basic_starting_resources}\n      </div>\n      <div class=\"cell contFJ\">\n        <div>\n          {L_sys_metal}\n          <input name=\"eco_planet_starting_metal\" maxlength=\"10\" size=\"9\" value=\"{C_eco_planet_starting_metal}\" type=\"text\">\n        </div>\n        <div>\n          {L_sys_crystal}\n          <input name=\"eco_planet_starting_crystal\" maxlength=\"10\" size=\"9\" value=\"{C_eco_planet_starting_crystal}\" type=\"text\">\n        </div>\n\n        <div>\n          {L_sys_deuterium}\n          <input name=\"eco_planet_starting_deuterium\" maxlength=\"10\" size=\"9\" value=\"{C_eco_planet_starting_deuterium}\" type=\"text\">\n        </div>\n      </div>\n\n      <div class=\"header c_l\">\n        {L_res_basic_income} ({L_adm_opt_per_hour})\n      </div>\n      <div class=\"cell contFJ\">\n        <div>\n          {L_sys_metal}\n          <input type=\"text\" name=\"metal_basic_income\" maxlength=\"10\" size=\"5\" value=\"{C_metal_basic_income}\" />\n        </div>\n        <div>\n          {L_sys_crystal}\n          <input type=\"text\" name=\"crystal_basic_income\" maxlength=\"10\" size=\"5\" value=\"{C_crystal_basic_income}\">\n        </div>\n        <div>\n          {L_sys_deuterium}\n          <input type=\"text\" name=\"deuterium_basic_income\" maxlength=\"10\" size=\"5\" value=\"{C_deuterium_basic_income}\">\n        </div>\n        <div>\n          {L_sys_energy}\n          <input type=\"text\" name=\"energy_basic_income\" maxlength=\"10\" size=\"5\" value=\"{C_energy_basic_income}\">\n        </div>\n      </div>\n\n      <div class=\"header c_l\">{L_res_basic_storage_size}</div>\n      <div class=\"cell contFJ\">\n        <div>\n          {L_sys_metal}\n          <input name=\"eco_planet_storage_metal\" maxlength=\"10\" size=\"9\" value=\"{C_eco_planet_storage_metal}\" type=\"text\">\n        </div>\n        <div>\n          {L_sys_crystal}\n          <input name=\"eco_planet_storage_crystal\" maxlength=\"10\" size=\"9\" value=\"{C_eco_planet_storage_crystal}\" type=\"text\">\n        </div>\n        <div>\n          {L_sys_deuterium}\n          <input name=\"eco_planet_storage_deuterium\" maxlength=\"10\" size=\"9\" value=\"{C_eco_planet_storage_deuterium}\" type=\"text\">\n        </div>\n      </div>\n      <div class=\"cell c_l\">\n        <input name=\"eco_scale_storage\" id=\"eco_scale_storage\" type=\"checkbox\" value=\"1\"<!-- IF ECO_SCALE_STORAGE --> checked<!-- ENDIF -->/>\n        <label for=\"eco_scale_storage\">{L_adm_opt_eco_scale_storage}</label>\n      </div>\n\n    </div>\n\n    <div id=\"tab_admin_players\">\n      <div class=\"header\">\n        {L_adm_opt_player_defaults}\n      </div>\n      <div class=\"cell\">\n        {L_adm_opt_game_default_language}\n        <select name=\"game_default_language\">\n          <!-- BEGIN game_languages -->\n          <!-- IF game_languages.ID == game_language -->\n          <!-- DEFINE $GAME_LANGUAGE = 'selected' -->\n          <!-- ELSE -->\n          <!-- DEFINE $GAME_LANGUAGE = '' -->\n          <!-- ENDIF -->\n          <option value=\"{game_languages.ID}\" {$GAME_LANGUAGE}>{game_languages.NAME}</option>\n          <!-- END game_languages -->\n        </select>\n      </div>\n      <div class=\"cell\">\n        {L_adm_opt_game_default_skin}\n        <input name=\"game_default_skin\" size=\"40\" maxlength=\"254\" value=\"{C_game_default_skin}\" type=\"text\">\n      </div>\n      <div class=\"cell\">\n        {L_adm_opt_game_default_template}\n        <input name=\"game_default_template\" size=\"40\" maxlength=\"254\" value=\"{C_game_default_template}\" type=\"text\">\n      </div>\n      <div class=\"cell contFS\">\n        <label for=\"game_news_overview\">\n          {L_adm_opt_int_news_count} {L_adm_opt_int_page_imperor}\n        </label>\n        <input id=\"game_news_overview\" name=\"game_news_overview\" type=\"text\" size=3 maxlength=3 value=\"{C_game_news_overview}\" />\n        {L_adm_opt_game_zero_disable}\n      </div>\n      <div class=\"cell\">\n        <label for=\"game_user_changename\">{L_adm_opt_player_change_name}</label>\n        <select name=\"game_user_changename\" id=\"game_user_changename\">\n          <!-- BEGIN change_name_options -->\n          <!-- IF change_name_options.KEY == GAME_CHANGE_NAME -->\n          <!-- DEFINE $SELECTED = 'selected' -->\n          <!-- ELSE -->\n          <!-- DEFINE $SELECTED = '' -->\n          <!-- ENDIF -->\n          <option value=\"{change_name_options.KEY}\" {$SELECTED}>{change_name_options.VALUE}</option>\n          <!-- END change_name_options -->\n        </select>\n      </div>\n      <div class=\"cell\">\n        <label for=\"game_user_changename_cost\">{L_adm_opt_player_change_name_cost}</label>\n        <input type=\"text\" name=\"game_user_changename_cost\" value=\"{C_game_user_changename_cost}\" size=\"10\" maxlength=\"20\" id=\"game_user_changename_cost\"/>\n        {L_sys_dark_matter}\n      </div>\n\n      <div class=\"header\">\n        {L_adm_opt_user_birthday_gift}\n      </div>\n      <div class=\"cell\">\n        <label for=\"user_birthday_gift\">{L_adm_opt_user_birthday_gift}</label>\n        <input id=\"user_birthday_gift\" name=\"user_birthday_gift\" size=\"10\" maxlength=\"20\" value=\"{C_user_birthday_gift}\" type=\"text\">\n        {L_sys_dark_matter_sh}\n        {L_adm_opt_game_zero_disable}\n      </div>\n      <div class=\"cell\">\n        <div>\n          <label for=\"user_birthday_range\">{L_adm_opt_user_birthday_range}</label>\n          <input id=\"user_birthday_range\" name=\"user_birthday_range\" size=\"20\" maxlength=\"20\" value=\"{C_user_birthday_range}\" type=\"text\">\n        </div>\n        <div>\n          {L_adm_opt_user_birthday_range_hint}\n        </div>\n      </div>\n    </div>\n\n    <div id=\"tab_admin_UBE\">\n      <div class=\"header c_l\">\n        {L_sys_opt_bash_info}\n      </div>\n\n      <div class=\"cell\">\n        <label for=\"fleet_bashing_attacks\">{L_sys_opt_bash_attacks}</label>\n        <input id=\"fleet_bashing_attacks\" name=\"fleet_bashing_attacks\" maxlength=\"10\" size=\"10\" value=\"{C_fleet_bashing_attacks}\" type=\"text\" />\n        {L_adm_opt_game_zero_disable}\n      </div>\n      <div class=\"cell\">\n        <label for=\"fleet_bashing_waves\">{L_sys_opt_bash_waves}</label>\n        <input id=\"fleet_bashing_waves\" name=\"fleet_bashing_waves\" maxlength=\"10\" size=\"10\" value=\"{C_fleet_bashing_waves}\" type=\"text\">\n      </div>\n      <div class=\"cell\">\n        <label for=\"fleet_bashing_interval\">{L_sys_opt_bash_interval}</label>\n        <input id=\"fleet_bashing_interval\" name=\"fleet_bashing_interval\" maxlength=\"10\" size=\"10\" value=\"{C_fleet_bashing_interval}\" type=\"text\"> {L_sys_sec}\n      </div>\n      <div class=\"cell\">\n        <label for=\"fleet_bashing_scope\">{L_sys_opt_bash_scope}</label>\n        <input id=\"fleet_bashing_scope\" name=\"fleet_bashing_scope\" maxlength=\"10\" size=\"10\" value=\"{C_fleet_bashing_scope}\" type=\"text\"> {L_sys_sec}\n      </div>\n      <div class=\"cell\">\n        <label for=\"fleet_bashing_war_delay\">{L_sys_opt_bash_war_delay}</label>\n        <input id=\"fleet_bashing_war_delay\" name=\"fleet_bashing_war_delay\" maxlength=\"10\" size=\"10\" value=\"{C_fleet_bashing_war_delay}\" type=\"text\"> {L_sys_sec}\n      </div>\n\n    </div>\n\n    <div id=\"tab_admin_urls\">\n      <div class=\"header c_l\">\n        {L_adm_opt_links}\n      </div>\n\n      <div class=\"cell contFJ\">\n        <label for=\"url_rules\">{L_adm_opt_game_rules}</label>\n        <input id=\"url_rules\" name=\"url_rules\" maxlength=\"254\" value=\"{C_url_rules}\" type=\"text\">\n      </div>\n\n      <div class=\"cell contFJ\">\n        <label for=\"url_faq\">{L_adm_opt_game_faq}</label>\n        <input id=\"url_faq\" name=\"url_faq\" maxlength=\"254\" value=\"{C_url_faq}\" type=\"text\">\n      </div>\n\n      <div class=\"cell contFJ\">\n        <label for=\"url_forum\">{L_adm_opt_game_forum}</label>\n        <input id=\"url_forum\" name=\"url_forum\" maxlength=\"254\" value=\"{C_url_forum}\" type=\"text\">\n      </div>\n\n      <div class=\"cell contFJ\">\n        <label for=\"url_purchase_metamatter\">{L_adm_opt_game_metamatter}</label>\n        <input id=\"url_purchase_metamatter\" name=\"url_purchase_metamatter\" maxlength=\"254\" value=\"{C_url_purchase_metamatter}\" type=\"text\">\n      </div>\n\n      <div class=\"cell contFJ\">\n        <label for=\"geoip_whois_url\">{L_adm_opt_geoip_whois_url}</label>\n        <div class=\"c_c\">\n          <input name=\"geoip_whois_url\" id=\"geoip_whois_url\" size=\"60\" maxlength=\"254\" value=\"{C_geoip_whois_url}\" type=\"text\" />\n          <div>{L_adm_opt_geoip_whois_url_example}</div>\n        </div>\n      </div>\n\n    </div>\n\n    <div id=\"tab_admin_stats\">\n      <div class=\"header\">\n        {L_adm_opt_stats_and_records}\n      </div>\n\n      <div class=\"cell\">\n        <input name=\"stats_hide_pm_link\" id=\"stats_hide_pm_link\" type=\"checkbox\" value=\"1\"<!-- IF STATS_HIDE_PM_LINK --> checked<!-- ENDIF --> />\n        <label for=\"stats_hide_pm_link\">{L_adm_opt_stats_hide_pm_link}</label>\n      </div>\n\n      <div class=\"cell\">\n        <input name=\"stats_hide_admins\" id=\"stats_hide_admins\" type=\"checkbox\" value=\"1\"<!-- IF STATS_HIDE_ADMINS --> checked<!-- ENDIF -->/>\n        <label for=\"stats_hide_admins\">{L_adm_opt_stats_hide_admins} ({L_adm_opt_stats_hide_admins_detail})</label>\n      </div>\n\n      <div class=\"cell contFJ\">\n        <label for=\"stats_hide_player_list\">{L_adm_opt_stats_hide_player_list}</label>\n        <div class=\"c_c\" style=\"flex-grow: 1\">\n          <!--rows=\"\" cols=\"\"-->\n          <textarea id=\"stats_hide_player_list\" name=\"stats_hide_player_list\" rows = 5>{C_stats_hide_player_list}</textarea>\n          <!--<input id=\"stats_hide_player_list\" name=\"stats_hide_player_list\" maxlength=\"254\" value=\"{C_stats_hide_player_list}\" type=\"text\">-->\n          <div>{L_adm_opt_stats_hide_player_list_detail}</div>\n        </div>\n      </div>\n\n      <div class=\"header\">\n        <label for=\"stats_schedule\">{L_adm_opt_stats_schedule}</label>\n      </div>\n      <div class=\"cell contFJ\">\n        <div style=\"flex-grow: 1\">\n          <input id=\"stats_schedule\" name=\"stats_schedule\" maxlength=\"254\" value=\"{C_stats_schedule}\" type=\"text\"><br />\n          {L_adm_opt_stats_schedule_detail}\n        </div>\n      </div>\n\n    </div>\n\n    <div id=\"tab_admin_advertise\">\n      <div class=\"header\">\n        {L_adm_opt_game_advertise}\n      </div>\n\n      <div>\n        <input name=\"advGoogleLeftMenuIsOn\" id=\"advGoogleLeftMenuIsOn\" type=\"checkbox\" value=\"1\"<!-- IF ADV_LEFT_MENU --> checked<!-- ENDIF -->/> <label for=\"advGoogleLeftMenuIsOn\">{L_adm_opt_game_oth_adds}</label>\n        <textarea name=\"advGoogleLeftMenuCode\" rows=\"10\">{C_advGoogleLeftMenuCode}</textarea>\n      </div>\n    </div>\n\n  </div>\n  <input name=\"save\" value=\"{L_adm_opt_btn_save}\" type=\"submit\" />\n</form>\n\n<script type=\"text/javascript\"><!--\n  $(\".tabs\").tabs();\n  $(\"#tab_admin_settings\").show();\n// --></script>\n"
  },
  {
    "path": "design/templates/OpenGame/admin/simple_header.tpl.html",
    "content": ""
  },
  {
    "path": "design/templates/OpenGame/admin/tests/_number_color_value.tpl.html",
    "content": "<tr>\n  <td>\n    &lt;!-- DEFINE $T_MAXIMUM = 100 --&gt;<br />\n    &lt;!-- DEFINE $T_VALUE = XXX --&gt;<br />\n    &lt;!-- INCLUDE _number_color_value --&gt;\n  </td>\n  <td class=\"ptl_etalon\">\n    <span class=\"error\">101</span><span class=\"warning\">100</span><span class=\"warning\">99</span><span class=\"warning\">91</span><span class=\"notice\">90</span><span class=\"notice\">89</span><span class=\"notice\">76</span><span class=\"info\">75</span><span class=\"info\">74</span><span class=\"info\">51</span><span class=\"ok\">50</span><span class=\"ok\">49</span><span class=\"ok\">0</span><span class=\"error\">1</span><span class=\"zero_number\">0</span><span class=\"zero_number\">-1</span>\n  </td>\n  <td class=\"ptl_result\">\n    <!-- DEFINE $T_MAXIMUM = 100 -->\n    <!-- DEFINE $T_VALUE = 101 -->\n    <!-- INCLUDE _number_color_value -->\n\n    <!-- DEFINE $T_MAXIMUM = 100 -->\n    <!-- DEFINE $T_VALUE = 100 -->\n    <!-- INCLUDE _number_color_value -->\n\n    <!-- DEFINE $T_MAXIMUM = 100 -->\n    <!-- DEFINE $T_VALUE = 99 -->\n    <!-- INCLUDE _number_color_value -->\n\n    <!-- DEFINE $T_MAXIMUM = 100 -->\n    <!-- DEFINE $T_VALUE = 91 -->\n    <!-- INCLUDE _number_color_value -->\n\n    <!-- DEFINE $T_MAXIMUM = 100 -->\n    <!-- DEFINE $T_VALUE = 90 -->\n    <!-- INCLUDE _number_color_value -->\n\n    <!-- DEFINE $T_MAXIMUM = 100 -->\n    <!-- DEFINE $T_VALUE = 89 -->\n    <!-- INCLUDE _number_color_value -->\n\n    <!-- DEFINE $T_MAXIMUM = 100 -->\n    <!-- DEFINE $T_VALUE = 76 -->\n    <!-- INCLUDE _number_color_value -->\n\n    <!-- DEFINE $T_MAXIMUM = 100 -->\n    <!-- DEFINE $T_VALUE = 75 -->\n    <!-- INCLUDE _number_color_value -->\n\n    <!-- DEFINE $T_MAXIMUM = 100 -->\n    <!-- DEFINE $T_VALUE = 74 -->\n    <!-- INCLUDE _number_color_value -->\n\n    <!-- DEFINE $T_MAXIMUM = 100 -->\n    <!-- DEFINE $T_VALUE = 51 -->\n    <!-- INCLUDE _number_color_value -->\n\n    <!-- DEFINE $T_MAXIMUM = 100 -->\n    <!-- DEFINE $T_VALUE = 50 -->\n    <!-- INCLUDE _number_color_value -->\n\n    <!-- DEFINE $T_MAXIMUM = 100 -->\n    <!-- DEFINE $T_VALUE = 49 -->\n    <!-- INCLUDE _number_color_value -->\n\n    <!-- DEFINE $T_MAXIMUM = 100 -->\n    <!-- DEFINE $T_VALUE = 0 -->\n    <!-- INCLUDE _number_color_value -->\n\n    <!-- DEFINE $T_MAXIMUM = 0 -->\n    <!-- DEFINE $T_VALUE = 1 -->\n    <!-- INCLUDE _number_color_value -->\n\n    <!-- DEFINE $T_MAXIMUM = 0 -->\n    <!-- DEFINE $T_VALUE = 0 -->\n    <!-- INCLUDE _number_color_value -->\n\n    <!-- DEFINE $T_MAXIMUM = 0 -->\n    <!-- DEFINE $T_VALUE = -1 -->\n    <!-- INCLUDE _number_color_value -->\n  </td>\n  <td class=\"ptl_status\"></td>\n  <th>_number_color_value.tpl.html</th>\n</tr>\n"
  },
  {
    "path": "design/templates/OpenGame/admin/userlist.tpl.html",
    "content": "<script type=\"text/javascript\">\n  jQuery(document).on('click', '.i_icon_mail_userlist', function () {\n    document.location = \"messages.php?mode=write&id=\" + jQuery(this).parent().parent().attr('uid');\n  });\n\n  jQuery(document).on('click', '.i_icon_delete_userlist', function () {\n    if (confirm('!{LA_adm_ul_delete_confirm} ' + jQuery(this).parent().parent().attr('uname'))) {\n      document.location = \"admin/userlist.php?sort={SORT}&action={D_ACTION_DELETE}&uid=\" + jQuery(this).parent().parent().attr('uid');\n    }\n  });\n\n  jQuery(document).on('click', '.i_icon_impers_userlist', function () {\n    document.location = \"admin/userlist.php?sort={SORT}&action={D_ACTION_USE}&uid=\" + jQuery(this).parent().parent().attr('uid');\n  });\n\n  jQuery(document).on('click', '#user_list tr td:first-child, #user_list tr td:nth-child(2)', function () {\n    document.location = \"index.php?page=admin/user_view&uid=\" + jQuery(this).parent().attr('uid');\n  });\n\n  jQuery(document).on('click', '.js_admin_ban', function () {\n    document.location = \"admin/banned.php?name=\" + jQuery(this).parent().attr('uname');\n  });\n\n  jQuery(document).on('click', '.js_admin_whois', function () {\n    openInNewTab(\"{GEOIP_WHOIS_URL}\" + jQuery(this).parent().attr('uip'));\n  });\n</script>\n\n<h2>{PAGE_HEADER} ({USER_COUNT})</h2>\n\n<style>\n  #user_list a\n  {\n    text-decoration: underline;\n  }\n</style>\n\n<table id=\"user_list\">\n  <!-- @formatter:off -->\n  <!-- BEGIN header -->\n    <tr class=\"c_c\" style=\"border: 1px solid #cccccc;\">\n    <!-- BEGIN header_row -->\n      <!-- IF header_row.TITLE -->\n          <!--suppress HtmlUnknownAttribute -->\n          <th colspan=\"{header_row.COLSPAN}\" rowspan=\"{header_row.ROWSPAN}\">\n            <!-- IF header_row.SORT -->\n              <!-- DEFINE $DESC_PARAM = '' -->\n              <!-- IF header_row.SORT == SORT -->\n                <!-- IF DESC -->▼<!-- ELSE --><!-- DEFINE $DESC_PARAM = '&desc=1' -->▲<!-- ENDIF -->\n                <!-- ↑ &#8593; up ▲ ↓ &#8595; down ▼  ⇅ ▲▼ -->\n                <!-- DEFINE $SORT_CHAR = '' -->\n              <!-- ELSE -->\n                <!-- DEFINE $SORT_CHAR = '' -->\n              <!-- ENDIF -->\n              <!--suppress HtmlUnknownTarget -->\n              <a href=\"{PAGE_URL}?sort={header_row.SORT}{$DESC_PARAM}\">{header_row.TITLE} {$SORT_CHAR}</a>\n            <!-- ELSE -->\n              {header_row.TITLE}\n            <!-- ENDIF -->\n          </th>\n      <!-- ENDIF -->\n    <!-- END header_row -->\n    </tr>\n  <!-- END header -->\n\n  <!-- IF GEOIP_WHOIS_URL -->\n    <!-- DEFINE $GEOIP_CLASS = 'js_admin_whois' -->\n  <!-- ENDIF -->\n  <!-- @formatter:on -->\n\n  <!-- BEGIN user -->\n  <tr uid=\"{user.ID}\" uname=\"{user.NAME_HTML}\" uip=\"{user.IP}\" class=\"highlight\">\n    <td>{user.ID}</td>\n    <td class=\"c_l\">{user.NAME}</td>\n    <!-- IF METAMATTER -->\n    <td>{user.METAMATTER}</td>\n    <!-- ENDIF -->\n    <td class=\"c_r\">{user.REFERRAL_COUNT}</td>\n    <td class=\"c_r\">{user.REFERRAL_DM}</td>\n    <td>{user.TIME_REGISTERED}</td>\n    <td>{user.TIME_PLAYED}</td>\n    <td>{user.ACTIVITY}</td>\n    <!-- IF user.BANNED -->\n    <!-- DEFINE $BAN_CLASS = ' warning' -->\n    <td class=\"js_admin_ban {$BAN_CLASS}\"\n        title=\"{user.BAN_ISSUER} {L_adm_ban_msg_issued_date} {user.BAN_DATE}. {L_adm_bn_reas}: '{user.BAN_REASON}'\">\n      {user.BANNED}\n    </td>\n    <!-- ELSE -->\n    <!-- DEFINE $BAN_CLASS = '' -->\n    <td class=\"js_admin_ban\">{L_sys_no}</td>\n    <!-- ENDIF -->\n    <!-- IF ! RESTRICTED -->\n    <td>{user.VACATION}</td>\n    <td>{user.EMAIL}</td>\n\n    <!-- IF user.IP_MULTI -->\n    <!-- DEFINE $IP_CLASS = ' error' -->\n    <!-- ELSE -->\n    <!-- DEFINE $IP_CLASS = '' -->\n    <!-- ENDIF -->\n    <td class=\"{$GEOIP_CLASS}{$IP_CLASS}\">{user.IP}<!-- IF user.IP_MULTI --> ({user.IP_MULTI})<!-- ENDIF --></td>\n    <!-- IF GEOIP -->\n    <th>{user.COUNTRY}</th>\n    <!-- ENDIF -->\n    <!-- ENDIF -->\n\n    <td>\n      <div class=\"i_icon_mail_userlist\"></div>\n      <!-- IF user.ACTION -->\n      <div class=\"i_icon_delete_userlist\"></div>\n      <div class=\"i_icon_impers_userlist\"></div>\n      <!-- ENDIF -->\n    </td>\n  </tr>\n  <!-- END user -->\n</table>\n"
  },
  {
    "path": "design/templates/OpenGame/affilates.tpl.html",
    "content": "<h1>{L_aff_title}</h1>\n\n<table width=\"519\">\n  <tr>\n    <th class=\"c_c\">{L_aff_title}</th>\n  </tr>\n  <tr>\n    <td>\n      {L_aff_text1} {C_rpg_bonus_divisor} {L_aff_text2}<br/>\n      <span class=\"notice\">{L_aff_text3} {C_rpg_bonus_minimum} {L_sys_dark_matter_sh}</span>\n    </td>\n  </tr>\n  <tr>\n    <th>\n      <h2>{L_aff_link}</h2>\n      {L_aff_link_direct}<br>\n      <input name=\"htmlbbcode\" type=\"text\" id=\"htmlbbcode\"\n             value=\"{D_SN_ROOT_VIRTUAL_PARENT}login.php?id_ref={user_id}#tab_register\" size=\"80\"/><br/><br/>\n\n      {L_aff_link_bb}<br>\n      <input name=\"htmlbbcode\" type=\"text\" id=\"htmlbbcode\"\n             value=\"[url={D_SN_ROOT_VIRTUAL_PARENT}login.php?id_ref={user_id}#tab_register]{C_game_name}[/url]\"\n             size=\"80\"/><br/><br/>\n\n      {L_aff_link_html}<br>\n      <input name=\"htmllink\" type=\"text\" id=\"htmllink\"\n             value=\"<a href={D_SN_ROOT_VIRTUAL_PARENT}login.php?id_ref={user_id}#tab_register>{C_game_name}</a>\"\n             size=\"80\">\n    </th>\n  </tr>\n  <tr>\n    <th>\n      <h2>{L_aff_banner}</h2>\n      <img src=\"{bannerURL}\"><br><br>\n\n      {L_aff_banner_bb}<br>\n      <input name=\"bannerbbcode\" type=\"text\" id=\"bannerbbcode\"\n             value=\"[url={D_SN_ROOT_VIRTUAL_PARENT}login.php?id_ref={user_id}#tab_register][img]{bannerURL}[/img][/url]\"\n             size=\"80\"><br><br>\n\n      {L_aff_banner_html}<br>\n      <input name=\"bannerlink\" type=\"text\" id=\"bannerlink\"\n             value=\"<a href={D_SN_ROOT_VIRTUAL_PARENT}login.php?id_ref={user_id}#tab_register><img src={bannerURL}></a>\"\n             size=\"80\">\n    </th>\n  </tr>\n  <tr>\n    <th>\n      <h2>{L_aff_userbar}</h2>\n      <img src=\"{userbarURL}\"><br><br>\n\n      {L_aff_userbar_bb}<br>\n      <input name=\"userbarbbcode\" type=\"text\" id=\"userbarbbcode\"\n             value=\"[url={D_SN_ROOT_VIRTUAL_PARENT}login.php?id_ref={user_id}#tab_register][img]{userbarURL}[/img][/url]\"\n             size=\"80\"><br><br>\n\n      {L_aff_userbar_html}<br>\n      <input name=\"userbarlink\" type=\"text\" id=\"userbarlink\"\n             value=\"<a href={D_SN_ROOT_VIRTUAL_PARENT}login.php?id_ref={user_id}#tab_register><img src={userbarURL}></a>\"\n             size=\"80\">\n    </th>\n  </tr>\n</table>\n\n<h2>{L_aff_list}</h2>\n<table>\n  <tr class=\"c_c\">\n    <th>{L_sys_player}</th>\n    <th>{L_sys_register_date}</th>\n    <th>{L_aff_gained}</th>\n    <th>{L_aff_your_bonus}</th>\n  </tr>\n\n  <!-- IF .affilates -->\n    <!-- BEGIN affilates -->\n    <tr class=\"c_r\">\n      <td class=\"c_l\">{affilates.USERNAME}</td>\n      <td class=\"c_c\">{affilates.REGISTERED}</td>\n      <td>{affilates.DARK_MATTER}</td>\n      <td>{affilates.GAINED}</td>\n    </tr>\n    <!-- END affilates -->\n\n    <tr class=\"c_l\">\n      <th colspan=3>{L_sys_total}</th>\n      <th class=\"c_r\">{GAINED}</th>\n    </tr>\n  <!-- ELSE -->\n    <tr class=\"c_c\">\n      <td colspan=4>{L_aff_none}</td>\n    </tr>\n  <!-- ENDIF -->\n</table>\n"
  },
  {
    "path": "design/templates/OpenGame/agreement.tpl.html",
    "content": "N/A"
  },
  {
    "path": "design/templates/OpenGame/ali_admin.tpl.html",
    "content": "<h1>{L_ally_admin} [{ally_tag}]</h1>\n<a class=\"link button_pseudo\" href=\"alliance.php\">{L_ali_sys_main_page}</a>\n<br/>\n<br/>\n<form action=\"\" method=\"POST\" enctype=\"multipart/form-data\">\n  <table width=519>\n    <tr class=\"c_l\">\n      <th colspan=2>\n        <div class=\"contFJ\">\n          {L_ali_adm_options}\n        </div>\n      </th>\n    </tr>\n    <tr>\n      <th>{L_Name}</th>\n      <th><input type=text name=\"name\" value=\"{ally_name}\" size=\"40\" maxlength=\"32\"></th>\n    </tr>\n    <tr>\n      <th>{L_Tag}</th>\n      <th><input type=text name=\"tag\" value=\"{ally_tag}\" size=\"40\" maxlength=\"8\"></th>\n    </tr>\n    <tr>\n      <th>{L_Main_Page}</th>\n      <th><input type=text name=\"web\" value=\"{ally_web}\" size=\"40\" maxlength=\"255\"></th>\n    </tr>\n    <tr>\n      <th>\n        <div>{L_Alliance_logo}</div>\n      </th>\n      <td class=\"c_l\">\n        <!-- IF AVATAR_UPLOAD_MESSAGE -->\n        <span class=\"<!-- IF AVATAR_UPLOAD_STATUS == 0 -->success<!-- ELSEIF AVATAR_UPLOAD_STATUS == 1 -->warning<!-- ELSE -->error<!-- ENDIF -->\">{AVATAR_UPLOAD_MESSAGE}</span><br/>\n        <!-- ENDIF -->\n        <!-- IF ALLY_IMAGE -->\n        <div align=\"center\">\n          <img src=\"{D_SN_HTTP_AVATAR}/ally_{ALLY_ID}.png\" alt=\"logo\"/><br/>\n          <input name=\"avatar_remove\" type=\"checkbox\" id=\"avatar_remove\"/><label for=\"avatar_remove\">{L_opt_avatar_remove}</label>\n        </div>\n        <!-- ELSE -->\n        <label for=\"avatar\">{L_opt_upload}</label>&nbsp;<input type=\"file\" name=\"avatar\" id=avatar size=\"40\"><br/>\n        <!-- ENDIF -->\n      </td>\n    </tr>\n    <tr>\n      <th>{L_ali_adm_requests}</th>\n      <th>\n        <span class=\"fl\"><select name=\"request_notallow\">\n          <option value=1{ally_request_notallow_0}>{L_not_allow_request}</option>\n          <option value=0{ally_request_notallow_1}>{L_Allow_request}</option>\n        </select></span>\n        <span class=\"fr\"><a href=\"alliance.php?mode=admin&edit=requests\">{request_count}</a></span>\n      </th>\n    </tr>\n    <tr>\n      <th>{L_Founder_name}</th>\n      <th><input type=\"text\" name=\"owner_range\" value=\"{ally_owner_range}\" size=30></th>\n    </tr>\n    <tr>\n      <th colspan=2><input type=\"submit\" name=\"isSaveOptions\" value=\"{L_sys_save}\"></th>\n    </tr>\n  </table>\n</form>\n\n<form action=\"\" method=\"POST\">\n  <table width=519>\n    <tr class=\"c_c\">\n      <th colspan=3>{L_Texts}</th>\n    </tr>\n    <tr class=\"c_c\">\n      <td><a class=\"link\" href=\"alliance.php?mode=admin&edit=ally&t=1\">{L_External_text}</a></td>\n      <td><a class=\"link\" href=\"alliance.php?mode=admin&edit=ally&t=2\">{L_Internal_text}</a></td>\n      <td><a class=\"link\" href=\"alliance.php?mode=admin&edit=ally&t=3\">{L_Request_text}</a></td>\n    </tr>\n    <tr>\n      <th class=\"c_c\" colspan=3>{request_type} (<span id=\"cntChars\">0</span> / 5000)</th>\n    </tr>\n    <tr>\n      <th colspan=3>\n        <textarea name=\"text\" id=\"ally_text\" cols=70 rows=15 onkeyup=\"cntchar(5000)\">{text}</textarea>\n      </th>\n    </tr>\n    <tr>\n      <th colspan=3>\n        <input type=\"hidden\" name=\"t\" value=\"{t}\">\n        <input type=\"reset\" value=\"{L_ali_sys_clear}\">\n        <input type=\"submit\" name=\"isSaveText\" value=\"{L_sys_save}\">\n      </th>\n    </tr>\n  </table>\n</form>\n\n<form action=\"\" method=\"POST\" style=\"width:48em;margin-top:1em;\">\n  <div class=\"header\">{L_ally_admin}</div>\n  <div class=\"cell\">\n    <div style=\"display: inline-block\">\n      <!-- BEGIN admin_actions -->\n      <div>\n        <a class=\"button_pseudo w100\" href=\"alliance.php?mode=admin&edit={admin_actions.ACTION}\">\n          {admin_actions.LOCALE}\n        </a>\n      </div>\n      <!-- END admin_actions -->\n    </div>\n  </div>\n  <div class=\"cell contFJ\" style=\"padding: 0 1em; {hideNotOwner}\">\n    <div>\n      {L_ali_adm_transfer}&nbsp;&nbsp;&nbsp;\n      <select name=\"idNewLeader\">\n        <option disabled selected>{L_ali_adm_newLeader}</option>\n        {adminMembers}\n      </select>\n    </div>\n    <input type=\"submit\" name=\"isTransfer\" value=\"{L_ali_confirm}\">\n  </div>\n  <div class=\"cell contFJ\" style=\"padding: 0 1em; {hideNotOwner}\">\n    <div>\n      <input type=\"checkbox\" name=\"isConfirmDisband\" value=\"1\" id=\"isConfirmDisband\">\n      <label for=\"isConfirmDisband\">{L_ali_adm_disband}</label>\n    </div>\n    <input type=\"submit\" name=\"isDisband\" value=\"{L_ali_confirm}\">\n  </div>\n</form>\n"
  },
  {
    "path": "design/templates/OpenGame/ali_admin_diplomacy.tpl.html",
    "content": "<h2>{L_ali_dip_title} - {L_ali_dip_negotiate}</h2>\n\n<a class=\"button_pseudo link\" href=\"alliance.php?mode=admin\">{L_ali_adm_return}</a><br/>\n\n<table width=519>\n  <tr class=\"c_l\"><th>{L_ali_dip_offer_new}</th></tr>\n\n  <tr class=\"c_l\">\n    <td>\n      <form action=\"alliance.php?mode=admin&edit=diplomacy\" name=\"ali_dip_offer\" method=\"post\">\n      {L_ali_dip_offer_to_ally} \n      <select name=\"alliance_negotiation_contr_ally_id\">\n        <!-- BEGIN alliance -->\n          <option value=\"{alliance.ID}\">{alliance.NAME} [{alliance.TAG}]</option>\n        <!-- END alliance -->\n      </select>\n\n      <select name=\"alliance_negotiation_relation\">\n        <!-- BEGIN relation -->\n          <option value=\"{relation.ID}\">{relation.TEXT}</option>\n        <!-- END relation -->\n      </select>\n      \n      <br />\n      {L_ali_dip_offer}\n      <textarea name=\"alliance_negotiation_propose\" style=\"width:98%\"></textarea>\n    </td>\n  </tr>\n  <tr><th class=\"c_c\"><input type=\"submit\" name=\"ali_dip_offer_make\" value=\"{L_ali_dip_offer_make}\"></th></tr>\n</table>\n\n<h2>{L_ali_dip_offers}</h2>\n\n<table width=519>\n<!--\n  <tr>\n    <td class=c><center><a href=\"alliance.php?mode=admin&edit=requests&show=0&sort=1\">{L_sys_date_time}</a></center></td>\n    <td class=c><center><a href=\"alliance.php?mode=admin&edit=requests&show=0&sort=0\">{L_sys_from_person}</a></center></td>\n    <td class=c><center>{L_ali_dip_offer}</center></td>\n    <td class=c><center><img src=\"design/images/r4.png\" alt=\"{L_ali_req_accept}\" title=\"{L_ali_req_accept}\" border=\"0\"></center></td>\n    <td class=c><center><img src=\"design/images/r1.png\" alt=\"{L_ali_req_deny}\" title=\"{L_ali_req_deny}\" border=\"0\"></center></td>\n  </tr>\n-->\n  <!-- BEGIN offer -->  \n    <tr class=\"c_c\">\n      <th>{offer.TIME}</th>\n      <th><!-- IF offer.OWNER -->{L_ali_dip_offer_to}<!-- ELSE -->{L_ali_dip_offer_from}<!-- ENDIF -->:&nbsp;{offer.NAME}</th>\n      <th>{offer.RELATION}</th>\n      <th><!-- IF offer.OWNER -->&nbsp;<!-- ELSE --><a href=\"alliance.php?mode=admin&edit=diplomacy&answer=accept&offer_id={offer.ID}\"><img src=\"design/images/icon_accept.png\" width=\"16\" height=\"16\" alt=\"{L_ali_dip_offer_accept}\" title=\"{L_ali_dip_offer_accept}\" border=\"0\"></a><!-- ENDIF --></th>\n      <th>\n\t\t  <!-- IF offer.OWNER -->\n          <a href=\"alliance.php?mode=admin&edit=diplomacy&answer=deny&offer_id={offer.ID}\"><img src=\"design/images/icon_deny.png\" alt=\"{L_ali_dip_offer_delete}\" title=\"{L_ali_dip_offer_delete}\" border=\"0\"></a>\n\t\t  <!-- ELSE -->      \n          <a href=\"alliance.php?mode=admin&edit=diplomacy&answer=deny&offer_id={offer.ID}\"><img src=\"design/images/icon_deny.png\" alt=\"{L_ali_dip_offer_deny}\" title=\"{L_ali_dip_offer_deny}\" border=\"0\"></a>\n        <!-- ENDIF -->\n      </th>\n    </tr>\n    <tr>\n      <td colspan=5 class=\"c_l\">\n         {offer.TEXT}&nbsp;\n    <!-- IF offer.STATUS -->\n    <hr /><span class=\"negative\"><!-- IF offer.OWNER -->{L_ali_dip_offer_answer}<!-- ELSE -->{L_ali_dip_offer_deny_reason}<!-- ENDIF --></span>\n      <!-- <hr />{offer.RESPONSE} -->\n    <!-- ENDIF -->\n      </td>\n    </tr>\n  <!-- BEGINELSE offer -->  \n    <tr><th colspan=5>{L_ali_dip_offer_none}</th></tr>\n  <!-- END offer -->  \n</table>\n\n<a class=\"button_pseudo link\" href=\"alliance.php\">{L_ali_sys_main_page}</a>\n"
  },
  {
    "path": "design/templates/OpenGame/ali_admin_mail.tpl.html",
    "content": "<h1>{PAGE_HEADER}</h1>\n<a class=\"button_pseudo link\" href=\"alliance.php?mode=admin\">{L_ali_adm_return}</a><br/>\n\n<form action=\"alliance.php?mode=circular\" method=\"post\">\n  <div style=\"display: inline-block\" class=\"border_image_large\">\n    <div class=\"header\">\n      {L_Destiny}\n\n      <select name=\"r\">\n        <option value=\"-1\">{L_All_players}</option>\n        <!-- BEGIN ranks -->\n        <option value=\"{ranks.VALUE}\">{ranks.NAME}</option>\n        <!-- END ranks -->\n      </select>\n    </div>\n    <div class=\"cell\">\n      {L_Text_mail}<br/>\n      <textarea name=\"text\" cols=\"60\" rows=\"10\" onkeyup=\"cntchar(5000)\"></textarea><br/>\n      <span id=\"cntChars\">0</span> / 5000 {L_ali_req_characters}\n    </div>\n    <div class=\"header contFJ\">\n      <input type=\"reset\" value=\"{L_Clear}\">\n      <input type=\"submit\" value=\"{L_Send}\">\n    </div>\n  </div>\n</form>\n"
  },
  {
    "path": "design/templates/OpenGame/ali_admin_request.tpl.html",
    "content": "<h1>{L_ali_req_admin_title} [{ally_tag}]</h1>\n\n<a class=\"button_pseudo link\" href=\"alliance.php?mode=admin\">{L_ali_adm_return}</a><br/>\n\n<table width=519>\n  <tr class=\"c_c\">\n    <th><a href=\"alliance.php?mode=admin&edit=requests&show=0&sort=1\">{L_ali_req_candidate}</a></th>\n    <th><a href=\"alliance.php?mode=admin&edit=requests&show=0&sort=0\">{L_ali_req_date}</a></th>\n    <th>{L_ali_req_text}</th>\n    <th><img src=\"design/images/r4.png\" alt=\"{L_ali_req_accept}\" title=\"{L_ali_req_accept}\"></th>\n    <th><img src=\"design/images/r1.png\" alt=\"{L_ali_req_deny}\" title=\"{L_ali_req_deny}\"></th>\n  </tr>\n\n  <!-- BEGIN alliance_request -->  \n    <tr class=\"c_c\">\n      <td><center>{alliance_request.USER_NAME}</center></td>\n      <td><center>{alliance_request.TIME}</center></td>\n      <td><div align=\"left\">\n        <!-- IF alliance_request.DENIED -->\n          {L_ali_req_deny_admin}\n        <!-- ELSE -->\n          {alliance_request.TEXT}\n        <!-- ENDIF -->\n      </div></td>\n      <td><a href=\"alliance.php?mode=admin&edit=requests&id_user={alliance_request.USER_ID}\"><img src=\"design/images/r4.png\" alt=\"{L_ali_req_accept}\" title=\"{L_ali_req_accept}\" border=\"0\"></a></td>\n      <td><a href=\"alliance.php?mode=admin&edit=requests&d={alliance_request.USER_ID}\"><img src=\"design/images/r1.png\" alt=\"{L_ali_req_deny}\" title=\"{L_ali_req_deny}\" border=\"0\"></a></td>\n    </tr>\n  <!-- BEGINELSE alliance_request -->  \n    <tr><th colspan=5>{L_ali_req_emptyList}</th></tr>\n  <!-- END alliance_request -->  \n</table>\n\n<a class=\"button_pseudo link\" href=\"alliance.php\">{L_ali_sys_main_page}</a>\n"
  },
  {
    "path": "design/templates/OpenGame/ali_admin_rights.tpl.html",
    "content": "<h1>{L_ali_adm_rights_title}</h1>\n\n<a class=\"button_pseudo link\" href=\"alliance.php?mode=admin\">{L_ali_adm_return}</a><br/>\n\n<table width=519>\n  <form action=\"alliance.php?mode=admin&edit=rights\" method=POST>\n    <tr>\n      <td class=\"c\"><img src=\"design/images/abort.gif\" alt=\"{L_ali_adm_rights_rank_delete}\" title=\"{L_ali_adm_rights_rank_delete}\" border=\"0\"></td>\n      <td class=\"c\">{L_ali_adm_rights_rank_name}</td>\n      <td class=\"c\"><img src=\"design/images/r8.png\" alt=\"{L_ali_adm_rights_mass_mail}\" title=\"{L_ali_adm_rights_mass_mail}\"></td>\n      <td class=\"c\"><img src=\"design/images/r7.png\" alt=\"{L_ali_adm_rights_view_online}\" title=\"{L_ali_adm_rights_view_online}\"></td>\n      <td class=\"c\"><img src=\"design/images/r5.png\" alt=\"{L_ali_req_check}\" title=\"{L_ali_req_check}\"></td>\n      <td class=\"c\"><img src=\"design/images/r2.png\" alt=\"{L_ali_adm_kick}\" title=\"{L_ali_adm_kick}\"></td>\n      <td class=\"c\"><img src=\"design/images/r9.png\" alt=\"{L_ali_adm_rights_helper}\" title=\"{L_ali_adm_rights_helper}\"></td>\n    </tr>\n\n    <!-- BEGIN rank -->\n      <tr>\n        <th>\n          <!-- IF .rank > 1 -->\n            <a href=\"alliance.php?mode=admin&edit=rights&d={rank.ID}\"><img src=\"design/images/abort.gif\" alt=\"{L_ali_adm_rights_rank_delete}\" title=\"{L_ali_adm_rights_rank_delete}\" border=\"0\"></a>\n          <!-- ELSE -->\n            &nbsp;\n          <!-- ENDIF -->\n        </th>\n        <th><input type=text name=\"u[{rank.ID}][name]\" value=\"{rank.NAME}\" size=\"60\" maxlength=\"60\"></th>\n        <th><input type=checkbox name=\"u[{rank.ID}][{rank.N1}]\"{rank.R1} value=\"1\"></th>\n        <th><input type=checkbox name=\"u[{rank.ID}][{rank.N2}]\"{rank.R2} value=\"1\"></th>\n        <th><input type=checkbox name=\"u[{rank.ID}][{rank.N3}]\"{rank.R3} value=\"1\"></th>\n        <th><input type=checkbox name=\"u[{rank.ID}][{rank.N4}]\"{rank.R4} value=\"1\"></th>\n        <th><input type=checkbox name=\"u[{rank.ID}][{rank.N5}]\"{rank.R5} value=\"1\"></th>\n      </tr>\n    <!-- BEGINELSE rank -->\n      <th colspan=8>{L_ali_adm_rights_rank_none}</th>      \n    <!-- END rank -->\n\n    <tr><th colspan=7><input type=submit value=\"{L_sys_save}\"></th></tr>\n  </form>\n\n  <form action=\"alliance.php?mode=admin&edit=rights&add=name\" method=POST>\n    <tr>\n      <th>&nbsp;</th>\n      <th><input type=text name=\"newRankName\" size=\"60\" maxlength=\"60\" value=\"{L_ali_adm_rights_rank_new}\"></th>\n      <th colspan=\"5\"><input type=submit value=\"{L_sys_create}\"></th>\n    </tr>\n  </form>\n</table>\n\n<br />\n\n<form action=\"alliance.php?mode=admin&edit=rights\" method=POST>\n<table width=519>\n\t<tr><th class=\"c_c\" colspan=\"2\">{L_ali_adm_rights_legend}</th></tr>\n\t<tr><th><img src=\"design/images/r8.png\"></th><td class=\"c_l\">{L_ali_adm_rights_mass_mail}</td></tr>\n\t<tr><th><img src=\"design/images/r7.png\"></th><td class=\"c_l\">{L_ali_adm_rights_view_online}</td></tr>\n\t<tr><th><img src=\"design/images/r5.png\"></th><td class=\"c_l\">{L_ali_req_check}</td></tr>\n\t<tr><th><img src=\"design/images/r2.png\"></th><td class=\"c_l\">{L_ali_adm_kick}</td></tr>\n\t<tr><th><img src=\"design/images/r9.png\"></th><td class=\"c_l\">{L_ali_adm_rights_helper}</td></tr>\n\t<tr><th class=\"c_c\" colspan=\"2\"><a class=\"link\" href=\"alliance.php\">{L_ali_sys_main_page}</a></th></tr>\n</table>\n</form>\n\t"
  },
  {
    "path": "design/templates/OpenGame/ali_external.tpl.html",
    "content": "<br />\n<form action=\"alliance.php?mode=make\" method=\"POST\"><table width=519>\n  <tr><td class=\"c\" colspan=2>{L_ali_make_title}</td></tr>\n  <tr>\n    <th>{L_ali_sys_tag} {L_ali_make_tag_length}</th>\n    <th><input type=\"text\" name=\"tag\" size=8 maxlength=8 value=\"\"></th>\n  </tr>\n  <tr>\n    <th>{L_ali_sys_name} {L_ali_make_name_length}</th>\n    <th><input type=\"text\" name=\"name\" size=20 maxlength=30 value=\"\"></th>\n  </tr>\n  <tr><th colspan=2><input type=\"submit\" value=\"{L_ali_make_confirm}\"></th></tr>\n</table></form>\n\n<!-- INCLUDE ali_search -->\n"
  },
  {
    "path": "design/templates/OpenGame/ali_external_make.tpl.html",
    "content": "<br />\n<form action=\"alliance.php?mode=make&yes=1\" method=\"POST\">\n<table width=519>\n\t<tr>\n\t  <td class=\"c\" colspan=2>{L_make_alliance}</td>\n\t</tr>\n\t<tr>\n\t  <th>{L_alliance_tag} (3-8 {L_sys_characters})</th>\n\t  <th><input type=\"text\" name=\"tag\" size=8 maxlength=8 value=\"\"></th>\n\t</tr>\n\t<tr>\n\t  <th>{L_allyance_name} (1-32 {L_sys_characters})</th>\n\t  <th><input type=\"text\" name=\"name\" size=20 maxlength=32 value=\"\"></th>\n\t</tr>\n\t<tr>\n\t  <th colspan=2><input type=\"submit\" value=\"{L_sys_create}\"></th>\n\t</tr>\n</table>\n\n</form>\n"
  },
  {
    "path": "design/templates/OpenGame/ali_info.tpl.html",
    "content": "<!-- IF ally_image -->\n  <!-- DEFINE $COLSPAN = 3 -->\n  <!-- DEFINE $COLSPAN2 = 2 -->\n<!-- ELSE -->\n  <!-- DEFINE $COLSPAN = 2 -->\n  <!-- DEFINE $COLSPAN2 = 3 -->\n<!-- ENDIF -->\n\n<h1>{L_sys_alliance}&nbsp;[{ally_tag}]</h1>\n\n<table width=519>\n  <tr>\n    <th class=\"c_l\" colspan=\"{$COLSPAN}\">\n      <!-- IF EXTERNAL -->\n      {L_ali_info_title}\n      <!-- ELSE -->\n      {L_your_alliance}\n      <span class=\"fr\">\n        <!-- IF ALLY_ADMIN -->\n          <a href=\"alliance.php?mode=admin&edit=ally\"><span class=\"ok\">[&nbsp;{L_ally_admin}&nbsp;]</span></a>\n        <!-- ELSEIF ALLY_CAN_KICK -->\n          <a href=\"alliance.php?mode=admin&edit=members\"><span class=\"ok\">[&nbsp;{L_members_admin}&nbsp;]</span></a>\n        <!-- ENDIF -->\n      </span>\n      <!-- ENDIF -->\n    </th>\n  </tr>\n  <tr><th width=150>{L_Tag}</th><th>{ally_tag}</th><!-- IF ally_image --><th rowspan=\"<!-- IF ALLY_DESCRIPTION -->5<!-- ELSE -->4<!-- ENDIF -->\" valign=\"top\"><img src=\"{D_SN_HTTP_AVATAR}/ally_{ally_id}.png\"></th><!-- ENDIF --></tr>\n  <tr><th>{L_Name}</th><th>{ally_name}</th></tr>\n  <tr><th>{L_Main_Page}</th><th><a href=\"{ally_web}\">{ally_web}</a></th></tr>\n  <tr>\n    <th>{L_Members}</th>\n    <th>\n      {ALLY_MEMBERS}<!-- IF ! EXTERNAL -->&nbsp;<a href=\"alliance.php?mode=memberslist\" class=\"fr\">[ {L_Members_list} ]</a><!-- ENDIF -->\n    </th>\n  </tr>\n  <!-- IF EXTERNAL && ! USER_ALLY_ID -->\n  <tr>\n    <th>{L_ali_req_make}</th>\n    <th><a href=\"alliance.php?mode=apply&a={ally_id}\">{L_Click_writerequest}</a></th>\n  </tr>\n  <!-- ENDIF -->\n  <!-- IF ALLY_DESCRIPTION --><tr><th colspan=\"{$COLSPAN2}\">{ALLY_DESCRIPTION}</th></tr><!-- ENDIF -->\n</table>\n\n<!-- IF ! EXTERNAL -->\n<table width=519>\n  <tr>\n    <td class=c colspan=2>\n      <span class=\"fl\">{L_ali_info_internal}</span>\n    </td>\n  </tr>\n  <tr><th width=150>{L_Range}</th><th>{range}</th></tr>\n  <!-- IF MANAGE_REQUESTS --><tr><th>{L_ali_req_check}</th><th><a href=\"alliance.php?mode=admin&edit=requests\"><!-- IF ALLY_REQUESTS -->{L_ali_req_requestCount}:&nbsp;{ALLY_REQUESTS}<!-- ELSE -->{L_ali_req_emptyList}<!-- ENDIF --></a></th></tr><!-- ENDIF -->\n  <!-- IF MASS_MAIL --><tr><th>{L_Circular_message}</th><th><a href=\"alliance.php?mode=circular\">{L_Send_circular_mail}</a></th></tr><!-- ENDIF -->\n  <!-- IF ! ALLY_ADMIN -->\n  <tr>\n    <th>{L_ali_info_leave}</th>\n    <th>\n      <form action=\"alliance.php\" method=\"post\">\n        <input type=\"checkbox\" name=\"ali_info_leave_confirm\" value=\"1\"> {L_ali_info_leave}\n        <input type=\"hidden\" name=\"mode\" value=\"exit\">\n        <input type=\"submit\" value=\"{L_sys_confirm}\">\n      </form>\n    </th>\n  </tr>\n  <!-- ENDIF -->\n  <!-- IF ALLY_TEXT --><tr><th colspan=2>{ALLY_TEXT}</th></tr><!-- ENDIF -->\n</table>\n<!-- ENDIF -->\n\n<table width=519>\n  <tr>\n    <td class=c colspan=3>\n      <span class=\"fl\">{L_ali_dip_title}</span>\n      <!-- IF ALLY_NEGOTIATE --><a class=\"fr\" href=\"alliance.php?mode=admin&edit=diplomacy\">[ {L_ali_dip_negotiate} ]</a><!-- ENDIF -->\n    </td>\n  </tr>\n  <!-- BEGIN relation -->\n  <tr>\n    <th width=120>{relation.TIME}</th>\n    <th>{relation.NAME}</th>\n    <th width=100>{relation.RELATION}</th>\n  </tr>\n  <!-- BEGINELSE relation -->\n  <tr><th>\n    {L_ali_dip_relation_none}\n  </th></tr>\n  <!-- END relation -->\n</table>\n"
  },
  {
    "path": "design/templates/OpenGame/ali_members.tpl.html",
    "content": "<!-- IF ADMIN_MODE -->\n  <!-- DEFINE $COLSPAN = 9 -->\n<!-- ELSE -->\n  <!-- DEFINE $COLSPAN = 8 -->\n<!-- ENDIF -->\n<h1>{L_Members_list} / {L_ali_sys_totalMembers}: {memberCount}</h1>\n\n<!-- IF ADMIN_MODE -->\n<a class=\"button_pseudo link\" href=\"alliance.php?mode=admin\">{L_ali_adm_return}</a><br/>\n<!-- ENDIF -->\n\n<table>\n  <tr class=\"c_c\">\n    <th>{L_Number}</th>\n    <th><a href=\"alliance.php?mode={mode}&sort1=1&sort2={s}\">{L_ali_sys_memberName}</a></th>\n    <th><img src=\"design/images/icon_mail.gif\" border=0 alt=\"{L_sys_write_message}\"></th>\n    <th><a href=\"alliance.php?mode={mode}&sort1=0&sort2={s}\">{L_ali_adm_rights_rank_name}</a></th>\n    <th><a href=\"alliance.php?mode={mode}&sort1=2&sort2={s}\">{L_ali_sys_points}</a></th>\n    <th><a href=\"alliance.php?mode={mode}&sort1=3&sort2={s}\">{L_sys_coordinates}</a></th>\n    <th><a href=\"alliance.php?mode={mode}&sort1=4&sort2={s}\">{L_ali_sys_joined}</a></th>\n    <th><a href=\"alliance.php?mode={mode}&sort1=5&sort2={s}\">{onlineMessage}</a></th>\n    <!-- IF ADMIN_MODE -->\n    <th><img src=\"{I_abort}\" border=0 alt=\"{L_ali_adm_kick}\" title=\"{L_ali_adm_kick}\"></th>\n    <!-- ENDIF -->\n  </tr>\n\n  <!-- BEGIN member -->\n    <tr>\n      <th>{member.COUNT}</th>\n      <th>{member.USER_NAME}</th>\n      <th><a href=\"messages.php?mode=write&id={member.USER_ID}\"><img src=\"design/images/icon_mail.gif\" border=0 alt=\"{L_sys_write_message}\"></a></th>\n      <th>{member.RANK}</th>\n      <th>{member.POINTS}</th>\n      <th><a href=\"galaxy.php?mode=3&galaxy={member.GALAXY}&system={member.SYSTEM}\">[{member.GALAXY}:{member.SYSTEM}:{member.PLANET}]</a></th>\n      <th>{member.REGISTER_TIME}</th>\n      <th>{member.ONLINE}</th>\n      <!-- IF ADMIN_MODE -->\n        <th>\n          <!-- IF member.SHOW_KICK -->\n            <a href=\"alliance.php?mode=admin&edit=members&kick={member.USER_ID}\" alt=\"{L_ali_adm_kick}\" title=\"{L_ali_adm_kick}\"\n              onclick=\"return confirm('{L_ali_adm_kick_confirm}');\"><img src=\"{I_abort}\" border=0 ></a>\n          <!-- ELSE -->\n            &nbsp;\n          <!-- ENDIF -->\n        </th>\n      <!-- ENDIF -->\n    </tr>\n  <!-- END member -->\n</table>\n\n<a class=\"button_pseudo link\" href=\"alliance.php\">{L_ali_sys_main_page}</a>\n"
  },
  {
    "path": "design/templates/OpenGame/ali_request.tpl.html",
    "content": "<br />\n<form action=\"alliance.php?mode=apply&a={allyid}\" method=POST>\n<table width=519>\n  <tr><td class=c>{L_ali_req_title}</td></tr>\n  <tr>\n    <th><span class=\"fl\">{L_ali_req_text}</span><span class=\"fr\"><span id=\"cntChars\">{chars_count}</span> / 6000 {L_ali_req_characters}</span><br>\n    <textarea name=\"text\" cols=60 rows=10 onkeyup=\"cntchar(6000)\">{text_apply}</textarea><br>\n  </tr>\n  <tr><th><span class=\"fl\"><input type=\"reset\" value=\"{L_sys_reset}\"></span><span class=\"fr\"><input type=submit value=\"{L_sys_send}\"></span></th></tr>\n</table>\n</form>\n"
  },
  {
    "path": "design/templates/OpenGame/ali_request_waiting.tpl.html",
    "content": "<br />\n<form action=\"\" method=POST>\n<table width=519>\n  <tr><td class=c>{L_ali_req_title}</td></tr>\n  <tr><th>{request_text}</th></tr>\n  <tr><th><input type=submit name=\"bcancel\" value=\"{L_ali_req_cancel}\"></th></tr>\n</table>\n</form>"
  },
  {
    "path": "design/templates/OpenGame/ali_search.tpl.html",
    "content": "<br />\n<table width=\"519\">\n  <tr>\n    <th>{L_ali_search_title}</th>\n    <th><form action=\"alliance.php?mode=search\" method=\"POST\">\n      <input type=\"text\" name=\"searchtext\" value=\"{SEARCH_TEXT}\" size=50>&nbsp;&nbsp;<input type=\"submit\" value=\"{L_ali_search_action}\">\n    </form></th>\n  </tr>\n  <tr>\n    <th colspan=\"2\" class=\"c_c\">\n      {L_ali_search_tip}<br/>\n      <a href=\"stat.php?type=0&who=2&range=1\" class=\"link warning\">{L_ali_search_show_all}</a>\n    </th>\n  </tr>\n  <tr><td colspan=\"2\" class=\"c\"></td></tr>\n</table>\n\n<!-- IF SEARCH_TEXT || .alliances -->\n<br />\n<table width=519>\n  <tr class=\"c_c\">\n    <th>{L_ali_sys_tag}</th>\n    <th>{L_ali_sys_name}</th>\n    <th>{L_ali_sys_members}</th>\n    <th>{ Разница }</th>\n    <th>{ Рейт }</th>\n    <th>{L_ali_req_make}</th>\n  </tr>\n\n  <!-- BEGIN alliances -->\n    <tr class=\"c_c\">\n      <!-- IF alliances.ID == -1 -->\n      <td colspan=\"6\" class=\"subheader info\">{L_ally_alliances_recommended}</td>\n      <!-- ELSE -->\n      <td><a href=\"alliance.php?mode=ainfo&a={alliances.ID}\" class=\"link\">{alliances.TAG}</a></td>\n      <td><a href=\"alliance.php?mode=ainfo&a={alliances.ID}\" class=\"link\">{alliances.NAME}</a></td>\n      <td class=\"c_r\">{alliances.MEMBERS}</td>\n      <td class=\"c_r\">{alliances.DIFF|num|format}</td>\n      <td class=\"c_r\">{alliances.RATE}</td>\n      <td>\n        <!-- IF alliances.NO_REQUESTS -->\n          <font color=\"red\">{L_ali_req_not_allowed}</font>\n        <!-- ELSE -->\n          <a href=\"alliance.php?mode=apply&a={alliances.ID}\" class=\"link\">{L_ali_req_make}</a>\n        <!-- ENDIF -->\n      </td>\n      <!-- ENDIF -->\n    </tr>\n  <!-- BEGINELSE alliances -->\n    <tr><th colspan=\"6\">{L_ali_search_result_none}</th></tr>\n  <!-- END alliances -->\n</table>\n<!-- ENDIF -->\n\n<!-- INCLUDE page_hint -->\n"
  },
  {
    "path": "design/templates/OpenGame/announce.tpl.html",
    "content": "<h1>{PAGE_HEADER}</h1>\n\n<!-- IF ANNOUNCE_ID && MODE == '' -->\n<h2><a class=\"link\" href=\"announce.php\">{L_news_all}</a></h2>\n<!-- ENDIF -->\n\n<!-- IF AUTHLEVEL >= 3 -->\n\n<!-- IF (MODE == 'edit' || MODE == 'copy') && ANNOUNCE_ID != 0 -->\n<!-- DEFINE $HIDE_NEWS_ADD_FORM = '' -->\n<!-- ELSE -->\n<!-- DEFINE $HIDE_NEWS_ADD_FORM = 'display: none;' -->\n<input type=\"button\" value=\"{L_news_add}\" id=\"js_news_show_add_form\" />\n<!-- ENDIF -->\n\n<script type=\"text/javascript\">\n  $(document).on('click', '#js_news_show_add_form', function () {\n    $(this).hide();\n    $(\"#js_news_add_form\").show();\n  });\n\n  // $(document).ready(function() {\n  //   $(\"#announceText\").val({strAnnounceJS});\n  // });\n</script>\n\n<div id=\"js_news_add_form\" style=\"{$HIDE_NEWS_ADD_FORM}\">\n  <br>\n  <form action=\"announce.php\" method=\"post\" name=\"fForm\">\n    <table width=\"519\">\n      <tr><td class=\"c\" colspan=2><!-- IF MODE == 'edit' -->{L_news_edit}<!-- ELSEIF MODE == 'copy' -->{L_news_copy}<!-- ELSE -->{L_news_add}<!-- ENDIF --></td></tr>\n      <tr>\n        <td>{L_news_date}</td>\n        <td>\n          <input type=\"text\" class=\"fl\" name=\"dtDateTime\" maxlength=\"19\" value=\"{tsTimeStamp}\">\n          <span class=\"fr\"><!-- IF MODE == 'edit' -->{L_news_mode_edit}<!-- ELSEIF MODE == 'copy' -->{L_news_mode_copy}<!-- ELSE -->{L_news_new}<!-- ENDIF --></span>\n          <input type=\"hidden\" name=\"id\" value=\"{ID}\">\n          <input type=\"hidden\" name=\"mode\" value=\"{MODE}\">\n        </td>\n      </tr>\n\n      <tr>\n        <td valign=\"top\">{L_news_announce}</td>\n        <td><textarea name=\"text\" cols=40 rows=5 id=\"announceText\">{strAnnounce}</textarea></td>\n      </tr>\n      <tr>\n        <td>{L_news_detail_url}</td>\n        <td><input type=\"text\" name=\"detail_url\" style=\"width: 330px\" maxlength=\"250\" value=\"{DETAIL_URL}\"></td>\n      </tr>\n\n      <tr>\n        <td>\n          {L_survey}<br />\n          <!-- IF MODE == 'edit' --><span class=\"error\">{L_survey_questions_hint_edit}</span><!-- ENDIF -->\n        </td>\n        <td><input type=\"text\" name=\"survey_question\" style=\"width: 330px\" maxlength=\"250\" value=\"{SURVEY_QUESTION}\"></td>\n      </tr>\n      <tr>\n        <td valign=\"top\">\n          {L_survey_questions}<br />\n          {L_survey_questions_hint}\n        </td>\n        <td><textarea name=\"survey_answers\" cols=40 rows=5>{SURVEY_ANSWERS}</textarea></td>\n      </tr>\n      <tr>\n        <td>{L_survey_until}</td>\n        <td>\n          <input type=\"text\" class=\"fl\" name=\"survey_until\" maxlength=\"19\" value=\"{SURVEY_UNTIL}\">\n        </td>\n      </tr>\n\n      <tr>\n        <td colspan=\"2\" align=\"center\">\n          <span class=\"fl\"><input type=\"checkbox\" id=\"news_mass_mail\" name=\"news_mass_mail\" value=\"1\">&nbsp;<label for=\"news_mass_mail\">{L_news_mass_mail}</label></span>\n          <input class=\"fr\" type=submit value=\"<!-- IF MODE == 'edit' -->{L_news_edit}<!-- ELSEIF MODE == 'copy' -->{L_news_copy}<!-- ELSE -->{L_news_add}<!-- ENDIF -->\">\n        </td>\n      </tr>\n    </table>\n  </form>\n</div>\n<!-- ENDIF -->\n\n<br>\n<table width=\"519\">\n  <!-- IF .announces && PAGER_MESSAGES -->\n  <tr class=\"c_c\">\n    <td class=\"subheader\">\n      {PAGER_MESSAGES}\n    </td>\n  </tr>\n  <!-- ENDIF -->\n\n  <tr><td class=\"c\"><div class=\"fl\">{L_news_title}</div><div class=\"fr\">{L_news_total} {NEWS_COUNT}</div></td></tr>\n  <!-- DEFINE $NEWS_FULL = true -->\n  <!-- INCLUDE news_list -->\n\n  <!-- IF .announces && PAGER_MESSAGES -->\n  <tr class=\"c_c\">\n    <td class=\"subheader\">\n      {PAGER_MESSAGES}\n    </td>\n  </tr>\n  <!-- ENDIF -->\n</table>\n<br>\n"
  },
  {
    "path": "design/templates/OpenGame/artifacts.tpl.html",
    "content": "<!--<h1>{PAGE_HEADER}</h1>-->\n\n<table <!-- IF PAGE_HEADER -->table_title=\"{PAGE_HEADER}\"<!-- ENDIF --> style=\"width: 530px\">\n  <tr>\n    <td colspan=\"2\" class=\"c\">{L_sys_dark_matter}</td>\n  </tr>\n\n  <tr>\n    <th width=\"120\" ><img src=\"design/images/DMaterie.jpg\" width=\"120\" height=\"120\"></th>\n    <th width=\"314\" >\n      <p align=\"justify\">{L_sys_dark_matter_desc}</p>\n      <a href=\"dark_matter.php\" id=\"off_get_dark_matter\" class=\"link\">{L_sys_dark_matter_obtain_header}</a>\n    </th>\n  </tr>\n\n  <tr>\n    <td width=\"535\" colspan=\"2\" class=\"c\">{L_tech[1000]}</td>\n  </tr>\n\n  <!-- BEGIN artifact -->\n    <tr id=\"{artifact.ID}\">\n      <th width=120>\n        <a href=\"infos.php?gid={artifact.ID}\" class=\"link\">{artifact.NAME}</a>\n        <div class=\"tech_image\" unit_id=\"{artifact.ID}\"><img src=\"{I_[artifact.ID]}\" align=\"top\" width=\"120\" height=\"120\" /></div>\n        <!-- IF artifact.LEVEL -->\n          <div><a href=\"artifacts.php?action={D_ACTION_USE}&unit_id={artifact.ID}\" class=\"warning link\">{L_art_use}</a></div>\n        <!-- ENDIF -->\n      </th>\n\n      <th align=justify>\n        {artifact.DESCRIPTION}<br><br>\n        <div align=\"center\">\n          <div class=\"positive\" align=\"center\">{artifact.BONUS}&nbsp;{artifact.EFFECT}</div><br />\n          <div>{L_sys_quantity}:&nbsp;{artifact.LEVEL}<!-- IF artifact.LEVEL_MAX -->/{artifact.LEVEL_MAX}<!-- ENDIF --></div>\n          <!-- IF artifact.LEVEL_MAX && (artifact.LEVEL >= artifact.LEVEL_MAX) -->\n            <span class=\"negative\">{L_sys_quantity_maximum}</span>\n          <!-- ELSEIF artifact.CAN_BUY <= 0 -->\n            <span class=\"negative\">{L_sys_buy_for} {artifact.COST|num|format} {L_sys_dark_matter_sh} - {L_sys_eco_lack_dark_matter}</span>\n          <!-- ELSE -->\n            <a href=\"artifacts.php?action={D_ACTION_BUY}&unit_id={artifact.ID}\" class=\"link positive\">{L_sys_buy_for} {artifact.COST|num|format} {L_sys_dark_matter_sh}</a>\n          <!-- ENDIF -->\n        </div>\n      </th>\n    </tr>\n  <!-- END artifact -->\n</table>\n<!-- INCLUDE page_hint -->\n"
  },
  {
    "path": "design/templates/OpenGame/banned_body.tpl.html",
    "content": "<h2>{L_ban_title}</h2>\n<table>\n  <tr align=\"center\">\n    <td class=c>{L_ban_name}</td>\n    <td class=c>{L_ban_reason}</td>\n    <td class=c>{L_ban_from}</td>\n    <td class=c>{L_ban_to}</td>\n    <td class=c>{L_ban_by}</td>\n  </tr>\n  <!-- BEGIN banlist -->\n    <tr align=center>\n      <td class=\"c_l\"><b>{banlist.USER_NAME}</b></td>\n      <td class=\"c_l\"><b>{banlist.REASON}</b></td>\n      <td class=b><b>{banlist.FROM}</b></td>\n      <td class=b><b>{banlist.UNTIL}</b></td>\n      <td class=\"c_l\"><b>{banlist.ISSUER_NAME}</b></td>\n    </tr>\n  <!-- END banlist -->\n  <tr><td class=\"c\" colspan=\"5\">{L_ban_banned}{BANNED_COUNT}</td></tr>\n</table>\n"
  },
  {
    "path": "design/templates/OpenGame/blitz_register.tpl.html",
    "content": "<h1>{PAGE_HEADER}</h1>\n\n<br />\n<table>\n  <tr>\n    <!-- IF REGISTRATION_DISCLOSURE_NAMES -->\n    <th class=\"c_l\">\n      {Место}\n    </th>\n    <th class=\"c_l\">\n      {Очки}\n    </th>\n    <th class=\"c_l\">\n      {L_sys_blitz_registration_player_blitz_name}\n    </th>\n    <th class=\"c_l\">\n      {Награда ТМ}\n    </th>\n    <!-- ENDIF -->\n    <th class=\"c_l\">{L_sys_player}</th>\n  </tr>\n\n  <!-- BEGIN registrations -->\n  <tr>\n    <!-- IF REGISTRATION_DISCLOSURE_NAMES -->\n    <td class=\"c_l\">\n      {registrations.BLITZ_PLACE}\n    </td>\n    <td class=\"c_l\">\n      {registrations.BLITZ_POINTS}\n    </td>\n    <td class=\"c_l\">\n      {registrations.BLITZ_NAME}\n    </td>\n    <td class=\"c_l\">\n      {registrations.BLITZ_REWARD_DARK_MATTER}\n    </td>\n    <!-- ENDIF -->\n    <td class=\"c_l\">\n      {registrations.NAME}\n    </td>\n  </tr>\n  <!-- BEGINELSE -->\n  <tr>\n    <td>\n      {L_sys_blitz_registration_no_users}\n    </td>\n  </tr>\n  <!-- END registrations -->\n</table>\n<br />\n<!-- IF REGISTRATION_OPEN -->\n  <!-- IF PLAYER_REGISTERED -->\n    <form action=\"\" method=\"post\">\n      <input type=\"submit\" name=\"register_me_not\" value=\"{L_sys_blitz_registration_player_register_un}\" />\n    </form>\n  <!-- ELSE -->\n    <form action=\"\" method=\"post\">\n      <input type=\"submit\" name=\"register_me\" value=\"{L_sys_blitz_registration_player_register}\" /><br />\n      {L_sys_blitz_registration_price} {C_game_blitz_register_price} {L_sys_metamatter}\n    </form>\n  <!-- ENDIF -->\n<!-- ELSE -->\n  {L_sys_blitz_registration_closed}\n  <br />\n  <!-- IF REGISTRATION_SHOW_LOGIN && PLAYER_REGISTERED -->\n    <h2>\n      {L_sys_blitz_registration_player_name} {BLITZ_NAME}<br />\n      {L_sys_blitz_registration_player_password} {BLITZ_PASSWORD}<br />\n      <a href=\"http://blitz.supernova.ws/\" class=\"link\">{L_sys_blitz_registration_server_link} http://blitz.supernova.ws/</a>\n    </h2>\n  <!-- ENDIF -->\n  <h2><a href=\"stat.php?source=blitz\" class=\"link\">{L_sys_blitz_registration_view_stat}</a></h2>\n<!-- ENDIF -->\n\n\n<!-- IF USER_AUTHLEVEL >= 3 -->\n<div class=\"blitz_admin\">\n  <!-- IF GAME_BLITZ -->\n  <div>\n    <form action=\"\" method=\"post\">\n      <textarea rows=\"\" cols=\"\" name=\"generated_string\"></textarea>\n      <input type=\"submit\" name=\"import_generated\" value=\"{L_sys_blitz_registration_player_import_generated}\" />\n    </form>\n  </div>\n  <!-- ELSE -->\n  <div>\n    <textarea rows=\"\" cols=\"\">{BLITZ_GENERATED}</textarea>\n    <form action=\"\" method=\"post\">\n      <input type=\"submit\" name=\"generate\" value=\"{L_sys_blitz_registration_player_generate}\" />\n    </form>\n  </div>\n  <!-- ENDIF -->\n\n  <div>\n    {Результат раунда}\n    <form action=\"\" method=\"post\">\n      <textarea rows=\"\" cols=\"\" name=\"blitz_result_string\">{BLITZ_RESULT}</textarea>\n      <!-- IF ! GAME_BLITZ -->\n      <input type=\"submit\" name=\"import_result\" value=\"{Импортировать результаты}\" />\n      <!-- ENDIF -->\n    </form>\n  </div>\n\n  <!-- IF ! GAME_BLITZ -->\n  <div>\n    <form action=\"\" method=\"post\">\n      {Призовой фонд} <input type=\"text\" name=\"blitz_prize_dark_matter\" value=\"{BLITZ_PRIZE_DARK_MATTER}\" /><br />\n      {Количество призеров} <input type=\"text\" name=\"blitz_prize_places\" value=\"{BLITZ_PRIZE_PLACES}\" /><br />\n      <input type=\"submit\" name=\"prize_calculate\" value=\"{Начислить призы и медали}\" />\n    </form>\n  </div>\n  <!-- ENDIF -->\n</div>\n<!-- ENDIF -->\n\n<!-- INCLUDE page_hint -->\n"
  },
  {
    "path": "design/templates/OpenGame/buddy.tpl.html",
    "content": "<h1>{PAGE_HEADER}</h1>\n\n<br />\n<table>\n  <tr>\n    <th class=\"c_l\">{L_sys_player}/{L_sys_alliance}</th>\n    <th class=\"c_l\">{L_sys_capital}</th>\n    <th class=\"c_l\" width=\"300px\">{L_buddy_status}</th>\n    <th class=\"c_l\" width=\"55px\">\n      <img src=\"design/images/icon_mail.gif\" alt=\"{L_sys_writeMessage}\" title=\"{L_sys_writeMessage}\">\n      <img src=\"design/images/r1.png\" alt=\"{L_sys_delete}\" title=\"{L_sys_delete}\" border=\"0\">\n      <img src=\"design/images/r4.png\" alt=\"{L_buddy_request_accept}\" title=\"{L_buddy_request_accept}\" border=\"0\">\n    </th>\n  </tr>\n\n  <!-- BEGIN buddy -->\n  <tr>\n    <td class=\"c_l\">\n      {buddy.BUDDY_USER_NAME}<br />\n      <!-- IF buddy.BUDDY_ALLY_ID -->\n        {buddy.BUDDY_ALLY_NAME}\n      <!-- ELSE -->\n        &nbsp;\n      <!-- ENDIF -->\n    </td>\n    <td class=\"c_l\">\n      <!-- IF buddy.BUDDY_PLANET_NAME && buddy.BUDDY_PLANET_GALAXY -->\n        <a href=\"galaxy.php?mode=3&galaxy={buddy.BUDDY_PLANET_GALAXY}&system={buddy.BUDDY_PLANET_SYSTEM}&planet={buddy.BUDDY_PLANET_PLANET}\">[{buddy.BUDDY_PLANET_GALAXY}:{buddy.BUDDY_PLANET_SYSTEM}:{buddy.BUDDY_PLANET_PLANET}]<br/>{buddy.BUDDY_PLANET_NAME}</a>\n      <!-- ELSE -->\n        <span class=\"notice\">{L_sys_na}</span>\n      <!-- ENDIF -->\n    </td>\n    <td class=\"c_l\">\n      <!-- IF buddy.BUDDY_ACTIVE -->\n        <span class=\"ok\">{L_buddy_status_active}</span><br />\n        <!-- IF buddy.BUDDY_ONLINE < 5 -->\n          <span class=\"ok\">{L_sys_online}</span>\n        <!-- ELSEIF buddy.BUDDY_ONLINE < 15 -->\n          <span class=\"notice\">{L_sys_lessThen15min}</span>\n        <!-- ELSEIF buddy.BUDDY_ONLINE < 1440 -->\n          <span class=\"warning\">{L_sys_offline}</span>\n        <!-- ELSE -->\n          <span class=\"error\">{L_sys_offline}</span>\n        <!-- ENDIF -->\n\n      <!-- ELSE -->\n        <!-- IF buddy.BUDDY_INCOMING -->\n          <!-- IF buddy.BUDDY_DENIED -->\n            <span class=\"error\">{L_buddy_status_incoming_denied}</span>\n          <!-- ELSE -->\n            <span class=\"warning\">{L_buddy_status_incoming_waiting}</span>\n          <!-- ENDIF -->\n        <!-- ELSE -->\n          <!-- IF buddy.BUDDY_DENIED -->\n            <span class=\"error\">{L_buddy_status_outcoming_denied}</span>\n          <!-- ELSE -->\n            <span class=\"notice\">{L_buddy_status_outcoming_waiting}</span>\n          <!-- ENDIF -->\n        <!-- ENDIF -->\n        <br />\n        {buddy.BUDDY_REQUEST}\n      <!-- ENDIF -->\n    </td>\n    <td class=\"c_l\">\n      <a href=\"messages.php?mode=write&id={buddy.BUDDY_USER_ID}\"><img src=\"design/images/icon_mail.gif\" alt=\"{L_sys_writeMessage}\" title=\"{L_sys_writeMessage}\"></a>\n      <!-- IF ! (buddy.BUDDY_INCOMING && buddy.BUDDY_DENIED) -->\n        <a href=\"buddy.php?mode=delete&buddy_id={buddy.BUDDY_ID}\"><img src=\"design/images/r1.png\" alt=\"{L_sys_delete}\" title=\"{L_sys_delete}\" border=\"0\"></a>\n      <!-- ENDIF -->\n      <!-- IF buddy.BUDDY_INCOMING && ! buddy.BUDDY_ACTIVE && ! buddy.BUDDY_DENIED -->\n        <a href=\"buddy.php?mode=accept&buddy_id={buddy.BUDDY_ID}\"><img src=\"design/images/r4.png\" alt=\"{L_buddy_request_accept}\" title=\"{L_buddy_request_accept}\" border=\"0\"></a>\n      <!-- ENDIF -->\n    </td>\n  </tr>\n  <!-- BEGINELSE -->\n  <tr>\n    <td colspan=\"4\">\n      {L_buddy_request_none}\n    </td>\n  </tr>\n  <!-- END buddy -->\n\n  <tr>\n    <th class=\"c_l\" colspan=\"5\">\n      {L_buddy_request_write_header}\n      <span class=\"fr\">{bud_req_toUser}&nbsp;{username}&nbsp;(<span id=\"cntChars\">0</span> / 2000 {sys_characters})</span>\n    </th>\n  </tr>\n  <form action=\"buddy.php\" method=\"post\">\n    <tr>\n      <td class=\"c_l\">\n        {L_buddy_request_player_name}\n      </td>\n      <td colspan=\"4\" class=\"c_l\">\n        <input type=\"hidden\" name=\"request_user_id\" value=\"{REQUEST_USER_ID}\" />\n        <input type=\"text\" name=\"request_user_name\" size=\"60\" maxlength=\"64\" value=\"{REQUEST_USER_NAME}\" /><br />\n      </td>\n    </tr>\n\n    <tr>\n      <td class=\"c_l\">\n        {L_buddy_request_text}\n      </td>\n      <td colspan=\"4\" class=\"c_l\">\n        <input type=\"text\" name=\"request_text\" size=\"60\" maxlength=\"255\" value=\"{L_buddy_request_text_default}\" />\n      </td>\n    </tr>\n\n    <tr>\n      <th class=\"c_c\" colspan=\"5\">\n        <input type=\"submit\" value=\"{L_sys_send}\">\n      </th>\n    </tr>\n  </form>\n</table>\n\n<!-- INCLUDE page_hint -->\n"
  },
  {
    "path": "design/templates/OpenGame/buildings_builds.tpl.html",
    "content": "<h1>{PAGE_HEADER}</h1>\n\n<script type=\"text/javascript\">\nvar production = [];\nvar require = [];\nvar grants = [];\nvar STACKABLE = Math.intVal('{STACKABLE}');\nvar TEMPORARY = Math.intVal('{TEMPORARY}');\nvar CAN_AUTOCONVERT = Math.intVal('{CAN_AUTOCONVERT}');\nvar MARKET_AUTOCONVERT_COST = Math.intVal('{MARKET_AUTOCONVERT_COST}');\nvar DARK_MATTER = Math.intVal('{DARK_MATTER}');\n\nvar ALLY_ID = Math.intVal('{ALLY_ID}');\nvar PLANET_ID = Math.intVal('{PLN_ID}');\n\nvar que_id = Math.intVal('{QUE_ID}');\n\nvar UNIT_TECHNOLOGIES = Math.intVal('{D_UNIT_TECHNOLOGIES}');\n\n$.extend(language, {\n  eco_que_clear_dialog_title: '{LA_eco_que_clear_dialog_title}',\n  eco_que_clear_dialog_text: '{LA_eco_que_clear_dialog_text}',\n\n  eco_que_artifact_dialog_title: '{LA_eco_que_artifact_dialog_title}',\n  eco_que_artifact_dialog_text: '{LA_eco_que_artifact_dialog_text}',\n\n  level: '{LA_level}',\n  level_short: '{LA_sys_level_short}',\n  bld_create: '{LA_bld_create}',\n  bld_destroy: '{LA_bld_destroy}',\n  construction_time: '{LA_ConstructionTime}',\n  eco_price: '{LA_eco_price}',\n  eco_left: '{LA_eco_left}',\n  eco_left_fleet: '{LA_sys_fleet_and}',\n\n  sys_resources: '{LA_sys_resources}',\n  sys_metal: '{LA_sys_metal}',\n  sys_crystal: '{LA_sys_crystal}',\n  sys_deuterium: '{LA_sys_deuterium}',\n  sys_energy: '{LA_sys_energy}',\n  sys_dark_matter: '{LA_sys_dark_matter}',\n  energy: '{LA_sys_energy}',\n  shield: '{LA_sys_ship_shield}',\n  armor: '{LA_sys_ship_armour}',\n  weapon: '{LA_sys_ship_weapon}',\n  speed: '{LA_sys_ship_speed}',\n  consumption: '{LA_sys_ship_consumption}',\n  capacity: '{LA_sys_ship_capacity}',\n\n  'sys_expeditions': '{LA_sys_expeditions}',\n  'sys_colonies': '{LA_sys_colonies}',\n\n  'No_requirements': '{LA_No_requirements}',\n\n  'eco_bld_unit_info_extra_show': '{LA_eco_bld_unit_info_extra_show}',\n  'eco_bld_unit_info_extra_hide': '{LA_eco_bld_unit_info_extra_hide}',\n  'eco_bld_unit_info_extra_none': '{LA_eco_bld_unit_info_extra_none}',\n\n  'eco_bld_autoconvert_explain': '{LA_eco_bld_autoconvert_explain}'.replace(/\\\\r\\\\n/g, '\\r\\n'),\n  'eco_bld_autoconvert_dark_matter_none': '{LA_eco_bld_autoconvert_dark_matter_none}'.replace(/\\\\r\\\\n/g, '\\r\\n').format(sn_format_number(MARKET_AUTOCONVERT_COST - DARK_MATTER)),\n  'eco_bld_autoconvert_confirm': '{LA_eco_bld_autoconvert_confirm}'.replace(/\\\\r\\\\n/g, '\\r\\n').format(sn_format_number(MARKET_AUTOCONVERT_COST, 0)),\n});\n\nplanet = {\n  fleet_own: Math.intVal('{FLEET_OWN_COUNT}'),\n\n  metal: Math.floatVal('{METAL}'),\n  crystal: Math.floatVal('{CRYSTAL}'),\n  deuterium: Math.floatVal('{DEUTERIUM}'),\n  dark_matter: Math.floatVal('{DARK_MATTER}'),\n\n  metal_incoming: Math.floatVal('{METAL_INCOMING}'),\n  crystal_incoming: Math.floatVal('{CRYSTAL_INCOMING}'),\n  deuterium_incoming: Math.floatVal('{DEUTERIUM_INCOMING}'),\n  dark_matter_incoming: 0,\n\n  que_has_place: Math.intVal('{QUE_HAS_PLACE}'),\n  que_has_fields: Math.intVal('{QUE_HAS_FIELDS}'),\n  fields_free: Math.intVal('{FIELDS_FREE})'),\n};\n\n<!-- BEGIN production -->\n  <!-- IF production.S_FIRST_ROW -->\n  var production_id_first = Math.intVal('{production.ID}');\n  <!-- ENDIF -->\n\n  <!-- IF .production.require -->\n  require[Math.intVal('{production.ID}')] = [\n    <!-- BEGIN require -->\n    {\n      name: '{require.NAME}',\n      requerements_met: Math.intVal('{require.REQUEREMENTS_MET}'),\n      level_require: Math.intVal('{require.LEVEL_REQUIRE}'),\n      level: Math.intVal('{require.LEVEL}'),\n      level_basic: Math.intVal('{require.LEVEL_BASIC}'),\n      level_bonus: Math.intVal('{require.LEVEL_BONUS}'),\n      id: Math.intVal('{require.ID}'),\n    },\n    <!-- END require -->\n  ];\n  <!-- ENDIF -->\n\n  <!-- IF .production.grants -->\n  grants[Math.intVal('{production.ID}')] = [\n    <!-- BEGIN grants -->\n    {\n      name: '{grants.NAME}',\n      requerements_met: Math.intVal('{grants.REQUEREMENTS_MET}'),\n      level_require: Math.intVal('{grants.LEVEL_REQUIRE}'),\n      level: Math.intVal('{grants.LEVEL}'),\n      level_basic: Math.intVal('{grants.LEVEL_BASIC}'),\n      level_bonus: Math.intVal('{grants.LEVEL_BONUS}'),\n      id: Math.intVal('{grants.ID}'),\n    },\n    <!-- END grants -->\n  ];\n  <!-- ENDIF -->\n\n  production[Math.intVal('{production.ID}')] = {\n    id: Math.intVal('{production.ID}'),\n    name: '{production.NAME}',\n    'unit_type': '{production.UNIT_TYPE}',\n    'image': '{I_[production.ID]}',\n    level: Math.intVal('{production.LEVEL}'),\n    level_bonus: '{production.LEVEL_BONUS}',\n    change: Math.floorVal('{production.CHANGE}'),\n    description: '{production.DESCRIPTION}',\n    unit_busy: '{production.UNIT_BUSY}',\n    can_build: '{production.CAN_BUILD}',\n    can_autoconvert: Math.intVal('{production.CAN_AUTOCONVERT}'),\n    autoconvert_amount: Math.intVal('{production.AUTOCONVERT_AMOUNT}'),\n\n    metal: '{production.METAL}',\n    crystal: '{production.CRYSTAL}',\n    deuterium: '{production.DEUTERIUM}',\n    dark_matter: '{production.DARK_MATTER}',\n    time: '{production.TIME}',\n    time_seconds: Math.intVal('{production.TIME_SECONDS}'),\n    build_can: '{production.BUILD_CAN}',\n    build_result: '{production.BUILD_RESULT}',\n\n    destroy_metal: '{production.DESTROY_METAL}',\n    destroy_crystal: '{production.DESTROY_CRYSTAL}',\n    destroy_deuterium: '{production.DESTROY_DEUTERIUM}',\n    destroy_time: '{production.DESTROY_TIME}',\n    destroy_can: '{production.DESTROY_CAN}',\n    destroy_result: '{production.DESTROY_RESULT}',\n\n    map_is_resource: '{production.MAP_IS_RESOURCE}',\n\n    <!-- IF .production.resource -->\n    resource_map: [\n      <!-- BEGIN resource -->\n      {\n        'shield': ('{resource.ACTUAL_SHIELD}'),\n        'armor': ('{resource.ACTUAL_ARMOR}'),\n        'weapon': ('{resource.ACTUAL_WEAPON}'),\n        'speed': ('{resource.ACTUAL_SPEED}'),\n        'consumption': ('{resource.ACTUAL_CONSUMPTION}'),\n        'capacity': ('{resource.ACTUAL_CAPACITY}'),\n\n        'level': Math.floorVal('{resource.LEVEL}'),\n        'sys_metal': Math.floorVal('{resource.R901}'),\n        'metal_diff': ('{resource.D901}'),\n        'sys_crystal': Math.floorVal('{resource.R902}'),\n        'crystal_diff': ('{resource.D902}'),\n        'sys_deuterium': Math.floorVal('{resource.R903}'),\n        'deuterium_diff': ('{resource.D903}'),\n        'sys_energy': Math.floorVal('{resource.R904}'),\n        'energy_diff': ('{resource.D904}'),\n\n        'sys_colonies': Math.intVal('{resource.RCOLONIES_MAX}'),\n        'sys_colonies_diff': ('{resource.DCOLONIES_MAX}'),\n        'sys_expeditions': Math.intVal('{resource.REXPEDITIONS_MAX}'),\n        'sys_expeditions_diff': ('{resource.DEXPEDITIONS_MAX}'),\n      },\n      <!-- END resource -->\n    ]\n    <!-- ENDIF -->\n  };\n<!-- END production -->\n</script>\n\n<script type=\"text/javascript\" src=\"js/build_unit.min.js?{C_var_db_update}\"></script>\n\n<!-- DEFINE $QUE_ID = '{QUE_ID}' -->\n<!-- INCLUDE eco_queue -->\n\n<!-- IF $QUE_ID == QUE_RESEARCH -->\n<h2><a class=\"link\" href=\"index.php?page=techtree#tab_tech_tree_100\">{L_eco_bld_research_page_novapedia}</a></h2>\n<!-- ENDIF -->\n\n<table class=\"markup\" width=\"100%\">\n<tr>\n  <td align=\"center\" valign=\"top\">\n    <table id=\"unit_table\">\n      <tr class=\"c_c\">\n        <th>\n          {L_sys_sort}\n          <select name=\"sort_elements\" id=\"sort_elements\">\n            <!-- BEGIN sort_values -->\n            <option value=\"{sort_values.VALUE}\"<!-- IF sort_values.VALUE == SORT_OPTION --> selected<!-- ENDIF -->>{sort_values.TEXT}</option>\n            <!-- END sort_values -->\n          </select>\n          {L_sys_sort_inverse} <input type=\"checkbox\" name=\"sort_elements_inverse\" id=\"sort_elements_inverse\" value=\"1\"<!-- IF SORT_OPTION_INVERSE --> checked<!-- ENDIF -->/>\n        </th>\n      </tr>\n\n      <!-- IF ! U_opt_int_struc_vertical && $QUE_NOT_EMPTY -->\n      <tr>\n        <td class=\"c_c\">\n          <!-- INCLUDE eco_bld_queue_list -->\n        </td>\n      </tr>\n      <!-- ENDIF -->\n\n      <!-- IF SHOW_SECTORS -->\n      <tr>\n        <th class=\"c_c\">\n          <table class=\"markup noborder\" width=\"100%\">\n            <tr>\n              <th class=\"c_l\">\n                {L_bld_theyare} {L_bld_cellfree} {FIELDS_FREE}\n                (<!-- IF FIELDS_QUE != 0 --><span class=\"zero\">{FIELDS_QUE}</span>/<!-- ENDIF --><span class=\"negative\">{FIELDS_CURRENT}</span>/<span class=\"positive\">{FIELDS_MAX}</span>)\n              </th>\n              <th class=\"c_r\">\n                <!-- INCLUDE planet_sector_buy -->\n              </th>\n            </tr>\n          </table>\n        </th>\n      </tr>\n      <!-- ENDIF -->\n\n      <!-- IF METAL > 99999999999 || CRYSTAL > 9999999999 || DEUTERIUM > 9999999999 || METAL < -9999999999 || CRYSTAL < -999999999 || DEUTERIUM < -999999999-->\n        <!-- DEFINE $FONT_SIZE = '0.8em' -->\n      <!-- ELSE -->\n        <!-- DEFINE $FONT_SIZE = '1.0em' -->\n      <!-- ENDIF -->\n\n      <tr>\n        <td>\n          <div id=\"unit_info\">\n            <div id=\"unit_balance_wrapper\">\n              <div id=\"unit_balance\"></div>\n            </div>\n            <div id=\"unit_description_wrapper\">\n              <div id=\"unit_description_wrapper_cell\">\n                <div id=\"unit_info_image_outer\">\n                  <div id=\"unit_info_image_wrapper\">\n                    <img id=\"unit_info_image\" />\n                    <div id=\"unit_info_name\" class=\"a75\"></div>\n                    <span class=\"unit-icon-info-container link\" go=\"info\" unit_id=\"\" id=\"unit_info_wiki\">\n                      <div class=\"icon-info-cover\">&nbsp;</div>\n                    </span>\n                    <div id=\"unit_info_level\" class=\"a75\"></div>\n                  </div>\n                </div>\n                <div id=\"unit_description_wrapper_inner\">\n                  <div id=\"unit_info_description\"></div>\n                  <div id=\"unit_require_wrapper\">\n                    <div class=\"c_l neutral\">{L_wiki_requrements}</div>\n                    <ul id=\"unit_require\"></ul>\n                  </div>\n                  <div id=\"unit_grants_wrapper\">\n                    <div class=\"c_l neutral\">{L_wiki_grants}</div>\n                    <ul id=\"unit_grants\"></ul>\n                  </div>\n                </div>\n              </div>\n            </div>\n          </div>\n\n          <form action=\"buildings.php\" method=\"post\" id=\"form_unit\">\n            <input type=\"hidden\" id=\"page_file_name\" value=\"buildings.php\">\n\n            <input type=\"hidden\" name=\"mode\" id=\"mode\" value=\"{QUE_ID}\" />\n            <input type=\"hidden\" name=\"ally_id\" value=\"{ALLY_ID}\" />\n            <input type=\"hidden\" name=\"unit_id\" id=\"unit_id\" />\n            <input type=\"hidden\" name=\"action\" />\n\n            <!-- IF STACKABLE -->\n              <!-- DEFINE $CLASS_COST_CONTAINER = 'unit_create_stackable' -->\n            <!-- ELSE -->\n              <!-- DEFINE $CLASS_COST_CONTAINER = '' -->\n            <!-- ENDIF -->\n            <div id=\"unit_cost_container\" class=\"{$CLASS_COST_CONTAINER}\" align=\"left\">\n              <div class=\"wrap\">\n                <div id=\"unit_time_div\" align=\"center\">{STRING_BUILD_TIME}<span id=\"unit_time\"></span></div>\n                <table style=\"font-size: {$FONT_SIZE};\" id=\"unit_cost_table\" class=\"border_image_small\">\n                  <tr class=\"c_c\">\n                    <th width=45px>{L_sys_resources}</th>\n                    <th width=65>{L_eco_price}</th>\n                    <th width=65>{L_eco_left}</th>\n                    <th width=65 hide_no_fleet=\"yes\">{L_sys_fleet_and}</th>\n                  </tr>\n                  <tr id=\"unit_metal\" class=\"c_r\">\n                    <td width=45px class=\"c_c\">{L_sys_metal}</td>\n                    <td id=\"metal_price\">0</td>\n                    <td id=\"metal_left\">0</td>\n                    <td id=\"metal_fleet\" hide_no_fleet=\"yes\">0</td>\n                  </tr>\n                  <tr id=\"unit_crystal\" class=\"c_r\">\n                    <td width=45px class=\"c_c\">{L_sys_crystal}</td>\n                    <td id=\"crystal_price\">0</td>\n                    <td id=\"crystal_left\">0</td>\n                    <td id=\"crystal_fleet\" hide_no_fleet=\"yes\">0</td>\n                  </tr>\n                  <tr id=\"unit_deuterium\" class=\"c_r\">\n                    <td width=45px class=\"c_c\">{L_sys_deuterium}</td>\n                    <td id=\"deuterium_price\">0</td>\n                    <td id=\"deuterium_left\">0</td>\n                    <td id=\"deuterium_fleet\" hide_no_fleet=\"yes\">0</td>\n                  </tr>\n                  <tr id=\"unit_dark_matter\" class=\"c_r\">\n                    <td width=45px class=\"c_c\" nowrap style=\"font-size: 80%\">{L_sys_dark_matter}</td>\n                    <td id=\"dark_matter_price\">0</td>\n                    <td id=\"dark_matter_left\">0</td>\n                    <td id=\"dark_matter_fleet\" hide_no_fleet=\"yes\">0</td>\n                  </tr>\n                </table>\n              </div>\n            </div>\n\n            <!-- IF STACKABLE -->\n              <!-- DEFINE $CREATE_CLASS = 'unit_create_stackable' -->\n            <!-- ELSE -->\n              <!-- DEFINE $CREATE_CLASS = 'unit_create_level' -->\n            <!-- ENDIF -->\n\n            <!-- IF CAN_AUTOCONVERT -->\n              <!-- DEFINE $CLASS_AUTOCONVERT = 'zero' -->\n            <!-- ELSE -->\n              <!-- DEFINE $CLASS_AUTOCONVERT = 'negative' -->\n            <!-- ENDIF -->\n\n            <div id=\"unit_create\" class=\"{$CREATE_CLASS}\">\n              <!-- IF STACKABLE -->\n                <!-- IF ! PLAYER_OPTION_BUILD_AUTOCONVERT_HIDE -->\n                <div id=\"unit_create_stackable_autoconvert\">\n                  <input type=\"checkbox\" value=\"1\" name=\"auto_convert\" id=\"auto_convert\"/>\n                  <label for=\"auto_convert\" id=\"auto_convert_label\" class=\"{$CLASS_AUTOCONVERT}\">\n                    {L_eco_bld_autoconvert}:\n                    {MARKET_AUTOCONVERT_COST_TEXT} {L_sys_dark_matter_sh}\n                  </label>\n                </div>\n                <!-- ENDIF -->\n\n                <div id=\"unit_max\">\n                  {L_sys_maximum} <span id=\"unit_max_number\">0</span> /\n                  <span id=\"unit_max_number_autoconvert\" class=\"{$CLASS_AUTOCONVERT}\">0</span>\n                </div>\n                <ainput type=\"text\" name=\"unit_amount\" id=\"unit_amount\" max=\"0\"></ainput>\n                <script type=\"text/javascript\">\n                  sn_ainput_make_jquery();\n                </script>\n\n                <button id=\"unit_create_button\" class=\"unit_create positive\">\n                  {STRING_CREATE}\n                </button>\n              <!-- ELSE -->\n              <!-- IF ! PLAYER_OPTION_BUILD_AUTOCONVERT_HIDE -->\n              <button id=\"unit_create_button_auto\" class=\"unit_create_autoconvert {$CLASS_AUTOCONVERT}\">\n                {STRING_CREATE} {LA_level} <span id=\"unit_create_level_auto\"></span><br/>\n                {L_eco_bld_autoconvert}: {MARKET_AUTOCONVERT_COST_TEXT} {L_sys_dark_matter_sh}\n              </button>\n              <!-- ENDIF -->\n\n              <button id=\"unit_create_button\" class=\"unit_create positive\">\n                {STRING_CREATE} {LA_level} <span id=\"unit_create_level\"></span>\n              </button>\n              <!-- ENDIF -->\n            </div>\n\n            <!-- IF ! STACKABLE -->\n              <div id=\"unit_destroy\" style=\"visibility: hidden\">\n                <div id=\"unit_destroy_resources\">&nbsp;</div>\n                <div id=\"unit_destroy_time\">&nbsp;</div>\n                <div class=\"link negative unit_destroy\">{LA_bld_destroy} {LA_level} <span id=\"unit_destroy_level\"></span></div>\n              </div>\n            <!-- ENDIF -->\n          </form>\n        </td>\n      </tr>\n\n      <tr>\n        <td class=\"c_c\">\n        <!-- BEGIN production -->\n            <div class=\"unit_preview\" id=\"unit{production.ID}\" unit_id=\"{production.ID}\">\n              <img src=\"{I_[production.ID]}\" class=\"unit_preview_image\">\n\n              <!-- IF production.TIME_SECONDS -->\n                <span class=\"a75 time\">\n                  {production.TIME}\n                </span>\n              <!-- ENDIF -->\n\n              <span class=\"a75 <!-- IF STACKABLE -->count<!-- ELSE -->level<!-- ENDIF -->\">\n                <!-- IF production.LEVEL_OLD || STACKABLE -->{production.LEVEL_OLD}<!-- IF production.LEVEL_BONUS --><span class=\"bonus\">+{production.LEVEL_BONUS}</span><!-- ENDIF --><!-- ENDIF --><!-- IF production.LEVEL_QUED --><!-- IF production.LEVEL_QUED > 0 -->+<!-- ENDIF -->{production.LEVEL_QUED}<!-- ENDIF -->\n              </span>\n\n              <!-- IF QUE_HAS_PLACE && ! production.UNIT_BUSY && ! STACKABLE && ! TEMPORARY -->\n                <!-- IF QUE_HAS_FIELDS && production.BUILD_CAN && production.BUILD_RESULT == 0 -->\n                  <div class=\"right_top a75 unit_create icon_plus\"></div>\n                <!-- ENDIF -->\n              <!-- ENDIF -->\n\n              <span class=\"a75 name\">\n                {production.NAME}\n              </span>\n\n              <!-- IF     (production.METAL && (production.METAL_REST_NUM > 999999999 || production.METAL_REST_NUM < -99999999)) || (production.CRYSTAL && (production.CRYSTAL_REST_NUM > 999999999)) || production.CRYSTAL_REST_NUM < -99999999 || (production.DEUTERIUM && (production.DEUTERIUM_REST_NUM > 999999999  || production.DEUTERIUM_REST_NUM < -99999999)) -->\n                <!-- DEFINE $FONT_SIZE = '0.8em' -->\n              <!-- ELSEIF (production.METAL && (production.METAL_REST_NUM > 99999999  || production.METAL_REST_NUM < -9999999 )) || (production.CRYSTAL && (production.CRYSTAL_REST_NUM > 99999999 )) || production.CRYSTAL_REST_NUM < -9999999  || (production.DEUTERIUM && (production.DEUTERIUM_REST_NUM > 99999999   || production.DEUTERIUM_REST_NUM < -9999999 )) -->\n                <!-- DEFINE $FONT_SIZE = '0.9em' -->\n              <!-- ELSE -->\n                <!-- DEFINE $FONT_SIZE = '1em' -->\n              <!-- ENDIF -->\n              <span class=\"a75 resource_container\" style=\"font-size: {$FONT_SIZE};\">\n                <!-- IF production.METAL --><div><div class=\"left\"><!-- IF $FONT_SIZE != '1em' -->{L_sys_metal_sh}<!-- ELSE -->{L_sys_metal}<!-- ENDIF --></div><div class=\"fr\"><!-- IF STACKABLE -->{production.METAL_TEXT}<!-- ELSE -->{production.METAL_REST_NUM|num|color}<!-- ENDIF --></div></div><br><!-- ENDIF -->\n                <!-- IF production.CRYSTAL --><div><div class=\"left\"><!-- IF $FONT_SIZE != '1em' -->{L_sys_crystal_sh}<!-- ELSE -->{L_sys_crystal}<!-- ENDIF --></div><div class=\"fr\"><!-- IF STACKABLE -->{production.CRYSTAL_TEXT}<!-- ELSE -->{production.CRYSTAL_REST_NUM|num|color}<!-- ENDIF --></div></div><br><!-- ENDIF -->\n                <!-- IF production.DEUTERIUM --><div><div class=\"left\"><!-- IF $FONT_SIZE != '1em' -->{L_sys_deuterium_sh}<!-- ELSE -->{L_sys_deuterium}<!-- ENDIF --></div><div class=\"fr\"><!-- IF STACKABLE -->{production.DEUTERIUM_TEXT}<!-- ELSE -->{production.DEUTERIUM_REST_NUM|num|color}<!-- ENDIF --></div></div><!-- ENDIF -->\n                <!-- IF production.DARK_MATTER && ! TEMPORARY --><div><div class=\"left\">{L_sys_dark_matter_sh}</div><div class=\"fr\">{production.DARK_MATTER_REST_NUM|num|color}</div></div><!-- ENDIF -->\n              </span>\n\n\n              <!-- IF production.BUILD_RESULT != 0 -->\n                <span class=\"a75 unit_preview_image\">\n                  <div class=\"restrict <!-- IF production.BUILD_RESULT == BUILD_AUTOCONVERT_AVAILABLE --><!-- IF DARK_MATTER < MARKET_AUTOCONVERT_COST -->negative<!-- ELSE -->zero<!-- ENDIF --><!-- ENDIF -->\">{production.BUILD_RESULT_TEXT}</div>\n                </span>\n              <!-- ENDIF -->\n\n              <!-- IF QUE_HAS_PLACE && ! production.UNIT_BUSY && ! STACKABLE && ! TEMPORARY -->\n                <!-- IF production.LEVEL && production.DESTROY_CAN && production.DESTROY_RESULT == 0 -->\n                  <span class=\"left_top a75 unit_destroy icon_minus\" title=\"{L_bld_destroy}: {L_sys_metal} {production.DESTROY_METAL}; {L_sys_crystal} {production.DESTROY_CRYSTAL}; {L_sys_deuterium} {production.DESTROY_DEUTERIUM}; {L_sys_time} {production.DESTROY_TIME}\"></span>\n                <!-- ENDIF -->\n              <!-- ENDIF -->\n\n              <!-- IF ! ALLY_ID && ! production.DARK_MATTER_ONLY -->\n                <!-- IF production.METAL_REST_NUM < 0 --><!-- DEFINE $METAL_REST_NUM = 'metal=\"{production.METAL_REST_NUM}\"' --><!-- ELSE --><!-- DEFINE $METAL_REST_NUM = '' --><!-- ENDIF -->\n                <!-- IF production.CRYSTAL_REST_NUM < 0 --><!-- DEFINE $CRYSTAL_REST_NUM = 'crystal=\"{production.CRYSTAL_REST_NUM}\"' --><!-- ELSE --><!-- DEFINE $CRYSTAL_REST_NUM = '' --><!-- ENDIF -->\n                <!-- IF production.DEUTERIUM_REST_NUM < 0 --><!-- DEFINE $DEUTERIUM_REST_NUM = 'deuterium=\"{production.DEUTERIUM_REST_NUM}\"' --><!-- ELSE --><!-- DEFINE $DEUTERIUM_REST_NUM = '' --><!-- ENDIF -->\n                <span class=\"posRB a75 icon_gather\" title=\"{L_flt_gather_all}\" {$METAL_REST_NUM} {$CRYSTAL_REST_NUM} {DEUTERIUM_REST_NUM}></span>\n                <!-- ENDIF -->\n\n              <span class=\"posLB a75 unit_preview_image_icon\" go=\"info\">\n                <span class=\"icon-info-cover\"></span>\n              </span>\n            </div>\n        <!-- END production -->\n        </td>\n      </tr>\n    </table>\n  </td>\n  <!-- IF U_opt_int_struc_vertical && $QUE_NOT_EMPTY -->\n  <td width=\"20px\" align=\"center\" valign=\"top\">\n    <!-- INCLUDE eco_bld_queue_list -->\n  </td>\n  <!-- ENDIF -->\n</tr>\n</table>\n\n<!-- INCLUDE page_hint -->\n"
  },
  {
    "path": "design/templates/OpenGame/chat/chat_body.tpl.html",
    "content": "<!-- INCLUDE chat/chat_header -->\n\n<!--[if IE]><style type=\"text/css\">#chat_box1 {width: 80%;}</style><![endif]-->\n\n<!-- IF CHAT_IFRAME -->\n<style type=\"text/css\">\n  body, html, center {\n    border: 0 !important;\n    margin: 0 !important;\n    padding: 0 !important;\n    height: 100% !important;\n    width: 100% !important;\n    overflow-y: hidden;\n  }\n</style>\n<!-- ELSE -->\n<h2>{PAGE_HEADER} - <a class=\"link_external\" onClick=\"window.open('index.php?page=chat_msg&history=history&ally={ALLY}');\">{L_chat_history}</a></h2>\n\n<div style=\"min-height: 35em\">\n<!-- ENDIF -->\n\n<table id=\"chat_box\" cellpadding=\"0\" width=\"100%\" height=\"100%\">\n  <tr>\n    <th id=\"shoutbox_cell\" valign=\"top\">\n      <div id=\"shoutbox\"></div>\n    </th>\n\n    <th rowspan=\"2\" id=\"chat_online_dragger\">&lt;&lt;</th>\n\n    <th width=\"100px\" align=\"left\" valign=\"top\" id=\"chat_online_wrapper\" class=\"js_chat_side_panel\">\n      <table id=\"online_table\" class=\"noborder\" width=\"100%\" cellspacing=\"0\" border=\"0\" style=\"height: 100%;\">\n        <tr height=\"0px\">\n          <td nowrap class=\"noborder\">\n            <div class=\"contFS\">\n              {L_chat_advanced_online_players}&nbsp;<span id=\"online_players\"></span>&nbsp;(<img src='design/images/icon_invisible.png' /> <span id=\"online_invisibles\"></span>)\n            </div>\n          </td>\n        </tr>\n\n        <tr>\n          <td class=\"noborder\" height=\"100%\" id=\"chat_online_cell\">\n            <div style=\"vertical-align: text-top; overflow: auto;\" class=\"chat_online\" id=\"chat_online_div\">\n              <table id=\"onlinebox\" width=\"100%\" class=\"markup\"></table>\n            </div>\n          </td>\n        </tr>\n\n        <tr>\n          <td class=\"c_l noborder\" nowrap>\n            <label for=\"chat_player_invisible\">{L_chat_advanced_invisibility}</label> <input type=\"checkbox\" id=\"chat_player_invisible\" />\n          </td>\n        </tr>\n      </table>\n    </th>\n  </tr>\n\n\n  <tr id=\"chat_message_inputs\">\n    <th nowrap height=\"2px\">\n      <form action=\"index.php?page=chat_add\" name=\"chat_form\">\n        <input type=\"hidden\" name=\"color\" id=\"chat_color\" />\n\n        <span id=\"chat_color_button\"><div>A</div><div>B</div><div>C</div><div>D</div><div>E</div><div>F</div><div>G</div></span>\n        <img id=\"chat_smile_point\" src=\"design/images/smileys/smile.gif\" alt=\"{L_chat_advanced_smile_tooltip}\" title=\"{L_chat_advanced_smile_tooltip}\" />\n        <input type=\"button\" name=\"send\" value=\"{L_chat_send}\" id=\"send\" onClick=\"addMessage();event.returnValue = false;return false;\"  />\n        <span id=\"chat_msg_wrapper\">\n        <!-- IF USER_AUTHLEVEL > 2 -->\n          <textarea autocomplete=\"off\" autocorrect=\"off\" autocapitalize=\"off\" spellcheck=\"false\" autosuggest=\"off\" name=\"msg\" id=\"msg\" onKeyPress=\"if(event.keyCode == 13 && event.ctrlKey){document.chat_form.send.click();event.returnValue = false;return false;}\"></textarea>\n        <!-- ELSE -->\n          <input autocomplete=\"off\" autocorrect=\"off\" autocapitalize=\"off\" spellcheck=\"false\" autosuggest=\"off\" type=\"text\" name=\"msg\" id=\"msg\" onKeyPress=\"if(event.keyCode == 13){document.chat_form.send.click();event.returnValue = false;return false;}\" />\n        <!-- ENDIF -->\n        </span>\n      </form>\n    </th>\n    <th nowrap height=\"2px\" id=\"chat_aux_panel\" class=\"js_chat_side_panel\">\n<!--      <a class=\"fl link\" id=\"make_frame\" href=\"index.php?page=chat_frame&mode={CHAT_MODE}\">{L_chat_advanced_frame_on}</a>-->\n      <a class=\"fr link_external\" onClick=\"window.open('index.php?page=chat_msg&history=history&ally={ALLY}', 'ChatHistory', '');\">{L_chat_history}</a>\n    </th>\n  </tr>\n\n  <tr id=\"chat_message_refresh\" nowrap height=\"2px\" style=\"display: none;\">\n    <td class=\"c_c\" onclick=\"location.reload();\" colspan=\"2\">\n      <span class=\"link_action\">{L_chat_timeout}</span>\n    </td>\n  </tr>\n\n  <script type=\"text/javascript\">\n    if(sn_inframe) {\n      $('#make_frame').html('{L_chat_advanced_frame_off}').attr('href', '').click(function () {\n        // window.parent.window.document.location = 'index.php?page=chat&mode={CHAT_MODE}';\n        top.location.href = SN_ROOT_VIRTUAL + 'index.php?page=chat&mode={CHAT_MODE}';\n      });\n    }\n  </script>\n</table>\n\n<div id=\"chat_message_smiles2\">\n  <!-- BEGIN smiles -->\n    <div smile=\"{smiles.BBCODE}\"><img src=\"design/images/smileys/{smiles.FILENAME}.gif\" /></div>\n  <!-- END smiles -->\n</div>\n\n\n<div id=\"chat_color_select\">\n  <div color=\"cyan\">AAAAAAAA</div>\n  <div color=\"yellow\">AAAAAAAA</div>\n  <div color=\"green\">AAAAAAAA</div>\n  <div color=\"pink\">AAAAAAAA</div>\n  <div color=\"maroon\">AAAAAAAA</div>\n  <div color=\"orange\">AAAAAAAA</div>\n  <div color=\"white\">AAAAAAAA</div>\n</div>\n\n\n<div id=\"chat_muted_tooltip\"></div>\n<!-- IF AUTH_LEVEL -->\n<div id=\"chat_command_menu\">\n  <div id=\"chat_command\"></div>\n  <!-- BEGIN chat_advanced_command_interval -->\n  <div interval=\"{chat_advanced_command_interval.INTERVAL}\">{chat_advanced_command_interval.NAME}</div>\n  <!-- END smiles -->\n  <div>\n    <label for=\"chat_command_reason\">{L_chat_advanced_command_reason2}</label> <input type=\"text\" id=\"chat_command_reason\" />\n    <span><input type=\"checkbox\" id=\"chat_command_vacancy\" checked=\"checked\" /> <label for=\"chat_command_vacancy\">{L_chat_advanced_ban_vacancy}</label></span>\n  </div>\n</div>\n<!-- ENDIF -->\n"
  },
  {
    "path": "design/templates/OpenGame/chat/chat_frame.tpl.html",
    "content": "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">\n<html>\n  <head>\n    <style type=\"text/css\">\n    body, html\n    {\n      border: 0;\n      margin: 0;\n      padding: 0;\n      height: 100%;\n      width: 100%;\n      overflow:hidden;\n      font-size: 10px;\n    }\n    iFrame { border-width: 0; }\n    head {height: 0}\n/*\n    body\n    {\n      overflow-y: hidden;\n    }\n*/\n    </style>\n  </head>\n\n  <body>\n    <script type=\"text/javascript\" src=\"js/lib/jquery.js?{C_var_db_update}\"></script>\n    <script type=\"text/javascript\" src=\"js/lib/jquery-ui.js?{C_var_db_update}\"></script>\n    <script type=\"text/javascript\" src=\"js/sn_global.js?{C_var_db_update}\"></script>\n    <iframe id=\"main_frame\" name=\"sn_frame_main\" style=\"height: 100%; width: 100%; margin: 0 0 0 0\" src=\"overview.php\" seamless=\"seamless\" frameborder = 0 ></iframe>\n    <?php\n      $mode = sys_get_param_int('mode', CHAT_MODE_COMMON);\n      $mode = $mode == CHAT_MODE_ALLY ? CHAT_MODE_ALLY : CHAT_MODE_COMMON;\n    ?>\n    <iframe id=\"chat_resize_frame\" style=\"height: 10px; width: 100%; overflow: hidden; margin: 0 0 0 0\" src=\"about:blank\" seamless=\"seamless\" frameborder = 0></iframe>\n    <iframe id=\"chat_frame\" name=\"sn_frame_chat\" style=\"height: 0; width: 100%; overflow: hidden; margin: 0 0 0 0\" seamless=\"seamless\" frameborder = \"0\" src=\"index.php?page=chat&mode=<?php echo $mode; ?>&iframe=1\"></iframe>\n\n    <script type=\"text/javascript\">\n      var resize_frame = document.getElementById('chat_resize_frame').contentWindow.document;\n      resize_frame.open('text/html', 'replace');\n      resize_frame.write('<!DOCTYPE html><html><body><style>html,body,iframe,center{-webkit-touch-callout: none;-webkit-user-select: none;-khtml-user-select: none;-moz-user-select: none;-ms-user-select: none;user-select: none;padding: 0;margin: 0;border: 0;overflow: hidden;font-size: 10px;height: 10px;}</style><center unselectable=\"on\"># # #</center></body></html>');\n      resize_frame.close();\n\n      var chatResizeY;\n      var chatFrameMainMinHeight = 200;\n      var chatFrameChatMinHeight = 140;\n\n      function setHeightToParent(element) {\n        element.height(element.parent().height());\n      }\n\n      jQuery(\"#chat_frame\").load(function() {\n        jQuery(\"#chat_frame\").contents().find('#copyright').remove();\n\n        jQuery(\"#chat_resize_frame\").mouseenter(function(e){\n          jQuery(\"#chat_resize_frame\").contents().find('body,html').css('cursor','row-resize');\n        });\n        jQuery(\"#chat_resize_frame\").contents().mouseleave(function(e){\n          jQuery(\"#chat_resize_frame\").contents().find('body,html').css('cursor','default');\n        });\n\n        jQuery(\"#chat_resize_frame\").contents().mousedown(function(e){\n          jQuery(\"#chat_resize_frame\").contents().find('body,html').css('cursor','row-resize');\n\n          chatResizeY = e.screenY;\n        });\n\n        jQuery(\"#chat_resize_frame\").contents().mouseup(function(e) {\n          jQuery(\"#chat_resize_frame\").contents().find('body,html').css('cursor','default');\n\n          chatFrame = jQuery(\"#chat_frame\").contents();\n          chatResizeY = e.screenY - chatResizeY;\n          if(!isNaN(chatResizeY)) {\n            if(chatResizeY < (minResize = chatFrameMainMinHeight - jQuery(\"#main_frame\").height())) {\n              chatResizeY = minResize;\n            }\n\n            if((minResize = jQuery('body').height() - jQuery(\"#main_frame\").height() - jQuery(\"#chat_resize_frame\").height() - chatFrameChatMinHeight) < chatResizeY) {\n              chatResizeY = minResize;\n            }\n\n            jQuery(\"#main_frame\").height(jQuery(\"#main_frame\").height() + chatResizeY);\n            jQuery(\"#chat_frame\").height(jQuery('body').height() - jQuery(\"#main_frame\").height() - jQuery(\"#chat_resize_frame\").height());\n          } else{\n            jQuery(\"#main_frame\").height(jQuery(\"#main_frame\").height() - chatFrameChatMinHeight - 10);\n            jQuery(\"#chat_frame\").height(chatFrameChatMinHeight);\n            setHeightToParent(chatFrame.find('html'));\n          }\n\n          chatFrame.find('#chat_online_div').height(0);\n          chatFrame.find('#online_table').height(0);\n          chatFrame.find('#shoutbox').height(0);\n          chatFrame.find('#chat_box').height(0);\n          chatFrame.find('#main_content_center').height(0);\n          chatFrame.find('body > .markup').height(0);\n\n          setHeightToParent(chatFrame.find('body'));\n//          !isNaN(chatResizeY) ? setHeightToParent(chatFrame.find('body > .markup')) : chatFrame.find('#global_page_markup').height(140);\n          setHeightToParent(chatFrame.find('body > .markup'));\n\n          setHeightToParent(chatFrame.find('#main_content_center'));\n          setHeightToParent(chatFrame.find('#chat_box'));\n          setHeightToParent(chatFrame.find('#shoutbox'));\n          setHeightToParent(chatFrame.find('#online_table'));\n          setHeightToParent(chatFrame.find('#chat_online_div'));\n          if(isNaN(chatResizeY)) {\n            chatFrame.find('#global_page_markup').height(140);\n          }\n        });\n        jQuery(\"#chat_resize_frame\").contents().mouseup();\n      });\n    </script>\n  </body>\n</html>\n"
  },
  {
    "path": "design/templates/OpenGame/chat/chat_header.tpl.html",
    "content": "<script type=\"text/javascript\" xmlns=\"http://www.w3.org/1999/html\">\n  var ally_id = \"{ALLY}\";\n  var IS_HISTORY = '{HISTORY}';\n  var chat_refresh_rate = parseInt(\"{CHAT_REFRESH_RATE}\") * 1000;\n  var L_chat_advanced_command_mute = \"{LA_chat_advanced_command_mute}\";\n  var L_chat_advanced_online_ban = \"{LA_chat_advanced_online_ban}\";\n  var L_chat_advanced_online_mute = \"{LA_chat_advanced_online_mute}\";\n  var L_chat_advanced_online_banned_via_chat = \"{LA_chat_advanced_online_banned_via_chat}\";\n  var L_chat_advanced_online_unmute = \"{LA_chat_advanced_online_unmute}\";\n  var L_chat_advanced_command_reason = \"{LA_chat_advanced_command_reason}\";\n</script>\n\n<script type=\"text/javascript\" src=\"js/chat_advanced.min.js?{C_var_db_update}\"></script>\n"
  },
  {
    "path": "design/templates/OpenGame/chat/chat_messages.tpl.html",
    "content": "<!-- IF HISTORY -->\n<!-- INCLUDE chat/chat_header -->\n<h2>{L_chat_history}&nbsp;-&nbsp;<!-- IF ALLY -->{L_chat_ally}<!-- ELSE --> {L_chat_common}<!-- ENDIF --></h2>\n<!-- IF .page -->\n<h3>\n  <input type=\"button\" chat_history_go=\"1\" value=\"&lt;&lt;\"<!-- IF PAGE <= 1 --> disabled<!-- ENDIF --> />\n  <input type=\"button\" chat_history_go=\"{PAGE_PREV}\" value=\"&lt;\"<!-- IF PAGE <= 1 --> disabled<!-- ENDIF --> />\n  <label for=\"sheet\">{L_chat_page}:</label>\n  <select id=\"sheet\" name='sheet' chat_history_go=\"-1\">\n    <!-- BEGIN page -->\n    <option value=\"{page.NUMBER}\"<!-- IF PAGE == page.NUMBER --> selected<!-- ENDIF --> >{page.NUMBER}</option>\n    <!-- END page -->\n  </select> / {PAGES_TOTAL}\n  <input type=\"button\" chat_history_go=\"{PAGE_NEXT}\" value=\"&gt;\"<!-- IF PAGE >= PAGES_TOTAL --> disabled<!-- ENDIF -->\n  />\n  <input type=\"button\" chat_history_go=\"{PAGES_TOTAL}\" value=\"&gt;&gt;\"<!-- IF PAGE >= PAGES_TOTAL --> disabled\n  <!-- ENDIF --> />\n</h3>\n<!-- ENDIF -->\n<!-- ENDIF -->\n\n<!-- IF HISTORY -->\n<div id=\"shoutbox\" style=\"height: auto;\">\n<!-- ENDIF -->\n\n  <!-- BEGIN chat -->\n  <!-- IF chat.SENDER_ID && chat.RECIPIENT_ID --><!-- DEFINE $PRIVATE_CLASS = 'chat_private' --><!-- ELSE -->\n  <!-- DEFINE $PRIVATE_CLASS = '' --><!-- ENDIF -->\n  <div class=\"{$PRIVATE_CLASS} contFS chat_a\">\n    <!-- IF chat.TIME --><div class=\"chat_a_time\">[{chat.TIME}]&nbsp;</div><!-- ENDIF -->\n\n    <!-- IF chat.SENDER_ID && chat.RECIPIENT_ID -->\n    <div class=\"chat_a_private\">\n      {L_chat_advanced_whisper_sender_prefix}\n      <!-- IF chat.SENDER_ID == USER_ID -->\n      <b>{chat.SENDER_NAME}</b>{L_chat_advanced_whisper_sender_midfix}<span\n            safe_name=\"{online_player.chat.RECIPIENT_NAME_SAFE}\">{chat.RECIPIENT_NAME}</span>\n      <!-- ELSE -->\n      <span safe_name=\"{chat.SENDER_NAME_SAFE}\">{chat.SENDER_NAME}</span>{L_chat_advanced_whisper_recipient_midfix}<b>{chat.RECIPIENT_NAME}</b>\n      <!-- ENDIF -->\n      {L_chat_advanced_whisper_recipient_suffix}\n    </div>\n    <!-- ELSEIF chat.NICK -->\n    <div js_chat_nick_stripped=\"{chat.NICK_STRIPPED}\">{chat.NICK}&gt;</div><!-- ENDIF -->\n    <!-- IF chat.TEXT -->\n    <div class=\"chat_a_text\">{chat.TEXT}</div><!-- ENDIF -->\n    <!-- IF chat.DISABLE -->\n    <div class=\"warning\"><b>{L_chat_timeout}</b></div><!-- ENDIF -->\n  </div>\n  <!-- END chat -->\n\n  <!-- IF HISTORY -->\n</div>\n<!-- ENDIF -->\n"
  },
  {
    "path": "design/templates/OpenGame/chat/chat_online.tpl.html",
    "content": "<!-- BEGIN online_player -->\n  <tr>\n    <!-- IF USER_AUTHLEVEL -->\n    <td>{{online_player.ID}}</td>\n    <td player_id=\"{online_player.ID}\">\n      <!-- IF online_player.ID != USER_ID && USER_AUTHLEVEL > online_player.AUTH_LEVEL -->\n        <!-- IF USER_CAN_UNMUTE && online_player.MUTED -->\n          <img src='design/images/icon_muted.png' class=\"chat_unmute\" />\n        <!-- ELSEIF USER_CAN_MUTE && ! online_player.MUTED -->\n          <img src='design/images/icon_muted.png' class=\"chat_mute\" />\n        <!-- ENDIF -->\n\n        <!-- IF USER_CAN_BAN -->\n          <img src='design/images/icon_deny.png' class=\"chat_ban\" />\n        <!-- ENDIF -->\n      <!-- ENDIF -->\n    </td>\n    <!-- ENDIF -->\n    <td>{online_player.NAME}</td>\n    <td>\n      <!-- IF online_player.MUTED -->\n        <img src='design/images/icon_muted.png' chat_muted=\"{online_player.MUTED}\" chat_mute_reason=\"{online_player.MUTE_REASON}\" />\n      <!-- ENDIF -->\n      <!-- IF USER_AUTHLEVEL && online_player.INVISIBLE -->\n        <img src='design/images/icon_invisible.png' />\n      <!-- ENDIF -->\n    </td>\n  </tr>\n<!-- END online_player -->\n"
  },
  {
    "path": "design/templates/OpenGame/contact.tpl.html",
    "content": "<h1>{L_ctc_title}</h1>\n\n<table>\n  <tr>\n    <td colspan=\"3\" class=\"c_c warning\">{L_ctc_intro}</td>\n  </tr>\n\n  <tr class=\"ok c_c\">\n    <th>{L_ctc_name}</th>\n    <th>{L_ctc_rank}</th>\n    <th>{L_ctc_mail}</th>\n  </tr>\n\n  <!-- BEGIN contact -->\n  <tr>\n    <th>\n      <a href=\"messages.php?mode=write&id={contact.ID}\">\n        <img src=\"design/images/icon_mail.gif\" alt=\"{L_msg_compose}\" title=\"{L_msg_compose}\" border=\"0\">\n        {contact.NAME}\n      </a>\n    </th>\n    <th>{contact.LEVEL}</th>\n    <th>\n      <!-- IF contact.EMAIL -->\n      <a href=\"mailto:{contact.EMAIL}\">{contact.EMAIL}</a>\n      <!-- ELSE -->\n      &nbsp;\n      <!-- ENDIF -->\n    </th>\n  </tr>\n  <!-- END contact -->\n</table>\n"
  },
  {
    "path": "design/templates/OpenGame/dark_matter.tpl.html",
    "content": "<!-- IF ! $TABLE_WIDTH || $TABLE_WIDTH == 0 -->\n  <!-- DEFINE $TABLE_WIDTH = '519px' -->\n  <h1>{PAGE_HEADER}</h1>\n<!-- ENDIF -->\n\n<table width=\"{$TABLE_WIDTH}\">\n  <tr class=\"c_j\">\n    <th>\n      <span>{L_sys_dark_matter_what_header}</span>\n    </th>\n  </tr>\n  <tr class=\"c_j\">\n    <td>\n      <div style=\"display: table-row; position: relative; margin: 7px;\">\n        <img src=\"design/images/DMaterie.jpg\" align=\"top\" border=\"0\" height=\"120\" width=\"120\" class=\"fl\" style=\"margin-right: 7px;\">\n        {DARK_MATTER_DESCRIPTION}\n      </div>\n    </td>\n  </tr>\n\n  <tr class=\"c_j\">\n    <th>\n      <span>{L_sys_dark_matter_obtain_header}</span>\n    </th>\n  </tr>\n  <tr class=\"c_j\" id=\"dark_matter_obtain\">\n    <td>\n      {L_sys_dark_matter_obtain_text}\n      <!-- IF PAYMENT_AVAILABLE -->\n      {L_sys_dark_matter_obtain_text_convert}\n      <!-- ENDIF -->\n    </td>\n  </tr>\n\n  <tr>\n    <th class=\"c_l\">\n      {L_sys_dark_matter_description_header}\n    </th>\n  </tr>\n  <tr class=\"c_j\" id=\"dark_matter_usage\">\n    <td>\n      {L_sys_dark_matter_description_text}\n    </td>\n  </tr>\n</table>\n"
  },
  {
    "path": "design/templates/OpenGame/eco_bld_queue_list.tpl.html",
    "content": "<!-- IF U_opt_int_struc_vertical -->\n  <!-- DEFINE $ECO_QUE_VERTICAL = 'vertical' -->\n<!-- ELSE -->\n  <!-- DEFINE $ECO_QUE_VERTICAL = '' -->\n<!-- ENDIF -->\n\n<div class=\"bld_que_container {$ECO_QUE_VERTICAL}\">\n  <!-- IF $QUE_NOT_EMPTY -->\n\n  <!-- INCLUDE que_total -->\n\n  <div id=\"ov_{$QUE_ID}_que\" class=\"bld_que_items {$ECO_QUE_VERTICAL}\"></div>\n\n  <div class=\"bld_que_buttons\">\n    <a id=\"eco_que_clear\" class=\"button_pseudo\" href=\"buildings.php?mode={$QUE_ID}&action=clear&ally_id={ALLY_ID}\">{L_eco_que_clear}</a>\n\n    <!-- IF ARTIFACT_LEVEL -->\n    <a id=\"eco_que_artifact\" class=\"button_pseudo\" href=\"artifacts.php?action={D_ACTION_USE}&unit_id={ARTIFACT_ID}&REQUEST_URI=buildings.php?mode={$QUE_ID}\">{ARTIFACT_NAME}</a>\n    <!-- ENDIF -->\n\n    <a class=\"button_pseudo\" href=\"buildings.php?mode={$QUE_ID}&action=trim&ally_id={ALLY_ID}\">{L_eco_que_trim}</a>\n  </div>\n  <!-- ELSE -->\n  {L_eco_que_empty}\n  <!-- ENDIF -->\n</div>\n"
  },
  {
    "path": "design/templates/OpenGame/eco_queue.tpl.html",
    "content": "<!-- Check - if selected que is not empty -->\n<!-- DEFINE $QUE_NOT_EMPTY = false -->\n<!-- BEGIN que -->\n  <!-- IF $QUE_ID == que.QUE  -->\n    <!-- DEFINE $QUE_NOT_EMPTY = true -->\n  <!-- ENDIF -->\n<!-- END que -->\n\n<!-- Now is que not empty - adding appropriate JS variables and starting timer -->\n<!-- IF $QUE_NOT_EMPTY -->\n<!-- IF QUE_HTML -->\n  <!-- DEFINE $QUE_HTML = '{QUE_HTML}' -->\n<!-- ELSE -->\n  <!-- DEFINE $QUE_HTML = 'ov' -->\n<!-- ENDIF -->\n\n<script type=\"text/javascript\"><!--\nsn_timers.unshift({'id': '{$QUE_HTML}_{$QUE_ID}', 'type': TIMER_BUILD_QUE_V2, 'options': {\n  'msg_done': '{LA_eco_que_empty}',\n  <!-- IF $IN_NAVBAR_HUMAN -->\n  displayType:'human',\n  <!-- ENDIF -->\n  // 'url': document.location.href,\n  'template': '<div class=\"que_item {$QUE_HTML}_{$QUE_ID}_container_[UNIT_QUE_PLACE] que_item_[UNIT_QUE_PLACE]\" title=\"[UNIT_NAME] ([UNIT_LEVEL])\">\\\n    <span class=\"que_unit_picture\"><img src=\"[UNIT_IMAGE]\" align=\"top\" width=\"100%\" height=\"100%\"></span>\\\n    \\\n    <span class=\"que_unit_level que_unit_slot_bar {$QUE_HTML}_{$QUE_ID}_slot_bar_[UNIT_QUE_PLACE]\"></span>\\\n    <span class=\"a50 que_unit_level {$QUE_HTML}_{$QUE_ID}_level_[UNIT_QUE_PLACE]\">[UNIT_LEVEL]</span>\\\n    \\\n    <span class=\"que_unit_time que_unit_progress_bar {$QUE_HTML}_{$QUE_ID}_unit_bar_[UNIT_QUE_PLACE]\"></span>\\\n    <span class=\"a50 que_unit_time {$QUE_HTML}_{$QUE_ID}_timer_[UNIT_QUE_PLACE]\">[UNIT_TIME]</span>\\\n  </div>',\n  'que': [<!-- BEGIN que --><!-- IF $QUE_ID == que.QUE  -->\n    ['{que.ID}', '{que.NAME}', {que.TIME}, {que.AMOUNT}, {que.LEVEL}, {que.TIME_FULL}, '{I_[que.ID]}'],\n  <!-- ENDIF --><!-- END que -->]\n}});\n--></script>\n<!-- ENDIF -->\n"
  },
  {
    "path": "design/templates/OpenGame/fleet0.tpl.html",
    "content": "<!--\n@package fleet\n2.1 Overlib replaced with jQuery\n2.0 Utilize PTE\n    Comply with PCG\n-->\n\n<script type=\"text/javascript\"><!--\nvar ships = Array();\n<!-- BEGIN ships -->\n  <!-- IF (ships.SPEED > 0) -->\n  ships[{ships.ID}] = Array({ships.AMOUNT}, {ships.SPEED}, {ships.CONSUMPTION}, {ships.CAPACITY});\n  <!-- ENDIF -->\n<!-- END ships -->\nvar speed_factor   = {speed_factor};\n--></script>\n\n<!-- INCLUDE fleet_javascript -->\n\n<h2>{L_fl_new_miss}</h2>\n<!-- IF MISSION_NAME -->\n  <h3>{TYPE_NAME} [{galaxy}:{system}:{planet}], {MISSION_NAME}<!-- IF IS_COLONIZATION -->, {L_sys_colonies} {COLONIES_CURRENT}/{COLONIES_MAX}<!-- ENDIF -->\n  </h3>\n<!-- ENDIF -->\n\n<!-- IF PLAYER_OPTION_FLEET_SHIP_SELECT_OLD -->\n  <!-- DEFINE $FLEET_SHIP_SELECT_OLD = 'old' -->\n  <!-- DEFINE $FLEET_SHIP_SELECT_MAIN_COL_SPAN = '3' -->\n<!-- ELSE -->\n  <!-- DEFINE $FLEET_SHIP_SELECT_MAIN_COL_SPAN = '2' -->\n<!-- ENDIF -->\n\n<!-- IF PLAYER_OPTION_FLEET_SHIP_HIDE_CONSUMPTION -->\n  <!-- DEFINE $PLAYER_OPTION_FLEET_SHIP_HIDE_CONSUMPTION_CLASS = 'hide' -->\n<!-- ENDIF -->\n\n<!-- IF PLAYER_OPTION_FLEET_SHIP_HIDE_CAPACITY -->\n  <!-- DEFINE $PLAYER_OPTION_FLEET_SHIP_HIDE_CAPACITY_CLASS = 'hide' -->\n<!-- ENDIF -->\n\n<!-- IF PLAYER_OPTION_FLEET_SHIP_HIDE_SPEED -->\n  <!-- DEFINE $PLAYER_OPTION_FLEET_SHIP_HIDE_SPEED_CLASS = 'hide' -->\n<!-- ENDIF -->\n\n<!-- IF .ships -->\n<form action=\"fleet.php?fleet_page=1\" method=\"post\">\n  <input type=\"hidden\" name=\"thisgalaxy\"      value=\"{thisgalaxy}\" />\n  <input type=\"hidden\" name=\"thissystem\"      value=\"{thissystem}\" />\n  <input type=\"hidden\" name=\"thisplanet\"      value=\"{thisplanet}\" />\n  <input type=\"hidden\" name=\"thisplanet_type\" value=\"{thisplanet_type}\" />\n\n  <input type=\"hidden\" name=\"galaxy\" value=\"{galaxy}\">\n  <input type=\"hidden\" name=\"system\" value=\"{system}\">\n  <input type=\"hidden\" name=\"planet\" value=\"{planet}\">\n  <input type=\"hidden\" name=\"planet_type\" value=\"{planet_type}\">\n  <input type=\"hidden\" name=\"target_mission\" value=\"{target_mission}\">\n\n  <input type=\"hidden\" id=\"page_file_name\" value=\"fleet.php\">\n\n  <table border=\"0\" cellpadding=\"0\" cellspacing=\"1\">\n    <tr class=\"c_c\">\n      <td colspan=\"{$FLEET_SHIP_SELECT_MAIN_COL_SPAN}\">\n        <div>\n          {L_sys_sort}\n          <select name=\"sort_elements\" id=\"sort_elements\">\n            <!-- BEGIN ship_sort_list -->\n            <option value=\"{ship_sort_list.VALUE}\"<!-- IF ship_sort_list.VALUE == FLEET_SHIP_SORT --> selected<!-- ENDIF -->>{ship_sort_list.TEXT}</option>\n            <!-- END ship_sort_list -->\n          </select>\n          {L_sys_sort_inverse} <input type=\"checkbox\" name=\"sort_elements_inverse\" id=\"sort_elements_inverse\" value=\"1\"<!-- IF FLEET_SHIP_SORT_INVERSE --> checked<!-- ENDIF -->/>\n        </div>\n      </td>\n    </tr>\n\n    <!-- INCLUDE fleet_page_0_buttons_action -->\n    <!-- INCLUDE fleet_page_0_buttons -->\n\n    <!-- IF .ships -->\n      <!-- BEGIN ships -->\n        <tr>\n          <th class=\"{$SHIP_NO_SLOTS_SIZE_CLASS}\" colspan=\"{$SHIP_COLSPAN}\">\n            <style>\n              .ship_miniature_container[unit_id=\"{ships.ID}\"] {background-image: url({I_[ships.ID]});}\n            </style>\n            <div class=\"ship_miniature_container {$FLEET_SHIP_SELECT_OLD}\" go=\"info\" unit_id=\"{ships.ID}\">\n              <div class=\"a75 ship_miniature_name\">\n                {ships.NAME}\n              </div>\n\n              <div class=\"ship_miniature_data\">\n                <!-- IF ships.SPEED != 0 -->\n                <div class=\"ship_miniature_consumption {$PLAYER_OPTION_FLEET_SHIP_HIDE_CONSUMPTION_CLASS}\">\n                  <span class=\"a75\">\n                  <img src=\"{I_icon_ship_consumption_16}\" class=\"icon_1em\" />\n                  {ships.CONSUMPTION_TEXT}\n                  </span>\n                </div>\n\n                <div class=\"ship_miniature_capacity {$PLAYER_OPTION_FLEET_SHIP_HIDE_CAPACITY_CLASS}\">\n                  <span class=\"a75\">\n                  <img src=\"{I_icon_ship_capacity_16}\" class=\"icon_1em\" />\n                  {ships.CAPACITY_TEXT}\n                  </span>\n                </div>\n\n                <div class=\"ship_miniature_speed {$PLAYER_OPTION_FLEET_SHIP_HIDE_SPEED_CLASS}\">\n                  <span class=\"a75\">\n                  <img src=\"{I_icon_ship_speed_16}\" class=\"icon_1em\" />\n                  {ships.SPEED_TEXT}\n                  </span>\n                </div>\n                <!-- ENDIF -->\n\n                <div class=\"a75 ship_miniature_amount positive\">\n                  {ships.AMOUNT_TEXT}\n                </div>\n              </div>\n            </div>\n          </th>\n\n          <!-- IF PLAYER_OPTION_FLEET_SHIP_SELECT_OLD -->\n          <td class=\"c_r positive\">\n            {ships.AMOUNT_TEXT}\n          </td>\n          <!-- ENDIF -->\n\n          <!-- IF $SHIP_COLSPAN == '1' -->\n          <th>\n            <!-- IF (ships.SPEED == 0) || (FLYING_FLEETS >= MAX_FLEETS) -->\n              &nbsp;\n            <!-- ELSE -->\n              <ainput type=\"text\" name=\"ships[{ships.ID}]\" id=\"ships{ships.ID}\" max=\"{ships.AMOUNT}\"></ainput>\n              <script type=\"text/javascript\">\n                sn_ainput_make_jquery();\n                jQuery('#ships{ships.ID}slide').on('slide slidechange', fl_calc_stats);\n              </script>\n            <!-- ENDIF -->\n          </th>\n          <!-- ENDIF -->\n        </tr>\n      <!-- END ships -->\n\n      <!-- INCLUDE fleet_page_0_buttons -->\n      <!-- INCLUDE fleet_page_0_buttons_action -->\n\n    <!-- ELSE -->\n      <tr><th colspan=\"2\">{L_fl_noships}</th></tr>\n    <!-- ENDIF -->\n  </table>\n</form>\n<!-- ELSE -->\n<h2 class=\"error\">{L_fl_noships}</h2>\n<input type=\"button\" value=\"{L_flt_gather_all}\" onclick=\"document.location = 'fleet.php?fleet_page=5';\" />\n<!-- ENDIF -->\n\n<!-- IF FLYING_FLEETS < MAX_FLEETS && .ships -->\n  <br>\n  <table>\n    <tr><th class=\"c_l\" colspan=2>{L_fl_fleet_data}</th></tr>\n    <tr>\n        <td class=\"c_l\">{L_fl_speed}</td>\n        <td class=\"c_r\" width=\"100\" id='int_fleet_speed'>-</th>\n    </tr>\n    <tr>\n        <td class=\"c_l\">{L_fl_dist}</td>\n        <td class=\"c_r\" id='distance'>0</dh>\n    </tr>\n    <tr>\n        <td class=\"c_l\">{L_fl_fltime}</td>\n        <td class=\"c_r\" id='duration'>-</span></td>\n    </tr>\n    <tr>\n        <td class=\"c_l\">{L_fl_deute_need}</td>\n        <td class=\"c_r\" id='int_fleet_consumption'>0</td>\n    </tr>\n    <tr>\n        <td class=\"c_l\">{L_fl_fuel_on_planet}</td>\n        <td class=\"c_r zero\">{PLANET_DEUTERIUM}</td>\n    </tr>\n    <tr>\n        <td class=\"c_l\">{L_sys_capacity}</td>\n        <td class=\"c_r\" id='int_fleet_capacity'>0</td>\n    </tr>\n    <tr>\n        <td class=\"c_l\">{L_fl_planet_resources}</td>\n        <td class=\"c_r zero\">{PLANET_RESOURCES}</td>\n    </tr>\n  </table>\n<!-- ENDIF -->\n<!-- INCLUDE page_hint -->\n"
  },
  {
    "path": "design/templates/OpenGame/fleet1.tpl.html",
    "content": "<!-- INCLUDE fleet_javascript -->\n\n<h2>{L_fl_floten1_ttl}</h2>\n\n<!-- INCLUDE fleet_miniatures -->\n\n<form action=\"fleet.php?fleet_page=2\" method=\"post\">\n  <input type=\"hidden\" name=\"thisgalaxy\"      value=\"{thisgalaxy}\" />\n  <input type=\"hidden\" name=\"thissystem\"      value=\"{thissystem}\" />\n  <input type=\"hidden\" name=\"thisplanet\"      value=\"{thisplanet}\" />\n  <input type=\"hidden\" name=\"thisplanet_type\" value=\"{thisplanet_type}\" />\n\n  <input type=\"hidden\" name=\"target_mission\"  value=\"{target_mission}\" />\n\n  <input type=\"hidden\" name=\"usedfleet\"       value=\"{usedfleet}\" />\n\n  <table border=\"0\" cellpadding=\"0\" cellspacing=\"1\" style=\"width: 42em\">\n    <tr>\n      <th width=\"12em\">{L_fl_dest}</th>\n      <th>\n        <input type=\"text\" name=\"galaxy\" class=\"coordinate\" maxlength=\"3\" onChange=\"shortInfo()\" onKeyUp=\"shortInfo()\" value=\"{galaxy}\" />\n        <input type=\"text\" name=\"system\" class=\"coordinate\" maxlength=\"3\" onChange=\"shortInfo()\" onKeyUp=\"shortInfo()\" value=\"{system}\" />\n        <input type=\"text\" name=\"planet\" class=\"coordinate\" maxlength=\"3\" onChange=\"shortInfo()\" onKeyUp=\"shortInfo()\" value=\"{planet}\" />\n        <select name=\"planet_type\" onChange=\"shortInfo()\" onKeyUp=\"shortInfo()\">\n          <option value=\"1\" {t1}>{L_fl_planet}</option>\n          <option value=\"2\" {t2}>{L_fl_ruins}</option>\n          <option value=\"3\" {t3}>{L_fl_moon}</option>\n        </select>\n        <input type=\"hidden\" name=\"fleet_group\" id=\"fleet_group\" onChange=\"shortInfo()\" onKeyUp=\"shortInfo()\" value=\"0\" />\n        <input type=\"hidden\" name=\"acs_target_mr\" onChange=\"shortInfo()\" onKeyUp=\"shortInfo()\" value=\"0:0:0\" />\n      </th>\n    </tr>\n\n    <tr>\n      <th>{L_fl_speed}</th>\n      <th>\n        <select name=\"speed\" onChange=\"shortInfo()\" onKeyUp=\"shortInfo()\">\n          <option value=\"10\">100</option>\n          <option value=\"9\">90</option>\n          <option value=\"8\">80</option>\n          <option value=\"7\">70</option>\n          <option value=\"6\">60</option>\n          <option value=\"5\">50</option>\n          <option value=\"4\">40</option>\n          <option value=\"3\">30</option>\n          <option value=\"2\">20</option>\n          <option value=\"1\">10</option>\n        </select>% {L_sys_from_speed} <span id=\"fleet_speed\" class=\"ok\">0</span>\n    </tr>\n\n    <tr>\n      <th>{L_fl_dist}</th>\n      <th><div id=\"distance\">-</div></th>\n    </tr>\n\n    <tr>\n      <th>{L_fl_fltime}</th>\n      <th><div id=\"duration\">-</div></th>\n    </tr>\n\n    <tr>\n      <th rowspan=\"2\">{L_fl_dest_t}</th>\n      <th>{L_time_local}: <span id=\"time_dst_local\">-</span></th>\n    </tr>\n    <tr>\n      <th>{L_time_server}: <span id=\"time_dst\">-</span></th>\n    </tr>\n\n    <tr>\n      <th rowspan=\"2\">{L_fl_back_t}</th>\n      <th>{L_time_local}: <span id=\"time_src_local\">-</span></th>\n    </tr>\n    <tr>\n      <th>{L_time_server}: <span id=\"time_src\">-</span></th>\n    </tr>\n\n    <tr>\n      <th>{L_fl_deute_need}</th>\n      <th><div id=\"consumption\">-</div></th>\n    </tr>\n\n    <tr>\n      <td class=\"c_c\">{L_fl_fuel_on_planet}</td>\n      <td class=\"c_c zero\">{PLANET_DEUTERIUM}</td>\n    </tr>\n\n    <tr>\n      <th>{L_sys_capacity}</th>\n      <th><div id=\"capacity\">-</div></th>\n    </tr>\n\n    <tr><th colspan=\"2\" class=\"c_c\"><input type=\"submit\" value=\"{L_fl_continue}\" /></th></tr>\n  </table>\n  <br />\n  <table>\n    <tr>\n      <td valign=\"top\">\n        <table class=\"border_image_small\">\n        <tr><td colspan=2 class=c>{L_fl_myplanets}</td></tr>\n          <!-- BEGIN colonies -->\n            <tr>\n              <td class=\"c_l\">\n                <button onclick=\"return setTarget({colonies.GALAXY}, {colonies.SYSTEM}, {colonies.PLANET}, {colonies.TYPE});\" style=\"width: 100%\">\n                  [{colonies.GALAXY}:{colonies.SYSTEM}:{colonies.PLANET}] {colonies.TYPE_PRINT} {colonies.NAME}\n                </button>\n                <script type=\"text/javascript\">\n                  $.extend(planet_names, {'g{colonies.GALAXY}s{colonies.SYSTEM}p{colonies.PLANET}t{colonies.TYPE}': '{colonies.NAME}'});\n                </script>\n              </td>\n            </tr>\n          <!-- BEGINELSE colonies -->\n            <tr>\n              <th colspan=\"2\">{L_fl_nocolonies}</th>\n            </tr>\n          <!-- END colonies -->\n        </table>\n      </td>\n\n      <td valign=\"top\">\n        <table class=\"border_image_small\">\n          <tr><td class=c><a href=\"notes.php\" class=\"link\">{L_fl_shortcut}</a></td></tr>\n          <!-- BEGIN shortcut -->\n            <tr>\n              <td class=\"c_l {shortcut.PRIORITY_CLASS}\">\n                <button onclick=\"return setTarget({shortcut.GALAXY}, {shortcut.SYSTEM}, {shortcut.PLANET}, {shortcut.TYPE});\" style=\"width: 100%\">\n                  [{shortcut.GALAXY}:{shortcut.SYSTEM}:{shortcut.PLANET}] {shortcut.TYPE_PRINT} {shortcut.NAME}\n                </button>\n                <script type=\"text/javascript\">\n                  !planet_names['g{shortcut.GALAXY}s{shortcut.SYSTEM}p{shortcut.PLANET}t{shortcut.TYPE}']\n                          ? $.extend(planet_names, {'g{shortcut.GALAXY}s{shortcut.SYSTEM}p{shortcut.PLANET}t{shortcut.TYPE}': '{shortcut.NAME}'})\n                          : '';\n                </script>\n              </td>\n            </tr>\n          <!-- BEGINELSE shortcut -->\n            <tr>\n              <th colspan=\"2\">{L_shortcut_none}</th>\n            </tr>\n          <!-- END shortcut -->\n        </table>\n      </td>\n\n      <td valign=\"top\">\n        <table class=\"border_image_small\">\n        <tr><td class=c>{L_fl_grattack}</td></tr>\n          <!-- BEGIN acss -->\n            <tr>\n              <th>\n                <input type=\"button\" onclick=\"setTarget({acss.GALAXY},{acss.SYSTEM},{acss.PLANET},{acss.TYPE});setACS({acss.ID});setACS_target('g{acss.GALAXY}s{acss.SYSTEM}p{acss.PLANET}t{acss.TYPE}');return false;\" value=\"[{acss.GALAXY}:{acss.SYSTEM}:{acss.PLANET}] {acss.TYPE_PRINT} {acss.NAME}\"  style=\"width: 100%\" />\n                <script type=\"text/javascript\">\n                  !planet_names['g{acss.GALAXY}s{acss.SYSTEM}p{acss.PLANET}t{acss.TYPE}']\n                          ? $.extend(planet_names, {'g{acss.GALAXY}s{acss.SYSTEM}p{acss.PLANET}t{acss.TYPE}': '{acss.NAME}'})\n                          : '';\n                </script>\n              </th>\n            </tr>\n          <!-- BEGINELSE acss -->\n            <tr>\n              <th colspan=\"2\">{L_fl_noacss}</th>\n            </tr>\n          <!-- END acss -->\n        </table>\n      </td>\n    </tr>\n  </table>\n  <!-- INCLUDE page_hint -->\n</form>\n\n<script type=\"text/javascript\"><!--\nvar fleet_capacity = {fleet_capacity};\nvar fleet_speed    = {fleet_speed};\nvar speed_factor   = {speed_factor};\n\ndocument.getElementById('fleet_speed').innerHTML = sn_format_number(fleet_speed);\n\nvar ships = Array();\n<!-- BEGIN fleets -->\n<!-- BEGIN ships -->\n  <!-- IF (ships.SPEED > 0) -->\n  ships[{ships.ID}] = Array({ships.AMOUNT}, {ships.SPEED}, {ships.CONSUMPTION}, 0);\n  <!-- ENDIF -->\n<!-- END ships -->\n<!-- END fleets -->\n\nshortInfo();\n--></script>\n"
  },
  {
    "path": "design/templates/OpenGame/fleet2.tpl.html",
    "content": "<!-- INCLUDE fleet_javascript -->\n\n<script type=\"text/javascript\"><!--\nvar fleet_capacity = {fleet_capacity};\nvar resource_max = Array({planet_metal}, {planet_crystal}, {planet_deuterium});\nvar fleet_slide_changing = false;\n--></script>\n\n<h2>{L_flt_page2_title}</h2>\n\n<!-- INCLUDE fleet_miniatures -->\n\n<form action=\"fleet.php?fleet_page=3\" method=\"post\">\n  <input type=\"hidden\" name=\"galaxy\"          value=\"{galaxy}\" />\n  <input type=\"hidden\" name=\"system\"          value=\"{system}\" />\n  <input type=\"hidden\" name=\"planet\"          value=\"{planet}\" />\n  <input type=\"hidden\" name=\"planet_type\"     value=\"{planet_type}\" />\n\n  <input type=\"hidden\" name=\"thisgalaxy\"      value=\"{thisgalaxy}\" />\n  <input type=\"hidden\" name=\"thissystem\"      value=\"{thissystem}\" />\n  <input type=\"hidden\" name=\"thisplanet\"      value=\"{thisplanet}\" />\n  <input type=\"hidden\" name=\"thisplanet_type\" value=\"{thisplanet_type}\" />\n\n  <input type=\"hidden\" name=\"usedfleet\"       value=\"{usedfleet}\" />\n\n  <input type=\"hidden\" name=\"consumption\"     value=\"{consumption}\" />\n  <input type=\"hidden\" name=\"speed\"           value=\"{speed}\" />\n  <input type=\"hidden\" name=\"fleet_group\"     value=\"{fleet_group}\" />\n  <input type=\"hidden\" name=\"acs_target_mr\"   value=\"{acs_target_mr}\" />\n  \n  <input type=\"hidden\" name=\"speedallsmin\"    value=\"{speedallsmin}\" />\n  <input type=\"hidden\" name=\"speedfactor\"     value=\"{speedfactor}\" />\n\n  <table class=\"fleet_2_table\">\n    <tr><th>\n    <!-- BEGIN missions -->\n      <div class=\"fleet_mission_button_container\">\n        <label for=\"target_mission{missions.ID}\">\n          <img src=\"design/images/mission_{missions.ID}.png\" id=\"mission_button{missions.ID}\" class=\"mission_button_image button_image button_pseudo <!-- IF missions.CHECKED --> button_pseudo_pressed<!-- ENDIF -->\" /><br/>\n          <input id=\"target_mission{missions.ID}\" mission_id=\"{missions.ID}\" type=\"radio\" name=\"target_mission\"\n                 style=\"display: none\"\n                 value=\"{missions.ID}\" onChange=\"changeMission(this);\" onClick=\"changeMission(this);\" <!-- IF missions.CHECKED -->checked <!-- ENDIF -->>\n          <span id=\"mission_name\">{missions.NAME}</span>\n        <!-- IF missions.ID == 5 || missions.ID == 15 -->\n          <!-- IF MAX_DURATION -->\n            <br />{L_flt_stay_duration}&nbsp;<select name=\"missiontime\">\n            <!-- BEGIN !duration -->\n              <option value=\"{duration.ID}\">{duration.TIME}</option>\n            <!-- END !duration -->\n            </select>&nbsp;&nbsp;\n          <!-- ELSE -->\n            <!-- DEFINE $CANNOTSEND = 1 -->\n            <span class=\"error\">{L_flt_error_duration_wrong}</span>\n          <!-- ENDIF -->\n        <!-- ELSEIF missions.ID == 7 -->\n          <!-- IF PLAYER_COLONIES_CURRENT > PLAYER_COLONIES_MAX -->\n            <!-- DEFINE $COLONY_STYLE = 'error' -->\n          <!-- ELSEIF PLAYER_COLONIES_CURRENT == PLAYER_COLONIES_MAX -->\n            <!-- DEFINE $COLONY_STYLE = 'warning' -->\n          <!-- ELSEIF PLAYER_COLONIES_MAX - PLAYER_COLONIES_CURRENT == 1  -->\n            <!-- DEFINE $COLONY_STYLE = 'notice' -->\n          <!-- ELSE -->\n            <!-- DEFINE $COLONY_STYLE = 'ok' -->\n          <!-- ENDIF -->\n          <br /><span class=\"{$COLONY_STYLE}\">{L_sys_colonies}&nbsp;{PLAYER_COLONIES_CURRENT}/{PLAYER_COLONIES_MAX}</span>\n        <!-- ENDIF -->\n        </label>\n      </div>\n    <!-- BEGINELSE missions -->\n      <div class=\"error\">{L_fl_bad_mission}</div>\n    <!-- END missions -->\n    </th></tr>\n\n    <!-- IF CAPTAIN_ID -->\n    <tr>\n      <td class=\"c_l\" nowrap>\n        <input type=\"checkbox\" name=\"captain_id\" value=\"{CAPTAIN_ID}\" />\n        {L_module_unit_captain} {CAPTAIN_LEVEL}:\n        {L_module_unit_captain_shield} +{CAPTAIN_SHIELD}% /\n        {L_module_unit_captain_armor} +{CAPTAIN_ARMOR}% /\n        {L_module_unit_captain_attack} +{CAPTAIN_ATTACK}%\n      </td>\n    </tr>\n    <!-- ENDIF -->\n    <tr><th class=\"c_c\">\n      <div class=\"warning fleet_expedition_warning\">{L_fl_expe_warning}</div>\n      <div class=\"error fleet_expedition_not_enough_fuel\">{L_fl_not_enough_fuel}</div>\n      <input id=\"fleet_page2_submit\" value=\"{L_fl_continue}\" type=\"submit\"<!-- IF $CANNOTSEND --> disabled<!-- ENDIF -->/>\n    </th></tr>\n  </table>\n<br />\n\n  <!-- IF IS_TRANSPORT_MISSIONS -->\n  <table id=\"resTable\" style=\"display: none;\"><tbody>\n    <tr class=\"c_c\">\n      <th>{L_sys_resources}</th>\n      <th>{L_fl_on_stores}</th>\n      <th>{L_fl_load_cargo}</th>\n      <th>{L_fl_rest_on_planet}</th>\n    </tr>\n    <!-- BEGIN resources -->\n      <tr>\n        <td class=\"c_l\">{resources.NAME}</td>\n        <td class=\"c_r\">{resources.TEXT}</td>\n        <td class=\"c_c\">\n          <!-- <ainput type=\"text\" name=\"resource{resources.ID}\" id=\"resource{resources.ID}\" max=\"0\" step=\"1\">step=\"1000\"</ainput> -->\n          <script type=\"text/javascript\">\n            fleet_page_2_prepare_slider({resources.ID}, {resources.ON_PLANET}, {fleet_capacity});\n          </script>\n        </td>\n        <td id=\"rest_res{resources.ID}\" class=\"c_r\">{resources.ON_PLANET}</td>\n      </tr>\n    <!-- END resources -->\n\n    <tr>\n      <th colspan=\"2\" class=\"c_l\">{L_fl_space_left}</th>\n      <th class=\"c_c\">\n        <div class=\"fl\"><input type=\"button\" onclick=\"zero_resources()\" value=\"{L_fl_none_resources}\"></div>\n        <div class=\"fr\"><input type=\"button\" onclick=\"max_resources()\" value=\"{L_fl_allressources}\"></div>\n      </th>\n      <th id=\"remainingresources\" class=\"c_r\">-</th>\n    </tr>\n  </tbody></table>\n  <!-- ENDIF -->\n\n</form>\n\n<script type=\"text/javascript\">\n  fleet_page_2_loaded();\n</script>\n"
  },
  {
    "path": "design/templates/OpenGame/fleet3.tpl.html",
    "content": "<br />\n\n<h2 class=\"success\">{L_fl_fleet_send}</h2>\n\n<!-- INCLUDE fleet_miniatures -->\n\n<table border=\"0\" cellpadding=\"0\" cellspacing=\"1\">\n  <tr><td class=\"c_c\">{L_fl_mission}</td><td class=\"c_c\">{mission}</td></tr>\n  <tr><td class=\"c_c\">{L_fl_dist}</td><td class=\"c_c\">{dist}</td></tr>\n  <tr><td class=\"c_c\">{L_fl_speed}</td><td class=\"c_c\">{speed}</td></tr>\n  <tr><td class=\"c_c\">{L_fl_deute_need}</td><td class=\"c_c\">{deute_need}</td></tr>\n  <tr>\n    <td class=\"c_c\" rowspan=\"2\">{L_fl_time_go}</td>\n    <td class=\"c_c\">{L_time_local}:&nbsp;{time_go_local}</td>\n  </tr>\n  <tr>\n    <td class=\"c_c\">{L_time_server}:&nbsp;{time_go}</td>\n  </tr>\n  <tr>\n    <td class=\"c_c\" rowspan=\"2\">{L_fl_time_back}</td>\n    <td class=\"c_c\">{L_time_local}:&nbsp;{time_back_local}</td>\n  </tr>\n  <tr>\n    <td class=\"c_c\">{L_time_server}:&nbsp;{time_back}</td>\n  </tr>\n\n  <!--<tr><td class=\"c\" colspan=\"2\">{L_fl_title}</td></tr>-->\n  <!-- !BEGIN ships -->\n  <!--<tr>-->\n    <!--<td class=\"c_c\">{ships.NAME}</td>-->\n    <!--<td class=\"c_c\">{ships.COUNT}</td>-->\n  <!--</tr>-->\n  <!-- !END ships -->\n</table>\n"
  },
  {
    "path": "design/templates/OpenGame/fleet5.tpl.html",
    "content": "<!-- INCLUDE fleet_javascript -->\n\n<script type=\"text/javascript\" src=\"js/fleet_gather.js\"></script>\n\n<script type=\"text/javascript\">\n  /* <!-- BEGIN colonies --> */\n  colonies['{colonies.ID}'] = [Math.intVal('{colonies.METAL}'), Math.intVal('{colonies.CRYSTAL}'), Math.intVal('{colonies.DEUTERIUM}'), Math.intVal('{colonies.FLEET_CAPACITY}')];\n  /* <!-- END colonies --> */\n</script>\n\n<h2>{L_flt_gather_all}</h2>\n<!-- IF .results -->\n  <h3>{L_flt_gather_report}</h3>\n  <table border=\"0\" cellpadding=\"0\" cellspacing=\"1\">\n    <tr>\n      <td class=\"c\">{L_fl_from}</td>\n      <td class=\"c\">{L_flt_report}</td>\n    </tr>\n\n    <!-- BEGIN results -->\n    <tr>\n      <td>{results.TYPE_PRINT} &quot;{results.NAME}&quot; [{results.GALAXY}:{results.SYSTEM}:{results.PLANET}]</td>\n      <td>\n        <b>{results.MESSAGE}</b><br>\n        <!-- BEGIN units -->\n          {results.units.NAME} {results.units.AMOUNT}<br />\n        <!-- END units -->\n      </td>\n    </tr>\n    <!-- END results -->\n  </table>\n  <br />\n<!-- ENDIF -->\n\n\n<form action=\"fleet.php?fleet_page=5\" method=\"post\">\n  <table border=\"0\" cellpadding=\"0\" cellspacing=\"1\">\n    <tr>\n      <td class=\"c\">{L_sys_coordinates}</td>\n      <td class=\"c\">{L_fl_from}</td>\n      <td class=\"c\"><input type=\"checkbox\" checked class=\"planet_gather\" onchange=\"jQuery('.planet_metal').attr('checked', jQuery(this).is(':checked'));reCalcGathering();\">{L_sys_metal}</td>\n      <td class=\"c\"><input type=\"checkbox\" checked class=\"planet_gather\" onchange=\"jQuery('.planet_crystal').attr('checked', jQuery(this).is(':checked'));reCalcGathering();\">{L_sys_crystal}</td>\n      <td class=\"c\"><input type=\"checkbox\" checked class=\"planet_gather\" onchange=\"jQuery('.planet_deuterium').attr('checked', jQuery(this).is(':checked'));reCalcGathering();\">{L_sys_deuterium}</td>\n      <td class=\"c\"><input type=\"checkbox\" checked id=\"planet_gather_all\" onchange=\"jQuery('.planet_gather').attr('checked', jQuery(this).is(':checked'));reCalcGathering();\">{L_res_total}</td>\n      <td class=\"c\">{L_sys_cargo_bays}</td>\n      <td class=\"c\">{L_sys_time}</td>\n    </tr>\n\n    <!-- BEGIN colonies -->       \n      <tr>\n        <th>[{colonies.GALAXY}:{colonies.SYSTEM}:{colonies.PLANET}]</th>\n        <th>\n          <span class=\"fl\">{colonies.TYPE_PRINT} &quot;{colonies.NAME}&quot;</span>\n        </th>\n        <th>\n          <span class=\"fl\"><input type=\"checkbox\" checked name=\"resources[{colonies.ID}][{D_RES_METAL}]\"     id=\"ga_{colonies.ID}_0\" value=\"1\" class=\"planet_gather planet_metal planet_{colonies.ID}\" onchange=\"reCalcGathering();\"></span>\n          <span class=\"fr\">{colonies.METAL_TEXT}</span>\n        </th>\n        <th>\n          <span class=\"fl\"><input type=\"checkbox\" checked name=\"resources[{colonies.ID}][{D_RES_CRYSTAL}]\"   id=\"ga_{colonies.ID}_1\" value=\"1\" class=\"planet_gather planet_crystal planet_{colonies.ID}\" onchange=\"reCalcGathering();\"></span>\n          <span class=\"fr\">{colonies.CRYSTAL_TEXT}</span>\n        </th>\n        <th>\n          <span class=\"fl\"><input type=\"checkbox\" checked name=\"resources[{colonies.ID}][{D_RES_DEUTERIUM}]\" id=\"ga_{colonies.ID}_2\" value=\"1\" class=\"planet_gather planet_deuterium planet_{colonies.ID}\" onchange=\"reCalcGathering();\"></span>\n          <span class=\"fr\">{colonies.DEUTERIUM_TEXT}</span>\n        </th>\n        <th>\n          <span class=\"fl\"><input type=\"checkbox\" checked name=\"planets[]\" value=\"{colonies.ID}\" id=\"planet_{colonies.ID}\" class=\"planet_gather\" onchange=\"jQuery('.planet_{colonies.ID}').attr('checked', jQuery(this).is(':checked'));reCalcGathering();\"></span>\n          <span class=\"fr\" id=\"ga_{colonies.ID}_a\">0</span>\n        </th>\n        <!-- IF colonies.FLEET_CAPACITY -->\n        <th><span class=\"fr\">{colonies.FLEET_CAPACITY_TEXT}</span></th>\n        <th><span class=\"fr\">{colonies.DURATION_TEXT}</span></th>\n        <!-- ELSE -->\n        <th colspan=2><span class=\"negative\">{L_flt_no_transports}</span></th>\n        <!-- ENDIF -->\n      </tr>\n    <!-- BEGINELSE colonies -->\n      <th colspan=\"8\">{L_fl_nocolonies}</th>\n    <!-- END colonies -->\n\n    <!-- IF METAL_NEED > 0 || CRYSTAL_NEED > 0 || DEUTERIUM_NEED > 0 -->\n      <!-- DEFINE $ROWSPAN = 3 -->\n    <!-- ELSE -->\n      <!-- DEFINE $ROWSPAN = 2 -->\n    <!-- ENDIF -->\n    <tr class=\"c_r\">\n      <th class=\"c_l\" colspan=\"2\">{L_sys_total}</th>\n      <th><span id=\"ga_a_0\"></span></th>\n      <th><span id=\"ga_a_1\"></span></th>\n      <th><span id=\"ga_a_2\"></span></th>\n      <th><span id=\"ga_a_a\"></span></th>\n      <th class=\"c_c\" colspan=\"2\" rowspan=\"{$ROWSPAN}\"><input type=\"submit\" value=\"{L_flt_gather_all}\" /></th>\n    </tr>\n\n    <!-- IF $ROWSPAN > 2 -->\n    <tr class=\"c_r\">\n      <th class=\"c_l\" colspan=\"2\">{L_sys_need}</th>\n      <th>{METAL_NEED}</th>\n      <th>{CRYSTAL_NEED}</th>\n      <th>{DEUTERIUM_NEED}</th>\n      <th>&nbsp;</th>\n    </tr>\n    <!-- ENDIF -->\n\n    <tr class=\"c_r\">\n      <th class=\"c_l\" colspan=\"2\">{L_sys_on_planet}</th>\n      <th>{PLANET_METAL_TEXT}</th>\n      <th>{PLANET_CRYSTAL_TEXT}</th>\n      <th>{PLANET_DEUTERIUM_TEXT}</th>\n      <th>&nbsp;</th>\n    </tr>\n  </table>\n</form>\n\n<!-- INCLUDE page_hint -->\n"
  },
  {
    "path": "design/templates/OpenGame/fleet_aks_invite.tpl.html",
    "content": "<h1>{PAGE_HEADER}</h1>\n\n<!-- INCLUDE fleet_miniatures -->\n\n<div class=\"contFC\">\n  <div class=\"border_image_large\">\n\n    <div class=\"cell\">\n      <form action=\"fleet.php?fleet_page=4\" method=\"POST\">\n        <input type=\"hidden\" name=\"fleetid\" value=\"{FLEET_ID}\">\n\n        <label for=\"fleetAcsAddName\">{L_flt_aks_player_invite}</label>\n        <input id=\"fleetAcsAddName\" name=\"addtogroup\" type=\"text\"/>\n        <input type=\"submit\" value=\"{L_flt_aks_player_invite_do}\"/>\n      </form>\n    </div>\n\n    <div class=\"header\">\n      {L_flt_aks_players_in_aks}\n    </div>\n\n    <div class=\"subheader\">\n      <select size=\"5\" style=\"overflow-y: auto\">\n        <!-- BEGIN invited -->\n        <option>{invited.NAME}</option>\n        <!-- END invited -->\n      </select>\n    </div>\n\n  </div>\n</div>\n"
  },
  {
    "path": "design/templates/OpenGame/fleet_javascript.tpl.html",
    "content": "<script type=\"text/javascript\"><!--\n  var UNIVERSE_GALAXY_DISTANCE = {C_uni_galaxy_distance};\n--></script>\n<script type=\"text/javascript\" src=\"js/fleet.js?{C_var_db_update}\"></script>\n<script type=\"text/javascript\"><!--\nvar fleets = Array();\n<!-- BEGIN fleets -->\n  fleets['{fleets.ID}'] = [[\n    <!-- BEGIN ships -->\n    ['{ships.NAME}', '{ships.AMOUNT}', '{ships.SPEED}', '{ships.CONSUMPTION}', '{ships.CAPACITY}', '{ships.ID}'],\n    <!-- END ships -->\n  ], ['{fleets.METAL}', '{fleets.CRYSTAL}', '{fleets.DEUTERIUM}']];\n<!-- END fleets -->\nvar res_names = ['{LA_sys_metal}', '{LA_sys_crystal}', '{LA_sys_deuterium}'];\njQuery.extend(language, {sys_fleet_composition: '{LA_sys_fleet_composition}', sys_ships: '{LA_sys_ships}', sys_resources: '{LA_sys_resources}', sys_capacity: '{LA_sys_capacity}'});\n\nvar SHIP_SPY = {D_SHIP_SPY};\n\n--></script>\n<script type=\"text/javascript\">\n  var planet_types = {\n    <!-- BEGIN possible_planet_type_id -->\n    {possible_planet_type_id.ID}: '{possible_planet_type_id.NAME}',\n    <!-- END possible_planet_type_id -->\n  };\n  var planet_names = {};\n</script>\n"
  },
  {
    "path": "design/templates/OpenGame/fleet_miniatures.tpl.html",
    "content": "<script type=\"text/javascript\">\n  <!-- BEGIN fleets -->\n  <!-- IF fleets.START_LEFT -->\n  sn_timers.unshift({id: 'fleet_timer_start{fleets.ID}', type: TIMER_BUILD_QUE_V1, options: { msg_done: '{L_sys_fleet_arrived}', que: [\n    ['{fleets.ID}', '', {fleets.START_LEFT}, '0']\n  ]}});\n  <!-- ENDIF -->\n  <!-- IF fleets.END_LEFT -->\n  sn_timers.unshift({id: 'fleet_timer_end{fleets.ID}', type: TIMER_BUILD_QUE_V1, options: { msg_done: '{L_sys_fleet_arrived}', que: [\n    ['{fleets.ID}', '', {fleets.END_LEFT}, '0']\n  ]}});\n  <!-- ENDIF -->\n  <!-- END fleets -->\n</script>\n\n<!-- BEGIN fleets -->\n  <!-- IF fleets.START_COORDS -->\n  <div class=\"flt-mini-mission\">\n    <div class=\"flt-mini-mission-planet\">\n      {fleets.START_COORDS} {fleets.START_TYPE_TEXT_SH} {fleets.START_NAME}\n      <!-- IF ((fleets.MISSION == 7 || fleets.MISSION == 4) && fleets.MESSAGE == 1) || (fleets.MISSION != 7 && fleets.MISSION != 4) -->\n      <div id=\"fleet_timer_start{fleets.ID}\"></div>\n      {fleets.START_TIME_TEXT}\n      <!-- ELSE -->\n      -\n      <!-- ENDIF -->\n    </div>\n\n    <div id=\"fleet_mini_mission\" class=\"notice\">\n      <img src=\"design/images/mission_pointer.png\" />\n      <!-- IF MISSION_NAME -->\n      <br /><span>{MISSION_NAME}</span>\n      <!-- ENDIF -->\n    </div>\n\n    <div>\n      <div class=\"flt-mini-mission-planet\">\n        <span id=\"end_coords\">{fleets.END_COORDS}</span>\n        <span id=\"end_planet_type\">{fleets.END_TYPE_TEXT_SH}</span> <span id=\"end_name\">{fleets.END_NAME}</span>\n      </div>\n      <!-- IF fleets.MESSAGE == 0 -->\n      <div id=\"fleet_timer_end{fleets.ID}\"></div>\n      <div>\n        {fleets.END_TIME_TEXT}\n      </div>\n      <!-- ELSE -->\n      -\n      <!-- ENDIF -->\n    </div>\n  </div>\n  <!-- ENDIF -->\n\n  <div id=\"fleet_unit_miniatures_container\">\n    <!-- BEGIN ships -->\n    <div class=\"unit_miniatures_wrapper\" unit_id=\"{ships.ID}\">\n      <img src=\"{I_[ships.ID]}\" style=\"width: 100%; height: 100%;\" />\n      <!--<img id=\"unit_info_image\" />-->\n      <div style=\"width: 100%; height: 100%; position: absolute; top: 0; left: 0;\">\n        <div id=\"unit_info_name\" class=\"a75\">{ships.NAME}</div>\n        <div id=\"unit_info_level\" class=\"a75\">{ships.AMOUNT_TEXT}</div>\n      </div>\n    </div>\n    <!-- END ships -->\n  </div>\n<!-- END fleets -->\n"
  },
  {
    "path": "design/templates/OpenGame/fleet_page_0_buttons.tpl.html",
    "content": "<!-- IF FLYING_FLEETS < MAX_FLEETS -->\n<!-- DEFINE $SHIP_COLSPAN = '1' -->\n<tr class=\"c_c\">\n  <th>\n    {L_fl_fleet_typ}\n  </th>\n\n  <!-- IF PLAYER_OPTION_FLEET_SHIP_SELECT_OLD -->\n  <th>\n    {L_fl_count}\n  </th>\n  <!-- ENDIF -->\n\n  <th style=\"padding-right: 0; border-right: 0\" class=\"c_l\" >\n    <div id=\"fleet_page0_footer\" class=\"contFJ\">\n      <input type=\"button\" value=\"{L_fl_unselectall}\" onclick=\"zero_fleet();\" />\n      <!-- IF MAX_EXPEDITIONS > 1 -->\n      <input type=\"button\" value=\"1/{MAX_EXPEDITIONS}\" onclick=\"expe_fleet({MAX_EXPEDITIONS});\" />\n      <!-- ENDIF -->\n\n      <!-- IF FREE_EXPEDITIONS > 1 && MAX_EXPEDITIONS > 1 && FREE_EXPEDITIONS != MAX_EXPEDITIONS -->\n      <input type=\"button\" value=\"1/{FREE_EXPEDITIONS}\" onclick=\"expe_fleet({FREE_EXPEDITIONS});\" />\n      <!-- ENDIF -->\n      <input type=\"button\" value=\"{L_fl_selectall}\" onclick=\"max_fleet();\" />\n    </div>\n  </th>\n</tr>\n<!-- ELSE -->\n<!-- DEFINE $SHIP_COLSPAN = '2' -->\n<!-- DEFINE $SHIP_NO_SLOTS_SIZE_CLASS = 'no_slots' -->\n<!-- ENDIF -->"
  },
  {
    "path": "design/templates/OpenGame/fleet_page_0_buttons_action.tpl.html",
    "content": "<!-- IF FLYING_FLEETS < MAX_FLEETS -->\n<tr class=\"c_c\">\n  <th colspan=\"{$FLEET_SHIP_SELECT_MAIN_COL_SPAN}\">\n    <input type=\"button\" class=\"fl\" value=\"{L_flt_gather_all}\" onclick=\"document.location = 'fleet.php?fleet_page=5';\" />\n    <input class=\"fr\" type=\"submit\" value=\"{L_fl_continue}\" />\n  </th>\n</tr>\n<!-- ELSEIF ! $FLEET_PAGE_0_ACTION -->\n<!-- DEFINE $FLEET_PAGE_0_ACTION = '1' -->\n<tr>\n  <th colspan=\"{$FLEET_SHIP_SELECT_MAIN_COL_SPAN}\" class=\"error\">{L_fl_noslotfree}</th>\n</tr>\n<!-- ENDIF -->\n"
  },
  {
    "path": "design/templates/OpenGame/flying_fleets.tpl.html",
    "content": "<!--\n@package fleet\n2.1 Overlib replaced with jQuery\n2.0 Utilize PTE\n    Comply with PCG\n-->\n\n<script type=\"text/javascript\"><!--\nvar ships = Array();\n<!-- BEGIN ships -->\n  <!-- IF (ships.SPEED > 0) -->\n  ships[{ships.ID}] = Array({ships.AMOUNT}, {ships.SPEED}, {ships.CONSUMPTION}, {ships.CAPACITY});\n  <!-- ENDIF -->\n<!-- END ships -->\n\n  $(document).on('change', '#fleet_select_all', function(){\n    $('[name^=\"return\"]').prop('checked', $(this).is(':checked'));\n  });\n\n  $(document).on('click', '.return_fleet', function(){\n    $(this).parent().parent().find('[name^=\"return\"]').prop('checked', true).closest('form').submit();\n  });\n\n  $(document).on('click', '#fleet_return_do', function(){\n    $(this).closest('form').submit();\n  });\n--></script>\n\n<!-- INCLUDE fleet_javascript -->\n\n<br>\n<form action=\"\" method=\"post\">\n<table border='0' cellpadding='0' cellspacing='1'>\n  <tr class='c_c'>\n    <th colspan=9>\n      <div class=\"fl\">{L_fl_title}: {FLEETS_FLYING}/{FLEETS_MAX}</div>\n      <div class=\"fr\">{L_fl_expttl}: {EXPEDITIONS_FLYING}/{EXPEDITIONS_MAX}</div>\n    </th>\n  </tr>\n\n  <tr height='20'>\n    <th>{L_fl_id}</th>\n    <th>{L_fl_mission}</th>\n    <th>{L_fl_count_short}</th>\n    <th>{L_fl_dest}</th>\n    <th>{L_fl_dest_t}</th>\n    <th>{L_fl_from}</th>\n    <th>{L_fl_from_t}</th>\n    <th>{L_fl_order}</th>\n    <th><input type=\"checkbox\" id=\"fleet_select_all\" /></th>\n  </tr>\n\n  <!-- BEGIN fleets -->\n    <!-- IF fleets.MISSION == 1 -->\n      <!-- DEFINE $OV_FLEET_STYLE = 'attack' -->\n    <!-- ELSEIF fleets.MISSION ==  2 -->\n      <!-- DEFINE $OV_FLEET_STYLE = 'federation' -->\n    <!-- ELSEIF fleets.MISSION ==  3 -->\n      <!-- DEFINE $OV_FLEET_STYLE = 'transport' -->\n    <!-- ELSEIF fleets.MISSION ==  4 -->\n      <!-- DEFINE $OV_FLEET_STYLE = 'deploy' -->\n    <!-- ELSEIF fleets.MISSION ==  5 -->\n      <!-- DEFINE $OV_FLEET_STYLE = 'hold' -->\n    <!-- ELSEIF fleets.MISSION ==  6 -->\n      <!-- DEFINE $OV_FLEET_STYLE = 'espionage' -->\n    <!-- ELSEIF fleets.MISSION ==  7 -->\n      <!-- DEFINE $OV_FLEET_STYLE = 'colony' -->\n    <!-- ELSEIF fleets.MISSION ==  8 -->\n      <!-- DEFINE $OV_FLEET_STYLE = 'harvest' -->\n    <!-- ELSEIF fleets.MISSION ==  9 -->\n      <!-- DEFINE $OV_FLEET_STYLE = 'destroy' -->\n    <!-- ELSEIF fleets.MISSION == 10 -->\n      <!-- DEFINE $OV_FLEET_STYLE = 'missile' -->\n    <!-- ELSEIF fleets.MISSION == 15 -->\n      <!-- DEFINE $OV_FLEET_STYLE = 'expedition' -->\n    <!-- ENDIF -->\n\n    <tr height=20 class=\"own{$OV_FLEET_STYLE}\">\n      <th>{fleets.NUMBER}</th>\n      <th>{fleets.MISSION_NAME}<br>\n        <div><!-- IF fleets.MESSAGE -->{L_ov_fleet_return}<!-- ELSE -->{L_ov_fleet_arrive}<!-- ENDIF --></div>\n      </th>\n      <th style=\"cursor: pointer;\" onmouseover='fleet_dialog_show(this, {fleets.ID})' onmouseout='popup_hide()'>{fleets.AMOUNT}</th>\n      <th>{fleets.END_URL} {fleets.END_TYPE_TEXT_SH}</th>\n      <th><!-- IF fleets.MESSAGE == 0 --><div id=\"fleet_timer_end{fleets.ID}\"></div>{fleets.END_TIME_TEXT}<!-- ELSE -->-<!-- ENDIF --></th>\n      <th>{fleets.START_URL} {fleets.START_TYPE_TEXT_SH}</th>\n      <th>\n        <!-- IF ((fleets.MISSION == 7 || fleets.MISSION == 4) && fleets.MESSAGE == 1) || (fleets.MISSION != 7 && fleets.MISSION != 4) -->\n        <div id=\"fleet_timer_start{fleets.ID}\"></div>{fleets.START_TIME_TEXT}\n        <!-- ELSE -->-<!-- ENDIF -->\n      </th>\n      <th>\n        <!-- IF fleets.MESSAGE == 0 -->\n          <input value=\"{L_flt_return_fleet}\" type=\"button\" class=\"return_fleet\" />\n          <!-- IF fleets.MISSION == 1 || fleets.MISSION == 2 -->\n            <br/>\n            <input value=\"<!-- IF fleets.MISSION == 1 -->{L_fl_associate}<!-- ELSE -->{fleets.ACS}<!-- ENDIF -->\" type=\"button\"\n                   onclick=\"sn_redirect('fleet.php?fleet_page=4&fleetid={fleets.ID}');\" />\n          <!-- ENDIF -->\n        <!-- ELSE -->\n          {L_fl_isback}\n        <!-- ENDIF -->\n      </th>\n      <th>\n        <input type=\"checkbox\" name=\"return[]\" value=\"{fleets.ID}\" />\n      </th>\n    </tr>\n    <script type=\"text/javascript\"><!--\n      sn_timers.unshift({id: 'fleet_timer_start{fleets.ID}', type: TIMER_BUILD_QUE_V1, options: { msg_done: '{L_sys_fleet_arrived}', que: [\n        ['{fleets.ID}', '', {fleets.START_LEFT}, '0']\n      ]}});\n      sn_timers.unshift({id: 'fleet_timer_end{fleets.ID}', type: TIMER_BUILD_QUE_V1, options: { msg_done: '{L_sys_fleet_arrived}', que: [\n        ['{fleets.ID}', '', {fleets.END_LEFT}, '0']\n      ]}});\n    --></script>\n  <!-- BEGINELSE fleets -->\n    <tr><th colspan=\"9\">{L_fl_no_flying_fleets}</th></tr>\n  <!-- END fleets -->\n\n  <!-- IF FLEETS_FLYING >= FLEETS_MAX -->\n    <tr><th class=\"c_c negative\" colspan=\"9\">{L_fl_noslotfree}</th></tr>\n  <!-- ELSE -->\n    <tr>\n      <th class=\"c_c\" colspan=\"7\"><input type=\"button\" value=\"{L_flt_gather_all}\" onclick=\"document.location = 'fleet.php?fleet_page=5';\"></th>\n      <th class=\"c_c\" colspan=\"2\"><input type=\"button\" value=\"{L_flt_return_all}\" id=\"fleet_return_do\" /></th>\n    </tr>\n  <!-- ENDIF -->\n</table><br>\n</form>\n\n<!-- INCLUDE page_hint -->\n"
  },
  {
    "path": "design/templates/OpenGame/imperator.tpl.html",
    "content": "  <div class=\"border_image_small\" style=\"display: inline-flex; flex-direction: column; margin: 0.5em;\">\n    <div class=\"a50\" style=\"padding: 1em;\">\n      <h2>{L_imp_imperator} {user_username}</h2>\n\n      <h3>{L_sys_from} {REGISTRATION_DATE}</h3>\n\n      <!-- IF USER_AVATAR --><img src=\"{D_SN_HTTP_AVATAR}/avatar_{USER_ID}.png\"><!-- ENDIF -->\n      <h2 class=\"link\">\n        <a href=\"stat.php?start={user_rank}\">\n          {L_ov_rank}:\n          <span class=\"ov_user_rank\">{user_rank} ({RANK_DIFF})</span>\n          /\n          <span class=\"ov_user_total\">{USERS_TOTAL}</span>\n        </a>\n      </h2>\n\n      <a class=\"link\" href=\"ranks.php\">\n        <h3>{L_rank}: [{PLAYER_RANK_NUMBER}] {PLAYER_RANK_NAME}</h3>\n        <div class=\"rank rank-emperor rank-{PLAYER_RANK_NUMBER}\"></div>\n      </a>\n      <h3>{L_sys_gender} {GENDER_TEXT}</h3>\n      <!-- IF VACATION -->\n      <h3>{L_sys_vacation_in}</h3>\n      <!-- ENDIF -->\n    </div>\n  </div>\n<!-- IF 1 || (SAME_USER && NEW_MESSAGES) -->\n<div style=\"width: 50em; display: block; margin: 1em;\" class=\"border_image_small\">\n  <a href=\"messages.php\" class=\"a50 h100\" style=\"padding: 0.5em; display: block\">\n    {L_ov_you_have} <!-- IF NEW_MESSAGES == 1 -->{L_ov_new_message}<!-- ELSE -->{NEW_MESSAGES} {L_ov_new_messages}\n    <!-- ENDIF -->\n  </a>\n</div>\n<!-- ENDIF -->\n\n<table style=\"width: 50em\">\n  <tr class=\"c_c\">\n    <th rowspan=\"2\">{L_imp_statistics}</th>\n    <th rowspan=\"2\">{L_ov_points}</th>\n    <th colspan=\"2\">{L_ov_experience}</th>\n    <th rowspan=\"2\">{L_sys_level}</th>\n  </tr>\n  <tr class=\"c_c\">\n    <th>{L_imp_experience_current}</th>\n    <th>{L_imp_experience_to_level}</th>\n  </tr>\n\n  <tr class=\"c_r\">\n    <th class=\"c_l\">{L_Economica}</th>\n    <td>{build_points}</td>\n    <td>{builder_xp}</td>\n    <td>{builder_lvl_up}</td>\n    <td>{builder_lvl}</td>\n  </tr>\n  <tr class=\"c_r\">\n    <th class=\"c_l\">{L_imp_research}</th>\n    <td>{tech_points}</td>\n    <td>{tech_xp}</td>\n    <td>{tech_lvl_up}</td>\n    <td>{tech_lvl}</td>\n  </tr>\n  <tr class=\"c_r\">\n    <th class=\"c_l\">{L_imp_exploration}</th>\n    <td>--</td>\n    <td>{explore_xp}</td>\n    <td>{explore_lvl_up}</td>\n    <td>{explore_lvl}</td>\n  </tr>\n  <tr class=\"c_r\">\n    <th class=\"c_l\">{L_ov_raids}</th>\n    <td colspan=\"1\" align=\"center\">\n      <table width=100% class=\"markup\">\n        <tr>\n          <td class=\"c_l\">{L_NumberOfRaids}</td>\n          <td class=\"c_r\">{raids}</td>\n        </tr>\n        <tr>\n          <td class=\"c_l\">{L_RaidsWin}</td>\n          <td class=\"c_r\">{raidswin}</td>\n        </tr>\n        <tr>\n          <td class=\"c_l\">{L_RaidsLoose}</td>\n          <td class=\"c_r\">{raidsloose}</td>\n        </tr>\n      </table>\n    </td>\n    <td>{raid_xp}</td>\n    <td>{raid_lvl_up}</td>\n    <td>{raid_lvl}</td>\n  </tr>\n  <tr class=\"c_r\">\n    <th class=\"c_l\">{L_imp_user_points_fleet}</th>\n    <td>{fleet_points}</td>\n    <td rowspan=\"4\" colspan=\"3\" class=\"c_c\">{L_imp_TBA}</td>\n  </tr>\n  <tr class=\"c_r\">\n    <th class=\"c_l\">{L_imp_user_points_def}</th>\n    <td>{defs_points}</td>\n  </tr>\n  <tr class=\"c_r\">\n    <th class=\"c_l\">{L_imp_user_points_res}</th>\n    <td>{res_points}</td>\n  </tr>\n  <tr class=\"c_r\">\n    <th class=\"c_l\">{L_imp_user_points_all} {Очков}</th>\n    <th>{total_points}</th>\n  </tr>\n</table>\n\n<script type=\"text/javascript\">\n  var stat = {\n    <!-- BEGIN stat -->\n    '{stat.TYPE}': {\n      'MIN': '{stat.MIN}',\n      'MAX': '{stat.MAX}',\n      'AVG': '{stat.AVG}',\n      'DATA': {\n        <!-- BEGIN entry -->\n        '{entry.ID}': {\n          // 'ID': '{entry.ID}',\n          'VALUE': '{entry.VALUE}',\n          'DELTA': '{entry.DELTA}',\n          'PERCENT': '{entry.PERCENT}',\n        },\n        <!-- END entry -->\n      },\n    },\n    <!-- END stat -->\n  };\n  var STAT_COUNT = parseInt('{STAT_COUNT}') ? parseInt('{STAT_COUNT}') : 0;\n\n  var stat_date = {\n    <!-- BEGIN stat_date -->\n    '{stat_date.ID}': {\n      // 'ID': '{entry.ID}',\n      'VALUE': '{stat_date.VALUE}',\n      'TEXT': '{stat_date.TEXT}',\n    },\n    <!-- END stat_date -->\n  };\n\n  // console.log(stat);\n</script>\n\n<script type=\"text/javascript\">\n  $(document).ready(function () {\n    $(document).on('change', '#stat_type_select', function () {\n      stat_type = $(this).val();\n      $('#stat_cell_min_max').find('.stat_min').each(function () {\n        $(this).text(sn_format_number(stat[stat_type]['MIN']));\n        $(this).parent().find('.stat_max').text(sn_format_number(stat[stat_type]['MAX']));\n        $(this).parent().find('.stat_avg').text(sn_format_number(stat[stat_type]['AVG']));\n      });\n      $('.stat_cell').each(function () {\n        that = $(this);\n        stat_id = that.attr('stat_id');\n        // that.find('.stat_bar').height(stat[stat_type]['DATA'][stat_id]['PERCENT'] + '%');\n\n        that.find('.stat_bar').animate({height: stat[stat_type]['DATA'][stat_id]['PERCENT'] + '%'});\n        // that.find('.stat_bar').animate({height: stat[stat_type]['DATA'][stat_id]['PERCENT']}, 1000);\n\n\n        that.find('.stat_value').text(sn_format_number(stat[stat_type]['DATA'][stat_id]['VALUE']));\n        that.find('.stat_delta').html('(' + sn_format_number(stat[stat_type]['DATA'][stat_id]['DELTA'], 0, 'positive', 0, true) + ')');\n      });\n    });\n\n    $('#stat_type_select').change();\n  });\n</script>\n\n<!-- IF .stat -->\n<div class=\"border_image_large\" style=\"display: inline-block; margin: 1em;\">\n  <div class=\"a50 h100\" style=\"font-size: 125%; padding: 0.5em;\">\n    {L_imp_stat_header}\n    <select id=\"stat_type_select\" name=\"stat_type_select\">\n      <!-- BEGIN stat -->\n      <option value=\"{stat.TYPE}\">{stat.TEXT}</option>\n      <!-- END stat -->\n    </select>\n  </div>\n\n  <table class=\"no_border_image\">\n    <tr class=\"c_c\">\n      <th>{L_sys_date}</th>\n      <!-- BEGIN stat_date -->\n      <th class=\"stat_date_cell\">\n        {stat_date.TEXT}\n      </th>\n      <!-- END stat_date -->\n    </tr>\n    <tr>\n      <td id=\"stat_cell_min_max\">\n        <div class=\"stat_max positive\">\n        </div>\n\n        <div class=\"stat_avg neutral\">\n        </div>\n\n        <div class=\"stat_min negative\">\n        </div>\n\n        <div style=\"visibility: hidden;\">\n          <div class=\"stat_min\"></div>\n          <div class=\"stat_max\"></div>\n        </div>\n      </td>\n      <!-- BEGIN stat_date -->\n      <td stat_id=\"{stat_date.ID}\" class=\"stat_cell\">\n        <div class=\"ok_bg stat_bar\">\n        </div>\n\n        <div class=\"stat_value\"></div>\n        <div class=\"stat_delta\"></div>\n        <div style=\"visibility: hidden;\">\n          <div class=\"stat_value\"></div>\n          <div class=\"stat_delta\"></div>\n        </div>\n      </td>\n      <!-- END stat_date -->\n    </tr>\n  </table>\n</div>\n<!-- ENDIF -->\n\n<!-- IF SAME_USER -->\n  <!-- IF BANNER_URL -->\n  <table width=\"519\" align=\"center\">\n    <tr>\n      <td class=\"c\">{L_aff_banner}</td>\n    </tr>\n    <tr>\n      <th>\n        <img src=\"{BANNER_URL}\"><br><br>\n        {L_aff_banner_bb}<br>\n        <input type=\"text\" value=\"[img]{BANNER_URL}[/img]\" size=\"55\">\n      </th>\n    </tr>\n  </table>\n  <!-- ENDIF -->\n\n  <!-- IF USERBAR_URL -->\n  <table width=\"519\" align=\"center\">\n    <tr>\n      <td class=\"c\">{L_aff_userbar}</td>\n    </tr>\n    <tr>\n      <th>\n        <img src=\"{USERBAR_URL}\"><br><br>\n        {L_aff_userbar_bb}<br>\n        <input type=\"text\" value=\"[img]{USERBAR_URL}[/img]\" size=\"55\">\n      </th>\n    </tr>\n  </table>\n  <!-- ENDIF -->\n<!-- ENDIF -->\n"
  },
  {
    "path": "design/templates/OpenGame/imperium.tpl.html",
    "content": "<!-- INCLUDE fleet_javascript -->\n\n<!-- DEFINE $CELL_HEADER = '70px' -->\n<!-- DEFINE $CELL_PLANET_CLASS = 'planet_list_top_small' -->\n\n<h2>{L_imp_overview}</h2>\n<form method=\"post\">\n<table border=\"0\" cellpadding=\"0\" cellspacing=\"1\" align=center id=\"empire_overview\" class=\"c_c\"><tbody>\n  <tr><th width=\"{$CELL_HEADER}\" class=\"c_l\" colspan=\"{amount}\">{L_sys_planets}</th></tr>\n\n  <tr>\n    <td width=\"{$CELL_HEADER}\">{L_imp_name}</td>\n    <!-- BEGIN planet -->\n      <td>\n        <!-- IF planet.IS_CAPITAL -->&#9813;<!-- ENDIF -->\n        <!-- IF planet.IS_MOON -->&#9789;<!-- ENDIF -->\n        <!-- IF planet.ID -->\n          <a href=\"overview.php?cp={planet.ID}\">{planet.NAME}</a>\n        <!-- ELSE -->\n          {planet.NAME}\n        <!-- ENDIF -->\n      </td>\n    <!-- END planet -->\n  </tr>\n\n  <tr>\n    <td>{L_sys_coordinates}</td>\n    <!-- BEGIN planet -->\n      <td width=\"{$CELL_HEADER}\">\n        <!-- IF planet.ID -->\n          <a href=\"overview.php?cp={planet.ID}\">{planet.COORDINATES}</a>\n        <!-- ENDIF -->&nbsp;\n      </td>\n    <!-- END planet -->\n  </tr>\n\n  <tr>\n    <td>\n      {L_sys_colonies} {COLONIES_CURRENT}/{COLONIES_MAX}<br />\n      {L_sys_expeditions} {EXPEDITIONS_CURRENT}/{EXPEDITIONS_MAX}\n    </td>\n    <!-- INCLUDE planet_list -->\n  </tr>\n\n  <tr>\n    <td>{L_sys_temperature}</td>\n    <!-- BEGIN planet -->\n      <!-- IF planet.TEMP_MAX < 40 -->\n        <!-- DEFINE $TEMP_COLOR_STYLE = 'negative' -->\n      <!-- ELSEIF planet.TEMP_MAX > 40 -->\n        <!-- DEFINE $TEMP_COLOR_STYLE = 'positive' -->\n      <!-- ELSE -->\n        <!-- DEFINE $TEMP_COLOR_STYLE = '' -->\n      <!-- ENDIF -->\n\n      <td class=\"{$TEMP_COLOR_STYLE}\">\n        {planet.TEMP_MIN} / {planet.TEMP_MAX}\n      </td>\n    <!-- END planet -->\n  </tr>\n\n  <tr>\n    <td>\n      {L_sys_planet_density_core}\n      <div class=\"icons link icon-info_empire\" go=\"info\" unit_id=\"{D_UNIT_PLANET_DENSITY}\"></div>\n    </td>\n    <!-- BEGIN planet -->\n      <!-- IF planet.DENSITY_RICHNESS == PLANET_DENSITY_RICHNESS_NORMAL -->\n        <!-- DEFINE $HTML_DENSITY_COLOR = '' -->\n      <!-- ELSEIF planet.DENSITY_RICHNESS == PLANET_DENSITY_RICHNESS_AVERAGE -->\n        <!-- DEFINE $HTML_DENSITY_COLOR = 'zero' -->\n      <!-- ELSEIF planet.DENSITY_RICHNESS == PLANET_DENSITY_RICHNESS_GOOD -->\n        <!-- DEFINE $HTML_DENSITY_COLOR = 'warning' -->\n      <!-- ELSEIF planet.DENSITY_RICHNESS == PLANET_DENSITY_RICHNESS_PERFECT -->\n        <!-- DEFINE $HTML_DENSITY_COLOR = 'negative' -->\n      <!-- ELSE -->\n        <!-- DEFINE $HTML_DENSITY_COLOR = 'positive' -->\n      <!-- ENDIF -->\n\n      <td class=\"{$HTML_DENSITY_COLOR}\">\n        {planet.DENSITY_CLASS_TEXT}\n      </td>\n    <!-- END planet -->\n  </tr>\n\n  <tr>\n    <th class=\"c_l\" colspan=\"{amount}\">\n      {L_sys_resources}\n    </th>\n  </tr>\n  <tr>\n    <td>{L_sys_metal}<br>{L_imp_production}</td>\n    <!-- BEGIN planet -->\n      <!-- DEFINE $RESOURCE_AMOUNT = '{planet.METAL_CUR}' -->\n      <!-- DEFINE $RESOURCE_INCOMING = '{planet.RES_901_TEXT}' -->\n      <!-- DEFINE $RESOURCE_PRODUCTION_PERCENT = '{planet.METAL_PERCENT}' -->\n      <!-- DEFINE $RESOURCE_PRODUCTION = '{planet.METAL_PROD_TEXT}' -->\n\n      <!-- INCLUDE imperium_resource_cell -->\n    <!-- END planet -->\n  </tr>\n  <tr>\n    <td>{L_sys_crystal}<br>{L_imp_production}</td>\n    <!-- BEGIN planet -->\n      <!-- DEFINE $RESOURCE_AMOUNT = '{planet.CRYSTAL_CUR}' -->\n      <!-- DEFINE $RESOURCE_INCOMING = '{planet.RES_902_TEXT}' -->\n      <!-- DEFINE $RESOURCE_PRODUCTION_PERCENT = '{planet.CRYSTAL_PERCENT}' -->\n      <!-- DEFINE $RESOURCE_PRODUCTION = '{planet.CRYSTAL_PROD_TEXT}' -->\n\n      <!-- INCLUDE imperium_resource_cell -->\n    <!-- END planet -->\n  </tr>\n  <tr>\n    <td>{L_sys_deuterium}<br>{L_imp_production}</td>\n    <!-- BEGIN planet -->\n      <!-- DEFINE $RESOURCE_AMOUNT = '{planet.DEUTERIUM_CUR}' -->\n      <!-- DEFINE $RESOURCE_INCOMING = '{planet.RES_903_TEXT}' -->\n      <!-- DEFINE $RESOURCE_PRODUCTION_PERCENT = '{planet.DEUTERIUM_PERCENT}' -->\n      <!-- DEFINE $RESOURCE_PRODUCTION = '{planet.DEUTERIUM_PROD_TEXT}' -->\n\n      <!-- INCLUDE imperium_resource_cell -->\n    <!-- END planet -->\n  </tr>\n  <tr>\n    <td>{L_sys_energy}<br>{L_imp_production}</td>\n    <!-- BEGIN planet -->\n    <!-- IF planet.ID --><!-- DEFINE $RES_ATTR = 'go=\"res\" planet_id=\"{planet.ID}\"' --><!-- ELSE --><!-- DEFINE $RES_ATTR = '' --><!-- ENDIF -->\n    <td {$RES_ATTR}>\n      {planet.ENERGY_CUR|num|color}<br />\n      {planet.ENERGY_MAX|num|format}\n    </td>\n    <!-- END planet -->\n  </tr>\n\n  <!-- BEGIN prods -->\n  <tr prod_mode=\"{prods.MODE}\">\n    <!-- IF prods.MODE -->\n      <td>{prods.NAME}</td>\n    <!-- ELSE -->\n      <th colspan=\"{amount}\"><div class=\"prod_header\"><span>{prods.NAME}</span><input type=\"submit\" name=\"save_production\" value=\"{L_sys_save}\" /></div></th>\n    <!-- ENDIF -->\n\n    <!-- BEGIN planet -->\n      <!-- IF prods.URL -->\n        <!-- DEFINE $FIELD_URL = '{prods.URL}' -->\n      <!-- ELSE -->\n        <!-- DEFINE $FIELD_URL = '' -->\n        <!-- DEFINE $PLANET_ID = '{prods.planet.ID}' -->\n      <!-- ENDIF -->\n\n      <!-- IF prods.planet.LEVEL_PLUS_GREEN >= 0 -->\n        <!-- DEFINE $PLUS_CLASS = 'positive' -->\n      <!-- ELSEIF prods.planet.LEVEL_PLUS_GREEN < 0 -->\n        <!-- DEFINE $PLUS_CLASS = 'negative' -->\n      <!-- ENDIF -->\n\n      <!-- IF prods.planet.FACTORY && prods.planet.PERCENT != -1 --><!-- DEFINE $ECD_ECS = 'ecd' --><!-- ELSE --><!-- DEFINE $ECD_ECS = 'ecs' --><!-- ENDIF -->\n      <td {$ECD_ECS}>\n          <!-- IF prods.planet.PERCENT >= 0 -->\n            <!-- DEFINE $PRODUCTION_PERCENT = '{prods.planet.PERCENT}' --><!-- INCLUDE _number_color_production_bg -->\n            <span style=\"width: {prods.planet.PERCENT}%;\" class=\"{$PRODUCTION_PERCENT_STYLE_BG}\"></span>\n          <!-- ENDIF -->\n          <span>\n            {prods.planet.LEVEL_TEXT}<!-- IF prods.planet.LEVEL_PLUS_GREEN --><span class=\"{$PLUS_CLASS}\">{prods.planet.LEVEL_PLUS_GREEN_TEXT}</span><!-- ENDIF --><!-- IF prods.planet.LEVEL_PLUS_YELLOW --><span class=\"neutral\">{prods.planet.LEVEL_PLUS_YELLOW_TEXT}</span><!-- ENDIF -->\n            <!-- IF prods.planet.FACTORY && prods.planet.PERCENT >= 0 -->\n              <br />\n              <!-- IF prods.planet.ID == 0 --><!-- DEFINE $SELECTOR = 'selector=\"{prods.ID}\"' --><!-- ELSE --><!-- DEFINE $SELECTOR = '' --><!-- ENDIF -->\n              <select class=\"{$PRODUCTION_PERCENT_STYLE_BG}\" name=\"percent[{prods.ID}][{prods.planet.ID}]\" {$SELECTOR}>\n                <!-- IF prods.planet.ID == 0 --><option>-</option><!-- ENDIF -->\n                <!-- BEGIN !percent -->\n                  <!-- DEFINE $NUMBER = '{percent.PERCENT}' --><!-- INCLUDE _number_color_percent -->\n                  <!-- IF prods.planet.PERCENT === percent.PERCENT --><!-- DEFINE $PERCENT_SELECTED = ' selected' --><!-- ELSE --><!-- DEFINE $PERCENT_SELECTED = '' --><!-- ENDIF -->\n                  <option class=\"{$FIELD_COLOR}\" value=\"{percent.PERCENT}\"{$PERCENT_SELECTED}>{percent.PERCENT}%</option>\n                <!-- END !percent -->\n              </select>\n            <!-- ENDIF -->\n          </span>\n      </td>\n    <!-- END planet -->\n  </tr>\n  <!-- END prods -->\n</tbody></table>\n</form>\n"
  },
  {
    "path": "design/templates/OpenGame/imperium_resource_cell.tpl.html",
    "content": "<!-- IF planet.ID --><!-- DEFINE $RES_ATTR = 'go=\"res\" planet_id=\"{planet.ID}\"' --><!-- ELSE --><!-- DEFINE $RES_ATTR = '' --><!-- ENDIF -->\n<td {$RES_ATTR}>\n  {$RESOURCE_AMOUNT}<br />\n  <!-- IF $RESOURCE_INCOMING > 0 -->\n  <span class=\"neutral\">+{$RESOURCE_INCOMING}</span><br />\n  <!-- ENDIF -->\n\n  <!-- IF $RESOURCE_PRODUCTION_PERCENT < 50 -->\n    <!-- DEFINE $FIELD_COLOR = 'negative' -->\n  <!-- ELSEIF $RESOURCE_PRODUCTION_PERCENT < 80 -->\n    <!-- DEFINE $FIELD_COLOR = 'warning' -->\n  <!-- ELSEIF $RESOURCE_PRODUCTION_PERCENT < 100 -->\n    <!-- DEFINE $FIELD_COLOR = 'neutral' -->\n  <!-- ELSE -->\n    <!-- DEFINE $FIELD_COLOR = 'positive' -->\n  <!-- ENDIF -->\n  <span class=\"{$FIELD_COLOR}\">{$RESOURCE_PRODUCTION}</span>\n</td>\n"
  },
  {
    "path": "design/templates/OpenGame/index.html",
    "content": ""
  },
  {
    "path": "design/templates/OpenGame/jumpgate.tpl.html",
    "content": "<script type=\"text/javascript\"><!--\nfunction zero_fleet()\n{\n  jQuery('[id^=\"ships\"][id$=\"slide\"]').slider(\"value\", 0);\n}\n\nfunction max_fleet()\n{\n  jQuery('[id^=\"ships\"][id$=\"slide\"]').each(function(){\n    jQuery(this).slider(\"value\", jQuery(this).slider(\"option\", \"max\"));\n  });\n}\n--></script>\n\n<h2>{L_tech[D_STRUC_MOON_GATE]}</h2>\n\n<form action=\"jumpgate.php\" method=\"post\">\n  <table><tbody>\n    <tr>\n      <td class=\"c_l\">{L_gate_start_moon}</td>\n      <td colspan=\"2\" class=\"c_c\">\n        {gate_start_link} {gate_start_name} \n        <!-- IF GATE_JUMP_REST_TIME -->\n          <span id=\"jump_gate_timer\"></span>\n          <script type=\"text/javascript\"><!--\n            sn_timers.unshift({id: 'jump_gate_timer', type: TIMER_BUILD_QUE_V1, options: {msg_done: '', que: [['1', '', {GATE_JUMP_REST_TIME}, '1']]}});\n          // --></script>\n        <!-- ENDIF -->\n      </td>\n    </tr>\n\n    <tr>\n      <td class=\"c_l\">{L_gate_dest_moon}</td>\n      <td colspan=\"2\" class=\"c_c\">\n        <select name=\"jmpto\">\n          <!-- BEGIN moon -->\n          <option value=\"{moon.ID}\">[{moon.GALAXY}:{moon.SYSTEM}:{moon.PLANET}] {moon.NAME} {moon.NEXT_JUMP_TIME}</option>\n          <!-- END moon -->\n        </select>\n      </td>\n    </tr>\n\n    <tr class=\"c_c\">\n      <th>{L_fl_fleet_typ}</th>\n      <th>{L_fl_orbiting}</th>\n      <th>{L_fl_to_fly}</th>\n    </tr>\n\n    <!-- BEGIN fleet -->\n    <tr>\n      <td class=\"c_l\"><a href=\"infos.php?gid={fleet.SHIP_ID}\">{fleet.SHIP_NAME}</a></td>\n      <td class=\"c_r\">{fleet.SHIP_COUNT_TEXT}</td>\n      <th>\n        <script type=\"text/javascript\"><!--\n          sn_ainput_make('ships[{fleet.SHIP_ID}]', {max: {fleet.SHIP_COUNT}});\n        --></script>\n      </th>\n    </tr>\n    <!-- END fleet -->\n\n    <tr>\n      <th class=\"c_c\" colspan=\"2\"><input value=\"{L_gate_jump_btn}\" type=\"submit\"></th>\n      <th class=\"c_c\">\n        <div class=\"fl\"><input type=\"button\" value=\"{L_fl_unselectall}\" onclick=\"zero_fleet();\"></div>\n        <div class=\"fr\"><input type=\"button\" value=\"{L_fl_selectall}\" onclick=\"max_fleet();\"></div>\n      </th>\n    </tr>\n  </tbody></table>\n</form>\n"
  },
  {
    "path": "design/templates/OpenGame/login_body.tpl.html",
    "content": "<script type=\"text/javascript\">\n  $.extend(language, {\n    sys_login_password_show: '{LA_sys_login_password_show}',\n    sys_login_password_hide: '{LA_sys_login_password_hide}',\n  });\n</script>\n\n<div id=\"log_main\">\n  <div id=\"log_title\">{L_sys_supernova}: {L_sys_universe} \"{C_game_name}\"</div>\n  <div>\n    {L_adm_opt_galaxies}: <font color=\"red\">{C_game_maxGalaxy}</font> - {L_adm_opt_systems}: <font color=\"red\">{C_game_maxSystem}</font> - {L_adm_opt_planets}: <font color=\"red\">{C_game_maxPlanet}</font>\n  </div>\n  <div>\n    {L_adm_opt_speed}:\n    {L_adm_opt_game_gspeed} <font color=\"red\">x{C_game_speed}</font> -\n    {L_adm_opt_game_fspeed} <font color=\"red\">x{C_fleet_speed}</font> -\n    {L_adm_opt_game_pspeed} <font color=\"red\">x{C_resource_multiplier}</font>\n  </div>\n  <div>\n    {L_log_online}: <font color=\"red\">{online_users}</font> - {L_log_lastreg}: <font color=\"red\">{last_user}</font> - {L_log_numbreg}: <font color=\"red\">{C_users_amount}</font><br>\n  </div>\n  <div id=\"log_description\">{L_log_desc}</div>\n\n\n  <div id=\"log_error\">\n    {F_LOGIN_MESSAGE}\n  </div>\n  <!-- IF F_LOGIN_STATUS == LOGIN_ERROR_USERNAME || F_LOGIN_STATUS == PASSWORD_RESTORE_ERROR_WRONG_EMAIL -->\n  <div id=\"login_offer\">\n    <span class=\"link\" onclick=\"$('#login_container').tabs({selected: 1});\">{L_login_register_offer}</span>\n  </div>\n  <!-- ENDIF -->\n\n  <!-- IF F_LOGIN_STATUS == LOGIN_ERROR_PASSWORD || F_LOGIN_STATUS == REGISTER_ERROR_EMAIL_EXISTS -->\n  <div id=\"password_offer\">\n    <span class=\"link\" onclick=\"$('#login_container').tabs({selected: 2});\">{L_login_password_restore_offer}</span>\n  </div>\n  <!-- ENDIF -->\n\n  <div id=\"login_container\">\n    <ul>\n      <li><a class=\"nohover\" href=\"#tab_login\">{L_log_login_page}</a></li>\n      <!-- IF ! GAME_BLITZ -->\n      <li><a class=\"nohover\" href=\"#tab_register\">{L_log_reg}</a></li>\n      <!-- ENDIF -->\n      <li><a class=\"nohover\" href=\"#tab_password_reset\">{L_log_lost_header}</a></li>\n    </ul>\n\n    <div id=\"tab_login\">\n      <form action=\"{FILENAME}{REQUEST_PARAMS}#tab_login\" method=\"post\">\n        <table width=\"100%\" align=\"center\" class=\"c_c no_border_image\">\n          <tr>\n            <td width=\"30%\">\n              {L_login_account_name_or_email}\n            </td>\n            <td>\n              <input name=\"username\" type=\"text\" value=\"{USERNAME}\" maxlength=\"64\" class=\"log_input\" />\n            </td>\n          </tr>\n          <tr>\n            <td>{L_sys_password}</td>\n            <td>\n              <input id=\"login_password\" name=\"password\" type=\"password\" value=\"{PASSWORD}\" maxlength=\"32\" class=\"log_input\" /><br />\n              <input class=\"password_show\" type=\"button\" value=\"{L_sys_login_password_show}\" show_element=\"password\" />\n            </td>\n          </tr>\n          <tr>\n            <td colspan=\"2\">\n              <input name=\"rememberme\" type=\"checkbox\" value=\"1\"> {L_Remember_me}\n            </td>\n          </tr>\n          <tr>\n            <td colspan=\"2\">\n              <input name=\"lang\" type=\"hidden\" value=\"{LANG}\" />\n              <input name=\"login\" type=\"submit\" value=\"{L_log_enter}\" /><br />\n            </td>\n          </tr>\n        </table>\n      </form>\n    </div>\n\n    <!-- IF ! GAME_BLITZ -->\n    <div id=\"tab_register\">\n      <form name=\"registerForm\" method=\"POST\" action=\"{FILENAME}{REQUEST_PARAMS}#tab_register\" onsubmit=\"changeAction('register');\" >\n        <input type=\"hidden\" name=\"id_ref\" value=\"{id_ref}\">\n        <table width=\"100%\" align=\"center\" class=\"no_border_image\">\n          <tbody>\n          <tr style=\"margin-bottom: 10px;\">\n            <td colspan=\"2\" class=\"markup c_j\">\n              {L_log_reg_text0} <!-- IF URL_RULES --><a href=\"{URL_RULES}\" target=\"_blank\" style=\"color: red; text-decoration: underline; font-face: bold;\"><!-- ENDIF -->{L_reg_with_rules}<!-- IF URL_RULES --></a><!-- ENDIF -->. {L_log_reg_text1}\n            </td>\n          </tr>\n\n          <tr>\n            <th width=\"50%\">{L_sys_email}</th>\n            <th width=\"50%\"><input name=\"email\" type=\"text\" value=\"{EMAIL}\" size=\"20\" maxlength=\"64\" /></th>\n          </tr>\n          <tr>\n            <th colspan=\"2\">\n              {L_login_register_email_hint}\n            </th>\n          </tr>\n\n          <tr>\n            <th>{L_sys_password}</th>\n            <th>\n              <input id=\"register_password\" name=\"password\" type=\"password\" value=\"{PASSWORD}\" size=\"20\" maxlength=\"32\" class=\"log_input\" /><br />\n              <input class=\"password_show\" type=\"button\" value=\"{L_sys_login_password_show}\" show_element=\"password\" />\n            </th>\n          </tr>\n\n          <tr>\n            <th>{L_sys_password_repeat}</th>\n            <th>\n              <input id=\"register_password_repeat\" name=\"password_repeat\" type=\"password\" value=\"{PASSWORD_REPEAT}\" size=\"20\" maxlength=\"32\" class=\"log_input\" /><br />\n              <input class=\"password_show\" type=\"button\" value=\"{L_sys_login_password_show}\" show_element=\"password_repeat\" />\n            </th>\n          </tr>\n          <tr>\n            <th colspan=\"2\">\n              <input name=\"lang\" type=\"hidden\" value=\"{LANG}\" />\n              <input name=\"register\" type=\"submit\" value=\"{L_signup}\" />\n            </th>\n          </tr>\n          </tbody>\n        </table>\n      </form>\n    </div>\n    <!-- ENDIF -->\n\n    <div id=\"tab_password_reset\">\n      <form action=\"{FILENAME}{REQUEST_PARAMS}#tab_password_reset\" method=\"post\">\n        <table width=\"340\" align=\"center\" class=\"no_border_image\">\n          <tbody>\n          <tr>\n            <td colspan=\"2\" class=\"markup c_j\">{L_log_lost_description1}</td>\n          </tr>\n          <tr>\n            <th nowrap>{L_E-Mail}</th>\n            <th><input name=\"email\" type=\"text\" value=\"{EMAIL}\" size=\"20\" maxlength=\"64\" /></th>\n          </tr>\n          <tr>\n            <th colspan=2><input name=\"password_reset\" type=\"submit\" value=\"{L_log_lost_send_mail}\" /></th>\n          </tr>\n          </tbody>\n        </table>\n      </form>\n      <form action=\"{FILENAME}{REQUEST_PARAMS}#tab_password_reset\" method=\"post\">\n        <table width=\"340\" align=\"center\" class=\"no_border_image\">\n          <tbody>\n          <tr>\n            <td colspan=\"2\" class=\"markup c_j\">{L_log_lost_description2}</td>\n          </tr>\n          <tr>\n            <th>{L_log_lost_code}</th>\n            <th><input name=\"password_reset_code\" type=\"text\" size=\"{D_LOGIN_PASSWORD_RESET_CONFIRMATION_LENGTH}\" maxlength=\"{D_LOGIN_PASSWORD_RESET_CONFIRMATION_LENGTH}\" /></th>\n          </tr>\n          <tr>\n            <input name=\"lang\" type=\"hidden\" value=\"{LANG}\" />\n            <th colspan=2><input name=\"password_reset_confirm\" type=\"submit\" value=\"{L_log_lost_reset_pass}\" /></th>\n          </tr>\n          </tbody>\n        </table>\n      </form>\n  </div>\n\n    </div>\n\n  <!-- IF .language -->\n  <div id=\"log_menu\">\n    <table class=\"markup c_c no_border_image\"><tr>\n      <!-- BEGIN language -->\n      <td style=\"padding: 0px 8px\">\n        <a href=\"{FILENAME}?lang={language.LANG_NAME_ISO2}&id_ref={id_ref}\">\n          <img src=\"language/{language.LANG_NAME_ISO2}/{language.LANG_FLAG_MEDIUM}\" /><br />\n          {language.LANG_NAME_NATIVE}\n        </a>\n      </td>\n      <!-- END language -->\n    </tr>\n    </table>\n  </div>\n  <!-- ENDIF -->\n\n<!--\n  <div id=\"login_external\">\n    <form method=\"get\" action=\"{FILENAME}?lang={LANG}&id_ref={id_ref}\">\n      <input type=\"submit\" name=\"{D_AUTH_LOGIN_EXTERNAL_NAME}\" value=\"{D_AUTH_VKONTAKTE}\" />\n    </form>\n  </div>\n-->\n\n  <div id=\"login_menu\">\n    <div class=\"menu_line\">\n      <!-- IF URL_RULES -->\n      <a href=\"{URL_RULES}\">{L_log_rules}</a>\n      <!-- ENDIF -->\n      <!-- IF URL_FAQ -->\n      <a href=\"{URL_FAQ}\">{L_log_faq}</a>\n      <!-- ENDIF -->\n      <!-- IF URL_FORUM -->\n      <a href=\"{URL_FORUM}\">{L_log_forums}</a>\n      <!-- ENDIF -->\n    </div>\n\n\n    <div class=\"menu_line\">\n      <a href=\"server_info.php?lang={LANG}\">{L_log_cred}</a>\n      <a href=\"announce.php?lang={LANG}\">{L_log_news}</a>\n    </div>\n\n\n    <div class=\"menu_line\">\n      <a href=\"stat.php?lang={LANG}\">{L_log_stat_menu}</a>\n      <a href=\"banned.php?lang={LANG}\">{L_log_banned}</a>\n      <a href=\"index.php?page=contact&lang={LANG}\">{L_log_contacts}</a>\n    </div>\n  </div>\n</div>\n\n<script type=\"text/javascript\">\n  $(\"#login_container\").tabs().show();\n</script>\n"
  },
  {
    "path": "design/templates/OpenGame/login_header.tpl.html",
    "content": "  <div id=\"log_status\">\n    {L_adm_opt_galaxies}: <font color=\"red\">{C_game_maxGalaxy}</font> - {L_adm_opt_systems}: <font color=\"red\">{C_game_maxSystem}</font> - {L_adm_opt_planets}: <font color=\"red\">{C_game_maxPlanet}</font>\n  </div>\n\n  <div id=\"log_status\">\n    {L_adm_opt_speed}:\n    {L_adm_opt_game_gspeed} <font color=\"red\">x{C_game_speed}</font> -\n    {L_adm_opt_game_fspeed} <font color=\"red\">x{C_fleet_speed}</font> -\n    {L_adm_opt_game_pspeed} <font color=\"red\">x{C_resource_multiplier}</font>\n  </div>\n\n  <div id=\"log_status\">\n    {L_log_online}: <font color=\"red\">{online_users}</font> - {L_log_lastreg}: <font color=\"red\">{last_user}</font> - {L_log_numbreg}: <font color=\"red\">{C_users_amount}</font><br>\n  </div>\n"
  },
  {
    "path": "design/templates/OpenGame/login_menu.tpl.html",
    "content": "<!-- IF LANG -->\n  <!-- DEFINE $SEPARATOR = '&' -->\n<!-- ELSE -->\n  <!-- DEFINE $SEPARATOR = '?' -->\n<!-- ENDIF -->\n\n<!-- IF .language -->\n<div id=\"log_menu\">\n  <table class=\"markup c_c\"><tr>\n  <!-- BEGIN language -->\n    <td style=\"padding: 0px 8px\">\n    <a href=\"{FILENAME}?lang={language.LANG_NAME_ISO2}{referral}\">\n      <img src=\"language/{language.LANG_NAME_ISO2}/{language.LANG_FLAG_MEDIUM}\" /><br />\n      {language.LANG_NAME_NATIVE}\n    </a>\n    </td>\n  <!-- END language -->\n  </tr>\n  </table>\n</div>\n<!-- ENDIF -->\n\n<div id=\"login_menu\">\n  <div class=\"menu_line\">\n    <a href=\"login.php{LANG}{referral}\">{L_log_login_page}</a>\n    <a href=\"reg.php{LANG}{referral}\">{L_log_reg}</a>\n    <a href=\"lostpassword.php{LANG}{referral}\">{L_PasswordLost}</a>\n  </div>\n\n  <div class=\"menu_line\">\n    <!-- IF URL_RULES -->\n    <a href=\"{URL_RULES}\">{L_log_rules}</a>\n    <!-- ENDIF -->\n    <!-- IF URL_FAQ -->\n    <a href=\"{URL_FAQ}\">{L_log_faq}</a>\n    <!-- ENDIF -->\n    <!-- IF URL_FORUM -->\n    <a href=\"{URL_FORUM}\">{L_log_forums}</a>\n    <!-- ENDIF -->\n  </div>\n\n\n  <div class=\"menu_line\">\n    <a href=\"server_info.php{LANG}\">{L_log_cred}</a>\n    <a href=\"announce.php{LANG}\">{L_log_news}</a>\n  </div>\n\n\n  <div class=\"menu_line\">\n    <a href=\"stat.php{LANG}\">{L_log_stat_menu}</a>\n    <a href=\"banned.php{LANG}\">{L_log_banned}</a>\n    <a href=\"index.php{LANG}{$SEPARATOR}page=contact\">{L_log_contacts}</a>\n  </div>\n</div>\n"
  },
  {
    "path": "design/templates/OpenGame/login_player_register.tpl.html",
    "content": "<form method=\"POST\" action=\"\">\n  <div id=\"log_main\">\n    <div id=\"log_title\">{L_sys_supernova}: {L_sys_universe} \"{C_game_name}\"</div>\n\n    <div id=\"log_description\">\n      {L_login_player_register_description}\n    </div>\n\n    <!-- IF PLAYER_REGISTER_STATUS != LOGIN_UNDEFINED -->\n    <div id=\"log_error\">\n      {PLAYER_REGISTER_MESSAGE}\n    </div>\n    <!-- ENDIF -->\n    <div id=\"player_register\">\n      <input type=\"hidden\" name=\"id_ref\" value=\"{PARTNER_ID}\" />\n      <input type=\"hidden\" name=\"server_name\" value=\"{SERVER_NAME}\" />\n\n      {L_login_player_register_player_name} <input type=\"text\" maxlength=\"{D_AUTH_PLAYER_NAME_LENGTH}\" name=\"player_suggested_name\" value=\"{PLAYER_SUGGESTED_NAME}\" />\n      <input type=\"submit\" name=\"submit_player_name\" value=\"{L_login_player_register_do}\" />\n    </div>\n\n    <div id=\"player_register_logout\">\n      {L_login_player_register_logout_description}<br/>\n      <button type=\"submit\" id=\"login_player_register_logout\" name=\"login_player_register_logout\" value=\"1\">\n        {L_login_player_register_logout}\n      </button>\n    </div>\n  </div>\n</form>\n"
  },
  {
    "path": "design/templates/OpenGame/lostpassword.tpl.html",
    "content": "<style type=\"text/css\"><!--\n @import url(./design/css/login.css);\n--></style>\n\n<div id=\"log_skipper\"></div>\n<div id=\"log_main\">\n  <div id=\"log_title\">{L_sys_universe} \"{C_game_name}\"</div>\n  <div id=\"log_status\">{L_log_lost_header}</div>\n\n  <div id=\"log_description\">{L_log_lost_description1}</div>\n\n  <div id=\"log_form\">\n    <form action=\"\" method=\"post\">\n      <table width=\"400\" align=\"center\">\n        <tbody>\n          <tr>\n            <th>{L_E-Mail}:</th>\n            <th><input name=\"email\" type=\"text\" size=\"20\" maxlength=\"40\" /></th>\n          </tr>\n          <tr>\n            <th colspan=2><input name=\"submit\" type=\"submit\" value=\"{L_log_lost_send_mail}\" /></th>\n          </tr>\n        </tbody>\n      </table>\n    </form>\n  </div>\n\n  <div id=\"log_description\">{L_log_lost_description2}</div>\n  <div id=\"log_form\">\n    <form action=\"\" method=\"post\">\n      <table width=\"400\" align=\"center\">\n        <tbody>\n          <tr>\n            <th>{L_log_lost_code}:</th>\n            <th><input name=\"confirm\" type=\"text\" size=\"20\" maxlength=\"40\" /></th>\n          </tr>\n          <tr>\n            <th colspan=2><input name=\"submit\" type=\"submit\" value=\"{L_log_lost_reset_pass}\" /></th>\n          </tr>\n        </tbody>\n      </table>\n    </form>\n  </div>\n\n  <!-- INCLUDE login_menu -->\n</div>\n"
  },
  {
    "path": "design/templates/OpenGame/market.tpl.html",
    "content": "<script type=\"text/javascript\" src=\"js/market.js?{C_var_db_update}\"></script>\n\n<h2>{L_eco_mrk_title}</h2>\n\n<div class=\"market_services\">\n<div>\n  <div>{L_eco_mrk_description}</div>\n  <a href=\"market.php?mode={D_MARKET_RESOURCES}\" class=\"button_pseudo\">\n    {L_eco_mrk_trader_do}<br/>\n    {C_rpg_cost_trader}&nbsp;{L_sys_dark_matter_sh}\n  </a>\n  <a href=\"market.php?mode={D_MARKET_SCRAPPER}\" class=\"button_pseudo\">\n    {L_eco_mrk_scraper_do}<br/>\n    {C_rpg_cost_scraper}&nbsp;{L_sys_dark_matter_sh}\n  </a>\n  <a href=\"market.php?mode={D_MARKET_STOCKMAN}\" class=\"button_pseudo\">\n    {L_eco_mrk_stockman_do}<br/>\n    {C_rpg_cost_stockman}&nbsp;{L_sys_dark_matter_sh}\n  </a>\n  <a href=\"market.php?mode={D_MARKET_INFO}\" class=\"button_pseudo\">\n    {L_eco_mrk_info_do}<br/>\n    {C_rpg_cost_info}&nbsp;{L_sys_dark_matter_sh}\n  </a>\n</div>\n</div>\n\n<!--\n  <tr><th><a href=\"market.php?mode={D_MARKET_EXCHANGE}\">{L_eco_mrk_exchange}</a></th><th>{C_rpg_cost_exchange}&nbsp;{L_sys_dark_matter_sh}</th></tr>\n  <tr><th><a href=\"market.php?mode={D_MARKET_BANKER}\">{L_eco_mrk_banker}</a></th><th>{C_rpg_cost_banker}&nbsp;{L_sys_dark_matter_sh}</th></tr>\n  <tr><th><a href=\"market.php?mode={D_MARKET_PAWNSHOP}\">{L_eco_mrk_pawnshop}</a></th><th>{C_rpg_cost_pawnshop}&nbsp;{L_sys_dark_matter_sh}</th></tr>\n-->\n"
  },
  {
    "path": "design/templates/OpenGame/market_fleet.tpl.html",
    "content": "<script type=\"text/javascript\"><!--\nvar ships = Array();\n<!-- BEGIN ships -->\nships[{ships.ID}] = \n{\n  id: {ships.ID}, \n  metal: {ships.METAL}, \n  crystal: {ships.CRYSTAL}, \n  deuterium: {ships.DEUTERIUM}, \n  count: {ships.COUNT}, \n  element: null\n};\n<!-- END ships -->\n//--></script>\n\n<script type=\"text/javascript\"><!--\nfunction eco_mrk_ship_recalc()\n{\n  var t = Array(0,0,0);\n\n  for(i in ships)\n  {\n    ship_count = jQuery('#ships' + i + 'slide').slider(\"value\");\n    if( ship_count )\n    {\n      t[0] += ships[i]['metal'] * ship_count;\n      t[1] += ships[i]['crystal'] * ship_count;\n      t[2] += ships[i]['deuterium'] * ship_count;\n    }\n  }\n\n  document.getElementById('total_metal').innerHTML = sn_format_number(t[0]);\n  document.getElementById('total_crystal').innerHTML = sn_format_number(t[1]);\n  document.getElementById('total_deuterium').innerHTML = sn_format_number(t[2]);\n}\n//--></script>\n\n<h2>{L_eco_mrk_title}:&nbsp;<!-- IF MODE == 2 -->{L_eco_mrk_scraper}<!-- ELSE -->{L_eco_mrk_stockman}<!-- ENDIF --></h2>\n{message}\n<form action=\"\" method=\"POST\">\n  <input type=\"hidden\" name=\"mode\" value=\"{MODE}\">\n\n  <table>\n    <tr rowspan=2>\n      <th class=\"c_l\" rowspan=2>{L_sys_ships}</th>\n      <th class=\"c_c\" colspan=3>\n        <!-- IF MODE == 2 -->\n          {L_eco_mrk_scraper_price}&nbsp;{L_eco_mrk_scraper_perShip}\n        <!-- ELSE -->\n          {L_eco_mrk_stockman_price}&nbsp;{L_eco_mrk_stockman_perShip}\n        <!-- ENDIF -->\n      </th>\n      <th class=\"c_c\" rowspan=2>\n        <!-- IF MODE == 2 -->\n          {L_eco_mrk_scraper_onOrbit}\n        <!-- ELSE -->\n          {L_eco_mrk_stockman_onStock}\n        <!-- ENDIF -->\n      </th>\n      <th class=\"c_c\" rowspan=2>\n        <!-- IF MODE == 2 -->\n          {L_eco_mrk_scraper_to}\n        <!-- ELSE -->\n          {L_eco_mrk_stockman_buy}\n        <!-- ENDIF -->\n      </th>\n    </tr>\n    <tr>\n      <th class=\"c_c\">{L_Metal}</th>\n      <th class=\"c_c\">{L_Crystal}</th>\n      <th class=\"c_c\">{L_Deuterium}</th>\n    </tr>\n    <!-- BEGIN ships -->\n      <tr>\n        <td class=\"c_l\">{ships.NAME}</td>\n        <td class=\"c_r\">{ships.METAL}</td>\n        <td class=\"c_r\">{ships.CRYSTAL}</td>\n        <td class=\"c_r\">{ships.DEUTERIUM}</td>\n        <td class=\"c_r\">{ships.COUNT}</td>\n        <td class=\"c_c\">\n          <ainput type=\"text\" name=\"ships[{ships.ID}]\" id=\"ships{ships.ID}\" max=\"{ships.COUNT}\"></ainput>\n          <script type=\"text/javascript\">\n            sn_ainput_make_jquery();\n            //sn_ainput_make('ships[{ships.ID}]', {max: ships[{ships.ID}]['count'], value: '{ships.AMOUNT}'});\n\n            jQuery('#ships{ships.ID}slide').on('slide slidechange', eco_mrk_ship_recalc);\n          </script>\n        </td>\n      </tr>\n    <!-- BEGINELSE ships -->\n      <tr>\n        <td class=\"c_c\" colspan=\"6\">\n          <!-- IF MODE == 2 -->\n            {L_eco_mrk_scraper_noShip}\n          <!-- ELSE -->\n            {L_eco_mrk_stockman_noShip}\n          <!-- ENDIF -->\n        </td>\n      </tr>\n    <!-- END ships -->\n\n    <!-- IF .ships -->\n    <tr>\n      <th class=\"c_l\">{L_eco_mrk_scraper_total}</th>\n      <th class=\"c_r\"><span id=\"total_metal\">0</span></th>\n      <th class=\"c_r\"><span id=\"total_crystal\">0</span></th>\n      <th class=\"c_r\"><span id=\"total_deuterium\">0</span></th>\n      <th class=\"c_c\" colspan=\"2\" rowspan=\"2\">\n        <div>\n          {L_eco_mrk_service_cost}&nbsp;{rpg_cost}&nbsp;{L_sys_dark_matter_sh}\n        </div>\n        <div>\n          <!-- IF MODE == 2 -->\n            <input type=\"submit\" name=\"scrape\" value=\"{L_eco_mrk_scraper_to}\">\n          <!-- ELSE -->\n            <input type=\"submit\" name=\"stock\" value=\"{L_eco_mrk_stockman_buy}\">\n          <!-- ENDIF -->\n        </div>\n      </th>\n    </tr>\n    <!-- ENDIF -->\n\n    <tr>\n      <th class=\"c_l\">{L_sys_on_planet}</th>\n      <th class=\"c_r\">{PLANET_METAL_TEXT}</th>\n      <th class=\"c_r\">{PLANET_CRYSTAL_TEXT}</th>\n      <th class=\"c_r\">{PLANET_DEUTERIUM_TEXT}</th>\n    </tr>\n\n  </table>\n</form>\n"
  },
  {
    "path": "design/templates/OpenGame/market_info.tpl.html",
    "content": "<h2>{L_eco_mrk_info}</h2>\n\n{message}\n\n<table width=\"519px\">\n  <tr>\n    <td class=\"c_l\" colspan=\"2\">{L_eco_mrk_info_description} <i>&quot;{L_eco_mrk_info_description_2} {C_rpg_cost_info} {L_sys_dark_matter_sh}&quot;</i></td>\n  </tr>\n\n  <tr>\n    <th class=\"c_l\">{L_eco_mrk_info_player}</th>\n  </tr>\n  <tr>\n    <td class=\"c_l\">\n      <form method=\"post\">\n        <input type=\"hidden\" name=\"action\" value=\"{D_MARKET_INFO_PLAYER}\">\n        {L_eco_mrk_info_player_description}<br />\n        {L_sys_player_name} <input type=\"text\" name=\"user_info_name\"> <input type=\"submit\" value=\"{L_eco_mrk_info_buy}\"><br />\n        {L_sys_hint_player_name}\n      </form>\n    </td>\n  </tr>\n\n<!--\n  <tr>\n    <td class=\"c_l\"><a href=\"market.php?mode={D_MARKET_RESOURCES}\">{L_eco_mrk_trader}</a></th>\n    <td class=\"c_r\">{C_rpg_cost_trader}&nbsp;{L_sys_dark_matter_sh}</td>\n  </tr>\n  <tr>\n    <td class=\"c_l\"><a href=\"market.php?mode={D_MARKET_SCRAPPER}\">{L_eco_mrk_scraper}</a></td>\n    <td class=\"c_r\">{C_rpg_cost_scraper}&nbsp;{L_sys_dark_matter_sh}</td>\n  </tr>\n  <tr>\n    <td class=\"c_l\"><a href=\"market.php?mode={D_MARKET_STOCKMAN}\">{L_eco_mrk_stockman}</a></td>\n    <td class=\"c_r\">{C_rpg_cost_stockman}&nbsp;{L_sys_dark_matter_sh}</td>\n  </tr>\n  <tr>\n    <td class=\"c_l\"><a href=\"market.php?mode={D_MARKET_INFO}\">{L_eco_mrk_info}</a></td>\n    <td class=\"c_r\">{C_rpg_cost_stockman}&nbsp;{L_sys_dark_matter_sh}</td>\n  </tr>\n\n  <tr><th><a href=\"market.php?mode={D_MARKET_EXCHANGE}\">{L_eco_mrk_exchange}</a></th><th>{C_rpg_cost_exchange}&nbsp;{L_sys_dark_matter_sh}</th></tr>\n  <tr><th><a href=\"market.php?mode={D_MARKET_BANKER}\">{L_eco_mrk_banker}</a></th><th>{C_rpg_cost_banker}&nbsp;{L_sys_dark_matter_sh}</th></tr>\n  <tr><th><a href=\"market.php?mode={D_MARKET_PAWNSHOP}\">{L_eco_mrk_pawnshop}</a></th><th>{C_rpg_cost_pawnshop}&nbsp;{L_sys_dark_matter_sh}</th></tr>\n-->\n</table>\n"
  },
  {
    "path": "design/templates/OpenGame/market_trader.tpl.html",
    "content": "<script type=\"text/javascript\">\n  var RES_DARK_MATTER = parseInt('{D_RES_DARK_MATTER}');\n  var RES_METAL = parseInt('{D_RES_METAL}');\n  var C_rpg_cost_trader = parseInt('{C_rpg_cost_trader}');\n  var exchange_to_resource_id = parseInt('{EXCHANGE_TO_RESOURCE_ID}') ? parseInt('{EXCHANGE_TO_RESOURCE_ID}') : 0;\n\n  jQuery.extend(language, {LA_eco_mrk_trader_exchange_dm_confirm: '{LA_eco_mrk_trader_exchange_dm_confirm}'});\n  var eco_market_resources = {};\n</script>\n\n<script type=\"text/javascript\" src=\"js/market.js?{C_var_db_update}\"></script>\n\n<script type=\"text/javascript\">\n  <!-- BEGIN resources -->\n  eco_market_resources[parseInt('{resources.ID}')] = {\n    avail: parseFloat('{resources.AVAIL}'),\n    rate: parseFloat('{resources.RATE}'),\n    start: parseFloat('{resources.AVAIL}') + (parseInt('{resources.ID}') == RES_DARK_MATTER ? C_rpg_cost_trader : 0)\n  };\n  jQuery(document).on('keyup', '#spend{resources.ID}', eco_mrk_trader_recalc);\n  jQuery(document).on('slide slidechange', '#spend{resources.ID}slide', eco_mrk_trader_recalc);\n  <!-- END resources -->\n</script>\n\n<h2>{L_eco_mrk_title}:&nbsp;{L_eco_mrk_trader}</h2>\n<h3 class=\"warning\">{L_eco_mrk_service_cost} <span id=\"rpg_cost_trader\">{C_rpg_cost_trader}</span> {L_sys_dark_matter_sh}</h3>\n\n{message}\n\n<form name=\"form_trade\" method=\"POST\" id=\"form_trade\">\n  <table id=\"market_trader\">\n    <tr class=\"c_l\">\n      <th>{L_sys_resources}</th>\n      <th>{L_eco_mrk_trader_exchange}</th>\n      <th>{L_eco_mrk_trader_left}</th>\n    </tr>\n\n    <!-- BEGIN resources -->\n      <tr>\n        <td class=\"c_c\">\n          <div class=\"button_pseudo <!-- IF resources.ID == EXCHANGE_TO_RESOURCE_ID -->button_pseudo_pressed<!-- ENDIF -->\" resource_id=\"{resources.ID}\" >\n            <input id=\"resource_{resources.ID}\" type=\"radio\" name=\"exchangeTo\"\n                   style=\"display: none;\"\n                   value=\"{resources.ID}\" <!-- IF resources.ID == EXCHANGE_TO_RESOURCE_ID -->checked <!-- ENDIF -->>\n            {resources.NAME}\n          </div>\n          <div class=\"notice\">{L_eco_mrk_trader_course} <span id=\"course{resources.ID}\"></span></div>\n        </td>\n        <td>\n          <script type=\"text/javascript\">\n            sn_ainput_make('spend[{resources.ID}]', {max: {resources.AVAIL}, button_max: true, button_zero: true});\n          </script>\n        </td>\n        <td class=\"c_r\">\n          {resources.AVAIL_TEXT}\n          <div id=\"res_delta{resources.ID}\"></div>\n          <div id=\"res_left{resources.ID}\" style=\"border-top: 0.2em solid\"></div>\n        </td>\n      </tr>\n    <!-- END resources -->\n\n    <tr><th class=\"c_c\" colspan=4 align=center>\n      <input type=\"submit\" id=\"submit_trade\" name=\"exchange\" value=\"{L_eco_mrk_trader_do}\" />\n    </th></tr>\n  </table>\n</form>\n"
  },
  {
    "path": "design/templates/OpenGame/menu.tpl.html",
    "content": "<script type=\"text/javascript\">\n  sn_timers.unshift({'id': 'menu_admin_server_time', 'type': TIMER_CLOCK_REALTIME, 'options': {\n    'format': 3,\n    'delta': 0\n  }});\n\n  var MENU_HIDDEN = \"{MENU_START_HIDE}\" ? 1 : 0;\n  var SN_COOKIE = \"{D_SN_COOKIE}\";\n  var SN_ROOT_RELATIVE = \"{D_SN_ROOT_RELATIVE}\";\n  var PLAYER_OPTION_MENU_SHOW_ON_BUTTON = '{PLAYER_OPTION_MENU_SHOW_ON_BUTTON}';\n  var PLAYER_OPTION_MENU_HIDE_ON_BUTTON = '{PLAYER_OPTION_MENU_HIDE_ON_BUTTON}';\n  var PLAYER_OPTION_MENU_HIDE_ON_LEAVE = '{PLAYER_OPTION_MENU_HIDE_ON_LEAVE}';\n  var PLAYER_OPTION_MENU_ITEMS_AS_BUTTONS = '{PLAYER_OPTION_MENU_ITEMS_AS_BUTTONS}';\n\n  var LA_menu_show = \"{LA_menu_show}\";\n  var LA_menu_hide = \"{LA_menu_hide}\";\n  var LA_menu_unpin = \"{LA_menu_unpin}\";\n  var LA_menu_pin = \"{LA_menu_pin}\";\n  <!-- IF MENU_CUSTOMIZE -->\n  var LA_menu_customize_show_items = \"{LA_menu_customize_show_items}\";\n  var LA_menu_customize_hide_items = \"{LA_menu_customize_hide_items}\";\n  <!-- ENDIF -->\n</script>\n\n<!-- IF PLAYER_OPTION_MENU_ITEMS_AS_BUTTONS -->\n  <!-- DEFINE $MENU_BIG_BUTTONS = 'MENU_BIG_BUTTONS' -->\n<!-- ENDIF -->\n\n<!-- IF PLAYER_OPTION_MENU_OLD -->\n  <!-- DEFINE $MENU_NARROW_CELLS = 'MENU_NARROW_CELLS' -->\n<!-- ENDIF -->\n\n<!-- IF MENU_CUSTOMIZE -->\n  <!-- IF MENU_START_HIDE -->\n    <!-- DEFINE $MENU_START_BUTTON_PIN = '{LA_menu_pin}' -->\n    <!-- DEFINE $MENU_START_BUTTON_SHOW = '{LA_menu_show}' -->\n    <!-- DEFINE $MENU_START_HIDDEN = 'MENU_START_HIDDEN' -->\n  <!-- ELSE -->\n    <!-- DEFINE $MENU_START_BUTTON_PIN = '{LA_menu_unpin}' -->\n    <!-- DEFINE $MENU_START_BUTTON_SHOW = '{LA_menu_hide}' -->\n  <!-- ENDIF -->\n\n  <!-- IF PLAYER_OPTION_MENU_UNPIN_ABSOLUTE -->\n    <!-- DEFINE $MENU_ABSOLUTE = 'MENU_ABSOLUTE' -->\n  <!-- ENDIF -->\n\n  <!-- IF PLAYER_OPTION_MENU_HIDE_SHOW_BUTTON == 0 -->\n    <!-- DEFINE $MENU_SHOW_HIDE_CLASS = 'MENU_SHOW_HIDE_FIXED' -->\n  <!-- ELSEIF PLAYER_OPTION_MENU_HIDE_SHOW_BUTTON == 2 -->\n    <!-- DEFINE $MENU_SHOW_HIDE_CLASS = 'MENU_SHOW_HIDE_HIDDEN' -->\n  <!-- ENDIF -->\n<!-- ENDIF -->\n\n<!-- IF PLAYER_OPTION_MENU_WHITE_TEXT == 1 -->\n  <!-- DEFINE $MENU_WHITE_BUTTONS = 'MENU_WHITE_BUTTONS' -->\n<!-- ENDIF -->\n\n<!-- IF PLAYER_OPTION_MENU_ITEMS_AS_BUTTONS == 1 -->\n  <!-- DEFINE $MENU_ITEMS_BUTTONS = 'do-not-skin-child' -->\n<!-- ENDIF -->\n\n<div id=\"__menu\" class=\"js___menu\">\n  <table cellspacing=\"0\" cellpadding=\"0\" id=\"left_menu\" class=\"js_left_menu {$MENU_WHITE_BUTTONS} {$MENU_ITEMS_BUTTONS} {$MENU_BIG_BUTTONS} {$MENU_NARROW_CELLS} {$MENU_START_HIDDEN} {$MENU_ABSOLUTE} border_image_small\"><!-- BEGIN menu -->\n  <!-- IF (! menu.AUTH_LEVEL || menu.AUTH_LEVEL <= AUTH_LEVEL) && ! menu.DISABLED -->\n\n    <!-- IF menu.LEVEL == 'header' --><!-- DEFINE $TX = 'th' --><!-- ELSE --><!-- DEFINE $TX = 'td' --><!-- ENDIF -->\n\n    <!-- IF menu.CLASS --><!-- DEFINE $MENU_CLASS = ' class=\"{menu.CLASS}\"' --><!-- ELSE --><!-- DEFINE $MENU_CLASS = '' --><!-- ENDIF -->\n    <!-- IF menu.STYLE --><!-- DEFINE $MENU_STYLE = ' style=\"{menu.STYLE}\"' --><!-- ELSE --><!-- DEFINE $MENU_STYLE = '' --><!-- ENDIF -->\n    <!-- IF menu.TITLE --><!-- DEFINE $MENU_TITLE = 'title=\"{menu.TITLE}\"' --><!-- ELSEIF menu.ALT --><!-- DEFINE $MENU_TITLE = 'title=\"{menu.ALT}\"' --><!-- ELSE --><!-- DEFINE $MENU_TITLE = '' --><!-- ENDIF -->\n\n    <!-- IF menu.ALT -->\n      <!-- DEFINE $ALT_TITLE = 'alt=\"{menu.ALT}\"' -->\n    <!-- ELSEIF menu.TITLE -->\n      <!-- DEFINE $ALT_TITLE = 'alt=\"{menu.TITLE}\"' -->\n    <!-- ELSE -->\n      <!-- DEFINE $ALT_TITLE = '' -->\n    <!-- ENDIF -->\n\n    <!-- IF menu.LINK -->\n      <!-- IF menu.BLANK -->\n        <!-- DEFINE $LINK_BLANK = ' target=\"_blank\"' -->\n      <!-- ELSE -->\n        <!-- DEFINE $LINK_BLANK = '' -->\n      <!-- ENDIF -->\n\n      <!-- IF menu.TYPE == 'image' -->\n        <!-- DEFINE $MENU_LINK_IMAGE = ' class=\"lm_with_image\"' -->\n      <!-- ELSE -->\n        <!-- DEFINE $MENU_LINK_IMAGE = '' -->\n      <!-- ENDIF -->\n    <!-- ENDIF -->\n\n    <!-- IF menu.HIDE && ! menu.HIDE_LOCK -->\n      <!-- DEFINE $MENU_HIDE = ' is_hide=\"1\" style=\"display: none\"' -->\n    <!-- ELSEIF menu.HIDE_LOCK -->\n      <!-- DEFINE $MENU_HIDE = ' hide_lock=\"1\"' -->\n    <!-- ELSE -->\n      <!-- DEFINE $MENU_HIDE = '' -->\n    <!-- ENDIF -->\n\n    <!-- IF menu.MOVEABLE && menu.MOVE_LOCK -->\n      <!-- DEFINE $MENU_MOVABLE = ' move_lock=\"1\"' -->\n    <!-- ELSE -->\n      <!-- DEFINE $MENU_MOVABLE = '' -->\n    <!-- ENDIF -->\n\n    <tr id=\"{menu.ID}\"{$MENU_HIDE}{$MENU_MOVABLE}>\n      <{$TX} nowrap{$MENU_CLASS}{$MENU_STYLE}{$MENU_TITLE}>\n      <!-- IF menu.WRAP_START -->{menu.WRAP_START}<!-- ENDIF -->\n        <!-- IF menu.LINK -->\n          <!-- DEFINE $LINK_CLOSE = '</a>' -->\n          <a href=\"{menu.LINK}\"{$LINK_BLANK}{$MENU_LINK_IMAGE} style=\"position: relative\">\n        <!-- ELSE -->\n          <!-- DEFINE $LINK_CLOSE = '' -->\n        <!-- ENDIF -->\n\n        <!-- IF menu.ICON_PATH -->\n          <img src=\"{menu.ICON_PATH}\" class=\"menu_icon\" />\n        <!-- ELSEIF menu.ICON  -->\n          <span id=\"icon_{menu.ICON}\" class=\"menu_icons\"></span>\n        <!-- ENDIF -->\n\n        <!-- IF menu.TYPE == 'image' -->\n          <img style=\"display: inline-block\" border=\"0\" src=\"{menu.ITEM}\" {$ALT_TITLE} />\n        <!-- ELSE -->\n          <span<!-- IF menu.LINK --> class=\"<!-- IF $LINK_BLANK && menu.TYPE != 'image' -->link_external<!-- ELSE -->link_b<!-- ENDIF -->\"<!-- ENDIF -->>{menu.ITEM}</span>\n        <!-- ENDIF -->\n        <!-- IF menu.ITEM_FINISH -->{menu.ITEM_FINISH}<!-- ENDIF -->\n        <!-- IF menu.LINK --></a><!-- ENDIF -->\n        <!-- IF menu.WRAP_END -->{menu.WRAP_END}<!-- ENDIF -->\n      </{$TX}>\n    </tr>\n  <!-- ENDIF -->\n  <!-- END menu --></table>\n</div>\n\n<!-- IF MENU_CUSTOMIZE -->\n<input type=\"button\" id=\"left_menu_show\" value=\"{$MENU_START_BUTTON_SHOW}\" class=\"{$MENU_SHOW_HIDE_CLASS} js_left_menu_show\"/>\n<script type=\"text/javascript\">\n  jQuery(document).ready(function(){\n    MENU_HIDDEN ? jQuery('.js___menu').attr('menu_hidden', '1') : function (){\n        menuHideButtonPositionShow(true);\n        jQuery('.js_left_menu_show').css('visibility', 'visible');\n      }();\n  });\n</script>\n<!-- ENDIF -->\n"
  },
  {
    "path": "design/templates/OpenGame/message_body.tpl.html",
    "content": "<div class=\"admin_message_box border_image_small\">\n  <div class=\"header\">{TITLE}</div>\n  <div class=\"cell\">{MESSAGE}</div>\n  <!-- IF REDIRECT_TO -->\n  <div class=\"subheader\">\n    <a id=\"js_redirect\" href=\"{REDIRECT_TO}\" class=\"link\">{L_sys_click_here_to_continue}</a>\n  </div>\n  <!-- ENDIF -->\n</div>\n\n<!-- IF TIMEOUT -->\n<script type=\"text/javascript\">\n  setTimeout(function () {\n    document.getElementById(\"js_redirect\").click();\n  }, Math.intVal(\"{TIMEOUT}\" * 1000));\n</script>\n<!-- ENDIF -->\n"
  },
  {
    "path": "design/templates/OpenGame/metamatter.tpl.html",
    "content": "<script type=\"text/javascript\" src=\"{D_SN_ROOT_VIRTUAL}js/metamatter.js?{C_var_db_update}\"></script>\n\n<script type=\"text/javascript\">\n  language.addLocale({sys_show: \"{LA_sys_show}\", sys_hide: \"{LA_sys_hide}\"});\n\n  var exchange_mm_default = parseFloat(\"{PLAYER_CURRENCY_PRICE_PER_MM}\");\n\n  var mm_discounts = Array();\n  <!-- BEGIN discount -->\n  mm_discounts.push({sum: Math.intVal('{discount.SUM}'), discount: Math.floatVal('{discount.DISCOUNT_ONE}')});\n  <!-- END discount -->\n</script>\n\n<!-- IF ! $TABLE_WIDTH -->\n<!-- DEFINE $TABLE_WIDTH = '90%' -->\n<h1>{PAGE_HEADER}</h1>\n<!-- ENDIF -->\n\n<table id=\"payment_metamatter_info\" class=\"border_image_small\">\n  <tr class=\"c_c\">\n    <th>\n      <div class=\"contFJ\">\n        <div class=\"payment_mm_describe\">{L_sys_dark_matter_what_why_how}</div>\n        <button class=\"js_show_dm_info\">\n          <span class=\"link_action\">{L_sys_show}</span>\n        </button>\n      </div>\n    </th>\n  </tr>\n  <tr class=\"c_j\" id=\"dark_matter_what_it_is\" style=\"display: none;\">\n    <td>\n      <div style=\"display: table-row; position: relative; margin: 7px;\">\n        <img src=\"design/images/DMaterie.jpg\" align=\"top\" border=\"0\" height=\"120\" width=\"120\" class=\"fl\" style=\"margin-right: 7px;\">\n        {DARK_MATTER_DESCRIPTION}\n        <!-- IF PAYMENT_AVAILABLE -->\n        <div class=\"ok\">{L_sys_dark_matter_obtain_text_convert}</div>\n        <!-- ENDIF -->\n      </div>\n      <div>\n      {L_sys_dark_matter_description_text}\n      </div>\n      <div>\n      {L_sys_dark_matter_obtain_text}\n      </div>\n    </td>\n  </tr>\n  <tr class=\"c_j\" id=\"metamatter_what_description\" style=\"display: none;\">\n    <td>\n      <div style=\"display: table-row; position: relative; margin: 7px;\">\n        <img src=\"design/images/metamatter.png\" align=\"top\" border=\"0\" height=\"120\" width=\"120\" class=\"fl\" style=\"margin-right: 7px;\">\n        {L_sys_metamatter_what_description}\n      </div>\n      <div style=\"display: table-row; position: relative; margin: 7px;\">\n        {L_sys_metamatter_what_purchase}\n      </div><br />\n    </td>\n  </tr>\n</table>\n\n<table id=\"payment_metamatter\">\n  <!-- IF FALSE && (.exchange || .discount) -->\n  <tr class=\"c_j\">\n    <th style=\"display: flex;align-items: center; justify-content: space-between;\">\n      <span>{L_pay_mm_bonus_header}</span>\n      <div class=\"c_r\" onclick=\"sn_show_hide2(this, '#exchange_and_bonuses');\">\n        <span class=\"link_action\">[&nbsp;{L_sys_show}&nbsp;]</span>\n      </div>\n    </th>\n  </tr>\n\n  <tr class=\"c_j\" id=\"exchange_and_bonuses\" style=\"display: none;\">\n    <td>\n      <!-- IF .exchange -->\n      <table class=\"markup\" width=\"100%\">\n        <tr>\n          <td>{L_pay_currency_exchange_title}</td>\n        </tr>\n        <tr id=\"currency_exchange\">\n          <td colspan=\"2\" class=\"c_j\">\n            <table width=\"100%\">\n              <tr class=\"c_c\">\n                <th rowspan=\"2\">{L_pay_currency_name}</th>\n                <th colspan=\"2\">{L_pay_currency_exchange_rate}</th>\n                <th rowspan=\"2\">{L_pay_currency_exchange_mm}</th>\n              </tr>\n              <tr class=\"c_c\">\n                <th>{L_pay_currency_exchange_direct}</th>\n                <th>{L_pay_currency_exchange_reverse}</th>\n              </tr>\n              <!-- BEGIN exchange -->\n              <tr>\n                <td>{exchange.TEXT} ({exchange.SYMBOL})</td>\n                <td class=\"c_r\">{exchange.COURSE_DIRECT}</td>\n                <td class=\"c_r\">{exchange.COURSE_REVERSE}</td>\n                <td class=\"c_r\">{exchange.MM_PER_CURRENCY} / 1 {exchange.SYMBOL}</td>\n              </tr>\n              <!-- END exchange -->\n            </table>\n            <div class=\"note\">{L_pay_currency_exchange_note}</div>\n          </td>\n        </tr>\n      </table>\n      <br />\n      <!-- ENDIF -->\n\n      <!-- IF .discount -->\n      <table class=\"markup\" width=\"100%\">\n        <tr>\n          <td>{L_pay_mm_bonus}</td>\n        </tr>\n      </table>\n      <!--<ul id=\"bonuses\" style=\"display: none;\">-->\n      <ul id=\"bonuses\">\n        <!-- BEGIN discount -->\n        <li>{discount.TEXT}</li>\n        <!-- END discount -->\n      </ul>\n      <br />\n      <!-- ENDIF -->\n    </td>\n  </tr>\n  <!-- ENDIF -->\n\n  <tr class=\"c_j\">\n    <th>\n      <span style=\"font-size: 1.8em\">{L_pay_mm_buy}</span>\n    </th>\n  </tr>\n  <tr class=\"c_j\">\n    <td>\n      <!-- IF PAYMENT_AVAILABLE -->\n\n      <!-- IF ! PAY_LINK_URL -->\n        <!-- DEFINE $PAY_ACTION = '{D_SN_ROOT_VIRTUAL}metamatter.php' -->\n        <!-- DEFINE $PAY_METHOD = 'POST' -->\n      <!-- ELSEIF PAY_LINK_METHOD == 'GET' || PAY_LINK_METHOD == 'POST' -->\n        <!-- DEFINE $PAY_ACTION = '{PAY_LINK_URL}' -->\n        <!-- DEFINE $PAY_METHOD = '{PAY_LINK_METHOD}' -->\n      <!-- ELSEIF PAY_LINK_METHOD == 'LINK' -->\n        <!-- DEFINE $PAY_ACTION = '' -->\n        <!-- DEFINE $PAY_METHOD = '' -->\n      <!-- ENDIF -->\n      <form action=\"{$PAY_ACTION}\" method=\"{$PAY_METHOD}\" id=\"payment_form\">\n\n\n\n        <!-- IF ! UNIT_AMOUNT -->\n          {L_pay_mm_buy_text_cost} {PAYMENT_CURRENCY_EXCHANGE_DEFAULT} <span class=\"metamatter\">{L_sys_metamatter_sh}</span>\n          <label for=\"player_currency\">{L_pay_mm_buy_text_unit}</label>\n          <select name=\"player_currency\" id=\"player_currency\" class=\"notice\">\n            <!-- BEGIN exchange -->\n            <!-- IF PLAYER_CURRENCY == exchange.SYMBOL -->\n            <!-- DEFINE $SELECTED = 'selected' -->\n            <!-- ELSE -->\n            <!-- DEFINE $SELECTED = '' -->\n            <!-- ENDIF -->\n            <option value=\"{exchange.SYMBOL}\" {$SELECTED}>{exchange.LOT_PRICE} {exchange.SYMBOL} ({exchange.TEXT})</option>\n            <!-- END exchange -->\n          </select>\n\n          {L_pay_mm_buy_metamatter_amount}\n          <div class=\"payment_method_block\">\n            <!-- BEGIN mm_amount -->\n            <div class=\"payment_block_item js_payment_mm_amount\" value=\"{mm_amount.VALUE}\">\n              <!-- IF mm_amount.DISCOUNT -->\n                <del class=\"negative\">{mm_amount.TEXT}</del><br />\n                <span class=\"positive\">{mm_amount.TEXT_DISCOUNTED} {L_pay_mm_bonus_text} {mm_amount.DISCOUNT_PERCENT}%</span>\n              <!-- ELSE -->\n                <span class=\"positive\">{mm_amount.TEXT}</span>\n              <!-- ENDIF -->\n              <br />\n              {L_pay_mm_buy_unit}\n              <br />\n              <span class=\"zero\">{mm_amount.PRICE_TEXT} {mm_amount.CURRENCY}</span>\n            </div>\n            <!-- END mm_amount -->\n          </div>\n          <br />\n\n          <label for=\"metamatter\">{L_pay_mm_buy_metamatter_amount_enter}</label> <input type=\"text\" name=\"metamatter\" id=\"metamatter\" value=\"0\" style=\"width: 100px;\" maxlength=\"8\" /><br />\n          {L_pay_mm_buy_price_for} <del class=\"negative\"><span class=\"hide\" id=\"metamatter_undiscounted\">0</span></del>\n          <span class=\"positive\" ><span id=\"metamatter_total\">0</span><span class=\"hide\" id=\"metamatter_bonus_percent\"> ({L_pay_mm_bonus_text} <span>0</span>%)</span></span> {L_pay_mm_buy_unit}\n          <span class=\"neutral\"><span id=\"metamatter_price\">0,00</span> {PLAYER_CURRENCY}</span>\n          <br />\n          <br />\n        <!-- ELSE -->\n          <input type=\"hidden\" name=\"metamatter\" id=\"payment_amount\" value=\"{UNIT_AMOUNT}\" />\n          {L_pay_mm_buy_purchase}\n          <!-- IF UNIT_AMOUNT_BONUS_PERCENT -->\n          <span class=\"negative\"><del>{UNIT_AMOUNT_TEXT}</del></span>\n          <span class=\"positive\">{UNIT_AMOUNT_TEXT_DISCOUNTED} ({L_pay_mm_bonus_text} {UNIT_AMOUNT_BONUS_PERCENT}%)</span>\n          <!-- ELSE -->\n          <span class=\"positive\">{UNIT_AMOUNT_TEXT_DISCOUNTED}</span>\n          <!-- ENDIF -->\n          {L_pay_mm_buy_unit}\n          <br />\n          {L_pay_mm_buy_cost_base} <span class=\"zero\">{UNIT_AMOUNT_TEXT_COST_BASE} {PLAYER_CURRENCY}</span>\n          <br />\n          <br />\n\n          <input type=\"hidden\" name=\"payment_method\" id=\"payment_method\" value=\"{PAYMENT_METHOD}\" />\n          <!-- IF ! PAYMENT_METHOD -->\n            {L_pay_mm_buy_payment_method_select}\n\n            <!-- DEFINE $MAX_PAYMENTS_IN_ROW = 5 -->\n            <!-- BEGIN payment -->\n            <div class=\"payment_type_block\">\n              <div class=\"payment_type_name\">{payment.NAME}</div>\n\n              <div value=\"{payment.ID}\" class=\"payment_method_block\">\n                <!-- BEGIN method -->\n\n                  <!-- IF method.IMAGE && ! method.BUTTON-->\n                  <!-- DEFINE $METHOD_CLASS = 'payment_mm_method_nobutton' -->\n                  <!-- ELSE -->\n                  <!-- DEFINE $METHOD_CLASS = '' -->\n                  <!-- ENDIF -->\n\n                  <!-- IF method.S_ROW_COUNT >= $MAX_PAYMENTS_IN_ROW -->\n                  <!-- DEFINE $METHOD_HIDE = 'js_hidden_payments_{payment.ID} ptl_payment_method_hide' -->\n                  <!-- ELSE -->\n                  <!-- DEFINE $METHOD_HIDE = '' -->\n                  <!-- ENDIF -->\n\n                  <div class=\"payment_block_item js_payment_mm_method {$METHOD_CLASS} {$METHOD_HIDE}\" value=\"{method.ID}\" flex=\"1\">\n                    <!-- IF method.IMAGE -->\n                    <img src=\"{method.IMAGE}\"  alt=\"{method.NAME}\" />\n                    <!-- ENDIF -->\n                    <!-- IF ! method.IMAGE || method.NAME_FORCE -->\n                    <div>{method.NAME}</div>\n                    <!-- ENDIF -->\n                  </div>\n                <!-- END method -->\n              </div>\n\n              <!-- IF method.S_ROW_COUNT > $MAX_PAYMENTS_IN_ROW -->\n              <div>\n                <button class=\"js_show_hidden_methods\" method_id=\"{payment.ID}\">\n                  <span class=\"link_action\">{L_pay_mm_buy_payment_method_more}</span>\n                </button>\n              </div>\n              <!-- ENDIF -->\n\n            </div>\n            <!-- END payment -->\n\n          <!-- ELSE -->\n            {L_pay_mm_buy_payment_method_selected} <span class=\"positive\">{PAYMENT_METHOD_NAME}</span><br /><br />\n\n            <input type=\"hidden\" name=\"payment_module\" id=\"payment_module\" value=\"{PAYMENT_MODULE}\">\n            <!-- IF ! PAYMENT_MODULE -->\n              {L_pay_mm_buy_select}\n              <div class=\"payment_method_block\" value=\"{payment.ID}\">\n                <!-- BEGIN payment_module -->\n                <div class=\"payment_block_item js_payment_mm_module\" value=\"{payment_module.ID}\">\n                  {payment_module.NAME}\n                  <!-- IF payment_module.COST -->\n                  <br />\n                  <span class=\"notice\">~{payment_module.COST|num|money} {payment_module.CURRENCY}</span>\n                  <!-- ENDIF -->\n                </div>\n              <!-- END payment_module -->\n              </div>\n              <div style=\"clear:both;\"></div>\n              <span class=\"neutral\">{L_pay_mm_buy_method_detail}</span>\n            <!-- ELSE -->\n              {L_pay_mm_buy_payment_selected} <span class=\"ok\">\"{PAYMENT_MODULE_NAME}\"</span><br /><br />\n\n              <!-- BEGIN pay_link_data -->\n                <input type=\"hidden\" name=\"{pay_link_data.FIELD}\" value=\"{pay_link_data.VALUE}\">\n              <!-- END pay_link_data -->\n\n              <!-- BEGIN render -->\n                <!-- IF render.TYPE == 'select' -->\n                <label>\n                  <select name=\"{render.NAME}\">\n                    <!-- BEGIN value -->\n                      <option value=\"{render.value.FIELD}\">{render.value.VALUE}</option>\n                    <!-- END value -->\n                  </select>\n                </label>\n              <!-- ELSE -->\n                  {render.VALUE}\n                <!-- ENDIF -->\n              <!-- END render -->\n\n              <!-- IF PAY_LINK_URL -->\n                {METAMATTER_COST_TEXT}\n                <!-- IF UNIT_AMOUNT_BONUS_PERCENT -->\n                <br />{METAMATTER_COST_BONUS_TEXT}\n                <!-- ENDIF -->\n                {C_adv_conversion_code_payment}<br />\n                <br />\n                <span class=\"notice\">{L_pay_mm_buy_step2_text}</span>\n              <!-- ENDIF -->\n\n              <!-- IF METAMATTER_COST_ON_PAYMENT -->\n              <br /><br/>{METAMATTER_COST_ON_PAYMENT}\n              <!-- ENDIF -->\n            <!-- ENDIF -->\n          <!-- ENDIF -->\n        <!-- ENDIF -->\n        <br />\n        <br />\n        <!-- IF ! PAY_LINK_URL -->\n          <input class=\"fl\" type=\"submit\" value=\"{L_pay_mm_buy_confirm}\">\n        <!-- ELSEIF PAY_LINK_METHOD == 'GET' || PAY_LINK_METHOD == 'POST' -->\n          <button class=\"fl\" type=\"submit\">{L_pay_mm_buy_pay}</button>\n        <!-- ELSEIF PAY_LINK_METHOD == 'LINK' -->\n          <script type=\"text/javascript\">\n            function make_payment_popup() {\n              this.open = function(ob) {\n                $(ob).hide();\n                $('<span id=\"psload\" class=\"fl\">{LA_pay_mm_buy_in_progress}</span>').insertAfter(ob);\n                var win = window.open('{PAY_LINK_URL}', 'PayStation', 'width=850px, height=550px, scrollbars=yes, status=no');\n                setTimeout(function () {\n                  if(win.closed) {\n                    $('#psload').remove();\n                    $(ob).show();\n                  } else {\n                    setTimeout(arguments.callee,500);\n                  }\n                }, 500);\n              }\n            }\n            payment_popup = new make_payment_popup;\n          </script>\n\n          <button class=\"fl\" type=\"button\" onclick=\"payment_popup.open(this);return false;\">{L_pay_mm_buy_pay}</button>\n        <!-- ENDIF -->\n      </form>\n\n      <form action=\"{D_SN_ROOT_VIRTUAL}metamatter.php\" method=\"POST\">\n        <input class=\"fr\" type=\"submit\" value=\"{L_pay_mm_buy_reset}\" onclick=\"jQuery('#payment_amount').val('');jQuery('#payment_module').val('');\">\n      </form>\n\n      <!-- ELSEIF URL_PURCHASE -->\n        {L_pay_mm_buy_url_description} <a href=\"{URL_PURCHASE}\"><span class=\"link\">{L_pay_mm_buy_url_get}</span></a>\n      <!-- ELSE -->\n        {L_pay_mm_buy_url_none}\n      <!-- ENDIF -->\n    </td>\n  </tr>\n</table>\n"
  },
  {
    "path": "design/templates/OpenGame/mrc_mercenary_hire.tpl.html",
    "content": "<h1>{PAGE_HEADER}</h1>\n\n<table width=\"535\">\n  <tr>\n    <td colspan=\"2\" class=\"c\">{L_sys_dark_matter}</td>\n  </tr>\n\n  <tr>\n    <th width=\"120\" ><img src=\"design/images/DMaterie.jpg\" width=\"120\" height=\"120\"></th>\n    <th width=\"315\" >\n      <p align=\"justify\">{L_sys_dark_matter_desc}</p>\n      <a href=\"dark_matter.php\" id=\"off_get_dark_matter\" class=\"link\">{L_sys_dark_matter_obtain_header}</a>\n    </th>\n  </tr>\n\n  <tr>\n    <td width=\"100%\" colspan=\"2\" class=\"c\">{PAGE_HEADER}</td>\n  </tr>\n\n<script type=\"text/javascript\">\n  var mrc_cost_array = Array();\n  var mrc_discount_array = Array();\n  var dark_matter_amount = {DARK_MATTER};\n  var is_premanent = {IS_PERMANENT};\n  <!-- BEGIN period -->\n  mrc_discount_array[{period.LENGTH}] = {period.DISCOUNT};\n  <!-- END period -->\n</script>\n\n<script type=\"text/javascript\">\nfunction mrc_change(mercenary_id)\n{\n  var final_cost_element = document.getElementById('final_cost_' + mercenary_id);\n\n  var period_element = document.getElementById('period_' + mercenary_id);\n//  var period = mrc_discount_array[period_element.value]; // mrc_discount_array[discount_element.options[discount_element.selectedIndex].value];\n  var mrc_price = Math.ceil(mrc_cost_array[mercenary_id][document.getElementById('level_' + mercenary_id).value] * mrc_discount_array[period_element.value]);\n\n  final_cost_element.innerHTML = sn_format_number(mrc_price);\n  final_cost_element.className = mrc_price <= dark_matter_amount ? 'positive' : 'negative';\n  document.getElementById('make_purchase_' + mercenary_id).style.visibility = mrc_price <= dark_matter_amount ? 'visible' : 'hidden';\n}\n</script>\n\n  <!-- BEGIN officer -->\n<script type=\"text/javascript\">\n  mrc_cost_array[{officer.ID}] = Array();\n</script>\n\n    <tr>\n      <th width=120>\n        <div style=\"width: 11em; height: 11em; position: relative;\" class=\"tech_image\" unit_id=\"{officer.ID}\">\n          <img src=\"{I_[officer.ID]}\" class=\"posLT w100\" />\n          <span class=\"posLT w100 a50\">{officer.NAME}</span>\n          <!-- !IF officer.LEVEL || officer.LEVEL_BONUS -->\n\n          <div class=\"ok w100 posLB a50\">&nbsp;</div>\n\n          <!-- IF officer.HIRE_END && ! IS_PERMANENT -->\n            <!-- IF officer.HIRE_LEFT_PERCENT >= 50 -->\n            <!-- DEFINE $OFFICER_CLASS_BG = 'positive_bg' -->\n            <!-- ELSEIF officer.HIRE_LEFT_PERCENT >= 25 -->\n            <!-- DEFINE $OFFICER_CLASS_BG = 'neutral_bg' -->\n            <!-- ELSEIF officer.HIRE_LEFT_PERCENT >= 10 -->\n            <!-- DEFINE $OFFICER_CLASS_BG = 'warning_bg' -->\n            <!-- ELSE -->\n            <!-- DEFINE $OFFICER_CLASS_BG = 'negative_bg' -->\n            <!-- ENDIF -->\n            <div class=\"ok w100 posLB {$OFFICER_CLASS_BG}\" style=\"width: {officer.HIRE_LEFT_PERCENT}%\">&nbsp;</div>\n          <!-- ENDIF -->\n\n          <div class=\"posLB w100\">\n            {L_sys_level}\n            {officer.LEVEL}<!-- IF officer.LEVEL_BONUS --><span class=\"bonus\">+{officer.LEVEL_BONUS}</span><!-- ENDIF -->/{officer.LEVEL_MAX}\n          </div>\n\n          <!-- IF officer.LEVEL >= officer.LEVEL_MAX -->\n          <div class=\"ok w100 a75 posLB\" style=\"bottom: 1em;\">{L_sys_maximum_level}</div>\n          <!-- ENDIF -->\n\n\n          <!-- !ENDIF -->\n        </div>\n      </th>\n\n      <th align=justify>\n        {officer.DESCRIPTION}\n        <!-- IF IS_PERMANENT && officer.require -->\n        <div id=\"unit_require_wrapper\">\n          <div class=\"c_l neutral\">{L_wiki_requrements}</div>\n          <ul id=\"unit_require\">\n            <!-- BEGIN require -->\n              <!-- IF require.LEVEL_GOT >= require.LEVEL_REQUIRED -->\n              <!-- DEFINE $MERC_REQ_CLASS = 'positive' -->\n              <!-- ELSE -->\n              <!-- DEFINE $MERC_REQ_CLASS = 'negative' -->\n              <!-- ENDIF -->\n\n              <li class=\"{$MERC_REQ_CLASS}\">\n                {require.NAME} {require.LEVEL_GOT}/{require.LEVEL_REQUIRED}\n                <!--+ (!isNaN(req['level_basic']) ? ' ' + req['level_basic'] + (req['level_bonus'] ? '<span class=\"bonus\">+' + req['level_bonus'] + '</span>' : '') + '/' + req['level_require'] : '')-->\n              </li>\n            <!-- END require -->\n          </ul>\n        </div>\n        <!-- ENDIF -->\n        <br><br>\n        <div align=\"center\">\n          <div class=\"positive\" align=\"center\">{officer.BONUS} {officer.EFFECT}</div><br />\n\n          <form method=\"post\">\n            <input type=\"hidden\" name=\"mercenary_id\" value=\"{officer.ID}\">\n\n            <!-- IF officer.LEVEL -->\n              <!-- IF officer.LEVEL >= officer.LEVEL_MAX -->\n                <div class=\"neutral\">{L_sys_maximum_level}\n              <!-- ELSE -->\n                <div>{L_sys_level}\n              <!-- ENDIF -->\n\n              {officer.LEVEL}<!-- IF officer.LEVEL_BONUS --><span class=\"bonus\">+{officer.LEVEL_BONUS}</span><!-- ENDIF -->/{officer.LEVEL_MAX}\n              <!-- IF ! IS_PERMANENT && officer.HIRE_END -->\n                {L_mrc_up_to} {officer.HIRE_END}\n                <input type=\"submit\" value=\"{L_mrc_dismiss}\" onclick=\"return confirm('{LA_mrc_dismiss_confirm}');\"><br />\n                <span class=\"warning\">{L_mrc_dismiss_before_hire}</span>\n              <!-- ENDIF -->\n              </div>\n            <!-- ENDIF -->\n\n            <!-- IF (! officer.LEVEL && ! IS_PERMANENT) || (officer.LEVEL < officer.LEVEL_MAX && IS_PERMANENT) -->\n              <!-- IF .officer.level && officer.CAN_BUY -->\n                {L_sys_level}\n                <select name=\"mercenary_level\" id=\"level_{officer.ID}\" onchange=\"mrc_change({officer.ID});\">\n                  <!-- BEGIN level -->\n                  <option value=\"{level.VALUE}\"<!-- IF officer.LEVEL == level.VALUE --> selected<!-- ENDIF -->>{level.VALUE}</option>\n                  <script type=\"text/javascript\">\n                    mrc_cost_array[{officer.ID}][{level.VALUE}] = {level.PRICE};\n                  </script>\n                  <!-- END level -->\n                </select>\n\n                <!-- IF ! IS_PERMANENT -->\n                  <select name=\"mercenary_period\" id=\"period_{officer.ID}\" onchange=\"mrc_change({officer.ID});\">\n                    <!-- BEGIN !period -->\n                    <option value=\"{period.LENGTH}\"<!-- IF period.SELECTED --> selected<!-- ENDIF -->>{period.TEXT}</option>\n                    <!-- END period -->\n                  </select>\n                <!-- ELSE -->\n                  <input type=\"hidden\" name=\"mercenary_period\" id=\"period_{officer.ID}\" value=\"{C_empire_mercenary_base_period}\">\n                <!-- ENDIF -->\n                {L_sys_buy_for} <span id=\"final_cost_{officer.ID}\" class=\"<!-- IF officer.COST_TEXT <= DARK_MATTER -->positive<!-- ELSE -->negative<!-- ENDIF -->\">{officer.COST_TEXT}</span> {L_sys_dark_matter_sh}\n                <input type=\"submit\" id=\"make_purchase_{officer.ID}\" value=\"<!-- IF MODE == 600 -->{L_mrc_hire}<!-- ELSE -->{L_sys_buy}<!-- ENDIF -->\">\n                <script type=\"text/javascript\">\n                  mrc_change({officer.ID});\n                </script>\n              <!-- ENDIF -->\n\n              <!-- IF ! .officer.level --><br /><span class=\"error\">{L_mrc_msg_error_no_resource}</span><!-- ENDIF -->\n              <!-- IF ! officer.CAN_BUY --><br /><a href=\"index.php?page=techtree\"><span class=\"error\">{L_mrc_msg_error_requirements}</span></a><!-- ENDIF -->\n            <!-- ENDIF -->\n          </form>\n\n        </div>\n      </th>\n    </tr>\n  <!-- END officer -->\n</table>\n"
  },
  {
    "path": "design/templates/OpenGame/msg_message_class.tpl.html",
    "content": "<h2>{L_msg_page_header}</h2>\n<table width=\"519\">\n  <tr class=\"c_c\">\n    <th>{L_msg_head_type}</th>\n    <th>{L_msg_head_count}</th>\n    <th>{L_msg_head_total}</th>\n    <th><img src=\"design/images/r1.png\"></th>\n  </tr>\n\n  <!-- BEGIN message_class -->\n    <tr class=\"{message_class.STYLE} c_c\">\n      <td><a href=\"messages.php?mode=show&message_class={message_class.ID}\"><span class=\"{message_class.STYLE}\">{message_class.TEXT}</span></a></td>\n      <td><!-- IF message_class.ID != -1 --><span class=\"{message_class.STYLE}\">{message_class.UNREAD}</span><!-- ELSE -->&nbsp;<!-- ENDIF --></td>\n      <td><span class=\"{message_class.STYLE}\">{message_class.TOTAL}</span></td>\n      <td><!-- IF message_class.ID != -1 --><a href=\"messages.php?mode=delete&message_class={message_class.ID}&message_range=class&return=1\" title=\"{L_msg_del_class}\"><span class=\"{message_class.STYLE}\"><img src=\"design/images/r1.png\"></span></a><!-- ELSE -->&nbsp;<!-- ENDIF --></td>\n    </tr>\n  <!-- END message_class -->\n</table>\n\n<br />\n<input type=\"button\" value=\"{L_msg_compose}\" onclick=\"document.location = 'messages.php?mode=write';\" />\n<br />\n\n<!-- INCLUDE page_hint -->\n"
  },
  {
    "path": "design/templates/OpenGame/msg_message_compose.tpl.html",
    "content": "<h2>{L_msg_page_header}</h2>\n<h3>{L_msg_compose}</h3>\n\n<form action=\"messages.php\" method=\"post\">\n  <input type=\"hidden\" name=\"mode\" value=\"write\">\n  <input type=\"hidden\" name=\"id\" value=\"{RECIPIENT_ID}\">\n\n  <table width=\"519\">\n    <tr>\n      <td class=\"c_l\">{L_msg_recipient}</td>\n      <td class=\"c_l\"><input type=\"text\" name=\"recipient_name\" size=\"40\" value=\"{RECIPIENT_NAME}\" /></td>\n    </tr>\n    \n    <tr>\n      <td class=\"c_l\">{L_msg_subject}</td>\n      <td class=\"c_l\"><input type=\"text\" name=\"subject\" size=\"40\" maxlength=\"40\" value=\"{SUBJECT}\" /></td>\n    </tr>\n    \n    <tr>\n      <td class=\"c_c\" colspan=\"2\"><span class=\"fl\">{L_msg_text}</span><span class=\"fr\">(<span id=\"cntChars\">0</span> / 5000 {characters})</span></td>\n    </tr>\n\n    <tr>\n      <td class=\"c_c\" colspan=\"2\"><textarea name=\"text\" cols=\"40\" rows=\"10\" size=\"100\" onkeyup=\"cntchar(5000)\">{TEXT}</textarea></td>\n    </tr>\n    \n    <tr>\n      <th colspan=\"2\"><input name=\"msg_send\" type=\"submit\" value=\"{L_sys_send}\" /></th>\n    </tr>\n  </table>\n</form>\n\n<!-- IF RECIPIENT_ID && .messages -->\n<h3>{L_msg_header_dialog} {RECIPIENT_NAME}</h3>\n<table width=\"519\">\n  <!-- BEGIN messages -->\n  <tr class=\"<!-- IF RECIPIENT_ID == messages.FROM_ID -->mnl_joueur<!-- ELSE -->mnl_outbox<!-- ENDIF --> c_l\" >\n    <td>\n      {messages.DATE}\n    </td>\n    <td>\n      {messages.FROM}\n    </td>\n    <td>\n      {messages.SUBJ}<br/>\n    </td>\n  </tr>\n  <tr>\n    <td class=\"<!-- IF RECIPIENT_ID == messages.FROM_ID -->mnl_joueur<!-- ELSE -->mnl_outbox<!-- ENDIF --> c_l\" colspan=\"3\">\n      {messages.TEXT}\n    </td>\n  </tr>\n  <!-- END messages -->\n</table>\n<!-- ENDIF -->\n"
  },
  {
    "path": "design/templates/OpenGame/msg_message_list.tpl.html",
    "content": "<script type=\"text/javascript\">\n  $.extend(language, {\n    msg_ignore_message: '{LA_msg_ignore_message}',\n    msg_ignore_title: '{LA_msg_ignore_title}',\n  });\n\n  function f(target_url, win_name) {\n    var new_win = window.open(target_url, win_name, 'resizable=yes,scrollbars=yes,menubar=no,toolbar=no,width=550,height=280,top=0,left=0');\n    new_win.focus();\n  }\n\n  $(document).on('click', '.js_message_delete', function () {\n    $(\".js_message_range\").val(\"checked\");\n  });\n\n  $(document).on('change', '.js_message_checkbox_all', function () {\n    $('.msg_checkbox').attr('checked', $(this).is(':checked'));\n    $(\".js_message_delete\").button($(this).is(':checked') ? \"enable\" : \"disable\");\n  });\n\n  $(document).on('change', '.msg_checkbox', function () {\n    var someOneChecked = false, allIsChecked = true;\n    var js_message_checkbox_all = $('.js_message_checkbox_all');\n    $('.msg_checkbox').each(function () {\n      someOneChecked = someOneChecked || $(this).is(':checked');\n      allIsChecked = allIsChecked && $(this).is(':checked');\n    });\n    $(\".js_message_delete\").button(someOneChecked ? \"enable\" : \"disable\");\n\n    if (js_message_checkbox_all.attr('checked') && !someOneChecked) {\n      js_message_checkbox_all.attr('checked', false);\n    }\n\n    if (!js_message_checkbox_all.attr('checked') && allIsChecked) {\n      js_message_checkbox_all.attr('checked', true);\n    }\n  });\n\n  $(document).on('click', '.js_ignore', function (e) {\n    var element = $(this);\n\n    var senderId = element.attr('sender_id');\n    var senderName = element.attr('sender_name');\n    var senderNameHTML = '<span class=\"notice\">' + element.attr('sender_name') + '</span>';\n\n    if (!$(this).attr('disabled')) {\n      var title = language['msg_ignore_title'].replace('\\[PLAYER_NAME\\]', senderName);\n      var message = language['msg_ignore_message'].replace('\\[PLAYER_NAME\\]', senderNameHTML);\n\n      snConfirm({\n        that: $(this),\n        title: title,\n        message: message,\n        data: {id: senderId, name: senderName},\n        'confirm': function () {\n          jQuery.get(\n            SN_ROOT_VIRTUAL + \"index.php?page=ajax&mode=AjaxIgnore&action=ignorePlayer&subjectId=\" + senderId,\n            function (data) {\n              // Checking for correct JSON structure\n              if (\n                data.hasOwnProperty('player')\n                && data.player.hasOwnProperty('ignores')\n                && data.player.ignores.hasOwnProperty(senderId)\n                && data.player.ignores[senderId]\n              ) {\n                $(\"[sender_id=\" + senderId + \"]\").parent().parent().parent().parent().hide();\n              }\n            },\n            \"json\"\n          );\n        }\n      });\n    }\n\n    e.preventDefault();\n    return false;\n  });\n</script>\n\n<style type=\"text/css\">\n  .pm_subject\n  {\n    font-size: 120%;\n    border-bottom: 3px solid rgba(255, 255, 255, 0.5);\n  }\n\n  .pm_text\n  {\n    padding: 1em 0 1em 1em;\n    background-color: rgba(0, 0, 0, 0.2);\n  }\n\n  .pm_ignore, .pm_answer\n  {\n    margin: 0.2em 0 0.2em 1em;\n  }\n</style>\n\n<h2>{L_msg_page_header}</h2>\n<h3>{MESSAGE_CLASS_TEXT}</h3>\n<form action=\"messages.php\" method=\"post\">\n  <input type=\"hidden\" name=\"mode\" value=\"show\"/>\n  <input type=\"hidden\" name=\"message_class\" value=\"{MESSAGE_CLASS}\"/>\n  <input type=\"hidden\" name=\"message_range\" value=\"\" class=\"js_message_range\"/>\n\n  <table width=\"519\">\n    <!-- IF MESSAGE_CLASS != -1 -->\n    <!-- INCLUDE msg_message_list_paging -->\n    <!-- DEFINE $COL_SPAN = '2' -->\n    <!-- ELSE -->\n    <!-- DEFINE $COL_SPAN = '1' -->\n    <!-- ENDIF -->\n\n    <!-- IF .messages && PAGER_MESSAGES -->\n    <tr class=\"c_c\">\n      <td colspan=\"{$COL_SPAN}\" class=\"subheader\">\n        {PAGER_MESSAGES}\n      </td>\n    </tr>\n    <!-- ENDIF -->\n\n    <tr>\n      <!-- IF MESSAGE_CLASS != -1 -->\n      <th class=\"c_c\" width=\"20\">\n        <input type=\"checkbox\" class=\"js_message_checkbox_all\"/>\n      </th>\n      <!-- ENDIF -->\n\n      <th class=\"c_c\">{L_msg_message}</th>\n    </tr>\n\n    <!-- BEGIN messages -->\n    <tr class=\"{messages.STYLE}\">\n      <!-- IF MESSAGE_CLASS != -1 -->\n      <td valign=\"top\">\n        <input name=\"mark[{messages.ID}]\" value=\"{messages.ID}\" type=\"checkbox\" class=\"msg_checkbox\"/>\n      </td>\n      <!-- ENDIF -->\n\n      <td>\n        <div class=\"no_border_image\">\n          <div class=\"contFJ pm_subject\">\n            {messages.SUBJ}\n          </div>\n\n          <div class=\"contFJ\">\n            <div>\n              {messages.DATE}\n            </div>\n\n            <div class=\"contFS\">\n              <!-- IF MESSAGE_CLASS == -1 -->\n              {L_msg_recipient}\n              <!-- ELSE -->\n              {L_msg_from}\n              <!-- ENDIF -->\n              &nbsp;\n\n              <!-- IF messages.FROM_ID -->\n              <a href=\"messages.php?mode=write&id={messages.FROM_ID}&subject={L_msg_answer_prefix}{messages.SUBJ_SANITIZED}\"\n                 class=\"link\">\n                <img src=\"design/images/icon_mail.gif\" alt=\"{L_msg_answer}\" border=\"0\"> {messages.FROM}\n              </a>\n              <!-- ELSE -->\n              {messages.FROM}\n              <!-- ENDIF -->\n            </div>\n          </div>\n\n          <div class=\"c_l pm_text\" id='mess{messages.ID}'>\n            {messages.TEXT}\n          </div>\n\n          <!-- IF MESSAGE_CLASS != -1 && messages.FROM_ID -->\n          <div class=\"contFE\">\n            <!-- IF messages.CAN_IGNORE -->\n            <div class=\"js_ignore pm_ignore link_action contF\" sender_id=\"{messages.FROM_ID}\" sender_name=\"{messages.FROM}\">\n              <img src=\"design/images/icon_ignore_mail.gif\" alt=\"{L_msg_ignore}\" border=\"0\">&nbsp;{L_msg_ignore}\n            </div>\n            <!-- ENDIF -->\n\n            <a href=\"messages.php?mode=write&id={messages.FROM_ID}&subject={L_msg_answer_prefix}{messages.SUBJ_SANITIZED}\"\n               class=\"pm_answer link contF\">\n              <img src=\"design/images/icon_mail.gif\" alt=\"{L_msg_answer}\" border=\"0\">&nbsp;{L_msg_answer}\n            </a>\n          </div>\n          <!-- ENDIF -->\n        </div>\n      </td>\n    </tr>\n\n    <!-- BEGINELSE messages -->\n    <tr>\n      <td class=\"c_c notice\" colspan=\"{$COL_SPAN}\">\n        {L_msg_warn_no_messages}\n      </td>\n    </tr>\n    <!-- END messages -->\n\n    <!-- IF .messages && PAGER_MESSAGES -->\n    <tr class=\"c_c\">\n      <td colspan=\"{$COL_SPAN}\" class=\"subheader\">\n        {PAGER_MESSAGES}\n      </td>\n    </tr>\n    <!-- ENDIF -->\n\n\n    <!-- IF MESSAGES_IGNORED -->\n    <tr><td colspan=\"{$COL_SPAN}\" class=\"notice\">\n      {MESSAGES_IGNORED} {L_msg_ignored_messages}<br/>\n      {L_msg_ignore_control}\n    </td></tr>\n    <!-- ENDIF -->\n\n    <!-- IF MESSAGE_CLASS != -1 -->\n    <!-- INCLUDE msg_message_list_paging -->\n    <!-- ENDIF -->\n  </table>\n</form>\n\n"
  },
  {
    "path": "design/templates/OpenGame/msg_message_list_paging.tpl.html",
    "content": "<tr>\n  <th colspan=\"4\">\n    <div class=\"contFJ\">\n      <input name=\"msg_show_all\" value=\"{L_msg_show_all}\" type=\"submit\" class=\"js_message_show_all\" />\n\n      <input name=\"msg_delete\" value=\"{L_msg_delete_checked}\" type=\"submit\" class=\"js_message_delete\" disabled />\n    </div>\n  </th>\n</tr>\n"
  },
  {
    "path": "design/templates/OpenGame/msg_message_spy.tpl.html",
    "content": "<table class=\"no_border_image spy_table\" cellspacing=\"1\">\n  <tr class=\"c_c\">\n    <th colspan=\"2\">\n      {L_sys_mess_spy_report} -\n      <a class=\"link\" href=\"galaxy.php?mode=3&galaxy={TARGET_PLANET_GALAXY}&system={TARGET_PLANET_SYSTEM}&planet={TARGET_PLANET_PLANET}\">\n        [{TARGET_PLANET_GALAXY}:{TARGET_PLANET_SYSTEM}:{TARGET_PLANET_PLANET}] {TARGET_PLANET_TYPE_TEXT_SH}\n        {TARGET_PLANET_NAME}\n      </a>\n    </th>\n  </tr>\n\n  <tr class=\"c_l\">\n    <td>\n      {Время сканирования}\n    </td>\n    <td class=\"c_r\">\n      {REPORT_TIME}\n    </td>\n  </tr>\n\n\n  <tr class=\"c_l\">\n    <td>\n      {Владелец}\n    </td>\n    <td class=\"c_r\">\n      <a class=\"link\" href=\"index.php?page=imperator&int_user_id={TARGET_PLAYER_ID}\">\n        <img src=\"{I_menu_empire_emperor}\" border=\"0\" alt=\"{L_stat_details}\" title=\"{L_stat_details}\"/>\n        <!-- IF TARGET_PLAYER_ALLY_TAG -->[{TARGET_PLAYER_ALLY_TAG}] <!-- ENDIF -->{TARGET_PLAYER_NAME}\n      </a>\n    </td>\n  </tr>\n\n  <tr>\n    <th class=\"c_c\" colspan=\"2\">\n      <!-- IF SPIES_DESTROYED -->\n      {L_sys_mess_spy_destroyed}\n      <!-- ELSE -->\n      {L_sys_mess_spy_detect_chance_no_percent} {SPIES_DETECTION_CHANCE}%\n      <!-- ENDIF -->\n    </th>\n  </tr>\n\n  <tr class=\"с_с\">\n    <th class=\"c_c\">\n      <a class=\"link notice\" href=\"simulator.php?replay={SIMULATOR_DATA}\">{L_COE_combatSimulator}</a><br/>\n    </th>\n    <th class=\"c_c\">\n      <a class=\"link error\" href=\"fleet.php?target_mission=1&planet_type={TARGET_PLANET_TYPE}&galaxy={TARGET_PLANET_GALAXY}&system={TARGET_PLANET_SYSTEM}&planet={TARGET_PLANET_PLANET}\">{L_type_mission[1]}</a><br/>\n    </th>\n  </tr>\n\n  <!-- BEGIN group -->\n  <tr>\n    <th colspan=\"2\" class=\"c_l bg_subheader\">\n      {group.NAME}\n    </th>\n  </tr>\n  <!-- BEGIN unit -->\n  <tr class=\"c_l\">\n    <td>\n      {unit.NAME}\n    </td>\n    <td class=\"c_r\">\n      {unit.AMOUNT}\n    </td>\n  </tr>\n  <!-- END unit -->\n  <!-- END group -->\n\n</table>\n"
  },
  {
    "path": "design/templates/OpenGame/navbar_button_queued.tpl.html",
    "content": "<!-- INCLUDE eco_queue -->\n<div class=\"navbar_button {$NAVBAR_QUE_WIDE}\" title=\"{$NAVBAR_QUE_HINT}\" go_url=\"{$NAVBAR_QUE_URL}\">\n  <!-- IF $NAVBAR_QUE_IMAGE -->\n  <img class=\"navbar_button_image\" src=\"{$NAVBAR_QUE_IMAGE}\" alt=\"{$NAVBAR_QUE_HINT}\"/>\n  <!-- ELSE -->\n  <div class=\"{$NAVBAR_QUE_CLASS}\" title=\"{$NAVBAR_QUE_HINT}\" go_url=\"{$NAVBAR_QUE_URL}\"></div>\n  <!-- ENDIF -->\n\n  <!-- IF $QUE_NOT_EMPTY -->\n\n  <div class=\"a50 navbar_button_que\">\n    <div class=\"bld_que_total_time_text topnav_{$QUE_ID}_hide_on_complete\">\n      {L_que_slot_length}&nbsp;\n      <span id=\"topnav_{$QUE_ID}_slots\"></span>\n    </div>\n\n    <div class=\"bld_que_total_bar topnav_{$QUE_ID}_hide_on_complete\">\n      <div id=\"topnav_{$QUE_ID}_progress_bar\" class=\"bld_que_total_progress_bar\"></div>\n      <div id=\"topnav_{$QUE_ID}_total\" class=\"bld_que_total_timer\"></div>\n    </div>\n\n    <div class=\"navbar_button_que_current_unit\">\n      <div>\n        <span id=\"topnav_{$QUE_ID}\"></span>\n        <span id=\"topnav_{$QUE_ID}_units\"></span>\n      </div>\n    </div>\n\n    <div class=\"que_slot_time_container que_item_0 topnav_{$QUE_ID}_hide_on_complete\">\n      <div class=\"que_unit_time que_unit_progress_bar topnav_{$QUE_ID}_unit_bar_0\"></div>\n      <div class=\"que_unit_time topnav_{$QUE_ID}_timer_0 a50\"></div>\n    </div>\n\n    <div id=\"topnav_{$QUE_ID}_que\" class=\"hide\"></div>\n  </div>\n\n  <!-- ELSE -->\n\n  <span class=\"posLT w100 a50\">\n    {$NAVBAR_QUE_NAME}\n  </span>\n\n  <span class=\"a50 navbar-container-button-que-empty\">\n    {L_eco_que_empty}\n  </span>\n\n  <!-- IF $QUE_ID == QUE_STRUCTURES -->\n  <span id=\"topnav_colonies_counter_total\" class=\"a50 posRB w100\">\n    <span id='topnav_colonies_counter'>{TOPNAV_COLONIES_CURRENT}</span>/{TOPNAV_COLONIES_MAX}\n  </span>\n  <!-- ENDIF -->\n\n  <!-- ENDIF -->\n</div>\n"
  },
  {
    "path": "design/templates/OpenGame/navbar_news.tpl.html",
    "content": "<table width=519 id=\"fresh_news_table\" align=\"center\" class=\"border_image_small\">\n  <tr>\n    <th class=\"c_c\">\n      {L_news_fresh}\n      <img id=\"news_close\" class=\"fr pointer image_close\" src=\"{I_r1}\" alt=\"{L_sys_close}\" title=\"{L_sys_close}\" />\n    </th>\n  </tr>\n  <!-- INCLUDE news_list -->\n  <tr>\n    <th class=\"c_c\">\n      <a class=\"link\" href=\"announce.php\">{L_news_all}</a>\n    </th>\n  </tr>\n</table>\n"
  },
  {
    "path": "design/templates/OpenGame/navbar_notes.tpl.html",
    "content": "<table id=\"note_table\" align=\"center\" class=\"border_image_small\">\n  <tr>\n    <th colspan=2 class=\"c_c\">\n      <a class=\"link\" href=\"notes.php\">{L_sys_notes}</a>\n    </th>\n  </tr>\n  <!-- INCLUDE note_list -->\n</table>\n"
  },
  {
    "path": "design/templates/OpenGame/news_list.tpl.html",
    "content": "<script type=\"text/javascript\" src=\"{D_SN_ROOT_VIRTUAL}js/sn_survey.js?{C_var_db_update}\"></script>\n<script type=\"text/javascript\">\n  var LA_survey_result_sent = '{LA_survey_result_sent}';\n</script>\n\n<!-- IF MM_MODULE_ACTIVE && AUTHLEVEL >= 3 -->\n<!-- DEFINE $SURVEY_ANSWER_COL_SPAN = '4' -->\n<!-- ELSE -->\n<!-- DEFINE $SURVEY_ANSWER_COL_SPAN = '2' -->\n<!-- ENDIF -->\n\n<!-- BEGIN announces -->\n  <tr>\n    <td>\n      <div class=\"news_header contFJ\">\n        <div class=\"news_date\">\n          <!-- IF announces.FUTURE -->\n          <span class=\"news_future\">{L_news_future}</span>&nbsp;\n          <!-- ELSEIF announces.NEW -->\n          <span class=\"news_fresh\">{L_news_new}</span>&nbsp;\n          <!-- ENDIF -->\n          <a class=\"link\" href=\"announce.php?id={announces.ID}\">{announces.TIME}</a>\n        </div>\n\n        <!-- IF announces.USER_NAME -->\n        <div class=\"news_publisher\">\n          {announces.USER_NAME}\n        </div>\n        <!-- ENDIF -->\n      </div>\n      <div class=\"news_text\">\n\n        <!-- DEFINE $CLOSE_DIV = false -->\n        <!-- BEGIN paragraph -->\n        <!-- IF ! paragraph.S_FIRST_ROW --><br /><!-- ENDIF -->\n          {paragraph.TEXT}\n        <!-- IF paragraph.S_LAST_ROW --><!-- IF $CLOSE_DIV --></div><!-- ENDIF --><!-- ELSE --><br /><!-- ENDIF -->\n\n        <!-- IF paragraph.S_FIRST_ROW && ! paragraph.S_LAST_ROW && ! $NEWS_FULL -->\n          <!-- DEFINE $CLOSE_DIV = true -->\n          <span news_toggle=\"{announces.ID}\" class=\"button_pseudo\">\n            {L_news_show_rest}\n          </span>\n          <div id=\"news_{announces.ID}\" class=\"news_hide\">\n        <!-- ENDIF -->\n\n        <!-- END paragraph -->\n\n        <!-- IF announces.DETAIL_URL -->\n         <a class=\"news_link\" href=\"{announces.DETAIL_URL}\">{L_news_more}</a>\n        <!-- ENDIF -->\n      </div>\n      <!-- IF announces.SURVEY_TEXT -->\n      <div class=\"survey_block\" survey_id=\"{announces.SURVEY_ID}\">\n        <table width=\"100%\" class=\"c_l border_image_small\">\n          <tr>\n            <th class=\"survey_header\" colspan=\"{$SURVEY_ANSWER_COL_SPAN}\">\n              {L_survey}: {announces.SURVEY_TEXT}\n            </th>\n          </tr>\n        <!-- IF announces.SURVEY_CAN_VOTE -->\n          <!-- BEGIN survey_answers -->\n          <tr>\n            <td colspan=\"2\" class=\"button_pseudo\" survey_id=\"{announces.SURVEY_ID}\" answer_id=\"{survey_answers.ID}\">\n              <input type=\"radio\" name=\"survey[{announces.SURVEY_ID}]\" id=\"sa_{survey_answers.ID}\" value=\"{survey_answers.ID}\" />\n              {survey_answers.TEXT}\n            </td>\n          </tr>\n          <!-- END survey_answers -->\n          <tr>\n            <th class=\"survey_confirm\" colspan=\"2\">\n              {L_survey_select_one} <button name=\"survey_confirm\" value=\"{announces.SURVEY_ID}\">{L_survey_confirm}</button>\n            </th>\n          </tr>\n        <!-- ELSE -->\n          <!-- BEGIN survey_answers -->\n          <tr>\n            <td colspan=\"2\">\n              <div class=\"survey_votes\">\n                <div class=\"survey_votes_bar ok_bg\" style=\"width: {survey_answers.PERCENT}%;\"></div>\n                <div class=\"survey_votes_text\">\n                  {survey_answers.TEXT}\n                </div>\n\n                <div class=\"survey_votes_percent\">\n                  {survey_answers.PERCENT_TEXT}%\n                </div>\n                <div class=\"survey_votes_number\">\n                  {survey_answers.VOTES}\n                </div>\n              </div>\n            </td>\n            <!-- IF MM_MODULE_ACTIVE && AUTHLEVEL >= 3 -->\n            <td class=\"c_r\">{survey_answers.MM|num|format}</td>\n            <td class=\"c_r\">{survey_answers.PERCENT_MM|num|format}%</td>\n            <!--<td class=\"c_r\">{survey_answers.MONEY|num|format}</td>-->\n            <!--<td class=\"c_r\">{survey_answers.PERCENT_MONEY|num|format}%</td>-->\n            <!-- ENDIF -->\n          </tr>\n          <!-- END survey_answers -->\n        <!-- ENDIF -->\n          <!-- BEGIN total_votes -->\n          <tr>\n            <th class=\"survey_voted\" colspan=\"{$SURVEY_ANSWER_COL_SPAN}\">\n              <span class=\"warning\">\n              <!-- IF total_votes.TOTAL_VOTES -->\n                <!-- IF announces.SURVEY_CAN_VOTE -->\n                  {L_survey_votes_total_voted} {total_votes.TOTAL_VOTES}. {L_survey_votes_total_voted_join}\n                <!-- ELSE -->\n                  {L_survey_votes_total_voted_has_answer} {total_votes.TOTAL_VOTES}\n                <!-- ENDIF -->\n              <!-- ELSE -->\n                <!-- IF announces.SURVEY_CAN_VOTE -->\n                {L_survey_votes_total_none}\n                <!-- ENDIF -->\n              <!-- ENDIF -->\n              </span>\n            </th>\n          </tr>\n          <!-- END total_votes -->\n          <tr>\n            <th class=\"c_c survey_length\" colspan=\"{$SURVEY_ANSWER_COL_SPAN}\">\n              <!-- IF announces.SURVEY_COMPLETE -->\n                <span class=\"warning\">{L_survey_complete}</span>\n              <!-- ELSE -->\n                <span class=\"ok\">{L_survey_lasts_until} {announces.SURVEY_UNTIL}</span>\n              <!-- ENDIF -->\n            </th>\n          </tr>\n        </table>\n      </div>\n      <!-- ENDIF -->\n      <!-- IF AUTHLEVEL >= 3 -->\n      <div class=\"c_r subheader\">\n        <a href=\"announce.php?mode=edit&id={announces.ID}\" class=\"button_pseudo\"><img src=\"design/images/icon_edit.png\"></a>\n        <a href=\"announce.php?mode=copy&id={announces.ID}\" class=\"button_pseudo\"><img src=\"design/images/icon_copy.gif\"></a>\n        <a href=\"announce.php?mode=del&id={announces.ID}\" class=\"button_pseudo\"><img src=\"design/images/r1.png\"></a>\n      </div>\n      <!-- ENDIF -->\n    </td>\n  </tr>\n<!-- BEGINELSE announces -->\n  <tr><th class=\"c\">{L_news_none}</th></tr>\n<!-- END announces -->\n"
  },
  {
    "path": "design/templates/OpenGame/note_list.tpl.html",
    "content": "<!-- BEGIN note -->\n<tr>\n  <!-- IF note.ID != NOTE_ID_EDIT -->\n    <!-- IF NOTE_FULL_RENDER -->\n    <td class=\"c_c\">\n      <a name=\"a{note.ID}\"></a>\n      <input type=\"checkbox\" name=\"note[{note.ID}]\">\n    </td>\n    <!-- ENDIF -->\n\n    <td class=\"c_l\">\n      <div class=\"subheader contFJ {note.PRIORITY_CLASS} note_header\">\n        <div>\n          <!-- IF note.STICKY && NOTE_FULL_RENDER --><img src=\"{I_icon_note_pinned_16}\" class=\"icon_1em\" />&nbsp;<!-- ENDIF -->\n\n          <!-- IF note.GALAXY || note.SYSTEM || note.PLANET -->\n          <a class=\"link subheader note_coordinates\" href=\"galaxy.php?mode=2&galaxy={note.GALAXY}&system={note.SYSTEM}\">[{note.GALAXY}:{note.SYSTEM}:{note.PLANET}] {note.PLANET_TYPE_TEXT_SHORT}</a>&nbsp;\n          <!-- ENDIF -->\n          <span class=\"\">{note.TITLE}</span>\n        </div>\n\n        <!-- IF NOTE_FULL_RENDER -->\n        <div>{note.TIME_TEXT}</div>\n        <!-- ENDIF -->\n      </div>\n\n      <div style=\"font-weight: normal; text-align: justify;\" class=\"{note.PRIORITY_CLASS}\">\n        {note.TEXT}\n      </div>\n    </td>\n    <!-- IF NOTE_FULL_RENDER -->\n    <td class=\"c_c\">\n      <!-- IF NOTE_ID_EDIT -->\n        &nbsp;\n      <!-- ELSE -->\n        <img src=\"design/images/icon_edit.png\" data-note_id=\"{note.ID}\" />\n      <!-- ENDIF -->\n    </td>\n    <!-- ENDIF -->\n  <!-- ELSE -->\n  <td class=\"c_l\" colspan=\"2\">\n    <!-- IF NOTE_ID_EDIT --><a name=\"a{note.ID}\"></a><!-- ENDIF -->\n    <span class=\"fl\">\n      {L_note_priority}\n      <select name=\"note_priority\" id=\"note_priority\">\n        <!-- BEGIN !note_priority -->\n        <option value=\"{note_priority.ID}\" class=\"{note_priority.CLASS}\"<!-- IF note_priority.ID == note.PRIORITY --> selected<!-- ENDIF -->>{note_priority.TEXT}</option>\n        <!-- END !note_priority -->\n      </select>\n    </span>\n\n    <span class=\"fr {note.PRIORITY_CLASS}\">{L_note_date} {note.TIME_TEXT}</span>\n    <br />\n\n    <input type=\"text\" id=\"note_title\" name=\"note_title\" size=\"61\" maxlength=\"128\" value=\"{note.TITLE}\">\n    <br />\n\n    <div class=\"c_l\">\n      {L_sys_coordinates}\n      <input type=\"text\" id=\"note_galaxy\" name=\"note_galaxy\" size=\"4\" maxlength=\"4\" value=\"{note.GALAXY}\">\n      <input type=\"text\" id=\"note_system\" name=\"note_system\" size=\"4\" maxlength=\"4\" value=\"{note.SYSTEM}\">\n      <input type=\"text\" id=\"note_planet\" name=\"note_planet\" size=\"4\" maxlength=\"4\" value=\"{note.PLANET}\" />\n      <select name=\"note_planet_type\">\n        <!-- BEGIN !planet_type -->\n        <option value=\"{planet_type.ID}\"<!-- IF planet_type.ID == note.PLANET_TYPE --> selected<!-- ENDIF -->>{planet_type.TEXT}</option>\n        <!-- END !planet_type -->\n      </select>\n    </div>\n    <textarea id=\"note_text\" name=\"note_text\" cols=\"40\" rows=\"5\" class=\"do-not-skin\">{note.TEXT_EDIT}</textarea>\n    <br />\n\n    <input type=\"checkbox\" id=\"note_sticky\" name=\"note_sticky\" value=\"1\"<!-- IF note.STICKY --> checked<!-- ENDIF -->/>\n    <label for=\"note_sticky\">{L_note_stick_it}</label>\n  </td>\n  <td class=\"c_c\"><img src=\"design/images/icon_accept.png\" onclick=\"note_validate({note.ID});\">\n    <!-- IF NOTE_ID_EDIT -->\n    <br /><br /><br /><br /><img src=\"design/images/icon_deny.png\" data-note_id=\"0\">\n    <!-- ENDIF --></td>\n  <!-- ENDIF -->\n</tr>\n<!-- END note -->\n"
  },
  {
    "path": "design/templates/OpenGame/notes.tpl.html",
    "content": "<h1>{PAGE_HEADER}</h1>\n\n<script type=\"text/javascript\" src=\"js/sn_notes.js?{C_var_db_update}\"></script>\n<script type=\"text/javascript\">\n  var LA_note_new_title = '{LA_note_new_title}';\n  var LA_note_new_text = '{LA_note_new_text}';\n</script>\n\n<form action=\"notes.php\" name=\"note_form\" id=\"note_form\" method=\"POST\">\n<input type=\"hidden\" name=\"note_id_edit\" id=\"note_id_edit\" value=\"{NOTE_ID_EDIT}\" />\n<input type=\"hidden\" name=\"note_delete_range\" id=\"note_delete_range\" value=\"\" />\n<table id=\"note_main_table\">\n  <!-- IF .note > 0 -->\n  <tr>\n    <th class=\"c_c\" colspan=\"5\">\n      {L_sys_delete}\n      <select class=\"note_delete_range\">\n        <option value=\"\">{L_note_range_select}</option>\n        <option value=\"marked\">{L_note_range_marked}</option>\n        <option value=\"marked_not\">{L_note_range_marked_not}</option>\n        <option value=\"all\">{L_note_range_all}</option>\n      </select>\n      <input type=\"submit\" value=\"{L_note_delete}\" name=\"note_delete\" class=\"note_delete_button\" disabled />\n    </th>\n  </tr>\n  <!-- ENDIF -->\n  <tr>\n    <th class=\"c_c\" width=\"20px\"><input type=\"checkbox\" name=\"note_select\" id=\"note_select_all\" onclick=\"note_select_do(undefined);\"></th>\n    <th class=\"c_l\">{L_note_note}</th>\n    <th class=\"c_c\" width=\"20px\">Action</th>\n  </tr>\n  <!-- INCLUDE note_list -->\n  <!-- IF .note > 0 -->\n  <tr>\n    <th class=\"c_c\" colspan=\"5\">\n      {L_sys_delete}\n      <select class=\"note_delete_range\" onchange=\"jQuery('#note_delete').prop('disabled', jQuery(this).val() ? '' : 'disabled');\">\n        <option value=\"\">{L_note_range_select}</option>\n        <option value=\"marked\">{L_note_range_marked}</option>\n        <option value=\"marked_not\">{L_note_range_marked_not}</option>\n        <option value=\"all\">{L_note_range_all}</option>\n      </select>\n      <input type=\"submit\" value=\"{L_note_delete}\" name=\"note_delete\" class=\"note_delete_button\" disabled />\n    </th>\n  </tr>\n  <!-- ENDIF -->\n</table>\n</form>\n"
  },
  {
    "path": "design/templates/OpenGame/novapedia.tpl.html",
    "content": "<h1>{PAGE_HEADER}</h1>\n\n<table class=\"c_l novapedia\" width=\"519\">\n  <tr>\n    <th class=\"c_l\" colspan=\"3\">\n      {UNIT_TYPE_NAME} - {UNIT_NAME}\n    </th>\n  </tr>\n\n  <tr class=\"c_j\">\n    <td colspan=\"3\">\n      <!-- IF UNIT_IMAGE_LARGE -->\n      <div>\n        <div>\n          <img src=\"{I_[UNIT_ID]_large}\" class=\"unit_image_large\" unit_id=\"{UNIT_ID}\" align=\"top\" border=\"0\">\n        </div>\n\n        <div class=\"unit_description\">\n          {UNIT_DESCRIPTION}\n        </div>\n      </div>\n      <!-- ELSE -->\n      <div style=\"display: table-row; position: relative; margin: 7px;\">\n        <img src=\"{I_[UNIT_ID]}\" class=\"fl unit_image_small\" unit_id=\"{UNIT_ID}\" align=\"top\"/>\n        {UNIT_DESCRIPTION}\n      </div>\n      <!-- ENDIF -->\n\n      <!-- IF UNIT_EFFECT -->\n      <hr />\n      {UNIT_BONUS} {UNIT_EFFECT}\n      <!-- ENDIF -->\n\n    </td>\n  </tr>\n  <!-- IF .require -->\n  <tr>\n    <th class=\"c_l\" colspan=\"3\">\n    {L_wiki_requrements}\n    </th>\n  </tr>\n  <tr>\n    <td colspan=\"3\">\n      <ul>\n        <!-- BEGIN require -->\n        <li style=\"margin: 0; padding: 0;\"><span class=\"<!-- IF require.REQUEREMENTS_MET -->positive<!-- ELSE -->negative<!-- ENDIF -->\">{require.NAME}</span><!-- IF require.LEVEL_REQUIRE -->&nbsp;{require.LEVEL_BASIC}<!-- IF require.LEVEL_BONUS --><span class=\"bonus\">+{require.LEVEL_BONUS}</span><!-- ENDIF -->/{require.LEVEL_REQUIRE}<!-- ENDIF --></li>\n        <!-- END require -->\n      </ul>\n    </td>\n  </tr>\n  <!-- ENDIF -->\n</table>\n\n  <!-- IF UNIT_IS_COMBAT || (UNIT_IS_SHIP && .engine) -->\n<table width=\"519\">\n  <!-- IF UNIT_IS_SHIP && .engine -->\n    <tr class=\"c_l\">\n      <th>{L_wiki_ship_header}</th>\n      <th>{L_wiki_char_nominal}</th>\n      <th>{L_wiki_char_actual}</th>\n    </tr>\n    <tr class=\"c_r\">\n      <td class=\"c_l\"><div>{L_wiki_ship_speed}</div></td>\n      <td>{BASE_SPEED}</td>\n      <td>{ACTUAL_SPEED}</td>\n    </tr>\n    <tr class=\"c_r\">\n      <td class=\"c_l\">{L_wiki_ship_consumption}</td>\n      <td>{BASE_CONSUMPTION}</td>\n      <td>{ACTUAL_CONSUMPTION}</td>\n    </tr>\n    <tr class=\"c_r\">\n      <td class=\"c_l\">{L_wiki_ship_capacity}</td>\n      <td>{BASE_CAPACITY}</td>\n      <td>{ACTUAL_CAPACITY}</td>\n    </tr>\n    <tr>\n      <td class=\"c_j\" colspan=\"3\">\n        <ul>\n          {L_wiki_ship_hint}\n        </ul>\n      </td>\n    </tr>\n\n    <tr class=\"c_l\">\n      <th>{L_wiki_ship_engine_header}</th>\n      <th>{L_wiki_char_nominal}</th>\n      <th>{L_wiki_char_actual}</th>\n    </tr>\n\n    <!-- BEGIN engine -->\n      <tr class=\"c_l\">\n        <td colspan=\"3\"><span class=\"<!-- IF engine.MIN_LEVEL < engine.USER_TECH_LEVEL -->positive<!-- ELSE -->negative<!-- ENDIF -->\">{engine.NAME}</span>&nbsp;{engine.USER_TECH_LEVEL}/{engine.MIN_LEVEL}</td>\n      </tr>\n      <tr class=\"c_r\">\n        <td class=\"c_l\"><div>{L_wiki_ship_speed}</div></td>\n        <td>{engine.BASE_SPEED}</td>\n        <td>{engine.ACTUAL_SPEED}</td>\n      </tr>\n      <tr class=\"c_r\">\n        <td class=\"c_l\">{L_wiki_ship_consumption}</td>\n        <td>{engine.BASE_CONSUMPTION}</td>\n        <td>{engine.ACTUAL_CONSUMPTION}</td>\n      </tr>\n    <!-- END engine -->\n  <!-- ENDIF -->\n\n  <!-- IF UNIT_IS_COMBAT -->\n    <tr class=\"c_l\">\n      <th>{L_wiki_combat_header}</th>\n      <th>{L_wiki_char_nominal}</th>\n      <th>{L_wiki_char_actual}</th>\n    </tr>\n    <tr class=\"c_r\">\n      <td class=\"c_l\">{L_wiki_combat_attack}</td>\n      <td>{BASE_WEAPON}</td>\n      <td>{ACTUAL_WEAPON}</td>\n    </tr>\n    <tr class=\"c_r\">\n      <td class=\"c_l\">{L_wiki_combat_shield}</td>\n      <td>{BASE_SHIELD}</td>\n      <td>{ACTUAL_SHIELD}</td>\n    </tr>\n    <tr class=\"c_r\">\n      <td class=\"c_l\">{L_wiki_combat_armor}</td>\n      <td>{BASE_ARMOR}</td>\n      <td>{ACTUAL_ARMOR}</td>\n    </tr>\n\n    <!-- IF .volley -->\n    <tr>\n      <th class=\"c_l\">\n        {L_wiki_combat_volley_header}\n      </th>\n\n      <th class=\"c_l positive\">\n        {L_wiki_combat_volley_to}\n      </th>\n\n      <th class=\"c_l negative\">\n        {L_wiki_combat_volley_from}\n      </th>\n    </tr>\n    <!-- BEGIN volley -->\n      <tr>\n        <td class=\"c_l\">\n          {volley.ENEMY_NAME}\n        </td>\n\n        <td class=\"c_r positive\">\n          <!-- IF volley.TO >= 1 -->\n          {volley.TO}\n          <!-- ELSE -->\n          &lt;&nbsp;1\n          <!-- ENDIF -->\n        </td>\n\n        <td class=\"c_r negative\">\n          <!-- IF volley.FROM >= 1 -->\n          {volley.FROM}\n          <!-- ELSE -->\n          &lt;&nbsp;1\n          <!-- ENDIF -->\n        </td>\n      </tr>\n    <!-- END volley -->\n    <!-- ENDIF -->\n  <!-- ENDIF -->\n</table>\n  <!-- ENDIF -->\n"
  },
  {
    "path": "design/templates/OpenGame/options.tpl.html",
    "content": "<script type=\"text/javascript\">\n  $.extend(language, {\n    sys_login_password_show: '{LA_sys_login_password_show}' + 1,\n    sys_login_password_hide: '{LA_sys_login_password_hide}' + 2,\n  });\n\n  var C_player_vacation_time = '{C_player_vacation_time}';\n  var C_player_delete_time = '{C_player_delete_time}';\n  var JS_FMT_DATE = '{JS_FMT_DATE}';\n</script>\n\n<script type=\"text/javascript\">\n  $(document).on('change', '#username_confirm', function () {\n    var userText = $(\"#js_username_new\");\n    if ($(this).is(\":checked\")) {\n      userText.show();\n    } else {\n      userText.hide();\n    }\n  });\n\n  $(document).on('focus', '#user_birthday', function () {\n    if ($(this).val() == JS_FMT_DATE) {\n      $(this).val('');\n    }\n  });\n  $(document).on('blur', '#user_birthday', function () {\n    if ($(this).val() == '') {\n      $(this).val(JS_FMT_DATE);\n    }\n  });\n\n  $(document).ready(function () {\n    $(\".tabs\").tabs();\n    $(\"#tab_container\").show();\n\n    // var index = $('.tabs a[href=\"#tab_ignores\"]').parent().index();\n    // $(\".tabs\").tabs(\"option\", \"active\", index);\n    // $(\".tabs\").tabs(\"option\", \"active\", 3);\n  });\n</script>\n\n<script type=\"text/javascript\">\n  $(document).on('click', '.js_unignore', function (e) {\n    var element = $(this);\n\n    var senderId = element.attr('sender_id');\n\n    jQuery.get(\n      SN_ROOT_VIRTUAL + \"index.php?page=ajax&mode=AjaxIgnore&action=unIgnorePlayer&subjectId=\" + senderId,\n      function (data) {\n        // Checking for correct JSON structure\n        if (\n          data.hasOwnProperty('player')\n          && data.player.hasOwnProperty('ignores')\n          && data.player.ignores.hasOwnProperty(senderId)\n        ) {\n          $(\"[sender_id=\" + senderId + \"]\").parent().parent().hide();\n        }\n      },\n      \"json\"\n    );\n\n    e.preventDefault();\n    return false;\n  });\n</script>\n\n<h1>{PAGE_HEADER}</h1>\n<!-- IF CHANGE_PASS -->\n  <!-- IF CHANGE_PASS == -1 -->\n    <h3 class=\"positive\">{L_opt_msg_pass_changed}</h3>\n  <!-- ELSEIF CHANGE_PASS == 1  -->\n    <h3 class=\"negative\">{L_opt_err_pass_wrong}</h3>\n  <!-- ELSEIF CHANGE_PASS == 2  -->\n    <h3 class=\"negative\">{L_opt_err_pass_unmatched}</h3>\n  <!-- ENDIF -->\n<!-- ENDIF -->\n\n<form action=\"index.php?page=options&mode=change\" method=\"post\" name=\"fOptions\" enctype=\"multipart/form-data\" style=\"display: inline-block;\">\n  <input type=\"hidden\" name=\"refresh\" value=\"1\" />\n\n  <div id=\"tab_container\" style=\"font-size: 1em; width: 50em; display: none;\" class=\"tabs\">\n    <ul>\n      <li><a href=\"#tab_account\">{L_opt_account}</a></li>\n      <li><a href=\"#tab_interface\">{L_opt_int_options}</a></li>\n      <li><a href=\"#tab_alerts\">{L_opt_alerts}</a></li>\n      <li><a href=\"#tab_ignores\">{L_opt_ignores}</a></li>\n    </ul>\n\n    <div id=\"tab_account\">\n      <table width=\"100%\" class=\"no_border_image\">\n        <!-- IF ADM_PROTECT_PLANETS -->\n        <tr class=\"c_c\">\n          <th colspan=\"2\">\n            <!-- IF adm_pl_prot -->\n            <!-- DEFINE $PLANET_PROTECTION = 'checked=\"checked\"' -->\n            <!-- ELSE -->\n            <!-- DEFINE $PLANET_PROTECTION = '' -->\n            <!-- ENDIF -->\n            <input name=\"adm_pl_prot\" type=\"checkbox\" id=\"adm_pl_prot\" {$PLANET_PROTECTION} />\n            <label for=\"adm_pl_prot\">{L_opt_adm_planet_prot}</label>\n          </th>\n        </tr>\n        <!-- ENDIF -->\n\n        <tr>\n          <th class=\"c_c\" colspan=\"2\">{L_opt_common}</th>\n        </tr>\n        <!-- IF AUTH_PROVIDER -->\n        <tr>\n          <th>{L_opt_account_name}</th>\n          <th width=330>\n            {AUTH_PROVIDER}\n            {ACCOUNT_NAME}\n          </th>\n        </tr>\n        <!-- ENDIF -->\n        <tr>\n          <th>\n            {L_opt_account_name}\n          </th>\n          <th width=330 class=\"ok\">\n            {ACCOUNT_NAME}\n          </th>\n        </tr>\n\n        <tr>\n          <th>\n            {L_opt_game_user_name}\n          </th>\n          <th width=330>\n            <!-- IF SERVER_NAME_CHANGE -->\n              {L_username_old} <span class=\"ok\">{opt_usern_data}</span><br />\n              <div>\n                <!-- IF ! SERVER_NAME_CHANGE_ENABLED -->\n                <!-- DEFINE $DISABLE_USERNAME_CONFIRM = 'disabled' -->\n                <!-- ELSE -->\n                <!-- DEFINE $DISABLE_USERNAME_CONFIRM = '' -->\n                <!-- ENDIF -->\n                <input type=\"checkbox\" name=\"username_confirm\" id=\"username_confirm\" value=\"1\" {$DISABLE_USERNAME_CONFIRM} />\n                <label for=\"username_confirm\">{L_username_change_confirm}</label>\n                <!-- IF SERVER_NAME_CHANGE_PAY -->\n                  {L_username_change_confirm_payed} {DARK_MATTER} {L_sys_dark_matter_sh}\n                <!-- ENDIF -->\n              </div>\n              <div id=\"js_username_new\" style=\"display: none;\">\n                {L_username_new}\n                <input type=\"text\" name=\"username\" value=\"{opt_usern_data}\" maxlength=\"32\" size=\"32\" {$DISABLE_USERNAME_CONFIRM} />\n              </div>\n            <!-- ELSE -->\n            <div>{opt_usern_data}</div>\n            <div class=\"notice\">{L_option_change_nick_disabled}</div>\n            <!-- ENDIF -->\n          </th>\n        </tr>\n        <tr>\n          <th>{L_lastpassword}</th>\n          <th>\n            <input id=\"db_password\" name=\"db_password\" size=\"20\" maxlength=\"32\" value=\"\" type=\"password\" />\n            <input class=\"password_show\" type=\"button\" value=\"{L_sys_login_password_show}\" show_element=\"db_password\" />\n          </th>\n        </tr>\n        <tr>\n          <th>{L_newpassword}</th>\n          <th>\n            <input id=\"newpass1\" name=\"newpass1\" size=\"20\" maxlength=\"32\" type=\"password\" />\n            <input class=\"password_show\" type=\"button\" value=\"{L_sys_login_password_show}\" show_element=\"newpass1\" />\n          </th>\n        </tr>\n        <tr>\n          <th>{L_newpasswordagain}</th>\n          <th>\n            <input id=\"newpass2\" name=\"newpass2\" size=\"20\" maxlength=\"32\" type=\"password\">\n            <input class=\"password_show\" type=\"button\" value=\"{L_sys_login_password_show}\" show_element=\"newpass2\" />\n          </th>\n        </tr>\n        <tr title=\"{L_emaildir_tip}\">\n          <th>{L_emaildir}</th>\n          <th>\n            <input name=\"db_email\" maxlength=\"64\" size=\"20\" value=\"{opt_mail1_data}\" type=\"text\"><br />\n            {L_opt_mail_optional_description}\n          </th>\n        </tr>\n        <tr>\n          <th>{L_permanentemaildir}</th>\n          <th>\n            {opt_mail2_data}\n          </th>\n        </tr>\n        <tr>\n          <th><label for=\"user_birthday\">{L_opt_birthday}</label></th>\n          <th>\n            <!-- IF user_birthday -->\n            {user_birthday}\n            <!-- ELSE -->\n            <input type=\"text\" name=\"user_birthday\" id=\"user_birthday\" size=\"10\" maxlength=\"10\" value=\"{FMT_DATE}\" />\n            <!-- ENDIF -->\n          </th>\n        </tr>\n\n        <tr>\n          <th>{L_sys_gender}</th>\n          <th>\n            <!-- IF GENDER -->\n            {GENDER_TEXT}\n            <!-- ELSE -->\n            <select name=\"gender\">\n              <!-- BEGIN gender_list -->\n              <option value=\"{gender_list.VALUE}\"<!-- IF gender_list.SELECTED --> selected<!-- ENDIF -->>{gender_list.NAME}</option>\n              <!-- END gender_list -->\n            </select>\n            <!-- ENDIF -->\n          </th>\n        </tr>\n\n        <tr>\n          <th class=\"c_c\" colspan=\"2\">{L_opt_avatar}</th>\n        </tr>\n        <tr>\n          <th colspan=\"2\">\n            <label for=\"avatar\">{L_opt_upload}</label>&nbsp;<input type=\"file\" name=\"avatar\" size=\"26\" id=\"avatar\" /><br />\n            <div>\n              <!-- IF opt_avatar -->\n              <img src=\"{D_SN_HTTP_AVATAR}/avatar_{USER_ID}.png\"><br />\n              <input name=\"avatar_remove\" type=\"checkbox\" id=\"avatar_remove\" /><label for=\"avatar_remove\">{L_opt_avatar_remove}</label>\n              <!-- ELSE -->\n              <a href=\"http://www.google.com.ar/imghp\" target=\"_blank\">{L_opt_avatar_search}</a>\n              <!-- ENDIF -->\n            </div>\n          </th>\n        </tr>\n\n        <tr>\n          <td class=\"c\" colspan=\"2\">{L_delete_vacations}</td>\n        </tr>\n        <tr>\n          <td class=\"c_l\" colspan=\"2\">\n            <input name=\"opt_time_diff_clear\" type=\"checkbox\" id=\"opt_time_diff_clear\" />\n            <label for=\"opt_time_diff_clear\">{L_opt_time_diff_clear}</label><br />\n\n            <script type=\"text/javascript\">\n              jQuery(document).on('click change', '#PLAYER_OPTION_TIME_DIFF_FORCED', function (){\n                $(this).is(\":checked\") ? jQuery('#opt_time_diff_clear').attr('disabled', 'disabled').removeAttr('checked') : jQuery('#opt_time_diff_clear').removeAttr('disabled');\n              });\n              $('#PLAYER_OPTION_TIME_DIFF_FORCED').change();\n            </script>\n\n            {L_opt_time_diff_explain}<br/>\n            <input name=\"PLAYER_OPTION_TIME_DIFF_FORCED\" type=\"checkbox\" id=\"PLAYER_OPTION_TIME_DIFF_FORCED\" value=\"1\" <!-- IF user_time_diff_forced --> checked=\"checked\" <!-- ENDIF --> />\n            <label for=\"PLAYER_OPTION_TIME_DIFF_FORCED\">{L_opt_time_diff_manual}</label>\n            <input name=\"PLAYER_OPTION_TIME_DIFF\" type=\"text\" id=\"PLAYER_OPTION_TIME_DIFF\" value=\"{D_SN_CLIENT_TIME_DIFF}\" style=\"width: 60px;\" /> <label for=\"PLAYER_OPTION_TIME_DIFF\">{L_sys_sec}</label><br/>\n          </td>\n        </tr>\n        <!-- IF ! USER_VACATION_DISABLE -->\n        <tr title=\"{L_vacations_tip}\">\n          <th colspan=2>\n            <!-- IF VACATION_NEXT > SN_TIME_NOW && ! USER_AUTHLEVEL-->\n            {L_opt_vacation_err_timeout}<br />\n            {L_opt_vacation_next} {VACATION_NEXT_TEXT}\n            <!-- ELSE -->\n            <span class=\"fl\"><input id=\"vacation\" name=\"vacation\" type=\"checkbox\" {opt_modev_data} /> <label for=\"vacation\">{L_mode_vacations} {L_opt_vacation_min} <span id=\"vacancy_to\"></span></label></span>\n            <!-- ENDIF -->\n          </th>\n        </tr>\n        <!-- ENDIF -->\n      </table>\n    </div>\n\n    <div id=\"tab_interface\" class=\"tabs\">\n      <ul id=\"tabs_interface-subtabs\">\n        <li><a href=\"#tab_interface-common\">{L_opt_common}</a></li>\n        <li><a href=\"#tab_interface-tutorial\">{L_opt_tutorial}</a></li>\n        <!-- IF MENU_CUSTOMIZE -->\n        <li><a href=\"#tab_interface-menu\">{L_menu_customize_customization}</a></li>\n        <!-- ENDIF -->\n        <li><a href=\"#tab_interface-navbar\">{L_opt_navbar_title}</a></li>\n        <li><a href=\"#tab_interface-universe\">{L_opt_universe_title}</a></li>\n        <li><a href=\"#tab_interface-fleet\">{L_option_fleets}</a></li>\n      </ul>\n\n      <div id=\"tab_interface-common\">\n        <table class=\"no_border_image\">\n          <tr class=\"c_c\">\n            <th colspan=\"2\">\n              <!-- DEFINE $OPTION_BLOCK_CURRENT = '{GROUP_DESIGN_BLOCK_COMMON_TWO}' -->\n              <!-- INCLUDE options_block -->\n            </th>\n          </tr>\n\n          <tr>\n            <th class=\"c_c\" colspan=\"2\">{L_opt_common}</th>\n          </tr>\n          <tr>\n            <th>{L_opt_language}</th>\n            <th>\n              <select name=\"langer\">\n                <!-- BEGIN languages -->\n                <option value=\"{languages.VALUE}\"<!-- IF languages.SELECTED --> selected<!-- ENDIF -->>[{languages.VALUE}] {languages.NAME}</option>\n                <!-- END languages -->\n              </select>\n            </th>\n          </tr>\n          <tr>\n            <th>{L_skins_example}</th>\n            <th>\n              <input name=\"design\"{opt_sskin_data} style=\"display:none\" type=\"checkbox\" id=\"design\" />\n              <select name=\"skin_name\" size=\"1\">\n                <!-- BEGIN skin_list -->\n                <option value=\"{skin_list.VALUE}\"<!-- IF skin_list.SELECTED --> selected<!-- ENDIF -->>{skin_list.NAME}</option>\n                <!-- END skin_list -->\n              </select>\n            </th>\n          </tr>\n\n          <tr>\n            <td class=\"c_l\" colspan=\"2\">\n              <div class=\"contFJ\">\n                <label for=\"planet_sort_options\">{L_opt_planet_sort_title}</label>\n                <select name=\"options[{D_PLAYER_OPTION_PLANET_SORT}]\" id=\"planet_sort_options\">\n                  <!-- BEGIN planet_sort_options -->\n                  <option value=\"{planet_sort_options.VALUE}\"<!-- IF planet_sort_options.SELECTED --> selected<!-- ENDIF -->>{planet_sort_options.NAME}</option>\n                  <!-- END planet_sort_options -->\n                </select>\n\n                <!-- DEFINE $OPTION_BLOCK_CURRENT = '{GROUP_DESIGN_BLOCK_PLANET_SORT}' -->\n                <!-- INCLUDE options_block -->\n              </div>\n            </td>\n          </tr>\n\n\n          <!-- IF .options_3 -->\n          <!-- BEGIN options_3 -->\n          <!-- IF options_3.NAME != 'opt_int_navbar_resource_force' -->\n          <tr>\n            <!-- IF options_3.TYPE == 'integer' -->\n            <td class=\"c_c\"><label for=\"{options_3.NAME}\">{options_3.TEXT}</label></td>\n            <td class=\"c_c\">\n              <input type=\"text\" id=\"{options_3.NAME}\" name=\"{options_3.NAME}\" value=\"{options_3.VALUE}\" />\n              <!-- IF options_3.HINT --><br />{options_3.HINT}<!-- ENDIF -->\n            </td>\n            <!-- ELSE -->\n            <td class=\"c_l\" colspan=\"2\">\n              <input type=\"checkbox\" id=\"{options_3.NAME}\" name=\"{options_3.NAME}\" value=\"1\" <!-- IF options_3.VALUE --> checked<!-- ENDIF --> />\n              <label for=\"{options_3.NAME}\">{options_3.TEXT}</label>\n            </td>\n            <!-- ENDIF -->\n          </tr>\n          <!-- ENDIF -->\n          <!-- END options_3 -->\n          <!-- ENDIF -->\n\n          <tr>\n            <td class=\"c_l\" colspan=\"2\">\n              <!-- DEFINE $OPTION_BLOCK_CURRENT = '{GROUP_DESIGN_BLOCK_COMMON_ONE}' -->\n              <!-- INCLUDE options_block -->\n            </td>\n          </tr>\n\n        </table>\n      </div>\n\n      <div id=\"tab_interface-tutorial\">\n        <!-- DEFINE $OPTION_BLOCK_CURRENT = '{GROUP_DESIGN_BLOCK_TUTORIAL}' -->\n        <!-- INCLUDE options_block -->\n      </div>\n\n      <!-- IF MENU_CUSTOMIZE -->\n      <div id=\"tab_interface-menu\">\n        <!-- INCLUDE ../../../modules/menu_customize/design/templates/OpenGame/menu_customize_options -->\n      </div>\n      <!-- ENDIF -->\n\n      <div id=\"tab_interface-navbar\">\n        <div>\n          {L_opt_navbar_description}\n        </div>\n        <!-- DEFINE $OPTION_BLOCK_CURRENT = '{GROUP_DESIGN_BLOCK_RESOURCEBAR}' -->\n        <!-- INCLUDE options_block -->\n\n        <!-- IF .options_3 -->\n        <!-- BEGIN options_3 -->\n        <!-- IF options_3.NAME == 'opt_int_navbar_resource_force' -->\n        <div class=\"cell option_block_option\">\n          <input type=\"checkbox\" id=\"{options_3.NAME}\" name=\"{options_3.NAME}\" value=\"1\" <!-- IF options_3.VALUE --> checked<!-- ENDIF --> />\n          <label for=\"{options_3.NAME}\">{options_3.TEXT}</label>\n        </div>\n        <!-- ENDIF -->\n        <!-- END options_3 -->\n        <!-- ENDIF -->\n\n        <!-- DEFINE $OPTION_BLOCK_CURRENT = '{GROUP_DESIGN_BLOCK_NAVBAR}' -->\n        <!-- INCLUDE options_block -->\n      </div>\n\n      <div id=\"tab_interface-universe\">\n        <table class=\"no_border_image\">\n          <tr><td colspan=\"2\">\n            <!-- DEFINE $OPTION_BLOCK_CURRENT = '{GROUP_DESIGN_BLOCK_UNIVERSE}' -->\n            <!-- INCLUDE options_block -->\n          </td></tr>\n\n          <!-- IF .options_2 -->\n          <!-- BEGIN options_2 -->\n          <tr>\n            <td class=\"c_l\" colspan=\"2\">\n              <input type=\"checkbox\" id=\"{options_2.NAME}\" name=\"{options_2.NAME}\" value=\"1\" <!-- IF options_2.VALUE --> checked<!-- ENDIF -->>\n              <label for=\"{options_2.NAME}\">{options_2.TEXT}</label>\n            </td>\n          </tr>\n          <!-- END options_2 -->\n          <!-- ENDIF -->\n\n          <tr title=\"{L_spy_cant_tip}\">\n            <th>{L_spy_cant}</th>\n            <th><input name=\"options[{D_PLAYER_OPTION_FLEET_SPY_DEFAULT}]\" maxlength=\"2\" size=\"2\" value=\"{PLAYER_OPTION_FLEET_SPY_DEFAULT}\" type=\"text\"></th>\n          </tr>\n          <tr>\n            <th>{L_tooltip_time}</th>\n            <th><input name=\"options[{D_PLAYER_OPTION_TOOLTIP_DELAY}]\" maxlength=\"4\" size=\"4\" value=\"{PLAYER_OPTION_TOOLTIP_DELAY}\" type=\"text\"> {L_sys_milliseconds} (0 - по умолчанию)</th>\n          </tr>\n          <tr>\n            <th>{L_shortcut}</th>\n            <td class=\"c_c\" nowrap>\n              <table class=\"markup\">\n                <tr class=\"c_l\">\n                  <td>\n                    <input type=\"hidden\" name=\"options[{D_PLAYER_OPTION_UNIVERSE_ICON_SPYING}]\" value=\"0\" />\n                    <input type=\"checkbox\" name=\"options[{D_PLAYER_OPTION_UNIVERSE_ICON_SPYING}]\" value=\"1\" id=\"settings_esp\" <!-- IF user_settings_esp -->checked=\"checked\" <!-- ENDIF -->/>&nbsp;<label for=\"settings_esp\"><img height=\"14px\" src=\"design/images/icon_espionage.png\" alt=\"{L_spy}\" />&nbsp;{L_spy}</label>\n                  </td>\n                  <td>\n                    <input type=\"hidden\" name=\"options[{D_PLAYER_OPTION_UNIVERSE_ICON_MISSILE}]\" value=\"0\" />\n                    <input type=\"checkbox\" name=\"options[{D_PLAYER_OPTION_UNIVERSE_ICON_MISSILE}]\" value=\"1\" id=\"settings_mis\" <!-- IF user_settings_mis -->checked=\"checked\" <!-- ENDIF -->/>&nbsp;<label for=\"settings_mis\"><img height=\"14px\" src=\"design/images/icon_missile.png\" alt=\"{L_attack_with_missile}\" />&nbsp;{L_attack_with_missile}</label>\n                  </td>\n                </tr>\n                <tr class=\"c_l\">\n                  <td>\n                    <input type=\"hidden\" name=\"options[{D_PLAYER_OPTION_UNIVERSE_ICON_PM}]\" value=\"0\" />\n                    <input type=\"checkbox\" name=\"options[{D_PLAYER_OPTION_UNIVERSE_ICON_PM}]\" value=\"1\" id=\"settings_wri\" <!-- IF user_settings_wri -->checked=\"checked\" <!-- ENDIF -->/>&nbsp;<label for=\"settings_wri\"><img height=\"14px\" src=\"design/images/icon_mail.gif\" alt=\"{L_write_a_messege}\" />&nbsp;{L_write_a_messege}</label>\n                  </td>\n                  <td>\n                    <input type=\"hidden\" name=\"options[{D_PLAYER_OPTION_UNIVERSE_ICON_PROFILE}]\" value=\"0\" />\n                    <input type=\"checkbox\" name=\"options[{D_PLAYER_OPTION_UNIVERSE_ICON_PROFILE}]\" value=\"1\" id=\"settings_info\" <!-- IF user_settings_info -->checked=\"checked\" <!-- ENDIF -->/>&nbsp;<label for=\"settings_info\"><img height=\"14px\" src=\"{I_menu_empire_emperor}\" alt=\"{L_opt_settings_info}\" />&nbsp;{L_opt_settings_info}</label>\n                  </td>\n                </tr>\n              </table>\n            </td>\n          </tr>\n        </table>\n      </div>\n\n      <div id=\"tab_interface-fleet\">\n        <!-- DEFINE $OPTION_BLOCK_CURRENT = '{GROUP_DESIGN_BLOCK_FLEET_COMPOSE}' -->\n        <!-- INCLUDE options_block -->\n      </div>\n    </div>\n\n    <div id=\"tab_alerts\">\n      <table class=\"no_border_image\">\n        <!-- IF .options_1 -->\n        <tr>\n          <th class=\"c_c\" colspan=\"<!-- IF SERVER_SEND_EMAIL -->3<!-- ELSE -->2<!-- ENDIF -->\">{L_opt_alerts}</th>\n        </tr>\n        <!-- BEGIN options_1 -->\n\n        <!-- IF options_1.PM --><!-- DEFINE $PLAYER_OPTION_PM_CHECKED = 'checked=\"checked\"' --><!-- ELSE --><!-- DEFINE $PLAYER_OPTION_PM_CHECKED = '' --><!-- ENDIF -->\n        <!-- IF options_1.EMAIL --><!-- DEFINE $PLAYER_OPTION_EMAIL_CHECKED = 'checked=\"checked\"' --><!-- ELSE --><!-- DEFINE $PLAYER_OPTION_EMAIL_CHECKED = '' --><!-- ENDIF -->\n        <tr>\n          <td class=\"c_c\"><label for=\"{options_1.NAME}\">{options_1.TEXT}</label></td>\n          <td class=\"c_c\">\n            <!-- IF options_1.PM != -1 -->\n            <input type=\"checkbox\" id=\"opt_{options_1.NAME}\" name=\"opt_{options_1.NAME}\" value=\"1\" id=\"{options_1.NAME}\" {$PLAYER_OPTION_PM_CHECKED} />&nbsp;\n            <label for=\"opt_{options_1.NAME}\"><img src=\"design/images/icon_mail.gif\" alt=\"{write_a_messege}\" />&nbsp;{L_msg_personal_messages}</label>\n            <!-- ENDIF -->&nbsp;\n          </td>\n          <!-- IF config_game_email_pm -->\n          <td class=\"c_c\">\n            <!-- IF options_1.EMAIL != -1 -->\n            <input type=\"checkbox\" id=\"opt_email_{options_1.NAME}\" name=\"opt_email_{options_1.NAME}\" value=\"1\" id=\"{options_1.NAME}\" {$PLAYER_OPTION_EMAIL_CHECKED}/>&nbsp;\n            <label for=\"opt_email_{options_1.NAME}\">@ e-mail</label>\n            <!-- ENDIF -->&nbsp;\n          </td>\n          <!-- ENDIF -->\n        </tr>\n        <!-- END options_1 -->\n        <!-- ENDIF -->\n      </table>\n    </div>\n\n    <div id=\"tab_ignores\">\n      <table class=\"no_border_image\">\n        <tr class=\"c_c\">\n          <th colspan=\"2\">{L_opt_ignores}</th>\n        </tr>\n\n        <!-- BEGIN ignores -->\n        <tr>\n          <td colspan=\"1\">\n            {ignores.NAME}\n          </td>\n          <td colspan=\"1\">\n            <div class=\"js_unignore link_action\" sender_id=\"{ignores.ID}\" sender_name=\"{ignores.NAME}\">\n              {L_opt_unignore_do}\n            </div>\n          </td>\n        </tr>\n        <!-- BEGINELSE ignores -->\n        <tr>\n          <td colspan=\"2\" class=\"notice\" style=\"padding: 0.5em 1em;\">\n            {L_opt_ignore_list_empty}\n          </td>\n        </tr>\n        <!-- END ignores -->\n      </table>\n    </div>\n\n  </div>\n\n  <br/>\n  <input value=\"{L_save_settings}\" type=\"submit\" name=\"save_settings\" id=\"save_settings\" />\n</form>\n\n<script type=\"text/javascript\">\n  sn_timers.unshift({\n    id: 'vacancy_to',\n    type: TIMER_CLOCK_REALTIME,\n    options: {format: 3, delta: C_player_vacation_time}\n  });\n  sn_timers.unshift({\n    id: 'delete_on',\n    type: TIMER_CLOCK_REALTIME,\n    options: {format: 3, delta: C_player_delete_time}\n  });\n</script>\n"
  },
  {
    "path": "design/templates/OpenGame/options_block.tpl.html",
    "content": "<!-- BEGIN player_options -->\n<!-- IF player_options.ID == $OPTION_BLOCK_CURRENT -->\n\n<!-- IF player_options.NAME -->\n<div class=\"header options_block_header\">\n  {player_options.NAME}\n</div>\n<!-- ENDIF -->\n\n<!-- IF .player_options.option -->\n<!-- BEGIN option -->\n<div class=\"option_block_option {option.CLASS}\">\n  <!-- IF option.VALUE && ! option.ALWAYS_OFF -->\n  <!-- DEFINE $PLAYER_OPTION_THIS_CHECKED = 'checked=\"checked\"' -->\n  <!-- ELSE -->\n  <!-- DEFINE $PLAYER_OPTION_THIS_CHECKED = '' -->\n  <!-- ENDIF -->\n  <input type=\"hidden\" name=\"options[{option.ID}]\" value=\"0\"/>\n  <input type=\"checkbox\" name=\"options[{option.ID}]\" value=\"1\" id=\"PLAYER_OPTION_{option.ID}\" {$PLAYER_OPTION_THIS_CHECKED} />\n  <label for=\"PLAYER_OPTION_{option.ID}\">{option.NAME}</label>\n</div>\n<!-- END option -->\n<!-- ENDIF -->\n\n<!-- ENDIF -->\n<!-- END player_options -->\n"
  },
  {
    "path": "design/templates/OpenGame/overview_fleet_event.tpl.html",
    "content": "<tr class=\"{fleet_status}\">\n\t{fleet_javai}\n\t<th>\n\t\t<div id=\"bxx{fleet_order}\">-</div>\n\t\t<font color=\"lime\">{fleet_time}</font>\n\t</th><th colspan=\"3\">\n\t\t<span class=\"{fleet_status} {fleet_prefix}{fleet_style}\">{fleet_descr}</span>\n\t</th>\n\t{fleet_javas}\n</tr>"
  },
  {
    "path": "design/templates/OpenGame/page_hint.tpl.html",
    "content": "<!-- Preventing double include -->\n<!-- IF ! $PAGE_HINT_INCLUDED -->\n  <!-- DEFINE $PAGE_HINT_INCLUDED = 1 -->\n\n  <!-- IF PAGE_HINT -->\n    <!-- DEFINE $PAGE_HINT = '{PAGE_HINT}' -->\n  <!-- ELSEIF L_PAGE_HINT -->\n    <!-- DEFINE $PAGE_HINT = '{L_PAGE_HINT}' -->\n  <!-- ENDIF -->\n\n  <!-- IF $PAGE_HINT && ! PAGE_HINT_HIDE -->\n<div style=\"text-align: center;\">\n  <br />\n  <div style=\"max-width: 50em; min-width: 15em; display: inline-block;\">\n    <table width=\"100%\" border=\"0\" cellpadding=\"0\" cellspacing=\"1\" class=\"border_image_small\">\n      <tr><th class=\"c_c  neutral\">{L_sys_hint}</th></tr>\n      <tr><td><div class=\"hint warning\">{$PAGE_HINT}</div></td></tr>\n    </table>\n  </div>\n</div>\n  <!-- ENDIF -->\n<!-- ENDIF -->\n"
  },
  {
    "path": "design/templates/OpenGame/planet_fleet_list.tpl.html",
    "content": "<!-- INCLUDE fleet_javascript -->\n\n<table width=\"522\" id=\"fleet_list_table\" class=\"border_image_small\">\n  <tr class=\"c_c\">\n    <th width=70>{L_event_time}</th>\n    <th width=46>{L_ov_fleet}</th>\n    <th>{L_ov_destination}</th>\n    <th>{L_ov_source}</th>\n    <th>{L_ov_mission}</th>\n  </tr>\n\n  <!-- DEFINE $THIS_PLANET = 'a' -->\n  <!-- BEGIN fleets -->\n  <!-- IF fleets.NUMBER -->\n    <!-- IF $THIS_PLANET == 'a' && $OVERVIEW -->\n      <!-- IF fleets.OV_THIS_PLANET -->\n        <tr><th colspan=\"5\" class=\"c\">{L_ov_flying_fleets} {PLANET_NAME} [{PLANET_GALAXY}:{PLANET_SYSTEM}:{PLANET_PLANET}]</th></tr>\n      <!-- ENDIF -->\n      <!-- DEFINE $THIS_PLANET = 1 -->\n    <!-- ENDIF -->\n\n    <!-- IF $THIS_PLANET == 1 && fleets.OV_THIS_PLANET != 1 -->\n      <tr><th colspan=\"5\" class=\"c\">{L_ov_flying_fleets} {L_ov_other_planets}</th></tr>\n      <!-- DEFINE $THIS_PLANET = 2 -->\n    <!-- ENDIF -->\n\n    <!-- IF fleets.OV_LABEL == 0 -->\n      <!-- DEFINE $OV_FLEET_ACTION = 'flight' -->\n    <!-- ELSEIF fleets.OV_LABEL == 1 -->\n      <!-- DEFINE $OV_FLEET_ACTION = 'holding' -->\n    <!-- ELSEIF fleets.OV_LABEL == 2 -->\n      <!-- DEFINE $OV_FLEET_ACTION = 'return' -->\n    <!-- ENDIF -->\n\n    <!-- IF USER_ID == fleets.OWNER -->\n      <!-- DEFINE $OV_FLEET_PREFIX = 'own' -->\n    <!-- ELSE -->\n      <!-- DEFINE $OV_FLEET_PREFIX = '' -->\n    <!-- ENDIF -->\n\n    <!-- IF fleets.MISSION == 1 -->\n      <!-- DEFINE $OV_FLEET_STYLE = 'attack' -->\n    <!-- ELSEIF fleets.MISSION ==  2 -->\n      <!-- DEFINE $OV_FLEET_STYLE = 'federation' -->\n    <!-- ELSEIF fleets.MISSION ==  3 -->\n      <!-- DEFINE $OV_FLEET_STYLE = 'transport' -->\n    <!-- ELSEIF fleets.MISSION ==  4 -->\n      <!-- DEFINE $OV_FLEET_STYLE = 'deploy' -->\n    <!-- ELSEIF fleets.MISSION ==  5 -->\n      <!-- DEFINE $OV_FLEET_STYLE = 'hold' -->\n    <!-- ELSEIF fleets.MISSION ==  6 -->\n      <!-- DEFINE $OV_FLEET_STYLE = 'espionage' -->\n    <!-- ELSEIF fleets.MISSION ==  7 -->\n      <!-- DEFINE $OV_FLEET_STYLE = 'colony' -->\n    <!-- ELSEIF fleets.MISSION ==  8 -->\n      <!-- DEFINE $OV_FLEET_STYLE = 'harvest' -->\n    <!-- ELSEIF fleets.MISSION ==  9 -->\n      <!-- DEFINE $OV_FLEET_STYLE = 'destroy' -->\n    <!-- ELSEIF fleets.MISSION == 10 -->\n      <!-- DEFINE $OV_FLEET_STYLE = 'missile' -->\n    <!-- ELSEIF fleets.MISSION == 15 -->\n      <!-- DEFINE $OV_FLEET_STYLE = 'expedition' -->\n    <!-- ENDIF -->\n\n    <tr class=\"{$OV_FLEET_ACTION} {$OV_FLEET_PREFIX}{$OV_FLEET_STYLE}\">\n      <th>\n        <div id=\"ov_fleer_timer_{$OV_FLEET_ACTION}{fleets.ID}\">00:00:00</div>\n        {fleets.EVENT_TIME_TEXT}\n      </th>\n      <th style=\"cursor: pointer;\" onmouseover='fleet_dialog_show(this, {fleets.ID})' onmouseout='popup_hide()'>\n        {fleets.AMOUNT}\n      </th>\n      <!-- IF fleets.OV_LABEL == 0 || fleets.OV_LABEL == 1  || fleets.OV_LABEL == 3 -->\n        <th>\n          {fleets.END_NAME}<br>\n          {fleets.END_URL} {fleets.END_TYPE_TEXT_SH}\n        </th>\n        <th>\n          {fleets.START_NAME}<br>\n          {fleets.START_URL} {fleets.START_TYPE_TEXT_SH}\n        </th>\n      <!-- ELSEIF fleets.OV_LABEL == 2 -->\n        <th>\n          {fleets.START_NAME}<br>\n          {fleets.START_URL} {fleets.START_TYPE_TEXT_SH}\n        </th>\n        <th>\n          {fleets.END_NAME}<br>\n          {fleets.END_URL} {fleets.END_TYPE_TEXT_SH}\n        </th>\n      <!-- ENDIF -->\n      <th>\n        {fleets.MISSION_NAME}<br>\n        <div><!-- IF fleets.OV_LABEL == 0 -->{L_ov_fleet_arrive}<!-- ELSEIF fleets.OV_LABEL == 1 -->{fleets.MISSION_NAME} - {L_ov_fleet_hold}<!-- ELSEIF fleets.OV_LABEL == 2 -->{L_ov_fleet_return}<!-- ELSEIF fleets.OV_LABEL == 3 -->{L_ov_fleet_rocket}<!-- ENDIF --></div>\n      </th>\n    </tr>\n\n    <script type=\"text/javascript\"><!--\n      sn_timers.unshift({id: 'ov_fleer_timer_{$OV_FLEET_ACTION}{fleets.ID}', type: TIMER_BUILD_QUE_V1, options: {\n        msg_done: '{L_sys_fleet_arrived}',\n        que: [\n          ['{fleets.ID}', '', {fleets.OV_LEFT}, '0']\n        ]\n      }});\n    --></script>\n  <!-- ENDIF -->\n  <!-- BEGINELSE fleets -->\n    <tr><th colspan=5>{L_ov_fleet_no_flying}</th></tr>\n  <!-- END fleets -->\n</table>\n"
  },
  {
    "path": "design/templates/OpenGame/planet_governor.tpl.html",
    "content": "<!-- IF ! $PLANET_GOVERNOR_ID -->\n  <!-- DEFINE $PLANET_GOVERNOR_ID = '{PLANET_GOVERNOR_ID}'-->\n<!-- ELSE -->\n  <!-- DEFINE $PLANET_GOVERNOR_ID = ''-->\n<!-- ENDIF -->\n<!-- IF ! $PLANET_GOVERNOR_LEVEL -->\n  <!-- DEFINE $PLANET_GOVERNOR_LEVEL = '{PLANET_GOVERNOR_LEVEL}'-->\n<!-- ELSE -->\n  <!-- DEFINE $PLANET_GOVERNOR_LEVEL = ''-->\n<!-- ENDIF -->\n\n<!-- IF PLANET_GOVERNOR_LEVEL > 0 && $PLANET_GOVERNOR_ID -->\n  <div class=\"unit_preview\" id=\"unit{production.ID}\" unit_id=\"{$PLANET_GOVERNOR_ID}\">\n    <div onclick=\"document.location='overview.php?mode=manage&cp={$PLANET_ID}'\">\n      <span class=\"posFill\">\n        {I_[$PLANET_GOVERNOR_ID]|html|height=\"100%\"}\n      </span>\n\n      <span class=\"posT icon_alpha\">\n        <!-- IF $PLANET_GOVERNOR_NAME -->{$PLANET_GOVERNOR_NAME}<!-- ELSE -->{PLANET_GOVERNOR_NAME}<!-- ENDIF -->\n      </span>\n\n      <span class=\"posB icon_alpha\">\n        <!-- IF $PLANET_GOVERNOR_LEVEL -->\n          {$PLANET_GOVERNOR_LEVEL}<!-- IF PLANET_GOVERNOR_LEVEL_PLUS --><span class=\"zero\"><!-- IF PLANET_GOVERNOR_LEVEL_PLUS > 0 -->+<!-- ENDIF -->{PLANET_GOVERNOR_LEVEL_PLUS}</span><!-- ENDIF -->\n        <!-- ENDIF -->\n        <!-- IF PLANET_GOVERNOR_LEVEL_MAX --> / {PLANET_GOVERNOR_LEVEL_MAX}<!-- ENDIF -->\n      </span>\n    </div>\n\n    <span class=\"unit-icon-info-container\" onclick=\"document.location='infos.php?gid={$PLANET_GOVERNOR_ID}'\">\n      <div class=\"icon-info-cover\">&nbsp;</div>\n    </span>\n  </div>\n<!-- ELSE -->\n  {L_sys_governor_none}\n<!-- ENDIF -->\n\n\n<!-- IF $IS_PLANET_OVERVIEW -->\n<a class=\"button_pseudo\" href=\"overview.php?mode=manage&cp={$PLANET_ID}\">\n  <!-- IF PLANET_GOVERNOR_LEVEL > 0 && $PLANET_GOVERNOR_ID -->\n  {L_sys_governor_upgrade_or_change}\n  <!-- ELSE -->\n  {L_sys_governor_hire}\n  <!-- ENDIF -->\n</a>\n<!-- ENDIF -->\n"
  },
  {
    "path": "design/templates/OpenGame/planet_list.tpl.html",
    "content": "<!-- IF $OVERVIEW -->\n  <!-- DEFINE $CELL_CLASS = 'tr' -->\n<!-- ELSE -->\n  <!-- DEFINE $CELL_CLASS = '' -->\n<!-- ENDIF -->\n\n<!-- IF $OVERVIEW && .planet > 5 -->\n  <!-- DEFINE $COLUMN_COUNT = 2 -->\n<!-- ENDIF -->\n\n<!-- BEGIN planet -->\n  <!-- DEFINE $PLANET_PLANET_ID = '{planet.ID}' -->\n\n  <!-- IF $OVERVIEW && (! LIST_COLUMN_COUNT || planet.S_ROW_COUNT % LIST_COLUMN_COUNT == 0)  -->\n  <tr>\n  <!-- ENDIF -->\n  <td valign=\"top\" class=\"{$CELL_CLASS} pointer\" align=\"center\">\n  <!-- IF planet.ID -->\n\n    <!-- INCLUDE planet_list_info -->\n\n    <!-- IF $OVERVIEW -->\n    <br /><br />\n    <!-- ENDIF -->\n  <!-- ENDIF -->\n  </td>\n  <!-- IF $OVERVIEW && (! LIST_COLUMN_COUNT || (planet.S_ROW_COUNT + 1) % LIST_COLUMN_COUNT == 0 || planet.S_LAST_ROW)  -->\n    </tr>\n  <!-- ENDIF -->\n<!-- END planet -->\n"
  },
  {
    "path": "design/templates/OpenGame/planet_list_info.tpl.html",
    "content": "<!-- BEGIN !planet --><!-- IF $PLANET_PLANET_ID == planet.ID -->\n\n<!-- IF planet.ID == PLANET_ID || (planet.MOON_ID == PLANET_ID && $OVERVIEW) -->\n  <!-- DEFINE $FRAME_WIDTH = 'planet_list_top_medium' -->\n  <!-- DEFINE $PLANET_IMG_SIZE_PREFIX = '' -->\n<!-- ELSE -->\n  <!-- DEFINE $FRAME_WIDTH = 'planet_list_top_small' -->\n  <!-- DEFINE $PLANET_IMG_SIZE_PREFIX = 's_' -->\n<!-- ENDIF -->\n\n<!-- DEFINE $FRAME_WIDTH = 'planet_list_top_medium' -->\n<!-- DEFINE $PLANET_IMG_SIZE_PREFIX = 's_' -->\n\n<div class=\"{$FRAME_WIDTH} planet_list_item\">\n  <div class=\"planet_list_wrap\">\n    <div class=\"left_top images\" planet_id=\"{$PLANET_PLANET_ID}\">\n      <img src=\"{I_[$PLANET_IMG_SIZE_PREFIX][planet.IMAGE]}\" class=\"element_fill_wrap\" go=\"\" />\n\n\n      <div class=\"icon_25per_left_1\" title=\"{planet.STRUCTURE}\" go=\"build\">\n        <img src=\"{D_SN_ROOT_VIRTUAL}design/images/build_building.png\" class=\"icon_fill <!-- IF planet.STRUCTURE_SLOTS -->a75<!-- ELSE -->a50 o50<!-- ENDIF -->\" />\n        <!-- IF planet.STRUCTURE_SLOTS -->\n        <span class=\"a75 left_top\">{planet.STRUCTURE_SLOTS}</span>\n        <!-- ENDIF -->\n      </div>\n      <div class=\"icon_25per_left_2\" title=\"{planet.HANGAR}\" go=\"build\" mode=\"fleet\" >\n        <img src=\"{D_SN_ROOT_VIRTUAL}design/images/build_hangar.png\" class=\"icon_fill <!-- IF planet.HANGAR_SLOTS -->a75<!-- ELSE -->a50 o50<!-- ENDIF -->\" />\n        <!-- IF planet.HANGAR_SLOTS -->\n        <span class=\"a75 left_top\">{planet.HANGAR_SLOTS}</span>\n        <!-- ENDIF -->\n      </div>\n      <div class=\"icon_25per_left_3\" title=\"{planet.DEFENSE}\" go=\"build\" mode=\"defense\">\n        <img src=\"{D_SN_ROOT_VIRTUAL}design/images/build_defense.png\" class=\"icon_fill <!-- IF planet.DEFENSE_SLOTS -->a75<!-- ELSE -->a50 o50<!-- ENDIF -->\" />\n        <!-- IF planet.DEFENSE_SLOTS -->\n        <span class=\"a75 left_top\">{planet.DEFENSE_SLOTS}</span>\n        <!-- ENDIF -->\n      </div>\n\n      <!-- IF planet.PLANET_GOVERNOR_ID && planet.PLANET_GOVERNOR_LEVEL -->\n      <span class=\"a75 icon_25per_left_4\" title=\"{planet.PLANET_GOVERNOR_NAME} {planet.PLANET_GOVERNOR_LEVEL}\" go=\"\" mode=\"manage\">\n            <img src=\"{I_[planet.PLANET_GOVERNOR_ID]}\" class=\"icon_fill o75\">\n            <span class=\"a75 left_bottom\">{planet.PLANET_GOVERNOR_LEVEL}</span>\n          </span>\n      <!-- ENDIF -->\n\n      <span title=\"{L_flt_gather_all}\" class=\"a75 icon-gather-wrapper icon_gather\" go=\"fleet\" mode=\"5\"></span>\n\n      <!-- IF planet.FLEET_OWN -->\n      <img src=\"{D_SN_ROOT_VIRTUAL}design/images/icon_fleet_own.png\" class=\"a75 icon_25per_top_2\" go=\"flying\" onmouseover=\"fleet_dialog_show(this, '{planet.PLANET_FLEET_ID}')\" />\n      <!-- ENDIF -->\n\n      <!-- IF planet.FLEET_ENEMY -->\n      <img src=\"{D_SN_ROOT_VIRTUAL}design/images/icon_warning.png\" class=\"planet_list_moon_attack\" go=\"\" />\n      <!-- ENDIF -->\n\n      <!-- IF planet.MOON_ID -->\n        <!-- IF planet.MOON_ID == PLANET_ID -->\n          <!-- DEFINE $MOON_IMG_SIZE = '66' -->\n          <!-- DEFINE $MOON_SIZE = 1 -->\n        <!-- ELSE -->\n          <!-- DEFINE $MOON_IMG_SIZE = '33' -->\n          <!-- DEFINE $MOON_SIZE = 0 -->\n        <!-- ENDIF -->\n\n        <div class=\"right_top percent{$MOON_IMG_SIZE}\" planet_id=\"{planet.MOON_ID}\">\n          <img src=\"{I_s_[planet.MOON_IMG]}\" class=\"icon_fill\" go=\"\" title=\"{planet.MOON_NAME} [{planet.GALAXY}:{planet.SYSTEM}:{planet.PLANET}]\" />\n\n          <!-- IF planet.MOON_ID == PLANET_ID -->\n          <span title=\"{L_flt_gather_all}\" class=\"a75 icon_25per_right_4\" go=\"fleet\" mode=\"5\"><div class=\"icons icon-gather\"></div></span>\n          <!-- ENDIF -->\n\n          <!-- IF planet.MOON_FILL >= 100 -->\n            <!-- DEFINE $MOON_FILL_BG_STYLE = 'negative_bg' -->\n          <!-- ELSEIF planet.MOON_FILL > 80 -->\n            <!-- DEFINE $MOON_FILL_BG_STYLE = 'neutral_bg' -->\n          <!-- ELSE -->\n            <!-- DEFINE $MOON_FILL_BG_STYLE = 'positive_bg' -->\n          <!-- ENDIF -->\n          <span class=\"planet_list_submoon_fields\"><div class=\"fl planet_list_submoon_filler {$MOON_FILL_BG_STYLE}\" style=\"width: {planet.MOON_FILL}%;\"></div></span>\n\n          <!-- IF planet.MOON_ENEMY != 0 -->\n          <img src=\"{D_SN_ROOT_VIRTUAL}design/images/icon_warning.png\" class=\"planet_list_moon_attack\" go=\"\">\n          <!-- ENDIF -->\n        </div>\n      <!-- ENDIF -->\n    </div>\n\n    <!-- DEFINE $PRODUCTION_PERCENT = '{planet.METAL_PERCENT}' -->\n    <!-- INCLUDE _number_color_production_bg -->\n    <div class=\"{$PRODUCTION_PERCENT_STYLE_BG} res_bar metal_bar\">\n      <div class=\"planet_list_bar metal_bg\" style=\"height: {planet.METAL_PERCENT}%\"></div>\n    </div>\n    <!-- DEFINE $PRODUCTION_PERCENT = '{planet.CRYSTAL_PERCENT}' -->\n    <!-- INCLUDE _number_color_production_bg -->\n    <div class=\"{$PRODUCTION_PERCENT_STYLE_BG} res_bar crystal_bar\">\n      <div class=\"planet_list_bar crystal_bg\" style=\"height: {planet.CRYSTAL_PERCENT}%\"></div>\n    </div>\n    <!-- DEFINE $PRODUCTION_PERCENT = '{planet.DEUTERIUM_PERCENT}' -->\n    <!-- INCLUDE _number_color_production_bg -->\n    <div class=\"{$PRODUCTION_PERCENT_STYLE_BG} res_bar deuterium_bar\">\n      <div class=\"planet_list_bar deuterium_bg\" style=\"height: {planet.DEUTERIUM_PERCENT}%\"></div>\n    </div>\n  </div>\n\n  <div class=\"planet_list_fields\">\n    <!-- DEFINE $PLANET_FILL_PERCENT = '{planet.FILL}' --><!-- INCLUDE _number_color_planet_fill_bg -->\n    <div style=\"width: {planet.FILL}%;\" class=\"{$PLANET_FILL_STYLE_BG} bar\"></div>\n    <div class=\"numbers\">{planet.FIELDS_CUR}&nbsp;/&nbsp;{planet.FIELDS_MAX}</div>\n  </div>\n\n  <!-- IF $OVERVIEW -->\n  <div class=\"bg_header\"><!-- IF planet.IS_CAPITAL -->&#9813; <!-- ENDIF --><!-- IF planet.MOON_ID == PLANET_ID -->&#9789; {planet.MOON_NAME}<!-- ELSE -->{planet.NAME}<!-- ENDIF --><br />[{planet.GALAXY}:{planet.SYSTEM}:{planet.PLANET}]</div>\n  <!-- ENDIF -->\n\n  <div class=\"ov_struc_timer\" id=\"ov_building{$PLANET_PLANET_ID}\">{L_sys_no_task}</div>\n  <!-- IF .planet.building_que -->\n  <div id=\"ov_building{$PLANET_PLANET_ID}_timer\" style=\"color: darkgrey;\"></div>\n\n  <script type='text/javascript'>\n    sn_timers.unshift({id: 'ov_building{$PLANET_PLANET_ID}', type: TIMER_BUILD_QUE_V1, options: { msg_done: '{L_sys_no_task}', que: [\n          <!-- BEGIN building_que -->['{building_que.ID}', '{building_que.NAME} ({building_que.LEVEL})', {building_que.TIME}, '{building_que.AMOUNT}'],<!-- END building_que -->\n        ]}});\n  </script>\n  <!-- ENDIF -->\n\n</div>\n<!-- ENDIF --><!-- END planet -->\n"
  },
  {
    "path": "design/templates/OpenGame/planet_list_overview.tpl.html",
    "content": "<!-- DEFINE $CELL_CLASS = 'tr' -->\n\n<!-- IF .planet > 5 -->\n  <!-- DEFINE $COLUMN_COUNT = 2 -->\n<!-- ENDIF -->\n\n<!-- BEGIN planet -->\n  <!-- DEFINE $PLANET_PLANET_ID = '{planet.ID}' -->\n\n  <!-- IF ! LIST_COLUMN_COUNT || planet.S_ROW_COUNT % LIST_COLUMN_COUNT == 0  -->\n  <!-- ENDIF -->\n<!-- IF planet.ID -->\n\n<!-- IF planet.ID == PLANET_ID || (planet.MOON_ID == PLANET_ID && $OVERVIEW) -->\n  <!-- DEFINE $PLANET_SELECTOR = 'green' -->\n<!-- ELSE -->\n  <!-- DEFINE $PLANET_SELECTOR = 'transparent' -->\n<!-- ENDIF -->\n\n  <div style=\"border: 0.2em dashed {$PLANET_SELECTOR}; margin: 0; padding: 1.0em; border-radius: 1em;\" class=\"planet_info_overview\">\n    <!-- INCLUDE planet_list_info -->\n  </div>\n<!-- ENDIF -->\n  <!-- IF (! LIST_COLUMN_COUNT || (planet.S_ROW_COUNT + 1) % LIST_COLUMN_COUNT == 0 || planet.S_LAST_ROW)  -->\n  <!-- ENDIF -->\n<!-- END planet -->\n"
  },
  {
    "path": "design/templates/OpenGame/planet_manage.tpl.html",
    "content": "<script type=\"text/javascript\" src=\"js/planet_manage.min.js?{C_var_db_update}\"></script>\n\n<br />\n\n<table width=519 style=\"max-width: 33em\">\n  <tr>\n    <th class=\"c_c\" nowrap>\n      <div class=\"contFJ\">\n        <div>\n          <a href=\"galaxy.php?mode=0&galaxy={PLANET_GALAXY}&system={PLANET_SYSTEM}\" class=\"link\">\n            <!-- IF IS_MOON -->&#9789;<!-- ELSEIF IS_CAPITAL -->&#9813;<!-- ENDIF -->\n            [{PLANET_GALAXY}:{PLANET_SYSTEM}:{PLANET_PLANET}]\n            {PLANET_TYPE_TEXT}\n            \"{PLANET_NAME}\"\n          </a>\n        </div>\n        <button onclick=\"sn_redirect('overview.php');return false;\"><span class=\"ok\">{L_ov_return}</span></button>\n      </div>\n    </th>\n  </tr>\n  <tr>\n    <th>\n      <form action=\"overview.php?mode=manage\" method=\"POST\" class=\"contF\">\n        <input type=\"hidden\" name=\"planet_id\" value=\"{PLANET_ID}\">\n\n        <div style=\"min-width: 11em;\">{L_ov_new_name}</div>\n        <input type=\"text\" name=\"new_name\" size=25 maxlength=20 value=\"{PLANET_NAME}\">\n\n        <input type=\"submit\" name=\"rename\" value=\"{L_ov_rename}\">\n      </form>\n    </th>\n  </tr>\n  <!-- IF PLANET_TYPE == 1 -->\n  <tr>\n    <th>\n      <form action=\"overview.php?mode=manage\" method=\"POST\">\n        <input type=\"hidden\" name=\"planet_id\" value=\"{PLANET_ID}\">\n        <div class=\"contF\">\n          <div>\n            <img src=\"{I_icon_help_32}\" class=\"link\" onclick=\"document.location='infos.php?gid={D_UNIT_PLANET_DENSITY}'\" />\n          </div>\n          <div>\n            {L_ov_core_type_current}: \"{PLANET_CORE_TEXT}\"<br />\n              <label for=\"density_type\">{L_ov_core_change_to}</label>\n              <select name=\"density_type\" id=\"density_type\">\n                <!-- BEGIN densities -->\n                <!-- IF PLANET_DENSITY_INDEX == densities.ID -->\n                <!-- DEFINE $DENSITY_SELECTED = 'selected current=\"1\"' -->\n                <!-- ELSE -->\n                <!-- DEFINE $DENSITY_SELECTED = '' -->\n                <!-- ENDIF -->\n                <option value=\"{densities.ID}\" rest=\"{densities.REST}\" html=\"{densities.COST_TEXT}\" html_class=\"{densities.COST_TEXT_CLASS}\" {$DENSITY_SELECTED}>\n                  {densities.TEXT} - {densities.COST_TEXT} {L_sys_dark_matter_sh}\n                </option>\n                <!-- END densities -->\n              </select>\n          </div>\n          <button type=\"submit\" id=\"transmute_button\" name=\"transmute\" value=\"transmute\" disabled=\"disabled\">\n            {L_sys_change}<br />\n            {L_sys_for} <span id=\"transmutation_cost\" class=\"zero\">0</span> {L_sys_dark_matter_sh}\n          </button>\n        </div>\n      </form>\n    </th>\n  </tr>\n  <!-- ENDIF -->\n  <tr>\n    <td class=\"c_c\">\n      <!-- INCLUDE planet_sector_bar -->\n      <!-- INCLUDE planet_sector_buy -->\n    </td>\n  </tr>\n\n  <tr>\n    <th class=\"c_l\" colspan=4>{L_sys_governor}</th>\n  </tr>\n  <tr>\n    <td class=\"c_c\">\n      <table class=\"markup\" width=\"100%\">\n        <tr>\n          <td>\n            <!-- DEFINE $PLANET_ID = '{PLANET_ID}' -->\n            <!-- INCLUDE planet_governor -->\n          </td>\n          <td>\n            <!-- BEGIN governors -->\n            <div class=\"planet-manage-governor-hire-container\">\n              <div class=\"planet-manage-governor-hire-image-container link\" onclick=\"document.location='infos.php?gid={governors.ID}'\">\n                <img src=\"{I_[governors.ID]}\" align=\"top\" class=\"w100\" />\n                <span class=\"posT a75\">{governors.NAME}</span>\n                <span class=\"posB a75\">\n                  {governors.LEVEL}<!-- IF governors.LEVEL_PLUS --><span class=\"zero\"><!-- IF governors.LEVEL_PLUS > 0 -->+<!-- ENDIF -->{governors.LEVEL_PLUS}</span><!-- ENDIF -->\n                  <!-- IF governors.MAX --> / {governors.MAX}<!-- ENDIF -->\n                </span>\n                <span class=\"unit-icon-info-container\">\n                  <div class=\"icon-info-cover\">&nbsp;</div>\n                </span>\n              </div>\n              <br />\n              <a href=\"infos.php?gid={governors.ID}\"></a><br />\n              <!-- IF governors.MAX && governors.LEVEL >= governors.MAX -->\n              <span class=\"negative\">{L_sys_maximum_level}</span>\n              <!-- ELSEIF governors.COST <= DARK_MATTER -->\n              <!-- IF ! PLANET_GOVERNOR_ID || governors.ID == PLANET_GOVERNOR_ID -->\n              <a href=\"overview.php?mode=manage&hire={governors.ID}\" class=\"positive button_pseudo\">{L_mrc_hire_for} {governors.COST} {L_sys_dark_matter_sh}</a>\n              <!-- ELSE -->\n              <a onclick=\"if(confirm('{L_ov_mrc_confirm_1} {PLANET_GOVERNOR_NAME} {L_ov_mrc_confirm_2} {PLANET_GOVERNOR_LEVEL} {L_ov_mrc_confirm_3} {governors.NAME} {L_ov_mrc_confirm_4}'))window.location='overview.php?mode=manage&hire={governors.ID}';return false;\" class=\"positive button_pseudo\">{L_mrc_hire_for} {governors.COST} {L_sys_dark_matter_sh}</a>\n              <!-- ENDIF -->\n              <!-- ELSE -->\n              <span class=\"negative\">{L_mrc_hire_for} {governors.COST} {L_sys_dark_matter_sh}</span>\n              <!-- ENDIF -->\n            </div>\n            <!-- END governors -->\n          </td>\n        </tr>\n      </table>\n    </td>\n  </tr>\n\n\n\n  <tr>\n    <th class=\"c_l\" colspan=4>{L_ov_manage_special}</th>\n  </tr>\n  <!-- IF PLANET_TYPE == 1 -->\n  <!-- IF IS_CAPITAL -->\n  <tr>\n    <th colspan=\"3\">\n      <!-- IF CAN_CAPITAL -->\n      <!-- ELSE -->\n      <!-- DEFINE $CAPITAL_DISABLED = 'disabled=\"disabled\"' -->\n      <!-- ENDIF -->\n      {Эта планета является столицей}\n    </th>\n  </tr>\n  <!-- ELSE -->\n  <tr>\n    <th colspan=\"3\">\n      <!-- IF CAN_CAPITAL -->\n      <!-- ELSE -->\n      <!-- DEFINE $CAPITAL_DISABLED = 'disabled=\"disabled\"' -->\n      <!-- ENDIF -->\n\n      <a id=\"planet_make_capital\"\n         message=\"{L_ov_capital} {PLANET_NAME_JS}?\"\n         href=\"overview.php?mode=manage&action=make_capital&planet_id={PLANET_ID}\"\n         class=\"button_pseudo\" {$CAPITAL_DISABLED}\n      >\n        {L_ov_capital}<br />\n        {L_sys_for} {CAPITAL_COST_TEXT} {L_sys_dark_matter_sh}\n      </a>\n    </th>\n  </tr>\n  <!-- ENDIF -->\n\n  <tr>\n    <td class=\"c_l subheader\" colspan=\"3\">\n      {L_ov_teleport}\n    </td>\n  </tr>\n  <tr>\n    <!-- IF CAN_TELEPORT -->\n    <th nowrap colspan=\"3\">\n      <form action=\"overview.php?mode=manage&action=planet_teleport\" method=\"POST\" id=\"planet_teleport_form\">\n        <input type=\"hidden\" name=\"planet_id\" value=\"{PLANET_ID}\">\n        <!--<div style=\"display: flex; justify-content: space-around; align-content: space-around;\">-->\n        <div class=\"contF\">\n          <div>\n            {L_ov_teleport_new_coordinates}<br/>\n            <input type=\"text\" name=\"new_galaxy\" maxlength=3 value=\"{PLANET_GALAXY}\" class=\"coordinate\"> :\n            <input type=\"text\" name=\"new_system\" maxlength=3 value=\"{PLANET_SYSTEM}\" class=\"coordinate\"> :\n            <input type=\"text\" name=\"new_planet\" maxlength=3 value=\"{PLANET_PLANET}\" class=\"coordinate\">\n          </div>\n\n          <button type=\"submit\" name=\"teleport\" value=\"teleport\">\n            {L_ov_teleport}<br />\n            {L_sys_for} {TELEPORT_COST_TEXT} {L_sys_dark_matter_sh}\n          </button>\n        </div>\n      </form>\n    </th>\n    <!-- ELSE -->\n    <th colspan=\"3\" class=\"error\">\n      {CAN_NOT_TELEPORT_MSG}\n    </th>\n    <!-- ENDIF -->\n  </tr>\n  <!-- ENDIF -->\n\n  <tr>\n    <td class=\"c_l subheader\" colspan=\"3\">\n      {L_colony_abandon}\n    </td>\n  </tr>\n  <tr>\n    <!--<th>{L_colony_abandon}</th>-->\n    <th colspan=\"3\">\n      <form action=\"overview.php?mode=manage&action=planet_abandon\" method=\"POST\" id=\"planet_abandon_form\">\n        <input type=\"hidden\" name=\"planet_id\" value=\"{PLANET_ID}\">\n        <div class=\"contF\">\n          <div>\n            {L_ov_password}<br />\n            <input type=\"password\" name=\"abandon_confirm\" maxlength=\"32\" value=\"\" />\n          </div>\n\n          <input type=\"submit\" name=\"abandon\" value=\"{L_colony_abandon}\" class=\"h100\"/>\n        </div>\n      </form>\n    </th>\n  </tr>\n</table>\n\n<!-- INCLUDE page_hint -->\n"
  },
  {
    "path": "design/templates/OpenGame/planet_overview.tpl.html",
    "content": "<!-- DEFINE $OVERVIEW = true -->\n<script type=\"text/javascript\" src=\"js/sn_ajax_send_fleet.min.js?{C_var_db_update}\"></script>\n<script type=\"text/javascript\">\n  var MT_SPY = {D_MT_SPY};\n  var MT_COLONIZE = {D_MT_COLONIZE};\n  var MT_RECYCLE = {D_MT_RECYCLE};\n  var MT_MISSILE = {D_MT_MISSILE};\n  var uni_galaxy = \"{PLANET_GALAXY}\";\n  var uni_system = \"{PLANET_SYSTEM}\";\n  jQuery.extend(language, {\n    ov_rename: '{LA_ov_rename}',\n    sys_confirm: '{LA_sys_confirm}',\n    sys_cancel: '{LA_sys_cancel}',\n  });\n</script>\n\n<script type=\"text/javascript\" src=\"js/planet_manage.min.js?{C_var_db_update}\"></script>\n\n<div id=\"dialog-rename-planet\" title=\"{L_ov_planet_rename_dialog_title}\" style=\"display: none; background-color: #344566;\">\n  <form action=\"overview.php\" method=\"POST\" id=\"dialog-rename-planet-form\" style=\"margin: 1em; text-align: center;\">\n    <label for=\"planet_new_name\">{L_ov_new_name}</label><br/><br/>\n    <input type=\"text\" name=\"new_name\" id=\"planet_new_name\" size=32 maxlength=32 value=\"{PLANET_NAME}\" />\n    <input type=\"hidden\" name=\"rename\" value=\"1\" />\n    <input type=\"hidden\" name=\"cp\" />\n  </form>\n</div>\n\n<br />\n<h2>\n  <!-- IF IS_MOON -->&#9789;<!-- ELSEIF IS_CAPITAL -->&#9813;<!-- ENDIF -->\n  [{PLANET_GALAXY}:{PLANET_SYSTEM}:{PLANET_PLANET}] {PLANET_TYPE_TEXT} \"{PLANET_NAME}\"\n</h2>\n\n<style type=\"text/css\">\n</style>\n\n\n<div class=\"planet_overview\">\n  <div id=\"planet_overview_double\">\n      <table id=\"planet_info_table\">\n        <tr>\n          <th colspan=5 nowrap class=\"c_c\">\n            <table class=\"markup\" width=\"100%\">\n              <tr>\n                <th class=\"c_l\" nowrap>\n                  <a href=\"galaxy.php?mode=0&galaxy={PLANET_GALAXY}&system={PLANET_SYSTEM}\" class=\"link\">\n                    <!-- IF IS_MOON -->&#9789;<!-- ELSEIF IS_CAPITAL -->&#9813;<!-- ENDIF -->\n                    [{PLANET_GALAXY}:{PLANET_SYSTEM}:{PLANET_PLANET}] {PLANET_TYPE_TEXT} \"{PLANET_NAME}\"\n                  </a>\n                </th>\n                <th class=\"c_r\" nowrap width=\"11em\">\n                  <input type=\"button\" name=\"rename\" id=\"planet_rename\" value=\"{L_ov_rename}\" />\n                </th>\n                <th class=\"c_r\" nowrap width=\"11em\">\n                  <button  go=\"\" mode=\"manage\">\n                    <span class=\"positive\">{L_ov_manage}</span>\n                  </button>\n                </th>\n              </tr>\n            </table>\n          </th>\n        </tr>\n\n        <!-- IF GATE_LEVEL -->\n          <tr>\n            <th class=\"c_c\" colspan=\"2\">{L_tech[D_STRUC_MOON_GATE]}</th>\n            <th class=\"c_c\" colspan=\"3\">\n              <a href=\"jumpgate.php\">\n                <!-- IF GATE_JUMP_REST_TIME -->\n                  {L_ov_gate_time_left} <span  id=\"gate_jump_time\" class=\"error\"></span>\n                  <script type=\"text/javascript\"><!--\n                    sn_timers.unshift({id: 'gate_jump_time', type: TIMER_BUILD_QUE_V1, options: {\n                      msg_done: '{L_gate_ready}',\n                      que: [\n                        ['1', '', {GATE_JUMP_REST_TIME}, '1']\n                      ]\n                    }});\n                  // --></script>\n                <!-- ELSE -->\n                  <span class=\"ok\">{L_gate_ready}</span>\n                <!-- ENDIF -->\n              </a>\n            </th>\n          </tr>\n        <!-- ENDIF -->\n\n        <tr>\n          <td class=\"c_c\" width=\"11em\" rowspan=\"5\">\n            <!-- DEFINE $PLANET_ID = '{PLANET_ID}' -->\n            <!-- DEFINE $IS_PLANET_OVERVIEW = 1 -->\n            <!-- INCLUDE planet_governor -->\n          </td>\n\n          <th colspan=\"4\">\n            <!-- INCLUDE planet_sector_bar -->\n            <!-- INCLUDE planet_sector_buy -->\n          </th>\n        </tr>\n\n        <tr>\n          <th colspan=\"4\" id=\"fleetstatusrow\">{L_orb}: {L_sys_metal}: {metal_debris} / {L_sys_crystal}: {crystal_debris}\n            <!-- IF PLANET_RECYCLERS -->\n              <br><button id=\"ov_recycle\" style=\"cursor: pointer\" onclick=\"doit(MT_RECYCLE, {PLANET_PLANET}, {PLANET_TYPE});\"<!-- IF ! PLANET_DEBRIS --> disabled=\"disabled\"<!-- ENDIF -->><span>[ {L_type_mission[8]} ]</span></button>\n              <table id=\"fleetstatustable\" class=\"no_border_image\" cellspacing=\"0\" cellpadding=\"0\"></table>\n            <!-- ENDIF -->\n          </th>\n        </tr>\n        <tr>\n          <th colspan=\"2\">{L_Diameter}&nbsp;{planet_diameter}&nbsp;{L_km}</th>\n          <th colspan=\"2\">\n            {L_sys_planet_density}:&nbsp;{planet_density}&nbsp;{L_sys_planet_density_units}\n          </th>\n        </tr>\n        <tr>\n          <th colspan=\"4\">{L_Temperature}&nbsp;({L_min_avg_max}):&nbsp;{planet_temp_min}&deg;C&nbsp;/&nbsp;{planet_temp_avg}&deg;C&nbsp;/&nbsp;{planet_temp_max}&deg;C</th>\n        </tr>\n\n        <!-- IF PLANET_TYPE == 1 -->\n        <tr>\n          <th colspan=\"4\" nowrap>\n            <form action=\"overview.php\" method=\"POST\">\n              <input type=\"hidden\" name=\"planet_id\" value=\"{PLANET_ID}\">\n\n              <div class=\"contF\">\n              <div>\n                <img src=\"{I_icon_help_32}\" class=\"link\" onclick=\"document.location='infos.php?gid={D_UNIT_PLANET_DENSITY}'\" />\n              </div>\n              <!--<div class=\"icons icon-info link\" style=\"display: inline-block; vertical-align: middle;\" onclick=\"document.location='infos.php?gid={D_UNIT_PLANET_DENSITY}'\"></div> {L_ov_core_type_current} \"{PLANET_CORE_TEXT}\"<br/>-->\n                <div>\n                  {L_ov_core_type_current}: \"{PLANET_CORE_TEXT}\"<br />\n                <label for=\"density_type\">{L_ov_core_change_to}</label>\n                <select name=\"density_type\" id=\"density_type\">\n                  <!-- BEGIN densities -->\n                  <option value=\"{densities.ID}\" rest=\"{densities.REST}\" html=\"{densities.COST_TEXT}\" html_class=\"{densities.COST_TEXT_CLASS}\"<!-- IF PLANET_DENSITY_INDEX == densities.ID --> selected current=\"1\"<!-- ENDIF -->>{densities.TEXT}</option>\n                  <!-- END densities -->\n                </select>\n              </div>\n              <div style=\"display: inline-block; margin-left: 10px;\">\n                <button type=\"submit\" id=\"transmute_button\" name=\"transmute\" value=\"transmute\" disabled=\"disabled\">\n                  {L_sys_change}\n                  {L_sys_for} <span id=\"transmutation_cost\" class=\"zero\">0</span> {L_sys_dark_matter_sh}\n                </button>\n\n              </div>\n              </div>\n            </form>\n          </th>\n        </tr>\n        <!-- ENDIF -->\n\n        <tr><td colspan=5 class=\"c\">{L_Planet_menu}</td></tr>\n        <!-- BEGIN ques -->\n          <tr class=\"c_l\">\n            <td class=\"c_c\" width=\"9em\" height=33%>\n              <!-- IF ques.LENGTH -->\n                <!-- DEFINE $QUE_ID = '{ques.ID}' -->\n                <div class=\"bld_que_container {$ECO_QUE_VERTICAL}\">\n                  <!-- INCLUDE que_total -->\n                </div>\n\n                <!-- DEFINE $QUE_ID = '{ques.ID}' -->\n                <!-- INCLUDE eco_queue -->\n              <!-- ELSE -->\n                {ques.NAME}<br>\n              <!-- ENDIF -->\n            </td>\n            <td class=\"c_l\" colspan=\"4\" id=\"ov_{ques.ID}_que\">\n              {L_eco_que_empty}\n            </td>\n          </tr>\n        <!-- END ques -->\n\n        <tr><td colspan=5 class=\"c\">{L_sys_resources}</td></tr>\n        <tr><td colspan=\"5\" align=\"center\">\n        <!-- INCLUDE _page/navbar_resources -->\n        </td></tr>\n      </table>\n    <!-- INCLUDE planet_fleet_list -->\n  </div>\n\n  <div class=\"planet_overview_list\">\n    <!-- INCLUDE planet_list_overview -->\n  </div>\n</div>\n\n<div id=\"admin_message\"></div>\n"
  },
  {
    "path": "design/templates/OpenGame/planet_sector_bar.tpl.html",
    "content": "<div class=\"planet-manage-sectors-container\">\n  <!-- IF PLANET_FILL <= 50 -->\n  <!-- DEFINE $BAR_COLOR = 'ok_bg' -->\n  <!-- ELSEIF PLANET_FILL <= 75 -->\n  <!-- DEFINE $BAR_COLOR = 'notice_bg' -->\n  <!-- ELSEIF PLANET_FILL <= 90 -->\n  <!-- DEFINE $BAR_COLOR = 'warning_bg' -->\n  <!-- ELSE -->\n  <!-- DEFINE $BAR_COLOR = 'error_bg' -->\n  <!-- ENDIF -->\n  <div class=\"posLT h100 {$BAR_COLOR}\" style=\"width: {PLANET_FILL_BAR}%;\">&nbsp;</div>\n  <div class=\"planet-manage-sectors-text\">{L_buildings_on_planet} {planet_field_current}/{planet_field_max} ({PLANET_FILL}%)</div>\n</div>\n"
  },
  {
    "path": "design/templates/OpenGame/planet_sector_buy.tpl.html",
    "content": "<!-- IF PLANET_TYPE == 1 -->\n<!-- IF SECTOR_CAN_BUY -->\n<script type=\"text/javascript\">\n  jQuery.extend(language, {\n    sys_confirm: '{LA_sys_confirm}',\n    sys_cancel: '{LA_sys_cancel}',\n  });\n//  console.log(language);\n</script>\n<script type=\"text/javascript\">\n  jQuery(document).on('click', \".planet_sector_buy\", function () {\n    $('#dialog-sector-buy').dialog('open');\n  });\n\n  $(function() {\n    $( \"#dialog-sector-buy\" ).dialog({\n      autoOpen: false,\n      resizable: false,\n      // height: 100,\n      // width: 330,\n      modal: true,\n      open: function() {\n        var element = $(this).parent();\n        var selected_planet = $('.js_navbar_planet_select').find('option:selected');\n        element.find('.ui-dialog-titlebar').css('display', 'block');\n        element.find('.ui-dialog-buttonpane button:last').focus();\n        element.find('input[name=cp]').val(selected_planet.val());\n        $('#dialog-sector-buy-planet-name').text(selected_planet.text());\n      },\n      buttons: [\n        {\n          text: language['sys_confirm'],\n          class: \"ok\",\n          click: function () {\n            var element = $(this).parent();\n            // Отключаем все кнопки кроме крестика закрытия в тайтл-баре\n            element.find(':button:not(.ui-dialog-titlebar-close)').button('disable');\n            element.find(\"form\").submit();\n            // $( this ).dialog( \"close\" );\n          }\n        },\n        {\n          text: language['sys_cancel'],\n          click: function() {\n            $(this).dialog(\"close\");\n          }\n        }\n      ]\n    });\n  });\n\n</script>\n\n<div id=\"dialog-sector-buy\" title=\"{L_sys_purchase_confirm}\">\n  <form action=\"\" method=\"POST\" id=\"dialog-sector-buy-form\">\n    <div>{L_sys_buy_doing} 1 {L_sys_planet_sector} {L_sys_for} {SECTOR_COST_TEXT} {L_sys_dark_matter_sh} {L_sys_planet_on}</div>\n    <div id=\"dialog-sector-buy-planet-name\"></div>\n    <!--<input type=\"hidden\" name=\"mode\" value=\"{D_QUE_STRUCTURES}\" />-->\n    <input type=\"hidden\" name=\"sector_buy\" value=\"1\" />\n    <input type=\"hidden\" name=\"cp\" />\n  </form>\n</div>\n\n<span class=\"positive button_pseudo planet_sector_buy\">{L_sys_sector_buy} {L_sys_for} {SECTOR_COST_TEXT} {L_sys_dark_matter_sh}</span>\n<!-- ELSE -->\n<span class=\"negative\">{L_sys_sector_buy} {L_sys_for} {SECTOR_COST_TEXT} {L_sys_dark_matter_sh}\n<!-- ENDIF -->\n<!-- ELSE -->\n&nbsp;\n<!-- ENDIF -->\n"
  },
  {
    "path": "design/templates/OpenGame/que_total.tpl.html",
    "content": "<div class=\"bld_que_total\">\n  <div class=\"bld_que_total_time_text ov_{$QUE_ID}_hide_on_complete\">{L_que_slot_length_long}&nbsp;<span id=\"ov_{$QUE_ID}_slots\"></span></div>\n  <div id=\"ov_{$QUE_ID}_finish\" class=\"bld_que_total_finish ov_{$QUE_ID}_hide_on_complete\" style=\"color: yellow\"></div>\n  <div class=\"bld_que_total_bar ov_{$QUE_ID}_hide_on_complete\">\n    <div id=\"ov_{$QUE_ID}_progress_bar\" class=\"bld_que_total_progress_bar\"></div>\n    <div id=\"ov_{$QUE_ID}_total\" class=\"bld_que_total_timer\"></div>\n  </div>\n  <div id=\"ov_{$QUE_ID}\" class=\"bld_que_total_item\"></div>\n</div>\n"
  },
  {
    "path": "design/templates/OpenGame/quest.tpl.html",
    "content": "<h1>{L_qst_quests}</h1>\n<h3>{L_qst_total}:&nbsp;{TOTAL}</h3>\n<!-- IF IN_ADMIN && ! USER_ID -->\n<!-- INCLUDE quest_edit_form -->\n<br>\n<!-- ENDIF -->\n\n<div class=\"quest_list border_image_large\">\n  <div class=\"quest_filter_wrapper\">\n    <label for=\"filterQuestStatus\">{L_qst_filter_by_status}</label>\n    <select name=\"filterQuestStatus\" id=\"filterQuestStatus\">\n      <!-- BEGIN status -->\n      <!-- IF status.ID == PLAYER_OPTION_QUEST_LIST_FILTER -->\n      <!-- DEFINE $STATUS_SELECTED = 'selected' -->\n      <!-- ELSE -->\n      <!-- DEFINE $STATUS_SELECTED = '' -->\n      <!-- ENDIF -->\n      <option value=\"{status.ID}\" {$STATUS_SELECTED}>{status.NAME}</option>\n      <!-- END status -->\n    </select>\n  </div>\n\n  <!-- BEGIN quest -->\n  <!-- IF quest.QUEST_STATUS == QUEST_STATUS_COMPLETE -->\n  <!-- DEFINE $QUEST_CLASS = 'success' -->\n  <!-- ELSEIF quest.QUEST_STATUS == QUEST_STATUS_STARTED -->\n  <!-- DEFINE $QUEST_CLASS = 'notice' -->\n  <!-- ELSE -->\n  <!-- DEFINE $QUEST_CLASS = 'warning' -->\n  <!-- ENDIF -->\n\n  <div class=\"quest_admin_wrapper border_image_small\">\n    <div class=\"quest_wrapper\">\n      <div class=\"quest_header contFJ header {$QUEST_CLASS}\">\n        <div class=\"quest_name\">\n          {quest.QUEST_NAME}\n        </div>\n\n        <div class=\"quest_status\">\n          {quest.QUEST_STATUS_NAME}\n        </div>\n      </div>\n\n      <div class=\"quest_description cell\">\n        {quest.QUEST_DESCRIPTION}\n      </div>\n\n      <div class=\"quest_details contFJ\">\n        <div class=\"subheader\">\n          <ul>\n            {L_qst_conditions}\n            <li class=\"info\">{quest.QUEST_UNIT_AMOUNT|num|format}&nbsp;x&nbsp;{quest.QUEST_UNIT_NAME}</li>\n          </ul>\n        </div>\n        <div class=\"subheader\">\n          <ul>\n            {L_qst_rewards}\n            <!-- BEGIN quest_rewards_list -->\n            <li class=\"ok\">{quest_rewards_list.AMOUNT|num|format}&nbsp;x&nbsp;{quest_rewards_list.NAME}</li>\n            <!-- END quest_rewards_list -->\n          </ul>\n        </div>\n      </div>\n    </div>\n\n    <!-- IF IN_ADMIN && ! USER_ID -->\n    <div class=\"quest_admin_buttons\">\n      <a class=\"button_pseudo\" href=\"admin/adm_quest.php?mode=edit&id={quest.QUEST_ID}\">\n        <img src=\"design/images/icon_edit.png\">\n      </a>\n      <a class=\"button_pseudo\" href=\"admin/adm_quest.php?mode=copy&id={quest.QUEST_ID}\">\n        <img src=\"design/images/icon_copy.gif\">\n      </a>\n      <a class=\"button_pseudo\" href=\"admin/adm_quest.php?mode=del&id={quest.QUEST_ID}\">\n        <img src=\"design/images/r1.png\">\n      </a>\n    </div>\n    <!-- ENDIF -->\n  </div>\n  <!-- END quest -->\n</div>\n"
  },
  {
    "path": "design/templates/OpenGame/quest_edit_form.tpl.html",
    "content": "<table width=\"519\">\n  <form action=\"admin/adm_quest.php\" method=\"post\" name=\"fForm\">\n    <input type=\"hidden\" name=\"mode\" value=\"{mode}\">\n    <input type=\"hidden\" name=\"id\" value=\"{QUEST_ID}\">\n\n    <tr>\n      <td class=\"c\" colspan=2><!-- IF mode == 'edit' -->{L_qst_edit}<!-- ELSEIF mode == 'copy' -->{L_qst_copy}\n        <!-- ELSE -->{L_qst_add}<!-- ENDIF --></td>\n    </tr>\n    <tr>\n      <td>{L_qst_name}</td>\n      <td>\n        <input class=\"fl\" name=\"QUEST_NAME\" size=\"50\" maxlength=\"255\" value=\"{QUEST_NAME}\">\n        <span class=\"fr\"><!-- IF mode == 'edit' -->{L_qst_mode_edit}<!-- ELSEIF mode == 'copy' -->{L_qst_mode_copy}\n          <!-- ELSE -->{L_qst_mode_add}<!-- ENDIF --></span>\n      </td>\n    </tr>\n\n    <tr>\n      <td valign=\"top\">{L_qst_description}</td>\n      <td><textarea name=\"QUEST_DESCRIPTION\" cols=40 rows=5 width=\"80%\">{QUEST_DESCRIPTION}</textarea></td>\n    </tr>\n    <tr class=\"c_l\">\n      <td>{L_qst_adm_conditions}</td>\n      <td class=\"c_l\">\n        <input name=\"QUEST_UNIT_AMOUNT\" size=\"15\" maxlength=\"15\" value=\"{QUEST_UNIT_AMOUNT}\"> x\n        <select name=\"QUEST_UNIT_ID\">\n          <!-- BEGIN allowed_unit -->\n          <option value=\"{allowed_unit.ID}\"<!-- IF allowed_unit.ID == QUEST_UNIT_ID --> selected<!-- ENDIF -->\n          >{allowed_unit.NAME}</option>\n          <!-- END allowed_unit -->\n        </select>\n      </td>\n    </tr>\n    <tr class=\"c_l\">\n      <td>{L_qst_rewards}</td>\n      <td>\n        <!-- BEGIN quest_rewards_list -->\n        <input name=\"QUEST_REWARDS_LIST[{quest_rewards_list.ID}]\" size=\"15\" maxlength=\"15\"\n               value=\"{quest_rewards_list.AMOUNT}\"> {quest_rewards_list.NAME}<br/>\n        <!-- END quest_rewards_list -->\n      </td>\n      <!-- IF false -->\n      <td>\n        <input name=\"QUEST_REWARDS_AMOUNT\" size=\"15\" maxlength=\"15\" value=\"{QUEST_REWARDS_AMOUNT}\"> x\n        <select name=\"QUEST_REWARDS_ID\">\n          <!-- BEGIN allowed_reward -->\n          <option value=\"{allowed_reward.ID}\"<!-- IF allowed_reward.ID == QUEST_REWARDS_ID --> selected<!-- ENDIF -->\n          >{allowed_reward.NAME}</option>\n          <!-- END allowed_reward -->\n        </select>\n      </td>\n      <!-- ENDIF -->\n    </tr>\n    <tr>\n      <td colspan=\"2\" class=\"c_c\">\n        <input type=\"submit\" name=\"confirm\"\n               value=\"<!-- IF mode == 'edit' -->{L_qst_edit}<!-- ELSEIF mode == 'copy' -->{L_qst_copy}<!-- ELSE -->{L_qst_add}<!-- ENDIF -->\">\n      </td>\n    </tr>\n  </form>\n</table>\n"
  },
  {
    "path": "design/templates/OpenGame/rank_list.tpl.html",
    "content": "<h1>{PAGE_HEADER}</h1>\n\n<div class=\"rank-table contFC flexW border_image_large\">\n  <!-- BEGIN player_rank -->\n  <!-- IF player_rank.SELECTED -->\n  <!-- DEFINE $RANK_SELECTED = 'rank-selected' -->\n  <!-- ELSE -->\n  <!-- DEFINE $RANK_SELECTED = 'rank-selected-not' -->\n  <!-- ENDIF -->\n  <div class=\"cell {$RANK_SELECTED}\">\n    <div class=\"rank rank-{player_rank.ID}\">\n    </div>\n    [{player_rank.ID}] {player_rank.NAME}\n  </div>\n  <!-- END player_rank -->\n</div>\n"
  },
  {
    "path": "design/templates/OpenGame/records.tpl.html",
    "content": "<h1>{L_rec_title}</h1>\n<table width=\"590\">\n<!-- BEGIN records -->\n  <!-- IF records.HEADER -->\n    <tr>\n      <th class=\"c_l\">{records.UNIT}</th>\n      <th class=\"c_c\">{L_sys_player}</th>\n      <th class=\"c_r\">{records.COUNT}</th>\n    </tr>\n  <!-- ELSE -->\n  <tr>\n    <td class=\"c_l\">{records.UNIT}</td>\n    <td class=\"c_c\">{records.USER}</td>\n    <td class=\"c_r\">{records.COUNT}</td>\n  </tr>\n  <!-- ENDIF -->\n<!-- END records -->\n</table>\n"
  },
  {
    "path": "design/templates/OpenGame/registry_form.tpl.html",
    "content": "<style type=\"text/css\"><!--\n @import url(./design/css/login.css);\n--></style>\n\n<div id=\"log_skipper\"></div>\n\n<div id=\"log_main\">\n  <div id=\"log_title\">{L_log_reg} - {L_sys_universe} \"{C_game_name}\"</div>\n\n  <!-- INCLUDE login_header -->\n\n  <div id=\"log_description\">{L_log_reg_text0} <!-- IF URL_RULES --><a href=\"{URL_RULES}\" style=\"color: red; text-decoration: underline; font-face: bold;\"><!-- ENDIF -->{L_reg_with_rules}<!-- IF URL_RULES --></a><!-- ENDIF -->. {L_log_reg_text1}</div>\n\n  <form name=\"registerForm\" method=\"POST\" action=\"reg.php{LANG}{referral}\" onsubmit=\"changeAction('register');\" >\n    <input type=\"hidden\" name=\"id_ref\" value=\"{id_ref}\">\n    <table width=\"340\" align=\"center\">\n      <tbody>\n        <tr>\n          <th width=\"179\">{L_User_name}</th>\n          <th width=\"161\"><input name=\"username\" type=\"text\" size=\"20\" maxlength=\"32\" /></th>\n        </tr>\n\n        <tr>\n          <th>{L_sys_password}:</th>\n          <th><input name=\"password\" type=\"password\" size=\"20\" maxlength=\"32\" /></th>\n        </tr>\n\n        <tr>\n          <th>{L_E-Mail}:</th>\n          <th><input name=\"email\" type=\"text\" size=\"20\" maxlength=\"64\" /></th>\n        </tr>\n\n        <tr>\n          <th>{L_MainPlanet}:</th>\n          <th><input name=\"planet_name\" type=\"text\" size=\"20\" maxlength=\"20\" /></th>\n        </tr>\n\n        <tr>\n          <th>{L_Sex}:</th>\n  \n          <th>\n            <select name=\"gender\">\n              <option value=\"M\" selected=\"selected\">{L_Male}</option>\n              <option value=\"F\">{L_Female}</option>\n            </select>\n          </th>\n        </tr>\n\n        <!--\n        <tr>\n          <th>{L_Languese}:</th>\n          <th>\n            <select name=\"language\">\n              <option value=\"ru\" selected=\"selected\">{ru}</option>\n            </select>\n          </th>\n        </tr>\n\n        <tr>\n          <th><img src=\"captcha.php\" /></th>\n          <th><input name=\"captcha\" type=\"text\" size=\"20\" maxlength=\"20\" /></th>\n        </tr>\n        -->\n        <tr>\n          <th colspan=2>\n            <input type=\"hidden\" name=\"language\" value=\"ru\">\n            <input name=\"register\" type=\"checkbox\" value=\"1\" /> {L_reg_i_agree} <!-- IF URL_RULES --><a href=\"{URL_RULES}\" style=\"color: red; text-decoration: underline; font-face: bold;\"><!-- ENDIF -->{L_reg_with_rules}<!-- IF URL_RULES --></a><!-- ENDIF -->\n          </th>\n        </tr>\n      </tbody>\n    </table>\n    <input name=\"register\" type=\"submit\" value=\"{L_signup}!\" />\n  </form><br>\n  {L_log_reg_already} <a href=\"login.php{LANG}{referral}\" class=\"link\">{L_log_login_page}</a><br>\n  {L_log_reg_already_lost} <a href=\"lostpassword.php{LANG}{referral}\" class=\"link\">{L_PasswordLost}</a><br>\n  <br>\n\n  <!-- INCLUDE login_menu -->\n</div>\n"
  },
  {
    "path": "design/templates/OpenGame/resources.tpl.html",
    "content": "<script type=\"text/javascript\"><!--\nvar production = [];\n<!-- BEGIN production -->\n  <!-- IF production.ID && production.LEVEL -->\n    production[Math.intVal('{production.ID}')] = [Math.floorVal('{production.LEVEL}')];\n  <!-- ENDIF -->\n<!-- END production -->\n\nfunction res_set_all(obj)\n{\n  for(production_id in production)\n  {\n    document.getElementById('production' + production_id).value = obj.value;\n  }\n}\n--></script>\n\n<br>\n  <table width=\"569\"><tbody>\n    <tr><td class=\"c\" colspan=\"6\">{L_res_planet_production} \"{PLANET_NAME}\"</td></tr>\n    <tr>\n      <th colspan=\"6\" style=\"position: relative; border: 1px solid rgb(153, 153, 255); padding: 0px;\">\n      <div style=\"position: relative; width: 100%;\">\n        <!-- IF PRODUCTION_LEVEL == 100 -->\n          <!-- DEFINE $BAR_COLOR = '#00C000' -->\n        <!-- ELSE -->\n          <!-- DEFINE $BAR_COLOR = '#C00000' -->\n        <!-- ENDIF -->\n        <div style=\"position: absolute; top: 0px; left: 0px; width: {PRODUCTION_LEVEL}%; background-color: {$BAR_COLOR};\">&nbsp;</div>\n        <div style=\"position: absolute; top: 0px; left: 0px; width: 100%;\">{PRODUCTION_LEVEL}%</div>\n        &nbsp;\n      </div>\n      </th>\n    </tr>\n\n    <!-- IF PLANET_TYPE == 1 -->\n    <tr>\n      <th colspan=\"6\">\n        <form method=\"POST\">\n        <div class=\"icons icon-info link\" style=\"display: inline-block; vertical-align: middle;\" onclick=\"document.location='infos.php?gid={D_UNIT_PLANET_DENSITY}'\"></div>\n        {L_ov_core_type_current}: \"{PLANET_CORE_TEXT}\"<br/>\n        <label for=\"density_type\">{L_ov_core_change_to}</label>\n        <select name=\"density_type\" id=\"density_type\">\n          <!-- BEGIN densities -->\n          <option value=\"{densities.ID}\" rest=\"{densities.REST}\" html=\"{densities.COST_TEXT}\" html_class=\"{densities.COST_TEXT_CLASS}\"<!-- IF PLANET_DENSITY_INDEX == densities.ID --> selected current=\"1\"<!-- ENDIF -->>{densities.TEXT}</option>\n          <!-- END densities -->\n        </select>\n        <input type=\"submit\" id=\"transmute_button\" name=\"transmute\" value=\"{L_sys_change}\" disabled=\"1\"/> {L_sys_for} <span id=\"transmutation_cost\" class=\"positive\">0</span> {L_sys_dark_matter_sh}\n        </form>\n\n        <script type=\"text/javascript\">\n          jQuery(\"#density_type\").change(function(){\n            selected = jQuery(\"#density_type option:selected\");\n            $(\"#transmutation_cost\").html(selected.attr(\"html\")).removeClass().addClass(selected.attr(\"html_class\"));\n            $(\"#transmute_button\").button({disabled: parseInt(selected.attr(\"rest\")) <= 0 || selected.attr(\"current\") == '1'});\n          });\n        </script>\n      </th>\n    </tr>\n    <!-- ENDIF -->\n\n<form action=\"\" method=\"post\">\n    <tr align=center>\n      <td class=\"c\">&nbsp;</td>\n      <td class=\"c\" width=\"60\">{L_sys_metal}</td>\n      <td class=\"c\" width=\"60\">{L_sys_crystal}</td>\n      <td class=\"c\" width=\"60\">{L_sys_deuterium}</td>\n      <td class=\"c\" width=\"60\">{L_sys_energy}</td>\n      <td class=\"c\">\n        <select size=\"1\" onChange=\"res_set_all(this)\" id=\"res_set_all()\">\n          <option>-</option>\n          <!-- BEGIN !option -->\n            <option value=\"{option.VALUE}\">{option.VALUE}%</option>\n          <!-- END option -->\n        </select>\n      </td>\n    </tr>\n    <!-- BEGIN production -->\n      <tr>\n        <th height=\"22\">{production.TYPE}<!-- IF production.LEVEL --> (<!-- IF production.LEVEL_TYPE -->{production.LEVEL_TYPE}&nbsp;<!-- ENDIF -->{production.LEVEL}<!-- IF production.LEVEL_BONUS > 0 --><span class=\"bonus\">+{production.LEVEL_BONUS}</span><!-- ENDIF -->)<!-- ENDIF --></th>\n        <th>\n          {production.METAL_TYPE|num|color}\n          <!-- IF production.METAL_FULL != '' && production.METAL_FULL != production.METAL_TYPE --><br />({production.METAL_FULL|num|color})<!-- ENDIF -->\n        </th>\n        <th>\n          {production.CRYSTAL_TYPE|num|color}\n          <!-- IF production.CRYSTAL_FULL != '' && production.CRYSTAL_FULL != production.CRYSTAL_TYPE --><br />({production.CRYSTAL_FULL|num|color})<!-- ENDIF -->\n        </th>\n        <th>\n          {production.DEUTERIUM_TYPE|num|color}\n          <!-- IF production.DEUTERIUM_FULL != '' && production.DEUTERIUM_FULL != production.DEUTERIUM_TYPE --><br />({production.DEUTERIUM_FULL|num|color})<!-- ENDIF -->\n        </th>\n        <th>\n          {production.ENERGY_TYPE|num|color}\n          <!-- IF production.ENERGY_FULL != '' && production.ENERGY_FULL != production.ENERGY_TYPE --><br />({production.ENERGY_FULL|num|color})<!-- ENDIF -->\n        </th>\n        <th>\n          <!-- IF production.LEVEL && production.P_MINING_IS_MANAGED -->\n            <select name=\"production[{production.ID}]\" id=\"production{production.ID}\" size=\"1\">\n              <!-- BEGIN !option -->\n                <option value=\"{option.VALUE}\"<!-- IF option.VALUE == production.PERCENT --> selected<!-- ENDIF -->>{option.VALUE}%</option>\n              <!-- END option -->\n            </select>\n          <!-- ELSE -->\n            &nbsp;\n          <!-- ENDIF -->\n        </th>\n      </tr>\n    <!-- END production -->\n    <tr><th class=\"c\" colspan=\"6\"><input value=\"{L_res_calculate}\" type=\"submit\"></th></tr>\n  </tbody></table>\n</form>\n\n<table width=\"569\"><tbody>\n  <tr><td class=\"c\" colspan=\"4\">{Widespread_production}</td></tr>\n  <tr>\n    <th width=\"100\">&nbsp;</th>\n    <th>{L_res_hourly}</th>\n    <th>{L_res_daily}</th>\n    <th>{L_res_weekly}</th>\n    <th>{L_res_monthly}</th>\n  </tr>\n  <!-- BEGIN resources -->\n    <tr>\n      <th>{resources.NAME}</th>\n      <th><div align=\"right\">{resources.HOURLY|num|color}</div></th>\n      <th><div align=\"right\">{resources.DAILY|num|color}</div></th>\n      <th><div align=\"right\">{resources.WEEKLY|num|color}</div></th>\n      <th><div align=\"right\">{resources.MONTHLY|num|color}</div></th>\n    </tr>\n  <!-- END resources -->\n</tbody></table>\n<br>\n\n<table width=\"569\"><tbody>\n  <tr><td class=\"c\" colspan=\"2\">{L_res_storage_fill}</td></tr>\n  <!-- BEGIN resources -->\n    <tr>\n      <th>{resources.NAME}</th>\n      <th width=\"469\" style=\"position: relative; border: 1px solid rgb(153, 153, 255); padding: 0px; height: 100%;\">\n      <div style=\"position: relative; width: 100%; height: 100%;\">\n        <!-- IF resources.STORAGE > 100 -->\n          <!-- DEFINE $BAR_COLOR = '#C00000' -->\n        <!-- ELSEIF resources.STORAGE > 80 -->\n          <!-- DEFINE $BAR_COLOR = '#C0C000' -->\n        <!-- ELSE -->\n          <!-- DEFINE $BAR_COLOR = '#00C000' -->\n        <!-- ENDIF -->\n        <div style=\"position: absolute; top: 0px; left: 0px; height: 100%; width: {resources.BAR}%; background-color: {$BAR_COLOR};\">&nbsp;</div>\n        <div style=\"position: absolute; top: 0px; left: 0px; height: 100%; width: 100%; vertical-align: middle;\">{resources.STORAGE}%</div>\n        &nbsp;\n      </div>\n      </th>\n    </tr>\n  <!-- END resources -->\n</tbody></table>\n\n<!-- INCLUDE page_hint -->\n"
  },
  {
    "path": "design/templates/OpenGame/search.tpl.html",
    "content": "<form action=\"search.php\" method=\"post\">\n  <table width=\"519\">\n    <tr>\n      <th class=\"c_c\">{L_srch_title}</th>\n    </tr>\n    <tr>\n      <td class=\"c_c\">\n        <select name=\"type\">\n          <!-- BEGIN type -->\n            <option value=\"{type.ID}\"<!-- IF type.SELECTED --> selected<!-- ENDIF -->>{type.TEXT}</option>\n          <!-- END type -->\n        </select>\n        <input type=\"text\" name=\"searchtext\" value=\"{TEXT}\"/>\n        <input type=\"submit\" value=\"{L_srch_search_do}\" />\n      </td>\n    </tr>\n  </table>\n</form>\n\n<table width=\"519\">\n<!-- IF ! .search_result -->\n  <tr>\n    <th class=\"c_c\">\n      {L_srch_result_none}\n    </th>\n  </tr>\n  <!-- ELSEIF IS_ALLY -->\n    <tr class=\"c_l\">\n      <th>{L_sys_alliance}</th>\n      <th>{L_srch_ally_name}</th>\n      <th>{L_srch_ally_members}</th>\n      <th>{L_srch_rank}</th>\n    </tr>\n    <!-- BEGIN search_result -->\n      <tr class=\"c_l\">\n        <td><a href=\"alliance.php?mode=ainfo&tag={search_result.ALLY_TAG}\">[{search_result.ALLY_TAG}]</a></td>\n        <td>{search_result.ALLY_NAME}</td>\n        <td class=\"c_r\">{search_result.ALLY_MEMBERS}</td>\n        <td class=\"c_r\">\n          <!-- IF search_result.PLAYER_RANK_RAW --><a href=\"stat.php?who=2&range={search_result.ALLY_RANK_RAW}#{search_result.ALLY_RANK_RAW}\">{search_result.ALLY_RANK}</a><!-- ELSE -->-<!-- ENDIF -->\n        </td>\n      </tr>\n    <!-- END search_result -->\n  <!-- ELSE -->\n    <tr class=\"c_l\">\n      <th>\n        <img src=\"{I_menu_empire_emperor}\" border=\"0\" alt=\"{L_stat_details}\" />\n        <!-- IF ! STATS_HIDE_PM_LINK --><img src=\"design/images/icon_mail.gif\" alt=\"{L_srch_action_pm}\" title=\"{L_srch_action_pm}\" /><!-- ENDIF -->\n        <img src=\"{I_icon_buddy}\" alt=\"{L_srch_action_buddy}\" title=\"{L_srch_action_buddy}\" border=\"0\" />\n        {L_srch_player_name}\n      </th>\n      <th>{L_sys_alliance}</th>\n      <th>{L_sys_planet}</th>\n      <th>{L_srch_rank}</th>\n    </tr>\n    <!-- BEGIN search_result -->\n      <tr class=\"c_l\">\n        <td>\n          <a href=\"index.php?page=imperator&int_user_id={search_result.PLAYER_ID}\">\n            <img src=\"{I_menu_empire_emperor}\" border=\"0\" alt=\"{L_stat_details}\" />\n          </a>\n          <!-- IF ! STATS_HIDE_PM_LINK --><a href=\"messages.php?mode=write&id={search_result.PLAYER_ID}\"><img src=\"design/images/icon_mail.gif\" alt=\"{L_srch_action_pm}\" title=\"{L_srch_action_pm}\" /></a><!-- ENDIF -->\n          <a href=\"buddy.php?request_user_id={search_result.PLAYER_ID}\" alt=\"{L_srch_action_buddy}\"><img src=\"{I_icon_buddy}\" alt=\"{L_srch_action_buddy}\" title=\"{L_srch_action_buddy}\" border=\"0\" /></a>\n          {search_result.PLAYER_NAME}\n          <!-- IF search_result.PLAYER_NAME != search_result.PLAYER_NAME_OLD -->\n            ({L_srch_aka} <span class=\"warning\">{search_result.PLAYER_NAME_OLD}</span>)\n          <!-- ENDIF -->\n        </td>\n        <td><!-- IF search_result.ALLY_TAG --><a href=\"alliance.php?mode=ainfo&tag={search_result.ALLY_TAG}\">[{search_result.ALLY_TAG}]</a><!-- ELSE -->&nbsp;<!-- ENDIF --></td>\n        <td>\n          {search_result.PLANET_TYPE}\n          {search_result.PLANET_NAME}\n          <a class=\"fr\" href=\"galaxy.php?mode=3&galaxy={search_result.PLANET_GALAXY}&system={search_result.PLANET_SYSTEM}\">[{search_result.PLANET_GALAXY}:{search_result.PLANET_SYSTEM}:{search_result.PLANET_PLANET}]</a>\n        </td>\n        <td class=\"c_r\">\n          <!-- IF search_result.PLAYER_RANK_RAW --><a href=\"stat.php?range={search_result.PLAYER_RANK_RAW}#{search_result.PLAYER_RANK_RAW}\">{search_result.PLAYER_RANK}</a><!-- ELSE -->-<!-- ENDIF -->\n        </td>\n      </tr>\n    <!-- END search_result -->\n  <!-- ENDIF -->\n</table>\n<!-- INCLUDE page_hint -->\n"
  },
  {
    "path": "design/templates/OpenGame/server_info.tpl.html",
    "content": "<h1>{L_sys_universe} &quot;{C_game_name}&quot;</h1>\n\n<table width=\"519\" style=\"color:#FFFFFF\"><tbody>\n  <tr><td class=\"c\" colspan=\"2\">{L_adm_opt_game_settings}</td></tr>\n  <tr>\n    <th>{L_adm_opt_game_name}</th>\n    <th>{C_game_name}</th>\n  </tr>\n  <tr>\n    <th class=\"c\">{L_adm_opt_game_mode}</th>\n    <th class=\"c\">\n      <!-- IF C_game_mode == 1 -->\n        {L_sys_game_mode[1]}\n      <!-- ELSE -->\n        {L_sys_game_mode[0]}\n      <!-- ENDIF -->\n    </th>\n  </tr>\n  <tr>\n    <th>{L_adm_opt_multiaccount_enabled}</th>\n    <th>\n      <!-- IF GAME_MULTIACCOUNT_ENABLED -->\n        {L_sys_yes}\n      <!-- ELSE -->\n        {L_sys_no}\n      <!-- ENDIF -->\n    </th>\n  </tr>\n  <tr>\n    <th>{L_adm_opt_empire_mercenary_temporary}</th>\n    <th>\n      <!-- IF EMPIRE_MERCENARY_TEMPORARY -->\n        {L_sys_yes}\n      <!-- ELSE -->\n        {L_sys_no}\n      <!-- ENDIF -->\n    </th>\n  </tr>\n  <tr>\n    <th>{L_adm_opt_speed} {L_adm_opt_game_gspeed}</th>\n    <!--<th>x&nbsp;{GAME_SPEED}</th>-->\n    <th>\n      <!-- IF GAME_SPEED != GAME_SPEED_PLAIN -->\n      <span class=\"ok\">{L_sys_system_speed_for_action} x&nbsp;{GAME_SPEED}</span><br/>\n      <span class=\"warning\">{L_sys_system_speed_original} x {GAME_SPEED_PLAIN}</span>\n      <!-- ELSE  -->\n      x&nbsp;{GAME_SPEED}\n      <!-- ENDIF -->\n    </th>\n  </tr>\n  <tr>\n    <th>{L_adm_opt_speed} {L_adm_opt_game_fspeed}</th>\n    <!--<th>x&nbsp;{FLEET_SPEED}</th>-->\n    <th>\n      <!-- IF FLEET_SPEED != FLEET_SPEED_PLAIN -->\n      <span class=\"ok\">{L_sys_system_speed_for_action} x&nbsp;{FLEET_SPEED}</span><br/>\n      <span class=\"warning\">{L_sys_system_speed_original} x {FLEET_SPEED_PLAIN}</span>\n      <!-- ELSE  -->\n      x&nbsp;{FLEET_SPEED}\n      <!-- ENDIF -->\n    </th>\n  </tr>\n  <tr>\n    <th>{L_adm_opt_speed} {L_adm_opt_game_pspeed}</th>\n    <th>\n      <!-- IF RESOURCE_MULTIPLIER != RESOURCE_MULTIPLIER_PLAIN -->\n      <span class=\"ok\">{L_sys_system_speed_for_action} x&nbsp;{RESOURCE_MULTIPLIER}</span><br/>\n      <span class=\"warning\">{L_sys_system_speed_original} x {RESOURCE_MULTIPLIER_PLAIN}</span>\n      <!-- ELSE  -->\n      x&nbsp;{RESOURCE_MULTIPLIER}\n      <!-- ENDIF -->\n    </th>\n  </tr>\n  <tr>\n    <th class=\"c\">{L_adm_opt_galaxies}</th>\n    <th class=\"c\">{C_game_maxGalaxy}</th>\n  </tr>\n  <tr>\n    <th class=\"c\">{L_adm_opt_systems}</th>\n    <th class=\"c\">{C_game_maxSystem}</th>\n  </tr>\n  <tr>\n    <th class=\"c\">{L_adm_opt_planets}</th>\n    <th class=\"c\">{C_game_maxPlanet}</th>\n  </tr>\n  <tr>\n    <td class=\"c_c\">{L_adm_uni_price_galaxy}</th>\n    <td class=\"c_c\">{C_uni_price_galaxy}</th>\n  </tr>\n  <tr>\n    <td class=\"c_c\">{L_adm_uni_price_system}</th>\n    <td class=\"c_c\">{C_uni_price_system}</th>\n  </tr>\n\n  <tr><td class=\"c\" colspan=\"2\">{L_adm_opt_plan_settings}</td></tr>\n  <tr>\n    <th>{L_adm_opt_plan_initial}</th>\n    <th>{C_initial_fields} {L_adm_opt_sectors}</th>\n  </tr>\n  <tr>\n    <th>{L_adm_opt_plan_base_inc} {L_sys_metal}</th>\n    <th>{C_metal_basic_income} {L_adm_opt_per_hour}</th>\n  </tr>\n  <tr>\n    <th>{L_adm_opt_plan_base_inc} {L_sys_crystal}</th>\n    <th>{C_crystal_basic_income} {L_adm_opt_per_hour}</th>\n  </tr>\n  <tr>\n    <th>{L_adm_opt_plan_base_inc} {L_sys_deuterium}</th>\n    <th>{C_deuterium_basic_income} {L_adm_opt_per_hour}</th>\n  </tr>\n  <tr>\n    <th>{L_adm_opt_plan_base_inc} {L_sys_energy}</th>\n    <th>{C_energy_basic_income} {L_adm_opt_per_hour}</th>\n  </tr>\n  <tr>\n    <th>{L_adm_opt_max_colonies}</th>\n    <th><!-- IF PLAYER_MAX_COLONIES < 1 -->{L_sys_unlimited}<!-- ELSE -->{PLAYER_MAX_COLONIES}<!-- ENDIF --></th>\n  </tr>\n\n    <tr><td class=\"c\" colspan=\"2\">{L_sys_opt_bash_info}</td></tr>\n    <!-- IF FLEET_BASHING_ATTACKS -->\n    <tr>\n      <th>{L_sys_opt_bash_attacks}</th>\n      <th>{C_fleet_bashing_attacks}</th>\n    </tr>\n    <tr>\n      <th>{L_sys_opt_bash_waves}</th>\n      <th>{C_fleet_bashing_waves}</th>\n    </tr>\n    <tr>\n      <th>{L_sys_opt_bash_interval}</th>\n      <th>{fleet_bashing_interval}</th>\n    </tr>\n    <tr>\n      <th>{L_sys_opt_bash_scope}</th>\n      <th>{fleet_bashing_scope}</th>\n    </tr>\n    <tr>\n      <th>{L_sys_opt_bash_war_delay}</th>\n      <th>{fleet_bashing_war_delay}</th>\n    </tr>\n    <!-- ELSE -->\n      <tr><td style=\"c_l\" colspan=2>{L_sys_opt_bash_disabled}</td></tr>\n    <!-- ENDIF -->\n\n\n  <tr><td class=\"c\" colspan=\"2\">{L_adm_opt_game_oth_info}</td></tr>\n  <!-- IF ALI_BONUS_MEMBERS -->\n  <tr>\n    <th>{L_sys_ali_bonus_members}</th> \n    <th>{ALI_BONUS_MEMBERS}</th>\n  </tr>\n  <!-- ENDIF -->\n  <tr>\n    <th>{L_adm_opt_exchange}</th>\n    <th>{L_sys_metal}&nbsp;{C_rpg_exchange_metal}&nbsp;:&nbsp;{L_sys_crystal}&nbsp;{C_rpg_exchange_crystal}&nbsp;:&nbsp;{L_sys_deuterium}&nbsp;{C_rpg_exchange_deuterium}&nbsp;:&nbsp;{L_sys_dark_matter_sh}&nbsp;{C_rpg_exchange_darkMatter}</th>\n  </tr>\n  <tr>\n    <th>{L_adm_opt_vacation_mode_is}</th> \n    <th>\n      <!-- IF USER_VACATION_DISABLE -->\n        {L_sys_off}\n      <!-- ELSE -->\n        {L_sys_on}\n      <!-- ENDIF -->\n    </th>\n  </tr>\n  <tr>\n    <th>{L_adm_opt_build_on_research}</th>\n    <th>\n      <!-- IF game_build_and_research -->\n        {L_sys_yes}\n      <!-- ELSE -->\n        {L_sys_no}\n      <!-- ENDIF -->\n    </th>\n  </tr>\n  <tr>\n    <th>{L_adm_opt_allow_buffing}</th>\n    <th>\n      <!-- IF ALLOW_BUFFING -->\n        {L_sys_yes}\n      <!-- ELSE -->\n        {L_sys_no}\n      <!-- ENDIF -->\n    </th>\n  </tr>\n  <tr>\n    <th>{L_adm_opt_ally_help_weak}</th>\n    <th>\n      <!-- IF ALLY_HELP_WEAK -->\n        {L_sys_yes}\n      <!-- ELSE -->\n        {L_sys_no}\n      <!-- ENDIF -->\n    </th>\n  </tr>\n\n  <tr><td class=\"c\" colspan=\"2\">{L_adm_ver_versions}</td></tr>\n  <tr>\n    <th>{L_adm_ver_version_sn}</th>\n    <th>{D_SN_VERSION}-{DB_PATCH_VERSION}</th>\n  </tr>\n  <tr>\n    <th>{L_adm_ver_version_db}</th>\n    <th>{C_db_version}p{DB_PATCH_VERSION}</th>\n  </tr>\n\n</tbody></table>\n"
  },
  {
    "path": "design/templates/OpenGame/simple_table.tpl.html",
    "content": "<h1>{PAGE_HEADER}</h1>\n<!-- BEGIN table -->\n<!-- IF table.TABLE_HEADER --><h3>{table.TABLE_HEADER}</h3><!-- ENDIF -->\n<table>\n  <!-- IF .table.row -->\n    <!-- IF table.COLUMN_NAME_1 && table.COLUMN_NAME_2 -->\n    <tr align=\"center\">\n      <th class=\"c_l\">{table.COLUMN_NAME_1}</th>\n      <th class=\"c_l\">{table.COLUMN_NAME_2}</th>\n    </tr>\n    <!-- ENDIF -->\n\n    <!-- BEGIN row -->\n      <tr align=center>\n        <td class=\"c_l\"><b>{row.VALUE_1}</b></td>\n        <td class=\"c_l\"><b>{row.VALUE_2}</b></td>\n      </tr>\n    <!-- END row -->\n    <!-- IF table.TABLE_FOOTER && .row -->\n    <tr><th class=\"c_c\" colspan=\"2\">{table.TABLE_FOOTER}: {row.S_ROW_COUNT}</th></tr>\n    <!-- ENDIF -->\n  <!-- ELSEIF table.TABLE_EMPTY -->\n    <tr><th class=\"c_c\" colspan=\"2\">{table.TABLE_EMPTY}</th></tr>\n  <!-- ENDIF -->\n</table>\n<!-- END table -->\n"
  },
  {
    "path": "design/templates/OpenGame/simulator.tpl.html",
    "content": "<script type=\"text/javascript\"><!--\nvar sym_list = Array();\n\n<!-- BEGIN simulator -->\n  <!-- IF simulator.ID && simulator.GROUP != UNIT_DEFENCE -->\n    sym_list[{simulator.ID}] = '{simulator.VALUE}';\n  <!-- ENDIF -->\n<!-- END simulator -->\n\nfunction sym_set(id, value)\n{\n  if(!value)\n  {\n    value = 0;\n  }\n  else\n  {\n    value = sym_list[id];\n  }\n\n  $('#attacker' + id).val(value);\n}\n\nfunction sym_set_all(group, value)\n{\n  for(sym_id in sym_list)\n  {\n    if(sym_id >= group && sym_id < group +100)\n    {\n      sym_set(sym_id, value);\n    }\n  }\n}\n--></script>\n\n<h2>{L_COE_combatSimulator}</h2>\n<form action='simulator.php' method='post'>\n  {L_ube_simulation} <input type=\"checkbox\" name=\"simulator\" value=\"1\" />\n<!--Проверка записи/чтения <input type=\"checkbox\" name=\"reload\" />-->\n  <table>\n    <!-- BEGIN simulator -->\n    <tr class=\"c_c\">\n      <!-- IF simulator.ID -->\n        <td>{simulator.NAME}</td>\n          <!-- IF simulator.GROUP != UNIT_DEFENCE && simulator.GROUP != UNIT_GOVERNORS -->\n<!--\n            <td colspan=\"3\">\n              <table class=\"markup\"><tr>\n-->\n                <td>\n                  <input style=\"height: 24px; width: 24px\" type=\"button\" class=\"button\" tabindex=-1 value=\"0\" onclick=\"sym_set({simulator.ID});\">\n                </td>\n                <td>\n                  <input type='text' tabindex=1{simulator.NUM} group=\"attacker{simulator.ID}\" id=\"attacker{simulator.ID}\" name='attacker[{simulator.ID}]' value='{simulator.ATTACKER}' />\n                </td>\n                <td>\n                  <input style=\"height: 24px;\" type=\"button\" class=\"button\" tabindex=-1 value=\"{L_sys_max}\" onclick=\"sym_set({simulator.ID}, 1);\">\n<!--\n                </td>\n              </tr></table>\n-->\n            </td>\n          <!-- ELSE -->\n            <td colspan=\"3\">\n              &nbsp;\n            </td>\n          <!-- ENDIF -->\n        <td>\n          <input type='text' tabindex=2{simulator.NUM} name='defender[{simulator.ID}]' value='{simulator.DEFENDER}'>\n        </td>\n      <!-- ELSE -->\n        <!-- IF simulator.GROUP != UNIT_DEFENCE && simulator.GROUP != UNIT_GOVERNORS -->\n          <th>{simulator.NAME}</th>\n<!--\n          <th colspan=\"3\">\n            <table class=\"markup\"><tr>\n-->\n              <th>\n                <input style=\"height: 24px; width: 24px\" type=\"button\" class=\"button\" tabindex=-1 value=\"0\" onclick=\"sym_set_all({simulator.GROUP})\" />\n              </th>\n              <th>{L_sys_attacker}</th>\n              <th>\n                <input style=\"height: 24px;\" type=\"button\" class=\"button\" tabindex=-1 value=\"{L_sys_max}\" onclick=\"sym_set_all({simulator.GROUP}, true)\">\n              </th>\n<!--\n            </tr></table>\n          </th>\n-->\n          <th class=c>{L_sys_defender}</th>\n        <!-- ELSE -->\n          <th>{simulator.NAME}</th>\n          <th colspan=\"3\">&nbsp;</th>\n          <th>{L_sys_defender}</th>\n        <!-- ENDIF -->\n      <!-- ENDIF -->\n    </tr>\n    <!-- END simulator -->\n\n    <tr class=\"c_c\"><th colspan='5'><input type='submit' class=\"button\" name='submit' value='{L_COE_simulate}'></th></tr>\n  </table>\n  <input type='hidden' name='BE_DEBUG' value=\"{BE_DEBUG}\">\n</form>\n"
  },
  {
    "path": "design/templates/OpenGame/stat_statistics.tpl.html",
    "content": "<h1>{L_stat_header}</h1>\n<!-- IF ! SOURCE -->\n  <!-- IF REFRESH_DATE -->\n  <h2>{L_stat_refresh_last}&nbsp;{REFRESH_DATE}</h2>\n  <!-- ENDIF -->\n  <!-- IF NEXT_DATE -->\n  <h3>{L_stat_refresh_next}&nbsp;{NEXT_DATE}</h3>\n  <!-- ENDIF -->\n<!-- ENDIF -->\n\n<!-- IF STATS_HIDE_PM_LINK && SUBJECT == 1 -->\n  <!-- IF SOURCE -->\n    <!-- DEFINE $HEADER_COLSPAN = '4' -->\n  <!-- ELSE -->\n    <!-- DEFINE $HEADER_COLSPAN = '5' -->\n  <!-- ENDIF -->\n\n<!-- ELSE -->\n  <!-- DEFINE $HEADER_COLSPAN = '6' -->\n<!-- ENDIF -->\n\n<table width=\"519\">\n  <tr>\n    <th class=\"c_c\" colspan=\"{$HEADER_COLSPAN}\" nowrap>\n      <form name=\"stat_form\" method=\"get\">\n        {L_stat_show}\n        <select name=\"type\" onChange=\"document.stat_form.submit()\">\n          <!-- BEGIN type -->\n          <option value=\"{type.ID}\"<!-- IF type.SELECTED --> selected<!-- ENDIF -->>{type.HEADER}</option>\n          <!-- END type -->\n        </select>\n        {L_stat_by}\n        <select name=\"who\" onChange=\"document.stat_form.submit()\">\n          <!-- BEGIN subject -->\n          <option value=\"{subject.ID}\"<!-- IF subject.SELECTED --> selected<!-- ENDIF -->>{subject.HEADER}</option>\n          <!-- END subject -->\n        </select>\n        {L_stat_range}\n        <select name=\"range\" onChange=\"document.stat_form.submit()\">\n          <!-- BEGIN range -->\n          <option value=\"{range.ID}\"<!-- IF range.SELECTED --> selected<!-- ENDIF -->>{range.HEADER}</option>\n          <!-- END range -->\n        </select>\n        <input type=\"hidden\" name=\"source\" value=\"{SOURCE}\" />\n      </form>\n    </th>\n  </tr>\n\n  <tr>\n    <th class =\"c_c\" width=\"30\">{L_stat_rank}</th>\n    <th class =\"c_c\" width=\"30\">{L_stat_rank_diff}</th>\n\n    <!-- IF SUBJECT == 1 -->\n      <th class =\"c_c\">{L_sys_player}</th>\n      <!-- IF ! SOURCE -->\n      <!--<th class =\"c_c\"><img src=\"{I_menu_empire_emperor}\" border=\"0\" alt=\"{L_stat_details}\" title=\"{L_stat_details}\" /></th>-->\n      <th class =\"c_c\">{L_sys_alliance}</th>\n      <!-- ENDIF -->\n    <!-- ELSE -->\n      <th class =\"c_c\">{L_sys_alliance}</th>\n      <th class =\"c_c\">{L_stat_members}</th>\n      <th class =\"c_c\">{L_stat_per_member}</th>\n    <!-- ENDIF -->\n\n    <th class =\"c_c\">{L_stat_points}</th>\n    <!-- IF SUBJECT == 1 && ! STATS_HIDE_PM_LINK -->\n    <th class =\"c_c\"><img src=\"design/images/icon_mail.gif\" border=\"0\" alt=\"{L_stat_message_write}\" title=\"{L_stat_message_write}\" /></th>\n    <!-- ENDIF -->\n  </tr>\n\n  <!-- BEGIN stat -->\n    <!-- IF stat.RANK_CHANGE > 0 -->\n      <!-- DEFINE $RANK_CLASS = 'positive' -->\n      <!-- DEFINE $RANK_SIGN = '+{stat.RANK_CHANGE}' -->\n    <!-- ELSEIF stat.RANK_CHANGE < 0 -->\n      <!-- DEFINE $RANK_CLASS = 'negative' -->\n      <!-- DEFINE $RANK_SIGN = '{stat.RANK_CHANGE}' -->\n    <!-- ELSE -->\n      <!-- DEFINE $RANK_CLASS = 'zero' -->\n      <!-- DEFINE $RANK_SIGN = '*' -->\n    <!-- ENDIF -->\n    <tr>\n      <td class=\"c_r\"><!-- IF stat.RANK == RANGE --><a name=\"{stat.RANK}\"><span class=\"fl\">&gt;</span></a><!-- ENDIF -->{stat.RANK}</td>\n      <td class=\"c_r {$RANK_CLASS}\">{$RANK_SIGN}</td>\n      <!-- IF SUBJECT == 1 -->\n        <td class=\"c_l<!-- IF USER_ID == stat.ID --> same_player<!-- ENDIF -->\" nowrap>\n          <a href=\"index.php?page=imperator&int_user_id={stat.ID}<!-- IF SOURCE -->&source={SOURCE}<!-- ENDIF -->\" class=\"link\">\n            {stat.NAME}\n          </a>\n        </td>\n        <!-- IF ! SOURCE -->\n        <!--<td class =\"c_c\">-->\n          <!--<a href=\"index.php?page=imperator&int_user_id={stat.ID}&lt;!&ndash; IF SOURCE &ndash;&gt;&source={SOURCE}&lt;!&ndash; ENDIF &ndash;&gt;\">-->\n            <!--<img src=\"{I_menu_empire_emperor}\" border=\"0\" alt=\"{L_stat_details}\" title=\"{L_stat_details}\"  />-->\n          <!--</a>-->\n        <!--</td>-->\n        <td class=\"c_l\" nowrap><!-- IF stat.ALLY_ID --><a href=\"alliance.php?mode=ainfo&a={stat.ALLY_ID}\" class=\"link\"><span class=\"<!-- IF USER_ALLY == stat.ALLY_ID -->same_alliance<!-- ENDIF -->\">{stat.ALLY_NAME}</span></a><!-- ELSE -->&nbsp;<!-- ENDIF --></td>\n        <!-- ENDIF -->\n      <!-- ELSE -->\n        <td class=\"c_l\"><a href=\"alliance.php?mode=ainfo&a={stat.ID}\"><span class=\"<!-- IF USER_ALLY == stat.ID -->same_alliance<!-- ENDIF -->\">{stat.NAME}</span></a></td>\n        <td class=\"c_r\">{stat.MEMBERS}</td>\n        <td class=\"c_r\">{stat.POINTS_PER_MEMBER}</td>\n      <!-- ENDIF -->\n      <td class=\"c_r\">{stat.POINTS}</td>\n\n      <!-- IF SUBJECT == 1 && ! STATS_HIDE_PM_LINK -->\n      <td class=\"c_c\">\n        <a href=\"messages.php?mode=write&id={stat.ID}\">\n          <img src=\"design/images/icon_mail.gif\" border=\"0\" alt=\"{L_stat_message_write}\" title=\"{L_stat_message_write}\" />\n        </a>\n      </td>\n      <!-- ENDIF -->\n    </tr>\n  <!-- END stat -->\n\n</table>\n"
  },
  {
    "path": "design/templates/OpenGame/techtree.tpl.html",
    "content": "<!-- IF PLAYER_OPTION_TECH_TREE_TABLE -->\n  <!-- INCLUDE techtree_computer -->\n<!-- ELSE -->\n  <!-- INCLUDE techtree_mobile -->\n<!-- ENDIF -->\n\n\n"
  },
  {
    "path": "design/templates/OpenGame/techtree_computer.tpl.html",
    "content": "<table>\n<!-- BEGIN techtree -->\n  <tr>\n    <!-- IF ! techtree.ID -->\n   <th class=\"c_l\" colspan=\"2\">\n     {techtree.NAME}\n   </th>\n   <!-- ELSE -->\n   <td class=\"c_l\">\n     <a href=\"infos.php?gid={techtree.ID}\">{techtree.NAME}</a><!-- IF techtree.LEVEL -->&nbsp;({techtree.LEVEL_BASIC}<!-- IF techtree.LEVEL_BONUS --><span class=\"bonus\">+{techtree.LEVEL_BONUS}</span><!-- ENDIF --><!-- IF techtree.LEVEL_MAX -->/{techtree.LEVEL_MAX}<!-- ENDIF -->)<!-- ENDIF -->\n   </td>\n\n   <td class=\"c_l\"><ul>\n     <!-- DEFINE $REQUIRE_PRINTED = 0 -->\n     <!-- BEGIN require -->\n     <!-- IF ! $REQUIRE_PRINTED -->\n     <!-- DEFINE $REQUIRE_PRINTED = 1 -->\n     {L_wiki_requrements}:\n     <!-- ENDIF -->\n     <li style=\"margin: 0; padding: 0;\"><span class=\"<!-- IF require.REQUEREMENTS_MET -->positive<!-- ELSE -->negative<!-- ENDIF -->\">{require.NAME}</span><!-- IF require.LEVEL_REQUIRE -->&nbsp;{require.LEVEL_BASIC}<!-- IF require.LEVEL_BONUS --><span class=\"bonus\">+{require.LEVEL_BONUS}</span><!-- ENDIF -->/{require.LEVEL_REQUIRE}<!-- ENDIF --></li>\n     <!-- END require -->\n\n     <!-- DEFINE $GRANTS_PRINTED = 0 -->\n     <!-- BEGIN grants -->\n     <!-- IF ! $GRANTS_PRINTED -->\n     <!-- DEFINE $GRANTS_PRINTED = 1 -->\n     <!-- IF $REQUIRE_PRINTED -->\n     <br />\n     <!-- ENDIF -->\n     {L_wiki_grants}:\n     <!-- ENDIF -->\n     <li style=\"margin: 0; padding: 0;\"><span class=\"<!-- IF grants.REQUEREMENTS_MET -->positive<!-- ELSE -->negative<!-- ENDIF -->\">{grants.NAME}</span><!-- IF grants.LEVEL_REQUIRE -->&nbsp;{grants.LEVEL_BASIC}<!-- IF grants.LEVEL_BONUS --><span class=\"bonus\">+{grants.LEVEL_BONUS}</span><!-- ENDIF -->/{grants.LEVEL_REQUIRE}<!-- ENDIF --></li>\n     <!-- END grants -->\n     <!-- IF ! $REQUIRE_PRINTED && ! $GRANTS_PRINTED -->\n     &nbsp;\n     <!-- ENDIF -->\n     </ul>\n   </td>\n   <!-- ENDIF -->\n  </tr>\n<!-- END techtree -->\n</table>\n"
  },
  {
    "path": "design/templates/OpenGame/techtree_mobile.tpl.html",
    "content": "<!-- DEFINE $PREV_GROUP_CLOSING_TAG = '' -->\n<div id=\"tab_tech_tree\" class=\"border_image_large\" table_title=\"{PAGE_HEADER}\">\n  <ul>\n    <!-- BEGIN techtree -->\n    <!-- IF ! techtree.ID -->\n    <li><a href=\"#tab_tech_tree_{techtree.GROUP_ID}\">{techtree.NAME}</a></li>\n    <!-- ENDIF -->\n    <!-- END techtree -->\n  </ul>\n  <!-- BEGIN techtree -->\n\n  <!-- IF ! techtree.ID  -->\n  {$PREV_GROUP_CLOSING_TAG}\n  <!-- DEFINE $PREV_GROUP_CLOSING_TAG = '</div>' -->\n  <div id=\"tab_tech_tree_{techtree.GROUP_ID}\">\n    <!-- ELSE -->\n    <div class=\"tech_container\">\n      <div class=\"tech_image button_pseudo\" go=\"info\" unit_id=\"{techtree.ID}\">\n        <img src=\"{I_[techtree.ID]}\" />\n        <span class=\"a75\">{techtree.NAME}<!-- IF techtree.LEVEL -->&nbsp;({techtree.LEVEL_BASIC}<!-- IF techtree.LEVEL_BONUS --><span class=\"bonus\">+{techtree.LEVEL_BONUS}</span><!-- ENDIF --><!-- IF techtree.LEVEL_MAX -->/{techtree.LEVEL_MAX}<!-- ENDIF -->)<!-- ENDIF --></span>\n      </div>\n      <!-- IF .techtree.require || .techtree.grants -->\n      <div class=\"tech_require\">\n        <!-- IF .techtree.require -->\n        {L_wiki_requrements}:\n        <ul>\n          <!-- BEGIN require -->\n          <li style=\"margin: 0; padding: 0;\"><span go=\"info\" unit_id=\"{require.ID}\" class=\"link <!-- IF require.REQUEREMENTS_MET -->positive<!-- ELSE -->negative<!-- ENDIF -->\">{require.NAME}</span><!-- IF require.LEVEL_REQUIRE -->&nbsp;{require.LEVEL_BASIC}<!-- IF require.LEVEL_BONUS --><span class=\"bonus\">+{require.LEVEL_BONUS}</span><!-- ENDIF -->/{require.LEVEL_REQUIRE}<!-- ENDIF --></li>\n          <!-- END require -->\n        </ul>\n        <!-- ENDIF -->\n\n        <!-- IF .techtree.grants -->\n        {L_wiki_grants}:\n        <ul>\n          <!-- BEGIN grants -->\n          <li style=\"margin: 0; padding: 0;\"><span class=\"<!-- IF grants.REQUEREMENTS_MET -->positive<!-- ELSE -->negative<!-- ENDIF -->\">{grants.NAME}</span><!-- IF grants.LEVEL_REQUIRE -->&nbsp;{grants.LEVEL_BASIC}<!-- IF grants.LEVEL_BONUS --><span class=\"bonus\">+{grants.LEVEL_BONUS}</span><!-- ENDIF -->/{grants.LEVEL_REQUIRE}<!-- ENDIF --></li>\n          <!-- END grants -->\n        </ul>\n        <!-- ENDIF -->\n      </div>\n      <!-- ENDIF -->\n    </div>\n    <!-- ENDIF -->\n    <!-- END techtree -->\n  {$PREV_GROUP_CLOSING_TAG}\n</div>\n\n<script type=\"text/javascript\">\n  $(\"#tab_tech_tree\").tabs().show();\n</script>\n"
  },
  {
    "path": "design/templates/OpenGame/tutorial.tpl.html",
    "content": "<script type=\"text/javascript\" src=\"js/tutorial.js\"></script>\n\n<script type=\"text/javascript\">\n  $.extend(language, {\n    tutorial_error_load: '{LA_tutorial_error_load}',\n    tutorial_error_next: '{LA_tutorial_error_next}',\n    tutorial_error_prev: '{LA_tutorial_error_prev}'\n  });\n  //\n  <!-- BEGIN tutorial -->\n  tutorial['{tutorial.KEY}'] = '{tutorial.VALUE}';\n  //\n  <!-- END tutorial -->\n</script>\n\n<div id=\"tutorial_block\">\n  <div id=\"tutorial_container\" class=\"border_image_small\">\n    <div id=\"tutorial_header\" class=\"contFJ bg_title\">\n      <div id=\"tutorial_header_text\">Обучение <span id=\"tutorial_header_additional\"></span></div>\n      <div><img id=\"tutorial_close\" class=\"fr pointer image_close hide\" src=\"{I_r1}\" alt=\"{L_sys_close}\"\n                title=\"{L_sys_close}\"/></div>\n    </div>\n    <div>\n      <div id=\"tutorial_text_adviser\"><img src=\"design/images/adviser.gif\"/></div>\n      <div id=\"tutorial_text\"></div>\n    </div>\n    <div style=\"clear:left;\"></div>\n    <div id=\"tutorial_buttons\">\n      <input type=\"hidden\" id=\"tutorial_current\" name=\"tutorial_current\" value=\"{TUTORIAL_CURRENT}\"/>\n      <table class=\"no_border_image markup\" width=\"100%\">\n        <tr>\n          <td class=\"c_l\" width=\"33%\">\n            <input type=\"button\" class=\"hide\" id=\"tutorial_button_prev\" name=\"tutorial_button_prev\"\n                   value=\"{L_tutorial_prev}\"/>\n          </td>\n          <td>\n            <input type=\"button\" class=\"hide\" id=\"tutorial_button_window\" name=\"tutorial_button_window\"\n                   value=\"{L_tutorial_window}\" data-windowed=\"1\"/>\n            <input type=\"button\" class=\"hide\" id=\"tutorial_button_window_off\" name=\"tutorial_button_window\"\n                   value=\"{L_tutorial_window_off}\" data-windowed=\"0\"/>\n          </td>\n          <td class=\"c_r\" width=\"33%\">\n            <input type=\"button\" class=\"hide\" id=\"tutorial_button_next\" name=\"tutorial_button_next\"\n                   value=\"{L_tutorial_next}\"/>\n            <input type=\"button\" class=\"hide\" id=\"tutorial_button_finish\" name=\"tutorial_button_finish\"\n                   value=\"{L_tutorial_finish}\"/>\n          </td>\n        </tr>\n      </table>\n    </div>\n    <div id=\"tutorial_footer\" class=\"hide\"></div>\n  </div>\n</div>\n\n<script type=\"text/javascript\">\n  tutorial_change();\n  tutorial_window_switch(tutorial_windowed.windowed);\n</script>\n"
  },
  {
    "path": "design/templates/OpenGame/ube_combat_report.tpl.html",
    "content": "<h1>{PAGE_HEADER}</h1>\n\n<table style=\"margin-top: 1em;\">\n  <tr>\n    <td class=\"c_c transparent_cell\" colspan=\"3\">\n      <h2>{L_ube_report_info_main}</h2>\n    </td>\n  </tr>\n\n  <!-- IF COMBAT_TIME -->\n    <tr>\n      <td>\n        {L_ube_report_info_date}\n      </td>\n      <td colspan=\"2\" class=\"c_c\">\n        {COMBAT_TIME_TEXT}\n      </td>\n    </tr>\n  <!-- ENDIF -->\n\n  <!-- IF PLANET_GALAXY -->\n    <tr>\n      <td>\n        {L_ube_report_info_location}\n      </td>\n      <td colspan=\"2\" class=\"c_c\">\n        <a class=\"link\" href=\"galaxy.php?mode=3&galaxy={PLANET_GALAXY}&system={PLANET_SYSTEM}\">[{PLANET_GALAXY}:{PLANET_SYSTEM}:{PLANET_PLANET}]&nbsp;{PLANET_TYPE_TEXT}&nbsp;{PLANET_NAME}</a>\n      </td>\n    </tr>\n  <!-- ENDIF -->\n\n  <tr>\n    <td>\n      {L_ube_report_info_rounds_number}\n    </td>\n    <td colspan=\"2\" class=\"c_c\">\n      {COMBAT_ROUNDS}\n    </td>\n  </tr>\n  <tr>\n    <td>\n      {L_ube_report_info_outcome}\n    </td>\n    <td colspan=\"2\" class=\"c_c\">\n      <!-- IF UBE_COMBAT_RESULT == UBE_COMBAT_RESULT_WIN -->\n        {L_ube_report_info_outcome_win}\n      <!-- ELSEIF UBE_COMBAT_RESULT == UBE_COMBAT_RESULT_LOSS -->\n        {L_ube_report_info_outcome_loss}\n      <!-- ELSE -->\n        {L_ube_report_info_outcome_draw}\n      <!-- ENDIF -->\n    </td>\n  </tr>\n\n  <!-- IF UBE_SFR -->\n    <tr>\n      <td colspan=\"3\" class=\"c_c notice\">\n        {L_ube_report_info_sfr}\n      </td>\n    </tr>\n  <!-- ENDIF -->\n\n  <!-- IF .debris -->\n    <tr>\n      <th class=\"c_c\" colspan=\"3\">\n        {L_ube_report_info_debris}\n        <!-- IF UBE_IS_SIMULATOR -->\n        {L_ube_report_info_debris_simulator}\n        <!-- ENDIF -->\n      </th>\n    </tr>\n    <!-- BEGIN debris -->\n      <tr>\n        <td>\n          {debris.NAME}\n        </td>\n        <td class=\"c_r\" colspan=\"2\">\n          {debris.AMOUNT}\n        </td>\n      </tr>\n    <!-- END debris -->\n  <!-- ENDIF -->\n\n  <!-- IF UBE_MOON != UBE_MOON_NONE -->\n    <tr>\n      <th class=\"c_c\" colspan=\"3\">\n        <!-- IF UBE_MISSION_TYPE == MT_DESTROY -->\n          <!-- IF UBE_MOON_REAPERS == UBE_MOON_REAPERS_NONE -->\n            {L_ube_report_moon_reapers_none}\n          <!-- ELSE -->\n            {L_ube_report_moon_reapers_wave}<br />\n            {L_ube_report_moon_reapers_chance} {UBE_MOON_DESTROY_CHANCE}%.\n            <!-- IF UBE_MOON == UBE_MOON_DESTROY_SUCCESS -->\n              {L_ube_report_moon_reapers_success}\n            <!-- ELSE -->\n              {L_ube_report_moon_reapers_failure}\n            <!-- ENDIF --><br />\n\n            {L_ube_report_moon_reapers_outcome} {UBE_MOON_REAPERS_DIE_CHANCE}%.\n            <!-- IF UBE_MOON_REAPERS == UBE_MOON_REAPERS_RETURNED -->\n              {L_ube_report_moon_reapers_survive}\n            <!-- ELSE -->\n              {L_ube_report_moon_reapers_died}\n            <!-- ENDIF --><br />\n          <!-- ENDIF -->\n        <!-- ELSEIF UBE_MOON == UBE_MOON_WAS -->\n          {L_ube_report_moon_was}\n        <!-- ELSE -->\n          {L_ube_report_moon_chance} {UBE_MOON_CHANCE}%\n          <!-- IF UBE_MOON == UBE_MOON_CREATE_SUCCESS -->\n            <br />{L_ube_report_moon_created} {UBE_MOON_SIZE}\n          <!-- ENDIF -->\n        <!-- ENDIF -->\n      </th>\n    </tr>\n  <!-- ENDIF -->\n\n\n  <!-- IF UBE_CAPTURE_RESULT -->\n  <tr>\n    <td class=\"c_c transparent_cell\" colspan=\"3\" style=\"padding-top: 1em;\">\n      <h2>{L_ube_report_capture}</h2>\n    </td>\n  </tr>\n  <tr style=\"padding-bottom: 1em;\">\n    <td class=\"c_c\" colspan=\"3\">\n      <h3 style=\"word-wrap: normal;\">{UBE_CAPTURE_RESULT_TEXT}</h3>\n    </td>\n  </tr>\n  <!-- ENDIF -->\n\n\n  <!-- IF .loot > 0 -->\n  <tr>\n    <td class=\"c_c transparent_cell\" colspan=\"3\" style=\"padding-top: 1em;\">\n      <h2>{L_ube_report_info_loot}</h2>\n    </td>\n  <tr>\n  <!-- BEGIN loot -->\n    <!-- IF .loot.param -->\n      <tr>\n        <td class=\"c_c transparent_cell\" colspan=\"3\">\n          <h3><!-- IF loot.IS_ATTACKER -->{L_ube_report_side_attacker}<!-- ELSE -->{L_ube_report_side_defender}<!-- ENDIF --> {loot.NAME}</h3>\n        </td>\n      <tr>\n      <!-- BEGIN param -->\n        <tr>\n          <!-- IF ! param.LOSS -->\n            <th colspan=\"3\" class=\"c_c\">\n              {param.NAME}\n            </th>\n          <!-- ELSE -->\n            <!-- IF ! param.START -->\n              <td colspan=\"2\">\n                {param.NAME}\n              </td>\n            <!-- ELSE -->\n              <td>\n                {param.NAME}\n              </td>\n              <td class=\"c_r\">\n                {param.START}\n              </td>\n            <!-- ENDIF -->\n            <td class=\"c_r\">\n              {param.LOSS}\n            </td>\n          <!-- ENDIF -->\n        <tr>\n      <!-- END param -->\n    <!-- ENDIF -->\n  <!-- END loot -->\n<!-- ENDIF -->\n\n\n  <!-- IF UBE_REPORT_CYPHER -->\n  <tr>\n    <td class=\"c_c transparent_cell\" colspan=\"3\" style=\"padding-top: 1em;\">\n      <h3>{L_ube_report_info_page_header_cypher}</h3>\n    </td>\n  </tr>\n\n  <tr>\n    <td>\n      {L_ube_report_info_page_header_cypher}\n    </td>\n    <td colspan=\"2\" class=\"c_c\">\n      {UBE_REPORT_CYPHER}\n    </td>\n  </tr>\n\n  <tr>\n    <td>\n      {L_ube_report_info_link}\n    </td>\n    <td colspan=\"2\" class=\"c_c\">\n      <a class=\"link battle_report_link\" href=\"index.php?page=battle_report&cypher={UBE_REPORT_CYPHER}\">{UBE_REPORT_CYPHER}</a>\n    </td>\n  </tr>\n\n  <tr>\n    <td>\n      <!--<label for=\"ube_cypher_bbcode\">{L_ube_report_info_bbcode}</label>-->\n      {L_ube_report_info_bbcode}\n    </td>\n    <td colspan=\"2\" class=\"c_c\">\n      <div style=\"min-width: 30em;\">\n        <input type=\"text\" readonly=\"readonly\" value=\"[ube={UBE_REPORT_CYPHER}]\" id=\"ube_cypher_bbcode\"\n               style=\"width: 90%\"/>\n      </div>\n      <!--[ube={UBE_REPORT_CYPHER}]-->\n    </td>\n  </tr>\n  <!-- ENDIF -->\n\n\n\n<!-- IF .loss > 1 -->\n  <tr>\n    <td class=\"c_c transparent_cell\" colspan=\"3\" style=\"padding-top: 1em;\">\n      <h2>{L_ube_report_info_loss}</h2>\n    </td>\n  <tr>\n  <!-- BEGIN loss -->\n    <!-- IF .loss.param -->\n      <tr>\n        <td class=\"c_c transparent_cell\" colspan=\"3\">\n          <h3><!-- IF loss.IS_ATTACKER -->{L_ube_report_side_attacker}<!-- ELSE -->{L_ube_report_side_defender}<!-- ENDIF -->&nbsp;{loss.NAME}</h3>\n        </td>\n      <tr>\n      <!-- BEGIN param -->\n        <tr>\n          <!-- IF ! param.LOSS -->\n            <th colspan=\"3\" class=\"c_c\">\n              {param.NAME}\n            </th>\n          <!-- ELSE -->\n            <!-- IF ! param.START -->\n              <td colspan=\"2\">\n                {param.NAME}\n              </td>\n            <!-- ELSE -->\n              <td>\n                {param.NAME}\n              </td>\n              <td class=\"c_r\">\n                {param.START}\n              </td>\n            <!-- ENDIF -->\n            <td class=\"c_r\">\n              {param.LOSS}\n            </td>\n          <!-- ENDIF -->\n        <tr>\n      <!-- END param -->\n    <!-- ENDIF -->\n  <!-- END loss -->\n<!-- ENDIF -->\n</table>\n\n<!-- BEGIN round -->\n<table class=\"border_image_small\" style=\"margin-top: 1em\">\n  <tr>\n    <td colspan=\"8\" class=\"c_c markup\">\n      <h3 style=\"margin-bottom: 0\">{L_ube_report_round} {round.NUMBER}</h3>\n    </td>\n  </tr>\n  <!-- BEGIN fleet -->\n  <!-- IF .round.fleet.ship -->\n    <tr>\n      <td colspan=\"8\" class=\"c_c transparent_cell\">\n        <h4>\n          <!-- IF fleet.IS_ATTACKER -->{L_ube_report_side_attacker}<!-- ELSE -->{L_ube_report_side_defender}<!-- ENDIF -->\n          <!-- IF fleet.PLAYER_NAME -->&nbsp;{fleet.PLAYER_NAME}<!-- ENDIF -->\n          <!-- IF fleet.PLANET_GALAXY -->&nbsp;<a href=\"galaxy.php?mode=3&galaxy={fleet.PLANET_GALAXY}&system={fleet.PLANET_SYSTEM}\">[{fleet.PLANET_GALAXY}:{fleet.PLANET_SYSTEM}:{fleet.PLANET_PLANET}]</a><!-- ENDIF -->\n          <!-- IF fleet.PLANET_TYPE_TEXT -->&nbsp;{fleet.PLANET_TYPE_TEXT}<!-- ENDIF -->\n        </h4>\n      </td>\n    </tr>\n    <tr class=\"c_l\">\n      <th>\n        {L_ube_report_unit}\n      </th>\n      <th>\n        {L_sys_quantity}\n      </th>\n      <th>\n        {L_ube_report_attack}\n      </th>\n      <th>\n        {L_ube_report_shields}\n      </th>\n      <th>\n        {L_ube_report_shields_passed}\n      </th>\n      <th>\n        {L_ube_report_armor}\n      </th>\n      <th>\n        {L_ube_report_damage}\n      </th>\n      <th>\n        {L_ube_report_loss}\n      </th>\n    </tr>\n    <!-- BEGIN ship -->\n      <tr class=\"c_r\">\n        <!-- IF ship.UNITS_LOST == ship.UNITS -->\n          <!-- DEFINE $SHIP_NAME_CLASS = 'error' -->\n        <!-- ELSEIF ship.UNITS_LOST -->\n          <!-- DEFINE $SHIP_NAME_CLASS = 'warning' -->\n        <!-- ELSEIF ship.ARMOR_LOST -->\n          <!-- DEFINE $SHIP_NAME_CLASS = 'notice' -->\n        <!-- ELSE -->\n          <!-- DEFINE $SHIP_NAME_CLASS = 'ok' -->\n        <!-- ENDIF -->\n        <td class=\"{$SHIP_NAME_CLASS} c_l\">\n          {ship.NAME}\n        </td>\n        <td>\n          {ship.UNITS}\n        </td>\n        <td>\n          {ship.ATTACK}\n        </td>\n        <td>\n          {ship.SHIELD}\n        </td>\n        <td class=\"{$SHIP_NAME_CLASS}\">\n          {ship.SHIELD_LOST}\n        </td>\n        <td>\n          {ship.ARMOR}\n        </td>\n        <td class=\"<!-- IF ship.ARMOR_LOST -->{$SHIP_NAME_CLASS}<!-- ENDIF -->\">\n          {ship.ARMOR_LOST}\n        </td>\n        <td class=\"<!-- IF ship.UNITS_LOST -->{$SHIP_NAME_CLASS}<!-- ENDIF -->\">\n          <!-- IF ship.UNITS_BOOM -->({ship.UNITS_BOOM})&nbsp;<!-- ENDIF -->{ship.UNITS_LOST}\n        </td>\n      </tr>\n    <!-- END ship -->\n    <!-- ENDIF -->\n  <!-- END fleet -->\n</table>\n<!-- END round -->\n\n<!-- IF MICROTIME -->\n  <br />{L_ube_report_info_generate} {MICROTIME}\n<!-- ENDIF -->\n"
  },
  {
    "path": "design/templates/OpenGame/universe.tpl.html",
    "content": "<script type=\"text/javascript\" src=\"js/sn_universe.min.js?{C_var_db_update}\"></script>\n<script type=\"text/javascript\" src=\"js/sn_ajax_send_fleet.min.js?{C_var_db_update}\"></script>\n<!-- INCLUDE fleet_javascript -->\n\n<script type=\"text/javascript\"><!--\nvar uni_missile_planet = 0;\nvar uni_user_galaxy = \"{curPlanetG}\";\nvar uni_user_system = \"{curPlanetS}\";\nvar uni_user_planet = \"{curPlanetP}\";\nvar uni_user_planet_type = \"{curPlanetPT}\";\nvar uni_galaxy = {galaxy};\nvar uni_system = {system};\n\nvar user_id = {USER_ID};\nvar game_user_count = Math.intVal(\"{userCount}\");\nvar game_ally_count = Math.intVal(\"{ALLY_COUNT}\");\n\nvar opt_uni_avatar_user = '{opt_uni_avatar_user}';\nvar opt_uni_avatar_ally = '{opt_uni_avatar_ally}';\nvar opt_uni_tooltip_time = parseInt('{opt_uni_tooltip_time}') ? parseInt('{opt_uni_tooltip_time}') : 500;\n\nvar uni_phalanx = '{PLANET_PHALANX}';\nvar uni_spies = '{ACT_SPIO}';\nvar uni_death_stars = '{deathStars}';\nvar uni_missiles = parseInt('{MIPs}') ? parseInt('{MIPs}') : 0;\nvar PLANET_RECYCLERS = {PLANET_RECYCLERS};\n\nvar MT_MISSILE = {D_MT_MISSILE};\nvar MT_RECYCLE = {D_MT_RECYCLE};\nvar PT_DEBRIS = {D_PT_DEBRIS};\nvar PLAYER_OPTION_UNIVERSE_OLD = parseInt('{PLAYER_OPTION_UNIVERSE_OLD}') ? parseInt('{PLAYER_OPTION_UNIVERSE_OLD}') : 0;\n\njQuery.extend(language, {\n  sys_ships: '{L_uni_incoming_fleets}',\n  sys_planet: '{L_sys_planet_type[D_PT_PLANET]}',\n  sys_moon: '{L_sys_planet_type[D_PT_MOON]}',\n  sys_planet_short: '{L_sys_planet_type_sh[D_PT_PLANET]}',\n  sys_moon_short: '{L_sys_planet_type_sh[D_PT_MOON]}',\n});\n\nvar users = new Array();\n<!-- BEGIN users --><!-- IF users.ID -->\nusers[{users.ID}] = {\n  name: '{users.NAME_JS}',\n  rank: '{users.RANK}',\n  ally_id: parseInt('{users.ALLY_ID}') ? parseInt('{users.ALLY_ID}') : 0,\n  ally_tag: '{users.ALLY_TAG}',\n  ally_title: '{users.ALLY_TITLE}',\n  avatar: '{users.AVATAR}'\n};\n<!-- ENDIF --><!-- END users -->\n\nvar allies = new Array();\n<!-- BEGIN alliances --><!-- IF alliances.ID -->\nallies[{alliances.ID}] = {\n  'name': '{alliances.NAME_JS}',\n  'rank' : '{alliances.RANK}',\n  'members': '{alliances.MEMBERS}',\n  'url': '{alliances.URL}',\n  avatar: '{alliances.AVATAR}'\n};\n<!-- ENDIF --><!-- END alliances -->\n\nvar uni_row = new Array();\n<!-- BEGIN galaxyrow --><!-- IF galaxyrow.PLANET_NUM -->\nuni_row[{galaxyrow.PLANET_NUM}] = {\n  owner: '{galaxyrow.USER_ID}',\n  planet: '{galaxyrow.PLANET_NUM}',\n  planet_name: '{galaxyrow.PLANET_NAME_JS}',\n  planet_image: '{galaxyrow.PLANET_IMAGE}',\n  planet_fleet_id: '{galaxyrow.PLANET_FLEET_ID}',\n  planet_diameter: '{galaxyrow.PLANET_DIAMETER}',\n  planet_destroyed: '{galaxyrow.PLANET_DESTROYED}',\n\n  moon_name: '{galaxyrow.MOON_NAME_JS}',\n  moon_diameter: '{galaxyrow.MOON_DIAMETER}',\n  moon_image: '{galaxyrow.MOON_IMAGE}',\n  moon_fleet_id: '{galaxyrow.MOON_FLEET_ID}',\n\n  debris: '{galaxyrow.DEBRIS}',\n  debris_metal: '{galaxyrow.DEBRIS_METAL}',\n  debris_crystal: '{galaxyrow.DEBRIS_CRYSTAL}',\n  debris_reserved: '{galaxyrow.DEBRIS_RESERVED}',\n  debris_reserved_percent: '{galaxyrow.DEBRIS_RESERVED_PERCENT}',\n  debris_will_gather: '{galaxyrow.DEBRIS_WILL_GATHER}',\n  debris_will_gather_percent: '{galaxyrow.DEBRIS_WILL_GATHER_PERCENT}',\n  debris_gather_total: '{galaxyrow.DEBRIS_GATHER_TOTAL}',\n  debris_gather_total_percent: '{galaxyrow.DEBRIS_GATHER_TOTAL_PERCENT}',\n};\n<!-- ENDIF --><!-- END galaxyrow -->\n// --></script>\n\n<!-- IF UNIVERSE_SCAN_MODE -->\n  <!-- DEFINE $SCAN_CLASS = 'uni_scan' -->\n  <!-- DEFINE $SCAN_CLASS_NO_BORDER = 'no_border_image' -->\n<!-- ENDIF -->\n<br />\n<!-- INCLUDE universe_navigation -->\n\n<!-- IF PLAYER_OPTION_UNIVERSE_OLD || UNIVERSE_SCAN_MODE -->\n  <!-- INCLUDE universe_old -->\n<!-- ELSE -->\n  <!-- INCLUDE universe_new -->\n<!-- ENDIF -->\n\n<form id=\"uni_missile_form\" style=\"display: none;\" name=\"uni_missile_form\" method=POST>\n  <br>\n  <table style=\"border: 2em solid red\">\n    <tr>\n      <td class=c colspan=3>\n         <span class=\"fl\">{L_gm_launch} [{galaxy}:{system}:<span id=\"uni_missile_planet\">{planet}</span>]</span>\n         <span class=\"fr\">{L_gal_mis_rest}<span id=\"missile2\">{MIPs}</span></span>\n      </td>\n    </tr>\n    <tr>\n      <th class=c>{L_gal_mis_toLaunch} <input type=text value=\"{MIPs}\" name=\"SendMI\" id=\"SendMI\" size=7 maxlength=7 /></th>\n      <th class=c>{L_gm_target}\n        <select name=Target>\n          <option value=0 selected>{L_gm_all}</option>\n          <!-- BEGIN defense_active -->\n          <option value={defense_active.ID}>{defense_active.NAME}</option>\n          <!-- END defense_active -->\n        </select>\n      </th>\n      <th class=c>\n        <input type=\"button\" value=\"{L_gal_mis_launch}\" onclick=\"doit({D_MT_MISSILE}, uni_missile_planet, {D_PT_PLANET}, document.uni_missile_form.SendMI.value);jQuery('#uni_missile_form').hide();\">\n      </th>\n    </tr>\n  </table>\n</form>\n\n<span style=\"display: none\">\n  <span id=\"legend_template\">\n    <table class=\"legend_template no_border_image\">\n      <tr><th class=\"c_l\" colspan=2>{L_sys_planet}</th></tr>\n      <tr class=myplanet><td colspan=2>{L_uni_legend_myplanet}</td></tr>\n      <tr class=allymember><td colspan=2>{L_uni_legend_allyplanet}</td></tr>\n\n      <tr><th class=\"c_l\" colspan=2>{L_sys_player}</th></tr>\n      <tr><td>{L_sys_birthday}</td><td><span class=\"birthday\">&nbsp;</span></td></tr>\n      <tr><td colspan=\"2\"><span class=vacation>{L_Way_vacation}</span></td></tr>\n      <tr class=protected><td>{L_uni_protected_player}</td><td>{L_uni_protected_player_shortcut}</td></tr>\n      <tr class=noob><td>{L_Weak_player}</td><td>{L_weak_player_shortcut}</td></tr>\n      <tr class=strong><td>{L_Strong_player}</td><td>{L_strong_player_shortcut}</td></tr>\n      <tr class=\"banned\"><td colspan=\"2\">{L_Pendent_user}</td></tr>\n      <tr class=player_active><td>{L_Active}</td><td>{L_active_shortcut}</td></tr>\n      <tr class=inactive><td>{L_Inactive_7_days}</td><td>{L_inactif_7_shortcut}</td></tr>\n      <tr class=longinactive><td>{L_Inactive_28_days}</td><td>{L_inactif_28_shortcut}</td></tr>\n      <!-- IF SHOW_ADMIN -->\n      <tr class=admin><td>{L_user_level[3]}</td><td>{L_user_level_shortcut[3]}</td></tr>\n      <tr class=admin><td>{L_user_level[2]}</td><td>{L_user_level_shortcut[2]}</td></tr>\n      <tr class=admin><td>{L_user_level[1]}</td><td>{L_user_level_shortcut[1]}</td></tr>\n      <!-- ENDIF -->\n\n      <tr><th class=\"c_l\" colspan=2>{L_Actions}</th></tr>\n      <tr><td>{L_gl_espionner}</td><td><img src=\"design/images/icon_espionage.png\" border=0 style=\"height: 1.5em\" /> </td></tr>\n      <tr><td>{L_gl_mipattack}</td><td><img src=\"design/images/icon_missile.png\" border=0 style=\"height: 1.5em\" /></td></tr>\n      <tr><td>{L_stat_details}</td><td><img src=\"{I_menu_empire_emperor}\" border=0 style=\"height: 1.5em\" /></td></tr>\n      <tr><td>{L_gl_sendmess}</td><td><img src=\"design/images/icon_mail.gif\" border=0 style=\"height: 1.5em\" /></td></tr>\n      <tr><td>{L_gl_buddyreq}</td><td><img src=\"{I_icon_buddy}\" border=0 style=\"height: 1.5em\" /></td></tr>\n      <tr><td>{L_gl_stats}</td><td><img src=\"design/images/icon_statistics.png\" border=0  style=\"height: 1.5em\" /></td></tr>\n    </table>\n  </span>\n\n  <span id=\"planet_template\">\n    <table class=\"planet_template no_border_image\">\n      <tr>\n        <th class=\"c_c\" colspan=\"2\">\n          [{galaxy}:{system}:[PLANET_POS]]&nbsp;[PLANET_TYPE_TEXT_SHORT]&nbsp;[PLANET_NAME]\n        </th>\n      </tr>\n\n      <tr>\n        <td class=\"c_c\">\n          <div class=\"uni_popup_planet\">\n            <img src=\"{I_PLANET_IMAGE_SMALL}\" />\n            <div>&empty; [PLANET_DIAMETER]</div>\n          </div>\n        </td>\n\n        <td class=\"c_c\" planet_planet=\"[PLANET_POS]\" planet_type=\"[PLANET_TYPE]\">\n          <div class=\"owndeploy button_pseudo\" style=\"[HIDE_PLANET_RELOCATE]\" go=\"fleet\" mission=\"{D_MT_RELOCATE}\">{L_type_mission[D_MT_RELOCATE]}</div>\n          <div class=\"owntransport button_pseudo\" go=\"fleet\" mission=\"{D_MT_TRANSPORT}\">{L_type_mission[D_MT_TRANSPORT]}</div>\n          <div class=\"ownhold button_pseudo\" style=\"[HIDE_PLANET_HOLD]\" go=\"fleet\" mission=\"{D_MT_HOLD}\">{L_type_mission[D_MT_HOLD]}</div>\n          <div class=\"owndestroy button_pseudo\" style=\"[HIDE_PLANET_DESTROY]\" go=\"fleet\" mission=\"{D_MT_DESTROY}\">{L_type_mission[D_MT_DESTROY]}</div>\n          <div class=\"ownattack button_pseudo\" style=\"[HIDE_PLANET_ATTACK]\" go=\"fleet\" mission=\"{D_MT_ATTACK}\">{L_type_mission[D_MT_ATTACK]}</div>\n          <div class=\"ownespionage button_pseudo\" style=\"[HIDE_PLANET_SPY]\" onclick=\"doit({D_MT_SPY}, [PLANET_POS], [PLANET_TYPE], {ACT_SPIO});\">{L_type_mission[D_MT_SPY]}</div>\n          <div class=\"ownmissile button_pseudo\" style=\"[HIDE_PLANET_MISSILE]\">{L_type_mission[D_MT_MISSILE]}</div>\n          <div class=\"button_pseudo\" style=\"[HIDE_PLANET_PHALANX]\" go=\"phalanx\" target=\"_blank\">{L_gl_phalanx}</div>\n        </td>\n      </tr>\n    </table>\n\n    [FLEET_TABLE]\n  </span>\n\n  <span id=\"debris_template\">\n    <table width=100% class=\"debris_template no_border_image\">\n      <tr><th class=\"c_c\" colspan=\"4\">[{galaxy}:{system}:[CURRENT_PLANET]] {L_sys_planet_type[D_PT_DEBRIS]}</th></tr>\n      <tr><td class=\"c_c\" rowspan=\"7\"><img src=\"{I_debris}\" style=\"width: 7em; font-size: 1em;\" /></td></tr>\n\n      <tr>\n        <th class=\"c_l\">{L_gl_ressource}</th>\n        <th class=\"c_r\">[DEBRIS]</th>\n        <th class=\"c_r\">100%</th>\n      </tr>\n      <tr>\n        <td class=\"c_l\">{L_sys_metal}</td>\n        <td class=\"c_r\">[DEBRIS_METAL]</td>\n        <td class=\"c_r\">[DEBRIS_METAL_PERCENT]%</td>\n      </tr>\n      <tr>\n        <td class=\"c_l\">{L_sys_crystal}</td>\n        <td class=\"c_r\">[DEBRIS_CRYSTAL]</td>\n        <td class=\"c_r\">[DEBRIS_CRYSTAL_PERCENT]%</td>\n      </tr>\n\n      <tr>\n        <th class=\"c_l\">{L_uni_debris_recyclable}</th>\n        <th class=\"c_r\">[DEBRIS_GATHER_TOTAL]</th>\n        <th class=\"c_r\">[DEBRIS_GATHER_TOTAL_PERCENT]%</th>\n      </tr>\n      <tr>\n        <td class=\"c_l\">{L_uni_debris_incoming_recyclers}</td>\n        <td class=\"c_r\">[DEBRIS_RESERVED]</td>\n        <td class=\"c_r\">[DEBRIS_RESERVED_PERCENT]%</td>\n      </tr>\n      <tr>\n        <td class=\"c_l\">{L_uni_debris_on_planet}</td>\n        <td class=\"c_r\">[DEBRIS_WILL_GATHER]</td>\n        <td class=\"c_r\">[DEBRIS_WILL_GATHER_PERCENT]%</td>\n      </tr>\n\n      <tr class=\"link c_c\" style=\"[HIDE_RECYCLER_LINK]\" >\n        <th colspan=\"4\">\n          <div class=\"button_pseudo ownharvest\" planet_planet=\"[CURRENT_PLANET]\">\n            {L_uni_recyclers_send}\n          </div>\n        </th>\n      </tr>\n    </table>\n  </span>\n\n  <span id=\"user_template\">\n    <table class=\"user_template no_border_image\">\n      <tr>\n        <th class=\"c_c\" colspan=\"[USER_COLSPAN]\">\n          [USER_NAME]\n        </th>\n      </tr>\n      <tr>\n        <td class=\"c_c subheader\" style=\"[HIDE_USER_ALLY]\" colspan=\"[USER_COLSPAN]\">\n          [USER_ALLY_NAME]<br />\n          [USER_ALLY_TITLE]\n        </td>\n      </tr>\n      <tr>\n        <td class=\"c_c\" style=\"[HIDE_USER_AVATAR]\">\n          <div><img src=\"{D_SN_HTTP_AVATAR}/avatar_[USER_ID].png\" style=\"width: 9em;\" /></div>\n        </td>\n        <td class=\"c_c\" user_id=\"[USER_ID]\">\n          <div class=\"button_pseudo\" go=\"stat\" rank=\"[USER_RANK]\">\n            {L_gl_stats}: {L_Place} [USER_RANK]/{userCount}\n          </div>\n          <div class=\"button_pseudo\" go=\"imperator\">\n            {L_stat_details}\n          </div>\n          <div class=\"button_pseudo\" go=\"messages\" mode=\"write\">\n            {L_gl_sendmess}\n          </div>\n          <div class=\"button_pseudo\" go=\"buddy\">\n            {L_gl_buddyreq}\n          </div>\n        </td>\n      </tr>\n    </table>\n  </span>\n\n  <span id=\"ally_template\">\n    <table class=\"ally_template no_border_image\">\n      <tr>\n        <th class=\"c_c\" colspan=\"[ALLY_COLSPAN]\">\n          [ALLY_NAME]\n        </th>\n      </tr>\n      <tr>\n        <td class=\"c_c\" rowspan=\"4\" style=\"[HIDE_ALLY_AVATAR]\">\n          <img src=\"{D_SN_HTTP_AVATAR}/ally_[ALLY_ID].png\" style=\"width: 9em;\" />\n        </td>\n        <td class=\"c_c\">\n          {L_gal_sys_members}&nbsp;[ALLY_MEMBERS]\n        </td>\n      </tr>\n      <tr>\n        <td class=\"c_c\" ally_id=\"[ALLY_ID]\">\n          <div class=\"button_pseudo\" go=\"stat\" rank=\"[ALLY_RANK]\" who=\"2\">\n            {L_gl_stats}: {L_Place}&nbsp;[ALLY_RANK]/{ALLY_COUNT}\n          </div>\n          <div class=\"button_pseudo\" go=\"alliance\" mode=\"ainfo\">\n            {L_gl_ally_internal}\n          </div>\n          <a class=\"link\" style=\"[HIDE_ALLY_URL]\" href=\"[ALLY_URL]\" target=\"_new\">{L_gl_ally_web}</a>\n        </td>\n      </tr>\n    </table>\n  </span>\n\n  <span id=\"message_template\">\n    <table>\n      <tr>\n        <td class=\"c_c [CLASS]\" style=\"padding: 0.5em 1em;\">\n          [MESSAGE]\n        </td>\n      </tr>\n    </table>\n  </span>\n</span>\n\n<!-- IF ! UNIVERSE_SCAN_MODE -->\n<!-- INCLUDE page_hint -->\n<!-- ENDIF -->\n"
  },
  {
    "path": "design/templates/OpenGame/universe_navigation.tpl.html",
    "content": "<form action=\"galaxy.php?mode=1\" method=\"post\" id=\"galaxy_form\">\n  <input type=\"hidden\" id=\"auto\" value=\"dr\">\n  <input type=\"hidden\" id=\"universe_scan_mode\" name=\"scan\" value=\"{UNIVERSE_SCAN_MODE}\">\n\n  <div id=\"uni_nav_scan_wrapper\">\n    <!-- IF UNIVERSE_SCAN_MODE -->\n    <span class=\"button_pseudo uni_scan\" id=\"universeScanStop\">{L_uni_scan_stop}</span>\n    <!-- ELSE -->\n    <span class=\"button_pseudo uni_scan\" id=\"universeScanStart\">{L_uni_scan_start}</span>\n    <!-- ENDIF -->\n  </div>\n\n  <div class=\"uni_nav_wrapper header\">\n    <div class=\"uni_nav_wrapper2 header\">\n      <div class=\"uni_nav_child\">\n        <label for=\"galaxy\">{L_Galaxy}</label>\n        <span class=\"button_pseudo\" id=\"galaxyLeft\">&lt;</span>\n        <input name=\"galaxy\" id=\"galaxy\" value=\"{galaxy}\" size=\"5\" maxlength=\"3\" tabindex=\"1\" type=\"text\"/>\n        <span class=\"button_pseudo\" id=\"galaxyRight\">&gt;</span>\n      </div>\n\n      <div class=\"uni_nav_child\">\n        <label for=\"system\">{L_Solar_system}</label>\n        <span class=\"button_pseudo\" id=\"systemLeft\">&lt;</span>\n        <input name=\"system\" id=\"system\" value=\"{system}\" size=\"5\" maxlength=\"3\" tabindex=\"2\" type=\"text\"/>\n        <span class=\"button_pseudo\" id=\"systemRight\">&gt;</span>\n      </div>\n    </div>\n\n    <div class=\"uni_nav_child\">\n      <input value=\"{L_sys_goto}\" type=\"submit\"/>\n    </div>\n  </div>\n</form>\n"
  },
  {
    "path": "design/templates/OpenGame/universe_new.tpl.html",
    "content": "<!-- IF ACT_SPY || ACT_MISSILE || ACT_WRITE || ACT_STATISTICS || ACT_INFO || ACT_FRIEND -->\n  <!-- DEFINE $ACTION_PRESENT = '1' -->\n<!-- ENDIF -->\n\n<table id=\"universe_main\" class=\"universe_main_new\"><tbody>\n<tr><th class=\"c_l\" colspan=\"{COL_SPAN_NEW}\">\n  {L_sys_galaxy}&nbsp;[{galaxy}]&nbsp;<span class=\"ok\">{GALAXY_NAME}</span>&nbsp;<span class=\"button_pseudo\" go=\"galaxy\" planet_system=\"0\" mode=\"name\"><!-- IF GALAXY_NAME -->{L_uni_rename}<!-- ELSE -->{L_uni_to_name}<!-- ENDIF --></span>\n  <span class=\"fr notice link_action button_pseudo\" id=\"universe_legend\">{L_Legend}</span>\n</th></tr>\n\n<tr><th class=\"c_l\" colspan=\"{COL_SPAN_NEW}\">\n  <span>{L_sys_system}&nbsp;[{galaxy}:{system}] <span class=\"ok\">{SYSTEM_NAME}</span>&nbsp;<a class=\"link button_pseudo\" href=\"galaxy.php?mode=name&galaxy={galaxy}&system={system}\"><!-- IF SYSTEM_NAME -->{L_uni_rename}<!-- ELSE -->{L_uni_to_name}<!-- ENDIF --></a></span>\n  <div style=\"height: 100%; display: inline-block; vertical-align: middle;\"><!-- IF planets -->{L_gal_planets} {planets}<!-- ELSE -->{L_gal_planetNone}<!-- ENDIF --></div>\n</th></tr>\n\n<tr class=\"c_c\">\n  <th>{L_Name}</th>\n  <th>\n    {L_Player}<br />\n    {L_sys_alliance}\n  </th>\n  <!-- IF $ACTION_PRESENT -->\n  <th>{L_Actions}</th>\n  <!-- ENDIF -->\n</tr>\n\n<!-- BEGIN galaxyrow -->\n\n<!-- IF galaxyrow.USER_ID && USER_ID != galaxyrow.USER_ID -->\n  <!-- DEFINE $DIFFERENT_USER = 1 -->\n<!-- ELSE -->\n  <!-- DEFINE $DIFFERENT_USER = 0 -->\n<!-- ENDIF -->\n\n<!-- IF     galaxyrow.DEBRIS >= 1000000000000 -->\n  <!-- DEFINE $DEBRIS_BG_CLASS = 'error_bg' -->\n<!-- ELSEIF galaxyrow.DEBRIS >= 1000000000 -->\n  <!-- DEFINE $DEBRIS_BG_CLASS = 'warning_bg' -->\n<!-- ELSEIF galaxyrow.DEBRIS >= 1000000 -->\n  <!-- DEFINE $DEBRIS_BG_CLASS = 'notice_bg' -->\n<!-- ELSEIF galaxyrow.DEBRIS >= 1000 -->\n  <!-- DEFINE $DEBRIS_BG_CLASS = 'ok_bg' -->\n<!-- ELSE -->\n  <!-- DEFINE $DEBRIS_BG_CLASS = '' -->\n<!-- ENDIF -->\n\n\n<tr class=\"uni_planet_row\" user_id=\"{galaxyrow.USER_ID}\" ally_id=\"{galaxyrow.ALLY_ID}\" planet_pos=\"{galaxyrow.PLANET_NUM}\">\n  <!-- IF galaxyrow.PLANET_ID -->\n    <td class=\"c_c\" nowrap>\n      <div class=\"uni_show_planet uni_show_planet_new\" planet_type=\"{galaxyrow.PLANET_TYPE}\" id=\"planet_{galaxyrow.PLANET_ID}\" style=\"position: relative;\">\n        <img src=\"{I_s_[galaxyrow.PLANET_IMAGE]}\" />\n        <!-- IF galaxyrow.PLANET_FLEET_ID -->\n          <img class=\"alpha50 uni_own_fleet\" src=\"{D_SN_ROOT_VIRTUAL}design/images/icon_fleet_own.png\" />\n        <!-- ENDIF -->\n        <div class=\"uni_object_type\">{L_sys_planet_type[D_PT_PLANET]}</div>\n      </div>\n\n      <!-- IF galaxyrow.MOON_DIAMETER -->\n      <div class=\"uni_show_planet uni_show_planet_new\" planet_type=\"{D_PT_MOON}\">\n        <img src=\"{I_s_[galaxyrow.MOON_IMAGE]}\" />\n        <!-- IF galaxyrow.MOON_FLEET_ID -->\n          <img class=\"alpha50 uni_own_fleet\" src=\"{D_SN_ROOT_VIRTUAL}design/images/icon_fleet_own.png\" />\n        <!-- ENDIF -->\n        <div class=\"uni_object_type\">{L_sys_planet_type[D_PT_MOON]}</div>\n        <!--<div style=\"position: absolute; left: 0; top: 0; width: 100%;\">Луна</div>-->\n      </div>\n      <!-- ENDIF -->\n\n      <!-- IF galaxyrow.DEBRIS -->\n      <div class=\"uni_show_debris uni_show_debris_new {$DEBRIS_BG_CLASS}\">\n        <img src=\"{I_s_debris}\" />\n        <!-- IF ! galaxyrow.DEBRIS_RESERVED_PERCENT -->\n        <!-- DEFINE $DEBRIS_FONT_CLASS = 'error' -->\n        <!-- ELSEIF galaxyrow.DEBRIS_RESERVED_PERCENT == 100 -->\n        <!-- DEFINE $DEBRIS_FONT_CLASS = 'ok' -->\n        <!-- ELSEIF galaxyrow.DEBRIS_GATHER_TOTAL_PERCENT == 100 -->\n        <!-- DEFINE $DEBRIS_FONT_CLASS = 'notice' -->\n        <!-- ELSE -->\n        <!-- DEFINE $DEBRIS_FONT_CLASS = 'warning' -->\n        <!-- ENDIF -->\n        <span class=\"icon_alpha uni_debris_percent {$DEBRIS_FONT_CLASS}\">{galaxyrow.DEBRIS_RESERVED_PERCENT}%</span>\n        <div class=\"uni_object_type\">{L_uni_debris}</div>\n        <!--<div style=\"position: absolute; left: 0; top: 0; width: 100%;\">Обломки</div>-->\n      </div>\n      <!-- ENDIF -->\n\n      <div class=\"g_galaxy_row\">\n        <!-- IF galaxyrow.PLANET_DESTROYED -->\n         [{galaxy}:{system}:{galaxyrow.PLANET_NUM}] {L_gl_destroyedplanet}\n        <!-- ELSE -->\n          <!-- DEFINE $PLANET_ACTIVITY = '' -->\n          <!-- IF ! $DIFFERENT_USER -->\n            <!-- DEFINE $PLANET_CLASS = 'myplanet' -->\n          <!-- ELSE -->\n            <!-- IF ALLY_ID == galaxyrow.ALLY_ID -->\n              <!-- DEFINE $PLANET_CLASS = 'allymember' -->\n            <!-- ELSE -->\n              <!-- DEFINE $PLANET_CLASS = '' -->\n            <!-- ENDIF -->\n\n            <!-- IF galaxyrow.PLANET_ACTIVITY < 15 -->\n              <!-- DEFINE $PLANET_ACTIVITY = '<15' -->\n            <!-- ELSEIF galaxyrow.PLANET_ACTIVITY < 60 -->\n              <!-- DEFINE $PLANET_ACTIVITY = '{galaxyrow.PLANET_ACTIVITY}' -->\n            <!-- ELSE -->\n              <!-- DEFINE $PLANET_ACTIVITY = '' -->\n            <!-- ENDIF -->\n          <!-- ENDIF -->\n\n          <!-- IF PLANET_PHALANX && $DIFFERENT_USER -->\n            <!-- DEFINE $PLANET_CLASS_BUTTON = ' button_pseudo' -->\n            <!-- DEFINE $PLANET_GO = ' go=\"phalanx\" target=\"_blank\"' -->\n          <!-- ELSE -->\n            <!-- DEFINE $PLANET_CLASS_BUTTON = '' -->\n            <!-- DEFINE $PLANET_GO = '' -->\n          <!-- ENDIF -->\n          <span class=\"{$PLANET_CLASS}{$PLANET_CLASS_BUTTON}\"{$PLANET_GO} planet_type=\"{galaxyrow.PLANET_TYPE}\" planet_planet=\"{galaxyrow.PLANET_NUM}\">\n            <!-- IF galaxyrow.IS_CAPITAL -->&#9813;<!-- ENDIF -->\n            [{galaxy}:{system}:{galaxyrow.PLANET_NUM}] {galaxyrow.PLANET_NAME}<!-- IF $PLANET_ACTIVITY -->&nbsp;({$PLANET_ACTIVITY}&nbsp;{L_sys_min_short})<!-- ENDIF -->\n          </span>\n        <!-- ENDIF -->\n      </div>\n    </td>\n\n    <!-- IF galaxyrow.USER_ACTIVITY >= 28 -->\n      <!-- DEFINE $FONT_TYPE = 'longinactive' -->\n    <!-- ELSEIF galaxyrow.USER_ACTIVITY >= 7 -->\n      <!-- DEFINE $FONT_TYPE = 'inactive' -->\n    <!-- ELSE -->\n      <!-- DEFINE $FONT_TYPE = 'player_active' -->\n    <!-- ENDIF -->\n\n    <!-- IF galaxyrow.USER_BANNED -->\n      <!-- DEFINE $USER_CLASS_BANNED = 'banned' -->\n    <!-- ELSE -->\n      <!-- DEFINE $USER_CLASS_BANNED = '' -->\n    <!-- ENDIF -->\n\n    <!-- IF galaxyrow.USER_PROTECTED -->\n      <!-- DEFINE $USER_CLASS = 'protected' -->\n    <!-- ELSEIF galaxyrow.USER_NOOB -->\n      <!-- DEFINE $USER_CLASS = 'noob' -->\n    <!-- ELSEIF galaxyrow.USER_STRONG -->\n      <!-- DEFINE $USER_CLASS = 'strong' -->\n    <!-- ELSE -->\n      <!-- DEFINE $USER_CLASS = '' -->\n    <!-- ENDIF -->\n\n    <!-- IF galaxyrow.USER_BIRTHDAY -->\n      <!-- DEFINE $USER_CLASS_BIRTHDAY = 'birthday' -->\n    <!-- ELSE -->\n      <!-- DEFINE $USER_CLASS_BIRTHDAY = '' -->\n    <!-- ENDIF -->\n\n    <th width=150 align=center nowrap>\n      <!-- IF galaxyrow.USER_ID -->\n      <div class=\"button_pseudo uni_show_user {$USER_CLASS_BIRTHDAY} {$USER_CLASS_BANNED}\">\n        <span class=\"{$USER_CLASS} {$FONT_TYPE}\">{galaxyrow.USER_NAME}</span>&nbsp;(<!-- IF SHOW_ADMIN && galaxyrow.USER_AUTH && galaxyrow.USER_ADMIN --><span class=\"admin\">{galaxyrow.USER_ADMIN}</span><!-- ENDIF --><!-- IF galaxyrow.USER_BANNED --><span class=\"banned\">{L_banned_shortcut}</span><!-- ENDIF --><!-- IF galaxyrow.USER_ACTIVITY >= 28 --><span class=\"longinactive\">{L_inactif_28_shortcut}</span><!-- ELSEIF galaxyrow.USER_ACTIVITY >= 7 --><span class=\"inactive\">{L_inactif_7_shortcut}</span><!-- ELSE -->{L_active_shortcut}<!-- ENDIF --><!-- IF galaxyrow.USER_PROTECTED --><span class=\"protected\">{L_uni_protected_player_shortcut}</span><!-- ENDIF --><!-- IF galaxyrow.USER_NOOB --><span class=\"noob\">{L_weak_player_shortcut}</span><!-- ENDIF --><!-- IF galaxyrow.USER_STRONG --><span class=\"strong\">{L_strong_player_shortcut}</span><!-- ENDIF -->)\n      </div>\n\n      <!-- IF galaxyrow.ALLY_ID -->\n        <div class=\"button_pseudo uni_show_ally <!-- IF ALLY_ID == galaxyrow.ALLY_ID --> allymember<!-- ENDIF -->\">[{galaxyrow.ALLY_TAG}]</div>\n      <!-- ENDIF -->\n      <!-- ELSE -->\n      &nbsp;\n      <!-- ENDIF -->\n    </th>\n  <!-- IF $ACTION_PRESENT -->\n  <td>\n    <!-- IF $DIFFERENT_USER -->\n      <!-- IF ACT_SPY && (! galaxyrow.USER_NOOB || galaxyrow.USER_ATTACKABLE) -->\n        <a class=\"button_pseudo\" alt=\"{L_gl_espionner}\" title=\"{L_gl_espionner}\" onclick=\"doit({D_MT_SPY}, {galaxyrow.PLANET_NUM}, 1, {ACT_SPIO});\">\n          <img src=\"{D_SN_ROOT_VIRTUAL}design/images/icon_espionage.png\" style=\"height: 1.5em\" />\n        </a>\n      <!-- ENDIF -->\n      <!-- IF ACT_MISSILE && (! galaxyrow.USER_NOOB || galaxyrow.USER_ATTACKABLE) -->\n        <a class=\"button_pseudo missile_attack_prepare\" planet_planet=\"{galaxyrow.PLANET_NUM}\" alt=\"{L_gl_mipattack}\" title=\"{L_gl_mipattack}\" >\n          <img src=\"{D_SN_ROOT_VIRTUAL}design/images/icon_missile.png\" style=\"height: 1.5em\" />\n        </a>\n      <!-- ENDIF -->\n      <br />\n      <!-- IF ACT_WRITE -->\n        <a class=\"button_pseudo\" href=\"messages.php?mode=write&id={galaxyrow.USER_ID}\"><img src=\"{D_SN_ROOT_VIRTUAL}design/images/icon_mail.gif\" alt=\"{L_gl_sendmess}\" title=\"{L_gl_sendmess}\" style=\"height: 1.5em\" /></a>\n      <!-- ENDIF -->\n      <!-- IF ACT_INFO -->\n        <a class=\"button_pseudo\" href=\"index.php?page=imperator&int_user_id={galaxyrow.USER_ID}\"><img src=\"{I_menu_empire_emperor}\" alt=\"{L_stat_details}\" title=\"{L_stat_details}\" style=\"height: 1.5em\" /></a>\n      <!-- ENDIF -->\n    <!-- ELSE -->\n      &nbsp;\n    <!-- ENDIF -->\n  </td>\n  <!-- ENDIF -->\n\n  <!-- ELSE -->\n    <td colspan=\"{COL_SPAN_NEW}\" class=\"c_c\">\n      <!-- IF ! PLAYER_OPTION_UNIVERSE_DISABLE_COLONIZE -->\n      <span class=\"button_pseudo\" go=\"fleet\" planet_planet=\"{galaxyrow.PLANET_NUM}\" mission=\"{D_MT_COLONIZE}\">\n        {L_uni_colonize} {galaxyrow.PLANET_NUM}\n      </span>\n      <!-- ELSE -->\n        [{galaxy}:{system}:{galaxyrow.PLANET_NUM}]\n      <!-- ENDIF -->\n    </td>\n  <!-- ENDIF -->\n</tr>\n<!-- END galaxyrow -->\n<tr>\n  <th colspan=\"{COL_SPAN_NEW}\" class=\"subheader\">\n    <span class=\"button_pseudo ownexpedition\" go=\"fleet\" planet_planet=\"{PLANET_EXPEDITION}\" mission=\"{D_MT_EXPLORE}\">\n      {L_gf_unknowsp}\n    </span>\n  </th>\n</tr>\n<tr>\n  <td colspan=\"{COL_SPAN_NEW}\">\n    <table width=\"100%\" class=\"no_border_image\">\n      <tr>\n        <td class=c width=\"33%\">\n          <span id=\"missile\">{MIPs}</span> {L_gf_mi_title}\n        </td>\n        <td class=c width=\"33%\">\n          <span id=\"flt_recyclers\">{PLANET_RECYCLERS_TEXT}</span> {L_gf_rc_title}\n        </td>\n        <td class=c>\n          <span id=\"flt_spies\">{SPs}</span> {L_gf_sp_title}\n        </td>\n      </tr>\n    </table>\n  </td>\n</tr>\n<tr style=\"display: none;\" id=\"fleetstatusrow\"><th class=c colspan=\"{COL_SPAN_NEW}\"><table width=\"100%\" id=\"fleetstatustable\" class=\"no_border_image\"></table></th></tr>\n</tbody></table>\n"
  },
  {
    "path": "design/templates/OpenGame/universe_old.tpl.html",
    "content": "<table width=595 id=\"universe_main\" class=\"{$SCAN_CLASS_NO_BORDER}\"><tbody>\n<!-- IF ! UNIVERSE_SCAN_MODE -->\n<tr><th class=\"c_l\" colspan=\"{COL_SPAN}\">\n  {L_sys_galaxy}&nbsp;[{galaxy}]&nbsp;<span class=\"ok\">{GALAXY_NAME}</span>&nbsp;<span class=\"button_pseudo\" go=\"galaxy\" planet_system=\"0\" mode=\"name\"><!-- IF GALAXY_NAME -->{L_uni_rename}<!-- ELSE -->{L_uni_to_name}<!-- ENDIF --></span>\n  <span class=\"fr notice link_action button_pseudo\" id=\"universe_legend\">{L_Legend}</span>\n</th></tr>\n\n<tr><th class=\"c_l\" colspan=\"{COL_SPAN}\">\n  <span>{L_sys_system}&nbsp;[{galaxy}:{system}] <span class=\"ok\">{SYSTEM_NAME}</span>&nbsp;<a class=\"link button_pseudo\" href=\"galaxy.php?mode=name&galaxy={galaxy}&system={system}\"><!-- IF SYSTEM_NAME -->{L_uni_rename}<!-- ELSE -->{L_uni_to_name}<!-- ENDIF --></a></span>\n  <div style=\"height: 100%; display: inline-block; vertical-align: middle;\"><!-- IF planets -->{L_gal_planets} {planets}<!-- ELSE -->{L_gal_planetNone}<!-- ENDIF --></div>\n</th></tr>\n<!-- ENDIF -->\n\n<tr class=\"c_c\">\n  <th>{L_Pos}</th>\n  <th>{L_Planet}</th>\n  <th>{L_Name}</th>\n  <th>{L_Moon}</th>\n  <th>{L_uni_debris}</th>\n  <th>{L_Player}</th>\n  <th>{L_sys_alliance}</th>\n  <!-- IF ACT_SPY -->\n  <th><img src=\"{D_SN_ROOT_VIRTUAL}design/images/icon_espionage.png\" alt=\"{L_gl_espionner}\" title=\"{L_gl_espionner}\" style=\"height: 1.5em\" /></th>\n  <!-- ENDIF -->\n  <!-- IF ACT_MISSILE -->\n  <th><img src=\"{D_SN_ROOT_VIRTUAL}design/images/icon_missile.png\" alt=\"{L_gl_mipattack}\" title=\"{L_gl_mipattack}\" style=\"height: 1.5em\" /></th>\n  <!-- ENDIF -->\n\n  <!-- IF ACT_WRITE -->\n  <th><img src=\"{D_SN_ROOT_VIRTUAL}design/images/icon_mail.gif\" alt=\"{L_gl_sendmess}\" title=\"{L_gl_sendmess}\" style=\"height: 1.5em\" /></th>\n  <!-- ENDIF -->\n\n  <!-- IF ACT_INFO -->\n  <th><img src=\"{I_menu_empire_emperor}\" alt=\"{L_stat_details}\" title=\"{L_stat_details}\" style=\"height: 1.5em\" /></th>\n  <!-- ENDIF -->\n</tr>\n\n\n<!-- BEGIN galaxyrow -->\n\n<!-- IF galaxyrow.USER_ID && USER_ID != galaxyrow.USER_ID -->\n  <!-- DEFINE $DIFFERENT_USER = 1 -->\n<!-- ELSE -->\n  <!-- DEFINE $DIFFERENT_USER = 0 -->\n<!-- ENDIF -->\n\n<tr class=\"uni_planet_row {$SCAN_CLASS}\" user_id=\"{galaxyrow.USER_ID}\" ally_id=\"{galaxyrow.ALLY_ID}\" planet_pos=\"{galaxyrow.PLANET_NUM}\">\n  <!-- IF galaxyrow.PLANET_ID -->\n    <th width=30>\n      {galaxyrow.PLANET_NUM}\n    </th>\n    <th class=\"uni_show_planet\" planet_type=\"{galaxyrow.PLANET_TYPE}\" id=\"planet_{galaxyrow.PLANET_ID}\">\n      <div>\n        <img src=\"{I_s_[galaxyrow.PLANET_IMAGE]}\" />\n        <!-- IF galaxyrow.PLANET_FLEET_ID -->\n          <img class=\"alpha25 uni_own_fleet\" src=\"{D_SN_ROOT_VIRTUAL}design/images/icon_fleet_own.png\" />\n        <!-- ENDIF -->\n      </div>\n    </th>\n    <th nowrap>\n      <div class=\"g_galaxy_row\">\n        <!-- IF galaxyrow.PLANET_DESTROYED -->\n          {L_gl_destroyedplanet}\n        <!-- ELSE -->\n          <!-- DEFINE $PLANET_ACTIVITY = '' -->\n          <!-- IF ! $DIFFERENT_USER -->\n            <!-- DEFINE $PLANET_CLASS = 'myplanet' -->\n          <!-- ELSE -->\n            <!-- IF ALLY_ID == galaxyrow.ALLY_ID -->\n              <!-- DEFINE $PLANET_CLASS = 'allymember' -->\n            <!-- ELSE -->\n              <!-- DEFINE $PLANET_CLASS = '' -->\n            <!-- ENDIF -->\n\n            <!-- IF galaxyrow.PLANET_ACTIVITY < 15 -->\n              <!-- DEFINE $PLANET_ACTIVITY = '<15' -->\n            <!-- ELSEIF galaxyrow.PLANET_ACTIVITY < 60 -->\n              <!-- DEFINE $PLANET_ACTIVITY = '{galaxyrow.PLANET_ACTIVITY}' -->\n            <!-- ELSE -->\n              <!-- DEFINE $PLANET_ACTIVITY = '' -->\n            <!-- ENDIF -->\n          <!-- ENDIF -->\n\n          <!-- IF PLANET_PHALANX && $DIFFERENT_USER -->\n            <!-- DEFINE $PLANET_CLASS_BUTTON = ' button_pseudo' -->\n            <!-- DEFINE $PLANET_GO = ' go=\"phalanx\" target=\"_blank\"' -->\n          <!-- ELSE -->\n            <!-- DEFINE $PLANET_CLASS_BUTTON = '' -->\n            <!-- DEFINE $PLANET_GO = '' -->\n          <!-- ENDIF -->\n          <span class=\"{$PLANET_CLASS}{$PLANET_CLASS_BUTTON}\"{$PLANET_GO} planet_type=\"{galaxyrow.PLANET_TYPE}\" planet_planet=\"{galaxyrow.PLANET_NUM}\">\n            <!-- IF galaxyrow.IS_CAPITAL -->&#9813;<!-- ENDIF -->\n            {galaxyrow.PLANET_NAME}<!-- IF $PLANET_ACTIVITY -->&nbsp;({$PLANET_ACTIVITY}&nbsp;{L_sys_min_short})<!-- ENDIF -->\n          </span>\n        <!-- ENDIF -->\n      </div>\n    </th>\n    <th class=\"uni_show_planet\" planet_type=\"{D_PT_MOON}\">\n      <!-- IF galaxyrow.MOON_DIAMETER -->\n      <div>\n        <img src=\"{I_s_[galaxyrow.MOON_IMAGE]}\" />\n        <!-- IF galaxyrow.MOON_FLEET_ID -->\n        <img class=\"alpha25 uni_own_fleet\" src=\"{D_SN_ROOT_VIRTUAL}design/images/icon_fleet_own.png\" />\n        <!-- ENDIF -->\n      </div>\n      <!-- ELSE -->\n      &nbsp;\n      <!-- ENDIF -->\n    </th>\n\n    <!-- IF     galaxyrow.DEBRIS >= 1000000000000 -->\n      <!-- DEFINE $DEBRIS_BG_CLASS = 'error_bg' -->\n    <!-- ELSEIF galaxyrow.DEBRIS >= 1000000000 -->\n      <!-- DEFINE $DEBRIS_BG_CLASS = 'warning_bg' -->\n    <!-- ELSEIF galaxyrow.DEBRIS >= 1000000 -->\n      <!-- DEFINE $DEBRIS_BG_CLASS = 'notice_bg' -->\n    <!-- ELSEIF galaxyrow.DEBRIS >= 1000 -->\n      <!-- DEFINE $DEBRIS_BG_CLASS = 'ok_bg' -->\n    <!-- ELSE -->\n      <!-- DEFINE $DEBRIS_BG_CLASS = '' -->\n    <!-- ENDIF -->\n\n    <td class=\"c_c uni_show_debris {$DEBRIS_BG_CLASS}\">\n      <!-- IF galaxyrow.DEBRIS -->\n      <div>\n        <img src=\"{I_s_debris}\" />\n        <!-- IF ! galaxyrow.DEBRIS_RESERVED_PERCENT -->\n          <!-- DEFINE $DEBRIS_FONT_CLASS = 'error' -->\n        <!-- ELSEIF galaxyrow.DEBRIS_RESERVED_PERCENT == 100 -->\n          <!-- DEFINE $DEBRIS_FONT_CLASS = 'ok' -->\n        <!-- ELSEIF galaxyrow.DEBRIS_GATHER_TOTAL_PERCENT == 100 -->\n          <!-- DEFINE $DEBRIS_FONT_CLASS = 'notice' -->\n        <!-- ELSE -->\n          <!-- DEFINE $DEBRIS_FONT_CLASS = 'warning' -->\n        <!-- ENDIF -->\n        <span class=\"icon_alpha uni_debris_percent {$DEBRIS_FONT_CLASS}\">{galaxyrow.DEBRIS_RESERVED_PERCENT}%</span>\n      </div>\n      <!-- ELSE -->\n      &nbsp;\n      <!-- ENDIF -->\n    </td>\n\n    <!-- IF galaxyrow.USER_ACTIVITY >= 28 -->\n      <!-- DEFINE $FONT_TYPE = 'longinactive' -->\n    <!-- ELSEIF galaxyrow.USER_ACTIVITY >= 7 -->\n      <!-- DEFINE $FONT_TYPE = 'inactive' -->\n    <!-- ELSE -->\n      <!-- DEFINE $FONT_TYPE = 'player_active' -->\n    <!-- ENDIF -->\n\n    <!-- IF galaxyrow.USER_BANNED -->\n      <!-- DEFINE $USER_CLASS_BANNED = 'banned' -->\n    <!-- ELSE -->\n      <!-- DEFINE $USER_CLASS_BANNED = '' -->\n    <!-- ENDIF -->\n\n    <!-- IF galaxyrow.USER_PROTECTED -->\n      <!-- DEFINE $USER_CLASS = 'protected' -->\n    <!-- ELSEIF galaxyrow.USER_NOOB -->\n      <!-- DEFINE $USER_CLASS = 'noob' -->\n    <!-- ELSEIF galaxyrow.USER_STRONG -->\n      <!-- DEFINE $USER_CLASS = 'strong' -->\n    <!-- ELSE -->\n      <!-- DEFINE $USER_CLASS = '' -->\n    <!-- ENDIF -->\n\n    <!-- IF galaxyrow.USER_BIRTHDAY -->\n      <!-- DEFINE $USER_CLASS_BIRTHDAY = 'birthday' -->\n    <!-- ELSE -->\n      <!-- DEFINE $USER_CLASS_BIRTHDAY = '' -->\n    <!-- ENDIF -->\n\n    <!-- IF $DIFFERENT_USER -->\n      <!-- DEFINE $USER_CLASS_ACTION = 'link_action' -->\n    <!-- ELSE -->\n      <!-- DEFINE $USER_CLASS_ACTION = '' -->\n    <!-- ENDIF -->\n\n    <th width=150 align=center nowrap class=\"uni_show_user\">\n      <!-- IF galaxyrow.USER_ID -->\n      <span class=\"{$USER_CLASS_ACTION} {$USER_CLASS_BIRTHDAY} {$USER_CLASS_BANNED}\">\n        <span class=\"{$USER_CLASS} {$FONT_TYPE}\">{galaxyrow.USER_NAME}</span>&nbsp;(<!-- IF SHOW_ADMIN && galaxyrow.USER_AUTH && galaxyrow.USER_ADMIN --><span class=\"admin\">{galaxyrow.USER_ADMIN}</span><!-- ENDIF --><!-- IF galaxyrow.USER_BANNED --><span class=\"banned\">{L_banned_shortcut}</span><!-- ENDIF --><!-- IF galaxyrow.USER_ACTIVITY >= 28 --><span class=\"longinactive\">{L_inactif_28_shortcut}</span><!-- ELSEIF galaxyrow.USER_ACTIVITY >= 7 --><span class=\"inactive\">{L_inactif_7_shortcut}</span><!-- ELSE -->{L_active_shortcut}<!-- ENDIF --><!-- IF galaxyrow.USER_PROTECTED --><span class=\"protected\">{L_uni_protected_player_shortcut}</span><!-- ENDIF --><!-- IF galaxyrow.USER_NOOB --><span class=\"noob\">{L_weak_player_shortcut}</span><!-- ENDIF --><!-- IF galaxyrow.USER_STRONG --><span class=\"strong\">{L_strong_player_shortcut}</span><!-- ENDIF -->)\n      </span>\n      <!-- ELSE -->\n      &nbsp;\n      <!-- ENDIF -->\n    </th>\n\n    <th width=80 nowrap class=\"uni_show_ally\">\n      <!-- IF galaxyrow.ALLY_ID -->\n      <span class=\"link_action<!-- IF ALLY_ID == galaxyrow.ALLY_ID --> allymember<!-- ENDIF -->\">[{galaxyrow.ALLY_TAG}]</span>\n      <!-- ELSE -->\n      &nbsp;\n      <!-- ENDIF -->\n    </th>\n    <!-- IF ACT_SPY -->\n    <td class=\"c_c\">\n      <!-- IF $DIFFERENT_USER && (! galaxyrow.USER_NOOB || galaxyrow.USER_ATTACKABLE) -->\n        <a class=\"button_pseudo {$SCAN_CLASS}\" alt=\"{L_gl_espionner}\" title=\"{L_gl_espionner}\" onclick=\"doit({D_MT_SPY}, {galaxyrow.PLANET_NUM}, 1, {ACT_SPIO});\"><img src=\"{D_SN_ROOT_VIRTUAL}design/images/icon_espionage.png\" style=\"height: 1.5em\" /></a>\n      <!-- ENDIF -->\n    </td>\n    <!-- ENDIF -->\n    <!-- IF ACT_MISSILE -->\n    <td class=\"c_c\">\n      <!-- IF $DIFFERENT_USER && (! galaxyrow.USER_NOOB || galaxyrow.USER_ATTACKABLE) -->\n      <a class=\"button_pseudo {$SCAN_CLASS} missile_attack_prepare\" planet_planet=\"{galaxyrow.PLANET_NUM}\" alt=\"{L_gl_mipattack}\" title=\"{L_gl_mipattack}\" ><img src=\"{D_SN_ROOT_VIRTUAL}design/images/icon_missile.png\" style=\"height: 1.5em\" /></a>\n      <!-- ENDIF -->\n    </td>\n    <!-- ENDIF -->\n    <!-- IF ACT_WRITE -->\n    <td class=\"c_c\">\n      <!-- IF $DIFFERENT_USER -->\n      <a class=\"button_pseudo {$SCAN_CLASS}\" href=\"messages.php?mode=write&id={galaxyrow.USER_ID}\"><img src=\"{D_SN_ROOT_VIRTUAL}design/images/icon_mail.gif\" alt=\"{L_gl_sendmess}\" title=\"{L_gl_sendmess}\" style=\"height: 1.5em\" /></a>\n      <!-- ENDIF -->\n    </td>\n    <!-- ENDIF -->\n    <!--&lt;!&ndash; IF ACT_STATISTICS &ndash;&gt;-->\n    <!--<td class=\"c_c\">-->\n    <!--&lt;!&ndash; IF $DIFFERENT_USER && galaxyrow.USER_RANK != '-' &ndash;&gt;-->\n    <!--<a class=\"button_pseudo\" href=\"stat.php?who=1&range={galaxyrow.USER_RANK}#{galaxyrow.USER_RANK}\"><img src=\"{D_SN_ROOT_VIRTUAL}design/images/icon_statistics.png\" alt=\"{L_Place} {galaxyrow.USER_RANK}/{userCount}\" title=\"{L_Place} {galaxyrow.USER_RANK}/{userCount}\" style=\"height: 1.5em\" /></a>-->\n    <!--&lt;!&ndash; ENDIF &ndash;&gt;-->\n    <!--</td>-->\n    <!--&lt;!&ndash; ENDIF &ndash;&gt;-->\n    <!-- IF ACT_INFO -->\n    <td class=\"c_c\">\n      <!-- IF $DIFFERENT_USER -->\n      <a class=\"button_pseudo {$SCAN_CLASS}\" href=\"index.php?page=imperator&int_user_id={galaxyrow.USER_ID}\"><img src=\"{I_menu_empire_emperor}\" alt=\"{L_stat_details}\" title=\"{L_stat_details}\" style=\"height: 1.5em\" /></a>\n      <!-- ENDIF -->\n    </td>\n    <!-- ENDIF -->\n  <!-- ELSE -->\n    <td colspan=\"{COL_SPAN}\" class=\"c_c\">\n      <!-- IF ! PLAYER_OPTION_UNIVERSE_DISABLE_COLONIZE -->\n        <span class=\"button_pseudo {$SCAN_CLASS}\" go=\"fleet\" planet_planet=\"{galaxyrow.PLANET_NUM}\" mission=\"{D_MT_COLONIZE}\">\n          {L_uni_colonize} {galaxyrow.PLANET_NUM}\n        </span>\n      <!-- ELSE -->\n        [{galaxy}:{system}:{galaxyrow.PLANET_NUM}]\n      <!-- ENDIF -->\n    </td>\n  <!-- ENDIF -->\n</tr>\n<!-- END galaxyrow -->\n<tr>\n  <th colspan=\"{COL_SPAN}\" class=\"subheader\">\n    <span class=\"button_pseudo {$SCAN_CLASS} ownexpedition\" go=\"fleet\" planet_planet=\"{PLANET_EXPEDITION}\" mission=\"{D_MT_EXPLORE}\">\n      {L_gf_unknowsp}\n    </span>\n  </th>\n</tr>\n<tr>\n  <td class=c colspan=3>\n    <span id=\"missile\">{MIPs}</span> {L_gf_mi_title}\n  </td>\n  <td class=c colspan=3>\n    <span id=\"flt_recyclers\">{PLANET_RECYCLERS_TEXT}</span> {L_gf_rc_title}\n  </td>\n  <td class=c colspan=\"{COL_SPAN_PLUS}\">\n    <span id=\"flt_spies\">{SPs}</span> {L_gf_sp_title}\n  </td>\n</tr>\n<tr style=\"display: none;\" id=\"fleetstatusrow\"><th class=c colspan=\"{COL_SPAN}\"><table width=\"100%\" id=\"fleetstatustable\" class=\"no_border_image\"></table></th></tr>\n</tbody></table>\n"
  },
  {
    "path": "design/templates/OpenGame/universe_rename.tpl.html",
    "content": "<h1>{L_uni_naming}: <!-- IF SYSTEM -->{L_sys_system} [{GALAXY}:{SYSTEM}]<!-- ELSE -->{L_sys_galaxy} {GALAXY}<!-- ENDIF --></h1>\n<h2><!-- IF NAME -->{NAME}<!-- ENDIF --></h2>\n\n<form action=\"galaxy.php?mode=name&galaxy={GALAXY}&system={SYSTEM}\" method=\"post\">\n  <div class=\"contFC\">\n    <div class=\"border_image_large\">\n      <div class=\"contFC cell\">\n        <label>\n          {L_uni_name}\n          <input type=\"text\" name=\"uni_name\" value=\"{NAME}\" maxlength=\"64\" size=\"32\">\n        </label>\n        <label>\n          {L_uni_for}\n          <input type=\"text\" name=\"uni_price\" value=\"{PRICE}\" size=\"10\">\n          {L_sys_dark_matter_sh}\n        </label>\n        <input type=\"submit\" name=\"uni_name_submit\" value=\"{L_uni_to_name}\">\n      </div>\n    </div>\n  </div>\n\n</form>\n\n"
  },
  {
    "path": "design/templates/OpenGame/vacation.tpl.html",
    "content": "<br />\n<table width=\"720\">\n  <tr>\n    <td class=\"c\">\n      <span class=\"fl negative\">{NAME}, {L_sys_vacation} {VACATION_END}!</span>\n      <!-- IF CAN_LEAVE -->\n      <!-- <span class=\"fr positive link\" onclick=\"window.location.href = window.location.pathname + '?vacation=leave';\">[ {L_sys_vacation_leave} ]</span> -->\n      <span class=\"fr positive link\" onclick=\"window.location.href = 'overview.php?vacation=leave';\">[ {L_sys_vacation_leave} ]</span>\n      <!-- ENDIF --> \n    </td>\n  </tr>\n  <tr><th class=\"errormessage\"><img width=\"720\" src=\"design/images/vacation{RANDOM}.jpg\"></th></tr>\n</table>\n"
  },
  {
    "path": "design/templates/OpenGame/viewreport.tpl.html",
    "content": "<br />\n<form method=\"get\" action=\"index.php\">\n  <input type=\"hidden\" name=\"page\" value=\"battle_report\">\n  <table width=\"517\">\n    <tr>\n      <td class=\"c\">\n        <span class=\"fl\">{L_cr_view_title}</span>\n        <a class=\"fr link\" href=\"messages.php?mode=show&message_class=3\">{L_cr_view_my}</a>\n      </td>\n    </tr>\n\n    <tr>\n      <th>\n        <span class=\"fl\">{L_cr_view_prompt}</span>\n        <input type=\"text\" name=\"cypher\" maxlength=32 size=35>\n        <input class=\"fr\" type=\"submit\" value=\"{L_cr_view_button}\">\n      </th>\n    </tr>\n  </table>\n</form>\n\n<!-- INCLUDE page_hint -->\n"
  },
  {
    "path": "design/templates/index.html",
    "content": ""
  },
  {
    "path": "docs/balance.txt",
    "content": "Корабли делятся на классы.\n\nЛегкие корабли: ЛИ, ТИ, МТ\nСредние корабли: крейсер, линкор, бомбардировщик, большой транспорт, переработчик\nТяжелые корабли: линейный корабль, уничтожитель, колонизатор\nСверхтяжелые корабли: ЗС и Нова\n\nВводится новая категория в дополнение к скорострелу: \"увеличенный урон\". Атакующий, имеющий \"увеличенный урон Х\" против защитника, наносит защитнику в Х раз больше урона. Щиты принимаются в расчет.\n\nИстребители - \"пушечное мясо\" полегче и потяжелее. ЛИ получает увеличенный урон против сверхтяжелых кораблей и бомбардировщиков, а ТИ - дополнительно против тяжелых кораблей. Скорость ЛИ будет увеличена, возможно - сравнимо со скоростью крейсеров\nКрейсер - основной корабль для быстрых рейдов. Скорострел против ЛИ будет уменьшен до 4, получит скорострел 2 против ТИ и, возможно, скорострел против транспортников, разработчиков и спутников\nЛинкор - основной корабль для сил вторжения, костяк флота. Средняя скорость, относительно дешевый, не требует дейтрия. Получит скорострел против легких кораблей. Скорее всего 3 против ЛИ и 2 против ТИ\nБомбардировщик - корабль подавления планетарной обороны. Медленный, с мощной броней и щитами. Получит увеличенный урон против планетарных щитов. Получит либо скорострел, либо увеличенный урон против плазменных орудий.\nЛинейка - основной тяжелый корабль для борьбы со средними кораблями противника. Получит скорострел против колонизатора и переработчика. Возможно, будет подкорректирован скорострел по другим кораблям или заменен на увеличенный урон.\nУничтожитель - корабль подавления флота. Возможно, будет увеличен общий урон.\nЗС - корабль общепланетарного подавления. Скорострел по истребителям будет заменен на увеличенный урон либо вообще убран. Так же, возможно, будет подкорректирован скорострел по крейсерам.\nСупернова. В текущем виде не вижу ей места в игре. Если оставить корабль в игре, его характеристики будут сильно подкорректированы - тем более, что за это высказалась большая часть голосовавших. Есть несколько сценариев развития.\n0. Убрать корабль вообще.\n1. Противофлотский корабль. Убрать или сильно убавить скорострел по наземным целям, уменьшить общий урон или скорострел по кораблям.\n2. Уникальный мега-корабль. Усилить характеристики, увеличить цену (250кк-500кк вместо нынешних 70), ограничить одним кораблем во всем флоте.\n3. Корабль за деньги. П.2 плюс дополнительный взнос.\n\n\nОборона\nРакетные установки - \"пушечное мясо\". Возможно, будет немного усилена, получит штраф на стрельбу по скоростным кораблям (ЛИ после апгрейда и крейсера)\nЛегкий лазер - для борьбы с малобронированными быстролетящими целями. Получит либо скорострел, либо увеличенный урон против ЛИ\nТяжелый лазер - для борьбы с низколетящими целями. Получит скорострел против ЛИ, увеличенный урон против ТИ и бомбардировщиков\nГауссовка - для борьбы с бронированными среднеразмерными целями. Получит увеличенный урон против крейсеров\nИонная пушка - для борьбы с зашилдованными средне- и крупноразмерными целями. Характеристики будут пересмотрены. Получит увеличенный урон против бомбардировщиков, линеек и уников\nПлазменная пушка - для борьбы с крупными, сильнобронированными, медленными целями. Получит увеличенный урон против ЗС и Суперновы\nМалый щит - пересмотр характеристик в сторону усиления. Возможно, будет несколько уровней щитов или возможность строительства нескольких щитов.\nБольшой щит - пересмотр характеристик в сторону усиления. Возможно, будет несколько уровней щитов или возможность строительства нескольких щитов.\nПланетарная защита - пересмотр характеристик. Будет уменьшен скорострел или заменен на увеличенный урон по отдельным типам кораблей. Цена будет приведена в соответствии с уроном, броней и щитами.\n\nТак же большинство орудий будет иметь скорострел или увеличенный урон против небоевых кораблей\n\nОчень удачное изобретение — privateer (в русской версии — капёр). Этот пиратский кораблик мог невозбранно грабить торговые суда конкурентов без объявления войны"
  },
  {
    "path": "docs/changelog.dev.todo.txt",
    "content": "todo.actual.txt\n===============\n\n[*] Покупка ММ - 15% бонус. Округлять вверх до тысяч\n\n[*] Закладки\nОтдельные иконки на \"Заметки\", \"Закладки\" и \"Полётные задания\"\n- Так же отдельные вкладки \"Все\" + см. выше\n\n[*] Модули\n- Поле autoinit - для автоматической инициализации модуля\n- Для упрощения работы суб-модулей - которые будут иницилизироваться самими модулями\n- Например - Highspot и HighspotAction инициализируются в Festival, который грузится в core_festival\n\n\n$supernova->design -> classSupernova::$design;\n\npayment_currency_exchange_mm_ -> get_mm_cost();\n\npayment_currency_exchange_ - проверить на MM\n\nВООБЩЕ ИЗБАВИТЬСЯ ОТ $config! Все данные должны вытаскиваться соответствующими функциями!\n\ngetElementById - убрать\n\n[*] icon-info - просмотреть, поменять icon-info-1\n\n[*] Планета/Управление\n- Вице-губернатор\n- Трансфер губернатора\n- Сделать \"управление\" и \"возврат\" ссылками\n\n[*] Строительство\n- Отфлекбоксить блок информации о юните на странице построек и переделать на шаблоны\n\n[*] В Опере12 длинные страницы при скролле до конца вниз норовят вспрыгнуть вверх - проверить почему\n\nЭкспы - в зависимости от левела давать летать дальше. В зависимости от дальности - увеличивать риск и награду\n\n\nХайспоты - межсерверные хайспоты\n\n!!!!!!!!! Поменять назад константы во флаинг_флит_хандлер\n\nВ списке игроков печатать ИМЯ АККАУНТА - для начисления ММ\n\n\n\nChangelog\n=========\n\nunits_used - хранить в NULL ячейке\n\nМАСШТАБИРОВАНИЕ ИВЕНТОВ ОТ СРЕДНЕСУТОЧНОГО ОНЛАЙНА!\n\nФиксировать вид картинки между рефрешами - т.е. записывать имя картинки\n\nФИЛЬТР \"УДАЧЛИВЫХ\" игроков\n\nНЕ ВЫДАВАТЬ КАРТИНКУ, ЕСЛИ РАЗМЕР ЭКРАНА - МЕНЬШЕ МИНИМУМА!\n\n\ncore_festival->mvc_proxy вызывается дважды для ('view', '') - посмотреть, в чём там дело\n\n\nonhover или онклик - посмотреть полную картинку\n\nВ 6 раз больше\n1782*6 = 10692\n\n\nМАСШТАБИРОВАНИЕ ПРИЗА НА ПАЗЗЛ ОТ КОЛИЧЕСТВА ЁЛОЧЕК!\n\nОсада - удержание наоборот. Враждебный флот ставится на орбиту и мешает приходящим-уходящим флотам\n\nНа плашке \"Наука\" показывать процент обязательных исследований\n- Строить дерево один раз после ресета и запоминать куда-нибудь\n\nfleet_array\n{{fleets }}\n{{iraks}}\ntpl_parse_fleet_db - унифицировать окружающий код при вызове\n\n\ndb_normalize_id - REMOVE!\n\nФлоты в полёте - фильтр по планете прибытия/отправления\n\n\nclassSupernova::db_get_record_list - внести обратно в функции, не использовать напрямую!\n\nИзолировать aks\nfleet_aks_invite (page4) - выставить список игроков-друзей и соаловцев\n\n\nПроверить систему работы проверки версии сервера\n\n\nПреобразование чисел с нулями через intval/floatval - если число начинается с 0 или 0х - оно будет преобразовано как восьмеричное/шестнадцетиричное\nОсобенно неприятно это в яваскрипте - проверить\n\n\nПри отправке флота добавить сортировку по расходу и ёмкости трюма\n\nПеревод ресов между планетами - 0.8% от суммы перевода в ТМ. Типа - вебмани, кошельки WMD, WMC, WMM\n\nФлитПейдж0 - сделать общую информацию о флоте флексбоксом\n\n\n\n[22.01.2016 23:50:12] \tСингулярность[БАС]>\tЕще надо добавить цветовое кодирование по скорости корабля - и вообще будет зашибись\n[22.01.2016 23:50:27] \tСингулярность[БАС]>\tВот сейчас всем удобнее - но вот я теряюсь, когда пытался подобрать корбали в экспу по скорости\n[22.01.2016 23:50:39] \tИван[Феникс]>\t(Сингулярность[БАС]) разрабу не понравилось творение. ..... хотя мож привыкнем\n[22.01.2016 23:51:24] \tСингулярность[БАС]>\tГде я сказал, что \"не понравилось\"?\n[22.01.2016 23:52:00] \tСингулярность[БАС]>\tПо большему количеству критериев новый интерфейс - удобнее. Единственный минус - теряется видение скорости кораблей и подбора по скорости во флот\n[22.01.2016 23:53:01] \tСингулярность[БАС]>\tВ принципе - понятно, что сделать. Заменить надписи иконками и прописать цветовое кодирование для скорости. И сразу попустит\n\n\nPTE тест - Доделать\n- Блоки\n- Условные структруры\n\n\nBONUS_ABILITY - deprecated ???\n\nНа новых наградах 8 марта-2016 не отображается надписи\n\nинфа для размышления. добавить страницу заказа модулей в админку публичной версии игры\n\n\n\ninit.php@241 // TODO - костыль, что бы работали старые модули. Убрать!\n\n\nАльтернативный next для туториала - если не установлен модуль (?)\nПозволять писать премодеренные новости игрокам (за карму? за ТМ?)\n\n\nАпдейтер - полностью сбрасывать кэш после обновления\nuserOptions - get() должен возвращать значение по умолчанию если нет значения в БД\n\ndoquery()\n- убрать параметр table и посортировать по типам вызова\n- переписать на DbQuery\n\nAccessorsV2\n- добавить возможность мерджить пачку аццессоров - как массивом так и из другого AccessorsV2\n\nЗаменить\n- htmlentities($text, ENT_COMPAT, 'UTF-8');\n- Заменить nl2br хелпером ?\n\nМигрировать опции\n$options = sys_user_options_pack($user);\n\nУбрать classSupernova::$db_prefix и classConfig::db_prefix и сделать protected db::db_prefix\n\nОбучение\n+ Продолжение туториала - добавление нового текста после окончания\n+ Проверить добавление новой записи\n+ Новая запись не должна открывать следующий текст\n+ Ресет - сбрасывать финишед\n+ Локализация кнопок и надписей - доделать в JS\n+ Парсинг bbCode\nОкно\n+ Хранить настройки интерфейса - окно, позиция - в куках\n+ Не меняется хеадер\n+ Окно перекрывается результатами голосования в голосовалке (?!) - проблемы с z-index?\n- Закрывается по Esc - отключить\n- Менять размер окна ????\n+ Код\n+ public function htmlize($result, $jsSafe = false) { -> перенести в Text\\TextEntity/Text\\TextModel\n+ Сделать перетаскивание на мобилках\n- Сделать мигание глазами у советника\n- Загнать настройки туториала в JS объект\n- Базовое обучение\n- Сделать кнопку \"Закрыть\" рабочей как-то по-другому\n\n\ndisplay()\n- Поубирать параметры из процедуры - использовать темплейтовые переменные\n\n\n\n- проверить начисление статуса \"бессмертный\"\n    - Статус Бессмертного у тебя есть - просто звезды нету\n\nДобавить в релиз\ncase ADM_TOOL_FORCE_ALL:\nclassSupernova::$config->db_saveItem('db_version', 37);\n\n\njQueryUI https://jqueryui.com/download/#!version=1.12.1&components=111111111111101011011101010010110000000000000000\nhttp://jqueryui.com/download/#!version=1.11.4&components=1111101011010111010110000000000000000&zThemeParams=5d00000100e105000000000000003d8888d844329a8dfe02723de3e5701db9777083d4196cdf2e05cb978ae1580fa707593b54f9fa4872d22569ccccc82e2c2cdd85acf9679c8ad3251f01fea6685b5f8aea5ad2ddab3da975e5144ce275b688fe0a64167e7a2d21024b7f1f458ff6a48bf95e55fdc5bd887a91e8cbb37e0660da11fd2094ccae6bc54c98e52d552c92378136f82c5ba2a6e53fe9fce674ba617ff41e5ec8e32578838b23fc8cb8222c7ec8b787c68d9fe33c907d0e639559740a1265d5bd42493bc8730c59825b1166b5bd0c9a2e37fdd3049cedb6e4cede8edc70921014560433bd45d7a5b752579d27044cb984d5c17d5a3b444194304032b03e854284f39ccecc06daa5a2b4478ff730484ae0454a7d3d6bc5624ecc4f9a1234d98c89f346052e06c3f8d59ff0286613c38d746f5477fb2d120006d250174991366691d2cf8b8c1fba17b31c12339d492eaa7c948687edf4436abf9644fc98fdb0c811d60a50af0a1cd0561044a51198ab80e144d7f19fb5fc81fb6d332d001f08c78ec16a45d6e36cedbf096b742ef7f6d4c02c7009044519998202183a8fb57608dc92b7e71857163dbd5498fdd44f6e\n\n\nКомпиляция веб-ресурсов на сервере\n- Компилировать все зарегестрированные CSS в один файл\n- Комплировать все зарегестрированные js в один файл\n\nНастройки\n- Разобраться, почему не работают ссылки второго уровня\n- Разобраться, почему не выделяются табы\n\n\nНавбар\n+ Свернуть очереди - унифицировать и вынести их в отдельный файл\n\n\nРесурс-бар\n+ Переделать\n+ Flex\n+ Тесты\n+ Вертикальный\n+ Горизонтальный\n+ В Альянсе\n+ Адаптивный дизайн\n+ Попап\n+ Разделители тысяч\n+ Заменить буквы-маркеры на картинки\n+ Раскидать стили и JS-код по файлам из темплейта\n+ Возможно, разбить на две части - МК-ДЭ, что бы красивее врапалось на маленьких экранах\n+ Сделать опцию включения нового навбара в Настройках\n? Прогрессбар заполнения\n- Отдельный таймер изменения заполнения\nПопап:\n- Отдельный для энергии\n- Цветовое кодирование заполнения - зел-жел-оранж-крас\n? Добавить летящие флоты\n? Перенести опции настройки в куки\n- Проверить как работает на мобилках\n+ Добавить склады\n? Иконка склада\n\n\nОбучение\n- добавить выделение цветом в текст - ссылки, кнопки итд\n\nЭкстратеха\n- См.\n\nУбрать из списка кораблей те, что нельзя строить по расовым ограничениям\n\n\n? Заменить ainput на спиннеры\n\n- Сделать страницу подобора цветов под тему\n    - Включить её для игроков - сохранять в настройках\n\n\nПостройки\n- Инфа о производстве - сжираются пустые колонки - там где 0\n\n\nОбслуживание\n- Удалять кумулятивы (32) с нулевыми данными\n- Удалять кумулятивы удалённых игроков\n- Сделать FOREIGN KEY\n\nАрхивация аккаунтов\n\n\nНавбар\n? может добавить флекса и при вертикальном ресурсбаре?\n\nПодтверждение автоконвертации\n- Переделать попап под стандартный конфирейшн диалог\n\n\nПоставить что-нибудь с реквайр лаб-левел 1, что бы игроки сразу могли че-то исследовать\n\n[*] prettyNumberStyledDefault() - избавиться и перенести формат на сторону темплейта\n[*] HelperString::numberFloorAndFormat() - избавиться и перенести формат на сторону темплейта\n\n\nJS/CSS\n- В постройках - вынести загрузку JS-файла в PHP\n- Сделать автоматическое определение наличие min-файлов\n$sn_mvc['javascript']\n$sn_mvc['css']\n\n\n#test {\nz-index: 0;\ncolor: red\n}\n\n.class_test {\ncolor: white;\nwidth: 40%;\nposition: absolute;\nright: 0;\nborder: 1px solid red;\nbackground-color: rgba(0,255,0,0.5);\n\nz-index: -1;\n}\n\n$(document).ready(\nfunction() {\n$('#test').append(\"<span class='class_test'>Q</span>\");\n}\n);\n\nautoexpanded textarea\nhttp://codepen.io/vsync/pen/frudD\n\n\n- Таки унифицировать опции\n  - $options = sys_user_options_unpack($user);\n\n\n\n- Обзор планеты\n    - сделать уникальные глобальные счётчики по классам - одновременно обновлять данные в нескольких элементах, а не по ИД\n\n\n- Плохо нажимается кнопко постройки в Опере\n\n- ИЕ11\n  Не работает адаптивный навбар от слова \"совсем\"\n\n- Проверить работу модуля menu_customize с новой вёрсткой\n\n- Переделать управление альянсом на табы\n\nдубликаты assign_recursive - поубирать\n\n- Больше тестов богу тестов!\n    - Проверить работу _inherit в скинах\n\nИстория чата - добавить нормальную паджинацию\n\nMENU => GLOBAL_DISPLAY_MENU\nNAVBAR => GLOBAL_DISPLAY_NAVBAR\nРабота PAGE_TITLE и PAGE_HEADER\n\n- Вертикальный навбар\n    - Переключение темплейтов\n        - Фоллбэки, если темплейта нет\n        - Инхеритед-темлпейты\n            - Фоллбэки, если нет файла темплейта\n        - Опция в Настройках\n            - Добавить в таблицу user поле для имени темплейта\n    - В таблице user - переименовать dpath\n        - Сделать хранение только имени скина, а не пути к скину\n\n- Автоматический выбор ближайшей фаланги\n\n- За ТМ - сохрнение результатов скана фаланги и показ их в попапе\n\nУпростить вот это вот всё\npublic function getImageCurrent($image_tag, $template = null) {\nreturn $this->getImageFrom($this->activeSkin->getName(), $image_tag, $template);\n}\n\npublic function getImageFrom($skinName, $image_tag, $template) {\nreturn $this->getSkin($skinName)->imageFromStringTag($image_tag, $template);\n}\n$skin = $this->model->getSkin($params[self::PARAM_SKIN]);\n$image_string = $skin->imageFromStringTag($ptlTag->resolved, $ptlTag->template);\n\nУбрать\nnavbar_v\nУпростить вёрстку\n\n\nИмператор\n- сделать выбор и настройку выводить статистику за Х дней\n- Для себя\n- Для других\n- Вертикальная статистика\n\n\nTools::fillPercentStyle\n- сделать аналог для JS\n- сделать аналог для TPL\n- Позаменять везде самопал на этот код\n\nGlobal message! aka notifications\n\nТехи:\n- Общая технология Супертеха (бустер?)\n- Позволяет добавить к одной выбранной технологии количество левелов супертехи\n- Рассовая абилка Экстратеха\n- Как экстратеха - только к разным?\n- Артефакт на +к технологии\n- Временный ?\n- Патч?\n\npretty_number\n- Растащить на функции, а то слишком много в одном месте\n\nИнфу по ТЭ - в новапедию\n\n[01.03.2017 16:28:30] \tAyaaN[SPIRIT]>\tБлин\n[01.03.2017 16:28:39] \tAyaaN[SPIRIT]>\tВпервые строю термо\n[01.03.2017 16:29:00] \tAyaaN[SPIRIT]>\tПотому что СолЭс работают в минус\n[01.03.2017 16:29:14] \tAyaaN[SPIRIT]>\t15 лвл\n[01.03.2017 16:29:33] \tAyaaN[SPIRIT]>\tСолЭс а энергии наоборот -374\n[01.03.2017 16:29:52] \tСингулярность[БАС]>\t(AyaaN[SPIRIT])Забавно\n[01.03.2017 16:30:01] \tСингулярность[БАС]>\tВ принципе - так и должно быть, но вижу какое-то кидалово\n[01.03.2017 16:30:04] \tСингулярность[БАС]>\tКакая планета?\n\n\nПозаменять global:\n- $debug\n- $lang\n- $config\n- $user (!)\n\n\nРасшить работу с чейнджсетами\nstatic::db_changeset_apply();\n\n\n\n\n\nUse DbQuery:\npublic static function db_ins_record($location_type, $set) {\npublic static function db_del_record_by_id($location_type, $safe_record_id)\npublic static function db_del_record_list($location_type, $condition)\n\n\nQuest\n- quest_conditions - в отдельную таблицу\n- мультикондишн\n- Фильтр квестов\n\nСобытия\n\nfunction doquery($query, $table = '', $fetch = false, $skip_query_check = false) {\n=> doqueryNew - без $table\n\n\nТуториал - свернуть/развернуть\n\nНайти аналогичные куски и оформить отдельным кодом\nforeach ($lang['qst_status_list'] as $statusId => $statusName) {\n$template->assign_block_vars('status', array(\n'ID' => $statusId,\n'NAME' => $statusName,\n));\n}\n\n\nАльянсы:\n- История названий и тэгов Альянса а-ля история имён\n\nJS:\n- parseInt => parseInt(x, 10)\n- Или вообще - Math.intVal\n\n\nПлатежи\n- Кнопка \"очистить незавершенные старше 2 месяцев\"\n- Показывать платежи по дефолту только за последние 2 месяца\n- Кнопка \"Показать все платежи\"\n- При клике на иконку платежа - делать письмо по шаблону\n- Уменьшить точность суммы до двух цифр\n- Добавить форматирование количества ММ и сумм\n\n\n+ Сделать интерфейс для подарков на 8е марта\n    + Кавалер\n        - JS-код, если не хватает ММ\n        + Место в конкурсе\n        - Галочка disclosure:\n            - По умолчанию - не пишется имя дарившего\n            - Если стоит галочка - имя пишется\n    + Дама\n        + Список входящих подарков\n        - Королева Весны\n    - Оно\n        - Ссылка на \"Настройки\", профиль\n    - Письмо о подарке\n        - Сумма и кто подарил, если стоит disclosure\n    - Выдача ПЗ сразу по факту подарка\n    - Обсчёт и выдача результатов конкурса\n\nРекорды:\n- Рекордсменам давать какие-нибудь бонусы\n\nНастройки:\n- Ужать темплейт - переделать на блоки\n- Убрать старый опции, которые хранятся в таблице `users`\n\nРанняя загрузка\n- Для включения - выставить $sn_mvc['pages'][<имя страницы>][PAGE_OPTION_EARLY_HEADER] в true;\n- Сделать возможность быть каллабле - анализировать строки запросов и отключать там, где нинада\n- Например, где оно может быть редиректом или вывод другого header\n- Такие случаи конечны и легко просчитываются\n- $sn_mvc['pages'][<имя страницы>][PAGE_OPTION_TITLE]\n- Поддержка $lang в содержимом\n- Грузить языки заранее - тоже через $sn_mvc\n- Тайтл можно менять после вывода страницы через JS!\n\n\nПредупреждение при возврате флота на удалённую планету (!)\n- Подтверждение удаление планеты на которую летит / возвращается СВОЙ флот\n\n\nСделать в игре рекламу ФИЧ ИГРЫ!\n\nНа странице квестов написать, что награды начисляются автоматически.\n\nОтключать донаторам рекламу\n\nЗапихнут иконки меню в один файл\nСделать настройки отображения displaymenu итд свойствами страниц - тогда можно будет выводить раньше хидер\n\nВ мобильном виде\n- вместо ввода чисел (ainput в частности) делать по тапу/клику попап, в котором отдельные элементы ввода - типа спин-бокса или даже тот же аинпут\n\nОпция:\n- Обновление страницы по окончании очереди\n\n\nELO\n\n\nПакетные функции должны быть вынесены из Энтитей в модели, например (?) - типа, получение списка итд\n- А вот работа над данными энтити должны быть в самих энтити\n\n\nВернуть сообщения о получении уровня и начислении ТМ\n\nБиржа:\n- Обмен ресурсов\n- Фиксированный курс\n- Плавающий курс\n\nСтатистика\n- Убрать ТМ и ММ из расчёта рейтинга ?\n\n\nСделать classSupernova контейнером Pimple саму по себе\n\nhttps://tech.yandex.ru/money/doc/dg/reference/notification-p2p-incoming-docpage/\nhttps://money.yandex.ru/embed/quickpay/small.xml\n\nКурс белорусского рубля к доллару: 2 бр за 1 усд\n\n\n- Админка, стата - не считать ботов!\n\n- Глобальные сообщения (player_notifications)\n    - Фаза 0:\n        - Таблица в БД\n        - Автоочистка таблицы по обслуживанию\n        - Вывод сообщений:\n            - Тикер на каждой странице\n                - Кнопка на закрытие\n            - Lastread\n            - Страница для просмотра всех сообщений\n        - ГС:\n            - Покупка ММ\n            - Увеличение уровня\n                - Здания\n                - Рейд\n                - Теха\n                - Экспа\n    - Фаза Х:\n        - Поддержка BBcode\n        - ГС на квесты\n            - Ссылка на квест, к которому имеет отношение ГС\n              ? ГС на новости?\n\n[*] ? Дописать начисление ТМ\n- Начисление по имени ID/имени/емейлу АККАУНТА, а не игрока\n\n[*] Переписать начисление ММ\n- Вытаскивать имя/ИД игрока если ММ меняется по аккаунту, а не по игроку\n- Расчёт ММ по валюте\n- Отсылка письма с уведомлением\n\n[*] core_auth.php:437\n$account_translate = PlayerToAccountTranslate::db_translate_get_account_by_user_id($user_selected['id'], self::$main_provider->provider_id);\nreplace with\nAccount::dbGetByPlayerId($player_id_unsafe)\n\n\n\n[*] Релиз 43:\n- PHP 5.4 + PHP 7 compatibility\n- Убрать патчи по 39й включительно\n- Интегрировать модули GIT-сабмодулями\n\n\n[*] Убрать global\n- global $ListCensure;\n- $ListCensure = array ( '/</', '/>/', '/script/i', '/doquery/i', '/http/i', '/javascript/i');\n\n\n[*] class OldDbChangeSet\n- deprecate\n\n[*] FleetLock - блокировать в МЛФ записи в зависимости от типа ивента - отдельно по прибытию, удержанию, возвращению\n[*] protected function flt_flyingFleetsSort($a, $b) - Переписать на более чёткую сортировку по ивентам\n- Из двух флотов первее обратаывается события флота с меньшим ID\n- FLEET_ARRIVE всегда наступает первым\n- За ним - FLEET_ACOMPLISH\n- И только за тем - FLEET_RETURN\n\n[*] // init constants from db - TODO - REMOVE\n\n[*] Убрать \\phpbb_hook из кода и ссылки на его использование\n\n[*] mrc_get_level - разбить на разные функции:\n- unitGetLevelOnUser\n- итд\n\n[*] Заменить двойные кавычки одинарными\n\n[*] Проверить все таймлимиты - и использовать константы:\n- TIMEOUT_SHORT\n- TIMEOUT_MEDIUM\n- TIMEOUT_LONG\n- или PERIOD ???? - лучше TIMEOUT, что бы не смешивать с остальными периодами\n\n[*] Класс TimeMeasure:\n- start($mark)\n- mark($mark)\n- end\n- diff($mark)\n- format($mark)\n\n[*] Аккаунт для поисковых пауков\n\n\n[*] DB:\n- `alliance`:\n- Убрать поле ally_request_waiting\n\n    global $ally_rights;\n\n\n[*] ActiveRecord - а нужен ли теперь reload()?\n- Теперь перед insert/update устанавливаются дефолтные значения из БД\n\n[*] После окончания update должен редиректить на какую-нибудь страничку - что бы перечитались данные\n\n\n[*] Кэширование схем БД/таблиц\n\n[*] AR - проверка существования полей в схеме БД\n\n[*] deprecate Invoker class\n\n[*] AccessLogged::_startValues - не нужны?!\n\n\n[*] ActiveRecordAbstract\n- вынести всё, относящееся к работе с DB в отдельные функции\n- Унаследовать от этого класса ActiveRecord и перекрыть абстрактные методы\n- Написать тесты\n\n\n[*] Учитывать тип ядер и размер планеты в exploration bonus\n\n\n[*] Мультики\n- Ограничивать передачу ресурсов нижележащим\n[21.09.2017 20:29:08] \trival[NewSTAR]>\tвобщем не важно - мульты разрешенные - позволяют задействовать все механизмы прокачек ) выживают самые толстые) Топу мелкого не достать - быстро вкачивается мульт свежий - и имея ресусную базу топа - мультом сносится вся мелочь\n[21.09.2017 20:29:34] \tмарк рикман[Казаки]>\t(rival[NewSTAR]) ну и что в этом плохого?\n[21.09.2017 20:30:27] \tмарк рикман[Казаки]>\tвылез топ вверх. внизу никого достать не может. сваял мульта и игра получает новый импульс. опять же... если топ будет занят только мультом, то его будут лупить и грабить.\n[21.09.2017 20:30:30] \tСингулярность[абиБАС]>\t(rival[NewSTAR])Что значит \"быстро\"? Каким-таким механизмом?\n[21.09.2017 20:31:36] \trival[NewSTAR]>\t(Сингулярность[абиБАС]) условно быстро ) а тут вообще мгновенно практически - через рынок флота можно делать) ну да - минус по ресам - зато муха надоедлевая сбита)\n- Подумать о пересчёте скорости ресёча в Альянсе в зависимости от уровня игроков - что бы не набирали мелочь\n[21.09.2017 20:38:08] \tKAPACb>\t2. мультами можно задирать скорость изучения технологий в альянсе\n[21.09.2017 20:38:10] \tСингулярность[абиБАС]>\t(KAPACb)То же самое он может сделать союзником/членом Академии\n[21.09.2017 20:38:11] \trival[NewSTAR]>\t(Сингулярность[абиБАС]) я говорю чем плохи мульты - своими возможностями - абсолютным совпадением онлайна\n[21.09.2017 20:38:32] \tСингулярность[абиБАС]>\t(KAPACb)То же самое можно сделать, набрав ВОТ ВСЕХ в Альянс. Были тут одни, с 70+ членами... Брали всех\n[21.09.2017 20:38:36] \tмарк рикман[Казаки]>\t(KAPACb) это хорошо по твоему мнению, или плохо?\n- Разобраться с ботами и ситтерами\n[21.09.2017 20:50:09] \trival[NewSTAR]>\tа бывает ещё ситтинг\n[21.09.2017 20:50:23] \trival[NewSTAR]>\tтоже боты) только живые\n- Разобраться с прокачкой - передачей ресурсов через и-шки\n[21.09.2017 21:18:08] \tСингулярность[абиБАС]>\tПрокачку - да, надо закрывать. Примерно понятно - как. Надо еще подумать немного и сделать\n- Противопрокачка\n- Отключить транспорт на Iшки и i-шки - в общем, когда там прокачка снимается\n- Ввести ограничение на транспорт в зависимости от уровня Станции Альянсов - переименовать её, кстате\n- Ввести ограничение на принципиальное количество транспорта слабому игроку\n- Добавить ресурсы на Удержание (?)\n- Удержание только на планете со Станциями - час за уровень станции\n- Сбрасывать на планеты без Станций можно только ограниченное количество ресурсов\n- Режимы работы станции: всем, только соаловцам, только друзьям, соаловцам и друзьям, никому\n- Проверять наличие Склада Альянса в момент установки УД (или во время боя?)\n[27.09.2017 14:41:38] \tjeka_ua[™]>\t(rival[NewSTAR]) для начала склад альянса везде построй\n[27.09.2017 15:20:02] \trival[NewSTAR]>\t(jeka_ua[™]) а зачем тебе склад, если я склад грохнул на одной за 35 минут до прилёта, а ты всё равно повис без склад\n- Ограничить передачу ресурсов\n- Подумать, как переделать реферралку, что бы куча мультиков не давала особых бонусов\n[23.09.2017 06:59:45] \tMorhold[Казаки]>\tО, вот еще что. На мультов наверное не должны распространяться реферальные ссылки. Иначе начнут абузить ТМ за первоначальные квесты. Выжал мультика, бросил, завел следующего. И так далее\n- Дополнительные ограничения для:\n- Играющих через тор-ноды\n- Играющих через анонимные проксики\n- Играющих с одного пула адресов\n\n[*] Ультрапул - моментальная постройка кораблей\n\n[*] Использование SVG в качестве Placeholder’a https://habrahabr.ru/company/edison/blog/342848/\n\n[*] Ракетный автостроитель - платный, дорогой, временный\n\n[*] Вход по аккаунту\n[1:56:29] SNP-A-Morhold: Гор, ты тут?\n[1:56:56] SNP-A-Morhold: Так ты же сам писал, когда логинился после переыва, что не мог зайти по емейлу...\nУ старых игроков логин - это имя игрока (в силу очевидных причин). У новых - емейл.\n[1:57:23] Project \"SuperNova.WS\": И?\n[1:57:44] SNP-A-Morhold: Все так. И я стормозил. А чего взять с новичка. Ну он тупой, не прологинился, матернулся и пошел на новый проект\n[1:58:30] SNP-A-Morhold: Поэтому я и подумал, не уходят ли лишние игроки из за того, за что я плюху на почту получил :)\n\n\n[*] Заменить serialize/unserialize на json_encode/json_decode\n\n[*] Разобраться с mail.ru\n- http://php.net/manual/en/function.mail.php\n\n\n[*] Возмещение\n- !!! Наверное, всё-таки, юниты - ресы можно передать и так\n- Возможность возместить ресурсы на потерянный флот (возместить юниты?)\n- Должен иметь ETA что бы не стать эксплоитом\n- Запоминаем скользящим окном, сколько стоит флот\n- В момент Возмещения - считаем количество ресурсов во флоте (без загрузки)\n- Разница может быть Возмещена с коэффициентом и, возможно, за ММ\n- Должна действовать нуб-защита - не более /5 от текущего уровня\n- ВВЕРХ ВОЗМЕЩАТЬ НЕЛЬЗЯ - ПРОКАЧКА!\n\n\n[*] EXPLOSIVE ACCELERATORS\nhttps://lurkmore.to/Оружие_в_компьютерных_играх#mws_52JPLKA\nEXPLOSIVE ACCELERATORS\nhttp://www.islandone.org/LEOBiblio/SPBI134.HTM\n\n\n[*] Проверять\n- sql_mode = ''\n- Права на SET\n- Если sql_mode не тот - менять\n\n\n\n[*] classConfig->db_loadItem - заменить на конструкцию pass()->переменная\n- То же самое с db_saveItem\n? А сам pass() переименовать в direct()\n\n\n[*] /includes/template.php 323\nUse $template_result to turn menu and navbar or ond off instead of\n- isset($page->_rootref['MENU']) ? $isDisplayMenu = $page->_rootref['MENU'] : false;\n- isset($page->_rootref['NAVBAR']) ? $isDisplayTopNav = $page->_rootref['NAVBAR'] : false;\n\n\n[*] Поощрилка за регулярную игру\n- Буст настарте новичку\n\n[*] Собрать статистику по потреблению памяти игрой:\n- PHP\n- xCache\n- MySQL\n\n[*] Ускорять исследования в зависимости от постоянности состава Альянса и срока его существования (?)\n\n\n#ctv\n\n[*] Логгировать \"Прибытие\" как самую длинную операцию флота. В файл?\n\n[?] Не работает нормально \"Обслуживание\" - не возвращает результат\n\n[*] Стата\n- Если пересчёт происходит повторно - т.е. ДО того как посчитается новая стата:\n- то надо постить данные как новую стату и затем при следующем пересчётё заменять эти данные\n- Так не будет лишних \"шагов\" статы при внеплановых пересчётах\n- В админке не показывается результат расчёта статистики\n\n[*] Отключить пока лысого\n- Проанализировать стату\n- Сделать отключение по красному крестику\n\n\n[*] При расчёте статы не лочить всё сразу - или не держать лок так долго\n\n\n[*] ActiveRecordAbstract - разбить на два-три класса\n\n\n[*] scheduler_process - sys_maintenance подвесить выше для того, что бы удалять лишние Альянсы/юниты ДО расчётов\n\n\n[*] Экспа\n- Искать чертежи по кусочкам. По 10к на кусочек\n\n[*] Наёмники - цена для Альянсов должна быть выше кратно\n\n\n[*] template - модификаторы показа через параметры {VALUE|parms...}, что бы не надо было везде дублировать через pretty_number\n\n\n[*] Империя\nДобавить еще кнопок в начале для \"сохранить\", если ОЧЕНЬ много планет/лун\n\n\n\n[*] !!!! Логгировать изменения емейлов!\n\n\n\n[*] Листание сообщений (?)\n\n[*] Шпионаж - генерировать отчёты не в форме текста\n\n\n\nСкорость - на самом деле не скорость, а ускорение - сектора/секунду^2\n\n[*] Дейтреедобывающий корабль с отрицательным потреблением\nЗвездный ковш (?)\n\n\n#ctv\n\n\n\n[*] Добавить точки - разделители тысяч.\n- Про экран симулятора я подумаю основательно. В строке ввода может и не получится красиво сделать -\nно вот, например, можно добавить прямо под строкой ввода отдельно добавить еще и введённое количество,\nнаверное, можно\n\n[*] Сообщения\n- Редирект после удаления - что бы при рефреше не повторилась операция\n- Рефакторинг\n- Пейджинация - учесть, что при удалении \"неотмеченных\" на странице NOT IN должны инвертировать выбор и давать конкретные ИД\n- messageLastRead - тотал и по категориям\n- Использование DBQuery - переписать куски\n- Полноценный Outbox с копиями сообщений\n- Унифицировать рендеринг сообщений\n- (?) Архивная папка - даже отдельная архивная таблица (?)\n- \"Чёрный список\" у игроков - фильтрация адресатов\n- (после \"черного списка\") Пересылка сообщений - форвард. В частности - шпионских отчётов\n\n#ctv\n\nOldDbChangeSet - избавиться\n\nActiveRecord не транслирует ID-поле в $this->id - и тесты этого не ловят!\n- ActiveRecordAbstract надо разбивать на подклассы\n- В следующем классе должны быть операции с ID и методы DELETE, UPDATE, findById\n- Проверить все унаследованные от ActiveRecordAbstract классы - что бы они по-прежнему работали корректно\n- И тесты\n\nActiveRecordAbstract\n- Вынести вниз функционал пересчёта имён полей в названия свойств\n\n\n\nПерейти на новый ResourceCalculator\nЗакомментировать\n$planet_row['caps'] = $caps;\n\n\nРазобраться с освобождением результатов mysqli_result->free()\n\n\nПроверить работу с ключом устройства - нет ли там эксплойта\n\nМеню админа - убрать дубликаты категории/пункты - \"Игроки\"/\"Список игроков\"\n\n- Поддержка апдейтеров в модулях - что бы не менять update.php в ветках\n\n\nСобирать статистику по страницам\nУдалять в маинтенансе старые визиты ?\n- После того, как данные о подозрительных пользователях перенесены в отдельную таблицу\n- Отдельный модуль \"антибот\"\n- Оптимизация\n    - не писать ничего если toDelete у нас пустой\n\n$debug - зацикливается, если ошибка произошла в БД - пытается записать опять в БД, в которой ошибка итд\n\n\nПолные исходные логи счётчика - в бэкапе от 2017-10-18 13:31\n\nПроверить работу cutTails = true в VisitMerger\n\nВести историю мьютов\nПри бане/мьюте - выдавать историю банов и мьютов\n\n- ТАКИ НЕТ -    Теперь корректно обрабатываются визиты, находящиеся на стыке пакетов\n\n- Сделать ОТДЕЛЬНЫЙ индикатор ресурсов для Альянса - что бы было видно, сколько ресов у игрока\n- ВЕЗДЕ включить индикатор ресурсов - проверить, где это реально не нужно\n\n- https://webdevwonders.com/dom-storage-super-cookie/\n  // Set a session_id if sessionStorage object is available\n  if (window.sessionStorage) {\n  var session_id = \"2bf039d0-f0c1-11df-ae7e-3137382e3633\";\n  window.sessionStorage.setItem(\"session_id\", session_id);\n  }\n  // Try to retrieve the user_id if localStorage object is available\n  // or set a new user_id if user_id isn't set yet\n  if (window.localStorage) {\n  var user_id = window.localStorage.getItem(\"user_id\");\n  if (!user_id) {\n  user_id = \"6b67ce02-c61e-11df-96b2-38352e313833\";\n  window.localStorage.setItem(\"user_id\", user_id);\n  }\n  }\n\n\n- ТМ - из экспы только супертяжами\n\nЭкспу - в бинарные логи\n\n\nРассылка Альянса - одним письмом\n\n\nIllegal mix of collations (latin1_bin,IMPLICIT) and (utf8_general_ci,COERCIBLE) for operation '='\n/* tID 6\n(db_mysql)db_mysql->doquery() - 'includes/db.php' Line 102\ndoquery() - 'classes/RequestInfo.php' Line 94\n(RequestInfo)RequestInfo->__construct() - 'classes/core_auth.php' Line 183\n(core_auth)core_auth->__construct() - 'includes/init.php' Line 108\ninclude() - 'login.php' Line 16\n*/ SELECT `device_id` FROM beta_security_device WHERE `device_cypher` = '1\\0\n\n\n\nЁмкость хранилища:\n$levelPremium = $level + $premium\n$base = function($levelPremium)\n\napplyBonus($base, MRC_STOCKMAN)\n*= $storageScaling\n\nbonusData - описание бонуса, в хранилище $bonusDataList[snid]\n\nMySQL Views:\n- game_v_counter\nselect `c`.`visit_time` AS `visit_time`,(`c`.`visit_time` + interval `c`.`visit_length` second) AS `end`,round((((`c`.`visit_length` / 60) / 60) / 24),2) AS `day`,round(((`c`.`visit_length` / 60) / 60),1) AS `hour`,round((`c`.`visit_length` / 60),1) AS `min`,`c`.`visit_length` AS `sec`,`c`.`hits` AS `hits`,`c`.`device_id` AS `device_id`,`c`.`browser_id` AS `browser_id`,inet_ntoa(`c`.`user_ip`) AS `IP`,`c`.`user_id` AS `user_id`,`u`.`username` AS `user`,`u`.`ally_tag` AS `ally`,`c`.`counter_id` AS `counter_id`,`c`.`user_ip` AS `user_ip`,`c`.`user_proxy` AS `user_proxy`,`c`.`page_url_id` AS `page_url_id`,`c`.`plain_url_id` AS `plain_url_id` from (`game_counter` `c` left join `game_users` `u` on((`u`.`id` = `c`.`user_id`))) order by `c`.`visit_time`,`c`.`counter_id`\n- game_v_users\n\n\n\n\nПроизводство ресурсов:\n\nАстротехи - на 2017-10-29\nМаксимум планет - 28, фактический максимум - 23, на что и надо ориентироваться\nМаксимум астротехи - 22, фактический 20\nМаксимум астро+прем - 25, фактический - 23 с провалом до 19\nMaxLevel Astro = 22(18+4) x 5; 21(17+4) x 6; 20(16+4) x 11; 19 x 14;\n\nТаким образом рассчитываем, что:\n28я планетарка с премом +7 и +1 Марс\n- 20я планетарка по цене, как сейчас 22я астротеха - 4 трлн\n- 21я планетарка по цене - не меньше 23й астротехи - 10 трлн\n- вообще-то 22 астротеха дает 18 колоний и 4 экспы\n\nMaxLevel Astro+Prem =\n25 x 4\n24 x 6\n23 x 8\n22 x 4\n21 x 7\n20 x 5\n19 x 11\n18 x 34\n17 x 49\n\nREAL Planets: pcs x players\n- 28 x  3   +7 = Артьом 21 + 7, Xeon 22 + 6, 1 старый от Игоря\n- 27 x  1   +6 = auro007 22 + 5\n- 26 x  0   +5\n- 25 x  3   +4 = bak и Dimitrius 21 + 4, 1 старый\n- 24 x  6   +3\n- 23 x  9   +2\n- 22 x 15   +1\n- 21 x 13   +0\n- 20 x 10\n- 19 x 16\n- 18 x 25\n- 17 x 20\n- 16 x 33\n- 15 x 52\n- 14 x 81\n- 13 x 58\n- 12 x 76\n- 11 x 65\n- 10 x 86\n\n\n\n[*] Считать среднее время работы статистики и, возможно, флота\n- Хранить старое среднее время и общее количество обсчётов, что бы можно было потом обновлять среднее время\n\n[*] Добавить на сервере время замера разницы во времени, что бы можно было форсировать замер времени с сервера\n\n[*] Ежедневно в рамках обновления статы синхронизировать статус с аккаунта на юзера и начислять медальки\n\n\nПроверить на модулях\n- db_get_user_by_username\n- db_get_user_by_where\n- db_get_record_by_id - количество параметров и сократить\nИзбавиться от\n- _SnCacheInternal::$queries\n\ndb_unit_by_location()\ndb_planet_by_id()\n\n\nМодули - фестиваль - гезер - админка - баннилки\n- Глюк - если повторный бан, то банит +24 от старого\n\nПоудалять еще и пустые аккаунты\n\n\n+ Удалять при подсчётет статы и ТО планеты с просроченной датой удаления - это будут луны\n    - Придумать что-то другое, что бы гарантировать удаление, даже если луна не сканируется\n\n\nТакой конфиг ставит стату раком\nvar_stat_update\t2017-11-11 04:08:30\nvar_stat_update_admin_forced\t2017-11-04 14:31:13\nvar_stat_update_end\t1510453157\nvar_stat_update_msg\tUpdate in progress. Step 14/14: updating Allys current rank and points.\n\n\nvar_scheduler_active\nvar_stats_lastUpdated\t0\nvar_stats_schedule\td@04:00:00\nvar_stat_update\t2017-11-11 04:08:50\nvar_stat_update_admin_forced\t2017-11-04 14:36:52\nvar_stat_update_end\t1510452767\nvar_stat_update_msg\tUpdate in progress. Step 14/14: updating Allys current rank and points.\nMemory usage: 3,670,016\nPrevious operation time: 0.01690\nvar_stat_update_next\t2017-11-12 04:00:00\n\n\n#ctv\n\n\n[*] И вообще больше тестов для бонусов\n\n\n[*] Токен-лотерея\n- Написать Ридми\n- Ограничивать юниты текущим уровнем игрока\n- Админка Токен-лотереи - сделать. Показывать % для каждого айтема\n- Показывать окно о получении токена\n- Разделители тысяч количеству токенов\n- Время получения токена - дату\n- Звуки на выигрышь и проигрышь\n- Логи:\n- Получение токена\n- Трата токена\n- Получение юнитов\n\n[*] Модули\n- Вынести общие функции чтения конфигурации - с пререндерингом префикса\n\n\n[*] Понять, почему у юзера обнуляется статистика при выходе из алла\n\n[*] Поискать ENT_COMPAT, 'UTF-8 и позаменять на HelperString::htmlSafe()\n\n\n\n[*]\n- Сделать пул Губернаторов\n- Запаковать старых игроков\n- Поменять PLANET_GOVERNOR_ID и PLANET_GOVERNOR_LEVEL на использование Planet\\Planet\n\n\n[*] На Обзоре и Управлении добавить цены на смену ядра в дропдауне\n- На \"Ресурсах\" - убирать цену в дропдауне, если ядро равно текущему (?)\n\n\n[*] prettyNumberStyledDefault - заменить на параметры PTL-тэгов в темплейте: num|format\n- HelperString::numberFloorAndFormat\n\n[*] Сделать пересчёт статы без добавления новой позиции\n\n[*] defence -> defense\n\n[*] Проверить, как работает UBE с уничтоженными лунами - и вообще уважаются ли уничтоженные луны?\n\n[*] Унифицировать создание луны:\n- заодно увеличить максимальный размер луны до 9999 и переделать sn_ube_combat_analyze_moon_destroy\n- Не создавать луну при миссии Уничтожить\n\n\n[*] [15.02.2018 12:37:28] \tsmotreashee[A-Казак]>\tчто за фигня у меня был шпионаж 23+4 2 из альянса и 2 за премиум я купил шпиона 3 лвл (прем еще+2 к его лвл даёт и того 5 лвл ) но шпионаж так же остался 23+4 должно быть 23+9 где меня обманули ?\n\n[*] TODO - убрать рассовые корабли из добычи Экспедиции\n\n[*] Статистика - сделать ссылками ники\n- Возможно - перенести иконки в конец ника\n\n[*] Пираты - на луны\n- Пираты будут вылетать на самую жирную планету с ресурсами\n\n\n[*] Чат\n- Избавиться от поля БД `chat_message_sender_name` в модуле чатов - имя можно выковаривать из `user`\n\n[*] Апдейтер\n- Переделать хелпер в объект\n\n\n\n\n[*] [!] Игроки/Уровни\n- Рендерер ников\n? Убрать из player_nick_render_to_html() вызов player_nick_uncompact()\n- Переделать рендерер ников в объект\n- Сделать список рендереров расширяемым\n- Поменять в player_nick_render_current_to_array() строковые идентификаторы опций на константы\n\n[*] Игроки/Ники\n- Добавить иконки неактивности - выдернутый джек ethernet или дисконнект\n\n[*] Собрать все иконки в один файл\n\n[*] Впихнуть куда-то количество очков на уровень\n\n[*] Вселенная - неправильно расцвечиваются auth_level 1-2\n\n[*] Переместить \"6 лет СН\" в памятные знаки (?)\n\n[*] Ник - выравнивать внешним дивом\n- chat_nick_msg - поискать и убрать\n\n[safe_name] - переименовать\n\n[*] Хайспот на бесплатные массовые операции\n[*] Хайспот на бонус к уровню Премиума - на 12 апреля\n[*] Хайспот на свободную постройку расовых юнитов\n[*] Карго-Мастер - переделать под увеличение трюмов и ввести нового Наёмника для увеличения складов\n[*] Капитаны - убрать пункт меню и сделать интерфейс работы с флотами\n- Добавить Капитанов на список юнитов (?) или на экран Управления (?)\n- Добавить здание для прокачки Капитанов\n- Добавить новые скиллы (?)\n- Размер Трюма\n- Размер бака (?)\n- Скорость полёта (?)\n\n[*] Замена стандартных попапов на jQuery\n- Автоконвертация - подтверждение\n\n\n[*] При удалении планеты - в попапе писать имя планеты и удалять зафиксированную планету\n- Возможно, указывать что будет потеряно\n- Луна\n- Наёмники\n- Флот\n- Количество зданий\n- Капитан (?)\n\n[*] Перенести признак столицы в планету (?)\n\n[*] Оптимизировать рендереры флотов и планет на страницах Империи и Обзора\n\n[*] Выделить уровень \"разработчик\" в отдельный модуль\n\n[*] {Эта планета является столицей} - добавить список бонусов\n\n[*] Седьмая раса - \"дарлоки\":\n- Расовый корабль (неизвестно) - сверх-лёгкий истребитель, который не светится нигде:\n- Ни в сканах\n- Ни в списке флотов во время полётов\n- Его характеристики неизвестны\n- Его нельзя посчитать в симуляторе - его там просто нет\n- Специабилка - что-то со шпионажем. Мегабонус?\n- Механизм \"Диверсии\"/\"Саботажа\"\n- Назначать Капитанов (?) на миссию \"контр-разведка\" - что бы понизить вероятность\n\n\n[*] Опция \"Построить моментально\"/Исследовать\n- Вычисляется количестов чипов/нанитов и покупается/пременяется за ТМ\n- Так же хелперы по АКК на новых планетах\n\n\n[*] Фильтры по типам планет и лун в Империи и МО\nMorhold[Казаки]> Гор, кстати нет в планах возможности фмльтровать планеты и луны в списке и/или в обзоре империи по признаку планета/луна? Тоже было бы очень удобно, часто нет никакого смысла просматривать всю простыню, если нужны только планеты или только луны\nAnarchis[SPIRIT]> (Morhold[Казаки]) да согласен, и ещё такоеже разделение в массовых операциях\n\n\n[*] Добавить в подсказку Лотереи, что можно выиграть еще и Артефакты\n[*] Добавить на страницу 8 марта переход в админку - понятно, только для админов\n\n[*] Провезти деноменацию ТМ и ММ\n\n\n[*] При входе в игру, неважно где - на планете или на луне, появляется активность на всех планетах игрока.\n- http://forum.supernova.ws/viewtopic.php?f=65&t=2896\n\n\n[*] Награды - разделить на аккаунтные и игроковые\n\n\n\n[*] Убрать пункт меню \"Технологии\" - перед этим надо добавить везде ссылки на Новапедию\n\n\n[*] Доработать админку:\n- Круд по планетам\n- Круд по юнитам\n\n[*] Пакеты/Бустеры - Личный резерв\n- Пакеты/Бустеры - бонусы на скорость производства/исследования в разрезе игрока/планеты\n- Бустеры на время - действуют 1-2-3-х часов\n- Бустеры одноразовые - получить добычу за Х часов\n- !!! Бустеры \"по времени\" это получаются \"Патчи\"\n- !!! Бустеры на ускорение постройки/ресеча - это Артефакты\n- Возможно - на флоты\n- Капитан - навык \"Использование бустера\"\n\n\n[*] Артефакты\n- Сжатие времени - позволяет получить выработку за Х часов на планете/всех планетах\n\n[*] Во всех операциях с планетами пробрасывать номер планеты, для которой это делается\n- При изменении активной планеты - выдавать оповещение \"Операция заблокирована потому что изменена активная планета и не совпадает с той, на которой поводится изменение\"\n\n[*] Добавить в список игроков количество бабла/ММ за всю историю - иконку (?)\n\n[*] Переместить настройки токенов из PLAYER_OPTIONS в отдельную таблицу! Что бы их нельзя было хакнуть\n\n[*] Системы игры:\n- Common - общеупотребительные классы, которые могут использоваться отдельно от SN\n- Core - Базовая функциональность\n- КОГДА ОТКЛЮЧЕНА: игра неработоспособна\n- Config - конфигурация игры и модулей\n- DBAL - DB Abstraction Layer:\n- Предоставляет игре унифицированный интерфейс доступа к БД\n- Installer - устанавливает и настраивает игру, а так же конфигурирует начальные параметры\n- Updater - обновления БД, манипуляция с данными в БД\n- Auth - система авторизации и аутентификации:\n- Account - хранит инфромацию об аккаунте\n- Авторизирует аккаунты\n- Вторичные методы авторизации/аутентификации, связывающие записи во внешних системах с аккаунтом\n- Player:\n- Хранит базовую информацию об игроке. Привязан к аккаунту\n- Репер для привязки количество юнитов Империи (игрока)\n- RPG:\n- Economic - покупка/продажа юнитов (т.е. постройка, исследование, ЧР итд):\n- Dark Matter - подсистема ТМ, добавляющая новый ресурс в игру\n= Metamatter - система платной ТМ:\n- ИСПОЛЬЗУЕТ: Account - идентификация аккаунта, к которому привязана ММ\n- Начисление и списание ММ\n- Payments - подсистема, предоставляющая интерфейс платёжных модулей\n- Офицеры:\n- Император:\n- Captains (Капитаны) - Офицер, привязанный к флоту\n- Наёмники (Mercenaries) - Офицер, привязанный к Империи\n- Губернаторы (Governors) - Офицер, привязанный к планете/луне\n- Герой: Офицер, участник Приключения\n- Mining - добыча юнитов (ресурсов)\n- Universe - организация Вселенной\n- Расчёт дистанций между точками Вселенной\n- Создание объектов Вселенной: Луны, Планеты, Обломки\n- Incident: Управление Происшествиями\n- Fleets:\n- ИСПОЛЬЗУЕТ: Universe - расчёт дистанций и времени полёта\n- ИСПОЛЬЗУЕТСЯ В:\n- КОГДА ОТКЛЮЧЕНА: прямой перевод ресурсов между планетами\n- составление и отправка флота (взлёт)\n- управление флотом в полёте (отзыв)\n- выгрузка юнитов на планету\n- Удержание\n- Группы Флотов\n- Mission - подсистема Миссий:\n- Рассчитывает результаты взаимодействий флотов в разных контекстах\n- UBE - боёвка:\n- ИСПОЛЬЗУЕТ: Fleets - информация о флотах и привязанных к ним бонусах\n- Расчёт результата столкновений флотов\n- Учитывается Удержание\n- Учитываются Группы Флотов\n- Quest - квесты\n- Module - модули в игре:\n- ModuleManager - управляет подключением модулей, их статусом итд итп\n- Добавляет/изменяет функционал любой другой подсистемы\n- Виды:\n- Payment\n- Player\n- Misc\n- Core\n\n\n\n[*] Избавиться от serialize()/unserialize()\n- Заменить по коду:\n- unserialize => unserializeOrJsonDecode()\n- serialize => json_encode\n- В апдейтере физически заменить старые данные на новые\n\n[*] htmlentities() -> HelperString::htmlSafe\n\n\n[*] Свернуть старые неуниверсальные картинки на 8 марта в универсальные\n[*] Вынести константы наград в модуль наград!\n[*] Внести константы в классы\n[*] Добавить таймштамп в festival_gifts\n[*] Фестиваль\n- Паззлы\n- добавить minLevel, maxLevel к параметрам\n- Переписать на использование Аутком-Менеджера\n- Gather\n- тоже добавить? minLevel, maxLevel\n- Переписать на использование Аутком-Менеджера\n\n[*] Награды:\n- Отсылать письмо о получении награды - на player_award::award()\n\n\n[*] Обзор планеты\n- Возможность отключить список планет, что бы не светилась активность\n\n[*] Империя\n- Добавить обломки на орбите\n- Добавить к планетарным иконкам иконку обломков. Раскрашивать в разные цвета\n\n\n[*] Радужные планеты - планеты, на которых ВСЯ добыча отличная. В них нельзя пережать планеты\n- Привязывать продуктивность нестандартных планет к текущим рейтам обмена\n\n[*] Прокачка Капитанов - сделать ограничение на 1% (5%?) от всего флота в бою\n\n\n\n[*] Избавиться от sn_function_call()\n[*] $manifest['active']->isActive()\n[*] убрать SN::$gc->modules->registerModule($class_module_name, $this); из sn_module::__constructor()\n[*] flt_page2 - убрать добавление капитана во флот в модуль капитанов\n- sn_fleet_page3()\n\n\n[*] Добавить поле stop_propagation к хукеру. Возможно - к пимпу. Или что бы сама функция сделала stop_propagation и использовала результат из хукера\n\n[*] Токен-лотерея\n- Добавить больше инфы на страницу токен лотереи - см. попап\n\n\n[*] Избавиться от функции messageBox()\n[*] Сделать SN контейнером GlobalContainer\n\n#ctv\n\n[*] !!!!!!!!!!!!!!!!!!!!!! Нет константы SN_PAYMENT_ROBOKASSA_RESPONSE_OK !!!!!!!!! Проверить чему она должна быть равна - и добавить\n- вообще ошибка в payment_request_process() как минимум в робокассе - не возвращает результат при успешной операции!\n- Надо так же проверить остальные модули платежа !!!!!!!!!!!!!!\n- Так же проверить мультиреспондеры\n\n[*] Переименовать класс sn_module => Module\n\n[*] Вынести все классы модулей в неймспейсы:\n- \\Core\\Autoloader::register($this->getRootRelative() . '/classes/', 'Modules\\player_login_token');\n\n[*] SN::$gc->modules->getModule() - добавить параметр onlyActive = true\n\n[*] Переименовать sn_module->addModuleTemplate - getTemplate\n\n[*] Переименовать DbQuery->SqlQuery (? SqlStringQuery)\n\n[*] Переписать\nPlayerStatic::DeleteSelectedUser($UserID)\n\n[*] Купить другому игроку ММ - НЕ ПОДАРИТЬ!\n\n[*] В админке сделать защиту от повторного начисления (!)\n\n[*] https://habr.com/company/cleantalk/blog/354374/ - базовая защита от DDoS и ботов, но с проблемами при AJAX\n\n[*] Убрать таблицу `annonce`\n\n[*] Слить статики для игроков и планет\n\n[*] Разобраться с destruyed - когда ставится и как обрабатывается\n\n[*] Добавить в ActiveRecord свитчер name() - для возвращения имени поля, что бы можно было детектить использование полей по коду в запросах\n\n[*] Опцию stats_hide_player_list переделать, что бы работала с массивами, а не со строками\n\n[*] Модули - проверить, где используется loadVar/saveVar а надо использовать loadArray/saveArray\n- Временно. Вообще-то нужно всё переделать на loadVar и использовать там json_encode/json_decode\n\n[*] MoonBot - играть от луны !!!\n\n[*] убрать порнуху в класс (init.php)\n- require_once(SN_ROOT_PHYSICAL . 'includes/includes/user_birthday_celebrate.php');\n- sn_user_birthday_celebrate();\n\n[*] Разобраться, что там за херня вообще с MVC массивами происходит\n- Добавлена опция PAGE_OPTION_ADMIN для обозначения страницы как админской до её загрузки\n- Разобраться - куда добавили\n- Переделать все страницы на её использование\n- Убрать константу IN_ADMIN\n- 'model'+FIELD_MODEL => MVC_MODEL\n- 'view'+FIELD_VIEW => MVC_VIEW\n\n[*] UNIX_TIMESTAMP(NOW()) - заменить на UNIX_TIMESTAMP()\n\n[*] !!!!!!!!!!!! Декораторы |num|format не работают на простых переменных {BLACK_MOONS_COUNT|num|format} ! Но работают на блочных {bracket.BLACK_MOONS_COUNT|num|format}\n\n\n[*] Включить E_NOTICE и отловить все баги - сделать хэндлер, который будет выбивать ошибку (?) или включить в конфиге ПХПы\n\n[*] Размещать луны не ВНУТРИ кластера, а ЗА ПРЕДЕЛАМИ кластера!\n- Особый случай если луна одна\n\n[*] core_auth - НЕ МОДУЛЬ!\n\n[*] sn_module - наследник pimple\n\n[*] убрать _checkStatus\n\n[*] http://php.net/manual/en/function.password-hash.php\n- http://php.net/manual/en/function.password-verify.php\n\n[*] Главный Инженер - увеличивает ёмкость слота построек. Наёмник (?) +100, +200 итд\n\n[*] Пересчитать скорости транспортов - уж очень резво они летают\n\n[*] На столице - удваивать и скорость производства юнитов, защиты, зданий\n\n\n[*] За X баксов-то вложенных в игру можно и сделать. Лояльность и ЧСВ игрока поднимет\n- 4.03.2018 02:24:45   Сингулярность [1:2:12]   RE:Новое сообщение\nМы тут посовещались - и решили по такому случаю ввести специальную награду - медаль \"Первый Участник Краудфандинга\". Это может занять немного времени для добавления в код - но у тебя она точно есть!\n- Для себя - можно награждать только тех, кто пожертвовал первые 100 баксов, скажем. Подумать\n\n[*] Модуль балансера с дампами всего, что хочется в админке admin_balance\n\n\n[*] Объединить все локализации в один файл. Уже прошли времена, когда надо было экономить байты памяти\n\n[*] Проверить год начала использования модуля, что бы обновить копирайт\n\n\n[*] Добавить в Новапедию:\n- Максимальную дальность полёта на стандартных и текущих техах в Дистансах, Галактиках и Вселенных\n- Расход топлива на стандартный полёт из Галактики в Галактику на стандартных и текущих техах\n- Время полёта из Г в Г на стандартных и текущих техах\n\n[*] В чате сворачивать http://ogame.supernova.ws/ > sn://\n\n[*] Спецагент/Агент - шпион/разведчик/контр-разведчик\n- Спецздание - КБУ - Комитет Безопасности (Безопасного?) Управления - найм и прокачка Агентов\n- Скиллы:\n- Разведка/Шпионаж\n- Диверсия - действие наносящий физический урон (подрыв и т. д)\n- Саботаж - невыполнение работы, указаний\n- Контр-разведка\n\n\n#ctv\n\n\n[*] !!!!!!! Проверять корректную работу Фестивалей при каждом новом Хайспоте и добавлять отметки тут\n\n\nДобавить в сообщение об ошибках при переименовании игрока причины отказа:\n- Переименование отключено\n- Пустое имя\n- Запрещенные символы\n\nВключить bbCode в сообщениях всем игрокам\n\n//    // Replacing news://xxx link with BBCode\n//    $message = preg_replace(\"#news\\:\\/\\/(\\d+)#\", \"[news=$1]\", $message);\n//    // Replacing news URL with BBCode\n//    $message = preg_replace(\"#(?:https?\\:\\/\\/(?:.+)?\\/announce\\.php\\?id\\=(\\d+))#\", \"[news=$1]\", $message);\n//    $message = preg_replace(\"#(?:https?\\:\\/\\/(?:.+)?\\/index\\.php\\?page\\=battle_report\\&cypher\\=([0-9a-zA-Z]{32}))#\", \"[ube=$1]\", $message);\n\n    $message = SN::$gc->bbCodeParser->compactBbCode($message, $user['authlevel']);\n\n\nДлина поля в темплейте обрезает hide_stat_players\n\n\n[*] Добавить корректную работу неизвестных юнитов по контексту:\n- Приземление неизвестных кораблей\n- Исследование неизвестных технологий\n- Постройка неизвестных кораблей\n\n\n\n[*] Fleet.php\n// TODO - Those two lines should be removed - fleet times should be filtered on interface side\n$this->timeArrive = SN_TIME_NOW;\n!empty($this->timeEndStay) ? $this->timeEndStay = SN_TIME_NOW : false;\n\n\n\n#ctv\n\n\n\n[~] Флоты/Экспедиции\nТеперь в Экспедиции могут быть найдены и корабли, которые не входят в состав флота\nТеперь в Экспедиции не может быть найден самый дорогой корабль во флоте\n\n[*] Страничка \"Ресурсы\" - вставить такой же контрол, как и на Обзоре с ценой в ТМ в дропдауне\n\n\n[*] Продвинутые фишки флотов:\n- UnReturn - отменить возврат флота (если флот еще не прибыл)\n- QuickReturn - всегда возврат по затраченному времени полёта, а не по timeArrive - после того, как будет сделан возврат по timeArrive\n- NoHoldReturn\n\n[*] Забор крови. Забор - существительное\n\n/** @noinspection PhpUnhandledExceptionInspection */\n/** @noinspection SqlResolve */\n\n\n::fleet_insert_set_dbq - все три вызова нужно объединить в один\nOldDbChangeSet::db_changeset_apply - а от этого вообще избавиться\n\n#zebra\n.logDbWrapper > :nth-child(even)\n{\nbackground-color: #EEEEEE;\nline-height: 1.5em;\n}\n\n        .logDbWrapper > :nth-child(odd).critical\n        {\n          background-color: #e69539;\n        }\n        .logDbWrapper > :nth-child(even).critical\n        {\n          background-color: #ff8c00;\n        }\n        .logDbWrapper > :nth-child(odd).error\n        {\n          background-color: #FFDDDD;\n        }\n        .logDbWrapper > :nth-child(even).error\n        {\n          background-color: #EEBBBB;\n        }\n        .logDbWrapper > :nth-child(odd).warning\n        {\n          background-color: #ffffa0;\n        }\n        .logDbWrapper > :nth-child(even).warning\n        {\n          background-color: #d9d96c;\n        }\n\nСделать Артефактов и находить их в Экспе или Лотереях:\n- Смена ядра\n- Смена температуры (несколько градаций)\n\n\nПроект Антарес - развитие проекта Орион - корабль на термоядерных бобмах\n\n[*] Софт-ресет ака Престиж\n-\n- Дебафф \"Decay\" - \"Увядание\"\n- При слишком долгой игре без софт-ресета вешается дебафф, который влияет на все аспекты игра игрока\n- Дебафф может быть снят софт-ресетом\n\n\nhttps://aftershock.news/?q=node/701115\n\n\n    /**\n     * Return IP address\n     *\n     * @return mixed\n     */\n    public static function ipGet()\n    {\n        $realIp = self::ipGetForwarded();\n\n        return empty($realIp) ? $_SERVER['REMOTE_ADDR'] : $realIp;\n    }\n\n    /**\n     * @return string\n     */\n    protected static function ipGetForwarded()\n    {\n        $realIp = '';\n\n        if (empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {\n            return $realIp;\n        }\n\n        $finalIp = trim($_SERVER['REMOTE_ADDR']);\n\n        $proxyChain = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']);\n        array_walk($proxyChain, 'trim');\n\n        foreach ($proxyChain as $proxyAddress) {\n            // Checking if IP address is not local or reserved for internal networks\n            // Also if IP is like in REMOTE_ADDR - ignoring this too. It's not our case\n            if ($proxyAddress != $finalIp && self::ipIsGlobal($proxyAddress)) {\n                $realIp = $proxyAddress;\n                break;\n            }\n        }\n\n        return $realIp;\n    }\n\n    /**\n     * @param string $ip\n     *\n     * @return bool\n     */\n    public static function ipIsGlobal($ip)\n    {\n        return\n            filter_var(\n                $ip,\n                FILTER_VALIDATE_IP,\n                FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE // FILTER_FLAG_IPV4 |\n            ) !== false;\n    }\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n[*] Добавить запрос в код\nSELECT\npayment_user_id, u.username, FROM_UNIXTIME(u.register_time) as reg_date, min(payment_date) as first_pay, max(payment_date) as last_pay,\nFORMAT((UNIX_TIMESTAMP(min(payment_date)) - (u.register_time))/60/60/24,3) as days_first_pay,\nFORMAT((UNIX_TIMESTAMP(now()) - UNIX_TIMESTAMP(max(payment_date)))/60/60/24,3) as days_last_pay,\nformat(sum(if(payment_currency = 'UAH', payment_amount/11,payment_amount)), 2) as payment_sum\nFROM `game_payment` as p\njoin game_users as u on u.id = p.payment_user_id\nwhere payment_status = 1\ngroup by payment_user_id\norder by u.register_time;\n[*] Проапдейдить таблицу платежей - перебить все платежи в USD по курсу\n\n\n\n[*] Переделать расчёт бонусов Альянса\n- Масштабировать бонусы в зависимости от количества игроков в Альянсе и прописанного минимума линейно.\n- Для 7 минимума и 3 игроков бонусы будут floor(3/7 * уровень)\n\n\n[*] БД\n- Дропнуть sn_annonce - не перепутать!\n- sn_iraks => sn_fleet\n- Дропнуть sn_log_halloween_2015\n\n\n[*] Холод Вершины - для алла\n- Дебаф на алл/игрока за занимание первого места\n- Одиночество на пике - для игрока\n\n\n[24.11.2018 22:47:56] taych[NewSTAR]>по поводу рекрутинга. а нельзя придумать что по поводу принятия в альянсы..например доску обьявлений по приёму или вступления.по клику просто мышкой.\n[24.11.2018 23:02:55] Сингулярность[БАС]>Хотя идея со списком Альянсов, открытых для приёма - в принципе хорошая\n\n[*] Удержание\nМожно. Тут прикол в чем. У меня полет на него около трех суток. Если уйти в РО, то можно только повернуть назад. Итого 6 суток. Т.е. неделю продержать не получается. Я придумал по другому. Я долетаю. Переработчики разбиваются. Это уже когда противник в РО. И я шлю рабов на обломки. А потом еще и заворачиваю. Короче, там до полутора, а то и двух недель можно довести\nПрошлый раз Спрут на этом и прокололся. Вышел из РО - а привязки все еще есть\nОн очень хочет упрыгнуть из 11-13\nНу а сейчас они тоже на вооружение взяли эту тактику. Меня держать по 800-900 часов в одну сторону. То есть уже и РО не спасет. И стоит мне пропустить выход Спрута, а я не могу все время не спать - он упрыгнет, а я уже никуда не денусь\n\n\n[*] Атаки\nВвести настраиваемый минимальный промежуток между атаками (и/или между отправкой флотов?)\n\n\n\n{function timerDump timer=[]}\n<table>\n<tr>\n<th>Message</th>\n<th>Length</th>\n<th>From start</th>\n<th>Location</th>\n</tr>\n{foreach from=$timer key=key item=timestamp}\n{if $key}\n<tr>\n<td>{$timestamp['message']}</td>\n<td>{$timestamp['time']-$timer[$key-1]['time']|round:5}</td>\n<td>{$timestamp['time']-$timer[0]['time']|round:5}</td>\n<td>{$timestamp['location']}</td>\n</tr>\n{/if}\n{/foreach}\n</table>\n{/function}\n{call timerDump timer=$timer}\n'timer' => Timer::getLog(),\n\n\n\ngame_counter\nI_counter_device_id\t(device_id, user_id)\n\n\nПервый - пошел\nParviz - бан. Ботоводство. Хозяину бота - (очень сильное подозрение на ailouros ) первое и последнее предупреждение\nАльянсу Proxima Prime - первое предупреждение\nВторой - пошел!\nauro007 - Ботоводство\nАльянсу Про - второе предупреждение\n\n[*] ? Бонус к добыче при потере большого процента флота\n\n[*] Страница Альянса - добавить список игроков\n[*] Статистика - добавить фильтр по Альянсам и, возможно, статусам\n\n[*] Лог отпусков\n\n[*] В \"Поиске\" игрока и \"Списке игроков\" - вывешивать полные регалии на нике\n\n#ctv\n\n[*] Пофиксить\n$multiplier = 5; // $this->valueStorage->getValue(UNIT_SERVER_FLEET_NOOB_FACTOR);\nclass PlayerLevelHelper::calcLevels()\n\n[*] JSON!!!!\n- SN::$user_options[PLAYER_OPTION_MENU_SORT] = serialize($this->menu_reorder($menuOrderNew));\n\n\n!!!!!!!!!!!!! Писать в таблицу counter параметры GET запроса в отдельную колонку\n\n\n[22.12.2018 20:34:02] Сингулярность[БАС]>Это новая фишка в игру добавлена\n[22.12.2018 20:34:12] Сингулярность[БАС]>Дальше будет использоваться, само собой\n[22.12.2018 20:34:17] Сингулярность[БАС]>Коды могут быть везде\n[22.12.2018 20:34:42] Сингулярность[БАС]>На форуме. В чате. В новостях\n[22.12.2018 20:34:54] Сингулярность[БАС]>Планирую загадки с охотой за кодами\n[22.12.2018 20:35:37] Сингулярность[БАС]>Вот сейчас прикидываю, какой можно интересный кодоквест замутить со ступенчатыми кодами.\n\n\nВ чате заменить ДИВ на СПАН, что бы при копировании было сообщение в одну строку\n\nЭкспа - дорогие полёты (по дейтерию), но убрать потери флота\n\n[*] Контроль территории Альянса\n[06.04.2019 17:41:11]ForMe[♨️RED♨️]>бонусы*\n[06.04.2019 18:41:44]Lexus>спасибо, а в какой стоит проситься ? или в какой не стоит ? )))\n[06.04.2019 19:46:48]ForMe[♨️RED♨️]>В какой хочешь) Но вряд ли тебя возьмут в тот, где ровно 7 человек, ибо столько необходимо для активации бонуса\n[06.04.2019 19:47:07]ForMe[♨️RED♨️]>Но чем больше людей в альянсе - тем меньше бонус начисляется его участникам\n[06.04.2019 19:48:11]ForMe[♨️RED♨️]>А следовательно, в альянс состоящий из 7 человек, где каждый получает максимум(из возможного) бонусов, тебя не возьмут\n[06.04.2019 19:48:23]ForMe[♨️RED♨️]>Не выгодно (:\n[06.04.2019 19:48:46]ForMe[♨️RED♨️]>Но есть альянсы, где больше людей - там на бонус чуток подзабили\n[06.04.2019 20:07:21]Lexus>а кроме бонусов и ресурсов чтото ещё есть ?\n[06.04.2019 20:08:03]Lexus>я про альянсы ? может там захват территорий и порабощение всего ))))\n[06.04.2019 20:18:20]taych[NewSTAR]>(ForMe[♨️RED♨️]) на счёт позабили-не совсем так...........................\n[06.04.2019 23:43:06]ForMe[♨️RED♨️]>(taych[NewSTAR])Ну, больше людей - больше веселья, но бонусов то меньше всё равно\n[06.04.2019 23:44:55]ForMe[♨️RED♨️]>(Lexus)Ну, существует определенное количество атак в день на планету, а альянсы могут объявлять друг другу войну\n[06.04.2019 23:45:39]ForMe[♨️RED♨️]>И против врагов альянса можно нападать сколько угодно)\n[06.04.2019 23:46:04]ForMe[♨️RED♨️]>(Lexus)Слева в меню есть вкладка \"О сервере\", в самом низу\n[06.04.2019 23:46:13]ForMe[♨️RED♨️]>Там ознакомься с некоторой информацией\n[06.04.2019 23:46:30]ForMe[♨️RED♨️]>А слева наверху, на самом, есть вкладка \"Как играть?\"\n[06.04.2019 23:46:36]ForMe[♨️RED♨️]>С ней тоже следовало бы ознакомиться (:\n[07.04.2019 03:45:45]taych[NewSTAR]>думаю-зажигание уже пожно включать.....по моимприлежным наблюдениям администрация становиться по чуть чуть скуповатой.рейты добовляет по нескольку десятых..штоб ишемии не было\n[07.04.2019 03:51:45]taych[NewSTAR]>у мну на прошлый день космонавтики 11 числа на металле в час влетало 4.7 млд металла.чёт не записал 12 ое.наверное замотался распихивать такое сакровищще\n[07.04.2019 03:52:45]taych[NewSTAR]>ЛЮДИ!СТРОЙТЕ ТРАНСПОРТЫ!ЧТО БЫ ПОТОМ НЕ СХОДИТЬ С УМА!\n[07.04.2019 06:18:11]семм[™]>гагаринка зажгися\n[07.04.2019 07:16:19]Sprut[™]>Наверное опять кто-то в ракете лишнюю дырку просверлил- пуск откладывается, замазка кончилась...\n[07.04.2019 07:28:52]Sprut[™]>Есть у кого-нибудь жвачка или скотч- дырку в союзе залепить?\n[07.04.2019 07:53:34]taych[NewSTAR]>Олим ещё отдыхает\n[07.04.2019 07:53:45]taych[NewSTAR]>Олимп\n[07.04.2019 09:40:46]семм[™]>(taych[NewSTAR])\n[07.04.2019 11:45:08]Sprut[™]>(семм[™]) залепи дырку и полетели...\n[07.04.2019 11:48:10]Sprut[™]>(семм[™])огурца посадил, ключ на старт!\n[07.04.2019 12:32:27]семм[™]>отсчет пошол адын, дыва, тиры-поехалиии!!!!!\n[07.04.2019 13:40:00]Man in red[Феникс]>(Sprut[™])Ану домой руки мыть и кушать , играется он тут с едой\n[07.04.2019 13:41:08]Sprut[™]>(семм[™]) чих- пых, чих-пых, не заводится\n[07.04.2019 13:42:54]Sprut[™]>(Man in red[Феникс]) так это ты дырку в ракете просверлил?\n[07.04.2019 13:47:27]Man in red[Феникс]>(Sprut[™])для того она и дырочка\n[07.04.2019 13:49:27]Sprut[™]>(Man in red[Феникс]) вот негодяй!\n[07.04.2019 13:53:43]Sprut[™]>Все, доигрались- Гагарин отказался лететь из-за деверсии и отсутствия скотча\nНу, посмотрим. Я со счетов ОвК не скидываю. Что-нибудь да придумаю. Может по кусочкам в игру внесу. Может по более мелким, но частым ивентам растащу.\nНадо сесть и подумать. И посчитать - самое главное. По одному кораблику изредка пускать - это проще, чем сразу пол-дюжины. И нет вариантов что строить - т.е. можно легче посчитать последствия. И следить потом за балансом проще. И наиболее одиозные кораблики (кха-спутники-кхх) можно исключить.\n\n\n\n\"Уники\"\n- Юниты с уникальными характеристиками, которые можно приаттачить куда-нибудь: к игроку, планете, флоту, луне\n\nАнти-РМФ - убедиться, что никто, кроме владельца планеты, не получает инфу о бое за 1 раунд\n\nВыборы Императора Сервера\n\nМиниигра: собирать отряд Героев и отправлять их в суперэкспы\n- Лутер: сходить в экспу, набить чего-то, повторить.\n- Приключения вместо эксп\n\n\nhttps://aftershock.news/?q=node/752122\n\n\n[*] Игровая система помощи\n\n\n\n[*] Раса-7. \"Разнообразие\" (?), \"Отверженные\" (?), \"Скитальцы\"/\"Странники\" (?)\n- .7race.txt\n\n\n[*] TOR - Tactic, Operation, Resources\nТОР - Тактика, Операции, Ресурсы\nАттрибуты:\n- Удача - только на себя\n- Неудача - на всех в бою\n\n[*] ТуДу\n- Пофиксить баги\n- Переехать под CentOS 7/PHP 7\n- Виртуальная машина\n- Скрипт установки\n- Платежные системы\n- Перевести игру на новый айпишник\n- ВебМани\n- Интеркасса\n- ПейПал (?)\n- ЯндексБабло\n- Договр с иксоллой\n- Авторизация через внешние источники\n- Гугль\n- Доделать вконтакт\n- ВК\n- ОК\n- Боты\n- Пираты в экспе\n- Сторящий бот\n- Раса-7\n- Распотрошить ОвК\n- Новый темплейт\n- Вернуть ЯД-Метрику\n- http://tods-blog.com.ua/statistics/metrika-ukraine/\n- Самонастраивающееся меню\n- Система помощи\n- Ачивки\n- Квесты\n- Интеграция с форумом\n- Jira/Trello\n- Запуск сервера на английском\n- Перевод на Английский\n\n[*] Починить редактирование планет - модуль admin_planet_edit\n\n\n!!!!!!!!!!!!!\n[*] Добавить штраф +10% за счёт каждого следующего флота\n- game_noob_factor\n\n[*] Сделать разрешение на обратную атаку\n- Не давать уходить в отпуск на это время\n- Так же проверять вылетающие флоты в отпуск\n\n\n#ctv\n\n[*] Переработать маинтенанс таблиц безопасности\n[*] Переделать запись SQL-запросов в лог:\n- debug_backtrace() должен выполнятся при записи ошибочного запроса ? Проверить - будет ли он работать так при вызове в хандлере ошибок и/или хандлере окончания операции\n- Писать без <br/> - в сыром виде\n- Переделать вывод логов с учётом этого\n\n!!! Конвертировать артефакты за ТМ\n\n\n!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! О! Надо сделать запоминания прошлой системы платежей!\n!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n\n!!!!!!!! НЕ НАЧИСЛЯТЬ ТМ в ивентах тем, кто в отпуске и/или забанен и/или неактивен!\n\n!!!!!!!! При логине/регистрации кнопка \"Показать пароль\" не меняет содержание на \"Скрыть пароль\" после нажатия и показа пароля\n\n\n!!!!!!!!!!!! Исправить - заодно переделать на <span>\n[15.08.2019 23:12:05]\nSandra\n>\nа... теперь и zz увидела\n[15.08.2019 23:13:25]\nSandra\n>\nа когда в чате поднимаешься наверх - опять вниз сбрасывает\n[15.08.2019 23:14:43]\nSandra\n>\nнеудобно - нужные строки меню(слева) сложно поймать\n[15.08.2019 23:16:05]\nSandra\n>\nя не в приложении, а в браузере с телефона играю\n\n\n[*] <input type=\"text\" placeholder=\"Enter your user name\">\n\n\n\n#ctv\n\n\n\n[*] Иконки для платёжных систем\nКартинка билайна\nКартинка applepay\n\n[*] UnitPay\nДобавить PayPal\nДобавить ApplePay\n\n\n!!!!!!!!!!! Протестировать все методы прямой оплаты у агрегаторов !\n- Интеркасса\n+ PAYMENT_METHOD_MOBILE_MTS       => 'mts_mpay_merchantContent_rub',\n+ PAYMENT_METHOD_MOBILE_MEGAPHONE => 'megafon_mpay_merchantContent_rub',\n+ PAYMENT_METHOD_MOBILE_BEELINE   => 'beeline_mpay_merchantContent_rub',\n+ PAYMENT_METHOD_EMONEY_QIWI      => 'qiwi_qiwi_wallet2wallet_rub',\n+ PAYMENT_METHOD_EMONEY_YANDEX    => 'yandexmoney_yandexmoney_merchant_rub',\n- Остальные?\n\nТак, с валютами разобраться. Например:\nWebMoneyZ считает в WMZ\nНо юнитпей принимает только рубли\nИ при этом предлагает сумму в баксах\nНо игроку надо показать сумму в WMZ\n\n\nКэшировать реальную стоимость оплат по провайдерам или брать из таблицы payments\n\n[*] Переделать PaymentMethods::$payment_methods - разбить и юзать?\n\n\n[*] При РМФ не отсылать флоты - сделать опцию\n\n#page>div.logo+ul#navigation>li*5>a{Item $$$}\n\n\n[*] adm_user_stat - кажется, не нужен? Это, кажется, модуль?\n\n[*] В модулях позаменять design/templates - там они всё равно неправильно используются\n\n[*] Убрать <!-- INCLUDE page_hint -->\n\n[*] Убрать gettemplate из основного кода - параметры передавать через $template_result\n- assign_block_vars\n\n\n\n\n!!!!! В настройках игрока при сохранении опять дублируется сообщение в хидере\n\n\n!!! Отключить поддержку вертикальных ресурсбаров в виде таблицы - и сделать недоступным соответствующую комбинаци опций\n- Может переделать старый ресурсбар в виде флекса?\n\nhttps://ourcodeworld.com/articles/read/674/top-7-best-php-http-client-libraries\n\n#ctv\n\n\n[*] going for sass/scss compass sass http://compass-style.org/\n[*] gradle -> npm\n\n\n[*] Интересный факт : если выбрать какую-то постройку, исследование или юнита, а затем навести мышь на другого и не выбирать его, а просто кликнуть \"+\", то в очередь попадет та, которая была выбрана, а не та, на которую был клик\n\nВ чате на тесте писали по поводу прыгающего чата на телефоне. Так вот это происходит если после нажатия на строку ввода (и появления клавиатуры) начать листать страницу чата. Каждые 2-3 секунды экран перекидывает на строку ввода.\nТелефон, браузер хром.\n\n(Chaka[NewSTAR])Ну, оно так и было раньше вообще-то. Список планет на экране был. Другой вопрос, что надо подумать - может добавить в \"Настройках\" отключение списка планет для мобильщиков\n\n\n<VirtualHost *:80>\nServerName c7.local\nDocumentRoot /var/www/c7.local\n\n    <IfModule mpm_event_module>\n        ProxyPassMatch ^/(.*\\.php(/.*)?)$ fcgi://127.0.0.1:9000/var/www/c7.local/$1\n    </IfModule>\n\n    <Directory \"/var/www/c7.local/\">\n        DirectoryIndex index.php index.html index.htm\n    </Directory>\n\n    ErrorLog /var/log/httpd/error.log\n    TransferLog /var/log/httpd/access.log\n</VirtualHost>\n\nhttps://phpdelusions.net/pdo/fetch_modes#FETCH_LAZY\n\n\n#ctv\n\n\n[*] !!!!!!! Добавить оповещение об атаке в правом нижнем углу как position absolute?\n- Туда же - оповещение о новых сообщениях от игроков\n- Настройки как это показывать\n\n!!!!!!!!!!!!!!!!!!!!!!!!!!!!\nСделать ежедневное обновление времени просчёта для игроков, которые давно оффлайн\n\n\nsee .___logs.txt\n\nhttps://stackoverflow.com/questions/14359926/send-http-request-from-php-without-waiting-for-response\nhttps://stackoverflow.com/questions/124462/how-to-make-asynchronous-http-requests-in-php?noredirect=1&lq=1\nIn my testing, using exec(\"curl $url > /dev/null 2>&1 &\"); is one of the fastest solutions here. It's immensely faster (1.9s for 100 iterations) than the post_without_wait() function (14.8s) in the \"accepted\" answer above. AND it's a one-liner... – rinogo Jan 28 '16 at 20:29\nhttps://stackoverflow.com/questions/1019867/is-there-a-way-to-use-shell-exec-without-waiting-for-the-command-to-complete?noredirect=1&lq=1\n"
  },
  {
    "path": "docs/changelog.txt",
    "content": "\nОбозначения\n~~~~~~~~~~~\n[#] Модуль, не входящий в публичную версию/Private module\n[!] Нововведение или важное изменение/New features or important change\n[+] Добавлено/New functions\n[~] Изменено/Changed functions\n[%] Исправление-багфикс/Bugfixes\n[@] Эта информация будет интересна только админам и/или разработчикам/This information is only for administrators and/or developers\n\nProject \"SuperNova.WS\" Release 46 \"Совершеннолетие\"\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n[!] Server Instances. Now several servers can work on one copy of SN engine\n    * Instance-specific files located in `/servers/{domainName}[{_port}]/` folders\n        * `_port` part is optional and used only for servers running on ports other then 80 or 443\n        * {domainName} is SN's domain name\n        * I.e. for `sn.domain.com` folder would be `/servers/sn.domain.com/`\n        * I.e. for `sn.other_domain.com:8080` folder would be `/servers/sn.other_domain.com_8080/`\n    * If SN detects `config.php` file in instance-specific folder it would use it instead of `/config.php`\n    * If SN detectes subfolder `avatars` in instance folder writable by web-server - it would be used for player and Alliance avatars\n        * I.e. if for domain `sn.domain.com` there is folder `/servers/sn.domain.com/avatars` and it writable by web server - it would be used as avatar storage\n[!] Brand new Fleet Dispatcher to prevent locks with main thread\n    * Rewrote dispatcher code in `classes/Fleet/FleetDispatcher.php`\n        * Added separate class `FleetDispatcherEvent` to handle all fleet event-related stuff\n        * Now for each fleet all related users/planets/fleets are locked\n        * Now missile attacks handled each in own transaction - not in large one. Should reduce probability of deadlocks\n    * Now `sys_o_get_updated()` accepts only user/planet IDs as parameters to reduce probability of index/gap DB lock\n    * Adjusted code to work with new internal API & FleetDispatcher\n        * includes/includes/flt_mission_relocate.php\n        * includes/includes/flt_mission_colonize.php\n        * includes/includes/flt_mission_transport.php\n    * Renamed internal fields and replaced internal representation from array to fields\n        * classes/Fleet/MissionData.php\n    * Moved transaction and locks into main missile loop\n        * includes/functions/flt_mission_missile.php\n    * Refactored mission code to class\n    * Now fleet dispatcher internal messages also contains count of processed IPRs\n    * Mission Explore constants moved to Fleet/Constants\n    * FFH now skips FLT_FLEET_ARRIVE events for MT_HOLD/MT_EXPEDITION\n    * Deadlock fixed: sn_fleet_page3() - 'fleet.php'@228: \"SELECT * FROM `game_fleets` WHERE `fleet_end_galaxy` = G AND `fleet_end_system` = S AND `fleet_end_planet` = P AND `fleet_end_type` = T AND `fleet_owner` = FO AND `fleet_mission` IN (1,2,9) AND `fleet_mess` = 0 FOR UPDATE\"\n    * Deadlock fixed: flotenajax.php@97: \"SELECT * FROM {{users}} WHERE `id` = X LIMIT 1 FOR UPDATE\"\n[!] Support for overriding skin images via `override.css`\n[!] Language\n    Added translation to Spanish (computer-generated)\n    Added translation to German (computer-generated)\n[!] JS cacher - merges all JS files into one and caches results\n[!] CSS cacher\n    Internal subsystem to compact several CSS files into one file and cache it\n    Updated skins to accomodate CSS cacher\n    CSS cacher now caches module's CSS files regardless of supplied extension (`.min.css`, `.css` or no extension)\n[!] Tools - Spritify\n    New tool to create sprite PNG and CSS for it from set of images\n    * Adjusted behavior in case if first frame in A-GIF smaller then largest one\n    * Now Spritify honors offset in first frame of A-GIF\n    * Spritify supports `RESTORE_TO_BACKGROUND_COLOR` disposition method\n    * Now Spritify generates pure CSS animations for extracted A-GIF frames, honoring delay between frames\n    * Animated GIF frame expansion - disposal methods `UNSPECIFIED` and `DO_NOT_DISPOSE`\n    * Basic support for animated GIFs - extract frames into sprite line\n[!] Menu\n    Switched to using sprites as menu icons\n[!] PHP 7-compatibility: SN now compatible with PHP 7.4\n    * Fixed no que items on building pages\n    * Fixed que for mass-operations\n    * Fixed other PHP 7 compatibility issues\n    * Added support for `fastcgi_finish_request()` where it supported\n    * More PHP7/MariaDB shenanigans\n\n[+] Admin/userlist\n    Added ability to reverse-sort by all sortable fields\n    Now userlist page opens by default sorted descending last login time\n[+] Admin/Ban\n    * Basic ban-by-ip facility - table `ban_ip`. Supports IPv4 ranges\n[+] Navbar\n    * Navbar now use sprites\n    * This should speed up page loading due to combining multiple standard navbar buttons into one sprite\n\n[~] Chat\n    Enchanced URL parsing/replacement in chat messages (for appropriate ACCESS_LEVELS)\n[~] Logs\n    * In log text \"\\n\" replaced with \"<br />\" on output\n[~] Admin\n    * Now in log viewer user options stored in `user.options` parsed for better readability\n[~] Admin\n    * Adjusted log detail output\n[~] Fleets\n    * `fleet_dispatch_max_time` renamed to `fleet_update_dispatch_time` for uniformity\n[~] Fleets\n    * Now length of each fleet dispatch job can be configured via `fleet_dispatch_max_time` config (float). Default: 3 seconds\n    * `GAME_FLEET_HANDLER_MAX_TIME` is obsolete\n[~] Юниты\n    Поддержка Фестиваля \"ДР-2023\"\n[~] FFH - Added total event amount to FFH timeout message\n[~] Now SN recognize `/avatars` folder in code root as folder for non-instanced servers and prefers it to use before `/image/avatar` folder\n    * It's recommended to move all avatars to this folder because in future `/image/avatar` folder would be deprecated and unsupported\n[~] DB\n    Драйвер db_mysql_v4 удалён\n    Для db_mysql_v5 `TRANSACTION ISOLATION LEVEL` установлен в `REPEATABLE READ`\n[~] Template engine fixes\n    Fixed `error: template->_tpl_load_file()` for module template files loaded on admin page\n    Fixed `error: template->_tpl_load_file()` when module template files tried to load files from core template\n        I.e. alliance manager page tries to load `eco_que` template from core template\n\n[%] HTML\n    Fixed vertical scroll bar on main content cell in latest Chrome versions\n[%] Language\n    * Fixed not loading module l10n files before user init\n[%] HTML\n    Fixed strange jQueryUI behavior on texteareas in new Chrome\n[%] News\n    Now announce editing/copying works again in Chrome\n[%] News\n    Now non-admin users doesn't see announces\n\n[@] Debug\n    Config `debug` option now can be customized\n    * if it set to `1` then `error_reporting` would be set to `E_ALL ^ E_NOTICE ^ E_DEPRECATED ^ E_WARNING`\n    * if it set to different value - then `error_reporting` would be set for this value\n        * Example: `debug` value for `E_ALL ^ E_NOTICE ^ E_DEPRECATED ^ E_WARNING` is `32767 ^ 8 ^ 8192 ^ 2` = 24565\n[@] Code - templates\n    * More robust way to add JS files for compiler\n[@] Replaced JQuery with large version for easier development\n[@] Language\n    * Changed some language variables\n        * language/en/system.mo.php\n        * language/en/tech.mo.php\n        * language/ru/system.mo.php\n        * language/ru/tech.mo.php\n[@] Code\n    `AwardConstants` now tried to be included in the `init.php` - to allow loading of modules that uses their constants\n[@] Code\n    * Functions moved from `SN` to `db_mysql`\n        * `db_transaction_start()`\n        * `db_transaction_commit()`\n        * `db_transaction_rollback()`\n        * `db_transaction_check()`\n    * `SN::DB_TRANSACTION_XXX` constants moved `db_mysql::TRANSACTION_LEVEL_XXX`\n    * `SN::TRANSACTION_XXX` constants moved `db_mysql::TRANSACTION_XXX`\n    * Variables moved from `SN` to `db_mysql`: `$db_in_transaction`, `$transaction_id`, `$transactionDepth`\n[@] Code\n    * `includes/db_deprecated.php` - removed\n    * `classes/DBAL/db_mysql_v5.php` - removed\n    * Addded `pre()` and `pred()` debug functions\n[@] Code\n    * `$sys_user_logged_in` => `SN::$sys_user_logged_in`\n    * Debug - now prepends sql log filename with current instance DB name\n    * `sys_o_get_updated` - simplified error reporting on empty user\n    * Removed `db_queries_user.php` -> `db_get_user_by_where`\n    * `db_queries_user.php` -> `db_user_by_id` now accepts only userId as first param\n    * Streamlined and formatted code a bit\n[@] DB\n    * fighting deadlocks\n        * Table `que` - index I_que_planet_id expanded to (`que_planet_id`, `que_player_id`)\n        * Table `planets` - index `id` dropped\n        * Table `users` - index `I_user_id_name` dropped\n[@] DB\n    * Transaction level for MySQL raised to 'SERIALIZABLE' to reduce deadlocks\n    * Adjusted how SQL queries reported\n[@] Fleet\n    * Added old fields to Fleet\\Fleet to use in low-level functions\n    * Rewrote fleet lock conditions from `?:` to `if`\n    * Replaces some strings with constants\n    * Some code reformatting\n[@] Code\n    * Added transaction depth counter into `SN` class\n[@] Code\n    * Now `DEBUG_SQL_XXX` constants ruled by appropriately named records in `config` table:\n        * `DEBUG_SQL_FILE_LOG` replaces `DEBUG_SQL_ONLINE` - implies `DEBUG_SQL_ERROR` and `DEBUG_SQL_COMMENT_LONG`\n        * `DEBUG_SQL_ERROR` - implies `DEBUG_SQL_COMMENT`\n        * `DEBUG_SQL_COMMENT_LONG` - implies `DEBUG_SQL_COMMENT`\n        * `DEBUG_SQL_COMMENT`\n[@] Code\n    * User options separator `|` move to constant `USER_OPTIONS_SPLIT`\n    * `EVENT_FLT_XXX` constants now are strings for better debug\n[@] DB\n    * Dropped unneeded indexes to reduce deadlocks\n        * counter.counter_id\n        * que.que_id\n        * captain.captain_id\n[@] SQL\n    * Added comments to transaction-related statements - to easier found them later in debug/dump\n    * Added separate table `festival_config` for festival-specific config\n    * Migrated festival config to separate table\n[@] Modules\n    * Added support for `javascript_filenames` in `sn_mvc`:\n        * Modules' JS now added to new subarray\n        * Now modules' JS can be cached too\n        * This fixed errors with modules' JS loaded too early and not working\n[@] Tools - `spritify` update\n[@] Code\n    Streamlined `classConfig` - listed all known config keys with types\n[@] HTML\n    Rearranged JS code/file include in `_40_js.tpl.html`\n[@] Code\n    Basic support for different environments - @see: .env.ini.example\n\nProject \"SuperNova.WS\" Release 45 \"11 years anniversary\"\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n[!] Установка/обновление\n    Улучшен процесс установки/обновления\n    Расширен раздел, посвященный проблемам с установкой или обновлением в файлах /docs/install.md и /docs/install-en.md\n[!] Чат\n    Полностью новый чат\n    Встроенная система команд с поддержкой алиасов команд\n    Встроенная система помощи по командам чата - команда /help\n    Добавлен список игроков в чате с дополнительными иконками статуса и командами управления для админов\n    Возможность игрокам управлять своим состоянием видимости в чате - команда /invisible. Администрация сервера (authlevel > 0) всегда видит невидимок\n    Возможность отправлять приватные сообщения другим игрокам - команда /whisper. Приватные сообщения выделяются специальным образом, видны во всех каналах и сохраняются в истории чата. В приватных сообщенях нельзя употреблять форматирование цветом\n    Администраторы имеют возможность запретить игроку писать в чат на определенный срок или вернуть такую возможность - соответственно, команды /mute и /unmute. Запрет распространяется на все каналы и на возможность писать личные сообщения. Соответствующая иконка в списке игроков лишает его права голоса на 1 час\n    Администраторы имеют возможность блокировать и разблокировать игроков прямо из чата - соответственно, команды /mute и /unmute. Иконка в списке игроков банит его на 1 неделю\n    Системные и приватные сообщения выделяются жирным шрифтом\n    Скорость обновления в AJAX части чата регулируется переменной 'chat_refresh_rate'\n    Игроки из онлайн-списка исчезают сразу после выхода из чата - таймаут попадания в список установлен как удвоенный 'chat_refresh_rate'\n    В чате доступен расширенный функционал BBCode\n[!] Личные сообщения\n    Переработан вид сообщений\n    Добавлена функция игнора игроков в ЛС:\n        - В личных сообщениях добавлена возможность добавить игрока в игнор-лист\n        - Сообщения от игроков в игнор-листе не видны в списке ЛС\n        - Убрать игрока из игнор-листа можно на соответствующей вкладке в \"Настройках\"\n    Добавлена иконка игнора в список сообщений\n[!] Флоты\n    Диспетчер флотов теперь использует системы Task+Worker и является неблокирующим\n    Таким образом, теперь нет визуальных задержек на обсчёт флотов (кому-то из игроков раньше не везло каждые 4 секунды)\n    Так же флоты обсчитываются не кусками по 3 секунды, а в пределах, заданным параметром `fleet_update_max_run_time` (30 секунд по умолчанию)\n[!] Админка/Редактирование планет\n    Переписана и включена админка редактирования планет. Можно редактировать:\n        - Строения\n        - Флот\n        - Оборону\n        - Ресурсы\n    Можно как добавлять юниты, так и удалять их (вводя значения с минусом)\n    На экране просмотра игрока добавлена вкладка со списком планет, клик на которых открыает экран редактирования планеты\n[!] Модули\n    Изменена система версионирования модулей\n    Теперь в качестве версии модуля используется версия билда, в котором были закоммичены изменения\n[!] Код\n    Новая система рабочих процессов Worker\n    Новая система задач и блокировок (Task/Lock). Пока используется только для обсчёта летящих флотов\n    Добавлена поддержка формата изображений WebP\n[!] Документация\n    Инструкция по установке/обновлению переделана в формат MarkDown:\n        - /README -> /README.MD\n        - /docs/install.txt -> /docs/install.md\n        - /docs/install-en.txt -> /docs/install-en.md\n\n\n[+] Альянсы/Поиск\n    В результаты поиска Альянсов и в \"Рекомендуемые Альянсы\" (см. ниже) добавлены две колонки:\n        - Колонка \"Разница в очках\" указывает на разницу между количеством очков у игрока и средним количеством очков на одного игрока в Альянсе. Если она отрицательная - средний игрок в Альянсе имеет больше очков, чем текущий игрок\n        - Колонка \"Рейт\" указывает на соотношение между количеством очков у игрока и средним количеством очков на одного игрока в Альянсе. Если она меньше единицы - средний игрок в Альянсе слабее, чем текущий игрок\n    Для игрока без Альянса на странице поиска Альянса добавлен список \"Рекомендуемые Альянсы\" (далее - РА):\n        - В РА попадают Альянсы, чьё среднее количество очков на игрока в Альянсе не более чем в 5 раз отличается от количества очков текущего игрока\n        - РА сортируется по убыванию модуля разницы очков между количеством очков игрока и средним количеством очков Альянса\n        - список РА показывается и при простом заходе на страницу Альянса без поиска других Альянсов\n    Нужно отметить, что для начинающих игроков список РА будет, скорее всего, пуст - почти нет активных Альянсов, в которые входят только начинающие игроки\n    Чем дальше играет и развивается игрок, тем больше Альянсов будет в этом списке. Однако, по мере приближения к Топу сервера, этот список будет очевидным образом сужаться\n[+] Интерфейс/Обзор планеты\n    В экспериментальном порядке дизайн страницы \"Обзор Планеты\" сделан респонзивным.\n    Т.е. на широких экранах элементы страницы - инфа о планете, список планет и список флотов в полете - будут выстроены в три колонки на всю ширину монитора\n    На мобильных устройствах устройствах с узкими дисплеями элементы страницы будут располагаться друг под другом\n\n[~] Список планет\n    Унифицирован показ статуса планеты (Столица, Луна):\n        - Теперь \"Обзоре планеты\" и \"Империи\" статус планеты показывается соответствующим значком;\n        - На страницах, указанных выше, а так же в меню выбора планеты для Луны Столицы показывается оба значка\n    В \"Обзоре\" теперь название и координаты планеты/луны показываются в самом низу блока планеты\n[~] Планета/Переименование\n    Теперь при переименовании планеты на странице \"Обзор\" и \"Управление планетой\" имя планеты меняется сразу, а не после повторного обновления страницы\n\n[%] Исследования\n    Исправлена ошибка, позволяющая запустить исследование на одной планете в то время, когда на другой планете строится/исследуется Лаборатория или Нанолаборатория при отключенной настройки сервера \"BuildLab\"\n    Исследования блокируются даже если Лаборатория или Нанолаборатория сейчас не строятся, а просто находятся в очереди построек\n\n[@] Код\n    CSS:\n        - Добавлена возможность отключать наложение скинов на элементы управления (input, button) у всех дочерних элементов сразу\n    Навбар:\n        - Количество ресурсов в ресбаре теперь всегда выравнивается по правой стороне ячейки\n    Меню:\n        - Раскраска пунктов меню теперь снимается через CSS, а не через JS\n        - Вид пунктов меню кнопки/ссылки теперь переключается через CSS, а не через JS\n        - Кнопка показа/скрытия меню теперь привязывается к самому меню, а не к абсолютным координатам\n    sn_timer.js:\n        - Добавлена поддержка human-readable времени в sn_timer.js\n        - Исправлена ошибка, когда длина бара в таймере отсчёта могла быть > 100%\n    Темплейты:\n        - Теперь SnTemplate может выводить страницу целиком. Это даёт возможность объединить хидер и футер в один файл\n        - SnTemplate::display() теперь закрывает все существующие буффера вывода\n        - Теперь если у темплейта нет родителя или он не существует, то в качестве fallback темплейта используется темплейт по умолчанию\n        - Добавлена поддержка наследования темплейтов (см. _template.ini):\n            - Поддерживается только один уровень наследования\n            - Поддерживается наследование и дозагрузка _template.css\n        - Добавлена поддержка переключения темплейтов (альфа-версия без вывод в интерфейс игроков)\n        - Убраны глобальные константы, относящиеся к темплейтам: TEMPLATE_NAME, TEMPLATE_DIR, TEMPLATE_PATH\n        - Добавлен метод для добавления JS файлов в темплейт из PHP\n        - Создан новый класс SnTemplate, куда перенесена часть функций в виде статических методов\n    JS таймер отчета (например - показ ресурсов псевдо-онлайн) теперь может работать по имени класса, а не только по ИД - т.е. управлять сразу несколькими элементами\n    Бенчмарк теперь старается вставить данные о производительности в <body>, а не после\n\n\nProject \"SuperNova.WS\" Release 44 \"10 years anniversary\"\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n[#] ad_promo_codes 1a2 - Промо-коды\n    (!) Промо-коды позволяют начислять указанное количество юнитов игроку, который ввёл соответствующий код:\n            - Использование промо-кода через настройки игрока. Пункт меню \"Настройки\"\n            - Максимальное количество использований (0 - без лимита)\n            - Отслеживание использований промо-кода игроками (лог)\n            - Поддерживаемый список юнитов для добавления:\n                - Планетарные ресурсы - металл, кристалл, дейтерий\n                - Тёмная Материя\n                - Корабли\n                - Артефакты\n        Админка:\n            - Список промо-кодов с количеством использований\n            - CRUD для промо-кода\n\n[#] payment_interkassa_form 0a1 - Платёжная система \"Interkassa\"\n    (!) Реализован протокол \"Interkassa Form SCI\"\n        Поддержка режима тестирования\n        Конфигурация отдельным файлом config.php в каталоге модуля. Если конфигурация недоступна - модуль отключается\n        Поддержка выбора способа платежа InterKassa\n        Генерик-плательщик InterKassa\n        Поддержка отображения приблизительной конечной цены\n\n[#] payment_unitpay_form 0a1 - Платёжная система \"UnitPay\"\n    (!) Реализован протокол \"UnitPay Form\"\n        Поддержка режима тестирования\n        Конфигурация отдельным файлом config.php в каталоге модуля. Если конфигурация недоступна - модуль отключается\n        Поддержка выбора способа платежа UnitPay\n        Генерик-плательщик UnitPay\n        Выбора метода платежа в UnitPay через интерфейс платежа СН\n        Теперь при выборе платёжной системы в СН в UnitPay не высвечивается приглашение к выбору другого метода\n\n[#] core_festival 10a5\n    (+) Админка\n            Изменена сортировка активити\n            Добавлена шкала Активити и отметки начала/конца активити\n    (+) Хайспот/Gather\n        Добавлена ловушка для тупых читеров\n        Добавлены флаги автобана\n        Добавлен пересчёт даты из шаблона в текущий год если вычисленная дата больше даты окончания хайспота\n            - Нужно в основном для НГ, что бы после НГ не отключались ништяки\n    (~) Хайспот/День Рождения СН\n        Немного переделана админка\n    (~) Объекты в космосе/Юниты\n        Sputnik теперь невозможно построить игроком\n\n[#] admin_stat 0a4\n    (~) Админка/Средний онлайн\n        График среднего онлайна внесен в таблицу с числами\n        Убраны неиспользуемые элементы\n        Удалён неиспользуемый код\n\n[#] player_award 1a1\n    (~) Улучшены медали за 1-2-3 места для 10-летия СН\n        Добавлены медали для 10-летия СН\n        Добавлены медали и их описания для СНГ-2019\n\n[!] Админка/Активность игрока\n    Добавлен просмотр активности игрока\n        - Работает только при включённом счётчике посещений\n        - Ссылка - на админской странице просмотра игрока\n        - Просмотреть можно активность с 1 января 2018 года\n        - Интервал просмотра активности - 1 час\n        - Пустой квадратик - активности в этот час не было\n        - Заполненный - была активность. При наведении курсора - указывается час и активность в минутах\n            - Из-за особенностей работы счётчика активность может быть более 1 часа\n    Красным баром добавлен процент активности в данном часе\n\n[+] Флоты/САБ\n    Максимальное количество флотов в САБе ограничено 5\n    Теперь САБ ограничивается по сумме очков игроков:\n        - Ограничение распространяется так же и в большую сторону, т.е. при атаке \"слабыми\" игроками \"сильных\"\n        - В остальном - правила и коэфициенты те же, что и при расчёте сильных/слабых игроков (ака \"нуб-защита\")\n[+] Счётчик\n    Оптимизирована работа счётчика посещений:\n        - Добавлена новая таблица `security_query_strings` для записи параметров запроса\n            - `counter` теперь ссылается на записи в ней, а не на полный URL страницы\n        - Изменена таблица `security_player_entry` - исключён ИД пользователя\n            - `counter` теперь ссылается на записи в ней и не содержит поля с ИД устройства, браузера, IP/прокси\n\n[~] Альянсы\n    Добавлена ссылка на страницу статистики Альянсов - список всех Альянсов и их статистика\n[~] Контакты\n    Добавлена ссылка на ЛС членов администрации сервера\n    Немного переверстана страница\n[~] Юниты\n    Добавлена поддержка юнитов, которые игрок не может построить, но может получить в ходе Фестиваля или других активностей - требование UNIT_CAN_NOT_BE_BUILD\n[~] Платежи\n    Добавлена поддержка отображения приблизительной конечной цены при покупке ММ (не для всех платёжных систем)\n    Немного перевёрстана страница платежей\n[~] Безопасность\n    Небольшой апдейт системы безопасности\n    ...а так же всякие мелкие оптимизации.\n\n\nProject \"SuperNova.WS\" Release 43 \"Double Release\"\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n[!] PHP\n    Для работы СН требуется PHP не ниже 5.5\n\n[#] player_login_token 0a8 - Струя удачи\n    (!) Новый модуль\n    (!) Награда за логин\n        Когда игрок логинится в игру первый раз за сутки по серверному времени - он получает в награду 1 логин-токен (далее - просто \"токен\"):\n            - При получении токена игроку на экран выдаётся извещение в виде попапа\n            - Попап появляется каждый раз до тех пор, пока игрок не закроет его нажатием кнопки \"Ок\"\n            - Теперь при входе в игру несколько дней подряд количество токенов, получаемых игроком в день, медленно увеличивается - это и называется \"струя удачи\"\n            - Если игрок не входит в игру хотя бы один - \"струя\" иссякает и количество токенов в день опять падает до 1\n            - Максимальное количество токенов, которые можно получить в день - 6\n        Токены можно использовать для участия в Токен-лотерее (далее - просто \"лотерея\"):\n            - Участие в 1 раунде лотереи стоит 1 токен\n            - Игрок может участвовать в лотерее неограниченное количество раз в день, пока у него хватает токенов\n            - В лотерее игрок может выиграть приз или не выиграть ничего\n            - В настоящее время список призов включает: ресурсы, ТМ, корабли, Артефакты\n            - Шанс на выигрыш и количество юнитов в призе пропорциональны стоимости юнита\n            - Общая стоимость выигрыша масштабируется от скорости добычи ресурсов на сервере и стоимости ТМ в металле\n        Тонкая настройка таблицы выигрышей производится в исходном коде модуля (см.)\n        Добавлена дополнительная фильтрация по процентной вероятности\n        Добавлено масштабирование приза в зависимости от уровня игрока\n        Добавлены разделители тысяч в количестве выигранных юнитов\n        Добавлена иконка к пункту меню\n        Сильно переработан попап с оповещением о получении токена. Теперь в нём выводится практически вся доступная информация по токену\n        Расширена и переработана подсказка игрока на странице\n        Добавлена отладка для просмотра распределения вероятностей\n\n[#] admin_balance 0a6 - Модуль \"Админ - Баланс\"\n    (!) Новый модуль\n    (!) Экран \"Юниты\" с поддержкой кораблей\n        Добавлена страница баланса UBE\n\n[#] core_festival 8b11\n    (!) Хайспот/Объекты в космосе\n        Новый хайспот\n        Теперь Премиум-аккаунт распространяется и на ядерные техи ОвК\n        Нанофабрики и Нанолаборатории для юнитов ОвК теперь действуют как Фабрики и Лаборатории соответственно для обычных юнитов\n        Добавлено описание ивента\n        В Новапедию добален раздел \"ивенты\"\n        Ядерная технология теперь считается фундаментальной и получает бонус от Марса\n        Добавлен вотчдог на попытку получить блокировку флотов\n        Админка\n            - Добавлены Звания к именам ботов и игроков\n            - Теперь заголовки и данные таблиц ЧЛ и ОвК различаются\n    (!) Хайспот/8 Марта\n        Добавлена возможность Кавалеру открыть свой ник\n        Добавлена возможность добавить текстовое поздравление к подарку\n        В списках подарков у Дам и Кавалеров каждый подарок теперь выводится отдельно\n        В списке полученных подарков у Дам:\n            - Выводится поздравление, если оно есть;\n            - Выводится игровой ник подарившего, если он решил его открыть;\n        В списке отправленных подарков у Кавалеров:\n            - Выводится значок (А) после имени Дамы, если подарок был сделан анонимно, т.е. без раскрытия ника;\n            - Выводится поздравление, если оно есть;\n        Теперь когда Кавалер дарит подарок, Даме отправляется личное сообщение с уведомлением. Сообщение содержит:\n            - Подаренную и начисленную (с учётом подарка от АД-ии) суммы ММ;\n            - Имя Кавалера, если он решил его раскрыть;\n            - Текст поздравления, если он есть\n        Изменена иконка в навбаре\n        Полностью переработан код админки и награждения\n        Теперь так же в админке начисляется ММ и выдаются Памятные Знаки Кавалеров\n    (!) Хайспот/Gathering\n        Рефакторинг:\n            - Теперь ништяк выставляется после полной прогрузки страницы\n        Админка:\n            - Добавлена базовая админка со статистикой ивента\n            - В админку добавлен список игроков и количества/стоимости найденных ими ништяков\n            - Добавлена возможность банить игроков в хайспоте\n            - Изменена сортировка получателей ништяков в админке. Теперь они сначала сортируются по полученной ТМ, а затем - по количеству кликов\n        Интерфейс:\n            - Кнопки хайспота теперь добавляются в основную часть навбара, а не в префиксную\n            - Теперь количество ТМ и ММ+ТМ в навбаре обновляется сразу после успешной подборки ништяка\n        Хеллоуин:\n            - Добавлены некоторые изображения в большем разрешении\n            - Изменены картинки подтверждения/провала при клике на ништяки\n            - Перебалансировано количество ништяков для Хэллоуина\n    (+) Хайспот/Чёрная Луна\n        Админка\n            - Подключен рандомайзер к экрану администрирования\n            - Реализован закат лун в автоматизированном режиме\n        Изменён алгоритм генерации брэкетов:\n            - Теперь верхняя граница первого брэкета определяется исходя из количества поинтов у топ-игрока\n            - Таким образом, топ-игрок теперь принципиально имеет шанс поучаствовать в ивенте, если не сильно оторвался от других игроков\n            - Все новички добавляются в самый последний из рассчитанных брэкетов\n            - Игроки с нулевым количеством поинтов участие в расчёте не принимают - вероятнее всего это заброшенные аккаунты\n    (+) Общий\n        Поддержка скидок на Премиум-аккаунты и Массовые Операции\n        Добавлена возможность Хайспотам добавлять переменные в общий пул\n    (+) Приз за ММ\n        Админка: добавлено начисление медалей участникам ивента и призёрам\n    (+) Админка\n        Улучшена общая админка для всех хайспотов\n    (~) Смайлики: В пути к смайликам можно использовать плейсхолдер {festival} - он будет оттранслирован в относительный путь, который требуется подсистемой смайликов\n\n[#] chat_advanced 6a4\n    Добавлена трансляция в сообщении полного URL с ссылкой на новость в BBCode\n    Добавлена трансляция в сообщении сокращённого URL news://XXX в BBCode\n    Теперь при обновлении чата так же обновляется количество игроков онлайн и общее количество игроков\n    Доработан вид сообщений в чате:\n        - Выровнены время сообщения, ник (и иконки) и само сообщение\n        - Уменьшен размер передаваемых данных для приходящих сообщений\n        - Улучшен генерируемый HTML-код\n        - Оптимизированы CSS-стили\n    Улучшено отображение чата для экранов разных размеров\n    Добавлено автоматическое скрытие правой панели (списка онлайн игроков) на мелких экранах с возможностью показать список онлайн\n\n[#] player_award 0d7\n    (+) Приз за ММ\n        Добавлены медали для СНГ-2018\n    (+) 8 марта\n        Универсальные медали и памятные знаки для ивента 8 марта\n        Разделены награды за количество ММ и за персон для Кавалеров и Дам\n        Добавлены все нужные изображения медалей\n\n    (~) Картинки\n        Переделаны миниатюры для медалей на 7-8 лет СН - улучшена чёткость\n        Пережаты картинки для уменьшения размера\n\n[#] menu_customize 0d8\n    (+) Сортировка пунктов\n        Теперь если в меню появляется новый пункт, которого нет в сортировке, он добавляется не в самый низ сортировки, а сразу вслед за пунктом, за которым он располагается в оригинальном меню\n\n[#] interface_batch_operations 1a4\n    (~) Результаты\n        Добавлены разделители тысяч в отчёт о работе МО\n\n\n[!] Воинские звания\n    В игру добавлены уровни игроков и военные звания для каждого уровня\n    Уровни присваиваются в зависимости от общего количества очков игрока\n    Если в настройках сервера отключён показ статистики для команды сервера, то все члены команды имеют 20й уровень и соответствующее звание вне зависимости от реального количества очков\n    Звания показываются перед ником игрока везде, где это имеет смысл\n    Так же картинка звания показывается на странице профиля игрока (ака \"Император\")\n    Всего в игре 21 звание - от Курсанта (уровень 0) до Генералиссимуса (уровень 20)\n    Посмотреть полный список званий и соответствующих им уровней можно кликнув на картинку звания на странице Император\n    Добавлен дополнительный уменьшенный комплект иконок специально для ников - для большей отзывчивости интерфейса\n    На иконки воинских званий в никах добавлены всплывающие подсказки с названиями званийи и уровнем\n[!] Столица\n    Теперь на Столице скорость строительства зданий, флота и обороны повышена в два раза\n    Теперь на Столице скорость добычи ресурсов повышена в два раза\n    Добавлена индикация Столицы в имени планеты иконкой ♕ &#9813;\n        - В списке планет в навбаре\n        - На странице \"Обзор планеты\"\n        - На странице \"Империя\"\n        - На странице \"Вселенная\"\n[!] ЛС\n    Добавлено листание сообщений по страницам. Каждая страница вмещает по 10 сообщений\n    На странице просмотра сообщений в дроп-дауне выделены цветом удаление всех сообщений категории и всех личных сообщений\n    Убрано удаление неотмеченных сообщений. Теперь просто отметить мультигалочкой (галочкой в заголовке) все сообщения на странице и удалить их\n    При просмотре сообщений оставлена лишь кнопка \"Удалить отмеченные сообщения\" ввиду бессмысленности остальных опций\n    Если нет отмеченных сообщений кнопка \"Удалить...\" неактивна\n    При просмотре сообщений добавлена кнопка \"Показать все\" - для показа всех сообщений в категории\n    Админитраторам включены BBCode и поддержка ссылок в отправляемых ими сообщениях\n    Добавлены разделители тысяч в количестве юнитов в шпионском отчёте\n[!] Админка\n    - Альянсы:\n        - Добавлена страница для просмотра списка Альянсов\n        - Добавлена страница для просмотра отдельного Альянса:\n            - Дамп записи Альянса;\n                - В админке теперь отображается форматирование текстов Альянса\n            - Список игроков с рангами, онлайном, отпуском, баном;\n            - Передача Альянса другому игроку;\n            - Цветовое кодирование Главы:\n                - Зеленый - активен на протяжении 30 дней;\n                - Красный - неактивен.\n    - Добыча игроков:\n        Страница просмотра общей добычи игроков\n        Добавлена колонка с суммарной производительностью в металле\n        На экран добавлены сортировки\n    - Модули\n        Новый экран со списком модулей, доступных в игре и их статусе\n        Есть сортировка по имени модуля и по его активности\n        Сортировка модулей по активности сделана дефолтной\n    - Перепаковка счётчиков посещений (СП)\n        См. страницу \"Утилиты\". Работает на машинах с 2Gb+ под PHP\n        Теперь не пакуются хиты свежее трёх недель\n        Теперь так же считаются общие хиты за визит\n        Теперь записи удаляются и обновляются пакетом\n        Теперь корректно обрабатываются визиты, находящиеся на стыке пакетов\n        Теперь выводится результат перепаковки\n        Теперь считается количество обработанных записей\n        Теперь неизменённые записи не записываются в БД\n        Добавлен счётчик обработанных и пропущенных записей\n        Добавлено новое условие прерывания обработки - если разница между ID обработанных записей меньше размера пакета\n        Добавлено дополнительное время на операцию удаления записей\n    - Платежи\n        Добавлена статистика на страницу платежей\n        В платежи добавлена листалка\n    - Список игроков\n        Добавлен емейл игрока в список игроков (для auth_level 3)\n        Добавлен статус отпуска игрока в список игроков (для auth_level 3) и сортировка по этому полю\n        К никам игроков добавлены иконки званий, отпуска, ДР и наград\n        Уменьшен размер страницы\n    - Просмотр игрока\n        Добавлена информация об аккаунте игрока\n        Добавлена возможность сменить пароль на аккаунте\n        Рефакторинг страницы просмотра игрока\n    - Настройки\n        Добавлена возможность настраивать опции смены имени игроком\n    - Обслуживание\n        Очистка СП от записей несуществующих пользователей\n    - Меню\n        Название уровня члена АД-ии (Модератор, Оператор итд) теперь является ссылкой на возврат в игру\n        Если в админском меню не стоит AUTH_LEVEL - прописывается высший (AUTH_LEVEL_ADMINISTRATOR = 3)\n    Обслуживание теперь не удаляет админские сообщения\n[!] Настройки\n    Рефакторинг кода и переработка дизайна страницы настроек\n    Теперь если смена имени игрока (ника) запрещена настройками сервера - то так и пишется в Настройках Игрока\n[!] Скины\n    Новые изображения Наёмников и Губренаторов в скине EpicBlue\n    Новые изображения Наёмников и Губренаторов в скине supernova-ivash\n    Добавлены большие изображения для Губернаторов\n    Добавлены полноформатные изображения Наёмников\n[!] Модули\n    Переписана и отрефакторена подсистема управления модулями\n    Добавлены новые методы в базовый класс модулей sn_module\n    Класс sn_module:\n        - Убрана зависимость от полей манифеста модуля:\n            - 'load_order' => getLoadOrder(), self::M_LOAD_ORDER\n    Класс Modules\\Manager:\n        - В этот класс вынесен функционал управления модулями из класса sn_module\n        - Включает в себя так же функционал убраных глобальных переменных $sn_module и $sn_module_list\n    Убрана поддержка модулей из одного файла и модулей без структуры sn_modules\n    sn_sys_load_php_files() - убран блок для $modules = true;\n    Добавлена поддержка MVC-опций в модулях (подмассив 'mvc' манифеста)\n\n[+] Флоты\n    Теперь статус \"Новый игрок\" рассчитывается с учётом скорости добычи ресурсов\n    Интерфейс: теперь при нехватке места в трюме под топливо на странице выбора миссии выводится соответствующее сообщение\n    Миссии:\n        - Шпионаж\n            Миссия переписана с нуля\n            Теперь в таблице `messages` сохраняются данные, а не HTML-код:\n                - Значительно уменьшен объем данных, сохраняемых в БД;\n                - Увеличено количество информации в БД;\n                - Рендеринг сообщения осуществляется непосредственно при просмотре, а не при сканировании;\n                - Уменьшено время на обсчёт миссии (см.пред.пункт);\n            Исправлена ошибка, при которой флоты с шансом обнаружения выше 100% не уничтожались\n            Теперь на электронную почту не отправляются полные шпионские отчёты, а лишь уведомление о сообщении типа \"шпионаж\" (без различия входящих и исходящих)\n            Добавлена поддержка анти-шпионажа\n        - Экспедиции: теперь в Экспедиции не могут быть найдены ивентовые или уникальные мировые корабли\n[+] Вселенная/Луны\n    Теперь при создании лун учитывается текущая скорость добычи ресурсов:\n        - Например, для сервера со скоростью добычи x10 стоимость создания луны так же удесятеряется\n        - Исправлена древняя ошибка, из-за которой луна после боя могла получится больше, чем на неё потрачено ресурсов\n    Унифицированы алгоритмы создания лун после боя, через Артефакты, через Админку итд\n[+] Империя\n    В тайтл страницы (заголовок окна браузера) добавлено название страницы \"Обзор Империи\"\n    Добавлены разделители тысяч ко всем количествам юнитов: имеющихся на планете, стоящих в очереди, летящих с флотами\n    К количеству ресурсов на планете и производству добавлена индикация ресурсов на летящих на планету флотах\n    Добавлен новый уровень цветового кодирования процентов производства. Теперь схема выглядит так:\n        - Голубой - 100% производства\n        - Зеленый - 90-80%%;\n        - Жёлтый - 70-50%%;\n        - Оранжевый - 40-10%%;\n        - Красный - 0%;\n    Теперь фон дроп-дауна так же подсвечивается в зависимости от процента производства по вышеописанной схеме\n    Добавлено цветовое кодирование для процентов в дропдауне - см. выше. Цвет букв более яркий для лучшего чтения на фоне текущего производства\n[+] Новости\n    Добавлена листалка на страницу Новостей. Теперь можно посмотреть Новости за всю историю игры!\n    Добавлена возможность ссылаться на отдельную новость:\n        - Ссылка на новость \"прячется\" под датой новости;\n        - При открытии - открывается только одна указанная Новость;\n        - Под заголовком страницы добавляется ссылка \"Все новости\" для возврата к полному списку новостей;\n    Добавлен заголовок страницы \"Новости\"\n    Форма добавления новости спрятана под кнопку \"Добавить новость\" - что бы не мозолила АД-ии глаза\n    Цвет ссылки \"Подробнее...\" изменен на ярко-голубой для лучшей читаемости\n[+] Симулятор\n    Добавлен чекбокс \"Симуляция\", отключающий фактор случайности и гарантирующий повторяемость результатов Симулятора\n    Теперь при создании Луны указывается количество обломков, которое образовалось бы без создания Луны\n[+] Вселенная\n    Перевёрстана панель выбора Галактики/Системы:\n        - Теперь она будет масштабироваться в зависимости от размера экрана\n        - Ну и на вид стала поприятнее\n    Теперь имена Игроков/Альянсов в случае длинных названий не \"ломаются\" по статусам, а остаются на одной строке\n[+] Награды/Бессмертный\n    Теперь не меняется дата/время установки статуса после повторных начислений ММ\n    Теперь так же при установке статуса начисляется памятный знак\n    Памятный знак будет начислен всем Бессмертным при апдейте\n    Лимит для получения статуса \"Бессмертный\" увеличен до 200.000 ММ\n    Количество ММ для награды Бессмертного теперь берется из конфигурации\n[+] BBCode\n    Добавлена поддержка BBCode [news=XXX] для вставки прямой ссылки на Новость\n    Добавлена поддержка сокращённого URL news://XXX для вставки прямой ссылки на Новость\n[+] Чат\n    Добавлена трансляция в сообщении полного URL с ссылкой на новость в BBCode\n    Добавлена трансляция в сообщении сокращённого URL news://XXX в BBCode\n    Оптимизированы CSS-стили\n    Вынесен JS-код из темплейта\n    Доработан вид сообщений в чате:\n        - Выровнены время сообщения, ник (и иконки) и само сообщение\n        - Уменьшен размер передаваемых данных для приходящих сообщений\n        - Улучшен генерируемый HTML-код\n[+] Апдейтер\n    Добавлен механизм патчей: проверка существования патча и его регистрация\n    Отформатирован код\n    Удалены апдейты по релиз 39 включительно\n    Проверка верcии движка\n        - /ajax_version_check.php преобразован в класс \\Tools\\VersionCheckerDeprecated\n        - В init.php переделан вызов версион-чекера\n[+] Респонзивный дизайн\n    Улучшено отображение чата для экранов разных размеров\n    Добавлено автоматическое скрытие правой панели (списка онлайн игроков) на мелких экранах с возможностью показать список онлайн\n\n[~] Интерфейс\n    Наёмники+Планы\n        - Добавлен список требований при покупке перманентных Наёмников\n        - Отключены элементы покупки при неудовлетворённых требованиях по юнитам (в случае перманентных Наёмников)\n        - Убрано дублирование сообщения об ошибках\n    Окно сообщений: теперь при отключении сервера не показывается навбар - не производится лишних расчётов/обращений к БД\n    Квесты: добавлены разделители тысяч к целям и наградам квестов\n    Навбар: добавлена поддержка изменения ТМ/ММ во время работы страницы\n    Список планет: в списке планет на иконках построек/верфи/обороны добавлено количество активных слотов в очереди\n    Статистика\n        - Изменен порядок вывода иконок для имени игрока\n        - Убрана отдельная колонка для ссылки на страницу игрока. Теперь клик на нике является такой ссылкой\n        - Добавлено выделение названия Альянса как ссылки\n        - Изменен порядок вывода иконок для имени игрока\n    Вид ников во всей игре унифицирован с новым видом на странице \"Статистика\"\n    Постройки\n        - Теперь пункты в списке требований к постройке являются ссылками на статью Новапедии о соответствующем юните/сущности игры\n        - Теперь при сносе здания учитываются наличие Губернатора и статус Столицы - снос при Губернаторе и/или на Столице происходит быстрее\n        - Теперь время строительства и сноса округляется вверх, а не математически. Это значит, что в среднем в половине случаев время постройки/сноса увеличится на 1 секунду\n    Исследования\n        - Изменено название страницы на 'Исследование технологий', что бы не было путаницы между пунктами меню \"Исследования\" и \"Технологии\"\n        - Добавлена ссылка на закладку списка Технологий в Новапедии\n    Луны: добавлена индикация Луны в имени планеты иконкой ☽ U+263D &#9789;\n        - В списке планет в навбаре\n        - На странице \"Обзор планеты\"\n        - На странице \"Империя\"\n    Меню: рамка меню изменена на тонкую\n    Вёрстка\n        - Сделан более универсальный патч, который снимет потенциальные проблемы с исчезающим скроллом в Сркщьу 65 на любых экранах\n        - Соответственно - исправлена ошибка с пропаданием скролла в режиме Сканирования Вселенной на маленьких экранах в Сркщьу\n[~] Артефакты\n    Артефакты класса \"Крюк\" теперь не меняют количество обломков на орбите - т.е. Луна появляется без траты обломков\n[~] О сервере\n    В футере и на странице информации о сервере к версии движка теперь добавляется номер патча БД\n[~] Бенчмарк\n    Добавлена дата и время начала операции, для которой проводится бенчмарк\n[~] Изображения\n    Пережаты изображения без потерь качества\n[~] Звуки\n    Немного пережаты звуки\n[~] Статистика/Обновление\n    Теперь при сбое всех стандартных способов уведомления о занятости сервера выдаётся стандартная простая заглушка\n\n[-] Поиск\n    В поиске отключён поиск ботов\n[-] Обучение\n    Обучение по умолчанию отключено\n\n...и множество других мелких правок. Посмотреть изменения в коде и мелкие правки можно в файле docs/changelog_dev.txt\n\n\n\nProject \"SuperNova.WS\" Release 42 \"8th anniversary\"\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n[#] core_festival 5a5.1 - Модуль \"Фестивали\"\n    (!) Хайспот/Ивент \"Чёрная Луна\"\n        Во вселенной появляются специальные Чёрные Луны, с которых при нахождении флота в Удержании капает ТМ игрокам\n        Админка:\n            - Команда на вычисление лун\n            - Вывод вычисленных лун\n            - Подключено и оттестировано создание лун\n            - Добавлена индикация активных лун\n            - Список флотов на каждой луне - включая владельца, доход, размер флота\n            - Список доходов каждого игрока, держащего флот в удержании\n            - Добавлена индикация поля обломков вокруг каждой из лун\n            - Добавлена сортировка пользователей по количеству ТМ в тик по убыванию\n            - Изменено цветовое кодирование процента получения ТМ:\n                - Красный - больше 100%;\n                - Синий - ровно 100%;\n                - Зеленый - >= 50%;\n                - Жёлтый - >= 10%;\n                - Оранжевый - >= 10%.\n            - Изменено цветовое кодирование имени игрока в таблице ЧЛ:\n                - Красный - игрок стоит ниже своего брэкета! Такого быть не должно - ошибка!\n                - Без цвета/Белый - игрок стоит в своём брэкете;\n                - Зеленый - на один брэкет выше;\n                - Желтый - на два брэкета выше;\n                - Оранжевый - на три или более брэкета выше.\n        Теперь тики по необходимости обновляют онлайн игроков-ботов - что бы те не превратились в и-шки\n\n    (!) Highspot \"8 Марта\":\n        При включённом Ивенте в навбаре в конце основного блока кнопок появляется новая кнопка, ведущая на страницу Ивента\n        На странице Ивента игрок с указанным в профиле мужским полом может подарить метаматерию игроку-женщине\n        Подарок будет снят со счёта игрока-мужчины и зачислен на счёт игрока-женщине\n        При зачислении игроку-женщине будет начислено на 25% больше ММ\n        Минимальное количество снимаемой метаматерии - 20.000 ММ\n        Игрок-мужчина может увидеть кому он делал подарки, сколько было списано со счёта и сколько было начислено\n        Добавлена информация об общей сумме сделанных подарков на мужской странице\n        Добавлена информация на женской странице о сумме и количестве полученной ММ\n        На мужской странице добавлена индикация позиции в конкурсе Кавалеров\n        На женской странице добавлена индикация позиции в конкурсе \"Королева Весны\"\n        Добавлена админская страница для автоматизированного награждения победителей конкурса\n\n    (!) Хайспот/Приз за ММ\n        Из хайспота SuperBorn выделен код, отвечающий за индикацию Памятного Знака за полученную ММ\n        На странице \"Император\" добавлен раздел \"Приз за ММ\":\n            - Индикация количества полученной ММ и названию соответствующей Медали;\n            - Индикация места в общей таблице призёров;\n            - Переверстан вывод данных в виде таблице;\n            - Добавлен HTML-якорь \"mm_race\";\n            - При нулевом количестве ММ у игрока в рамках Конкурса - на странице Императора показывается заглушка\n        Добавлена админка для просмотра общей таблицы призёров\n\n    (~) Админка:\n        Фестивали сортируются от новых к старым\n        Исправлена ошибка замены пункта меню \"Тёмная Материя\" на пункт меню \"Фестивали\" - теперь новый пункт добавляется сверху\n    (~) Прочее:\n        Хайспот/Паззл: Иконки хайспота в навбаре перенесены из отдельного блока в общий блок\n        День Космонавтики: Добавлены смайлы к Дню Космонавтики\n        Фестивали: Фестиваль считается активным только если имеет хоть один активный хайспот\n\n\n[#] admin_stat 0a0.2 - Модуль \"Массовые операции\"\n    Из админки перенеыены отдельно стоящие отчёты \"Онлайн игроков\", \"Регистрации игроков\" и \"Игроки-боты\"\n    Добавлены файлы локализации\n\n[#] player_award 0c9 - Модуль \"Награды игроков\"\n    (+) ДР СН\n        Добавлены памятный знаки для ивента \"День Рождения СН\" 2016 и 2017 годов\n    (+) Кавалер-2017\n        Памятный знак за подарок Даме в ходе ивента \"8 марта 2017 года\"\n        Медаль \"Лучший Кавалер-2017\"\n        Добавлена медаль \"Королева Весны-2017\"\n    (~) Картинки\n        Еще немного уменьшены размеры картинок\n    (@) Код\n        Добавлены методы для взаимодействия с другими модулями\n        Почищен код\n\n[!] Обучение\n    Добавлен блок \"Обучение\" в навбар:\n        - Заголовок с кнопкой \"Закрыть\" (действует только на текущей странице)\n        - Основной текстовой блок с картинкой Советника\n        - Блок с кнопками\n        - Футер\n    В \"Настройки\" добавлены опции:\n        - Новые опции располагаются на вкладке \"Интерфейс\", подвкладка \"Обучение\"\n        - Опция отключения Обучения\n        - Опция показа Обучения во всплывающем окне (popup)\n    Работающие кнопки \"Вперед\", \"Назад\", \"Закончить\"\n    Подгрузка новых текстов через AJAX\n    Теперь обучение можно открыть в окне\n        - По умолчанию окно открывается в правом нижнем углу страницы\n        - Окно прилеплено и не меняет местоположение при скролле\n        - Окно можно перемещать по странице, таская его за заголовок\n        - Статус обучения (на странице в навбаре или в окне), а так же положение окна на экране сохраняется в куках отдельно на каждом устройстве\n    Теперь можно задать ID первого текста из таблицы `text` в обучаловке - опция 'tutorial_first_item' в таблице `config`\n    Теперь при сбросе обучения так же сбрасывается статус \"обучение завершено\"\n    Текст и заголовок Обучения теперь могут использовать bbCode\n    В настояшее время \"Обучение\" включает только инструкцию по пользованию \"Обучением\"\n[!] Навбар\n    Новый ресурсбар (количество ресурсов на планете/в Альянсе) - теперь встроенный в навбар\n    Выводится количество ресурсов на планете, а для электроэнергии - баланс (т.е. производство минус потребление)\n    При наведении курсора мыши на ячейку с ресурсом (металл, кристалл, дейтерий) появляется попап, в котором указывается:\n        - Полное название ресурса;\n        - Количество ресурсов на складе;\n        - Размер склада;\n        - Заполнение склада в процентах\n    При открытом попапе клик на ячейку закроет попап\n    Так же попап открывается при клике/тапе на ячейке - для мобильных устройств\n    Планетбар переверстан с использованием flex и поддерживает адаптивный дизайн\n    Верстка: металл+кристалл и дейтерий+энергия/тм объединены в блоки - так красивее работает адаптивный дизайн\n    CSS и JS из темплейта вынесены в соответствующие файлы\n    В новый планетбар добавлена поддержка вертикального расположения в навбаре\n    Добавлены разделители тысяч в попапе нового планетбара\n    Включен стандартный попап для энергии в новом планетбаре\n    Вместо букв-маркеров ресурсов (М, К, Д итд) используются иконки\n    Новый планетбар используется по умолчанию\n    В \"Настройки\" добавлена опция включения старого планетбара\n    В настройках добавлена опция показа ёмкости складов в ресурсбаре. Опция действует как в новом, так и в старом ресурсбарах\n    Полностью переверстан новый ресурсбар\n    Улучшена поддержка IE в старом/новом ресурсбарах\n    В старом ресурсбаре теперь так же работают всплывающие окна с подсказками\n    Теперь в ресурсбаре Альянсов не показывается ненужная строка ёмкости хранилищ\n    В ресурсбаре улучшена индикация заполнения складов:\n        - Если количество ресурсов больше объема склада (> 100%) - размер склада будет выведен красным\n        - Если ресурсов <= 100%, но > 90% - оранжевым\n        - <= 90%, но > 75% - желтым\n        - <= 75%, но > 50% - синим\n        - <= 50% - зеленым\n        - И, наконец, если склада нет и количество ресурсов на планете равно нулю - размер склада будет выведен белым\n    Ресурсбар:\n        - Цветовое кодирование к количеству производимой энергии в ресурсбаре\n        - Цветовое кодирование к попапам в ресурсбаре\n        - Отдельный попап для энергии, выводящий потребление\n[!] Вселенная\n    Добавлен режим сканирования Вселенной:\n        - Вход в режим осуществляется нажатием кнопки \"Включить режима сканирования\"\n        - Выход - нажатием кнопки \"Выйти из режима сканирования\"\n        - Вид Вселенной переключается на минималистический\n        - Убираются лишние вертикальные отступы в ячейках\n        - В режиме сканирования отключаются: меню, навбар, подсказки\n        - Так же в режиме сканирования отключаются рамки вокруг таблиц, если они были включены в Настройках\n    В режиме сканирования теперь работают попапы на лунах/планетах/итд\n[!] Квесты\n    Полностью переделан интерфейс квестов\n    Новый статус квеста - \"Начат\":\n        - Квест отмечается как \"Начат\", если был построен хоть один юнит из условий квеста;\n        - Отметка выставляется в момент завершения постройки первого юнита\n        - Уже построенные юниты не учитываются - нужно построить хотя бы один юнит, что бы сменить статус квеста\n        - Количество начатых квестов отображаетя в навбаре на кнопке квестов желтым цветом\n    На страницу просмотра квестов добавлен фильтр квестов по статусу:\n        - Состояние фильтра запоминается между визитами на страницу квестов\n    В списке квестов к фильтру добавлен вариант \"Все, кроме выполненных\"\n[!] bbCode\n    Переписана работа с bbCode\n    Класс BBCodeParser переделан в динамический\n    Работа со списком смайликов и bbCode вынесены в новый класс Design\n    Из класса classSupernova убран теперь неиспользуемый массив $design\n    Базовые смайлики и bbCode вынесены в vars.php\n    Новый bbCode - [urlw=URL]text[/urlw] - разворачивается в активную ссылку, которая переодит по URL в текущем окне\n    Новый префикс \"faq://link.html\" - разворачивается в активную ссылку на \"link.html\" в ЧаВо (FAQ) - если ЧаВо сконфигурировано в настройках сервера\n    Активная ссылка на боевой отчёт теперь открывается в текущем окне\n    Для bbCode [c] появилась полная версия [color]\n    Теперь URL боевого отчёта с текущего сервера автоматически преобразуется в активную ссылку на боевой отчёт\n    Теперь ссылки bbCode, открывающиеся в новом окне, подчёркиваются двойной линией\n[!] Админка\n    Небольшой тест функиональности phpBB Template Engine в \"Утилитах\"\n    Добавлена страница с балансом материи\n    Рефакторинг страницы просмотра информации об игроке\n    Переверстаны страницы:\n        - Администрирование чата\n        - Утилиты\n    Пункт меню \"Тёмная материя\" стал заголовком\n    Теперь при неудачной попытке начислить ТМ/ММ назад в форму возвращается причина начисления\n    Добавлена опция настройки логотипа сервера в меню - опция 'menu_server_logo' в таблице `config`:\n        - Пустое значение - использовать логотип по умолчанию;\n        - В остальных случаях строка трактуется, как относительный путь к картинке (от корня игры), например - 'design/images/supernova.png'\n        - Опция 'menu_server_logo_disabled' позволяет отключить логотип в меню\n    Добавлена опция отключения названия сервера в меню - опция 'menu_server_name_disabled' в таблице `config`:\n    Добавлена опция отключения даты запуска в меню - опция 'menu_launch_date_disabled' в таблице `config`:\n    Изменение ММ:\n        - Полностью переписано начисление ММ\n        - Теперь можно изменять ММ так же по ID или имени игрока\n        - Теперь перед изменением ММ можно посмотреть, что будет изменено - и лишь потом подтвердить изменения\n    Админка/Изменение ТМ: Переписано изменение ТМ\n[!] Лицензия\n    Добавлен файл docs/credits.txt для списка используемых материалов и соответствующих копирайтов\n[!] Вёрстка\n    Перевёрстан базовый темплейт:\n        - Переделан на DIV-ах\n        - Меню, навбар и дополнения (новости, заметки итд) подключаются теперь в _global_header\n[!] Темплейты\n    Класс PTLTag:\n        - Замена элементов темплейта их значением\n        - Формат: {<prefix>{<text>|[elementID]|...}>\n            - <prefix> используется для идентификации. Должен быть отрезан перед передачей в PTLTag\n            - <text> - текст\n            - [elementID] - название элемента в квадратных скобках. Поддерживаются следующие элементы:\n                - Корневые значения {VAR} -> [VAR]\n                - Переменные темплейта из $DEFINE - {$VAR} -> [$VAR]\n                - Блоковые переменные на текущем уровне - {block.VAR] -> [block.VAR]\n            - Количество и комбинации текста и названий элементов могут быть любыми:\n                - На примере тэга {I_xxx}: {I_unit_[unit.ID]_red_[UNIT_SIZE]}\n            - Сейчас поддерживается в тэгах {R_xxx} и {I_xxx} - см. примеры ниже\n    Новый тег косвенной адресации {R_[XXX]}:\n        - Позволяет в рантайме выводить значение из элемента, чьё имя генерируется во время исполнения темплейта\n        - Пример:\n            - Пусть у нас в темплейте есть тэг {R_[RENDER]}\n            - Пусть во время исполнения корневой элемент 'VAR' равен 'VALUE';\n            - Пусть во время исполнения корневой элемент 'RENDER' равен '{VAR}'\n            - Тогда во время компиляции сгенерируется исполнимый код, который во время исполнения темплейта проделает следующее:\n                - Возьмет значение переменной 'RENDER', т.е. '{VAR}'\n                - Динамически скомпилирует код для вывода переменной '{VAR}'\n                - Исполнит его и выведет значение элемента 'VAR', т.е. в нашем конкретном случае - выведет 'VALUE'\n        - Тэг надо использовать с осторожностью, избегай вывода переменных, полученных напрямую от пользователя\n        - Содержимое элемента может быть любым тэгом из поддерживаемых темплейтом: {$VAR}, {block.VAR}, {D_xxx}, {I_xxx} итд\n    Тэг изображения {I_xxx} теперь работает через класс PTLTag\n        - Пример:\n            - Пусть в skin.ini есть записи\n                s_black_moon = \"planeten/small/s_black_moon.jpg\"\n                black_moon = \"planeten/black_moon.jpg\"\n            - Пусть в темплейте есть директива <!-- DEFINE $BLACK = 'black_moon' -->\n            - Тогда тэг {I_[$BLACK]} выведет '<HTTP путь к текущему скину>planeten/black_moon.jpg'\n            - А вот тэг {I_s_[$BLACK]} выведет '<HTTP путь к текущему скину>planeten/small/s_black_moon.jpg'\n        - В квадратных скобках можно использовать любые имена элментов, поддерживаемых PTLTag\n\n[+] Альянсы\n    Включена отключенная ранее опция рассылки сообщений членам Альянса - кнопка \"Послать сообщения всему Альянсу\" в блоке \"Управлеение Альянсом\"\n    Переверстаны некоторые страницы\n    Добавлены более понятные сообщения об ошибках при попытках сменить имя или тэг Альянса на уже существующие в игре\n[+] Интерфейс\n    Теперь так же работает перетаскивание элементов тапом на тач-скринах\n    В частности - теперь можно поменять положение окна Советника на мобильных устройствах\n[+] Обзор планеты\n    В список ресурсов на планете добавились всплывающие окна с подсказками\n\n[~] Новости\n    На обзоре страницы теперь можно ограничивать показываемые новости так же по времени публикации:\n        - Настройка game_news_overview_show в таблице config устанавливает давность новости;\n        - Задаётся в секундах. По умолчанию - новости давнее 2 недель (1209600 секунд) не показываются;\n        - При установке в 0 показывает все новости;\n    Переделана кнопка \"Закрыть\" в блоке новостей - вынесены стили в CSS, а обработчик нажатия - в JS\n[~] Флоты/Менеджер флотов\n    Теперь время работы менеджера флотов вычисляется непосредственно с начала его работы, а маскимальное время работы уменьшено до 3 секунд (регулируется константой GAME_FLEET_HANDLER_MAX_TIME)\n[~] Флоты/САБ\n    Переверстана страница подбора игроков в САБ\n[~] Вселенная/Переименование\n    Переверстана страница переименования Галактик/систем\n[~] UBE/Боевой отчёт\n    Добавлен BBCode для вставки в чат ссылки на бой\n\n\n\nProject \"SuperNova.WS\" Release 41 \"Festival batch fleet navbar\"\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n[#] core_festival 3a3.4 - Модуль \"Фестивали\"\n    Фестиваль - это тематическое объединение нескольких хайспотов, проходящих в одно и то же время\n    Хайспот - обощающее название для акций и ивентов. Реализованы следующие хайспоты в составе базового модуля:\n        - Изменение скоростей добычи, постройки, полёта флотов;\n        - Временное добавление сезонных смайликов в чат:\n            - Новогодний набор;\n            - Набор на Хэллоуин;\n        - Изменение стоимости покупки ММ (для реализации скидок/бонусов на ММ);\n        - Изменение уровней юнитов;\n        - Мораторий на агрессивные миссии флотов;\n    Хайспот \"Головоломка\" (Puzzle) - сбор предмета из компонент, которые находятся в Экспедициях:\n         - Добавлен Activity на просмотр паззла - актуально, когда паззл уже собран, но на итог надо посмотреть\n    Хайспот \"Сбор ништяков\" (Gather) - появляющиеся на экране ништяки, на которые должны кликать игроки:\n        - Виды:\n            - Новый Год - поиск Ёлочки и её наряжание;\n            - День Рождения СН - поиск исходных кодов СН и компиляция движка;\n        - При генерации ништяка выбирается картинка из списка доступных и запоминается. Далее она остается персистентной между рефрешами страницы и записывается в лог сбора ништяков;\n        - Для ненайденных ништяков показывается плейсхолдер без названия и описания\n        - При наведении курсора ништяки подсвечиваются;\n        - Поддержка минимального количества ништяков - для того, что бы в конце ивента каждый ништяк не был высокоуровневым;\n        - Добавлены комбинации ништяков - составные ништяки, которые для своего появления требуют найти несколько других типов ништяков;\n            - Аттрибут P_REQUIRE для ограничения юнитов/составных юнитов\n            - Вывод требований на странице со списком ништяков - название, картинка, сколько нужно, сколько открыто\n            - Название и картинка требований неоткрытых юнитов не выводится\n        - Поддержка min_time и max_time для ништяков - минимальная/максимальная дата/время, когда ништяк может появлятся на экране;\n        - Теперь в min_time и max_time можно использовать шаблоны функции date() - осторожно при межгодовых акциях!\n        - Хайспот автоматически определяет наличие больших картинок - директива HIGHSPOT_GATHER_ONLY_ICONS неактуальна\n    Все стили вынесены в отдельный CSS-файл в модуле\n    Дополнительная строка в навбаре специально для кнопок ивентов;\n    Добавлено действие Christmas Tree Burn\n\n[#] interface_batch_operations 1a1 - Модуль \"Массовые операции\"\n    Добавляет новый пункт меню \"Массовые операции\"\n    Массовый развоз ресурсов/передислокация кораблей с одной планеты на несколько\n    Массовый своз ресурсов/передислокация кораблей с одной планеты на одну\n    Массовая постройка кораблей на нескольких планетах одновременно\n    Доступ к \"Массовым операциям\" покупается за ТМ:\n        - Базовая стоимость - 50.000 ТМ за 30 дней;\n        - Возможность покупки фишки на 7, 14, 60 и 90 дней (два последних - со скидкой);\n        - Возможность досрочного продления фишки;\n    Полная локализация модуля\n    Транзакции теперь делаются per-planet, а не глобальные по всем планетам. Это должно существенно улучшить отзывчивость для остальных игроков\n\n[#] info_best_battles 1d2 - Модуль \"Лучшие бои\"\n    Добавляет в меню новый пункт \"Лучшие бои\", который открывает соответствующую страницу\n    Для каждого боя выводится:\n        - Порядковый номер в таблице;\n        - Дата и время боя;\n        - Общее количество обломков в пересчете на металл;\n        - Ссылка на просмотр соответствующего боевого отчёта;\n    На странице выводится 50 лучших боёв\n    Бои сравниваются по общему количеству обломков в пересчете на металл\n    В таблицу попадают только бои, которые произошли не ранее 2-х суток от текущей даты, т.е.:\n        - 2015-11-28 в 00:11:30 будут доступны бои, произошедшие до 2015-11-26 00:00:00 (не включая полуночь);\n        - Бои, происшедшие 2015-11-26 появятся в списке лучших боёв 2015-11-29 ровно в 00:00:00 (если, конечно, образовалось больше обломков, чем у худшего из лучших боёв);\n        - Это сделано специально, что бы дать всем заинтересованным сторонам собрать лом с орбиты;\n    Лучшие бои не удаляются из базы данных во время процедуры технического обслуживания\n    Добавлено уведомление о сроке появления боя в списке для тех, кому лень пролистать до конца страницы\n    Добавлена английская локализация\n\n\n[!] Флоты/Подбор флота\n    Переверстана страница подбора кораблей во флот:\n        - Уменьшена ширина списка кораблей для удобства мобильных пользователей;\n        - Миниатюра корабля:\n            - Вместо названия корабля и скорости полёта поставлена миниатюра корабля;\n            - Клик или тап на миниатюре переадресует на страницу о подробной информации корабля;\n            - Миниатюра корабля приведена к общему стандарту - название корабля в верхней строке и количество кораблей на орбите в нижней строке (вместо отдельной колонки);\n            - Так же на миниатюре корабля выводится: скорость полёта, расход топлива и ёмкость трюмов;\n            - Если корабль не может покинуть орбиту планеты/луны (Солнечный Спутнки, ТОП итд), то вместо характеристик выводится надпись \"Спутник\";\n        - В верхней части списка кораблей продублированы все кнопки. Теперь не обязательно листать до низа страницы, что бы быстро поднять все корабли или свезти ресурсы;\n        - Настройки сортировки перенесены в самое начало страницы - по аналогии с другими страницами;\n    Теперь при отсутствии свободных слотов для нового флота:\n        - Скрываются кнопки массового выбора кораблей, перехода на следующую страницу и своза ресурсов (поскольку слотов под своз всё равно нету);\n        - Скрывается мультиэлемент выбора кораблей;\n        - Миниатюры кораблей выводятся в большем размере - что бы легче читались характеристики.\n    Названия характерстик на миниатюрах кораблей заменены иконками\n    Название корабля теперь выделяется голубым цветом\n    Добавлены настройки (пункт меню \"Настройки\", вкладка \"Интерфейс\", подвкладка \"Флоты\"), на которых можно:\n        - Включить \"старый режим\" - без картинок и с выводом количества кораблей в отдельном столбце;\n        - Отключить показ каждой характеристики корабля отдельно: т.е. скорости, ёмкости трюмов, потреблении;\n    Выделены цветом названия кораблей (желтый) и количество на орбите (зеленый\n[!] Админка/Настройки\n    Полностью переверстана таблица настроек сервера - со вкладками и на div-ах!\n[!] Дизайн\n    Общий responsive бэкграуд Блица для всех скинов\n    Responsive бэкграунд при входе в игру в обоих режимах (СН/Блиц)\n    Опять сделан прозрачным основной фон навбара\n    Скин supernova-ivash: добавлены стили для TD/TH\n    Реформат:\n        - Страница партнерской программы;\n        - Страница управления Альянсом;\n[!] Навбар\n    Ресурсный навбар:\n        - Теперь ресурсный навбар может быть сделан вертикальным\n        - \"Настройки\", раздел \"Интерфейс\", вкладка \"Панель навигации\", опция \"Вертикальная панель ресурсов\"\n        - При этом ресурсный навбар \"прижимается\" сбоку от основного навбара - полезно игрокам с широкими мониторами\n    Добавлены поясняющие надписи к кнопкам\n    Немного переделан навбар - больше флексбоксов богу флексбоксов!\n    Поддержка добавочных кнопок вверху навбара\n\n\n[+] Заметки/Закладки\n    Изменения на основной странице Закладок:\n        - Заголовок и текст закладки выделяется цветом важности. Отдельный словесный маркер важности убран за ненадобностью;\n        - Название объекта в космосе, на который указывает закладка, отображается сокращённо - (П) для Планеты, (Л) для Луны и (О) для Поля обломков\n        - Статус \"прилепленной\" закладки показывается иконкой, а не надписью \"Прилеплена\";\n        - Дата в списке закладок перенесена в заголовок;\n        - Увеличена максимальная ширина таблицы с закладками - для обладателей широкоформатных мониторов;\n        - На маленьких экранах если заголовок не влазит по ширине - он будет разнесен на нужное количество строк;\n        - Диапазоны выбора какие закладки удалять в верхней и нижней части синхронизированы. Т.е. выбор диапазона в верхнем элементе приводит к изменению диапазона в нижнем элементе - и наоборот\n    Редактирование/создание закладки:\n        - При выборе важности закладки меняется цвет заголовка, текста и самого выбранного элемента;\n        - Если в закладке пустой текст и заголовок, но указана хотя бы одна координата (галактика, система или планета) - закладка будет сохранена;\n    Исправлена ошибка, делающая удаление Закладок невозможным при выборе некоторых диапазонов\n\n[-] Апгрейд\n    Апгрейд с очень старый версий двиэка (СН версии 36 и ниже) больше не поддерживается\n\n[~] Друзья\n    Теперь можно отправлять пустой запрос на дружбу (хотя кому и зачем это может понадобиться - непонятно)\n[~] МПР\n    Теперь можно атаковать ракетами свои же планеты. Таким образом можно избавится от излишних ракет или перехватчиков в шахтах, а так же уничтожать свои защитные сооружения\n[~] Флоты\n    Время возвращения флота из САБа теперь равно времени полёта на САБ, а не чистому времени полёта флота\n\n[@] Код\n    Папка с классами перемещена в корень движка - автолоадер изменен соответствующим образом\n    Переименованы файлы с классами для полной поддержки PSR-4 автолоадера\n    Изолирован код, обращающийся к таблице `fleets`\n    Теперь модули могут добавлять свои CSS-файлы и конструкции в заголовок\n    Унифицирован вызов MVC-хуков. Теперь их потенциально можно роутить в базовом варианте\n    Вьюшки с IN_ADMIN в модулях теперь корректно выдают страницу с обрамлением админки\n[@] Код/JS\n    Обновлена библиотеека Ion Sound до версии 3.0.7\n    В основном коде заменены deprecated функции jQuery:\n        - bind(), live() и delegate() на on();\n        - unbind() - на off();\n        - В этот раз - честно-честно!\n    В класс Math добавлены функции-аналоги PHP-шных intval() и floatval() и функции округлений\n        - Эти функции всегда возвращают только числовые значения, а NaN преобразуют в 0\n        - В некоторых местах parseFloat() и parseInt() заменены на новые функции;\n    sn_format_number():\n        - Функция оптимизирована и разбита на две части\n        - Первая - считает вид числа;\n        - Вторая - выдаёт соответствующий cssClass для расцветки;\n        - При сохранении обратной совместимости (deprecated по факту) теперь можно менять сразу класс элемента, без вставки <span /> в DOMик;\n        - Добавлена соотвествующая функция-враппер elementPrettyNumber().\n    Постройка:\n        - Все значения от PHP теперь пропускаются через новые функции;\n        - Оптимизирована работа разных кусков JS и улучшен код;\n        - Убрано обращение к document;\n    ...а так же всякие мелкие оптимизации.\n\n\n\nProject \"SuperNova.WS\" Release 40 \"RD auth planet universe que sn_timer\"\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n[!] Не прошло и года, как я представляю вам новый релиз СуперНовы\n    В самом деле - не прошло и года. Чуть более 7 месяцев со времени 39-го релиза\n    Как и прошлый релиз, этот делается для фиксации \"статуса-кво\" - как реперная точка в разработке и для того, что бы системные администраторы серверов на этом движке могли бы использовать новейшие разработки в области СН-строения\n    И, кстати, игра претерпела небольшой \"SEO-ребрендинг\" - анализ запросов с Гугля и Яндекса показал, что игру в основном находят как \"СуперНову\", а не как \"СверхНовую\". Поэтому было решено официально сменить русскоязычное название движка на \"Проект 'СуперНова'\". Вива, Гугль!\n[!] Редизайн игры\n    Не секрет, что я уделяю больше времени содержательной части игры, а не её внешнему виду. В конце-концов - я всё-таки программист, а не дизайнер\n    Однако неуклонный рост количества пользователей мобильных устройств в игре и одновременно - увеличение доли широкоформатных мониторов у игроков буквально вынудили уделить время и дизайну\n    В настоящий момент один и тот же темплейт (расположение элементов игры на странице) используется как для игроков с мобильных устройств, так и для пользоваетелей ПК\n    К сожалению, создание отдельного темплейта исключительно для малоформатных устройств требует огромное количество времени, а так же сильно затрудняет дальнейшую разработку из-за необходимости поддержки и синхронизации изменений в двух темплейтах\n    Поэтому было принято решение дорабатывать текущий темплейт, добавляя в него различные фишки для поддержки как малых, так и больших разрешений - не забывая при этом про среднеформатные мониторы. В число таких фишек входят:\n        - Полная переработка CSS-стилей;\n        - \"Отзывчивый дизайн\" (Responsive Design, далее - RD);\n        - Встроенная система масштабирования интерфейса;\n    Подробнее об этих и других изменениях во внешнем виде игры можно прочесть ниже\n    С учётом сложности поставленной задачи (без существенных изменений адаптировать существующую вёрстку ко всей гамме разрешений мониторов с учетом особенностей зоопарка компьютерных и мобильных браузеров) результат получился очень хорошим\n    Так же была проделана определенная работа по улучшению внешнего вида интерфейса:\n        - Совершенно новые виды для некоторых страниц - например, \"Обзор Вселенной\" и \"Технологии\" - список этим не ограничивается;\n        - Полностью переверстаны с нуля некоторые другие страницы. При небольших изменениях во внешнем виде заметно улучшилось юзабилити. К таким страницам относятся, например, \"Новости\";\n        - Полностью переписана \"Очередь построек\";\n        - Добавление элементов декора в интерфейс - рамки, тени под кнопками, эффекты итд итп. Значительную часть декора можно отключить в настройках интерфейса;\n        - Унификация раскраски элементов;\n        - ...и многое, многое другое!\n[!] Изменения в игровой механике\n    Хотя акцент в релизе сделан на внешний вид, некоторые игровые механики так же были переделаны. Порой - переписаны полностью\n    К последним относится полностью новая система генерации планет - см.ниже подробное описание изменений\n    Так же изменения притерпели и некоторые другие механики. Опять же - прочитать об этом можно ниже\n[!] Изменения в движке\n    Последнее по списку, но не по значимости. \"Внутренности\" движка так же были серьёзно доработаны - как PHP-часть на стороне стороне, так и JS-часть на стороне клиента. Можно отметить следующие изменения:\n        - Полностью переделана система авторизации (два раза за отчётный период);\n        - Полностью переписана работа JS-таймеров;\n        - Существенно переделана работа с БД - добавлена потенциальная возможность подключения к отличным от mysql БД;\n        - ...и многое, многое другое!\n    Краткий список изменений см. ниже\n\n[#] info_best_battles 1b0\n    (!) Модуль \"Лучшие бои\"\n        Добавляет в меню новый пункт \"Лучшие бои\", который открывает соответствующую страницу\n        Для каждого боя выводится:\n            - Порядковый номер в таблице;\n            - Дата и время боя;\n            - Общее количество обломков в пересчете на металл;\n            - Ссылка на просмотр соответствующего боевого отчёта;\n        На странице выводится 50 лучших боёв\n        Бои сравниваются по общему количеству обломков в пересчете на металл\n        В таблицу попадают только бои, которые произошли не ранее 2-х суток от текущей даты, т.е.:\n            - 2015-11-28 в 00:11:30 будут доступны бои, произошедшие до 2015-11-26 00:00:00 (не включая полуночь);\n            - Бои, происшедшие 2015-11-26 появятся в списке лучших боёв 2015-11-29 ровно в 00:00:00 (если, конечно, образовалось больше обломков, чем у худшего из лучших боёв);\n            - Это сделано специально, что бы дать всем заинтересованным сторонам собрать лом с орбиты;\n        Лучшие бои не удаляются из базы данных во время процедуры технического обслуживания\n\n[#] skins/immi\n    [!] В игру добавлен скин immi\n\n[#] menu_customize 0d5\n    (!) Настройки/Меню\n        Все настройки меню вынесены в отдельный таб \"Настройка меню\"\n        Настройки элементов меню сгруппированы по категориям\n    (+) Поддержка редизайна меню\n        Опция \"Показать пункты меню в виде кнопок\" переименована в \"БОЛЬШИЕ КНОПКИ В МЕНЮ\"\n        Добавлена опция \"Белые надписи на кнопка меню (отключить цвета)\"\n    (%) Пустые пункты меню\n        Теперь пустые пункты меню не показываются в меню при включенном модуле настройки меню\n    (%) Кнопка Скрыть/Показать меню\n        Исправлены проблемы с позиционированием кнопки\n\n[#] player_premium 3c3\n    (+) Ивенты\n        Поддержка бонусных уровней Премиума по ивентам\n\n[!] Миссии/Колонизация\n    !!!ВНИМАНИЕ!!! Полностью переписана с нуля генерация планет! Теперь она очень сильно отличается от оГеймовской!\n    Размер планеты:\n        - Теоретические размеры планет составляют: минимальный - 30 секторов, максимальный - 330 (бывают только при наличии в системе планет-странников);\n        - На стандартных слотах (с 1-го по 15-й) кардинально увеличен средний размер планеты - как за счёт пересчёта минимального и максимального количества секторов, так и за счёт исправления архитектурной ошибки xnova;\n        - Диаметр планеты теперь вычисляется по более вразумительной формуле;\n    Типы ядер:\n        - Полностью переработаны типы ядер\n        - Пересчитана добыча всех типов ядер с учётом экономической эффективности и собранной статистики по типам планет у игроков (Нерф? Кто сказал - \"НЕРФ\"?!. Наоборот!)\n        - Переработаны и переименованы старые типы ядер:\n            - Во всех старых типах ядер увеличена добыча;\n            - \"Лёд\" переименован в \"Метановый лёд\" и улучшена добыча;\n            - \"Камень\", \"Силикат\" и \"Руда\" - улучшена добыча;\n            - \"Металл\" переименован в \"Оливин\". Его добыча находится на уровне старого типа \"Тяжелый металл\";\n            - Ядра типа \"Тяжелый металл\" мигрированы в тип \"Оливин\" (с улучшением добычи);\n        Добавлены 4 новых типа ядра:\n            - \"Водяной лёд\" - приблизительный аналог \"Камня\" или \"Руды\" для водорода;\n            - Три новых типа ядра - \"Водородный лёд\", \"Кристалл\" и \"Металл\" (новая версия) - с охренительной добычей соответствующих типов ресурсов;\n        Десять типов ядер четырех классов:\n            - \"Базовый\" класс - тип ядра \"Стандарт\";\n            - \"Продвинутый\" - \"Водяной лёд\", \"Камень\", \"Руда\";\n            - \"Редкий\" - \"Метановый лёд\", \"Силикат\", \"Оливин\";\n            - \"Раритетный\" - \"Водородный лёд\", \"Кристалл\", \"Металл\";\n        Смена типа ядра:\n            - Стоимость смены типа ядра теперь не зависит от текущего - можно недорого поэкспериментировать как минимум с продвинутым классом ядер, а то и с редкими;\n            - Стоимость смены типа ядра теперь зависит от типа планеты;\n            - В среднем - смена ядра планеты стала ГОРАЗДО выгоднее (в пересчете затраченной ТМ на полученный прирост производительности);\n        ПРИМЕРНО половина планет во Вселенной имеет улучшенный тип ядра (или водяной лёд, или камень, или руда), еще треть имеют тип ядра \"Стандарт\". Остаток приходится на долю остальных типов ядер\n    Температура:\n        - Изменены диапазоны, в пределах которых меняется максимальная температура планеты;\n        - Разница между максимальной и минимальной температурами теперь не фиксирована (40 градусов в xnova), а определяется случайным образом в определенном диапазоне;\n        - Диапазон разницы температур зависит от места планеты в системе;\n        - !!!ВНИМАНИЕ!!! У всех старых планет (колонизированных до установки патча) минимальная температура установлена равной максимальной температуре для того, что бы изменения патча не повлияли на добычу;\n    Добавлена поддержка \"планет-странников\":\n        - Планета-странник - ранее блуждавшая по галактике экзопланета, захваченная тяготением звезды;\n        - Планеты-странники находятся за 15-м слотом в системе (если в настройках игры включен размер системы более 15 планет);\n        - Хотя планета-странник может быть достаточно тёплой, ожидать околозвёздных температур в сотни градусов Цельсия от неё не стоит. А вот получить промороженную насквозь планету - вполне реально;\n        - Планеты-странники имеют огромный разброс по всем параметрам - соответственно и типов таких планет несчётнок множество;\n        - Получить граничные значения размера планеты можно только на планете-страннике;\n        - По умолчанию размер системы увеличен до 16 планет (ПРОВЕРЬТЕ ВАШИ ЗАМЕТКИ/ЗАКЛАДКИ НА ЭКСПЕДИЦИЮ!);\n    Переработано распределение планет по системе:\n        - Естественно, средняя температура планеты падает на расстоянии от звезды (т.е. по мере увеличения номера слота);\n        - Теперь бесполезно искать планеты с ледяным ядром возле звезды или планеты с металлическим ядром на внешних границах системы;\n        - Кристаллические ядра встречаются практически по всей системе;\n        - Крупные планеты группируются в середине системы. Соответственно - на внешней и внутренней границах системы планеты более мелкие;\n        - Как и с размером планет - температурные диапазоны уменьшаются от центра системы к её границам;\n    Цветовое кодирование типов ядер - чем \"тревожнее\" цвет, тем реже этот тип ядра можно найти при колонизации:\n        - Белый - стандартное ядро\n        - Желтый - ядро \"Продвинутого\" класса\n        - Оранжевый - \"Редкий\" класс\n        - Красный - самый редкий, \"Раритетный\" класс;\n    Колонизация:\n        - Тип ядра, находимого в колонизации, ограничивается уровнем Астрокартографии:\n            - От 0 до 5 - могут быть найдены только планеты с типами ядер \"Стандарт\", \"Водяной лёд\", \"Камень\", \"Руда\"\n            - От 6 до 10 - так же могут быть найдены планеты с типами ядер \"Метановый лёд\", \"Силикат\", \"Оливин\n            - 11 и больше - так же могут быть найдены планеты с типами ядер \"Водородный лёд\", \"Кристалл\", \"Металл\"\n        - Это сделано для облегчения игры для новичков и уравнивания стартовых условий\n        - Например, если первая же найденная колония имеет тип ядра \"Водородный лёд\" - новый игрок получает сильное пенальти на развитие планеты (планеты \"Раритетного\" класса очень сложно застраивать сам-один и для нормальной застройки требуется транспорт других типов ресурсов с остальных планет)\n\n[!] Тёмная Материя\n    Теперь не нужно вручную конвертировать ММ в ТМ - конвертация осуществляется автоматически при нехватке ТМ. Это упростит обращение с ММ для новых игроков\n    Переработан интерфейс покупки Метаматерии с учётом добавления в игру автоконвертации\n    Убран ненужный блок с курсами и скидками: цена теперь всегда выводится в выбранной валюте, а скидки за оптовые покупки прописаны прямо на кнопках пакетов\n\n[!] Навигационная панель (Навбар)\n    Добавлена кнопка \"Планета\":\n        - Клик на кнопке откроет страницу \"Планета\" для текущей планеты;\n        - В правом нижнем углу кнопки показывается количество колоний у игрока и максимально возможное количество колоний;\n        - Если на планете есть очередь зданий она выводится на кнопке планеты аналогично Исследованиям и Ангару\n    Добавлена кнопка \"Верфь\":\n        - Клик на кнопке откроет страницу \"Верфь\";\n        - При наличии юнитов в очереди Верфи на кнопке будет отображено:\n            - Количество оставшихся в очереди слотов\n            - Общее время до окончания постройки всех юнитов в очереди\n            - Название юнита в текущем слоте очереди (т.е. корабль, который сейчас строится)\n            - Количество оставшихся юнитов в текущем слоте\n            - Время, оставшееся до окончания постройки текущего юнита в текущем слоте\n    Отключение кнопок:\n        - Добавлена возможность отключения следующих кнопок:\n            - \"Исследования\"\n            - \"Планета\"\n            - \"Верфь\"\n            - \"Флоты в полёте\"\n            - \"Экспедиции\"\n            - \"Квесты\"\n            - \"Метаматерия\" (при наличии ММ её количество показывается так же на кнопке \"Тёмная Материя\")\n        - Отключение кнопок происходит в пункте меню \"Настройки\", вкладка \"Интерфейс\", раздел \"Навигационная панель\"\n    Кнопка \"Сообщения\":\n        - Счетчики сообщений теперь располагаются не в линию, а по углам иконки сообщений:\n            - Левый верхний - личные сообщения;\n            - Правый верхний - сообщения от Альянса;\n            - Левый нижний - сообщения от Администрации;\n            - Правый нижний - общее количество сообщений.\n        - Таким образом на мобильных устройствах будет гораздо проще выбрать нужный тип сообщений\n        - Если в почтовом ящике есть сообщения - общее количество соощений в навбаре мягко мерцает\n    По умолчанию кнопка \"Исследования\" теперь имеет стандартную ширину. Вернуть старый вид можно включив в \"Настойках\" опцию \"Широкая кнопка исследований (старый вид)\"\n    Имена планет в дропдауне переключения приведены к стандартному виду [координаты] (тип) Имя\n    \"Локальное время\" переименовано во \"Время у игрока\", \"Серверное время\" - во \"Время на сервере\"\n    Добавлены прогресс-бары к плашке исследований\n    Убрана горизонтальная зеленая линия над общим количеством ТМ в случае, если на аккаунте нет ММ и/или ММ не подключена в игре\n    Навбар переверстан под flex-box - теперь корректно переносятся кнопки на маленьких экранах\n    Все инлайн-стили вынесены в _template.css\n[!] Меню\n    Редизайн пунктов меню:\n        - Теперь в стандартном режиме пункты меню выглядят, как кнопки. Однако при этом являются ссылками - т.е. позволяют открывать страницы в новом окне\n        - Теперь при наведении курсора на пункт меню он подсвечивается\n    Настройка меню:\n        - Опции меню вынесены в отдельный таб \"Настройка меню\"\n        - Настройка \"Показывать пункты меню в виде кнопок\" заменён на \"БОЛЬШИЕ КНОПКИ В МЕНЮ\" и теперь просто увеличивает размер пунктов меню - удобно для мобильных устройств\n        - Новая опция в \"Настройкх\": \"Белые надписи на пунктах меню (отключить раскраску меню)\" убирает раскраску пунктов меню, оставляя выделенными только категории\n        - Новая опция в \"Настройкх\": \"Низкие пункты меню\", которая возвращает меню старый вид меню\n    Немного переупорядочены пункты меню так, что бы уменьшить количество категорий и не было неактивных заголовков в категориях\n    Поддержка иконок меню, расположенных в любом месте движка\n    Ускорена работа меню в приложениях\n[!] Новости\n    Редизайн блока новостей\n    Если новость содержит больше 1 параграфа (строки, разделенные двумя CRLF подряд), то остаток новости скрывается под кнопкой \"Показать текст новости\"\n    Кнопка закрытия списка новостей теперь машстабируется вместе с интерфейсом\n    Опросы:\n        - Добавлена индикация общего количества проголосовавших\n        - Результаты опроса:\n            - Бар, показывающий процент проголосовавших, выводится под самим ответом\n            - К каждому варианту ответа добавилась индикация процента проголосовавших\n    Кнопки действия над новостью (для Администрации) перенесены из отдельных колонок под саму новость\n\n[!] Страница \"Планета\"\n    Для каждой очереди добавлена дата и время её завершения\n    Добавлено подтверждение на покупку сектора\n    Добавлены прогресс-бары к общей информации об очередях\n    Добавлены координаты и название планеты в виде заголовка страницы\n    Убрано уведомление \"У вас Х новых сообщений\"\n    Часть функционала \"Управления планетой\" вынесена на основной экран \"Планеты\":\n      - Переименование\n      - Смена типа ядра\n    Ссылка \"Управление\" переделана в кнопку\n    В расчёте миниатюр планет и иконок на них транзакции теперь делаются per-planet, а не глобально по всем планетам. Это должно существенно улучшить отзывчивость для остальных игроков\n[!] Страница \"Обзор Вселенной\"\n    Общие изменения:\n        - Изменилась цветовая кодировка обломков: добавились новые градации и изменились границы переключения цветов на более интуитивные:\n            - Без фона/рамочки - менее 1.000 единиц обломков;\n            - Зеленый фон/рамочка - не менее 1.000 и не более 1.000.000 единиц;\n            - Желтый - не менее 1.000.000 и не более 1.000.000.000 единиц;\n            - Оранжевый - не менее 1.000.000.000 и не более 1.000.000.000.000 единиц;\n            - Красный - не менее 1.000.000.000.000 единиц;\n        - Добавлена кнопка ракетной атаки в планетарные попапы\n        - При выборе \"Ракетной атаки\" страница скроллируется на форму запуска ракет. Ей добавлена толстая красная рамка, что бы визуально выделить среди остальных элментов;\n        - В \"Настройках\" на вкладке \"Интерфейс\" в разделе \"Вселенная\" добавлена возможность отключить кнопку \"Послать колонизатор для основания колонии на позиции номер Х\"\n        - После анализа востребованности фишек, убраны быстрые действия \"Просмотреть место игрока в статитстике\" и \"Добавить игрока в друзья\". Действия по-прежнему доступны в попапе игрока, однако быстрые кнопки использовались настолько мало, что было принято решение убрать эти опции;\n        - При активации AJAX-действия (отправка ракеты, отправка шпионов итд) в центре экрана пишется результат совершения действия\n    Новый вид страницы:\n        - Полный редизайн страницы\n        - Изменений настолько много, что нет смысла их тут описывать все - проще посмотреть самому\n        - Те, кому не нравится новый вид - могут включить старый вид в \"Настройках\", вкладка \"Интерфейс\", галочка 'Использовать старый вид \"Обзора Вселенной\"'\n        - Выделение поля обломков цветной рамкой в зависимости от количества ресурсов\n    Изменения в старом виде Вселенной:\n        - Быстрые действия на планету/аккаунт сделаны кнопками для облегчения судьбы наших мобильных друзей;\n        - Переработаны попапы игрока и Альянса аналогично кнопкам миссий\n        - Значительно уменьшен размер генерируемой страницы Вселенной:\n            - С иконок ракетной атаки убран код onclick и перенесен в jQuery-обработчики\n            - Кнопки миссий в планетарном попапе переделаны на button_pseudo, a onclick-код вынесен в jQuery-обработчики\n[!] Очередь построек\n    Переделана Очередь построек\n    В очередь постройки добавились прогресс-бары:\n        - Прогресс-бары можно отключить - пункт меню \"Настройки\", вкладка \"Интерфейс\", опция \"Отключить прогресс-бары\"\n        - Прогресс-бар постройки текущего юнита в текущем слоте очереди:\n            - Цвет - зеленый\n            - Выводится под таймером обратного отсчёта постройки текущего юнита\n            - Учитывается ПОЛНОЕ время постройки юнита - т.е. при перезагрузке страницы время продолжит идти\n            - Показывает процент завершения постройки текущего юнита - т.е. увеличивается со временем\n            - Виден во всех очередях постройки на первом слоте в очереди и в навбаре в плашке верфи\n        - Прогресс-бар постройки всех юнитов в текущем слоте:\n            - Цвет - синий\n            - Выводится под количеством юнитов в текущем слоте (для зданий, очевидно, выводится всегда - здания строятся поштучно)\n            - Показывает оставшееся количество юнитов к постройке в текущем слоте - т.е. уменьшается со временем\n            - Виден во всех очередях постройки на всех слотах\n        - Общий прогресс-бар очереди:\n            - Цвет - болотный;\n            - Выводится под общим таймером обратного отсчёта всей очереди\n            - Учитывается ОСТАВШЕЕСЯ время всех слотов в очереди - т.е. при перезагрузке страницы отсчёт стартует с начала\n            - Показывает поставшееся время до завершения всей очереди - т.е. уменьшается со временем\n            - Виден на странице постройки и в навбаре в плашке верфи\n    Теперь время до постройки одного юнита в первом слоте пишется на самом юните\n    \"Общее время\" теперь индицируется сверху\n    Добавлены эффекты анимации к списку юнитов в очереди:\n        - При начале строительства нового юнита таймер на миниатюре мигает один раз;\n        - При окончании строительства всех юнитов в слоте - слот исчезает;\n        - Отключить анимацию эффектов в игре можно в пункте меню \"Настройки\", вкладка \"Интерфейс\", галочка \"Отключить эффекты анимации в игре\"\n    Список юнитов в очереди переверстан на дивах с использованием flex-box\n    Оптимизирована работа таймера очередей, так что новые прогресс-бары не должны сказаться на быстродействии\n    Теперь не рендерится очередь постройки, если контейнер для неё невидим - например, в навбаре\n    На общей панели постройки количество юнитов/уровень в текущем слоте отображается на новой строке\n    Оптимизирована выдача HTML-кода - меньше использование свойства style\n    Добавлена поддержка нового кода sn_timer\n[!] Страница построек/исследований\n    Автоконвертация ресурсов:\n        - Постройка зданий/исследование технологий:\n            - На странице постройки зданий и исследования технологий добавлена возможность постройки с автоматической конвертацией ресурсов (далее просто - \"автоконвертация\")\n            - Автоконвертация доступна если для постройки/исследовния не хватает какого-то конкретного типа ресурсов, однако есть излишек других ресурсов, которые можно сконвертировать на ЧР в недостающий ресурс\n            - Конвертируются только планетарные ресурсы - металл, кристалл и дейтерий. ТМ автоматически не конвертируется\n            - Сразу после конвертации недостающих ресурсов постройка/исследование ставится в очередь\n            - Для предотвращения случайных срабатываний при нажатии на кнопку автоконвертации выскакивает дополнительное окно с подтверждением\n            - Автоконвертация ресурсов - платная. Стоимость операции составляет утроенную цену одной конвертации на ЧР или 3.000 ТМ, если ручной обмен на ЧР бесплатен\n        - Верфь/Оборона\n            - На странице постройки кораблей и обороны добавлена возможность постройки с автоконвертацией\n            - В строке максимального количества через слэш показывается максимально доступное количество юнитов при автоконвертации\n            - Для включения режима автоконвертации необходимо отметить галочку \"Автоконвертация\". При этом все элементы страницы работают так же, как и раньше, однако максимальное количество юнитов к постройке становится равно максимально доступному количеству юнитов с учётом автоконвертации\n            - Автоконвертация щадяща: даже если при включенной галочке автоконвертации поставить на постройку не больше максимального количества юнитов, доступных без автоконвертации - ТМ снята не будет и конвертация ресурсов производится не будет\n            - Автоконвертация переводит ресурсы исходя из заказанного количества юнитов - даже если в очередь может стать меньше. Это сделано специально для того, что бы можно было однократно воспользоваться автоконвертацией, а затем просто пополнять очередь новыми юнитами - с уже сконвертированных в правильном отношении ресурсов\n        - Отключить кнопку/чекбокс автоконвертации можно в \"Настройках\", вкладка \"Интерфейс\", чекбокс \"Скрыть кнопку автоконвертации\"\n    Сортировка списка юнитов:\n        - На страницы постройки (исследования, здания, верфь, оборона) добавлена возможность сортировки\n        - Для каждой из вышеперечисленных страниц настройки сортировки сохраняются отдельно\n        - Добавлена поддержка стандартной сортировки в обратном порядке\n    Количество превьюшек теперь динамически меняется в зависимости от разрешения экрана и настроек масштабирования\n    На больших экранах (там, где вмещается не менее 6 превьюшек в ряд) таблица с дополнительной информацией располагается справа от описания юнитов\n    На больших экранах блоки покупки/уничтожения теперь выстраиваются в один ряд с информацией о стоимости постройки\n    В очереди ссылки \"Очистить очередь\", ссылка Артефакта (\"Наностроитель\"/\"Эвристический чип\") и \"Отменить последнее\" сделаны кнопками. Это выделит данные элементы, а так же уменьшит количество ложных нажатий\n    Добавлено диалоговое окно с подтверждением при полной очистке очереди\n    Добавлено диалоговое окно с подтверждением при использовании Артефакта\n    При постройке зданий/исследовании технологий теперь показывается уровень, который будет строится/исследоваться\n    При постройке зданий/исследовании технологий кнопка постройки/исследования теперь отключается, если операция невозможна\n    Постройка зданий:\n        - Добавлено подтверждение на покупку сектора\n        - Теперь если при постройке зданий нет свободных секторов - превьюшки затеняются и выводится соответствующее сообщение\n    Добавлено разрежение ячеек в подтаблицы цены и допинформации о юните\n    Добавлено выделение четных рядов другим фоном в подтаблице допинформации\n[!] Страница \"Флот на орбите\"\n    На шаге \"Выбрать корабли\" при отсутствии кораблей на планете на экране подбора флота доступна кнопка \"Свезти ресурсы\"\n    На шаге \"Выбор точки назначения\" при выборе планеты/заметки/боевого союза точка назначения флота меняется соответственно\n    Шаг \"Выбор задания\":\n        - Выбор миссий на странице выбора миссий сделан иконками-пиктограммами. Это улучшит юзабилити для игроков на мобильных устройствах\n        - Для миссии \"Колонизация\" введено цветовое кодирование количества колоний:\n            - Красный - количество колоний больше максимально возможного количества (например, вследствие исчерпания срока Премиум-аккаунта или окончания специальных акций)\n            - Оранжевый - количество колоний равно максимальному количеству. Нельзя колонизировать ни одной новой планеты;\n            - Желтый - количество колоний на 1 меньше максимального количества. Текущая миссия заблокирует возможность дальнейшего расширения;\n            - Зеленый - количество колоний на 2 и более единиц меньше максимального количества. Можно спокойно запускать текущую миссию.\n        - Корявый указатель направления миссии \"=>\" заменен на красивую стрелочку с подписью типа миссии\n    Теперь при отправке флота на шагах \"Выбор точки назначения\", \"Выбор задания\" и \"Флот отправлен\" выводятся:\n      - Состав флота в виде картинок с названиями и количеством;\n      - Точка отправления флота;\n      - (если доступно) Точка назначения флота;\n      - (если доступно) Время и срок прибытия в точку отправления и назначения.\n    САБ:\n        - При создании/присоединении к САБу теперь видна дополнительная информация о флоте, к которому присоединяется САБ: состав флота, откуда и куда направляется флот, а так же дата и оставшееся время до прибытия/возвращения\n        - Теперь при нажатии кнопки \"Боевой союз\" автоматически создается САБ и в него добавляется текущий игрок. При этом ему не отсылается лишнее (в данном случае) сообщение\n        - Название САБА теперь имеет вид \"САБ <ID>\", где <ID> - назначаемый игрой идентификатор\n[!] Страница \"Технологии\"\n    Редизайн страницы:\n        - Вкладки на каждый вид технологий;\n        - Блочная, а не табличная вёрстка;\n        - Гибкая вёрстка в зависимости от размера экрана;\n        - Добавлены картинки юнитов;\n        - Списки \"Требуется\" и \"Предоставляет\" теперь являются ссылками - можно сразу перейти на просмотр описания требуемых/предоставляемых технологий;\n    Новый дизайн страницы должен подойти любому игроку - начиная от мобильных пользователей и заканчивая владельцами широкоформатных экранов: он динамичен, масштабируем, информация организована удобнее и сама страница занимает меньше экранного пространства\n    Табличный дизайн можно включить в \"Настройках\" вкладка \"Интерфейс\" опция \"Страница Технологий в виде таблицы (старый вид)\"\n[!] Страница \"Чёрный рынок\"\n    Улучшен интерфейс заглавной страницы\n    Обмен ресурсов:\n        - Переверстана страница Обмена Ресурсов. Теперь ею должно быть удобнее пользоваться мобильным пользователям, а так же меньше вероятность совершить неправильную операцию\n        - Добавлено подтверждение при обмене ТМ на ресурсы\n[!] Страница \"Настройки\"\n    Переписана с нуля работа с настройками игрока\n    Большая часть полей с настройками вынесены в отдельную таблицу player_options - уменьшен размер записи в таблице `users`\n    Настройки теперь кэшируются write-through - уменьшено количество обращений к БД\n    Закрыта возможность сменить имя игрока с использованием зарещенных символов\n    Настройки, которые имеют только один чекбокс приведены к общему виду: сначала идёт чекбокс, затем - описание. Это улучшает читаемость и удобство таблицы, а в некоторых случаях - уменьшает вертикальный размер страницы \"Настроек\"\n    Вкладка \"Профиль\":\n        - Отделено имя аккаунта от игрового имени\n        - Добавлены кнопки \"Показать пароль\" ко всем парольным полям\n    На вкладке \"Интерфейс\" добавлена опция \"Отключить рамки у таблиц\", которая убирает рамки-изображения у элементов дизайна\n    Вкладка \"Интерфейс\" теперь содержит подвкладки \"Общие\", \"Настройки меню\" (бывашя вкладка), \"Панель навигации\" и \"Вселенная\"\n\n[!] Responsive Design (RD)\n    RD - это не какая-то конкретная фишка, а целый комплекс подходов направленных на поддержку различных разрешений и ориентаций экранов, разных настроек масштабирования в браузере и СН, а так же автомасштабирования страниц в некоторых мобильных браузерах на некоторых платформах:\n        - Использование относительных единиц размера при вёрстке;\n        - Использование специальных приёмов при вёрстке;\n        - Изменение размеров и/или положения элементов страницы в зависимости от настроек экрана (использование @media запросов в CSS);\n        - ...и многое, многое другое.\n    Поэтому конкретные меры, предпринятые для поддержки подходов RD не собраны в одном месте, а отнесены к соответствующим описаниям изменений\n    Все новые страницы уже разрабатываются с учётом критериев RD. Здесь приведу только старые страницы и элементы, переделанных под RD:\n        - Страница \"Настройки\";\n        - Блок \"Подсказка\";\n        - Страница \"Обзор Вселенной\" (старая версия);\n        - Страница строительства переделана для поддержки масштабирования\n        - Чат\n        - Список планет\n[!] Дизайн\n    Внешний вид:\n        - Добавлены элементы декора - рамки\n        - Унифицирован внешний вид и раскраска сходных по функционалу элементов страницы\n        - Добавлено масштабирование фона в скины, где их не было и улучшено там, где были\n        - Отмеченные чекбоксы теперь подсвечиваются зеленым - в старой Опере и браузерах на основе WebKit\n    Вёрстка:\n        Переход на вёрстку с использованием относительных единиц размера (em, %) вместо фиксированных (px);\n        Проделана огромная работа по оптимизации и унификации CSS-стилей:\n            - Основная инофрмация о вёрстке теперь располагается в файле '_template.css' темплейта, а CSS-файлы скинов содержат в основном только раскраску элементов и настройки декора (рамки)\n            - Вынесено множество стилей из инлайна (описание вида элемента в HTML через аттрибут style) в CSS-файлы\n    Всё это, а так же конфигурабельность скинов через 'skin.ini' (см.), позволило веруть в данном релизе скин 'immi'\n    Изменены директивы viewport для лучшей поддержки мобильных устройств\n    Добавлен хак для отключения хромо-андроидовского FontBusting\n    Пережаты все картинки с сохранением качества изображения. В среднем размер картинок уменьшился примерно на 20%\n    Удалена почти все картинки-дубликаты - как просто дублирующиеся, так и появившиеся в результате добавления класса skin (см. тэги \"Код\" в changelog_dev.txt)\n    Скин EpicBlue:\n        - Добавлено изображение для ресурса \"Метаматерия\". Соответственно - оно появилось и во всех остальных скинах;\n        - Переделаны картинки ВебМани на странице платежей при выборе метода платежа\n        - Картинки юнитов:\n            - Заменены изображения Терраформера и Большого Планетарного Шита\n            - Изображения для Нанолаборатории, МИС, Астрокартографии, Гордыни, ТОПа, ПЗ, Малого Планетарного Щита, МПР и Перехватчика приведены к стилю остальных картинок\n            - За проделанную работу - особая благодарность игроку 4apaeff@Alpha\n    Скин supernova-ivash:\n        - Плашки в навбаре заменены на более подходящие по тону - спасибо Ivash@Alpha\n[!] Масштабирование интерфейса\n    В помощь Responsive Design в движок добавлена собственная система масштабирования интерфейса - кнопки в навбаре (панель вверху):\n        - Кнопка \"Шрифт -\" уменьшает размер шрифта;\n        - Кнопка \"Норма\" возвращает шрифт к размеру \"по умолчанию\" - высота 11 пикселей без учёта масштабирования браузера;\n        - Кнопка \"Шрифт +\" увеличивает размер шрифта;\n    Вместе с размерами шрифта так же масштабируются и другие элементы страницы: картинки, кнопки, чекбоксы, таблицы итд\n    При нажатии на кнопки размеры элементов меняются в реальном времени, давая возможность увидеть будущий вид страницы\n    Из-за неполной поддержки спецификаций CSS и DOM-модели в некоторых устаревших браузерах и/или на мобильных устройствах могут возникать глюки отображение. Обновление страницы решит эту проблему\n    Настройки масштабирования запоминаются в куках браузера и в настройках пользователя. При загрузке страницы они берутся сначала из куков и только если куки не установлены - из настроек. Таким образом можно иметь разные настройки масштабирования на разных устройствах\n\n[!] Админка\n    Обзор:\n        - Пункт меню \"Обзор\" в админке теперь использует тот же код, что и список пользователей - за исключением того, что \"Обзор\" показывает только игроков онлайн и показывает Альянсы\n        - Таким образом теперь в пункте меню \"Обзор\" можно использовать весь функционал страницы \"Список пользователей\": сортировку, просмотр информации о пользователе, имперсонейт итд\n        - Первыми в списке всегда выводятся игроки и только потом - Альянсы\n    Обслуживание:\n        - Добавлено удаление юнитов без планет\n        - Добавлено удаление пустых юнитов\n        - Добавлено удаление стандартных записей логов (обсчёт статистики, маинтенанс, апдейт) более чем недельной давности\n        - Добавлена чистка очередей на покинутых и удалённых планетах\n    Платежи:\n        - Добавлена ссылка на отправку письма игроку из списка платежа\n    Записи логов:\n        - Добавлена ссылка на удаление из логов записей об обновлении движка и статистики (неактуальную после успешно проведенных обновлений);\n        - Ссылка на чистку логов теперь так же удаляет записи об обслуживании БД;\n    В настройки сервера вынесены:\n        - Стартовый размер хранилищ на планете\n        - Стартовое количество ресурсов на планете\n    Добавлены разделители разрядов в сообщение о начислении игроку ТМ и ММ через админку\n\n[+] Страница \"Флоты в полёте\"\n    Добавлена возможность массового отзыва флотов\n[+] Список планет (Страницы \"Планета\" и \"Империя\")\n    Под полосу застройки и очередь на планете добавлена подложка с фоном\n    На каждой планете теперь даже при отсутствии очередей выводятся полупрозрачные иконки, при клике на которые можно сразу перейти к строительству зданий, кораблей и обороны\n    Иконка своза ресурсов на луну появляется только при выборе луны\n[+] BBCode\n    \"Макросы\":\n        - \"Макрос\" - специальная последовательность символов, которая разворачивается в контекстно-зависимые данные. См. ниже\n        - Макрос \"sn://\":\n            - Макрос доступен тем, кому доступны обычные ссылки\n            - Макрос развертывается в полный URL к корню игры, т.е. если игра стоит по адресу \"http://your_domain.tld/your_path/\", то строка \"sn://overview.php\" будет развернута в \"http://your_domain.tld/your_path/overview.php\"\n        - Макрос \"faq://\" - разворачивается в УРЛ ЧаВо, если он прописан в 'url_faq' конфигурации\n    Макросы \"sn://\" и \"faq://\" так же можно использовать в составе тэга BBCode 'url', например: \"[url=sn://index.php?page=options]Настройки игрока[/url]\"\n[+] Акции и ивенты\n    Теперь на странице информации \"О сервере\" выводится стандартное значение рейтов и значение рейтов в рамках акции\n    Теперь во время акций с увеличением скорости добычи ресурсов:\n        - Размеры хранилищ не увеличиваются - это вызывало много вопросов у новичков;\n        - Добыча И потребление энергии НЕ ИЗМЕНЯЮТСЯ - т.е. не надо строить дополнительные энергомощности или хранить избыток;\n        - Добыча в экспедиции не изменяется - как и было задумано;\n\n[~] Исследования\n    Гравитехнология для исследования теперь требует минимум 6-го уровня Гипертехнологии\n    ЗС теперь не требует напрямую Гипертехнологии для постройки\n    СН теперь не требует напрямую Гипертехнологии для постройки\n[~] Страница \"Боевой отчёт\"\n    Добавлен заголовок на страницу\n    Ссылки на планеты приведены к стандартному виду - [координаты] (тип) Имя\n    Улучшена вёрстка\n[~] Страница \"Закладки\"\n    Теперь в пределах одной категории важности заметки дополнительно сортируются в порядке убывания по координатам и типу планеты\n    Продублирована группа элементов для удаления заметок в самом верху страницы\n[~] Реклама\n    Реклама не показывается игрокам, которые играют меньше недели\n    Реклама не показывается игрокам, которые взяли Премиум-аккаунт\n[~] Партнерская программа\n    Теперь когда аффилейт (игрок, приглашенный реферралом) покупает МетаМатерию, то реферрал (игрок, пригласивший аффилейта) получает 20% от купленной ММ в виде Тёмной Материи (в отличии от 10% при получении аффилейтом ТМ)\n[~] Новапедия\n    Склады:\n        - \"Хранилища\" переименованы в \"склады\";\n        - Уточнено и расширено описание складов;\n[~] Чат\n    Теперь при маленьком размере экрана строка ввода сообщений переносится на отдельную строку - что бы было больше места для текста\n    Теперь при маленьком размере экрана текст сообщения переносится на отдельную строчку, а при совсем крохотном - так же на отдельную строчку переносится ник\n    При переносе ника/сообщения на отдельную строчку для лучшей читаемости блоки разделяются линией и перед сообщением делается отступ\n\n[-] Страница \"Симулятор\"\n    Убрана отладочная галочка \"Симуляция\" - симулятор всегда работает в режиме симуляции\n\n[@] Код\n    Огромное количество изменений в коде, выносить которые сюда не имеет смысла\n\n...и множество других мелких правок. Посмотреть изменения в коде и мелкие правки можно в файле docs/changelog_dev.txt\n\n\n\nProject \"SuperNova.WS\" Release 39 \"2014 annual joint operation report\"\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n[!] ВНИМАНИЕ! Требуется версия PHP >= 5.3.2\n[!] Рад представить вам очередной релиз СуперНовы. Перед вами - результат более чем года работы. Фактически, в этом мегапатче объединены целых ПЯТЬ релизов:\n      1. \"Зима-2014\" - системный релиз, где было проведена гигантская работа по упорядочиванию внутренних механизмов работы:\n        - Все юниты были отвязаны в БД от записей планет и пользователей;\n        - Вся работа с юнитами была переписана с нуля;\n        - С нуля была написана подсистема очередей;\n        - Разделены очереди кораблей и обороны;\n        - Полностью переписаны квесты;\n        - Добавлено глубокое сквозное кэширование, что позволило в 2-4 раза увеличить скорость работы движка;\n        - ...и многое, многое другое!\n      2. \"Весна-Лето 2014\" - багфиксы и небольшие оптимизации релиза З-2014;\n      3. \"Лето-Осень 2014\" - \"мобильнутый релиз\". Было сделано множество улучшений и усовершенствования для большего удобства игры с мобильных устройств (насколько это возможно без полной переделки темплейта и всех скинов);\n      4. \"Зима 2014-2015\" - \"бета релиз\". В начале зимы была выпущена бета 39-го релиза, но потом как-то всё закрутилось - новый год, фишки к НГ, ивенты к НГ, просто фишки, просто ивенты - и объем кода, написанного после беты неожиданно сам начал тянуть на отдельный релиз\n      5. \"Март-апрель 2015\" - \"бета релиз\". В середине февраля наконец-то было решено завязать с добавлением фишек и просто пофиксить баги... Очнулся я только в середине апреля. В игре появились звук, опросы, переработан код таймера, была добавлена пачка улучшений, пофикшены баги и переписана авторизация...\n    В общем, тут я решил все-таки выпустить 39й релиз as-is и сделать стабильную ветку\n    Общий объем проделанной работы можно оценить по количеству коммитов в GitHub. Ну или хотя бы по чейнджлогу релиза, приведенному ниже\n\n[#] payment_xsolla 1a0\n    (!) Новый модуль\n    (!) Стартовый релиз для СН 35a17.0+\n        Полностью реализован протокол \"Shopping Cart 3.0 Xsolla\" - команды 'check', 'pay' и 'cancel'\n        Поддержка режима тестирования\n        Конфигурация отдельным файлом config.php в каталоге модуля. Если конфигурация недоступна - модуль отключается\n        Поддержка выбора способа платежа xSolla\n        Генерик-плательщик xSolla\n        Хочу отметить часть методов платежа, добавленных к уже существующим:\n          - мобильные платежи: через SMS, со счета мобильного, сервия ZONG от PayPal, со счета Киевстар\n          - платежные системы: PayPal, EasyPay\n          - банковские переводы: Приват24, Сбербанк Онлайн, Банк24 Национальный кредит\n          - терминалы: EasyPay, Ibox, Терминалы Украины, Терминалы России\n          - кредитные карты American Express, JCB, UnionPay\n        Список далеко не полон - на сайте xSolla можно выбрать десятки других способов оплаты\n\n[#] menu_customize 0d0\n    (!) Новый модуль\n    (!) Базовая настройка меню\n        Новый раздел \"Настройки меню\" в \"Настройках\"\n        Настраивается вид кнопки \"Спрятать/Показать меню\". Она может быть:\n          - \"прилепленная\", т.е. всегда находится в левом верхнем углу экрана - даже когда страница скроллируется\n          - \"обычная\" - т.е. находится в левом верхнем углу СТРАНИЦЫ и скроллируется как нормальный элемент\n          - \"скрытая\" - т.е. кнопка не показывается. В этом случае недоступна кнопка \"Прикрепить/Открепить меню\". Так же при выборе этого варианта сбрасывается флаг открепления меню\n        Настраивается поведение кнопки \"Спрятать/Показать меню\":\n          - можно настроить скрытие меню при наведении курсора на кнопку \"Спрятать меню\"\n          - можно настроить показ меню при наведении курсора на кнопку \"Показать меню\"\n        Настраивается вид и поведение откреплённого меню:\n          - можно настроить скрытие откреплённого меню при выводе курсора за его пределы\n          - можно настроить показ откреплённого меню поверх всех элементов\n        Добавлена возможность выводить пункты меню в виде кнопок для большего удобства мобильных пользователей\n        Эта возможность доступна при базовой настройке меню: чекбокс \"Показывать пункты меню в виде кнопок\" в \"Настройках\", вкладка \"Профиль\", раздел \"Настройка меню\"\n    (!) Расширенная настройка меню\n        Для игроков с премиум-аккаунтом доступна пользовательская сортировка пунктов меню и скрытие отдельных пунктов (с определенными ограничениями в зависимости от уровня премиума, см. ниже)\n        Режим расширенной настройки меню включается при нажатии кнопки \"Настроить пункты меню\". При этом все остальные настройки становятся недоступными\n        У элементов, которые можно менять местами, слева появляется значок в виде двойной стрелки вверх-вниз. Такой пункт можно схватить курсором мышки и перетащить на новое место\n        У элементов, которые можно скрыть, справа появляется значок глаза. Клик на нём меняет видимость пункта меню. Белый глаз - пункт меню будет виден. Черный перечеркнутый глаз - пункт меню будет скрыт\n        После окончания настройки нужно нажать кнопку \"Сохранить настройки пунктов\". Настройки пунктов будут сохранены, а страница перегружена. При этом все остальные изменения в настройках сохранены НЕ БУДУТ\n        Так же нажатием кнопки \"Сбросить настройки пунктов меню\" можно вернуть меню первоначальный вид\n        Если скрыты какие-то пункты, то в самом низу меню появляется дополнительный пункт \"Показать скрытые\", который покажет скрытые пункты меню\n        Возможности по настройке меню зависят от уровня премиум-аккаунта игрока:\n          - нет премиум-аккаунта: доступны только базовые настройки\n          - Премиум 1-го уровня: игрок может менять местами пункты меню, кроме системных (название и логотип сервера, пункты \"Как играть\", \"Настройки\", \"Выход\" и логотип движка). Игрок не может скрывать пункты меню\n          - Премиум 2-го уровня: игрок может менять местами все пункты меню. Игрок может скрывать пункты меню, кроме системных\n          - Премиум 3-го уровня: игрок может менять местами и скрывать любые пункты меню\n        Кнопка \"Открепить меню\" сделана неперемещаемой\n        Администрация сервера имеет возможность менять/прятать все пункты меню даже без премиум-аккаунта\n        Все настройки из общего темплейта перенесены в соответствующий файл темплейта модуля\n\n[#] chat_advanced 5b6\n    (!) Функционал списка сообщений и онлайн-листа переписан на CSS и jQuery\n    (!) Администрирование\n        Полностью переписаны функции администрирования\n        На кнопках бана и мьюта добавлен попап с выбором сороков бана\n        На кнопка аньмюта при наведении курсора появляется соответствующая подсказка - что позволяет её отличить от кнопки мьюта\n        На замьюченом игроке в попапе видна причина мьюта\n        При операциях бана, мьюта и анмьюта чётко указывается, к какому пользователю будет применена команда. Это позволит исключить случайные промахи при обновлении онлайн-листа\n        Во всплывающее меню для бана/мьюта добавлено поле для ввода причины бана/мьюта. По умолчанию при мьюте поле пустое, а при бане заполнено стандартной причиной \"Заблокирован из чата\"\n        Иконки бана и мьюта теперь не показываются на аккаунты, которые выше игрока по иерархии\n        Добавлено больше сроков для команд мюьта и бана в меню администрирования\n        Во всплывающем меню для мьюта добавлен чекбокс \"Забанить без РО\" с соответствующим функционалом. По умолчанию галочка включена\n        И поле причины, и чекбокс бана без РО при открытии меню выставляются в значение по умолчанию. Это сделано специально для уменьшения вероятности ошибиться\n        Код меню администрирования теперь не рендерится для обычных игроков\n        Исправлена неработа некоторых диапазонов продолжительности мьюта/бана - в частности, \"y\" и \"w\"\n        Теперь можно забанить игрока из чата без РО. Для этого сразу после срока бана надо добавить восклицательный знак. Например, так:\n          /ban id 10 7d! Бан без РО\n    (!) Интерфейс\n        Изменено позиционирование попапов и подсказок с тем, что бы они не перекрывали вызывающий их элемент\n        Так же немного изменены сами попапы для лучшей читаемости\n        Смайлик, открывающий попап со смайликами, теперь сам стал кнопкой\n        Цвет фона в поле ввода сообщения изменен на черный\n        Выпадающий список с выбором цвета заменен на кнопку, при нажатии на которую выскакивает попап с вариантами выбора цвета\n        Выбранный цвет текста сразу же отражается в поле ввода сообщения, давая возможность увидеть, как будет выглядеть сообщение в игре\n        Панель элементов переверстана на чистом CSS без участия JS для расчёта размера элемента ввода - размер строки для сообщения теперь меняется динамически при ресайзе окна браузера\n        Добавлен код для устранения проблем с потерей фокуса строки ввода под некоторыми браузерами (в частности - IE 11, возможно поможет и на некоторых мобильных устройствах)\n        Раздвинуты элементы панели ввода: кнопка выбора цвета, кнопка смайла итд\n        Ссылка на историю чата вынесена в заголовок\n    (!) Список сообщений\n        Вывод сообщений переписан-таки на DIV-ах. В результате опять уменьшился объем передаваемых данных\n        Очень сильно оптимизирован вывод сообщений по размеру\n        Теперь вторая и последующие строки многострочных сообщений выравнены по первой строке, а не переносятся на следующую\n        Корректно выравнены иконки в нике относительно надписей\n        Благодаря новому парсеру, корректно выводятся URL-ы, стоящие сразу за символами \")\", \"]\" и \"}\"\n    (!) Список онлайна\n        Очень сильно оптимизирован вывод списка по размеру - даже без минимайзера на каждой строке выигрышь составляет более 0,5 кб!\n        При наведении на статус мьюта в списке онлайна сразу появляется подсказка с именем пользователя и сроком мьюта\n        ID пользователя перенесен из тултипа в онлайн-лист - так что его теперь легко увидеть\n    (!) Смайлики\n        Уменьшен размер выдачи: попап переделан на jQuery и вынесены стили в CSS, а так же убран лишний внутренний элемент\n        Улучшено позиционирование попапа, а сам попап стал выше и шире для удобства мобильных пользователей\n        Иконки заменены кнопками - для удобства мобильных пользователей\n        Клик в попапе вне иконки закрывает попап - для удобства мобильных пользователей\n        Смайлики теперь центрированы в своих кнопках и по вертикали\n        Уменьшен размер кнопок смайликов и попапа со смайликами\n        Исправлена ошибка неправильных кодов для смайликов \":)\" и \":(\"\n    (!) В чате теперь доступен расширенный функционал BBCode (см. ниже)\n    (+) Чат корректно работает с никами, содержащими символы \"'\", \" \", \"\\\", \"/\", \"&\"\n    (+) Звук\n        Добавлено звуковое уведомление при получении сообщения в чате\n    (%) Пермачат\n        Исправлена ошибка с неработающим ресайзингом элементов пермачата\n    (@) Добавлена поддержка компактизированных ников - соответственно в БД уменьшен размер таблицы с сообщениями\n    (@) JS и CSS\n        Файлы JS и CSS модуля маркируются датой и временем последнего обновления - для форсирования обновления браузерами при изменении этих файлов\n\n[#] player_premium 3c2\n    (!) Модуль переписан\n        Добавлена возможность продления Премиума и Апгрейда на более высокий уровень\n        В интерфейс страницы добавлена развернутая таблица с ценами на все комбинации уровня премиума и срока действия\n        При имеющемся премиуме дополнительно в таблице выводится информация о базовой стоимости премиума\n        Добавлена защита от двойного срабатывания при обновлении страницы\n        Базовая стоимость премиум-аккаунта уменьшена с 25.000 ТМ до 20.000 ТМ\n        Добавлен +6 премиум\n        Поддержка пунктов-кнопок в меню\n        Премиум-аккаунт добавляет 1 слот в очередь Исследований за каждый уровень Премиума\n    (+) Ивенты\n        Поддержка скидок на Премиум по ивентам\n\n[#] adm_user_stats\n    Блокировка по расчету недельных данных уменьшена до 1 недели - хотя данные и не совсем адекватные, однако лучше видеть не совсем адекватные данные, чем никаких\n    Добавлен рассчет % для активных (активность < 1 дня) и спящих (активность < 1 недели) пользователей\n\n[#] menu_applications_button 1с0\n    Иконки-ссылки для загрузки приложений под Android, Windows 8.1, Windows Phone 8\n    Иконки располагаются в каталоге модуля\n\n[#] unit_captain 3b0\n    (~) Работа с Капитаном при возвращении флота вынесена из основного кода в модуль\n    (~) Стоимость Капитана снижена до 20.000 ТМ\n\n[#] misc_radio v2c2\n    [!] Новый  HTML5/SWF плеер, совместимый с подавляющим большинством устройств\n        Теперь определение мобильныого устройства и переключение плеера на HTML5 версию работает корректно\n        Поправлены CSS-стили под поддержку бОльшего количества браузеров\n        Обновлен плейлист\n    (@) JS и CSS\n        Файлы JS и CSS модуля маркируются датой и временем последнего обновления - для форсирования обновления браузерами при изменении этих файлов\n\n[#] payment_robokassa 3c3\n    (!) Модуль переписан под новую систему платежей\n    (+) Добавлен generic-метод RoboKassa\n\n[#] payment_webmoney 3c0\n    (!) Модуль переписан под новую систему платежей\n\n[#] player_race 2d3\n    (@) Поддержка новой версии движка\n\n[#] player_award v0b0\n    (@) Поддержка новой версии движка\n\n[!] Очередь - полная переделка\n    С нуля написана универсальная подсистема очередей\n    Теперь очереди полностью независимые от записей пользователя и планеты\n    При обновлении очереди построек на планетах будут сконвертированы в новый формат\n    Очереди верфи и обороны будут обнулены, а стоимость юнитов в очереди - возвращена на планету\n    Полностью разделены очереди кораблей и обороны\n    На обзор планеты добавлена индикация очереди обороны\n    На картинки планет в обзоре планеты и обзоре Империи добавлена иконка активной очереди обороны (щит)\n    При ошибке постановки в очередь выдается соответствующее сообщение с подробным объяснением причины ошибки\n    При постановке в очередь юнитов больше, чем максимальный размер стэка очереди (2000 по умолчанию) в очередь ставятся подряд несколько стэков - до тех пор, пока не будет поставлено в очередь нужное количество юнитов или пока не закончатся свободные слоты в очереди\n    Унифицированный интерфейс особенно хорошо смотрится с вертикальной очередью построек (для экранов с разрешением свыше 1000 пикселов по горизонтали)\n    Вертикальная очередь построек включается в \"Настройках\" соответствующим чекбоксом\n[!] Покупка юнитов - унификация страниц \"Постройки\", \"Исследования\", \"Верфь\", \"Оборона\"\n    Вид страниц \"Постройки\", \"Исследования\", \"Верфь\", \"Оборона\" теперь унифицирован\n    Переверстана страница. Теперь она более дружелюбна к мобильным пользователям:\n      - Уменьшено количество юнитов в ряду до 4-х\n      - Переверстана панель детальной информации о юните\n      - Благодаря пп. 1 и 2 удалось на 20% уменьшить ширину страницы - теперь она практически не отличается по ширине от навбара и гораздо удобней для просмотра на узких экранах\n      - Кнопка постройки юнита сделана через jQueryUI - стала толще и красивее. Кнопка \"Удалить\" осталась такой же мелкой - для исключения случайных промахах на мелких экранах\n      - Размер страницы уменьшен на 25%-35%. Например, на тестовом прогоне размер уменьшился с 82676 до 64582 байт. И это со включенным минификатором!\n      - Ссылка на покупку юнита изменяется в контексте страницы: здания и боевые юниты \"строятся\", а технологии - \"исследуются\"\n    Описание юнитов:\n      - Теперь в описании юнита показывается не кумулятивная разница бонусов с текущим уровнем, а инкрементальная. Так гораздо лучше виден эффект перехода от уровня к уровню юнита, что позволяет лучше планировать своё развитие. Пример:\n        1. Пусть есть Астрокартография 2-го уровня\n        2. Раньше четвертая строка таблицы бонусов (Ур 4) показывала разницу в +2 колонии. Это была кумулятивная разницу с текущим 2-м уровнем. Т.е. +1 колония за 3 уровень (значение в строке Ур 3) и +1 колония за четвертый уровень - итого +2 колонии\n        3. Теперь четвертая строка будет показывать разницу в +1 колонию. Т.е. разницу между 3-м и 4-м уровнем Астрокартографии, которая и есть +1 колония. Значение в третьей строке (Ур 3) останется по-прежнему +1\n        4. На самом деле - изменение выглядит в интерфейсе горзадо проще и интуитивнее, чем его объяснение\n      - Если количество бонуса с прошлого уровня не изменилось - общее число бонуса подсвечивается желтым, а не зеленым. Таким образом, быстрый взгляд на таблицу дает полное представление об изменениях бонуса по уровням: зеленый цвет - положительные изменения, красный цвет - отрицательные, желтый цвет - нет изменений. Пример:\n        1. Возьмем опять же Астрокартографию 2-го уровня\n        2. Раньше в третьей строке таблицы бонусов (Ур 3) количество экспедиций (1) подсвечивало зеленым\n        3. Теперь в той же строке количество экспедиций (1) подсвечивается желтым - поскольку новых экспедиций на третьем уровне Астрокартографии не появится\n        4. Опять же - проще посмотреть в интерфейсе, чем объяснять\n      - На странице покупки юнитов показываются требования для покупки, а так же их выполнение\n      - Если к юниту нет требований - требования не показываются вообще\n      - Убрана надпись NaN/NaN в конце требования к юниту, когда требованием является определенный Родной Мир\n      - При входе на страницу первый элемент для отображения описания выбирается по порядку отображения, а не по ID. Например, на Верфи теперь отображается Легкий Истребитель, а не Супертранспорт, как раньше\n    Здания:\n      - Теперь здание можно удалить даже если требования к постройке не удовлетворены. Т.е. теперь здания можно удалять в любом порядке и не нужны соответствующие Планы\n    Верфь и оборона:\n      - Добавлена информация о боевых характеристиках (для всех юнитов) и скоростных характеристиках (для кораблей)\n      - В списке юнитов вместо остатка при постройке 1 юнита показывается цена постройки 1 юнита: красным - если не хватает ресурса, желтым - если хватает ресурса на 1 юнит, зеленым - если после постройки юнита еще остаются ресурсы\n      - В описании в таблице стоимости юнита цена и остаток ресурсов теперь меняются динамически с учетом количества выбранных для постройки юнитов\n      - В описании юнитов под таблицей стоимости теперь показывается максимальное количество юнитов, которое можно построить с имеющимися ресурсами\n      - Теперь при вводе корректного количества юнитов и нажатии кнопки \"Enter\" юниты ставятся в очередь\n      - Кнопка \"Построить\" блокируется, если не выбрано количество юнитов\n    Исследования:\n      - Премиум-аккаунт добавляет 1 слот в очередь Исследований за каждый уровень Премиума\n      - На странице технологий отображается \"Время исследования\", а не \"Время строительства\"\n      - На странице \"Исследования\" при выборе Астрокартографии в подробном описании добавлена таблица, показывающая увеличение количества экспедиций и колоний при апгрейде технологии\n[!] Обзор Вселенной - Переработка Обзора Вселенной\n    Страница сильно переработана\n    Полностью переделана работа с попапами:\n      - Изменен принцип позиционирования попапов - теперь они по минимуму закрывают информацию от пользователя, а так же стараются не вылазить за границы окна\n      - Устранены ошибки с позиционированием попапов у большинства мобильных пользователей - в отдельных браузерах ошибка может сохранится из-за некорректной реализации в браузере масштабирования\n      - Теперь попапы открываются не только при наведении мышки, а и по клику. Повторный клик на той же ячейке закрывает попап для удобства мобильных пользователей. Впрочем, \"мышисты\" тоже могут этим пользоваться\n      - Расширены области срабатывания попапов для удобства мобильных пользователей\n      - Передеалн попап игрока: в нём сдублированы все возможности, которые дают иконки. Так что теперь иконки можно отключать для экономии места на экране без потери функциональности\n    Восстановлена работа настройки \"Время показа подсказок\" в разделе \"Вселенная\" на вкладке \"Интерфейс\"\n      - Настройка даёт возможность задать задержку между наведением курсора мыши на элемент в Обзоре Вселенной и появлением попапа\n      - По умолчанию задержка выставлена в 500 миллисекунд (0,5 секунды)\n      - Значение \"0\" означает \"использовать задержку по умолчанию\". Для фактического отключения задержки можно использовать небольшие значения, например, \"1\"\n      - Задержка действует только при наведении курсора - при клике на элементе попап появляется сразу\n    Везде, где это имело смысл, ссылки изменены на кнопки для удобства мобильных пользователей\n    Заменены иконки шпионажа и ракетной атаки - теоретически их теперь не должны блокировать рекламорезки на мобильных устройствах\n    На пустой позиции в системе теперь высвечивается большая кнопка \"Колонизировать...\"\n    Кнопка \"Перейти\" в выборе Галактики/Системы вынесена вправо от элементов листания Вселенной\n    Исправлена ошибка появления отрицательных цифр в попапе обломков, если на планете не хватает дейтерия для отправки переработчиков\n    JS-код переписан с использованием jQuery и большая его часть вынесена в отдельный файл\n    Переверстана страница с активным использованием CSS - размер итоговой страницы уменьшен на несколько десятков % - в зависимости от населенности системы и активности флотов в ней\n    Теперь на уничтоженной планете/луне не всплывает попап\n[!] Империя\n    Дважды переверстана страница \"Империя\" с активным использованием CSS и jQuery\n    При идентичном виде и идентичной функциональности, на тестовом примере (15 планет при средней застройке) выигрышь в размере составил порядка 60-70%% при уже включенном минификаторе!\n    При большем количестве объектов или более плотной застройке, выигрышь может быть еще больше\n    Добавлено количество текущих/максимальных экспедиций\n[!] Планета\n    Размер страницы уменьшен на 5%-20%\n    Добавлено отображение бонусных уровней Губернатора\n    Ссылка \"Переработать\" сделана кнопкой. Она всегда показывается, если на планете есть переработчики. При этом, если обломков нет на орбите - кнопка неактивна\n[!] Планета/Управление\n    Переверстана страница. Теперь разные типы элементов разделены на группы и упорядочены так, что бы исключить случайное нажатие на мобильных устройствах\n    Тип ядра:\n     - Увеличена высота кнопки\n     - Количество ТМ, нужной для операции, теперь форматируется с группировкой тысяч и кодируется цветом\n    Телепорт:\n     - Увеличена высота кнопки\n     - Количество ТМ, нужной для операции, теперь форматируется с группировкой тысяч и кодируется цветом\n    Губернаторы: вынесены в отдельный блок\n    Перенос столицы:\n      - Сделано подтверждение на перенос столицы;\n      - Увеличена высота кнопки и кнопка теперь отключается, если перенос невозможен\n      - Количество ТМ, нужной для операции, теперь форматируется с группировкой тысяч и кодируется цветом\n    Уточнена надпись - какой пароль нужно ввести для сноса колонии\n[!] Меню - Крадущаяся мышка, затаившееся меню\n    Новые авторские иконки: уменьшенного размера, оптимизированные, оригинальные\n    Появилась возможность спрятать меню разово или однократно - до обновления страницы или перехода на другую страницу\n      - Возможность спрятать меню однократно бывает полезна, например, в окне чата, что бы увеличить его площадь или в окне \"Империя\", что бы больше информации влезло на экран\n      - Для того, что бы однократно спрятать меню нужно навестись мышкой или тапнуть по кнопке \"Спрятать меню\" в левом верхнем углу экрана. При этом кнопка изменится на \"Показать меню\"\n      - Что бы вернуть меню достаточно навестись мышкой или тапнуть по кнопке \"Показать меню\"\n    Так же можно спрятать меню на постоянной основе тем самым, увеличивать полезную площадь страницы\n    Для того, что бы постоянно спрятать меню, нужно нажать кнопку \"Открепить меню\" в самом верху меню. В открепленном режиме меню имеются следующие особенности:\n      - Статус открепленного меню запоминается в куках устройства, т.е. для каждого устройства открепление меню настраивается отдельно\n      - При каждом следующем открытии страницы меню будет сразу в спрятанном состоянии (см. выше)\n      - Кнопка \"Открепить меню\" меняется на кнопку \"Закрепить меню\"\n      - Что бы вернуть стандартное поведение меню достаточно нажать на кнопку \"Закрепить меню\"\n      - Что бы воспользоваться, нужно навестись мышкой или тапнуть на кнопке \"Показать меню\". При этом при выходе курсора мышки за пределы меню оно автоматически скрывается\n    Особенно полезна возможность скрытия меню будет для устройств с маленькими экранами - телефонов и мелкоразмерных планшетов\n    Однако она так же может оказаться полезной и для пользователей обычных компьютеров, благо из-за функционала кнопки \"Показать меню\" привычки в работе с меню практически не нужно менять\n    В стандартных скинах убраны \"скачки\" меню в процессе рендеринга страницы\n    В раздел \"Правила игры\" добавлена ссылка на подробную документацию к игре. Файл по ссылке открывается в новом окне\n    Ссылка на движок открывается в новом окне и на странице с описанием движка\n    Рекламная ссылка перенесена в самый низ меню\n    Поддержка модуля menu_customize\n    В темплейте включён рекламный блок и логотип СН\n[!] Заметки/Закладки - Объединение Заметок и Закладок\n    Теперь в \"Заметках\" можно добавлять координаты во Вселенной и тип объекта (Планета, Луна, Поле обломков):\n      - Ввиду полной бессмысленности дублирования функционала, \"Закладки\" убраны из игры\n      - Все существуещие в игре \"Закладки\" перенесены в \"Заметки\" вместе с комментариями\n      - Удалена таблица Закладок, код и темплейт\n    Теперь Заметку можно сделать прилепленной:\n      - Такие Заметки будут отображаться на всех страницах игры под навбаром сразу после Новостей в отдельной таблице\n      - Клик на заголовке переведет на страницу редактирования Заметок\n    Таблица на второй странице отправки флота теперь берет данные из Заметок:\n      - Выбираются только записи, у которых все три координаты планеты отличны от нуля\n      - В качестве текста используется заголовок Заметки. Работает сортировка по приоритету\n    В Заметке теперь может быть пустым либо заголовок, либо текст - но не оба одновременно\n    На странице редактирования теперь только важность Заметки выделяется цветом, а не вся Заметка, как раньше\n    Клик на координатах в обычных и прилепленных Заметках откроет страницу \"Вселенная\" в указанной галактике и системе\n    При редактировании заметки иконки подтверждения и отмены изменений разнесены в вертикальной плоскости\n    Можно делать закладки на слот Экспедиции\n[!] Артефакты\n    Новый тип Артефакта: Крюк. Он телепортирует астероид из ближайшего метеоритного пояса и запускает его на орбиту планеты, создавая таким образом луну. Доступны три вида Крюков: Малый, Средний и Большой. Малый создает луну минимального диаметра (1100 км), Большой - максимального диаметра (8999 км), а Средний - луну случайного диаметра (от 1100 до 8999 км)\n    Изменена логика работы Наностроителя и Эврестического Чипа. Теперь они уменьшают время соотвественно текущего исследования в Империи и постройки/разрушения текущего строения на планете/луне в два раза (если до окончания процесса осталось больше часа) или моментально заканчивают процесс (если до окончания осталось не более 1 часа)\n    В цене Артефакта добавлен разделитель тысяч\n    Добавлены временные картинки для Крюков\n    На Эвристический чип и Наностроитель добавлена защита от случайного срабатывания. Теперь они не срабатывают, если осталось меньше 1 минуты для постройки\n    Исправлена редкая ошибка, когда АКК мог пропасть при неудачной попытке развернуть его на полной планете\n[!] Опросы\n    Добавлена возможность проводить опросы/голосования\n    Опросы прикрепляются к новостям - один опрос на одну новость\n    Можно копировать новости с опросами\n    Можно редактировать новости с опросами, но при этом потеряются текущие результаты опроса\n    Поддерживается произвольное количество ответов, но не менее двух\n    Можно устанавливать срок действия опроса, используя синтаксис PHP-функции strtotime() или просто задавая дату окончания опроса (по серверному времени). По умолчанию опрос действует 1 сутки\n    Игрок может выбрать 1 вариант ответа из списка\n    Добавлена индикация срока опроса\n[!] UBEv4.1\n    Изменена процедура обсчета боя. Теперь щиты считаются индивидуально для каждого корабля\n    Это значит, что теперь практически невозможно провести бой без потерь с обеих сторон\n[!] Флоты\n    Проверка качества отправляемого флота на предмет наличия орбитальных структур (СС, ТОП, \"Лень\")\n    Поддержка внешней активации транзакции при отправке флота - для пакетной обработки массовой отправки со страницы своза ресурсов\n    Поддержка моратория на агрессивные миссии\n    Менеджер летящих флотов:\n      - Переписан менеджер летящих флотов для избежания зависаний\n      - Новый МЛФ должен гарантировать полное отсутствие дедлоков\n      - Интервал обсчёта флотов теперь задается в таблице `config` переменной 'fleet_update_interval'\n      - Так же теперь обновление флотов не производится во время отключений сервера\n      - Все мисиии переписаны для поддержки нового МЛФ\n      - Из основного кода удалена поддержка модуля Капитанов\n      - Добавлен таймер-сторожок против зависания флотов\n    Шпионаж:\n      - Включён \"Имперский шпионаж\":\n      - Уровень Имперского шпионажа (УИШ) - это сумма уровней Шпиона и Шпионской технологии с учётом всех доступных бонусов, но без учёта количества спутников-шпионов\n      - Если УИШ шпионящего больше или равен УИШ шпиониемого, то в отчете будут видны так же Имперские Технологии\n    Экспедиция:\n      - Поддержка дополнительных событий в Экспедиции\n    Колонизация:\n      - Исправлена ошибка, позволяющая колонизировать на 1 планету больше возможного количества\n[!] Статистика\n    Полностью переделан расчёт статистики\n    Скорость расчёта статистики заметно увеличена (при одновременном увеличении количества объектов для обсчёта!). Чем больше игроков и чем активнее игра - тем больше выигрышь в скорости обсчета. Ускорение обсчета на типичном сервере составляет от 10 раз и выше\n    Теперь полностью учитываются все юниты всех типов, включая корабли в полёте\n    Теперь в статистике по ресурсам полностью учитываются все ресурсы: на планетах, вложенные в очереди строительства/верфи/обороны/исследования, находящиеся на флотах в полёте. Так же учитывается наличная ТМ (ММ не учитывается)\n    Теперь при расчете статистики Альянса так же учитывается юниты Альянса и ресурсы в банке\n    Теперь расчитывается и отображается изменение места Альянса во всех типах статистики\n    Исправлена очепятка, из-за которой вообще не учитывался дейтерий\n    Исправлена очепятка, из-за которой в очках по ресурсам не учитывались ресурсы, находящиеся в очередях\n    Все вышеуказанные изменения приведут к однократной перетусовке в статистике и росту абсолютного значения всех видов статистики\n    Теперь в базе хранится статистика за 2 недели\n    Настраивается количество дней в таблице `config` переменная 'stats_history_days' (по умолчанию - 14 дней)\n    Теперь длинные ники игроков и названия Альянсов не переносятся на вторую строку\n    Теперь для новых аккаунтов при первом обсчете статистики в изменении места показывается \"*\", а не \"-(новое место)\"\n    Настройки страницы статистики (типа статистики, Игроки/Альянсы итд) теперь передаются в строке браузера - теперь можно легко обмениваться ссылками на конкретную страницу статистики\n[!] Настройки\n    Теперь при смене пароля игроку не надо логиниться заново - при смене пароля так же изменяется кука\n    Замер времени\n      - Замеры времени делаются индивидуально для каждого устройства\n      - Теперь время не будет сбиваться при переходе с устройства на устройство вне зависимости от разницы настроек во времени/часовых поясах\n      - Таймеры в JavaScript теперь не зависят от того, был ли произведен замер или нет - нужные данные вычисляются на клиентской стороне по данным с сервера\n      - Фактически, это означает, что все таймеры теперь всегда будут корректно работать - не взирая на правильность/неправильность часовых поясов, \"сбитых\" часов итд\n      - Замер времени сохраняется для рендеринга времени на стороне сервера (дата/время сообщений, чат, новости итд)\n      - На вкладке \"Профиль\" добавлена возможность вручную выставить разницу между серверным и клиентским временем. Для этого нужно выставить галочку \"Задать вручную разницу во времени\", ввести разницу во времени в секундах и сохранить изменения\n    Переделана страница настроек под табы\n    Добавлена возможность отключать колонок \"Статистика игрока\" и \"Информация об игроке\" в обзоре Вселенной\n    Добавлена возможность ввести основной емейл - если он еще не введен\n    Добавлены подсказки, объясняющие различие между основным и вторичным емейлом\n[!] BBCode - Расширение функционала BBCode\n    Расширен функционал BBCode - добавлена поддержка уровня автора сообщений при парсинге\n      Для пользователей с разным authlevel досутпны разные BBCode. В частности, Администраторам (authlevel 3) теперь доступны следующие возможности:\n        1. Автоматическое преобразование URL-ов в ссылки\n        2. BBCode [url=HREF]text[/url]\n        3. BBCode [c] может использоваться с любыми цветами в формате [c=#XXXXXX]text[/c], где #XXXXXX - HTML-код цвета\n      В новостях теперь доступен весь функционал BBCode\n      В чате теперь доступен расширенный функционал BBCode\n    Добавлены новые смайлики:\n      :accordion: :ban: :censored: :contract: :facepalm: :help: :hmm: :maniac: :panic: :poke: :pray: :whistle:\n    Заменены лучшими версиями (в основном - без подкладки белого \"креста\") смайлики:\n      :clap: :coffee: :nea: :popcorn: :rose: :quote: :shout: :sorry: :spiteful: :ups:\n    Убраны смайлики из-за их больших размеров, которые сильно портят форматирование\n      :tratata: :maniac:\n    Теперь парсер понимает URL-ы, стоящие сразу за символами \")\", \"]\" и \"}\"\n    Устаревшие HTML-тэги <u> и <s> заменены на <span style>\n    Исправлена ошибка добавления в конец URL закрывающего BBCode при парсинге чистых URL\n[!] Звуки\n    Добавлена библиотека для поддержки звуков ion.sound © 2014 Денис Инешин лицензия http://ionden.com/a/plugins/licence.html\n    По умолчанию звуки отключены. Для включения нужно на странице \"Настройки\" (вкладка \"Интерфейс\") поставить галочку \"Включить звуки в игре\" и сохранить изменения\n    Работа звуков в устаревших и/или мобильных версиях браузеров НЕ ГАРАНТИРУЕТСЯ!\n[!] Платежи\n    Полностью переделан интерфейс системы платежей\n    Изменены скидки за оптовую покупку. Теперь небольшие сумм покупок дают большую скидку, чем раньше\n    Курсы валют и бонусы оптовой покупки сведены в одну ячейку\n    Игрок теперь может выбрать валюту по-умолчанию\n    Валюта игрока будет использована во всех расчетах вместо базовой валюты сервера, т.е. при расчете стоимости ММ, стоимости пакетов итд\n    На каждом пакете теперь указывается его стоимость в валюте игрока\n    Выбор фиксированного пакета при покупке ММ теперь сразу переводит на следующую страницу\n    Добавлено 17 новых методов платежей и картинок к ним\n    Добавлены картинки для большинства поддерживаемых методов платежей\n    Если методов оплаты более 6 - остальные сворачиваются и прячутся с возможностью в дальнейшем развернуть\n    Переупорядочены доступные методы платежей - от наиболее используемых к наименее используемым\n    Исправлено вычисление рассчётной стоимости ММ в рублях\n    Добавлена поддержка WMB - белорусских рублей на WebMoney\n    Основной валютой сервера по умолчанию установлен доллар США (USD). Скорректированы курсы всех валют\n    Платежи теперь соблюдают порядок, назначенный им в module\n[!] Император\n    Переработана страница \"Император\"\n    Теперь на странице показывается статистика изменения основных показателей игрока за прошедшие 2 недели (меньше - если не накоплена нужная статистика)\n    Добавлен пробел между \"У вас\" и количеством сообщений\n[!] Режим отпуска\n    Переделана процедура ухода в отпуск\n    Теперь на странице отпуска не отображаются: меню, новости, навбар... По прежнему блокируется вход на любые страницы игры. Отдыхать - так отдыхать!\n[!] Квесты\n    Полностью переписан механизм квестов\n    Исправлен баг неполного начисления награды, когда одновременно выполняются более одного квеста\n[!] Авторизация\n    Полностью переписана с нуля система авторизации в игре\n    Все операции системы авторизации проводятся в init.php - дальше по коду передаются только результаты авторизации. Это дает возможность позже добавить плагины для авторизации во внешних сайтах\n    Страницы логина, регистрации и восстановления пароля сведены в одну и сделаны более дружественными для мобильных пользователей:\n      - При восстановлении пароля отсылается цифровой код, что облегчает ввод с экранной клавиатуры\n      - Код восстановления высылыается не чаще раза в час и действует 1 сутки\n    Теперь при сбросе пароля происходит автологин - не надо самому логиниться с новым паролем\n    Переделан выбор языков\n    Добавлены страницы редиректа старых адресов reg.php и lostpassword.php\n    Данные об user_agent и user_proxy вынесены в отдельную таблицу со справочником\n    Исправлена ошибка входа в игру, если текущей планетой является удаленная по Обслуживанию планета\n    Исправлена проблема пропадающих элементов на странице логина\n[!] Интерфейс\n    Мультиэлемент ввода чисел - полная переработка:\n      - Теперь можно не кликать несколько раз подряд для увеличения (+)/уменьшения (-) значения в ячейке, а достаточно зажать кнопку мышки - количество будет изменятся автоматически\n      - Изменение данных при зажатой мышке проводится с ускорением - чем дольше держать кнопку мышки, тем быстрее будет изменятся значение в строке ввода\n      - Добавлены кнопки \"0\" и \"М\" - соответственно устанавливающие значение поля в 0 и в максимальное значение\n      - Теперь при входе в ячейку если значение в ней отлична от нуля, то значение выбирается (как в операционных системах)\n      - Максимальное использование jQuery\n      - Элемент переделан под jQueryUI.button(). Выглядит получше и более дружественнен к мобильным пользователям (читай - больше по размеру)\n      - Убраны устаревшие функции bind(), live() и delegate()\n      - Уменьшено количество обработчиков\n      - Ускорена работа мультиэлемента\n      - Использование спецтега <ainput> и вставка мультиэлемента на его место методами jQuery\n      - Исправлена ошибка \"NaN\" при вводе первого нечислового символа\n      - Переверстан под таблицы из-за странного поведения float div в некоторых сценариях. Пока ребята из Вилларибо верстают сайт дивами....\n      - Установлена фиксированная ширина кнопок. Это сделало вид элементов аккуратнее. Ну, и заодно - исправило некрасивость на странице отправки флотов\n      - Новый мультиэлемент работает на страницах: постройки флота и обороны; подбора кораблей во флот; черный рынок - покупка и продажа кораблей, обмен ресурсов\n    Скины:\n      - При разрешении экрана менее 1224 пикселов фон либо не грузится вообще (страница входа/регистрации) или грузится облегченная версия фона если разрешение экрана выше 768 пикселов (скины EpicBlue и supernova-ivash)\n      - Это сделано для более быстрой работы на мобильных устройствах и маломощных компьютерах. Фишка работает только в перечисленных скинах и/или на указанных страницах\n      - Так же, если размер изображентия планеты невелик, то грузится файл с меньшим разрешением. В неподдерживаемых скинах картинки планет могу отсутствовать вовсе\n    Все строки ввода данных, чекбоксы и кнопки теперь используют jQueryUI в тех браузерах, в которых он работает\n    Переработано множество страниц для совместимости с новым видом интерфейса, сделано огромное количество мелких правок - так что даже не буду пытаться их все перечислить\n    Теперь текст кнопок и вводимые данные в элементах ввода имеют жирный шрифт\n    Везде, где возможно, поля ввода для логина и пароля ограничены 32 символами\n    Добавлена индикация аккаунтов, находящихся в отпуске. Такие аккаунты отмечаются специальной иконкой в нике и надписью \"В отпуске\" на странице \"Император\"\n    Для исключения блокировки корпоративными фаерволлами и прочим, \"sex\" заменен на \"gender\"\n    Добавлена возможность выбрать пол в \"Настройках\"\n    Удалены неиспользуемые картинки в каталоге OpenGame/img\n    Все темплейты теперь используют общую иконку для отправки писем из /design/images\n    Ссылки, открывающие дополнительные окна, теперь подчеркиваются двойной линией\n[!] Админка\n    Отключена \"Панель админа\"\n    Список игроков:\n      - Добавлена колонка \"Активен\", показывающее прошедшее время с момента прошлой активности игрока\n      - При наличии модуля платежей появляется колонка с общим количеством купленной игроком ММ\n      - Убрана колонка \"Е-Мейл\" - эту и другую иноформацию об игроке теперь можно посмотреть на отдельной странице\n      - В колонке \"Рефералы\" подколонки \"Игроки\" и \"ТМ\" выравнены по правому краю\n      - Добавлены разделители тысяч в количество ТМ, заработанной рефералами\n      - Колонки переупорядочены для большего удобства\n      - Уменьшен размер страницы примерно на 40-60%%\n      - Добавлена подсветка всей строки при наведении курсора для облегчения операций с аккаунтами\n    Информация об игроке:\n      - Добавлена базовая страница с информацией об игроке. Она доступна только Администраторам и выше. Перейти на неё можно кликнув по ИД или нику игрока на странице \"Список игроков\"\n      - В настоящий момент страница является чуть облагороженным дампом соответствующей записи в таблице users без возможности редактирования\n      - Отформатированы все даты и числа (там, где это имеет смысл)\n    Обслуживание:\n      - Добавлена упаковка логов транзакций Тёмной Материи. Записи, сделанные ранее 1 числа три месяца назад, пакуются в одну запись от 1 числа указанного месяца с суммой всех транзакций за период упаковки\n      - Добавлена агрегирование статистики онлайна игроков. Записи, сделанные ранее 1 числа три месяца назад, агрегируются в записи с интервалом по 10 минут со средним арифметическим онлайна за указанный интервал\n      - Добавлена чистка общих логов. Из таблицы `logs` при обслуживании удаляются записи, сделанные ранее 1 числа три месяца назад\n      - Добавлена чистка game_watchlist и stats_hide_player_list от несуществующих пользователей\n      - Теперь удаляются все сообщения (кроме личных и альянсовских) старше 4 недель у всех игроков\n      - Теперь удаляются все сообщения (кроме личных и альянсовских) любой давности у игроков, неактивных более 4 недель\n    Переделана работа режима обслуживания. Теперь различаются источники, переводящие сайт в режим обслуживания, для каждого из которых выводится своё сообщение:\n      - Блокировка из админки. Игроки в причине блокировки видят то, что введено в настройках сайта;\n      - Блокирование из статистики - своё сообщение;\n      - Блокирование из обновления - своё сообщение;\n      - Блокирования при первой инсталляции до окончания настройки\n      - Текущий режим отображается для игроков соответствующим сообщением, автовыбором соответствующего пункта в \"Настройках\" в админке и красным сообщением вверху странице в админке же\n      - Администратор может насильно отменить режимы блокировки, устанавливаемые статистикой и обновлением - однако делать это крайне не рекомендуется\n    Обновление статы:\n      Опять переделана процедура расчёта статистики\n      Теперь указывается не интервал расчёта статистики, а \"расписание\", т.е. конкретное время запуска в привязке к текущему времени. Ниже будет подробнее объяснено на примерах\n      Формат расписания изменился и теперь выглядит так:\n        <время запуска>[,<время запуска>...]\n        <время запуска>: [ГГГГ:[ММ:[ДД:[ЧЧ:[ММ:[СС]]]]]]\n      Пустые параметры приравниваются к нулю. Лидирующий ноль укзаывать не обязательно. Т.е. записи: \"0000:00:00:00:30:00\", \"0:30:0\" и \"30:\" - равноценны. Примеры:\n        - \"00:00:27:00\" означает \"запуск в 27 минут каждого часа\", т.е. в 00:27:00, 01:27:00, 02:27:00 итд;\n        - \"04::\" означает \"запуск в 4 утра каждого дня\"\n        - \"01::,17:15\" означает \"запуск в 1 утра каждого дня и в 17 минут 15 секунд каждого часа\", т.е. каждый день в 00:17:15, 01:00:00 (это сработало дополнительное расписание), 01:17:15, затем в 02:17:15, 03:17:15, 04:17:15 итд;\n        - \"1:4:30:00\" означает \"Запуск 1 числа каждого месяца в 04:30 утра\", т.е. 1 января в 04:30:00, 1 февраля в 04:30:00, 1 марта в 04:30:00 итд;\n        - \"2015:1:1:00:00:00\" означает \"Однократное срабатывание 1 января 2015 года ровно в полночь\"\n    Начисление ТМ:\n      - Убрано начисление по планете\n      - Теперь при начислении ТМ через админку в комментариях пишется причина начисления и кто произвел начисление\n      - Исправлена ошибка начисления ТМ игрокам, чье имя начинается со знака \"-\"\n    Начисление ММ:\n      - Теперь при начислении ММ через админку в комментариях пишется причина начисления и кто произвел начисление\n    Локализация:\n      - Восстановлена работа интерфейса локализации в админке\n    Записи логов:\n      - Определение дедлоков и добавочная информация для их диагностики\n      - Для получения добавчной инофрмации о дедлоках пользователь MySQL, под которым запускается игра, должен иметь право MySQL PROCESS\n[!] Инсталляция\n    Добавлен специальный режим отключения сервера \"Инсталляция и конфигурация\". В этом режиме игра стартует после инсталляции (логин по умолчанию - admin, пароль - admin). Так же его можно включить в админке или выставив в таблице `config` в записи 'game_disable' значение 4\n    Отличие данного режима отключения от остальных в том, что нём доступны страницы login.php и logout.php\n    Основное назначение режима - настройка сервера после инсталляции движка и аварийное восстановление после сбоев в работе сервера\n\n[+] Отправка флота\n    Добавлена кнопка 1/X, где X - количество неотправленных экспедиций. Появляется только если X > 1 и X < максимального количества экспедиций\n    Кнопка 1/(доступных экспедиций) теперь появляется только если не дублирует кнопки 1/(максимальное количество экспедиций) и \"Все корабли\"\n    Теперь при отправке флота виден уровень Капитана на планете и его скиллы\n    На второй странице отправки флота список текущих планет и список Заметок с координатами переделаны в кнопки для удобства мобильных пользователей\n    При подборе кораблей во флот теперь показывается актуальная скорость каждого корабля\n    Добавлены разделители тысяч к количеству кораблей во второй колонке\n    На странице подбора кораблей во флот добавлена опция сортировки списка кораблей\n    Возможна следующие виды сортировки: \"Стандартная\", \"По названию\", \"По скорости\", \"По количеству\" и \"По ID\" - по возрастанию характеристики\n    Так же возможна инверсная сортировка - по убыванию характеристики\n    Выбор вида и хода сортировки осуществляется дропдауном и галочкой в нижней части таблицы\n    Сделанный выбор сохраняется в настройках пользователя и затем используется при следующих открытиях той же страницы\n    Изменения вида или хода сортировки перегружает страницу\n[+] Своз ресурсов\n    Теперь в расчете строки ИТОГО учитывается ёмкость трюмов флота, т.е. показывается реальное количество ресурсов, которые будут свезены по текущим данным\n    Код расчёта переписан на jQuery\n    Исправлено PHP-предупреждение, если у игрока только одна планета\n[+] Сообщения\n    Отправка сообщений - \"Переписка\":\n      - Теперь при отправке сообщения в ответ на личное сообщения, доступна история переписки. Она показывается под формой создания сообщения\n      - В истории показываются в порядке написания сообщения от собеседника игроку и неудаленные сообщения от игрока собеседнику, но не более 20 сообщений\n      - Так же добавлена автоматическая замена нескольких подряд префиксов ответов (\"RE:\") на один\n    \"Написать сообщение\" теперь стало отдельной кнопкой\n[+] Рекорды\n    Для флотов и обороны теперь показывается суммарное количество юнитов на всех планетах и лунах. Флоты в полёте по-прежнему не учитываются\n[+] Чат\n    Добавлен звук при получении нового сообщения в чате\n    Теперь если по какой-либо причине отключается модуль chat_advanced, то в чате не видны личные и информационные сообщения\n    В списке онлайна Администраторы сервера теперь всегда идут первыми\n    Скруглены углы в попапах и в админском элементе ввода сообщений\n    Уменьшены размеры кнопок со смайликами, а так же уменьшен размер соответствующего попапа\n    Исправленна ошибка появления кнопок chat_advanced при открытии открепленного главного меню\n\n[~] Партнерская программа\n    Добавлена прямая ссылка\n    Теперь в простых ссылках указывается не УРЛ, а имя сервера\n    Изменены УРЛы на действительные (с reg.php на login.php)\n    Исправлена регистрация по партнерским ссылкам - теперь правильно регистрируются привлеченные игроки\n    Исправлена ошибка, при которой баннер не показывался без регистрации\n[~] Новости\n    Теперь статус новости (например, \"СВЕЖАЯ\") пишется перед датой, а не перед текстом новости\n    Теперь по умолчанию галочка \"Разослать новость всем игрокам\" отключена\n[~] О сервере\n    Добавлена индикация режима взаимодействия игроков с 1 IP (мультиаккаунтов)\n[~] Поиск\n    Отключён поиск планет. Я вообще с трудом понимаю, зачем он был изначально сделан в игре...\n[~] Симулятор боя\n    Переверстан интерфейс симулятора\n[~] Чёрный рынок\n    При полностью пустом списке б/у кораблей в продаже он пополняется случайным образом\n\n[%] Альянсы\n    Альянс теперь можно передать любому участнику - а не только заместителю главы\n    Исправлена ошибка с возможностью сделать Альянс пустым именем/тэгом/титулом главы\n    Исправлено незаполнение имени user_as_ally тэгом при создании Альянса\n    Исправлена ошибка отправки многострочных сообщений\n    Исправлена невозможность включить прием заявок после его отключения\n    Исправлена ошибка попадения в список кандидатов на передачу Альянса игроков не из Альянса\n    Исправлен прием игрока в Альянс, чей тег или название содержат спецсимволы\n\n[@] Документация\n    В пример конфигурационного файла добавлено уведомление о необходимости использования разных префиксов для нескольких копий СН на одном сервере\n[@] jQuery\n    Добавлены виджеты Droppable и Sortable\n[@] Темплейты\n    {-path_prefix-} заменен на {D_SN_ROOT_VIRTUAL}\n    Добавлена глобальная переменная SN_GOOGLE - в темплейт и JS\n    Добавлено подключение CSS, специфичного для темплейта - _template.css - из корня темплейта\n    Основная часть страницы теперь центрируется не через <center>\n[@] Код\n    Рендерер ников:\n      - Полностью переписан рендерер ников\n      - Добавлена поддержка компактизированных скин-независимых ников\n      - Расширена возможность отключать части ника\n      - Иконки теперь имеют фиксированные положения, не зависимые от порядка загрузки модулей\n      - Добавлена поддержка нового рендерера ников в стандартный чат\n    Статистика:\n      - Добавлено принудительное увеличение памяти в процедуру расчета статистики\n      - Размер памяти, резирвируемый под процесс PHP при обсчете статистики, можно менять переменной 'stats_php_memory' в таблице `config`. Синтаксис - такой же, как и в php.ini. Значение по умолчанию - 1024M\n      - Минимальный интервал обсчета задается переменной 'stats_minimal_interval' в таблице `config`. Значение по умолчанию - 600 секунд\n      - Теперь во время расчёта статистики движок переходит в режим обслуживания\n    Лог ТМ:\n      - Лог ТМ при найме/покупке Чертежа теперь пишется на языке текущего пользователя. Так же в него пишется стоимость и срок найма\n      - В лог ТМ теперь пишется запись об увольнении Наёмника с детальной информацией\n      - Более подробный лог траты ТМ при покупке секторов на планете\n    Метаматерия:\n      - Добавлено и заполнено поле dark_matter_total в таблице `users`. Поле так же изменяется при начислении ТМ внутренними механизмами движка\n    Локализация:\n      - Добавлена поддержка вариантов языка - типа, en-US и en-UK\n      - Добавлена подсистема сбора информации об употреблении строк локализации в коде. Включается переменной \"server_locale_log_usage\" в таблице `config`\n    Две новые директивы отладки в init.php:\n      - DEBUG_SQL_COMMENT - включает комментирование SQL-запросов\n      - DEBUG_SQL_ONLINE - включает лог SQL-запросов в таблицу `logs`. Так же подразумевает DEBUG_SQL_COMMENT\n    Теперь можно отключить защиту от взаимодействия аккаунтов с одним IP выставив в таблице `config` параметр `game_multiaccount_enabled` в 1\n    Убраны BOM-префиксы в исходниках - таким образом, восстановлена работа создания баннера на странице \"Заработай ТМ\" и, собственно, сама генерация баннера\n    Исправлено предупреждение в /includes/classes/supernova.php line 125\n    Исправлено предупреждение Warning: Invalid argument supplied for foreach() in includes/db.php on line 365\n    Исправлено предупреждение в uni_coordinates_valid()\n    Поставлены заглушки для будущей поддержки GeoIP\n[@] Модули\n    Теперь модули могут добавлять пункты в админское меню\n[@] Apache\n    Добавлены файлы .htaccess\n[@] JS и CSS\n    Все файлы JS и CSS в основном коде маркируются датой и временем последнего обновления - для форсирования обновления браузерами при изменении этих файлов\n    Информация из global.css, относящяяся к темплейту OpenGame перенесена в CSS темплейта\n[@] Локализация\n    Добавлена переменная активного языка в classLocale\n    Добавлено уведомление о критической ошибке при попытке вызвать функции локализации с $lang не в виде класса\n[@] БД\n    При старте уровень транзакций сессии устанавливается в REPEATABLE_READ для меньшей зависимости от настроек сервера\n    Добавлена возможность установливать уровень отдельной транзакции в sn_start_transaction()\n    В doquery() запрос теперь обрабатывается функцией trim()\n\n\n\nProject \"SuperNova.WS\" Release 38 \"Admin astro expo news bugfix\"\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n[#] player_award 0a2\n    (!) Новый модуль\n        Раздел \"Награды и достижения\" на странице Императора, видимый всем\n        Поддержка орденов, медалей, памятных знаков, вымпелов, бэйджей\n        Отдельный тип опции рендера ника\n    (!) Орден Спонсора четырех степеней - в комплекте\n        Иконка Ордена Спонсора в нике везде, где допускаются иконки\n    (!) Бессмертный\n        Памятный знак \"Бессмертный\"\n        Знак начисляется автоматически при покупке хотя бы одной единицы ММ\n        Статус \"Бессмертного\" означает сохранение аккаунта при автоматической чистке БД (Админка/Обслуживание)\n[#] misc_radio 0a0\n    (!) Новый модуль\n        Новый пункт меню 'Радио \"Космос\"', открываюший в новом окне плеер радио\n[#] chat_advanced 2c1\n    (!) Перманентный чат (миничат)\n        Реализован миничат через iframe. Чат делается перманентным при нажатии ссылки \"Прикрепить\" под списком онлайна пользователей. Можно прикреплять как общий чат, так и чат Альянса. Одновременно может быть прикреплен только один чат - общий или Альянса\n        Прикрепленный чат можно открепить - ссылка \"Открепить\" под списком онлайна в прикрепленном чате\n        Можно изменять соотношение фреймов, отведенных под основной экран и миничат. Для этого нужно потянуть за разделитель между фреймами\n    (+) История чата\n        Добавлены кнопки листания на страницу вперед/назад, на первую/последнюю страницы истории\n    (+) Смайлики\n        Добавлен смайлик :sarcasm:\n        Теперь смайлики размещены в отдельном попапе и не занимают лишнее место. Вызвать попап можно кликнув на смайлик слева от строки ввода сообщения\n    (~) Whisper\n        Команда \"/w\" теперь корректно работает с никами, в которых есть пробелы. Для этого ник нужно заключить в двойные кавычки. Подсказка по команде изменена соответствующим образом\n        Изменен формат вывода шепота: \"(от кого) -> (кому)> (сообщение)\"\n        Теперь клик на имени собеседника в списке сообщений так же добавит в строку текущего сообщения команду \"/w <имя адресата> \". Так будет легче переписываться с игроками, находящимися вне чата\n    (~) Интерфейс\n        Все неявные элементы, клик по которым совершает какое-либо действие на странице (например, ник в списке онлайна) выделены соответствующим образом\n        Теперь ники подчеркиваются цветом ника и не подчеркиваютя иконки (если таковые есть в оформлении)\n        Изменен алгоритм смены фокуса, что бы окно миничата не мешало работе в основном окне\n        Убрана дублирующаяся надпись \"Игроки онлайн\", а количество игроков перенесена в заголовок списка онлайна\n    (~) Клавиатура\n        По \"Ctrl+Enter\" теперь так же отсылаются сообщения\n    (~) Таймаут\n        Теперь при отключении чата по таймауту можно обновить окно чата/миничата соответсвующей ссылкой, которая появляется вместо строки ввода сообщения\n        Теперь таймаут так же убирает список игроков онлайн\n[#] player_race 2d0\n    (+) Марс\n        Марсиане так же получают +1 уровень к Астрокартографии\n    (+) Родные миры\n        Теперь родной мир можно выбрать сразу на странице \"Родные миры\"\n[#] unit_res_metamatter 0a0\n    (!) Новый модуль\n        Модуль активирует новый ресурс в игре - Метаматерию\n        Метаматерия - новый тип ресурсов, который можно приобрести только за реальные деньги. Таким образом отделяются ресурсы, которые можно приобрести внутри игры и ресурсы, которые можно только купить. Это нужно в первую очередь для добавления услуг и сервисов, которые требуют от движка платежей в реальных деньгах - например, СМС-информирование об атаках\n        Так же это позволит добавить в игру больше возможностей для взаимодействия игроков, не опасаясь сильного дисбаланса от такого взаимодействия и/или смещения экономики игры в сторону pay-2-win\n        Добавлена возможность начислить игроку ММ из админки\n        Модуль поставляется в пакете с любым платежным модулем\n        Добавлена иконка Метаматерии в навбар\n        Добавлено поле для общего количества полученной метаматерии - в частности для получения статуса \"Бессмертный\" (см. ниже). Статус \"Бессмертный\" работает так же при отсуствии модуля player_award\n        В админку добавлена страница просмотра платежей с фильтрами\n[#] Модули платежей\n    Все модули платежей полностью переписаны\n    Максимальная унификация модулей - все общие части вынесены в модуль-родитель\n    Все сообщения внутри модуля генерируются во внутренних кодах СН\n    Добавлена подсистема конвертации внутренних кодов в коды платежный систем (там, где это имеет смысл)\n    Все модули переделаны под работу с Метаматерией, а не ТМ\n\n\n[!] Технологии/Астрокартография\n    Экспедиционная технология и Колонизационная технология заменены одной технологией Астрокартографии\n    Стоимость имеющихся уровней устаревших технологий возвращена на главную планету игрока, а сами технологии удалены\n    Устаревшие технологии, находящиеся в исследовательской очереди игроков, удалены, а их стоимость возвращена на планету, где была запущена технология\n    По умолчанию убрано ограничение на максимальное количество колоний\n    Уровень развития Астрокартографии влияет на:\n      1. Максимальное количество колоний\n      2. Максимальное количество экспедиций\n      3. Максимальное время отправки флота в экспедицию\n    Теперь видно текущее и максимальное количество колоний:\n      1. В Обзоре Империи (первая колонка, вторая строка)\n      2. На странице выбора кораблей во флот при переходе на неё из Обзора Вселенной с миссией \"Колонизация\"\n      3. На странице выбора миссии \"Колонизация\"\n[!] Полностью переписаны Экспедиции\n    Экспедиции теперь планово-прибыльные, т.е. полеты в экспу одним и тем же флотом в среднем будут приносить прибыль, а не убытки, как раньше\n    Количественные результаты Экспедиций (нахождение флота, ресурсов, ТМ) теперь привязаны к стоимости отправляемого флота. Т.е. чем дороже флот в пересчете на ресурсы - тем больше будет найдено в Экспедиции\n    Экспедиции стали средне- и высокоуровневым контентом - существуют минимальные размеры флотов, которые вообще имеет смысл посылать в Экспедиции. Меньшие флоты попросту не будут ничего привозить (см.ниже). Хотя, например, фармить ресурсы транспортами можно прямо со старта игры - учитывая плановую прибыльность обновленных Экспедиций это вполне имеет смысл, особенно \"шахтерам\"\n    Теперь максимальная длительность Экспедиции зависит от уровня Астротехнологии - 1 час за каждый уровень технологии\n    Теперь время нахождения флота в Экспедиции влияет на шанс найти что-либо в процессе миссии - как на положительный шанс, так и на отрицательный\n    Изменены шансы происходящих событий (отношения расчитаны для обновленной Экспедиции в 1 час):\n      1. Шанс того, что в Экспедиции не произойдет ничего, увеличен примерно в 2,5 раза\n      2. Шанс потери флота уменьшен почти в 20 (!) раз\n      3. Шанс нахождения ресурсов увеличен на треть\n      4. Шанс нахождения флота уменьшен на треть\n      5. Шанс нахождения ТМ увеличен на треть\n    Изменено количество находимых в Экспедиции ништяков и оно теперь привязано к общей стоимости флота:\n      0. Количество ништяков может быть \"Нормальным\", \"Большим\" и \"Очень большим\". Соответственно меняются возможное количество находимых ништяков (идея упёрта с Огейма)\n      1. Количество ништяков отбалансированно для достижения планово-прибыльного характера Экспедиции и для компенсации изменения шанса происходящих событий\n      2. Количество находимых ТМ теперь вариабельно. Максимальное количество находимых ТМ - 10.000. Коэфцициент пересчета стоимости флота в ТМ зависит от курса ТМ (который, в свою очередь, зависит от скорости добычи ресурсов). Чем он выше - тем больше кораблей надо на шанс получения 1 ТМ. Для ориентировки - на х1 нужно запустить 10 эсминцев для получения 1 ТМ\n      3. Качество найденного флота теперь очень сильно зависит от качества исследовательского флота: все находимые корабли дешевле, чем самый дорогой корабль в Экспедиции. Максимальная стоимость флота зависит от скорости добычи на сервере\n      4. Качество найденных ресурсов теперь вариабельно. В среднем находится 50% металла, 37,5% кристалла и 12,5% дейтерия. Однако в частном случае доли ресурсов могут варьироваться в очень широких пределах. Максимальное количество находимых ресурсов прямо пропорционально скорости добычи ресов\n    Теперь за полёты в Экспедиции начисляется экспедиционный опыт. За набор экспедиционного опыта начисляются уровни. При получении нового уровня начисляется 1.000 ТМ\n    Количество опыта для получения уровня - геометрическая прогрессия с первым членом 10 и показателем 1,05. Ниже дается для ориентировки небольшая таблица: в первой колонке - экспедиционный уровень, во второй - количество опыта для перехода на следующий уровень, в третьей - общее количество экспедиционного опыта для перехода на следующий уровень. Собственно, таблица:\n        1      10        10\n        2      10        20\n        3      11        31\n        4      11        42\n        5      12        54\n        6      12        66\n        7      13        79\n        8      14        93\n        9      14       107\n       10      15       122\n       15      19       209\n       20      25       321\n       25      32       465\n       30      41       650\n       35      52       887\n       40      67     1.189\n       45      85     1.575\n       50     109     2.070\n       75     369     7.530\n      100   1.252    26.052\n      150  14.361   301.323\n      200 164.691 3.458.217\n    Посмотреть текущее текущий уровень, общее количество экспедиционного опыта и необходимое количество для перехода на следующий уровень можно на странице \"Император\"\n    Для облегчения регулярных Экспедиций с одной и той же планеты добавлена новая кнопка на экран подбора флота. При максимальном количестве экспедиций (Х) более одной доступна новая кнопка на странице подбора флота - \"1/X\"\n[!] Админка\n    Полностью переписана страница \"Список сообщений\"\n    Полностью переписана страница \"Флоты в полёте\". Процедуры унифицированы с пользовательской частью\n    Полностью переписана страница \"Обзор\"\n    Полностью переписана страница \"Добавить луну\"\n    Переработана страница \"Записи система логов\"\n    Переработана страница \"Начисление ТМ\"\n    Переработана страница \"Начисление ММ\"\n    Меню в админке переделано на динамическое - по типу меню игроков\n    Теперь сразу после обслуживания происходит обновление статистики - для устранения разрывов в местах игроков, которые могут появится из-за удаления старых аккаунтов\n    Убраны операции очистки таблиц, дублирующие работу констраинтов\n    Теперь для запуска обновления из админки используется гораздо более безопасная проверка по AUTHLEVEL пользователя, а не по HTTP_REFERRER вызывающей страницы\n[!] Страница игрока\n    Добавлена возможность просмотреть страницу игрока (ака \"Император\")\n    Для этого нужно кликнуть на иконку \"Император\", которая доступна:\n      1. На странице статистики\n      2. В результатах поиска\n      3. В Обзоре Вселенной\n\n\n[+] Новости\n    Добавлена информация о публикаторе новости\n    Изменено отображение новости\n    Теперь свежие новости показываются на всех страницах залогиненного пользователя\n    Теперь для того, что бы скрыть свежие новости не обязательно открывать страницу новостей - достаточно кликнуть на кнопку \"Закрыть\" в правом верхнем углу списка новостей\n    Добавлена вторичная сортировка новостей по ID\n[+] Меню\n    Переформатировано меню - убраны дублирующиеся пункты, ЧаВо перенесено вверх\n    Пункты меню \"ЧаВо\", \"Форум\" и \"Правила игры\" открываются в новых окнах\n[+] Обзор Империи\n    Оптимизирован HTML-код страницы. В среднем в минифицированном состоянии выигрыш составил порядка 6 кб на 1 планету/луну. Чем больше объектов в Империи и чем больше типов юнитов - тем больше выигрышь\n    Строка таблицы с координатами перемещена под строку с названием планет для унификации вывода\n    Строка с количеством секторов убрана - она дублирует информацию на иконке планеты\n\n\n[~] Отпуск\n    Минимальный срок отпуска составляет 1 неделю\n    Введен таймаут на следующий отпуск - 1 недели с момента выхода из предыдущего отпуска\n[~] Удержание\n    Длительность удержания теперь находится в промежутке от 1 до 12 часов\n[~] Статистика\n    Теперь на странице статистики показывается так же время следующего обновления\n    Время предыдущего и следующего обновления учитывает разницу между локальным и серверным временем\n[~] Навбар\n    Если страница с навбаром открыта во фрейме (например, при прикреплении чата) в под навбаром появляется ссылка \"Обновить страницу\", при нажатии которой страница по фрейме будет обновлена\n[~] Локальное время\n    Теперь замер разницы между клиентским и серверным временем производится автоматически каждый час\n\n\n[%] Альянсы\n    Исправлена ошибка \"налазания\" длинного внешнего текста на логотип Альянса\n[%] Флоты\n    Исправлена ошибка при отправке Капитана с миссией \"Транспорт\"\n    Усилена защита от отправки флотов в нетранспортную миссию с ресурсами\n[%] Сообщения\n    Исправлена ошибка в сообщениях, если указан неправильный класс сообщений\n[%] Поиск\n    Исправлена ссылка на страницу статистики для ранка 1000+\n    Убрана ссылка на страницу статистики для неучаствующих в подсчете аккаунтов (например - Адмиинистрации сервера)\n[%] Боевой отчет\n    Устранено появление строки \"Дата и время\" для симулированных отчетов при ненулевой разнице клиентского и серверного времени\n[%] Настройки\n    Исправлена индикация режима защиты планет Администрации\n    Исправлено отображение статуса удаления аккаунта\n\n\n[@] admin/Sypex Dumper\n    Sypex Dumper обновлен до версии 2.0.11\n[@] Темплейты\n    Добавлена поддержка нескольких темплейтов\n    Меню и навбар могут быть отключены параметрами в темплейте\n    Добавлена возможность подгрузки серверных CSS для скинов\n[@] Рендер ников\n    Добавлена опция, позволяющая присвоить нику дополнительные CSS-классы\n[@] БД\n    Изменена таблица `payment`\n[@] Расписание\n    Изменен формат расписания. Теперь он определяет интервал запуска задачи и имеет вид:\n      Г-М-Д Ч:И:С\n    где Г, М, Д, Ч, И, С - соответственно длина интервала в годах, месяцах, днях, часах, минутах и секундах\n    Значения левее первой значащей цифры можно не указывать. Например, \"0-0-1 0:0:0\" можно записать как \"1 0:0:0\" и это будет означать \"запустить задачу раз в сутки\"\n    Нулевые значения можно опустить. Например, предыдущий интервал можно записать так же в виде \"1 ::\". Обращаю внимание на пробел между \"1\" и \":\"! Пробел - значащий разделитель и его опускать в данном случае нельзя, потому что интервал \"1::\" будет истолкован как \"запустить задачу раз в час\"!\n[@] Вселенная\n    Добавлены картинки-плейсхолдеры для аватара/лого Альянса/миниатюры планеты\n[@] Прочее\n    $time_now теперь определяется из $microtime\n    Новая процедура определения локальных путей для поддержки PHP 5.3+\n    Методы локализации инкапсулированы в класс и при работе с объектами вызовы процедур редиректят в методы объекта\n    Убрана ошибка уровня PHP_STRICT в классах кэширования\n    message() теперь работает через PTE-объект\n    Чёрный рынок переписан на использование result вместо message()\n\n\n\nProject \"SuperNova.WS\" Release 37 \"Year of Work\"\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n[#] payment_webmoney 0a4 - модуль приема платежей на кошельки WebMoney\n    Требуется СН не ниже 37a9.20\n    Поддержка нескольких кошельков с разными валютами\n    Поддержка SUCCESS_URL\n[#] payment_robokassa 0a1 - модуль платежей через агрегатора RoboKassa\n    Требуется СН не ниже 37a9.30\n[#] Расширенный чат - chat_advanced v1d0\n    Требуется СН не ниже 37a4.0\n    Добавлена поддержка локального времени в чат и историю чата\n    Теперь можно использовать команды  при  выбранном  цвете  сообщения.  Ранее\n    такие команды не воспринимались системой чата\n    Произведена замена цветов для лучшей читаемости сообщений: red  ->  maroon,\n    blue -> cyan\n    Цвет green оставлен для пользвателей, а подтверждающие системные  сообщения\n    используют цвет lime - как и в остальном интерфейсе сервера\n    Системные и приватные сообщения теперь выделяются жирным шрифтом\n    Клик на имени игрока в списке онлайна теперь всегда добавляет команду  \"/w\"\n    в начало сообщения - а не в конец, как ранее\n    Скорость обновления  в  AJAX  части  чата  теперь  регулируется  переменной\n    'chat_refresh_rate'\n    Теперь игроки из онлайн-списка  исчезают  сразу  после  выхода  из  чата  -\n    таймаут попадания в список установлен как удвоенный 'chat_refresh_rate',  а\n    не как 'chat_timeout' ранее и вычисляется по дополнительному полю, а не  по\n    `chat_player_activity` как ранее\n[#] player_premium 1d3\n    Добавлена индикация уровня премиума в меню\n    Добавлена  индикация  остатка  времени  Премиума  в  пункт  меню   в   виде\n    прогресс-бара с цветовым кодированием:\n      Зеленый - осталось не менее 50% времени пермиума\n      Желтый - осталось меньше 50%, но не менее 25%\n      Оранжевый - осталось меньше 25%, но не менее 10%\n      Красный - осталось меньше 10%\n      Цвет фона - нет Премиума\n[#] Локализация/Узбекский\n    Добавлены переводы на узбекский для модулей: chat_advanced, player_premium,\n    player_race, player_race_units, unit_captain и unit_hope\n\n\n[!] Экономика/Плотность планеты\n    Добавлен новый параметр  планеты  -  плотность.  Он  определяет  химический\n    состав геосферы планеты и влияет на добычу ресурсов на ней\n    Плотность планеты лежит в диапазоне от 850 до 9250 кг/м3.  Плотность  новых\n    планет распределена случайным образом по нормальному распределению\n    Существует 7 классов плотности - с уникальным набором  коэфициентов  добычи\n    для каждого класса:\n      Ледяные планеты (<2000 кг/м3) - встречаются  очень  редко:  очень  низкая\n      добыча металла, очень низкая  добыча  кристаллов,  очень  высокая  добыча\n      дейтерия\n      Силикатные планеты (2000=3250 кг/м3) - встречаются  редко:  очень  низкая\n      добыча металла, очень высокая добыча  кристаллов  и  еще  хорошая  добыча\n      дейтерия\n      Каменные планеты (3250-4500 кг/м3) - встречаются  часто:  хорошая  добыча\n      металлов, высокая добыча кристаллов и низкая добыча дейтерия\n      Стандарнтые планеты (4500-5750 кг/м3) - встречаются очень часто:  хорошая\n      добыча металлов, хорошая добыча кристаллов и хорошая добыча дейтерия\n      Железнорудные  планеты  (5750-7000  кг/м3)  -  встречаются  часто:  очень\n      хорошая  добыча  металлов,  низкая  добыча  кристаллов  и  низкая  добыча\n      дейтерия\n      Металлические планеты (7000-8250 кг/м3)  -  встречаются  редко:  отличная\n      добыча металлов, низкая добыча кристаллов и низкая добыча дейтерия\n      Тяжелометаллические планеты (>8250  кг/м3)  -  встречаются  очень  редко:\n      великолепная добыча металлов, очень  низкая  добыча  кристаллов  и  очень\n      низкая добыча дейтерия\n    Стартовая планета имеет плотность 5500 кг/м3 и принадлежит  к  4-му  классу\n    плотности. Все луны имеют плотность 2500 кг/м3 и принадлежат ко 2-му классу\n    плотности\n    Тип ядра планеты можно изменить  за  ТМ.  Возможность  доступна  на  экране\n    управления  планетой  (Обзор  планеты  ->  Управление).   Стоимость   смены\n    высчитывается динамически и зависит от того,  насколько  сильно  отличается\n    текущий тип ядра от желаемого\n    Добавлено отображение типа ядра планеты на страницу \"Обзор планеты\"\n    На страницу \"Обзор Империи\"  добавлено  отображение  типа  ядра  планеты  с\n    цветовым кодированием:\n      Зеленый - тип ядра встречается очень часто\n      Желтый - тип ядра встречается часто\n      Оранжевый - тип ядра встречается редко\n      Красный - тип ядра встречается очень редко\n    В Новапедию добавлена статья про плотность и типы ядер планет\n[!] Артефакты\n    Добавлены два новых Артефакта: \"Эвристический чип\" и \"Наностроитель\"\n    Артефакты уменьшают на 1 час соответственно время текущего  исследования  и\n    время постройки/уничтожения текущего здания на текущей планете\n    Если  оставшееся  время  исследования/постройки/уничтожения  меньше  одного\n    часа, то Артефакт обнуляет время. Разница не переходит на следующий слот  в\n    очереди\n    Стоимость эвристического чипа составляет 20.000 ТМ\n    Стоимость наностроителя составляет 5.000 ТМ\n    В очередь построек добавлена возможность использовать Наностроитель  -  при\n    наличии Артефакта на складе\n    В очередь построек добавлена возможность использовать Эвристического чипа -\n    при наличии Артефакта на складе\n[!] Экономика\n    Изменен  алгоритм  расчетов  бонусов  добычи  ресурсов.  Список   изменений\n    приводится ниже:\n      1. Бонусы на добычу ресурсов улучшают так же базовую добычу на планете\n      2. Бонусы на добычу ресурсов так же увеличивают потребление сопутствующих\n      ресурсов - дейтерия (для Термоядерной Электростанции) и энергии (для всех\n      остальных шахт)\n      3. Бонусы на  добычу  ресурсов  улучшают  так  же  выработку  энергии  на\n      спутниках\n      4. Естественное производство  дает  100%  ресурсов  даже  при  недостатке\n      энергии\n    Изменен  алгоритм  работы  Термоядерной  электростанции.  Теперь   ТЭС   не\n    использует ресурсы со склада,  а  оперирует  только  балансом  производства\n    дейтерия. Т.е. ТЭС работает только при положительном  балансе  производства\n    дейтерия И генерации энергии одновременно. Это сделано  для  того,  что  бы\n    оставленная \"без присмотра\" ТЭС с отрицательным  балансом  по  дейтерию  не\n    выжрала весь ресурс со склада\n    Как следствие - ТЭС не отключается при положительном  балансе  производства\n    дейтерия и энергии, даже если количество дейтерия на планете равно  0.  Это\n    упростит своз ресурсов с планет, на которых энергия генерируется только  на\n    ТЭС\n    Теперь при эффективности добычи ресурсов менее 100%  вместе  с  актуальными\n    значениями добычи в ячейку добавляется рассчетное значение добычи в круглых\n    скобках. Это упростит балансировку производсва при недостатке ресурсов\n    Убрана задержка в обновлении информации о производстве ресурсов\n[!] Локальное (клиентское) и серверное время\n    Изменена процедура замера разницы между  локальным  и  серверным  временем.\n    Теперь она производится не каждый раз при обращении к серверу, а один раз и\n    сохраняется в БД. При заметном изменении разницы  можно  заново  произвести\n    эту  операцию,  установив  галочку  \"Замерить   разницу   между   локальным\n    (клиентским) и серверным временем\"  на  странице  настроек  пользователя  и\n    сохранив настройки. Замер будет произведен  при  следующем  открытии  любой\n    страницы игры\n    Теперь вместо локального или серверного времени одновременно показывается и\n    локальное, и серверное время в следующих местах:\n      1. В навбаре - часы реального времени\n      2. При отправке флота на экране выбора точки назначения - в графе времени\n      прибытия и возвращения флота\n      3. При отправке флота на экране подтверждения отправки - в графе  времени\n      прибытия и возвращения флота\n    Теперь  вместо  серверного  времени  показывается  локальное  в   следующих\n    местах:\n      1. В событиях навбара (флоты и экспедиции)\n      2. В новостях\n      3. На экране флотов в полете\n      4. На экране обзора планеты в списке летящих флотов\n      5. В чате и истории чата\n      6. В боевых отчетах\n      7. В сообщениях\n    Переформатирован навбар для добавления локального и серверного времени\n    Повышена устойчивость механизма к ошибкам на стороне клиента:  неправильный\n    часовой пояс, неправильные настройки DST  в  операционной  системе,  сильно\n    отстающие/спешащие часы итд\n[!] ТМ/Платежи\n    Понижена в 2,5 раза цена ТМ. Теперь за 1 гривну можно купить 2500 ТМ\n    Размер лота (шага покупки) установлен в 2500 ТМ\n    Изменена система бонусов за оптовые покупки ТМ:\n      от 50.000 ТМ - бонус 2% к количеству ТМ\n      от 100.000 ТМ - бонус 4% к количеству ТМ\n      от 200.000 ТМ - бонус 7% к количеству ТМ\n      от 250.000 ТМ - бонус 11% к количеству ТМ\n      от 375.000 ТМ - бонус 15% к количеству ТМ\n      от 500.000 ТМ - бонус 22% к количеству ТМ\n      от 750.000 ТМ - бонус 33% к количеству ТМ\n      от 1.000.000 ТМ - бонус 44% к количеству ТМ\n      от 1.250.000 ТМ - бонус 55% к количеству ТМ\n    Список доступных цен и список  скидок  строится  теперь  по  данным  модуля\n    sn_payment\n    Добавлена поддержка модулей с более чем одним количеством шагов при покупке\n    Добавлена поддержка мультивалютности\n    Добавлена поддержка SUCCESS_URL в платежных системах\n    Добавлена индикация внутренних курсов системы\n    Теперь большую часть информационных элементов на странице можно свернуть\n[!] Настройки пользователя/Смена имени пользователя\n    Добавлена  возможность  изменения  имени  пользователя  за  ТМ.   Стоимость\n    изменения - 100.000 ТМ\n    Игра сохраняет историю изменения имени пользователя. Только бывший владелец\n    может при желании вернуть себе старое имя - опять же за ТМ\n    Поиск по имени так же производится по старым именам. В случае, если  старое\n    имя  пользователя  соответствует  критериям  поиска,  в  результаты   будет\n    добавлена  еще  одна  строка,  в  которой   будет   указано   текущее   имя\n    пользователя, а после него в скобках  и  выделенное  цветом  -  старое  имя\n    пользователя. Никто не спрячется от своей истории!\n    Максимальная длина имени пользователя уменьшена до 32 символов\n    Переменная настроек сервера 'game_user_changename' отвечает за  возможность\n    смены имени пользователя самим пользователем:\n      0 - смена имени запрещена\n      1 - смена имени разрешена и свободна\n      2 - смена имени разрешена, но стоит ТМ. Стоимость смены имени  указана  в\n      переменной 'game_user_changename_cost' (100.000 ТМ по умолчанию)\n    По умолчанию включена смена пользователем своего имени за ТМ\n[!] Исследования\n    Изменен алгоритм рассчета эффективного уровня  лаборатории  и  необходимого\n    времени исследования при настройке сервера \"Строить  лабораторию  во  время\n    исследования: Нет\"\n    Теперь при  идущем  исследовании  блокируется  постройка/уничтожение  нано-\n    и/или лабораторий на все планетах\n    Теперь  блокируется  попытка  начать  исследование  на  планете,  где  идет\n    постройка/уничтожение нано- и/или лабораторий\n    Однако возможно начать исследование на другой планете. В таком исследовании\n    не будут участвовать все планеты где происходить  модификация  нано-  и/или\n    лабораторий. При этом по окончании постройки/уничтожения время исследования\n    не пересчитывается\n[!] Обновление\n    Теперь на время обновления сервер отключается\n    Теперь обновления можно запустить только из админ-консоли\n    В сообщение об обновлении сервера добавлена ссылка для Команды Сервера\n    Множество ускорений в процедуре обновления\n[!] Локализация/Узбекский\n    Добавлен перевод на узбекский от Акмалжона Мусаева\n[!] Очередь\n    Обновленная система очереди\n\n\n\n[+] Навбар\n    Теперь в событиях навбара (флоты и экпедиции) показывается тип  объекта,  к\n    которому относится событие (планета или луна)\n[+] Обзор Империи\n    Добавлена возможность управления  производством  шахт  со  страницы  Обзора\n    Империи\n    Дроп-дауны  в   колонке   \"ИТОГО\"   выставляют   соответствующие   проценты\n    производства для зданий соответствующего типа сразу на всех планетах\n    Кнопки  \"Сохранить\"  продублированы  в  заголовке  каждого  типа  юнитов  и\n    действуют сразу на всю страницу\n[+] Симулятор\n    Добвлена поддержка Фортификатора для защищающегося флота\n[+] Админка/Список игроков\n    Добавлены  две  колонки  со  сведениями  о  реферралах  игрока:  количестве\n    привлеченных игроков и количество заработанных ими ТМ\n[+] Чат\n    Добавлены смайлики\n    Произведена замена цветов для лучшей читаемости сообщений: red  ->  maroon,\n    blue -> cyan\n[+] Постройки\n    Теперь на луне можно строить Нанофабрику\n\n[-] Шпионаж\n    Временно отключен вывод технологий при  шпионаже  -  до  переделки  системы\n    шпионажа\n[-] Админка\n    Временно ограничен доступ к некоторым  админским  страницам  Модераторам  и\n    Операторам - до переделки системы доступа\n\n[~] Обзор Империи\n    Теперь не показываются \"пустые\" строчки для юнитов, которых нет в Империи\n    Для производства ресурсов и складов используется структура 'caps' планеты\n    Правильно считается общее количество полей на всех объектах Империи\n[~] Артефакты\n    Теперь  после  операций  по   покупке/применению   Артефакта   страница   с\n    соответствующим списком открывается на последнем Артефакте\n[~] Сообщения\n    Теперь если есть URL перехода после сообщения есть возможность  перейти  на\n    соответствующую  страницу  по  ссылке  \"Продолжить\"  под   сообщением,   не\n    дожидаясь таймаута\n[~] Рекорды\n    В несколько раз ускорена страница Рекордов\n[~] Скины\n    Заменена картинка \"Черетеж ТОП\"\n\n[%] Админка/Список пользователей\n    Убрана отладка\n[%] Навбар\n    Исправлено смещение надписи в индикаторе исследования влево\n[%] Настройки пользователя\n    Названия групп настроек отцентрированы\n[%] Админка/Обслуживание\n    Исправлена ошибка удаления покинутых планет\n[%] Сообщение\n    Исправлена ошибка отправки сообщения об окончании строительства  на  верфи.\n    Теперь сообщение отправляется один раз, а не каждый  раз,  когда  на  верфи\n    строится юнит\n[%] Меню\n    Исправлена смена названия пункта меню \"Настройки\" на \"Опции\" при заходе  на\n    страницу Альянса\n[%] Экспедиции\n    Исправлена редкая ошибка при которой можно было отправить экспедиций больше\n    максимального количества\n[%] Вселенная\n    Исправлен показ места в статистике и показ кнопки-ссылки на статистику  для\n    скрываемых из статистики пользователей (по умолчанию к таким  пользователям\n    относится команда сервера)\n[%] Боевой отчет\n    Исправлена ошибка открытия неправильной системы во Вселенной при  клике  на\n    координаты в отчете\n[%] Локализация/Английский\n    Исправлены сообщения боевого отчета\n[%] Локализация/Русский\n    Исправлены некоторые очепятки\n[%] Чёрный Рынок\n    Исправлена невозможность продать/купить ТОП на ЧР\n[%] Флоты\n    Исправлена ошибка при приглашении в САБ самого себя\n    Исправлено ошибочное сообщение \"неисследованное пространство\"  в  заголовке\n    страницы\n    Исправлена ошибка отправки флота дальше, чем позволяет запас топлива\n\n[@] Код\n    Расчеты уровня премиума вынесены в модуль\n    Изменены некоторые SQL-запросы\n    Добавлен простенький бенчмарк\n    infos.php  теперь  использует  прямое  обращение  к  production   юнита   и\n    подмассиву modifiers\n    eco_bld_structures.php теперь использует обращение к подмассиву modifiers\n    mercenaries и plans перенесены из таблицы powerup в таблицу unit\n    Константа MAX_OVERFLOW исключена из кода\n    Обработан eco_get_planet_caps и связанные процедуры\n    Добавлена функция вычисления случайного числа, распределенного нормально\n[@] Код/БД\n    Артефакты перенесены из таблицы игроков в таблицу юнитов\n    Удалены лишние поля Технологий из таблицы игрока\n    Добавлены констраинты в некоторые таблицы\n    Удалена колонка `que` из таблицы `users`\n    premium перенесен из таблицы powerup в таблицу unit\n    Исследования и очередь исследований перенесены в соответствующие таблицы\n[@] Код/JS\n    Переписаны некоторые процедуры fleet.js на использование jQuery\n[@] MVC\n    $sn_i18n['pages'] -> $sn_mvc['i18n']\n[@] Обслуживание\n    Процедура обслуживания теперь так же удаляет боевые отчеты  UBE  старше  60\n    дней\n[@] Модули\n    Изменен алгоритм слияния массивов переменных в модулях\n\n\n\nProject \"SuperNova.WS\" Release 36 \"UBEv4 captains chat Happy New Year 2013!!!\"\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n[#] Модуль \"Капитаны\"\n    Требуется СН не ниже 36a0.24\n    Капитан - это опытный командующий, который летает с флотами и за счет более\n    тонкого  управления  флотами  улучшает  эффективные   характеристики   всех\n    кораблей\n    Найм и управление Капитанами осуществляется  через  пункт  меню  \"Капитаны\"\n    (сразу под \"Наемниками\")\n    Каждый Капитан привязан к определенной планете или луне. Нельзя иметь  двух\n    Капитанов на одном небесном теле. Капитан,  летящий  с  флотом,  все  равно\n    считается привязанным к планете\n    Капитана   можно   перевозить   с   одной   планеты   на   другую   миссией\n    \"Передислокация\". При этом на время перелёта Капитан считается  привязанным\n    сразу к обоим планетам - стартовой и финишной\n    С флотом можно отправить только одного Капитана\n    При  гибели  флота  Капитан  так   же   погибает.   Под   \"гибелью   флота\"\n    подразумевается  уничтожение  всех  кораблей  флота.  Это  верно  как   для\n    атакующих флотов, так и для флотов, стоящих в удержании\n    Капитан на  планете  не  участвует  в  защите  планеты  при  атаке  -  этим\n    занимается Фортификатор. Зато при  полном  уничтожении  всего  планетарного\n    флота такой Капитан не погибнет\n    За каждый выигранный простой бой (САБы и миссия \"Уничтожить\" не  считаются)\n    Капитан  атакующего  флота  получает  1  пункт  опыта.  За   \"победы\"   над\n    неактивными игроками опыт не начисляется. Так же не начисляется опыт,  если\n    бой закончился выигрышем атакующего за 1 раунд\n    Капитаны всегда улучшают характеристики кораблей своего флота -  даже  если\n    участвуют в бою, за который они  не  получат  опыта:  атака  на  неактивных\n    игроков, удержание, САБ, уничтожение луны и т.д.\n    При наборе определенного количества опыта Капитан получает  новый  уровень.\n    Чем выше уровень - тем больше опыта нужно для получения следующего уровня\n    Повышение в уровне  дает  возможность  улучшать  умения  Капитанов.  Каждый\n    уровень умений дает 1% к базовому значению соответствующей характеристики\n    Умения Капитана включают бонусы к щитам, броне и атаке\n    Уровни Капитана вкладываются в умения один раз и навсегда - поэтому заранее\n    тщательно планируйте развитие своего Капитана\n    Уровни Капитанов указываются в списке юнитов на Обзоре Империи.  На  заднем\n    фоне ячейки с уровнем выводится прогресс-бар развития Капитана  с  цветовым\n    кодированием:\n      Пустая ячейка - Капитан не нанят, либо только что получил уровень\n      Красный прогресс-бар - до следующего уровня осталось больше 50% опыта\n      Оранжевый - не меньше 50% опыта, но меньше 80%\n      Желтый - не меньше 80% опыта\n      Зеленый - в следующем бою Капитан получит новый уровень\n    В списке флотов на странице \"Флоты в полёте\" и для своих флотов на странице\n    \"Обзор планеты\" если во флоте есть Капитан перед  количеством  кораблей  во\n    флоте высвечивается \"*\", а в попапе состава показывается его уровень\n[#] Модуль \"Продвинутый чат\"\n    Требуется СН не ниже 36a1.7\n    Встроенная система команд с поддержкой алиасов команд\n    Встроенная система помощи по командам чата - команда /help\n    Добавлен список  игроков  в  чате  с  дополнительными  иконками  статуса  и\n    командами управления для админов\n    Возможность игрокам управлять своим состоянием видимости в чате  -  команда\n    /invisible. Администрация сервера (authlevel > 0) всегда видит невидимок\n    Возможность  отправлять  приватные  сообщения  другим  игрокам  -   команда\n    /whisper. Приватные сообщения выделяются специальным образом, видны во всех\n    каналах  и  сохраняются  в  истории  чата.  В  приватных  сообщенях  нельзя\n    употрблять форматирование цветом\n    Администраторы  имеют  возможность  запретить  игроку  писать  в   чат   на\n    определенный срок или вернуть такую возможность -  соответственно,  команды\n    /mute и /unmute. Запрет распространяется на все  каналы  и  на  возможность\n    писать личные сообщения. Соответствующая иконка в списке игроков лишает его\n    права голоса на 1 час\n    Администраторы имеют возможность блокировать и разблокировать игроков прямо\n    из чата - соответственно, команды /mute и /unmute. Иконка в списке  игроков\n    банит его на 1 неделю\n    Максимальное время нахождения игрока в списке онлайн совпадает с  таймаутом\n    чата на странице сервера - т.е. в  списке  онлайн  игрок  будет  виден  еще\n    некоторое время после выхода из чата\n\n[!] UBEv4\n    Написан с нуля боевой движок и боевые отчеты\n    Особенности подготовки к бою:\n    1. Бой теперь считается не по $time_now, а по времени прилета  флота  -  на\n    случай, если бой сильно отложенный. Например, при сбоях движка  или  низкой\n    активности  сервера.  Так  будут  отработаны  корректно  все  удержания   в\n    правильное время\n    Особенности хода боя:\n    1. Броня не регенерируется между раундами\n    2. Если броня упала  ниже  75%  -  корабль  имеет  шанс  взорваться  равный\n    проценту от общего здоровья\n    3. Новый механизм боя: подлов атакующего или sneak defense. Если в САБе и в\n    удержании участвуют флоты одного и того же  игрока,  то  прилетающие  флоты\n    этого игрока будут сражаться на стороне защитника. Аккуратно смотрите, кого\n    приглашает в САБ. Хе-хе\n    Особенности подведения итогов боя:\n    1. Если в бою участвует хотя бы один флот Админов с любой стороны - лом  не\n    выпадает ни с кого!\n    2. Возвращение обломков с оборонных сооружений не производится\n    3. В миссии  \"Уничтожить\"  шанс  уничтожения  флота  от  взрыва  одного  из\n    кораблей при попытке уничтожить луну теперь так же  зависит  от  количества\n    гравидвигателей во флоте - чем их больше, тем шанс выше\n    4. В миссии \"Уничтожить\" корабли могут взорваться даже в  случае  успешного\n    уничтожения  луны.  Как  и  раньше,  подрыв  кораблей   с   гравидвигателем\n    уничтожает весь флот\n    5. Теперь в рейдовый опыт засчитываются исключительно одиночные  атаки.  Ни\n    \"Удержание\", ни \"САБ\" не засчитывается. Т.е. вообще не засчитываются -  вне\n    зависимости от результата боя\n    6. Теперь атаки на неактивных игроков (\"i-шки\") не приносят рейдовый опыт\n    7. Количество свободных полей на луне зависит от её размера и  определяется\n    по формуле Размер/1000 с округлением вверх до целого\n    8. Изменен расчет поля обломков. Теперь на орбите оказывается от 30% до 70%\n    выброшенных за  борт  ресурсов  и  от  20%  до  40%  обломков  кораблей.  В\n    детерминированном симуляторе процент обломков на орбите всегда равен 30%, а\n    обломки, выброшенные из трюма всегда составляют 50% от потерь\n    9. Шанс уничтожения луны теперь всегда лежит в пределах 1%-99%\n    Боевой отчет теперь состоит из трёх частей: \"Основная  информация  о  бое\",\n    \"Боевые потери\" и лог раундов\n    \"Основная информация о бое\" показывает:\n    1. Время проведения боя (если доступно)\n    2. Место боя (если доступно) - координаты планеты, её тип и имя\n    3. Результат боя (выигрыш атакующего, ничья, проигрыш атакующего)\n    4. Обломки на орбите\n    5. Шанс образования луны и результат такой попытки\n    6. (Для миссии  \"Уничтожить\")  Состояние  кораблей  с  гравидвигателями  по\n    итогам боя. Шанс уничтожения луны оставшимися кораблями и  результат  такой\n    попытки. Шанс взрыва кораблей и итог миссии\n    Раздел \"Боевые потери\" показывает:\n    1. (На планетах) Количество восстановленных боевых сооружений\n    2. Общие потери боевых единиц каждого из участвующих в бою игроков. Если  у\n    одного игрока участвовало в бою несколько флотов - будут показаны суммарные\n    потери по всем флотам. Это верно для всех параметров в  этом  разделе.  Для\n    планетарной обороны в потери не включаются восстановленные единицы\n    3. (В случае победы атакующих) Количество ресурсов, вывезенных  с  планеты.\n    Для  планеты  это  будет  положительное  число,  для  атакующих  флотов   -\n    отрицательное\n    4. (Для флотов) Количество ресурсов  потерянных  из-за  уменьшения  емкости\n    трюмов вследствии уничтожения части флота. Эти ресурсы рассматриваются  как\n    \"боевые потери\" - они  плюсуются  к  обломкам  на  орбите  и  к  потерям  в\n    пересчете на ресурсы\n    5. Общие потери в пересчете на ресурсы. Включает стоимость боевых единиц на\n    момент боя, вывоз с планеты и ресурсы, потерянные из-за уменьшения трюмов\n    6. Общие потери в ресурсах в пересчете на металл по курсу Черного Рынка  на\n    момент проведения боя. Писькомерка для сравнения\n    \"Лог раундов\" показывает результаты расчета каждого раунда для всех флотов\n    1.  Показывает  координаты  и  тип  планеты,  с  которой  прилетели   флоты\n    атакующих/защитников\n    2.  Расширено  количество  информации  о  боевых  подраздеениях   Добавлена\n    информация о \"Пробое\" и \"Уроне\". \"Пробой\" - атака, которая пришлась на щиты\n    и была ими поглощена (или пропущена - см. ниже). \"Урон\"  -  атака,  которая\n    пришлась на броню\n    3. Цветовое кодирование информации о подразделениях:\n       Зеленый означает, что вся атака в раунде поглощена щитами\n       Желтый - часть атаки пробила щиты (\"пробой\") и нанесла урон по броне, но\n       при этом ни одна боевая единица не уничтожена\n       Оранжевый - один или более боевых единиц уничтожено\n       Красный - все оставшиеся боевые единицы уничтожены в этом раунде\n       Число  в  скобках  в  столбце  потерь  -   количество   боевых   единиц,\n       взорвавшихся в раунде из-за фатальных повреждений\n    В боевом отчете координаты планет являются ссылками на Вселенную\n    Доработан симулятор для поддержки изменений в UBEv4:\n    1. Стандартный режим работы симулятора - полная определенность  результатов\n    в зависимости от начальной конфигурации (галочка \"Симуляция\" включена)\n    2. Добавлен второй режим работы  -  недетрминированный  симулятор  (галочка\n    \"Симуляция\" отключена). В этом режиме работы  проводится  полная  симуляция\n    боя (включая образование луны) с применением генератора случайных  чисел  -\n    т.е. так, как происходил бы обычный бой. В  этом  режиме  результаты  могут\n    сильно отличаться от симуляции к симуляции. Так же в этом режиме происходит\n    запись боевого отчета с результатом симуляции в БД\n    3. В  стандартном  режиме  если  шанс  образования  луны  больше  1  всегда\n    образуется луна со средним размером для данного шанса\n\n[+] Меню\n    Редизайн меню\n    Изменен порядок расположения пунктов\n    Высота пункта меню увеличена до 16 пикселов\n    Добавлены иконки. Размер иконки ограничен 14 пикселами в высоту\n\n[~] Чат\n    Добавился новый BBCode \"s\" - зачёркнутый текст\n    В чате Альянса в нике участника теперь не указывается Альянс\n    Переформатирован вывод списка смайлов. Список смайликов теперь генерируется\n    автоматически из всего доступного списка\n    При открытии окна чата курсор позиционируется в строку набора сообщения\n    Реформатирование HTML-кода страницы чата\n    Переделан в preMVC\n    Три файла чата интегрированы в один\n[~] Новости\n    Количество новостей ограничено 20-ю самыми свежими\n    На странице Обзора планеты добавлена подсказка как закрыть окно со  свежими\n    новостями\n[~] Флоты\n    Изменены ограничения на отправку Шпионов. Их можно посылать  в  одиночку  в\n    миссии Шпионаж, Передислокация и Транспорт. Во все остальные миссии Шпионов\n    тоже можно отсылать - но только в сопровождении других кораблей\n[~] Регистрация: Пароль теперь указывается в отдельной строке\n[~] Восстановление пароля: Новый пароль теперь указывается в отдельной строке\n[~] Админка/Бан:  Теперь  срок  бана  инкрементальный  -  каждый   новый    бан\n    увеличивает срок блокировки, а не заменяет его\n[~] Админка/Список игроков: Для забаненных игроков при наведении на ячейку бана\n    выскакивает тултип с ником забанившего, датой и причиной бана\n\n[%] Альянсы\n    Исправлена надпись при отправке письма членам Альянса\n[%] Обзор Империи\n    Исправлена пропажа индикации количества строящихся зданий\n    Исправлена ошибка смещения фона для производящих зданий\n[%] Флоты/Фаланга\n    Исправлена ошибка сканирования пустого места во Вселенной\n[%] Навбар\n    Изменена разметка навбара, что бы его не перекашивало в случае вывода блока\n    информации до него\n    Исправлена ошибка неправильного цветового кодирования  остатков  энергии  в\n    планетбаре\n    Исправлена ошибка смены планеты на preMVC-страницах\n[%] Исследования\n    Добавлен патч, исправляющий багоюз на медленных MySQL серверах\n[%] Чёрный Рынок\n    Исправлена несовместимость с Opera 12.x\n    Исправлена уязвимость в Скупщике лома\n[%] Наемники: Исправлена ссылка на дерево развития\n[%] Чат: Исправлена ошибка появленя всех сообщений пользователя  в  чате  после\n    его удаления\n[%] Отпуск: Исправлено сообщение об ошибке на загрушке отпуска\n[%] Админка/Обслуживание: Исправлена ошибка обслуживания базы\n\n[@] Модули\n    Устранена ошибка на случай, если модуль не возвращает темплейта. Такого  не\n    должно происходить в нормальной ситуации\n[@] Код\n    Переформатирован HTML-код обзора планеты\n    Поддержка опций движка в глобальном объекте $supernova\n    Поддержка опций на страницах в подмассиве 'pages'\n[@] БД\n    В таблицу флотов добавлены идентификаторы планет старта/назначения\n[@] Документация\n    Обновлен конвертер txt2html\n\n\n\nProject \"SuperNova.WS\" Release 35 \"MVC race reparse teleportation recycle\"\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n[#] Модули: Расы\n    Шесть фиксированных рас: земляне, луниты, меркурианцы, венериане, марсиане,\n    республиканцы\n    Иконка расы отображается в чате,  в  статистике,  в  попапе  информации  об\n    игроке во Вселенной и на странице Императора. Удержание курсора над иконкой\n    расы вызывает тултип с её названием. Клик - открывает страницу с  описанием\n    всех рас\n    Каждая раса имеет собственные бонусы. Бонусы рас действуют сразу  же  после\n    выбора родного мира - не нужно, например, исследовать техи, что бы получить\n    к ним бонус\n    Раса выбирается после регистрации на странице Императора\n    Первый выбор расы производится бесплатно, каждая смена расы  стоит  100.000\n    ТМ\n    Описание текущей расы доступно на странице Императора. Там же  есть  ссылка\n    на описание всех рас в игре с указанием их символов\n[#] Модули: Расовые юниты - требуется наличие модуля \"Расы\"\n    Шесть уникальных юнитов - по одной каждой из рас:\n    Земная \"Лень\" - боевой солнечный спутник\n    Лунная \"Зависть\" - легкий бомбардировщик\n    Меркурианское \"Обжорство\" - емкий переработчик\n    Венерианский \"Гнев\" - истребитель-перехватчик\n    Марсианская \"Гордыня\" - усовершенствованный линейный крейсер\n    Республиканская \"Жадность\" - боевой транспорт\n\n[!] Телепортация планеты - новая возможность, доступна на  странице  управления\n    планетой\n    Телепортация может производится только на свободное место - там, где нет ни\n    планет, ни лун, ни обломков, включая уничтоженные объекты\n    Телепортация перемещает  в  новые  координаты  планету  вместе  с  флотами,\n    находящимися на орбите планеты\n    Если у планеты есть луна - она  так  же  перемещается  в  новые  координаты\n    вместе с флотами\n    Телепортация  невозможна,  если  в  окрестностях  планеты   есть   какая-то\n    активность флотов (т.е. есть флоты, имеющие в  качестве  точки  отправления\n    или назначения саму планету, луну или поле обломков)\n    После телепортации  необходимо  выждать  некоторое  время  перед  следующей\n    телепортацией -  нарушенная  метрика  пространства  вокруг  планеты  должна\n    нормализироваться\n    Стоимость телепортации и таймаут перед следующим прыжком задаются в таблице\n    `config` соответственно переменными 'planet_teleport_cost' (по умолчанию  -\n    50.000 ТМ) и 'planet_teleport_timeout' (по умолчанию - 1 сутки)\n[!] Перенос столицы  -  новая  возможность,  доступна  на  странице  управления\n    планетой\n    Теперь любая планета может быть назначена столицей\n    Стоимость переноса столицы по умолчанию составляет 25.000 ТМ. Она  задается\n    в таблице `config` переменной 'planet_capital_cost'\n[!] МПР\n    Изменение алгоритма ракетного удара\n    Алгоритм  ракетного  удара  теперь  не  привязан  к   численным   значениям\n    идентификаторов юнитов и сильно оптимизирован по скорости\n    Теперь при атаке МПР учитываются  щиты  оборонных  сооружений.  Это  должно\n    слегка  уменьшить  эффективность  ракет  и  повысить   живучесть   защитных\n    сооружений с большим количеством щитов\n    Теперь при ракетном ударе рандомизируются параметры атаки, брони и щитов  у\n    соответствующих юнитов. Границы такие же, как и для сражений  флотов  -  от\n    80% до 120%\n    В результате изменений в алгоритме существенно повысилась живучесть ПЗ  при\n    ракетном ударе\n    Добавлена поддержка усиления залпа для МПР\n[!] Переработка\n    Полностью переделана работа с полем обломков\n    Полностью переписан алгоритм запуска переработчиков\n    В  попапе  вместе  с  абсолютными  теперь  показываются   и   относительные\n    значениями в процентах\n    В попапе добавилось три строки:\n    1. Строка \"В полете\" показывает емкость трюмов переработчиков пользователя,\n    которые уже летят на данное поле\n    2. Строка \"На орбите\" показывает емкость переработчиков на  орбите  текущей\n    планеты или луны\n    3. Строка \"К переработке\" показывает сумму двух предыдущих строк\n    На  основном  экране  Вселенной  к  иконке  обломков  добавлена   индикация\n    процентного значения из строки \"В полете\". Она имеет цветовое кодирование:\n    1.  Зеленый  цвет  означает,  что  прибывающие   флоты   игрока   полностью\n    переработают поле обломков на ресурсы\n    2.  Желтый  цвет  означает,  что  к   полю   летит   некоторое   количество\n    переработчиков, которых не хватит что бы целиком переработать  обломки,  но\n    на  текущей  планете  есть  достаточно  переработчиков,  что  бы  полностью\n    обработать поле\n    3. Оранжевый означает, что к полю летит флот иргока с  переработчиками,  но\n    их не хватит на полную обработку обломков, даже  включая  те  корабли,  что\n    находятся на орбите\n    4.  Красный  цвет  значит,  что  к  полю  обломков  не  летит   ни   одного\n    переработчика игрока\n[!] Статистика\n    Теперь можно управлять появлением игроков  в  статистике  и  рекордах.  Для\n    этого на странице настроек сервера появились дополнительные настройки.  Они\n    размещаются в разделе \"Статистика и рекорды\"\n    Отключение настройки \"Прятать админов\" добавит в статистику и рекорды  всех\n    пользователей с  authlevel > 0. По умолчанию она включена\n    Настройка \"Прятать игроков\" позволяет указать  через  запятую  перечень  ID\n    игроков, которые не будут участвовать в статистике и  рекордах.  Это  может\n    быть полезно для создания NPC - ботов или  игроков,  которые  исполняют  их\n    роли\n    Так  же  в  этот  раздел  вынесена  настройка  расписания   автоматического\n    обновления статистики. ВНИМАНИЕ!!! КРАЙНЕ НЕ РЕКОМЕНДУЕТСЯ МЕНЯТЬ  ЗНАЧЕНИЕ\n    ПО УМОЛЧАНИЮ!!!\n    Добавлена опция \"Скрывать  ссылки  на  ЛС\".  При  её  включении  в  таблице\n    статистики не показывается URL на создание личного сообщения игрокам\n    Теперь переход по определенной позиции  (например  со  страницы  Вселенной)\n    скроллирует страницу сразу на эту позицию\n    Немного уменьшен размер страницы статистики\n[!] MVC\n    Базовая поддержка MVC - встроенная система моделей и видов\n    Все страницы, переделанные под MVC, перемещены в /includes/pages\n[!] Рендерер имен\n    Добавлен механизм рендеринга имени пользователя\n    Чат, статистика, Вселенная и страница Императора  теперь  используют  общий\n    механизм рендеринга имени пользователя\n[!] Чат\n    Переписан чат\n    Чат теперь инкрементальный - с сервера передается не всё содержимое чата, а\n    только новые сообщения. Чат  корректно  работает  когда  у  игрока  открыто\n    несколько окон с чатом\n    Исправлена проблема со скроллированием чата в Chrome v20+\n    Теперь при отключении чата по таймауту содержимое окна не  стирается,  а  в\n    него добавляется соответствующее сообщение. Так же прячутся элементы ввода:\n    выбор цветов, строка сообщения, кнопка \"Отправить\" и панель смайлов\n    Основное окно чата переписано под preMVC\n    Новый код чата (как JS, так и PHP) заметно компактнее, аккуратнее и быстрее\n    старого\n    Увеличена длина поля для ника в чате\n[!] Дерево технологий\n    Полностью переписано дерево технологий (бывш. techtree.php)\n    Рядом с названиями юнитов там, где это имеет смысл, отображаются их  уровни\n    в Империи/на текущей планете\n    Теперь вместо полного уровня с учетом бонусов отображаются отдельно базовые\n    уровни и отдельно бонус к ним\n    Добавлена  поддержка  дополнительных  требований  к  строительству   юнитов\n    (например - модуля расовых юнитов)\n[!] Заметки\n    С нуля написаны заметки. Что еще сказать?\n[!] Друзья\n    Страница друзей написана с нуля\n    Теперь подробно сообщается  обо  всех  ошибках  и  результатах  операций  с\n    заявками\n    В личную почту отправляются сообщения по  приходу,  принятию  и  отверганию\n    заявки, а так же при разрыве дружеских отношений\n    Цветовое кодирование статуса друга: зеленый - онлайн, желтый -  бездействие\n    от 5 до 15 минут, оранжевый - оффлайн, красный - оффлайн более суток\n[!] Модули\n    Поддержка ali_ally_player 12a0\n    Поддержка player_premium 1b0\n[!] Новапедия\n    Полностью написана с нуля страница информации о юнитах\n    Теперь  в  Новапедии  показываются  требования  для  постройки/исследования\n    юнита\n    Теперь для корабля показываются данные для всех типов  двигателей,  которые\n    возможно на него установить\n    Улучшено отображение информации о кораблях и обороне\n[!] Поиск\n    Полностью переписан поиск\n    Добавлена подсказка\n    Добавлена сортировка по Альянсу, имени игрока, имени планеты\n    Настройка сервера \"Скрывать ссылки на  ЛС\"  теперь  распространяется  и  на\n    результаты поиска\n[!] Документация\n    Вся документация сконвертирована в UTF-8\n\n[+] Меню\n    Добавлен пункт меню \"Тёмная материя\"\n    Добавлена возможность добавления иконки к пункту меню.  Иконки  берутся  из\n    подкаталога 'icons' текущего скина\n    Добавлена прямая поддержка CSS-стилей для элементов меню\n    Под логотип сервера в ALT вместо 'supernova.ws' подкладывается имя сервера\n[+] Обзор Империи\n    Значительно оптимизирован HTML-код\n    Размер HTML-кода уменьшен на величину от 30% и в отдельных случаях до  80%.\n    Среднему игроку оптимизация даст уменьшение размера загружаемого  файла  на\n    40-50%% (включает так же выигрышь от оптимизации Списка планет - см.ниже)\n    В колонку \"ИТОГО\" добавлена сумма по строящимся и  прибывающим  на  планеты\n    юнитам\n    Теперь юниты всегда групируются согласно их принадлежности.  Например,  при\n    подключении модуля расовых юнитов они добавляются в категорию \"Флот\", а  не\n    как ранее в конце таблицы\n[+] Список планет\n    Значительно оптимизирован HTML-код\n    В Списке планет убрана иконка, отвечающая за исследование вследствие полной\n    бессмысленности\n[+] Реклама\n    Добавлена возможность управлять мета-тегами 'description' и 'keywords'  без\n    редактирования темплейта! Их содержимое хранится в таблице `config` в полях\n    `adv_seo_meta_description` и `adv_seo_meta_keywords` соответственно\n[+] Модули\n    Система модулей переписана с учетом базовой поддержки MVC\n    Автоматическая загрузка языков\n[+] Планетарные врата\n    Интерфейс переделан по примеру страницы \"Флоты на орбите\"\n[+] Вселенная\n    Шаблоны попапов легенды, планет, лун, обломков, игроков и альянсов вынесены\n    из JS-скрипта в шаблон страницы\n    Убрано количество летящих флотов - эта информация есть в навбаре\n    Полностью переписана работа AJAX-части,  отвечающей  за  отправку  шпионов,\n    переработчиков и ракет\n    Количество переработчиков теперь включает все виды кораблей, которые  могут\n    перерабатывать обломки\n    Интерфейс  запуска  ракет  использует  группу  защитных  сооружений,  а  не\n    хард-кодед перечень, как было раньше\n    В попап легенды добавлены расшифровки для иконок действия\n    В попапе планеты показывается её диаметр\n    В попапе луны миссия  \"Уничтожить\"  показывается  только  если  на  текущей\n    планете игрока есть ЗС\n    Из попапа игрока убраны ссылки - все, что можно было  сделать  по  ссылкам,\n    можно теперь\n    Добавлено новая  иконка  действия  -  \"Статистика\".  Её  тултип  показывает\n    статистику игрока\n    Расширена подсказка\n[+] Смайлы\n    В чат добавлены следующие смайлы: nea, ups, quote, shout, sorry, spiteful\n\n[~] Обзор планеты\n    Переписана процедура отсылки переработчиков\n    Теперь выводится результат отсылки переработчиков\n[~] На странице \"Тёмная материя\" дополнен список возможного использования ТМ  и\n    проставлены ссылки на соответствующие страницы\n[~] HTTPS\n    Теперь СН нормально работает и по HTTPS протоколу\n[~] Император\n    Со страницы убраны баннер и юзербар\n[~] UBEv3\n    Улучшена поддержка залпового огня\n[~] Юниты/Корабли\n    Небольшой ребаланс кораблей\n    СуперНова - атака уменьшена на  порядок.  При  этом  боевая  эффективность\n    корабля изменилось незначительно, благодаря изменению коэфициента  усиления\n    залпа. Немного уменьшилась эффективность против наземной обороны  и  легких\n    кораблей и увеличилась эффективность против средних кораблей\n    Бомбардировщик: понижена эффективность против ионных орудий  и  повышена  -\n    против плазменных\n[~] Исследования\n    Добавлена индикация бонусных уровней (например, от  премиума)  на  страницу\n    исследований\n[~] Наемники\n    Добавлена индикация бонусных уровней (например, от  премиума)  на  страницу\n    наемников\n[~] Стили\n    Цвет бонусов изменен с \"yellow\" на \"gold\" - это даст  возможность  отличать\n    их от, например, прибывающих на планету юнитов\n[~] Локализация\n    К эффектам Технолога и  Фортификатора  добавлена  информация  о  добавлении\n    слотов к очередям\n[~] Флот\n    Полностью локализована страница приглашения в САБ\n[~] Регистрация\n    Изменено сообщение при регистрации игрока\n[~] Тёмная Материя\n    Немного переработан интерфейс страницы\n[~] Админка/Утилиты\n    В шифрование паролей добавлен генератор паролей\n[~] Инициализация\n    Изменен алгоритм определения корневого каталога СН. Теперь движок корректно\n    работает в каталогах-симлинках\n\n[%] Фаланга\n    Теперь нельзя сканировать удаленную планету\n[%] Локализация\n    Исправлено описание фаланги\n    Исправлена ошибка чтения информации о локализации\n    Небольшая правка опечаток в русской локализации\n[%] Альянсы\n    Исправлена ошибка отображения полей информации Альянса при наличии  лого  и\n    отсутствии внешнего текста Альянса\n[%] Вселенная\n    Исправлена опечатка в легенде\n    Исправлена ошибка с перебросом по координатам [1:1:1] при выборе планеты из\n    выпадающего меню\n[%] Обзор планеты\n    Добавлена проверка на уровень губернатора при его отображении - если  вдруг\n    при прямых манипуляциях в базе у планеты есть ИД губернатора,  но  нет  его\n    уровня. В нормальных условиях такого произойти не может\n[%] Флот\n    Исправлена ошибка отправки корабля, если его ID больше 300 или меньше 200\n    Теперь невозможно пригласить в САБ игрока, на которого летит этот САБ\n[%] UBEv3\n    Исправлена ошибка с определением кораблей с  ID  >  300  как  защиты  и  их\n    восстановлением. Ошибка проявляется только в сторонних модулях\n[%] Альянсы\n    Исправлена проблема с отсылкой сообщений всему Альянсу\n[%] Корабли\n    Исправлено нулевое потребление некоторых юнитов (в частности  -  шпионского\n    зонда)\n\n[@] Темплейты\n    Рендерер страницы теперь подхватывает заголовок страницы, если  он  есть  -\n    переменная PTL {PAGE_HEADER}\n    Содержимое переменной $template_result автоматически загружаетя в  темплейт\n    в файле index.php\n    Файл темплейта _result_message автоматически  подгружается  при  рендеринге\n    темплейта, если в структуре переменных темплейта есть массив 'result'\n[@] Модули\n    Изменена процедура инициализации  -  модули  теперь  грузятся  до  проверки\n    наличия  страниц.  Это  сделано  на  случай,  если  модуль  добавляет  свои\n    собственные страницы как, например, модуль Премиума и модуль Рас\n    Теперь можно указывать  в  качестве  страницы  загрузки  файла  локализации\n    пустое множество '' - файлы в этом массиве будут загружаться всегда\n    Поддержка  дерева  зависимости  модулей  -  теперь  можно  делать   модули,\n    зависящие от других модулей\n    Автоматическая загрузка зависимых модулей в правильном порядке\n[@] Код\n    Из файла vars.php выделены три  отдельных  файла  со  структурами,  боевыми\n    юнитами и всеми остальными\n    Так же добавлена дополнительная служебная информация для  того,  что  бы  в\n    симуляторе не пропадали защитные сооружения  при  добавлении  новых  юнитов\n    через модули\n    Убраны неиспользуемые данные \"скорострела\"\n    Из информации о боевых юнитов убраны ненужные данные о единичных усилениях\n    Численные значения для  защитных  сооружений  и  ракет  заменены  везде  на\n    константы\n    Везде  из  текста  убраны  ссылки  на  переменную  $GLOBALS  для  поддержки\n    рефакторинга в IDE\n    Библиотека \"tw-sack.js\" больше не используется - она заменена на jQuery\n    Исправлена очепятка в названии константы технологии ионного двигателя\n    Убран неиспользуемый код \"скорострела\"\n    js_safe_string() теперь корректно работает со строками,  где  есть  перевод\n    строки, включая Линуксовские и Маковские форматы файлов\n    sn_function_call теперь корректно отрабатывает несуществующие функции\n    Добавлена функция sn_get_groups()\n    eco_bld_tech.php теперь не использует $sn_data\n    Оптимизирован код Альянсов\n    Все страницы интерфейса игроков переписаны без использования $parse\n    Теперь в doquery() префикс {{table}} не используется и не обрабатывается\n    Страницы login.php, phalanx.php переписаны без использования $parse\n    JS: В объявлениях скриптов все конструкции  language=\"javascript\"  заменены\n    на type=\"text/javascript\"\n    Теперь  движок  может   работать   с   неограниченным   количеством   типов\n    кораблей-переработчиков\n[@] Меню\n    Заменены типы элементов меню на \"lang\" там, где это было возможно\n    Стандартное меню вынесено из файла template.php в includes/vars_menu.php\n    Парсер меню теперь понимает вложенные конструкции и константы для типа меню\n    'lang' - т.е. конструкции вида 'info[STRUC_MINE_METAL][description]'\n[@] MVC\n    Частичная поддержка структуры MVCv2 в init.php\n    Добавлена поддержка анонимных MVC-страниц в common.php\n    Добавлена поддержка MVC-страниц на страницы логина/регистрации\n\n\n\nProject \"SuperNova.WS\" Release 34 \"Happy Birthday Supernova! 3rd anniversary\"\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n[!] Юбилей: 3 года назад 11 июня 2009 года был запущен первый сервер того,  что\n    превратилось в Проект \"СуперНова\"\n\n[#] Админка: Редактирование характеристик планеты\n    Модуль admin_planet_edit_extra v1c0\n    В  админке  можно  менять  основные   характеристики   планеты:   название,\n    изображение, размер, температуру, губернатора  и  его  уровень,  количество\n    обломков на орбите\n\n[#] Покупка ТМ: Модуль платежной системы XSolla\n    Модуль payment_xsolla_currency v1b0\n    Реализован протокол XSolla \"Виртуальная валюта\" (без отката платежей)\n    Поддержка плагина XSolla PayStation\n\n[#] Премиумный аккаунт\n    Модуль player_premium v1a2\n    Премиумный аккаунт покупается игроком за ТМ\n    Имеется 5 уровней премиума\n    Премиумный аккаунт дает  бонус,  эквивалентный  своему  уровню,  к  уровням\n    технологий и Наемников (не Губернаторов!)  Империи,  а  так  же  к  уровням\n    следующих зданий на планетах: ко всем шахтам и складам  ресурсов,  ко  всем\n    электростанциям, к Фабрике роботов, к Верфи, к Нанофабрике, к Лаборатории и\n    к Нанолаборатории\n    Ник обладателя премиумного аккаунта выделяется в чате отдельным стилем  (по\n    умолчанию - желтым цветом)\n\n[!] Файл \"/docs/release.txt\" приведен в соответствие с актуальной информацией\n[!] Покупка ТМ\n    Поддержка платежных модулей: таблица платежей,  базовые  настройки,  строки\n    локализации и константы\n    Интерфейс покупки ТМ (требуется хотя бы один модуль платежной системы)\n    Поддержка payment_xsolla_currency v1b0\n    Поддержка методов 'LINK', 'GET' и 'POST' в системах платежа\n[!] Админка: Воплощение\n    Теперь можно Воплотиться в любого игрока, посмотреть  игру  его  глазами  и\n    поуправлять игрой его ру... эээ... интерфейсом!\n    Воплощение доступно только Администраторам сервера\n    Воплотиться можно только в игрока меньшего уровня - т.е. нельзя Воплотиться\n    в такого же Администратора\n    Вложенные  Воплощения   недопустимы:   нельзя   Воплотиться,   будучи   уже\n    Воплощенным в кого-то. Сначала Развоплотитесь\n    Для Воплощения используйте соответствующую иконку в \"Списке игроков\"\n    При Воплощении изменяется только  onlinetime  пользователя.  Вся  остальная\n    информация (IP, User-agent итд) сохраняется\n    Для Развоплощения используйте соответствующий пункт меню или \"Выход\"\n    Если на аккаунте игрока есть ошибки,  или  игрок  заблокировае,  или  игрок\n    находится в отпуске, то попытке  Воплощения  будет  выведено  сообщение  об\n    ошибке/блокировке/отпуске,  которое  увидел  бы  игрок  на   вашем   месте.\n    Обновление страницы вернет вас в ваш аккаунт\n    После штатного Развоплощения (т.е. из меню, а не при ошибке и не из  игрока\n    в отпуске) Администратора возвращает на страницу списка игроков\n    ВНИМАНИЕ! Перед использованием Воплощения почистите куки в браузере!  Из-за\n    изменений в работе кукесов кэш браузера может содержать дубликаты куков\n[!] Чертежи\n    \"Чертеж\" - это  программный  пакет,  дающий  доступ  к  производсту  юнитов\n    определенного типа. Доступны следующие чертежи:\n    1. Здания: термоядерная электростанция\n    2. Корабли: супертранспорт, гипертранспорт, Звезда Смерти, \"СуперНова\"\n    3. Защитные постройки: планетарная защита\n    Чертеж  покупается  на  Империю,  после  чего  указанный  юнит  доступен  к\n    производству на всех планетах\n    Чертеж является перманентым\n    Чертежи заменяют Наемников в требованиях к постройке\n    Наемники \"Разрушитель\" и \"Ассасин\" сконвертированы соответственно в \"Чертеж\n    ЗС\" и \"Чертеж СН\". Остальные четрежи надо покупать самостоятельно\n[!] День Рождения: Добавлена подсистема подарков на ДР игрока\n    Игрок может ввести свой ДР на своей странице настроек. ДР вводится один раз\n    и  после  этого  не  может  быть  изменен.  Дата  проходит  валидизацию   в\n    соответствии с серверными настройками формата даты\n    Игрок с ДР на текущую дату будет отмечен специальной иконкой в статистике и\n    на странице Вселенной. При наведении на иконку всплывает подсказка с  датой\n    рождения\n    Амдинистратор сервера может назначить количество  ТМ  в  подарок  на  ДР  в\n    настройках (опция \"Подарок игроку на день  рождения\").  Если  это  значение\n    установлено в 0 - подарки отключены.\n    Выдача подарков происходит один раз в сутки  всем  игрокам,  день  рождения\n    которых находится не далее чем в \"Ретро-рождение\" дней от текущей даты.\n    При этом подарки выдаются только игрокам,  которые  на  момент  выдачи  уже\n    имели введенную дату рождения. Движок гарантированно начислит подарки  даже\n    если  ДР  пришелся  на  день  неактивности   сервера   (неисправность   или\n    обслуживание).\n    Такая система выбрана с одной стороны - что бы не обидеть игроков в  случае\n    проблем с сервером, а с другой стороны - что  бы  избежать  злоупотреблений\n    (например - ввести послезавтрашнюю дату ДР, на следующий день  получить  ТМ\n    за \"прошлый ДР\", а через день -  еще  и  за  \"нынешний\".  Такой  вариант  в\n    текущей системе начисления подарков не прокатит)\n[!] Навбар: Полностью переработан навбар.\n    Вся информация теперь выводится поверх кликабельных иконок  с  всплывающими\n    подсказками\n    Добавлена информация о текущих исследованиях пользователя\n[!] Админка: Список игроков\n    Полностью переписан \"Список игроков\" с использованием PTE\n    Сокращено количество строк локализации\n    Альянсы-игроки больше не выводятся в списке\n    Пишется полный срок бана\n    Для мультиаккаунтов подсвечиваются все адреса с одинаковым IP и  в  скобках\n    добавляется количество игроков с таким же адресом\n    Теперь невозможно удалить  игрока  того  же  уровня  -  для  предотвращения\n    разборок между членами команды одного уровня\n[!] Админка: Параметры MySQL сервера\n    В админку на страницу утилит  добавлен  вывод  информации  о  настройках  и\n    параметрах MySQL сервера\n[!] Админка: Шифрование пароля\n    Полностью переписана утилита шифрования пароля в MD5\n[!] Верфь: Полностью переписана работа верфи\n[!] Из данного файла удалена информация о ранних релизах (до момента разделения\n    чейнджлога на девелоперский и пользовательский, т.е. начиная с релиза 25  и\n    ранее) что бы исключить дублирование информации. Посмотреть эту  информацию\n    можно в девелоперском чейнджлоге /docs/changelog_dev.txt\n[!] Статистика\n    Полностью переписана страница вывода статистики игроков и Альянсов\n    Она теперь использует PTE\n    Полностью переписаны алгоритмы работы страницы\n    Для игроков добавлены отображение следующих  видов  статистики:  \"Проведено\n    боев\", \"Выиграно боев\", \"Проиграно боев\", \"Уровень за постройки\",  \"Уровень\n    за исследования\", \"Уровень  за  рейдерство\".  В  качестве  исходных  данных\n    используется информация из записей игроков (т.е. актуальная  информация  на\n    момент просмотра статистики), поэтому изменение для данных типов статистики\n    всегда будет равно 0\n[!] Покупка секторов на планете\n    Теперь можно за ТМ докупать дополнительные сектора на планете - один сектор\n    за раз, максимальное количество секторов не ограничено\n    Cектор можно купить в нескольких местах: в \"Обзоре планеты\", в  \"Управлении\n    планетой\" и на экране строительства зданий\n    Стоимость сектора для планеты -  геометрическая  прогрессия  с  количеством\n    секторов в качестве номера члена, БС = 1000 и Ф = 1.01\n    Ориентировочная стоимость покупи  1  сектора  на  планете  составляет:  для\n    планеты размером 100 секторов - 2678  ТМ,  150  секторов  -  4404  ТМ,  163\n    сектора - 5013 ТМ, 200 секторов - 7244 ТМ, 250 секторов  -  11913  ТМ,  300\n    секторов - 19493 ТМ, 330 секторов - 26508 ТМ\n[!] Строительство\n    Минимальное время постройки/исследования юнита уменьшено до 1 секунды\n\n[+] Корабли/Технологии\n    Бонус к скорости полета кораблей теперь вычисляется относительно требуемого\n    уровня технологии двигателя. При равной технологии пользователя бонус равен\n    нулю, при  отличной  -  разнице  уровней  между  требованиями  постройки  и\n    пользовательской умноженной на бонус  двигателя.  Если  уровень  технологии\n    пользователя  меньше,  чем  требуемый  уровень  (например,  для   кораблей,\n    купленных на Черном  Рынке),  то  корабль  получает  пенальти  к  скорости,\n    вычисляемое аналогично, но не более 95%\n    Пример.  Бомбардировщик  требует  Ионный  двигатель  6-го  уровня.  Базовая\n    скорость  полета  корабля  -  4.000.  Каждый  уровень   технологии   Ионных\n    двигателей дает 20% к скорости полета. Таким образом:\n      * При  технологии   Ионных  двигателей  8-го   уровня   скорость   полета\n        Бомбардировщика составит:\n          4.000 * (1 + (8 - 6) * (20 / 100)) = 4.000 * (1 + 2 * 0,2) = 5.600\n      * При технологии 6-го уровня - 4.000\n      * При технологии 3-го уровня\n          4.000 * (1 + (3 - 6) * (20 / 100)) = 4.000 * (1 - 3 * 0,2) = 1.600\n      * Без технологии пенальти к уровню будет равно 120%,  поэтому  вступит  в\n        силу ограничение:\n          4.000 * (1 + (0 - 6) * (20 / 100)) = 4.000 * (1 - 0,95) = 200\n    Технологии двигателей теперь  так  же  влияют  на  расход  топлива.  Каждый\n    уровень, выше требуемого, уменьшает расход  топлива  на  10%  от  бонуса  к\n    скорости за уровень, но не больше чем 50% от расхода. Каждый уровень,  ниже\n    требуемого - увеличивает расход на 20% от бонуса.\n    Например, для Бомбардировщика каждый уровень Ионного двигателя,  ниже  6-го\n    будет увеличивать расход  топлива  на  4%  до  12%  при  полном  отсутствии\n    технологии. Каждый уровень, выше 6-го будет уменьшать расход топлива на 2%,\n    вплоть до 25-го уровня, где вступит в силу ограничение.\n[+] Локализация: Добавлена система отката языков в случае, если включаемый файл\n    не найден в указанном месте движка/для указанного языка\n[+] Офицеры: Академик, Фортификатор, Инженер\n    Теперь Фортификатор добавляет +1 слот к  очереди  постройки  оборонительных\n    сооружений за каждый уровень. Вследствие этого фактор Губернатора измененен\n    с 1.00 до 1.25. Текущие Фортификаторы остались без изменений\n    Теперь Инженер добавляет +1 слот к очереди постройки кораблей и  зданий  за\n    каждый уровень. Кроме того, бонус к скорости строительства зданий  увеличен\n    до 10%. Вследствие этого БС увеличена до 500, фактор увеличен  до  1.65.  У\n    нанятых инженеров уровень понижен  в  два  раза  с  округлением  в  большую\n    сторону. В целом это означает, что Инженер стоит  дешевле  в  пересчете  на\n    эффективный уровень\n    Изменена логика работы Академика, Фортификатора и Инженера. Все они  влияют\n    на скорость постройки юнитов, однако раньше  зависимость  была  практически\n    экспоненциальная. Вдобавок слишком большой бонус от Академика в  Альянсе  в\n    сочетании с полностью  прокачанным  Наемником  у  игрока  мог  приводить  к\n    артефактам в работе исследований\n    Теперь бонус указанных офицеров - это процент увеличения скорости постройки\n    соответствующих  юнитов,  а  не  процент,  на  который  уменьшается   время\n    постройки. Т.е. это - слагаемое в знаменатели дроби. Если  говорить  совсем\n    просто: 100% бонуса от офицера уменьшают время постройки юнита  в  2  раза,\n    200% - в три раза, 300% - в четыре раза и так далее\n    В связи с данными изменениями  сняты  ограничения  на  максимальные  уровни\n    Фортификатора и Инженера. Кроме того, бонус Академика увеличен с 5% до 10%,\n    а его максимальный уровень - до 30\n[+] MVC: Базовая поддержка MVC\n[+] Постройки/Здания\n    На превьюшках зданий и в информационной панели  дополнительно  отображаются\n    бонусные уровни - включая таблицу расчета производства\n[+] Ресурсы\n    Выводятся бонусные уровни зданий\n\n[~] Альянсы: Немного оптимизирован код страницы Управления Альянсом\n[~] Флоты\n    На странице выбора миссии таблица загрузки ресурсов по умолчанию отключена\n    Добавлено дополнительное сообщение при совпадении планеты отправки и пункта\n    назначения\n    Добавлено дополнительное сообщение при попытке отправить незагруженный флот\n    с миссией \"Транспорт\"\n    Добавлено дополнительное сообщение при попытке отправить флот с ресурсами в\n    миссию, отличную от миссий \"Транспорт\", \"Передислокация\" и \"Колонизация\"\n[~] Очередь верфи\n    Теперь так же показывается количество юнитов, когда юнит один в очереди\n[~] Ракеты: небольшой ребаланс ракет\n    Емкость шахты увеличена до 12-и, а размер межпланетной ракеты  увеличен  до\n    3-х. Таким образом на один уровень шахты теперь влазит 12 перехватчиков или\n    4  ракеты  (вместо  10  перехватчиков  и  5   ракет,   как   было   ранее),\n    а количество ракет к перехватчикам на уровень шахты увеличилось до  3  к  1\n    вместо 2 к 1. Мощность ракет осталась прежней\n[~] Система: Усовершенствован способ определения корневого каталога игры\n[~] Админка/Редактирование планеты\n    При выбранной планете невозможно изменить её ID для  исключения  перезаписи\n    информации о текущей планете\n    Добавлено форматирование количества юнитов/ресурсов на планете\n    Поддержка admin_planet_edit_extra v1c0\n[~] Продажа ТМ: Доработан дизайн страницы. Добавлена информация о бонусах\n[~] Навбар: Виджет экспедиций теперь ведет на страницу отправки флотов\n[~] События флотов\n    Фаланга теперь показывает входящие и исходящие ракетные атаки\n    Индикатор атаки на планету теперь так же реагирует на ракетные атаки\n[~] Скины: Обновлен скин supernova-ivash\n[~] Меню\n    Добавлена дата запуска сервера (под логотипом). Для уже запущенных серверов\n    она  равна  дате  запуска  апдейта.  Она  хранится  в  таблице  `config`  в\n    переменной \"server_start_date\"\n[~] Воплощение: Теперь при Воплощении  в  забаненного  персонажа  администратор\n    автоматически развоплощается\n[~] Меню\n    Пункты \"Технология\" и \"Квесты\" перемещены в раздел \"Информация\"\n[~] Новости: Заголовок \"Новости\" таблицы актуальных  новостей  теперь  является\n    ссылкой - клик на него раскрывает список всех новостей\n[~] Обзор планеты\n    Переформатирован вывод статуса Ворот для луны\n    Убран таймер исследований вследствие его полной бессмысленности\n[~] Юниты/Гравитационная технология\n    Изменены   требования   и   цена:   теперь   для   исследования   требуется\n    Энергетическая  технология  12  уровня,  а  цена  исследования   составляет\n    100.000.000 металла, 100.000.000 кристаллов и 50.000.000 дейтерия\n[~] Черный Рынок/Инфотрейдер: Добавлена информация об уровне премиума\n\n[%] Своз ресурсов: Исправлена ошибка со смещением цветового кодирования емкости\n    транспортов на одну строку вниз\n[%] Локализация/EN: Исправлены очепятки\n[%] Статистика: Исправлено неотображение пола игрока при просмотре статистики с\n    экранов логина/регистрации\n[%] Меню: Исправлена ссылка с пункта \"Статистика\"\n[%] Флоты: Исправлена ошибка, дающая возможность отправить флот с  ресурсами  в\n    миссии \"Атака\" или \"Удержание\"\n[%] Очередь верфи и исследований: Исправлена ошибка с индикацией конца  очереди\n    - \"undefined\" вместо \"Очередь пуста\"\n[%] Админка: Закрыты защитой по authlevel не закрытые ранее файлы\n[%] Альянсы: Исправлена ошибка  незасчета  уровня  технологии  при  составлении\n    списка доступных к постройке юнитов на верфи (корабли и оборона)\n[%] Экономика/Строительство: Исправлен ошибка багоюз медленных  соединений  при\n    постройке зданий\n[%] Админка/Редактор локализаций\n    Добавлена поддержка констант SNC_VER_ в домене 'admin'\n[%] Исследования: Исправлена ошибка повторного начисления ресурсов  при  отмене\n    исследования\n[%] Артефакты\n    Исправлена ошибка, когда АКК мог быть развернут на  планете  с  уже  идущим\n    строительством\n[%] Флоты\n    Исправлена  мгновенная  скорость  полета  если  для   двигателей   кораблей\n    соответствующие технологии еще не исследованы (например - корабль куплен на\n    ЧР или найден в экспедиции)\n[%] Локализация/EN: Исправлены очепятки\n[%] Фаланга: Исправлена уязвимость в отправке флота\n[%] Исправлена ошибка вычисления стоимости Наемников\n[%] Исследования: Исправлена ошибка расчета времени для исследований Альянса  в\n    случае, если Альянсу известна технология МИС\n[%] Сообщения: Исправлена ошибка при попытке отправить сообщение без адресата\n[%] Исследования\n    Устранена ошибка  в  формуле  рассчета  скорости  исследования  технологии.\n    Теперь корректно рассчитывается время исследования для игроков и  Альянсов,\n    а так же корректно обрабатывается случай,  когда  у  игрока  нет  ни  одной\n    лаборатории\n    После исправления время исследования увеличится чуть более, чем в два  раза\n    для игроков с МИС,  а  для  игроков  без  МИС  -  упадет  на  один  уровень\n    лаборатории\n\n[@] Документация: readme преобразован в UTF8\n[@] Добавлена компенсация  работы  механизма  Magic  Quotes.  Подробнее  -  см.\n    /docs/install.txt, подраздел \"Magic Quotes\"\n[@] Юниты: Добавлена дополнительный аттрибут \"max\" ко всем юнитам и  его  общая\n    проверка в eco_get_build_data()\n[@] Меню\n    Меню теперь является динамическим\n[@] Модули\n    Автоматическая загрузка и регистрация модулей\n    Автоматическое перекрытие функций методами модуля из $manifest\n    Автоматическое подгрузка специфических пунктов меню\n    Автоматическая загрузка конфигурации модуля из файла\n    Теперь можно перекрывать функции методами из класса\n    Добавлена поддержка  \"цепи  перекрытий\".  Можно  протаскивать  сквозь  цепь\n    результат вычислений, модифицируя его на каждом шагу (см. пример реализации\n    перекрытия mrc_get_level)\n    Теперь в манифесте модуля можно задавать  список  констант,  которые  будут\n    автоматически назначены при его инициализации\n    Теперь  в  манифесте  модуля  можно  задавать  список  переменных,  которые\n    автоматически заменят (в случае обычных переменных) или дополнят (в  случае\n    одноуровневых массивов) соответствующие глобальные переменные.  Специальный\n    механизм гарантирует корректную работу с константами в таких  переменных  и\n    массивах - даже  тех,  которые  были  только  назначены  при  инициализации\n    модуля\n    Конструктор  теперь  поддерживает  загрузку  индексированныъ  элементов   в\n    многоуровневые массивы  типа  sn_data,  включая  использование  констант  в\n    качестве индексов. Подробнее см. в \"sn_module.php\"\n    Теперь  при  инициализации  модуля  в  цепочку  вызовов  функций  корректно\n    инсталлируется оригинальная основная функция из движка\n[@] Подсказки: Можно задавать ширину  подсказки  для  согласования  с  основной\n    страницей\n[@] Константы типов юнитов приведены к единому формату \"UNIT_xxx\"\n[@] Всем юнитам прописаны типы\n[@] События флотов\n    Переработана система событий флотов\n[@] Файлы\n    Расширение файлов локализации изменено с  \".mo\"  на  \".mo.php\"  для  лучшей\n    поддержки в различных IDE\n[@] Локализация\n    В дополнение к стандартным путям  \"language/<ISO2>/<domain>.mo.php\"  теперь\n    так же  поддерживаются  пути  вида  \"language/<domain>_<ISO2>.mo.php\".  Это\n    сделано для упрощения структуры подкаталогов в модуле\n[@] Очереди\n    Упразднена  константа  MAX_BUILDING_QUEUE_SIZE.   Теперь   размер   очереди\n    построек зданий и верфи/обороны задается переменными из таблицы `config`  -\n    соотвественно 'server_que_length_structures' и  'server_que_length_hangar'.\n    По умолчанию их значения равны 5\n[@] Классы\n    Новый метод  'assign_recursive'  класса  \"template\"  -  позволяет  в  одном\n    операторе заполнить  как  переменные  темплейта,  так  и  блоки  -  включая\n    вложенные\n[@] Скины\n    Изменена  организация  CSS-файлов.  Файл   \"formate.css\"   переименован   в\n    \"skin.css\". К нему присоединен в конце файл  \"default.css\".  Таким  образом\n    сохранена последовательность загрузки стилей и при  этом  все  стили  скина\n    находятся теперь в одном файле\n    Теперь движок подгружает файл \"/design/css/global_server.css\" .  Этот  файл\n    может использоваться для добавления специфичных глобальных стилей сервера -\n    он не входит в дистрибутив и не будет перезаписан  при  обновлении  движка.\n    Файл  грузится  после  \"global.css\"  и,  следовательно,  может  перекрывать\n    глобальные стили \"по умолчанию\". Однако он грузится после скинового CSS  и,\n    следовательно, будет  перекрыт стилями скина\n    Изменена система раскраски меню. Теперь каждому пункту  меню  присваиваются\n    присваиваются собственные аттрибуты HTML ID и CLASS.  КРАЙНЕ  рекомендуется\n    производить раскраску меню через аттрибут  ID  (см.  пример  в  formate.css\n    скина EpicBlue). Список ID элементов меню  можно  узнать  либо  в  браузере\n    (используя  функцию  \"Inspect  Element\"  или  аналогичную),  либо  в  файле\n    \"/includes/template.php\", функция tpl_render_menu(), переменная $sn_menu\n    В базовый CSS перенесено цветовое кодирование чисел  и  сообщений  (ошибка,\n    предупреждение итд). При желании они  могут  быть  перекрыти  в  CSS-файлах\n    стилей\n    Выделение Администрации и  премиумных  аккаунтов  проводится  через  стили.\n    Соответственно,  в  основной  скин  добавлены  стили  классов  \".nick_admin\",\n    \".nick_operator\", \".nick_moderator\"  и  \".nick_premium\"\n    Добавлены  классы  \".same_alliance\"  и  \".same_player\"  для   выделения   в\n    статистике соответственно Альянса игрока и самого игрока\n    supernova-ivash: Скин приведен в соответствие с текущим положением дел\n\n\n\nProject \"SuperNova.WS\" Release 33 \"Women Day v2012!\"\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n[@] ВНИМАНИЕ!!! В этой версии скорректирована работа партнерской  программы,  а\n    так же повторно применен патч для масштабирования ТМ -  для  тех  серверов,\n    где он не был применен  раньше.  Если  вы  вносили  изменения  в  настройки\n    партнерской системы или начисления ТМ - сверьте текущие настройки в таблице\n    \"config\" с эталонными настройками в конце файла /docs/sql/supernova.sql!\n[@] ВНИМАНИЕ!!! PHP 5.3.1 содержит баг, который делает невозможной  полноценную\n    работу СН начиная с v33a12! Обновите  PHP,  или  сделайте  откат  до  более\n    ранней версии PHP, или используйте предыдущую версию СН.\n    Описание бага: https://bugs.php.net/bug.php?id=50394\n[@] ВНИМАНИЕ!!! Удален скин 'xnova'\n    Из дистрибутива игры удален скин 'xnova' из-за занимаемого им размера\n    Скачать скин можно с основного сайта проекта по ссылке\n    http://supernova.ws/files/skins/supernova-skin-xnova.zip\n    либо с SourceForge по ссылке\n    http://sourceforge.net/projects/supernova-ws/files/skins/\n[@] Среда разработки изменена на WAMP Server 2.2. Конфигурация:\n    MySQL 5.1.41\n    Apache 2.2\n    PHP 5.2.9-2 + xCache 1.3.2\n\n[#] Альянсы:   Альянсы  теперь  могут  рекрутировать  Наемников  и  исследовать\n    технологии!\n    1. Каждый Альянс имеет счет с ресурсами металл/кристалл/дейтерий/ТМ\n    2. Член Альянса может перевести ресурсы на счет Альянса. Сделать это  можно\n       на главной странице Альянса в разделе \"Ресурсы Альянса\".  Там  же  можно\n       увидеть состояние счета Альянса и бонусы, предоставляемые Альянсом  (см.\n       ниже). Кроме того в топбар  добавлено  отображение  ресурсов  Альянса  и\n       индикация наличия/отсутствия бонусов участникам Альянса\n    3. Ресурсы со счета Альянса могут расходоваться только  на  нужды  Альянса.\n       Вывод ресурсов со счета Альянса невозможен\n    4. Владелец   Альянса  с  его  счета   может   исследовать   технологии   и\n       рекрутировать Наемников - соответственно пункты \"Технологии  Альянса\"  и\n       \"Наемники Альянса\" на странице управления\n    5. После достижения минимально необходимого размера Альянса (10 человек  по\n       умолчанию, задается  в  таблице  `config`  записью  'ali_bonus_members')\n       каждый член Альянса получает бонус к своим Наемникам и технологиям\n    6. Значение бонуса зависит от количества игроков в Альянсе и вычисляется по\n       формуле: Бонус = round(уровень  технологии  или  Наемника  /  количество\n       игроков),  где round() - операция математического округления.\n    7. Бонусы   от  Наемников  и  Технологий  так  же  действуют  при  проверки\n       требований  к  постройкам/исследованиям.  Например:  игрок   состоит   в\n       Альянсе, дающем бонус  +2  к  Лазерной  технологии,  а  его  собственный\n       уровень технологии равен 4. Эффективный уровень технологии этого  игрока\n       равен 6. Это  означает,  что  находясь  в  Альянсе  он  имеет  доступ  к\n       исследованию Ионной  технологии  (требуется  ЛТ  5-го  уровня)  и  может\n       строить Тяжелый Лазер (требуется ЛТ 6-го уровня). Очевидно, если  бы  он\n       не находился в Альянсе, эти постройки были бы заблокированы\n    8. При исследовании технологии уровень лаборатории равен количеству игроков\n       в Альянсе на момент начала  исследования.  Активные  исследования  видны\n       членам Альянса на странице информации\n    Примеры:\n      1. Альянс из 10 человек купил Технологию 4 уровня. Бонус = round(4/10)  =\n         round(0,4) = 0\n      2. Альянс из 10 человек купил Технологию 7 уровня. Бонус = round(7/10)  =\n         round(0,7) = 1\n    Выбранная механика бонусов  Альянса  призван  обеспечить  достижение  сразу\n    нескольких целей:\n    1. Исключить злоупотребление фишкой,  когда  2-3  игрока  формируют  Альянс\n       исключительно для получения бонусов\n    2. Активизировать межальянсную активность: бонусы от ресусов Альянса  можно\n       получить только начиная с определенного количества участников. Ну и  чем\n       больше игроков в Альянсе, тем больше у него ресурсов\n    3. Усилить лояльность игроков к  Альянсу  -  при  выходе  (или  выгоне)  из\n       Альянса игрок теряет  все  бонусы  и  (самое  неприятное)  все  ресурсы,\n       пожертвованные  в Альянс\n    4. Исключить появление  мегаальянсов:  чем  больше  игроков  -  тем  больше\n       ресурсов они могут пожертвовать, но тем меньше  бонусов  получит  каждый\n       отдельный игрок\n    5. Слабые игроки в сильных  Альянсах  получают  доступ  к  end-game  юнитам\n       (если, конечно, глава Альянса  решит  потратить  ТМ  на  соответствующих\n       Наемников) и бонус в развитии\n    6. Сильные игроки смогут  поднять  эффективные  уровни  Технологий  даже  в\n       больших Альянсах. Например, если в Альянсе 15 человек, то исследовать 15\n       уровень технологии всем Альянсом будет  проще  и  дешевле,  чем  каждому\n       игроку отдельно\n    7. То же самое распространяется и на Наемников. При этом только Альянс дает\n       возможность получить эффективный уровень Наемников больше максимального\n    Дополнительно доступны еще три метода  расчета  бонусов.  Подробнее  см.  в\n    /docs/readme.txt\n\n[!] Иконка сайта: Новая иконка сайта! Мегареспект ув.Помощнику Ivash!\n[!] Запущен сервер обновлений.\n    1. Движок сервера общается с сервером обновлений по протоколу HTTP.\n    2. Если установлен CURL и подключен к PHP, то  для  проверки  версии  будет\n       использован именно он. Убедитесь, что  CURL  правильно  настроен  и  ему\n       разрешен доступ к внешним ресурсам\n    3. Если CURL не установлен,  будет  осуществлена  попытка  получить  версию\n       через file_get_contents(). Убедитесь, что в PHP разрешается обращаться к\n       внешним сайтам через соответствующую функцию\n    В  настоящее  время  сервер  обновлений  поддерживает  следующие   функции:\n    проверка версии движка и регистрация сайта.  Доступ  к  ним  осуществляется\n    из админки со страницы настроек сервера\n    1. При проверке версии передаются только анонимные данные - текущая  версия\n       БД, номер релиза и версия игры.\n    2. Результат проверки - рекомендация  сервера  обновлений  о  необходимости\n       обновления текущей версии игры\n    3. Есть два варианта проверки версии: ручная и автоматическая\n    4. Ручная проверка версии выполняется в ручном  режиме  по  нажатию  кнопки\n       \"Проверить версию\" на странице настроек.\n    5. Автоматическая проверка версии (по умолчанию -  отключена)  производится\n       самостоятельно движком по расписанию. Как и при ручной,  передается  тот\n       же объем анонимных данных\n    6. Период автоматической проверки версии  задается  в  секундах  в  таблице\n       config  переменной  server_updater_check_period.  По  умолчанию   период\n       проверки равен 24 часам (раз в сутки).\n    7. Результат и время последней проверки выводится в левом меню.\n    8. Результат и время  последней  проверки  так  же  выводится  на  странице\n       настроек.\n    9. Предусмотрено цветовое кодирование результатов  проверки  (как  в  левом\n       меню, так и в настройках): зеленый - обновление необязательно, желтый  -\n       желательно обновить движок, оранжевый - крайне рекомендуется обновление,\n       красный - ошибка проверки версии\n    Регистрация сервера нужна для  ряда  запросов  к  серверу  обновлений.  При\n    регистрации передается минимум информации,  необходимой  для  идентификации\n    сервера:\n    1. Полный URL сервера - т.е. HTTP-адрес  и  подкаталог  сервера.  Например:\n       http://myserver.com/myfolder/.    Это    необходимо    для     первичной\n       идентификации сервера. Полный путь необходим для того, что бы  различать\n       несколько копий СуперНовы, установленных на одном IP или домене.\n    2. Внутреннее название сервера. Используется для подстановки в сообщения.\n    Зачем  вообще  регистрировать  свой  сервер?  В   будущем  планируется  ряд\n    возможностей, которые буду доступны только зарегистрированным  серверам.  В\n    их число входит (отсортированы по запланированным срокам реализации):\n    1. Автоматическое получение чейнджлога\n    2. Автоматизированное обновление движка\n    3. Участие в рейтинге серверов\n    4. Багрепорты от администраторов серверов\n    5. Чат для администраторов серверов\n    6. По запросу - удаленная диагностика сервера\n    7. ...и многое, многое другое\n    Зачем регистрировать свой сервер прямо сейчас?\n    1. Запросы от администраторов  зарегестрированных  серверов  имеют  больший\n       приоритет при диагностике проблем и обработке багрепортов.\n    2. При регистрации кроме индивидуального ключа серверу выдается  уникальный\n       идентификационный номер,  который  будет  использоваться  при  первичной\n       сортировке серверов. Чем раньше  будет  зарегистрирован  сервер  -  тем,\n       например, выше он будет в общем каталоге серверов...\n[!] Наемники: Добавлена поддержка временных Наемников  (ВН).  Осталась  так  же\n    поддержка постоянных Наемников (ПН). Тип Наемников выбирается в  настройках\n    сервера\n    1. ВН (как следует из названия) не являются постоянными,  а  нанимаются  на\n    определенный срок. По истечению срока Наемник исчезает\n    2. В режиме ВН отсутствует понятие \"веток развития\" и  для  найма  доступны\n    сразу все Наемники. Соответственно не отображаются требования  к  Наемникам\n    на странице \"Технологии\"\n    3. Базовая цена покупки ПН в режиме ВН становится ценой  найма  на  базовый\n    период найма (БПН). По умолчанию он равен одному среднекаелндарному  месяцу\n    (30 дней, 2592000 секунд). Изменить БПН можно на странице настроек сервера\n    4. Предусмотрена система скидок/наценок  в  зависимости  от  срока  покупки\n    Наемника.  Настройки  содержатся  в  массиве  $mrc_hire_discount  в   файле\n    /officer.php.  Индекс  элемента  -  количество  секунд  найма,  значение  -\n    коэффициент скидки. \"1\" означает, что  на  данный  интервал  найма  нет  ни\n    наценки, ни скидки и при пересчете на количество секунд в БПН его стоимость\n    будет в точности равна стоимости БПН.  Если  число  меньше  единицы  -  это\n    означает скидку; больше единицы - наценку\n    5. Временного наемника можно увольнять до истечения срока найма.  ВНИМАНИЕ!\n    При увольнении наемников вся портаченная на найм ТМ будет утеряна!\n    6. Режим Наемников отображается на странице \"Мировые константы\"\n    При переключении режима Наемников следует учитывать следующие особенности:\n    1.  При  включении  ВН  все  постоянные  Наемники  будут  преобразованы  во\n    временные со сроком действия равному БПН. В случае  необходимости  изменить\n    БПН нужно СНАЧАЛА его изменить, а затем переключать режим работы Наемников\n    2. После включения ВН изменение базового интервала найма не влияет  на  уже\n    рекрутированных Наемников, а влияет только на цену нового найма\n    3.  При  отключении  ВН  все  активные  на  этот  момент   Наемники   будут\n    преобразованы в постоянные - вне зависимости от того,  на  какой  срок  они\n    были наняты и сколько времени осталось до срока истечения найма. Информация\n    о сроках найма при этом теряется\n    4.  При  отключении  ВН  активизируются   ограничения   по   рекрутированию\n    Наемников, однако уже нанятые Наемники останутся активными и  будут  влиять\n    на игру вне зависимости от того, может  игрок  их  купить  или  нет.  Такой\n    способ переключение выбран для исключения потери ТМ, вложенных  игроками  в\n    Наемников\n    Переработана  страница  рекрутирования  Наемников:\n    1. Добавлена поддержка временных Наемников\n    2. Стоимость найма  отображается  динамически  в  зависимости  от  текущего\n    режима Наемников, выбранного уровня и срока найма\n    3. В режиме ПН видны все наемники - даже  недоступные  (с  соответствующими\n    пояснениями)\n    4. В режиме ПН можно нанимать сразу несколько уровней Наемников\n    5. При найме постоянных наемников показывается ровно  столько  уровней,  на\n    сколько хватает ТМ\n[!] Аватары: Добавлена поддержка серверных аватаров игроков\n    Аватары могут быть  загружены  с  локального  диска  на  странице  настроек\n    пользователя.\n    Поддерживаются файлы форматов JPG, GIF и PNG размером до 200КБ. Загруженные\n    картинки будут отмасштабированы до размеров 128х128.\n    Аватар отображается на странице \"Император\" и в попапе игрока  на  странице\n    \"Вселенная\"\n[!] Альянс: Добавлена поддержка серверных логотипов  Альянсов\n    Логотипы могут быть загружены с локального  диска  на  странице  управления\n    Альянсом\n    Поддерживаются файлы форматов JPG, GIF и PNG размером до 200КБ. Загруженные\n    картинки будут отмасштабированы до размеров 128х128\n    Логотип отображается на странице информации об Альянсе и в  попапе  Альянса\n    на странице \"Вселенная\"\n[!] Вселенная: Галактики и системы могут иметь собственные названия!\n    1. По умолчанию галактики и системы не имеют собственных названий\n    2. Увидеть текущее имя галактики или системы можно на странице \"Вселенная\"\n    3.  Назвать  галактику  или  систему  можно   по   ссылке   \"Переименовать\"\n    соответственно  возле  координат  галактики   или   системы   на   странице\n    \"Вселенная\"\n    4. Именование галактики или системы имеет  соотвествующую  стоимость  -  по\n    умолчанию 10000 ТМ для галактики и 1000 ТМ  для  системы  -  т.н.  \"базовую\n    стоимость  именования\".  Изменить  базовую  стоимость  именования  можно  в\n    настройках  сервера.  Игроки  могут  видеть   текущую   базовую   стоимость\n    именования на странице \"Мировые константы\"\n    5.  При  именовании  галактики  или  системы  игрок  может  назначить  цену\n    именования. Минимальная цена именования равна базовой стоимости именования\n    6. При переименовании уже именованной галактики или системы,  игрок  должен\n    уплатить ранее назначенную  стоимость  именования  плюс  базовая  стоимость\n    именования. Таким образом,  если  первый  игрок  назначил  цену  именования\n    системы в 2500 ТМ, а базовая цена именования системы составляет 1000 ТМ, то\n    игрок, желающий переименовать ту же систему должен уплатить не меньше  3500\n    ТМ. Таким образом более высокая цена именования  галактики  или  системы  в\n    определенной степени защищает объект от переименования\n    7. Как было сказано в п.2, имя галактики и системы видны всем игрокам\n    8. Все действия по переименованию галактик и систем записываются  в  лог  -\n    код события 104\n[!] Модульность: Базовая  поддержка  модульности  -  динамически  перекрываемые\n    функции. Подробное описание по использованию динамического перекытия см.  в\n    разделе \"Модульная система\" файла /docs/readme.txt\n[!] Исследования: Очередь исследований перенесена с планет на пользователя\n    1. Исследования теперь могут производится даже на планетах  со  строящимися\n       лабораториями/нанолабораториями\n    2.  В  случае  отсутствия  МИС  для  проведения   исследования   выбирается\n       лаборатория  с  максимальным  эффективным   уровнем   (т.е.   с   учетом\n       нанолабораторий)\n    3. При отмене исследования ресурсы возвращаются на ту  планету,  с  которой\n       были взяты\n    4. Награда за квесты на исследование теперь всегда начисляется на  основную\n       планету игрока\n    5. При апдейте все идущие исследования будут перенесены в  пользовательскую\n       очередь\n[!] Чёрный Рынок: Продавец информации\n    На Чёрном Рынке  доступна  новая  услуга:  продажа  информации.\n    Письма от Продавца Информации всегда приходят в почтовый ящик - даже если у\n    игрока отключено получение шпионских отчетов. Мистика какая-то!\n    Информация об игроке: текущие уровни активных Наемников.\n[!] Темплейты: Минификатор\n    ВНИМАНИЕ! ЭТО - ЭКСПЕРИМЕНТАЛЬНАЯ ФИШКА! ИСПОЛЬЗУЙТЕ ЕЁ  НА  СВОЙ  СТРАХ  И\n    РИСК!\n    Минификатор уменьшает размер генерируемого движком HTML-кода  путем  замены\n    нескольких  идущих  подряд  пустых  символов  (перевод  строки,  табуляция,\n    пробел) одним символом пробела.\n    Минификатор умеет сжимать HTML и встроенный JS-код. Для JS-кода он  так  же\n    удаляет однострочные комментарии.\n    Минификатор работает на уровне темплейтов и если включено  кэширование,  то\n    минификатор вызывается  только  один  раз  при  компиляции  кода  и  дальше\n    кэшируется   уменьшенный   скомпилированный   темплейт,    что    исключает\n    необходимость в повторном вызове минификатора. Этим он  выгодно  отличается\n    от  минификаторов, работающих на уровне сессии через ob_hanler()\n    В  среднем  по  сайту  минификатор   дает   выигрыш   порядка   7-8%%   при\n    незначительном падении производительности.\n    По  умолчанию  минификатор  отключен.  Включить  его  можно  в  админке   в\n    настройках сервера - пункт \"Минификатор темплейтов\"\n\n[+] Пол: Добавлено отображение пола игрока на странице \"Император\", на странице\n    статистики и в попапе игрока на странице \"Вселенная\"\n[+] Настройки: Отображение логотипов Альянсов и аватаров  игроков  на  странице\n    \"Вселенная\" может быть отключено в настройках игроков\n[+] Вселенная: На попапе игрока отображается его текущее звание в Альянсе\n[+] ЧР/Торговец ресурсами: Теперь  можно  поменять  ТМ  сразу  на  все  ресурсы\n    (опция \"Все ресурсы\" в дропдауне выбора ресурсов). При этом вводимая  сумма\n    будет разделена на три части и на  каждую  из  этих  третей  будет  куплено\n    соответствующее количество ресурсов по курсу. Стоимость такой операции -  в\n    три раза больше базовой стоимость обмена\n[+] Квесты: Теперь в награду за исполнение квеста можно одновременно ставить до\n    четырех видов ресурсов\n[+] Скины: Добавлена возможность перекрыть дефолтные стили  элементов  jQueryUI\n    (файл /design/css/jquery.css) стилями, специфическими для скина. Для  этого\n    в корневой каталог скина  нужно  положить  файл  jquerу.css  с  настройками\n    стилей элемента. Сгенерировать файл под свою тему можно на сайте jQuery  по\n    ссылке: http://jqueryui.com/themeroller/\n[+] Постройки:   Вертикальная  очередь  построек.   Включается   в   настройках\n    пользователя в секции \"Настройки интерфейса\"\n[+] Исследования: Добавлены подробные  сообщения  об  ошибке  в  случае,  когда\n    технология не может быть исследована (нехватка ресурсов,  неудовлетворенные\n    требования итд)\n[+] Обзор планеты: Настраиваемое количество колонок в списке планет\n    На странице настроке пользователя можно  указать,  сколько  колонок  должно\n    быть в списке планет - пункт \"Количество колонок в списке планет\" в разделе\n    \"Настройки интерфейса\"\n    Можно выставить количество колонок в 0 и  указать  максимальное  количество\n    рядов с списке - см. соответствующий пункт там же.  В  этом  случае  движок\n    рассчитает количество колонок исходя из этого  числа.\n    Обращаю внимание - указывается именно максимальное количество  рядов!  Т.е.\n    если у игрока 6  планет,  а  количество  рядов  указано  5,  то  количество\n    необходимых колонок для того, что бы число рядов не привысило 5 будет равно\n    двум. Соответственно, список планет будет сформирован в виде  двух  колонок\n    по три ряда. Если же колоний будет 12 - список планет будет  выглядеть  как\n    таблица три колонки по четыре ряда.\n    Данная особенность связана с построением списка планет -  слева  направо  и\n    сверху вниз. Естественно, не составило  бы  никакого  труда  сделать  вывод\n    списка сверху вниз, а затем справо налево - это было бы даже легче.  Однако\n    при выбранном способе  сохраняется  пользовательская  сортировка  планет  -\n    более \"важные\" колонии всегда будут \"выше\" в списке\n[+] Документация: Добавлен файл /docs/html/changelog.html - чейнджлог в html\n[+] Навбар: Переработан навбар\n    Из навбара убрано количество текущих ресурсов на  планете/в  альянсе  -  их\n    присутствие на большей части экранов не имело практического смысла при том,\n    что отнимало драгоценное вертикальное пространство страницы.\n    Сведения о количестве ресурсов на планете вынесены в планетарный навбар. На\n    странице настроек пользователя можно сделать планетарный навбар  постоянным\n    - т.е. будет полностью восстановлен функционал старого навбара\n    Там, где сведения о количестве  ресурсов  на  планете  смысл  имеет  -  они\n    добавлены или включен планетарный навбар (см. ниже)\n\n[~] Обзор планеты: Добавлено текущее количество ресурсов  на  планете,  текущий\n    размер хранилищ, а так же - количество ресурсов на прилетающих флотах\n[~] Черный Рынок/Скупщик кораблей и Продавец б/у кораблей\n    Добавлено количество текущих ресурсов на планету\n[~] Модуль \"Альянсы-игроки\" - v6\n    Добавлено количество  текущих  ресурсов  в  таблицу  перечислений  ресурсов\n    Альянсу\n    Модуль необходимо обновить до версии v6+ для работы с SN v33a29+!\n[~] Экономика/Строительство\n    На всех экранах строительства (здания, флот, оборона)  включен  планетарный\n    навбар\n[~] Флоты\n    Добавлено количество  дейтерия на  планете  на  страницу  выбора  кораблей,\n    страницу выбора точки назначения и страницу своза ресурсов\n    Так же на странице выбора кораблей включен планетарный навбар\n[~] Альянсы\n    Добавлен заголовок на страницу Альянсов с тэгом Альянса\n    Ссылка на управление Альянсом/игроками перенесена в самую верхнюю таблицу\n[~] Постройки: Информация о  постройках  кэшируется  при  заходе  на  страницу.\n    Ширина таблицы построек устанавливается на максимальную из  возможных.  Это\n    предотвращает \"баян\" - прыжки ширины таблицы построек\n[~] Император: Теперь на  страницы  показывается  так  же  очки  за  ресурсы  и\n    исследования. Немного переформатирована таблица статистики\n[~] Статистика:   Оптимизирован   алгоритм   подсчета   статистики.   Отключено\n    обновление \"очков планеты\"\n[~] Чат: Теперь при таймауте чата скрипт поллинга полностью прекращает работу\n[~] Экономика: При расчете  времени  постройки  юнитов  учитывается  не  только\n    количество ресурсов, но и  их  качество.  Время  постройки  нормированы  по\n    дейтерию, т.е. постройки с большей долей низкоуровневых  ресурсов  строятся\n    быстрее\n[~] Админка: Добавлены label for для всех чекбоксов\n[~] Скины: EpicBlue имеет собственное оформление элементов jQueryUI\n[~] Вселенная: При создании новой планеты из имени планеты  исключен  знак  \"№\"\n    для более корректной работы функций PHP\n[~] Альянсы:  Количество  игроков  в  Альянсе  теперь   изменяется  сразу   при\n    изменении, а не при апдейте статистики/обслуживании сервера\n[~] Альянсы: Теперь список членов Альянса распознает права просматривающего без\n    захода в админскую  часть,  поэтому  из  админки  убран  пункт  \"Управление\n    участниками\"\n[~] Альянсы: Изменено отображение логотипа на странице Альянса\n[~] Список планет: Клик на иконке  летящего  союзного  флота  теперь  открывает\n    сраницу \"Флоты в полете\"\n[~] Чёрный Рынок\n    Редизайн основного экрана и экрана обмена ресурсов\n    Откуда взялась эта странная надпись? Очень странно...\n[~] Меню: Переработано меню\n[~] Чат: Тэг  Альянса  после  имени  игрока  теперь  указывается  в  квадратных\n    скобках, а адресат сообщения - в круглых. Сделано для унификации  написания\n    тэга Альянса в движке\n[~] Статистика: Теперь полностью отрабатывается переход со ссылок Вселенной  на\n    страницу статистики: правильно выбирается тип статистики  (игрок/Альянс)  и\n    страница статистики, корректно отрабатываются дропдауны (выбирается  именно\n    текущий тип/страница статистики).\n    Cписок статистики скроллируется либо до выбранного объекта - если позволяет\n    размер страницы, либо максимально вниз, если размер страницы не позволяет\n    Выбранный объект отмечается знаком \">\" в столбце рангов\n    Все числа в ячейках отцентрированы по правому краю\n[~]  Логин/Регистрация:  Данные   по   серверу   (размер,   скорость,   онлайн)\n    сгруппированы в один блок и теперь видны так же на странице регистрации\n\n[%] Партнерка: Исправлено отображения количества начисленных ТМ\n[%] Наемники:  Исправлена  ошибка  начисления  процентных  бонусов:  в  случаях\n    многократных бонусов происходило  начисление  бонуса  на  бонус,  а  не  на\n    базовое значение. Это приводило к получению завышенных бонусов  -  например,\n    на Адмирале и Навигаторе\n[%] Альянсы: Исправлена ошибка при отправке письма группам Альянса\n[%] Альянсы:   Исправлена  ошибка  с  неправильным  отображением  звания  главы\n    Альянса\n[%] Регистрация: Исправлена ошибка, когда игрок мог создать  аккаунт/планету  с\n    пробелами в начале/в конце\n[%] Админка: Исправлена ошибка создания квеста\n[%] Ракетная атака: Исправлена ошибка, из-за которой ракеты били в  десять  раз\n    слабее\n[%] UBE: Исправлена ошибка неучета бонуса  Альянсов  в  бою.  ВНИМАНИЕ!  Бонусы\n    Альянса и от Наемников по-прежнему не будут видны в логе боя!\n[%] Альянсы: Исправлена ошибка выхода из Альянса - у игрока  не  затирался  тэг\n    Альянса\n[%] Флоты: Исправлена уязвимость, позволяющая дублировать флоты\n[%] Локализация/EN: Исправлены очепятки, спасибо tedk28\n\n[@] Админка: Редизайн интерфейса просмотра логов (бывш. \"Ошибки\")\n[@] Аватары: Для корректной  работы  подсистемы  аватаров  в  PHP  должен  быть\n    корректно настроен временный каталог и движку должна быть разрешена  запись\n    в каталог /images/avatar\n    Максимальный размер аватара настраивается  в  таблице  `config`  переменные\n    avatar_max_width и avatar_max_height\n[@] Пол: В скины добавлена иконка пола в подкаталог \"images\" скина.  Файлы  для\n    мужского  и  женского  пола  называются  соответственно  \"sex_male.png\"   и\n    \"sex_female.png\". Встроенные скины обновлены автоматически\n[@] Новости: Изменения в редактировании новости\n    При редактировании новости галочка рассылки новости по умолчанию отключена\n    При  редактировании  новости  не  изменяется  глобальное  время   написания\n    последней новости -  т.е.  отредактированная  новость  не  включает  список\n    последних новостей на обзоре планеты\n[@] JS: Обновлен jQuery до версии 1.7.1. Обновлен jQuery-UI до версии 1.8.17\n[@] БД: Доработан дамп. Теперь по умолчанию новосоздаваемый игрок имеет мужской\n    пол - так же, как и дефолтный администраторский аккаунт\n[@] Локализация: Все строки локализации, относящиеся к  наемникам,  вынесены  в\n    файл mrc_mercenary.mo\n[@] Исследования: Исследования теперь завернуты в  транзакции  -  это  позволит\n    избежать  злоупотреблений  связанных  с  частым  обновлением  страницы   на\n    медленных соединениях\n[@] Файл todo.txt заменен на todo.xls\n[@] $sn_data['groups']['prod'] => $sn_data['groups']['factories']\n[@] Чёрный Рынок: Почищены языковые файы от неиспользуемых строк\n[@] Темплейты\n    Расширение файлов темплейтов изменено с \".tpl\" на \".tpl.html\" для  большего\n    удобства разработки\n    Теперь при использовании директивы <!--  INCLUDE  -->  НЕ  НУЖНО  указывать\n    расширение подключаемого файла\n[@] Файлы: Удалены неиспользуемые файлы faq.php, faq1.php, faq2.php\n[@] Файлы: Сильно переработана организация файлов PHP\n    Многие процедуры поменяли свое местоположение\n    Множество файлов теперь не грузятся  автоматически  при  старте  движка,  а\n    грузятся лишь по потребности. В частности - все файлы  миссий  подгружаются\n    только в менеджере летящих флотов,  а  сам  менеджер  грузится  только  при\n    потребности в обработке флотов.  Кроме  того,  боевой  движок  подгружается\n    только в симуляторе и при обсчете боев (Миссии \"Атака\" и \"Уничтожить\")\n    Все это позволило заметно сократить размеры кода в памяти сервера\n[@] Модули: Загрузчики модулей теперь располагаются в каталоге /modules, а не в\n    /modules/_functions\n\n\n\nProject \"SuperNova.WS\" Release 32 \"Happy New Year v2012!\"\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n[!] Исправлена работа с большими числами:\n      Все числовые значение в HTTP-запросах трактуются как  числа  с  плавающей\n      запятой. Все идентификаторы передаются как строки\n      Все идентификаторы в  БД  являются  BIGINT(20).  Соответствующим  образом\n      переконфигурированы (добавлены или изменены) FOREIGHN KEYS\n      Переработаны  все  таблицы,  что  бы  исключить  переполнение  при  любом\n      разумном сценарии  использования  движка  (скажем,  вплоть  до  скоростей\n      x1000000)\n\n[+] Постройки:  На   ресурсогенерящие   постройки   добавлена   подсказка    по\n    производительности\n\n[~] Скины: Обновлен скин СН/Иваш\n[~] Постройки:  Показываются  все  возможные  строения  на  планете/луне.  Если\n    невозможно осуществить постройку - показывается причина\n    Иконки  информации  и   своза   ресурсов   поменялись   местами,   что   бы\n    соответствовать  расположению  иконки  своза  ресурсов  на  списке   планет\n    (Обзор/Империя)\n[~] Постройки: Теперь при невозможности постройки юнита не затемняется название\n    и текущий уровень юнита\n[~] Постройки: В описании юнитов разнесены ссылки на  постройку  и  уничтожение\n    юнита во избежании случайного выбора не того действия\n[~] Постройки:  Из  темплейта  постройки задний  яваскрипт  вынесен в отдельный\n    файл\n[~] Постройки: Увеличено  место  для  картинки  здания  до  150  пикселей.  Это\n    предотвратит \"скачки\"  описания  постройки  при  перемещении  выделения  на\n    иконках\n[~] Экономика: Производство и потребление электроэнергии теперь  масштабируется\n    согласно скорости добычи\n[~] Верфь: Обновлен интерфейс верфи\n[~] Верфь: Ограничено максимальное количество строящихся юнитов на верфи\n[~] Экономика:   Увеличена  базовая  добыча  шахты  кристаллов  с  20  до   32.\n    Соответственно увеличено энергопотребление с 10 до 16\n[~] Статистика:  Статистика  теперь  считается  не  в  транзакции,  что  бы  не\n    блокировать игроков. Сохраняется статистика за 10 дней\n[~] Рекорды: Переписана страница рекордов\n[~] Постройки: Ссылки на создание и  уничтожение  юнитов  раскрашены  в  цвета,\n    согласно  CSS  (по  умолчанию:  зеленый  для  создания,   красный   -   для\n    уничтожения)\n\n[%] Вселенная: Исправлена ошибка с запуском  отрицательного  количества  юнитов\n    через AJAX\n[%] Альянсы: Исправлена ошибка с неотображением тэга  у  членов  новосозданного\n    Альянса\n[%] Постройки: Исправлена ошибка  с  невозможностью  выбора  превьюшки  здания,\n    которое нельзя построить\n[%] Постройки: Исправлена ошибка с невозможностью уничтожить  постройку,  когда\n    не хватает ресурсов на создание постройки, но хватает на её уничтожение\n[%] Админка: Исправлена ошибка самопроизвольного сброса  флага  масштабирования\n    хранилищ\n[%] Друзья: Исправлена надпись с неправильной кодировкой при ответе  на  письмо\n    друга/кандидата\n[%] Исправлена уязвимость, позволяющая быстрыми многократными  постройками  или\n    отменами добиться увеличения ресурсов\n[%] Рекорды: Исправлена ошибка, когда в списке  рекордсменов  появлялись  члены\n    команды сервера\n[%] Флоты: Исправлена уязвимость, позволяющая передавать флоты другим игрокам\n\n[@] DB: Обновлен дамп БД до версии 32\n[@] PTE: Парсер темплейтов теперь понимает конструкции вида  {L_tech[D_CONST]},\n    которые будут развернуты в $lang['tech'][CONST]\n[@] eco_get_build_data() возвращает  время  постройки  в  отдельном  подмассиве\n    RES_TIME, а не в массивах действий BLD_CREATE/BLD_DESTROY\n[@] Числовые идентификаторы для строений заменены константами STRUC_xxx\n\n\n\nProject \"SuperNova.WS\" Release 31 \"Artifact governors edit localized inflation\"\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n[!] Скины: Новый скин \"supernova-ivash\". Автор - Ivash\n[!] Артефакты: Добавлена подсистема Артефактов - редких объектов с  уникальными\n    свойствами.\n    Артефакты можно купить за ТМ.\n    Артефакты являются одноразовыми - после использования Артефакт исчезает.\n    Некоторые Артефакты настолько мощные, что их  количество  в  одной  Империи\n    ограничено.\n    Использование некоторых Артефактов привязано к планетам -  т.е.  их  эффект\n    будет распространятся только на  эту  планету.  Эффекты  других  Артефактов\n    распространяются на всю Империю. Особо  мощные  Артефакты  могут  оказывать\n    влияние на солнечную систему, галактику или даже Вселенную\n    Добавлен Артефакт - \"Большой Адронный Колайдер\"\n    Добавлены Артефакты - Автономный  Колонизирующий  Комплекс  трех уровней\n[!] Наемники: Произведено разделение офицеров на \"наемников\" и  \"губернаторов\".\n    Наемники покупаются через общее меню слева и их бонусы распространяются  на\n    всю Империю. Губернаторы покупаются отдельно на каждую планету на  странице\n    \"Управление  планетой\"  и  их  бонусы   распространяются   только   на   ту\n    планету/луну,  на  которой  они  куплены.  Так  же  произведены   следующие\n    изменения:\n    1. Стоимость наемников и губернаторов теперь рассчитывается по формуле:\n      БС * (Фактор ^ Уровень),\n      где БС - базовая стоимость наемника, Фактор - заранее заданная  величина,\n      ^ - операция возведения в степень, Уровень - новый уровень наемника\n    2. По умолчанию БС = 3000, Фактор = 1\n    3. Максимальный уровень карго-мастера увеличен до 20\n    4. Упразднены  наемники  Геолог,   Энергетик,   Архиктектор,   Конструктор,\n    Фортификатор, Разрушитель - их функции переданы губернаторам (см. ниже)\n    5. Вся ТМ за упраздненных наемников возвернута игрокам\n    6. Реорганизованы ветки развития наемников\n       \"Шахтерская\" теперь выглядит как \"Карго-мастер\" - \"Шпион\" - \"Академик\" -\n       \"Разрушитель\"\n       \"Рейдерская\" теперь выглядит как \"Адмирал\" - \"Координатор\" - \"Навигатор\"\n       - \"Ассасин\"\n    7. Балансировка губернаторов проведена из  расчета  на  \"среднего  игрока\",\n    имеющего 6 планет. Такие игроки не  получат  пенальти  при  оснащении  всех\n    планет  губернаторами.  Понятно,  что  общая   производительность   Империи\n    уменшится, однако это те жертвы, на которые  я  готов  пойти.  В  целом  же\n    изменение направлено на уменьшение среднего  количества  ТМ  у  игроков.  В\n    особенности - у топов и саб-топов\n    8. Новый губернатор \"Технолог\" объединяет функции Геолога и Энергетика БС =\n    800, Фактор = 1.06, стоимость 20 уровня - 29418 ТМ, не имеет ограничения по\n    уровню. С 5-го уровня позволяет строить термоядерную электростанцию\n    9. Новый губернатор \"Инженер\" объединяет функции Архитектора и Конструктора\n    БС = 400, Фактор = 1.25, стоимость 10-го уровне - 13298  ТМ,  максимального\n    15-го - 43868 ТМ\n    10. Губернатор \"Фортификатор\" так же играет для планеты роль Защитника БС =\n    2000, Фактор = 1, стоимость максимального 8-го уровня - 16000  ТМ.  С  3-го\n    уровня позволяет строить Планетарную защиту. Дает хозяину планеты бонус 10%\n    за каждый уровень к атаке, броне и щитам при обороне\n    11. Вследствие полной бессмысленности найма Технолога на лунах во избежание\n    напрасных трат ТМ игроками он убран со страницы управления луной\n    12.  Страница  покупки  губернаторов  требует  подтверждение  операции  при\n    покупке губернатора, отличного от текущего. Страница покупки имеет защту от\n    случайной покупки губернатора при обновлении страницы\n[!] Локализация: СуперНова использует  кодировку  UTF-8  при  работе  с  БД  и\n    рендере HTML-страниц. Таким образом поддерживаются любые наборы символов\n    Все файлы локализации пропущены через редактор и приведены к одному виду\n    Добавлены заголовки в файлы локализации\n[!] Редактор локализаций: В админке добавлен редактор локализаций - пункт  меню\n    \"Локализация\" в разделе \"Утилиты\"\n    1. Выбор пункта  меню  \"Локализация\"  открывает  выбор  т.н.  \"домена\"  для\n    редактирования. Домен - это совокупность строк локализации,  относящихся  к\n    отдельному   аспекту   игры.   Домен   эквивалентен   языковому   файлу   с\n    соответствующим именем\n    2.  После  выбора  домена  и  подтверждения  выбора  открывается   страница\n    редактирования строк локализации. Открытие больших  файлов  может  занимать\n    существенное время - поэтому запаситесь терпением\n    3. После редактирования строк локализации и подтверждения редактор  создаст\n    файлы \"<имя домена>.mo.new\" в каждой папке языка\n    4. Файлы .mo.new имеют приоритет перед обычными  .mo  файлами  локализации.\n    Т.е. если в одном языковом каталоге присутствуют оба типа файлов,  редактор\n    загрузит для редактирования .mo.new\n    5. Для того, что бы движок  подгрузил  новый  файл  локализации,  требуется\n    изменить его расширение с .mo.new  на  .mo.  Обычно  это  перезапишет  файл\n    текущий файл локализации - поэтому следует заранее  сделать  его  резервную\n    копию\n    6. ВНИМАНИЕ! Следует соблюдать осторожность при  замене  старых  файлов  на\n    новые! Редактор не сохраняет комментарии и игнорирует дополнительный код  в\n    файлах локализации! В результате простая перезапись файлов  может  нарушить\n    нормальную  работу  подсистемы  локализации   движка!   Если   ваши   файлы\n    локализации  содержат  дополнительный  PHP-код,  то  они  требуют   ручного\n    вмешательства после обработке в редакторе!\n    7. Редактор локализаций корректно работает с константами внутри доменов\n    8. В редакторе есть возможность добавлять и удалять строки локализации\n[!] Межгалактические Врата: Интерфейс Врат  вынесен  на  отдельную  страницу  и\n    доступен с Обзора планеты (куда вынесен таймер готовности врат)\n    Все таймеры врат заменены на sn_timer\n[!] Экономика: ТМ смасштабирована в отношении 1 к 1000. Т.е. весь приход  ТМ  и\n    все цены в ТМ увеличены в 1000 раз\n[!] Тёмная Материя: За исследования начисляются опыт,  за  опыт  -  уровни,  за\n    уровни - ТМ. Таблица необходимого опыта  для  набора  уровней  эквивалентна\n    таблице опыта  за  постройки  (см.  /docs/readme.txt).  За  каждый  уровень\n    начисляется 1000 ТМ\n[!] Флот: Новый корабль - Гипертранспорт. Предназначен для  ТОП  игроков  и/или\n    скоростных Вселенных\n\n[+] Обзор  планеты/Обзор  Империи:  Таймер  очереди  строительства  зданий  под\n    иконкой планеты теперь переключается на  следующее  здание  в  очереди  при\n    окончании строительства текущего. Ранее таймер  показывал  только  прогресс\n    постройки первого здания в очереди\n[+] Список  планет:  На   изображения  планет   добавлен   индикатор   текущего\n    губернатора и его уровень\n[+] Инфо/Флот: Показываются текущие характеристики корабля (с учетом  наемников\n    и технологий)\n[+] Новости: Добавлена лента новостей на  страницу  обзора  планеты.  Выводятся\n    только  последние  непрочитанные  новости.  Количество  выводимых  новостей\n    настраивается на сервере (по умолчанию - 3)\n[+] Вселенная: В попап Альянсов добавлен его ранг\n[+] Экономика: Новая  настройка  сервера  \"Масштабировать  склады  от  скорости\n    добычи\". Настройка доступна в общих настройках сервера  в  разделе  \"Прочие\n    параметры\". По умолчанию возможность отключена\n[+] Строительство/Здания: В ссылке на уничтожение здания указывается количество\n    требуемых ресурсов и время\n[+] Новапедия: Включена страница информации для Артефактов. Добавлены  описания\n    всех стандартных ресурсов на русском и английском языках, а атк же включена\n    страница информации для них\n\n[~] ТМ: Изменения в ТМ текущего игрока отображаются сразу по факту\n[~] Луна: Имя создаваемой луны теперь не такое длинное\n[~] Список планет: Добавлена всплывающая подсказка на иконку губернатора\n[~] Поиск: Оптимизированы запросы поиска\n    При поиске Альянса поиск подстроки происходит одновременно в имени и тэге\n[~] Статистика: Изменен  расчет  статистики.  Теперь  в  тратах  каждый  ресурс\n    считается согласно курсу обмена.  Таким  образом  игроки  с  более  ценными\n    ресурсами получат больше очков\n[~] Вселенная: При обнаружении  планеты  с  отсутствующим  пользователем  в  БД\n    планета удаляется с отсрочкой 24 часа\n[~] Партнерка: Добавлено  ограничение  по  минимальному  количеству  ТМ,  после\n    которого   начинается   начисление   бонусов   реферралу    -    переменная\n    \"rpg_bonus_minimum\" в таблице \"config\"\n[~] Шпионаж: Оптимизирована процедура генерации шпионского рапорта\n[~] Фаланга:   Исправлен  и  переработан  алгоритм  работы  фаланги.  Добавлено\n    два исключения:\n      1. Флот, летящий с луны, фалангой не виден\n      2. Флот, летящий на задание \"Удержание\" фалангой не виден\n[~] Технологии:   Пересмотрены  технологии.  Устранены  противоречия  в  ветках\n    развития  (например,  ионный   двигатель   можно   было   исследовать   без\n    технологии). Технологии переупорядочены в более логичном порядке\n\n[%] Флоты: Добавлен патч для mysql-серверов со  включенным  STRICT_TRANS_TABLES\n    при отправке флотов\n[%] Локализация:   Добавлен  патч  для  серверов  с   неправильно   настроенной\n    кодировкой в HTTP-заголовке\n[%] Шпионаж: Корректно выставляется время шпионского рапорта\n[%] Локализация/EN: Исправлены ошибки с двумя l в \"Metall\" и \"Crystall\"\n[%] Исправлен   неработающий   просмотр   бана   при   входе   заблокированного\n    пользователя\n\n[@] SYS: Устаревшие функции заменены актуальными аналогами:\n      int_buildCounter => tpl_parse_planet\n      GetTargetDistance, GetMissionDuration, GetFleetConsumption =>\n        flt_travel_data\n      GetShipConsumption, get_ship_speed => get_ship_data\n      GetFleetMaxSpeed => flt_fleet_speed\n[@] SYS: В описании структуры кораблей (vars.php) данные о двигателях  вынесены\n    в  отдельный  массив  'engine'.  Теперь  можно   указывать   неограниченное\n    количество двигателей для апгрейда корабля\n[@] Апдейтер: Доработан апдейтер, что бы предотвратить зацикливание\n[@] Админка: В \"Утилиты\" добавлена  возможность  форсировать  только  последний\n    апдейт\n[@] Новости:  На  странице  новостей,  странице  Императора  и  обзоре  планеты\n    рендерятся одной процедурой и используют один  темплейт.  Индикатор  свежих\n    новостей теперь  ориентируется  на  дату  просмотра,  а  не  на  количество\n    новостей\n[@] ТМ: Исправлена лишняя ошибка в логах, если количество изменяемой ТМ равно 0\n    (например, при бесплатном рынке или офицерах)\n[@] ТМ: Изменение ТМ в переменной $user производится в теле rpg_points_change\n[@] БД: В таблице `users` убраны неиспользуемые поля. Изменены типы  нескольких\n    полей на более логичные.\n    Добавлено поле `ally_tag`\n    В таблицы `users` и `alliance` добавлены поля с текущими значениями ранга и\n    очков статистики\n[@] Админка/Настройки:   Состояние  все  чекбоксов  (включен/выключен)   теперь\n    определются в темлейте\n\n\n\nProject \"SuperNova.WS\" Release 30d4 \"Emailing phalanx research antiSSF\"\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n[!] Документация: Добавлен файл с английской версией  инструкции  по  установке\n    /docs/install-en.txt (google translated)\n[!] Локализация: Доступен выбор языка до входа в игру на страницах регистрации,\n    логина, восстановления забытого пароля.\n    Все доступные до входа в игру страницы (включая статистику, банлист, список\n    контактов, настройки вселенной итд) используют  выбранный  при  регистрации\n    язык.\n    При  регистрации  выбранный язык сохраняется в настройках пользователя\n\n[%] Отправка флотов: Исправлена ошибка на mySQL серверах со  включенной  опцией\n    STRICT_TRANS_TABLES\n[%] Сообщения:  Исправлена  ошибка  отправки  сообщений  от  системы  на  mySQL\n    серверах со STRICT_TRANS_TABLES\n[%] Локализация: Исправлена ошибка загрузки не-дефолтного языка\n\n\n\nProject \"SuperNova.WS\" Release 30 \"Emailing phalanx research antiSSF\"\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n[!] Сообщения:  Добавлена  возможность  пересылки  личных  сообщений  на  емейл\n    игрока. Возможность включается администратором сервера в настройках - опция\n    \"Разрешить  пересылку  ЛС  на  email\".  После  этого  в  настройках  игрока\n    появляются дополнительные опции для всех категорий входящих сообщений\n[!] Фаланга: Переписан вывод фаланги с использованием  функций  СН.  Теперь  он\n    выглядит аналогично списку  событий  флота  на  странице  \"Обзор  планеты\".\n    Алгоритм работы - почти оффовский:\n    1. Показываются все флоты, летящие от сканируемой планеты или же к ней\n    2. Полет A --> B\n       a) скан B => можно увидеть время прибытия флота\n       b) скан A => можно увидеть время возвращения флота (но не  его  прибытия\n       на B)\n    3. Возвращение B --> A\n       a) скан B => не видно ничего\n       b) скан A => виден возвращающийся флот\n    4. Особый случай: задание \"Передислокация\" A --> B\n       a) флот виден только на B, но не на A\n       b) после отзыва флот нигде не виден\n    5. Полнота информации о флотах зависит от уровня шпионажа (см. ниже)\n[!] Админка: С нуля создан интерфейс редактирования юнитов/ресурсов на  планете\n    - пункт меню  \"Редактировать\"  в  разделе  \"Планета\".  Он  доступен  членам\n    команды начиная с Оператора (authlevel=2) и выше\n[!] Исследования: Полностью переписан интерфейс Лаборатории\n    Очередь исследований приведена к стандартному виду\n    Обработка очереди исследований теперь производится  при  каждом  обновлении\n    страницы, а не только при входе в интерфейс Лаборатории\n[!] АнтиРМФ: Если флот атакующего уничтожен за один раунд, то:\n      1. Атакующий не получает отчета о бое\n      2. Флоты, находящиеся в удержании так и остаются на орбите\n\n[+] Антибашинг: Добавлена возможность настройки системы антибашинга на страницу\n    настроек сервера\n[+] Мировые константы: Добавлена информация о текущих настройках антибашинга\n[+] Обновление: Добавлена возможность форсировать обновление в случае проблем с\n    автоматическим   обновлением.    Возможность    доступна    в    интерфейсе\n    Администратора, пункт меню \"Утилиты\"\n[+] Сообщения:  Теперь  настройки  автоматических  уведомлений   включены   по\n    умолчанию для новых игроков\n[+] Верфь:  Переверстан  интерфейс   Верфи  и  Обороны.  Вид  очереди  построек\n    обновлен. Теперь они выглядят так же, как и очередь постройки зданий\n    Добавлена возможность удалить последний добавленный элемент из очереди\n    Кнопка \"Построить\" дублируется возле каждого юнита. Функционал  сохранен  -\n    по её нажатию будут построены все выбранные юниты\n[+] Обзор планеты: Вид очереди построек верфи  и  очереди  исследований  теперь\n    аналогичен очереди построек зданий\n[+] Обзор планеты/Фаланга: На  количество  отображаемой  информации  о  летящих\n    чужих флотах влияет эффективный уровень шпионажа (технология+наемник):\n    Меньше 4 - нет информации о летящем флоте\n    Больше 4 - видно общее количество кораблей во флоте и везет ли флот ресурсы\n    Больше 6 - виден качественный состав флота - т.е. сколько групп кораблей во\n    флоте и сколько кораблей в каждой группе\n    Больше 8 - видно точное количество ресурсов в трюмах кораблей\n    Больше 10 - виден количественный состав флота\n[+] Сообщения:  Изменена  цветовая  кодировка  сообщений.\n    Категории  сообщений переупорядочены\n    Добавлена  возможность  очистить  сообщения  определенной   категории,   не\n    открывая их - на случай переполнения  почтового ящика\n    Добавлена подсказка\n[+] Админка/Список планет: \"Список планет\", \"Список лун\" и  \"Активные  планеты\"\n    используют один и тот же код и шаблон. Список планет теперь показывает  тип\n    планеты, хозяина планеты (имя и ИД), а для лун - родительскую планету и  её\n    ИД\n[+] Чат:  Боевые  отчеты   теперь  преобразуются  в  ссылки.   Из   соображений\n    безопасности работают только ссылки на текущем сервере. По клику на  ссылку\n    боевой отчет открывается в новом окне\n[+] Сообщения: Изменена процедура  генерации  писем  с  уведомлением  о  боевом\n    отчете следующим образом:\n      1. Если бой  закончился  за  один  раунд  проигрышем  атакующего,  то  он\n         получает сообщение о том, что связь с флотом прервалась и не  получает\n         никакой дополнительной информации (включая ссылку на боевой отчет)\n      2. Теперь все участники боя (включая членов  САБа  и  хозяинов  флотов  в\n         удержании) получают одинаковые письма (кроме случая, описанного в п.1)\n      3. Уведомление о бое всегда содержит потери атакующих  и  оброняющихся  и\n         сведения о поле обломков\n      4. Сведения о вывозе ресурсов с планеты добавляются в  отчет  только  при\n         выигрыше атакующих\n      5. Уведомления теперь корректно окрашиваются  для  всех  участников  боя:\n         красным, если участник проиграл, зеленым - если выиграл, оранжевым - в\n         случае ничьи\n      6. Все числа в уведомлении теперь форматируются\n\n[~] Отладка: Теперь при отключенной  глобальной  отладке  не  генерируется  лог\n    запросов\n[~] Обновление: Автоапдейтер на время работы отключает отладку вне  зависимости\n    от глобальных настроек\n[~] Обновление: Оптимизирована работа апдейтера для апгрейда с 25-28 релизов\n[~] Обновление: Апдейтер использует собственные процедуры запросов к БД\n[~] Обновление:   Ускорен   автоапдейтер   при   повторном   запуске   на   уже\n    сконвертированных таблицах\n[~] Настройки:  Включение  защиты  планет   от   атак   доступно   только   для\n    Администраторов во избежание злоупотреблений\n[~] Флоты: Переупорядочен список кораблей\n[~] Новапедия: Отредактировано короткое описание большого транспорта\n[~] Офицеры: Уровень офицеров указывается и при максимальном уровне\n[~] Админка/Бан: По умолчанию включена галочка РО и выставлен срок бана в 3 дня\n    Бан и разбан объединены в один пункт меню и на одну страницу интерфейса\n[~] Админка/Меню: Объединены разделы \"Планета\" и \"Луна\" в раздел \"Вселенная\"\n[~] Админка/Меню: Под названием сервера добавлены часы серверного времени\n[~] Боевой отчет: Если в результате боя появилась луна, то в отчет  пишется  её\n    имя, а не имя планеты, на орбите которой она была создана\n[~] Вселенная:  Теперь  при  создании  луны  с  орбиты  списывается  количество\n    обломков, из которых сформировалась луна\n\n[%] Вселенная:  Исправлена  ошибка  создания  новой  планеты  при  регистрации,\n    возникающая при определенных настройках MySQL\n[%] Настройки:  Исправлена   ошибка  сохранения   настроек,   возникающая   при\n    определенных настройках MySQL\n[%] SQL: Исправлена ошибка с индексами в таблице users\n[%] Обслуживание: Исправлена ошибка при обслуживании таблицы Альянсов\n[%] Локализация/Английский: Исправлены баги в локализации\n\n[@] Логи: Изменения Тёмной Материи  вынесены  из  глобального  лога в отдельную\n    таблицу. Это  существенно  облегчило  поиск  неисправностей  на  сервере  и\n    подозрительных действий пользователей. Старые записи перенесены в отдельную\n    таблицу\n[@] БД: Все существующие таблицы сконвертированы в UTF8\n[@] БД: Все существующие таблицы переведены на InnoDB\n[@] sn_timer: Таймер корректно работает с очередью, в которой количество юнитов\n    больше 1\n[@] Сообщения: Переработаны файлы локализации\n[@] Общие: Устаревшая функция SYS_mysqlSmartEscape заменена на соответствующие\n[@] Общие: Устаревшие массивы  $pricelist,  $resources,  $reslist,  $sn_groups,\n    $CombatCaps заменены в коде на $sn_data\n[@] Обслуживание: Корректно пересчитывается количество аккаунтов в БД\n\n\n\nProject \"SuperNova.WS\" Release 29 \"Quest messaging\"\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n[!] Readme: Добавил раздел  \"Благодарности\".  Желающие  вычеркнуть  себя  могут\n    написать мне об этом в личку\n[!] Квесты: Добавлена подсистема квестов\n     1. Администратор сервера может самостоятельно добавлять  новые  квесты\n     2. В настоящий момент доступны квесты на постройку зданий  и  обороны,  на\n     исследование и на постройку кораблей (триггер срабатывает при  наличии  на\n     планете Х кораблей одного типа)\n     3. За квесты можно выставлять награду - определенного количество одного из\n     ресурсов: металл, кристалл, дейтерий или ТМ.\n     4. Доступ к конструктору квестов осуществляется из меню \"Квесты\"  страницы\n     администрирования. Создание квестов доступно только Администратору сервера\n     (auth_level = 3)\n     5.  Игрок  может  просматривать  список  доступных  квестов  и  их  статус\n     (выполнен или не выполнен)\n     6. По выполнению квеста игроку высылается письмо с уведомлением\n     7. Общее количество  и  количество  выполненных  квестов  видно  игроку  в\n     навбаре\n     8. Администратор может посмотреть выполненные квесты игрока  по  ссылке  в\n     его профиле (Поиск через админпанель)\n     9.   Игроки,   превысившие   условия   квеста,    автоматически    получат\n     вознаграждение при следующей проверке на  критерии  выполнения.  Например,\n     если целью квеста является постройка шахты 10го уровня, то  при  постройке\n     шахты  выше  9го  уровня  на  любой  планете   игрок   получит   квестовое\n     вознаграждение. То же самое верно и  по  отношению  к  уничтожению  шахты.\n     Однако, если при уничтожении шахты её уровень окажется ниже 10го, то игрок\n     вознаграждения не получит, хотя он уже и имел шахту 10го уровня\n[!] Сообщения: Полностью переписана  система  сообщений.  В  системе  произошли\n    следующие изменения:\n    1. Можно писать письма любому игроку! Форма создания нового  письма  теперь\n    корректно обрабатывает  имена  игроков,  введенных  в  строку  \"Кому\".  Эта\n    возможность доступна из списка  категорий  сообщений  по  ссылке  \"Написать\n    сообщение\" в самом низу таблицы категорий\n    2. В списке писем теперь работает чекбокс в заголовке. Клик на нем приведет\n    к выбору всех сообщений. Повторный клик - к снятию всех отметок\n    3. Добавлен дополнительный диапазон для удаления сообщений - \"Все сообщения\n    данной категории\". Внимание! В категории \"Все сообщения\" его выбор приведет\n    к полной очистке почтового ящика!\n    4. При  первом  открытии  страницы  создания  нового  сообщения  больше  не\n    выскакивают угрожающие красные надписи\n    5. Счетчик  сообщений в  навбаре  работает  без  задержек. Т.е. если  игрок\n    перешел  в  категорию  с  непрочитанными  сообщениями,  счетчик   изменится\n    соответствующим образом  сразу  же  после  перехода,  а  не  при  следующей\n    загрузке страницы\n    6. Множество мелких стилистических доработок\n    7. Оптимизированы алгоритмы работы подсистемы сообщений, а так же  почти  в\n    два раза уменьшен объем  передаваемой  информации  от  клиента  к  серверу.\n    Особенно это заметно при удалении большого количества сообщений\n[!] Сообщения:   Настройка  автоматических  уведомлений.  Теперь  в  настройках\n    пользователя можно отключить получения определенных категории сообщений.  В\n    этот список входят: Шпионские отчёты, Военные отчёты,  Отчеты  переработки,\n    Прибытие флота, Отчёты экспедиций, Сообщения очереди построек.\n[!] Настройки: Полностью переписана система настроек пользователя\n\n[+] Империя: Добавлено цветовое кодирование для производящих структур.  Уровень\n    производства, выставленный на странице \"Ресурсы\",  кодируется  цветом  фона\n    соответствующей ячейки: зеленый -  100%,  желтый  -  80-90%%,  оранжевый  -\n    70-50%%, красный - 40-10%%, цвет  фона  -  0%  или  структура  не  является\n    производящей. Пропорционально уровню производительности  меняется  и  длина\n    кодированной полоски\n[+] Интерфейс: Новый тип  сортировки  планет  -  по  общему  количество  полей.\n    Учитываются терраформеры (на планетах) и лунные базы (на лунах)\n[+] Боевой отчет: Локализован\n[+] Сообщения: Добавлена новый класс сообщений \"Сообщения Администрации\". К ним\n    относятся:\n    1. Сообщения системы квестов\n    2. Новости сервера\n    3. Сообщения Администрации\n    Уведомления этого класса НЕ МОГУТ быть отключены в настройках пользователя\n[+] Сообщения:  Восстановлена  функциональноксть  класса  сообщений  \"Сообщения\n    очереди построек\". К ним относятся:\n    1. Уведомления о  завершении  исследований.  Уведомление  высылается  после\n       входа на страницу исследований\n    2. Уведомления об окончании работы верфи на планете. Уведомление высылается\n       по окончании очереди строительства Верфи\n    3. Уведомление об окончалии строительных работ на  планете  (постройка  или\n       разрушение здания). Уведомление высылается пакетно в  полуавтоматическом\n       режиме.  Это  означает,  что  сообщение  генерируется  каждый  раз   при\n       обращении к планете (сканирование шпионажом  или  игроком,  переключение\n       активной планеты игроком итд). При  этом  в  сообщение  указываются  все\n       изменения, произошедшие на момент обращения\n    Уведомления этого класса могут быть отключены в настройках пользователя\n\n[~] Навбар: По клику на  счетчике  флотов  и  экспедиций  открывается  страница\n    флотов в полете\n[~] Список планет: Изменена цветовая  кодировка  полосы  застройки:  зеленый  -\n    менее 50% застройки, желтый - не меньше 50% и меньше 80%,  оранжевый  -  не\n    меньше 80% и меньше 100%, красный - 100% застройки\n[~] Список планет: На полосы застройки добавилось  застроенное  и  максимальное\n    количество полей на планете\n[~] Список планет: Сортировка учитывается в списке планет на  страницах  \"Обзор\n    планеты\" и \"Империя\"\n[~] Флоты: Переоформлена страница своза ресурсов. Теперь так же считается сумма\n    выбранных для своза ресурсов по каждой планете\n[~] Империя: Переформатирована страница Империи\n[~] Боевой отчет: Добавлено количество убитых корблей в предыдущем раунде.\n[~] Флоты/Сообщения: Приглашение в САБ теперь  относится  к  категории  \"Боевые\n    отчеты\", а не к категории \"Сообщения от игроков\"\n[~] Обзор планеты: Теперь если планет больше  5,  то  они  показываются  в  две\n    колонки\n[~] Мировые  константы:  Добавлен  вывод  информации  о  разешении  прокачки  и\n    разрешении удержания на слабом соаловце\n[~] Шпионаж: Изменены сообщения при уничтожении разведфлота\n\n[~] Навбар: Клик на индикаторе сообщений Администрации, Альянса или от  другого\n    игрока сразу открывает просмотр соответствующих сообщений\n[~] Вселенная:  Во  всплывающем  меню  на  поле  обломков  добавлена  индикация\n    количества летящих переработчиков игрока\n\n[%] Альянсы: Исправлена ошибка невозможности выхода из Альянса\n[%] Антибашинг: Исправлена ошибка в подсистеме  техобслуживания  из-за  которой\n    удалялись нужные записи из таблицы башинга и расчет волн сбрасывался\n[%] Вселенная: Исправлена ошибка  с  неправильным  наложением  иконки  летящего\n    флота, когда флоту отдана команда \"Обратно\"\n[%] Обзор планеты: Исправлена ошибка с добавлением лишнего события, когда флоту\n    отдана команда \"Обратно\"\n[%] Вселенная: Исправлена возможность  появления  фантомных  лун.  Существующие\n    фантомные луны удалены\n[%] JS: Исправлена ошибка в js_timer, приводящая к некорректной работе  таймера\n    после таймера с типом \"date&time with delta\"\n[%] Флоты:   Исправлена  ошибка  обработки  миссии  \"Транспорт\"  если   планета\n    назначения не существует\n[%] Флоты: Исправлена  ошибка,  позволяющая  атаковать  в  САБе  более  слабого\n    игрока\n[%] Вселенная: Исправлены ошибка  индикации  на  поле  обломков  чужих  летящих\n    переработчиков  и  ошибка   невозможности   послать   переработчики   через\n    всплывающее меню, если уже на то же поле летят чужие переработчики\n[%] Бой: В отчете правильно указывается количество захваченных ресурсов\n[%] Бой: Исправлены предупреждения, выдающиеся в случае,  когда  какой-либо  из\n    флотов пустой\n[%] Настройки: Исправлена  ошибка,  позволяющая  уйти  в  отпуск  когда  что-то\n    строится или исследуется на неосновной планете\n\n[@] SQL: Обновлен дамп БД до версии 29\n[@] Update: Изменен апдейтер так, что бы не  выдавать  ошибки  при  апгрейде  с\n    версии <26. Так же введена конфигурация времени блокировки базы апдейтером\n[@] Update: Добавлена процедура валидизации  таблицы  игроков  по  имени  и  ИД\n    Альянса. Так же добавлены constraint на эти поля\n[@] GIT: GIT теперь так же игнорирует SQL-файлы в каталоге бэкапа\n[@] Локализация: Немного оптимизирована локализация\n[@] Настройки: Изменена подсистема дополнительных настроек пользователя\n[@] Системное: sys_get_param_int_val теперь так же  обрабатывает  непрописанные\n    чекбоксы - те, которые возвращают \"on\"  и  \"off\".  Для  таких  значение  он\n    возвращаеет соответственно \"1\" и \"{$default}\"\n[@] Флоты: Добавлена возможность разрешения прокачки транспортировкой  ресурсов\n    от более слабого игрока  более  сильному.  Опция  \"Разрешить  прокачку\"  на\n    странице настроек сервера\n[@] Настройки:  Добавлена   опция  сервера,  разрешающая  удержание  на  слабом\n    со-аловце. Опция \"Разрешить  удержание  на  слабом  соаловце\"  на  странице\n    настроек сервера\n[@] Апдейтер: Исправлена ошибка добавления внешних ключей к таблице users\n\n\n\nProject \"SuperNova.WS\" Release 28 \"Chatting Diplomacy Alliance Bashing\"\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n[!] Чат: Полностью  переписан  внутренний  чат.\n      1.  Полностью  переписана  JS-часть.  В  частности  -  AJAX-вызовы   теперь\n      осуществляются  через jQuery\n      2. Добавлена  заплатка  для  корректной  работы  чата  в  глюкофоксе\n      3. Добавлена защита на стороне клиента от слишком частых обновлений\n      4. Полностью переработана PHP-часть чата\n      5. Корректно показывает заголовок в истории  чата  -  \"общий  чат\"  и  \"чат\n      альянса\" соответственно\n      6. История чата теперь грузиться в виде нормальной страницы СН, а не в виде\n      \"обмылка\", как раньше\n      7. Содержимое языкового файла chat.mo отфильтровано и влито в system.mo\n      8. Множество других добавлений и усовершенствований\n[!] Альянсы: Добавленв подсистема дипломатии\n    Информация о текущих дипломатических  отношениях  Альянса  отображается  на\n    странице информации об Альянсе и доступна для просмотра любому игроку\n    Глава Альянса может начинать переговоры и принимать предложения  от  других\n    Альянсов, выбрав пункт \"Переговоры\" в заголовке таблицы дипломатии. Там  же\n    можно сделать предложение об изменении отношений  другому  Альянсу.  Нельзя\n    сделать предложение  текущих  отношений  (т.е.  если  Альянсы  находятся  в\n    отношениях \"Война\" нельзя  опять  предложить  отношение  \"Война\")  В  общем\n    случае что бы изменилось отношения между Альянсами, другая  сторона  должна\n    потвердить предложение об изменении по ссылке \"Переговоры\", доступной главе\n    Альянса (исключения из данного правила изложены ниже).\n    Отношения между Альянсами бывают следующие:\n      1. Нейтралитет. Отношение  по  умолчанию.  Нет  никаких  ограничений  или\n      бонусов\n      2. Война. Отключается  система  защиты  башинга  между  членами  Альянса,\n      находящимися  в  состоянии  войны.   Автоматически   принимается   второй\n      стороной. Подробнее см.ниже в описании защиты от башинга\n      3. Мир. Рекомендуется выставлять этот статус  после  заключения  пакта  о\n      ненападении.  С  точки  зрения  движка  оно  ничем   не   отличается   от\n      \"Нейтралитета\" и нужно для информирования остального игрового  сообщества\n      о неких устных договоренностях - буде в  таком  информировании  возникнет\n      нужда. Альянсы вольны следовать или не следовать данной  рекомендации,  а\n      так же решать  -  хотят  они  оповестить  Вселенную  об  изменении  своих\n      отношений или нет\n[!] Антибашинг: Добавлена система защиты от башинга. Защита не  дает  отправить\n    больше флотов и волн, чем указано в правилах.\n    Настройки по умолчанию - в течении 24 часов 3 волны по 3 атаки не более  30\n    минут между атаками в одной волне.\n    Настроить систему защиты от башинга можно через таблицу `config`  -  группа\n    параметров fleet_bashing_*.\n    Установка параметра fleet_bashing_attack в  0  означает  полное  отключение\n    системы защиты.\n      1. Атакой считаются миссии: \"Атака\",  \"Совместная  атака\"  и  \"Уничтожить\n      луну\". Миссия \"Ракетная атака\" атакой не считается\n      2. Учитываются флоты в полете. Т.е. если игрок уже запустил две  волны  и\n      еще одна находится в полете - он больше не сможет запускать флоты.\n      3. Атаки засчитываются по факту - т.е.  если  полностью  отменить  волну,\n      находящуюся в полете, игрок сразу же  сможет  послать  на  планету  новые\n      флоты, не дожидаясь возвращения волны\n      4. Атаки учитываются  вне  зависимости  от  результата  (выигрыш,  ничья,\n      проигрыш)\n      5.  При  САБе  атака  засчитывается  ВСЕМ  нападающим  -  дабы   избежать\n      \"карусели\", т.е. когда несколько игроков по  очереди  запускают  САБы,  а\n      остальные к ним присоединяются\n      6. Все флоты одного игрока в одном САБе считаются как один флот\n      7. Если Альянсы находятся в  отношении  \"Война\",  защита  от  башинга  не\n      работает\n      8. Объявление войны не требует согласия. Это означает, что когда Альянс А\n      предложил  Альянсу  Б  отношение  \"Война\",  это  предложение  принимается\n      автоматически и отношения устанавливаются сразу для обоих Альянсов\n      9. Выход из состояния войны требует согласия обоих сторон\n      10. Выход из состояния войны обратной  силы  не  имеет!  Т.е.  если  было\n      объявлено перемирие  когда  планеты  одного  из  Альянсов  находятся  под\n      атакой, то флоты все равно долетят и совершат нападение  -  какое  бы  ни\n      было новое отношение между Альянсами  (если,  конечно,  атакующий  их  не\n      отзовет)\n      11.  А  вы  знаете  почему   такой   относительно   простой   вещи   (40+\n      человекочасов) нет на Оффе? А что бы денежки снимать за разбан!\n[!] Экономика: Изменена выработка энергии.\n      Во-первых  -  модификатор  скорости игры теперь не действует на энергию.\n      Во-вторых  -  температура   планеты   влияет   на   выработку   солнечных\n      электростанций\n      В-третьих - Производство энергии на  термоядерной  электростанции  теперь\n      считается по формуле оффа:\n        (Э) * 30 * (1,05 + (Е) * 0,01) ^ (Э)\n      где Э - уровень электростанции, Т - уровень энергетической технологии\n      В-четвертых - энергетическая технология  больше  не  дает  дополнительный\n      бонус к производительности электроэнергии\n      Выработка энергии изменена исходя из следующих правил:\n      1. Электростанция может поддерживать одну шахту и один синтезатор рудника\n      того же уровня (взято с оффа)\n      2. Формула выработки термоядерной электростанции взята с оффа\n      3.  Средняя  температура  на  планете  -  20  градусов  (это  планеты   с\n      минимальной температурой 0 градусов и максимальной 40 градусов). На такой\n      планете эффективность солнечной электростанции будет 100%\n\n[+] Интерфейс:  В  списках  летящих  флотов  к  количеству  кораблей  во  флоте\n    добавляется в конце знак \"+\" если флот везет ресурсы\n[+] Обзор Империи: Добавлена температура планеты. В колонке ИТОГО - минимальная\n    и максимальная среди всех температур\n[+] Новости: Добавлена возможность массовой рассылки новости всем игрокам\n[+] Экономика: Изменена схема работы МИС. Теперь МИС работает следующим образом:\n      1. По каждой планете вычисляется эффективный уровень исследования (ЭУИ) =\n      уровень лаборатории / (0,5 ^ уровень нанитки)\n      2. Планеты сортируются по эффективному уровню\n      3. Отсекаются планеты с уровенм лаборатории, недостаточным для проведения\n      данного исследования\n      4. Выбирается верхние (уровень МИС + 1) планет в списке и суммируется ЭУИ\n      этих планет\n      5. Получившееся число подставляется в формулу вычисления времени исследования\n      Следствия:\n      1. Нанолаборатория теперь увеличивают эффективность лаборатории только на\n      той планете, на которой они расположены\n      2. Время исследования теперь одинаково на  всех  планетах.  На  некоторых\n      планетах чуть больше, на некоторых - чуть меньше, но в среднем  -  лучше,\n      чем было раньше\n      3. Имеет смысл держать только (уровень МИС + 1) планет  с  лабораториями.\n      Остальные просто не будут подключаться.\n      4. Примечание к следствию 3 - собственно, так было и раньше -  все  равно\n      исследование могло проводиться только на одной планете\n[+] Навбар: Переработана ячейка сообщений\n[+] Навбар:  Добавлено  количество  флотов  и  экспедиций  в  полете  и  всего.\n    Количество флотов и экспедиций в  полете  интерактивно:  оно  автоматически\n    изменяется в соответствие с происходящими событиями - прибытие, возвращение\n    и окончание миссии флота (как они должны были произойти на момент  загрузки\n    страницы).  При  наведении  курсора  на  соответствующую  ячейку  всплывает\n    подсказка с описанием ближайшего события\n[+] Список планет: Справа от иконки планеты добавлены три колонки, показывающие\n    процент производительности шахт и  синтезаторов:  серый  -  шахта  металла,\n    голубой - синтезатор кристаллов, фиолетовый -  синтезатор дейтерия.  Высота\n    колонки пропорциональна проценту производства, а  фон  кодирует  диапазоны:\n    желтый - 80-90%, оранжевый - 50-70%%, красный - меньее 50%. На высоту и фон\n    колонки влияет ИСКЛЮЧИТЕЛЬНО процент производства, выставленный на странице\n    \"Ресурсы\"\n[+] Империя: Цифра производства ресурсов теперь  кодируется  цветом  аналогично\n    фону колонки прозиводства ресурсов (см. выше)\n[+] Свезти ресурсы: Добавлен JS-счетчик  общего  количества  свозимых  ресурсов\n    с учетом чекбоксов\n[+] Свезти ресурсы: Добавлено отображение количества необходимых  ресурсов  при\n    переходе по кнопке \"Свезти ресурсы\" со страницы постройки\n[+] Император: Добавлена дата регистрации: \"Император [Имярек] с [дата]\"\n\n[~] Экономика: Энергия считается более аккуратно\n[~] Экономика:  Изменена   формула  расчета  МИС.  Теперь  нанолаборатории   на\n    планетах, включенных в МИС так же работают\n[~] Вселенная: Правильно считается минимальная и максимальная температура луны\n[~] Вселенная: Стартовая планета теперь имеет температуру 0/40\n[~] Чат: Ники модераторов (auth_level=1) и  операторов  (auth_level=2)  в  чате\n    теперь тоже выделяются.  По  умолчании  соответственно  зеленым  и  красным\n    цветом\n[~] Новости: Чекбокс \"Разослать новость игрокам\" включен по умолчанию\n[~] НоваПедия: \"Ракетный двигатель\" переименован в \"Химический\", а \"Импульсный\"\n    - в  \"Ионный\".  Для  них  полностью  изменено  описание.  Так  же  изменено\n    соответствующе описание кораблей.\n[~] HTML: Исправлен хидер, что бы быть W3C-compliant\n[~] Флоты: Страница 0 -  перемещена  кнопка  \"Дальше\"  на  одну  строку  вверх.\n    Добавена кнопка \"Свезти ресурсы\"\n[~] Список  забаненных:  Полностью  переписан.  Список  теперь  сортируется  по\n    возрастанию даты бана - последние забаненные появляются  в  начале  списка.\n    Добавлено отображение разбанов\n[~] Чат: Добавлена трансляция в смайлы сочетания \":)\" - :smile\n[~] Произведено   разделение  между  \"Релизом\"   и   \"Версией\"   в   интерфейсе\n    пользователя. \"Релиз\" - это крупное обновление движка, выкладываемое в виде\n    одного файла в общий доступ. Версия - небольшое  обновление,  недостаточное\n    для  смены  номера  релиза.  Подробнее  об  этом  можно  прочесть  в  файле\n    /docs/html/developer.html\n\n[~] Флоты: Удалена ссылка на редактирование закладок со  страницы  1  -  теперь\n    закладки можно редактировать через левое меню\n\n[%] Рапорты: На странице просмотра  рапортов  максимальное  количество  в  поле\n    ввода кода выставлено в 32\n[%] Флоты:  Теперь   при  возврате  последнего  флота  в  САБе  САБ   корректно\n    уничтожается\n[%] Ракеты: Исправлена ошибка в процедуре ракетной атаки - технологии  щитов  и\n    брони были перепутаны местами\n[%] Экономика:  Исправлен   глюк  с  невычитанием  дейтерия  при  отрицательном\n    балансе\n[%] JS: Исправлен глюк в скрипте таймера из-за которого  не  отсчитывало  назад\n    ресурсы при переполненных складах\n[%] Империя: Исправлена ошибка отображения маскимального  количества  полей  на\n    луне\n[%] НоваПедия:   Исправлена  очепятка  из-за  которой  в  списке  кораблей   на\n    химических двигателях не показывался переработчик\n[%] Фаерфокс: Исправлен ВНЕЗАПНЫЙ отказ глюкобага отправлять сообщения  в  чат.\n    Тормозилла  -  так  поддерживать!  Ибо   то,   что   висит   можно   только\n    поддерживать.\n[%] Альянсы: Исправлены ошибки редактирования информации Альянсов\n[%] Статистика: Исправлено неправильно отображение даты  последнего  обновления\n    статистики при просмотре статистики Альянсов\n[%] Чат: Исправлена ошибка парсинга смайлов \":(\" и \";)\" (код последнего заменен\n    на \":wink:\" из-за непоняток с парсингом)\n[%] Админка: Исправлены сообщения \"Page not found\" в формах\n\n[@] БД: Изменена структура таблицы банов banned\n[@] Система:  Добавлена  процедура  ежедневного  обслуживания:  чистка  таблицы\n    башинга, чистка таблицы САБ\n[@] Админка: Проставлены права доступа к отдельным страницам в  зависимости  от\n    уровня.\n    1. Модератор (authlevel=1) имеет доступ к  следующим  страницам:  overview,\n    activeplanet, banned, changelog, planetlist, statbuilder, tools, md5enc. Он\n    может: видеть список  игроков  онлайн  и  их  активность,  видет  список  и\n    активность планет,  вручную  обновлять  статистику,  банить  и  разбанивать\n    пользователей\n    2. Оператор (authlevel=2) дополнительно имеет доступ к следующим страницам:\n    add_building,  add_def,  add_money,   add_moon,   add_research,   add_ship,\n    del_building,  del_def,  del_money,   del_research,   del_ship,   moonlist,\n    showfliyingfleets. Дополнительно к функциям модератора он может:  добавлять\n    и убирать на планетах здания, корабли, защиту, ресурсы; добавлять и убирать\n    технологии игрока; видеть все луны и добавлять луны к  планетам;  видеть  и\n    редактировать флоты в полете\n    3.  Администратор   (authlevel=3)   имеет   доступ   ко   всем   страницам,\n    включая     delete_user,     admin_darkmatter,     errors,     maintenance,\n    maintenance_ajax,   messagelist,    messall,    admin_chat,    paneladmina,\n    planetcompensate, settings, userlist. Дополнительно к функциям оператора он\n    может: добавлять и убирать ТМ у игроков; видеть  полный  список  игроков  с\n    IP-адресами;  удалять  игроков;  запускать   процедуру   обслуживания   БД;\n    просматривать и удалять личные сообщения; просматривать и удалять сообщения\n    чата; просматривать и  удалять  сообщения  системы  логов;  изменять  права\n    пользователей; изменять настройки игры; возмещать игроку  стоимость  затрат\n    на планету\n[@] Админка: Введена дополнительная защита от взлома. Теперь член команды  игры\n    не может назначить кому-либо уровень доступа, равный  или  больший  своего.\n    Таким образом через админку невозможно  назначить  второго  Администратора.\n    Однако это можно проделать напрямую в БД\n[@] Админка: Исправлено ложное  срабатывание  системы  определения  взлома  при\n    обновлении пользователем страницы  \"Флоты  в  полете\"  сразу  после  отдачи\n    команды \"Обратно\" последнему из текущих флотов.  Так  же  в  предупреждение\n    теперь логгится состав флот, который пытался вернуть пользователь\n[@] Разработка: добавлен каталог '.local' для облегчения  разработки.  Файлы  в\n    этом каталоге игнорируются GIT-ом, но при этом корректно подключают внешние\n    файлы для обработки и выполнения\n[@] Разработка:  добавлена  процедура  sn_db_perform().  Отныне  для   вставки\n    одиночных записей следует использовать только её. См. файл db.php\n\n\n\nProject \"SuperNova.WS\" Release 27 - We speak English! (2011-03-15 00:10)\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n[!] Движок:  Теперь  СН  может  размещаться  на  веб-сервере  на  любом  уровне\n    вложенности каталогов\n[!] Локализация: Добавлена английская локализация (с)  madmax1991.\n[!] Локализация: Серьезно переработаны файлы локализации: добавлена полноценная\n    информация о локализации (файл language.mo в каталоге локализации); удалено\n    множество неиспользуемых файлов;  несколько  маленьких  файлов  локализации\n    \"влиты\" в system.mo. Формат файла language.mo на текущий момент объявляется\n    финальным. Просьба всем локализаторам придерживаться этого формата\n[!] Включена возможность выбора языка для пользователей\n[!] Закладки: Полностью переписана система закладок. Теперь закладки хранятся в\n    отдельной таблице и не захламляют данные пользователя. Полностью переделано\n    редактирование закладок\n[!] Боевой отчет: Добавлена страница для просмотра боевого отчета по его  коду.\n    В меню в раздел \"Информация\" добавлена соответствующая ссылка\n[!] Интерфейс: Разнесены по разным страницам  отправка  флота  и  информация  о\n    флотах в полете. Теперь флот отправляется через пункт меню \"Флот на орбите\"\n    раздела \"Планета\", а информация о летящих флотах доступна через пункт  меню\n    \"Флоты в полете\" раздела \"Империя\"\n[!] \"Крейсер\" переименован в \"Эсминец\", а \"Линкор\" - в \"Крейсер\"\n\n[+] Админка: Язык игры по  умолчанию  теперь  выбирается  из  списка  доступных\n    языков\n[+] ЧаВо: В настройках сервера  добавлена  возможность  задать  URL  для  ЧаВо.\n    Добавлен соответствующий пункт в левое меню\n[+] Добавлен файл /README на английском языке.\n\n[~] Экономика/Ресурсы: По многочисленным просьбам добавлена колонка \"В час\"\n[~] Админка: Процедура  обслуживания  БД  теперь  удаляет  только  сообщения  с\n    неизвестным адресатом и сообщения, старше 30 дней\n[~] Регистрация: Пароль теперь так же указывается на странице  пост-регистрации\n    - на случай, если письмо с паролем не дойдет до адресата\n[~] Флоты: Немного изменил страницу 1 отправки флотов - теперь закладки, базы и\n    боевые союзы выводятся бок-о-бок в три колонки\n[~] Логин: Переработаны  меню  страниц  логина,  регистрации  и  восстановления\n    забытого пароля. Теперь оно одинаково  для  всех  страниц  и  кроме  старых\n    пунктов дополнительно включает блок ссылок логин/регистрация/восстановление\n    пароля, ссылку на FAQ, ссылку на новости сервера  (к  ним  теперь  разрешен\n    доступ незалогиненных/забаненных пользователей).\n\n[~] Интерфейс: Теперь если в настройках сервера отсутсвует какой  либо  из  URL\n    (адрес форума, ссылка на правила, ссылка на FAQ), то соответствующие пункты\n    меню и ссылки скрываются или не подсвечиваются. В дампе БД по умолчанию все\n    URL идут пустыми\n[~] Интерфейс: Переработана страница ТМ. Теперь если в конфигурации отсутствует\n    URL с подробностями покупки ТМ - информация о возможности покупки просто не\n    выводится\n\n[%] Экономика/Ресурсы: Добавлена проверка на корректный процент производства на\n    странице \"Ресурсы\"\n[%] Исследования: Теперь невозможно исследовать технологии во  время  постройки\n    лаборатории или нанолаборатории\n[%] Вселенная: Исправлена ошибка  с  неправильной  ссылкой  на  экспедицию  при\n    количестве планет в системе не равном 15\n[%] Флот: Закрыта уязвимость к передаче неправильных ИД  кораблей  на  странице\n    флота\n[%] Флот: Добавлена проверка на корректное время Экспедиций и Удержания\n[%] Флот: Теперь корректно выводится сообщени об ошибке при  попытке  отправить\n    флот на несуществующую планету\n[%] Флот: исправлена ошибка  создания  САБа  в  случае,  когда  летит  максимум\n    флотов\n[%] Флот: исправлена ошибка неудаления пустого САБа после атаки\n[%] Флот: Исправлена ошибка дублирования списка САБов\n[%] Альянсы: Исправлена ошибка вывода заявки  на  странице  управления  заявок.\n    Теперь  если  в  заявке  есть   переводы   строк,   то   заявка   корректно\n    форматируется\n[%] Админка: Исправлена баннерилка\n[%] Вселенная: Корректно выводится сообщение при попытке нападения на игрока  в\n    отпуске\n[%] Безопасность:   Исправлена  ошибка  невозможности  доступа   незалогиненных\n    пользователей к статистике, контактам итд\n[%] Реклама: Исправлена ошибка несохранения  параметров  рекламного  блока  при\n    перезапуске сервера\n\n[@] БД: Версия БД увеличена до 27. Обновлен дамп\n[@] Обновлены инструкции в файле /docs/install.txt\n[@] SQL: По умолчанию в дампе счетчик посещений - отключен, а игра - включена\n[@] Теперь можно отключить защиту слабых игроков, сбросив game_noob_factor в 0\n[@] Исправлено несколько участков кода, выдающих предупреждения в логи\n[@] Убрана запись сообщения в логи о постройке ПЗ/отмене очереди верфи\n[@] Чат: Изменилось выделение сообщений команды сервера. Теперь выделяется  ник\n    и  в  сообщениях  можно  использовать  все  стандартные  цвета.  Переменная\n    конфигурации     chat_admin_msgFormat      заменена      на      переменную\n    chat_admin_highlight. В ней можно использовать  HTML  коды.  Место  вставки\n    ника обозначается как '$1' - см. пример в БД\n[@] Изменена система слежения за  игроками.  Теперь  не  логгируются  неопасные\n    запросы (SELECT, START TRANSACTION,  COMMIT,  ROLLBACK).  При  логгировании\n    запроса так же записывается стандартный набор переменных\n[@] В процедуру апдейта добавлена очистка старого списка САБов\n[@] Альянсы: Страницы \"Настройка прав  доступа\",  \"Список  участиков  Альянса\",\n    \"Поиск Альянса\", \"Создание Альянса\", \"Управление заявками\"  переделаны  под\n    PTE\n\n\n\nProject \"SuperNova.WS\" Release 26 - Speed It Up! (2011-02-07 13:02)\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n[!] changelog.txt разделен на пользовательский и девелоперский. Все изменения в\n    процессе  разработки  новой  версии  вносятся  в   changelog_dev.txt.   При\n    подготовке релиза в changelog.txt переносятся только  финальные  изменения\n[!] Новый менеджер летящих флотов - теперь рядно-блокирующий,  тразакционный  и\n    кэширующий! Обновленный код на порядок уменьшает нагрузку на сервер за счет\n    отказа от табличной блокировки и встроенной системе  кэширования  запросов.\n    Система  \"событий\"  гарантирует  корректный  порядок  обработки  флотов  (с\n    точностью до секунды - предела текущей организации таблиц).  Целостность  и\n    валидность результатов обеспечена добавлением транзакций. Всё это позволило\n    уменьшить дискретизацию обработки флотов до 4х секунд на  серверах  с  300+\n    онлайна.\n\n[+] Свезти ресурсы: Теперь можно отдельно выбирать типы ресурсов, которые нужно\n    свезти\n[+] Свезти ресурсы: Добавлены колонки \"ВСЕГО\" - общее  количество  ресурсов  на\n    планете и \"Трюмы\" - общая грузоподъемность транспортного флота  с  цветовым\n    кодированием. Галочки в колонке \"ВСЕГО\" теперь не влияют на набор вывозимых\n    ресурсов, а используются только для облегчения выбора\n[+] Полностью переписана работа алгоритма миссии \"Шпионаж\". Теперь он полностью\n    соответствует оффовскому (расчет разницы  уровней,  влияние  зондов,  шансы\n    обнаружения шпионажа флотом итд). От старого кода осталась только генерация\n    рапорта\n[+] Скрипт обновления статистики завернут в транзакции.  Это  дало  50  кратное\n    увеличение скорости исполнения\n\n[~] Вселенная: Теперь отображаются  все  планеты  в  зависимости  от  настройки\n    game_max_planet, а не 16 штук\n[~] Восстановление пароля: Теперь  в  восстановлении  пароля  участвует  адрес,\n    указанный при регистрации\n[~] Автологин стал более параноидальным. Это позволило  избавиться  от  большей\n    части ошибок\n[~] Настройки: При попытке уйти в отпуск теперь  выдается  отдельное  сообщение\n    при летящих флотах и отдельное сообщение при постройке на планетах\n[~] Чёрный Рынок: Торговец ресурсами - Добавлена защита от повторного обмена\n[~] Сообщения: В навбаре теперь дополнительно отображаются количество сообщений\n    от других игроков  и  количество  сообщений  от  членов  альянса.  Цветовое\n    кодирование зависит от скина и такое же, как на странице сообщений\n[~] Навбар: Стартовое  время  и  количество  ресурсов  теперь  прописывается  в\n    темплейте. На медленных соединениях до конца загрузки  страницы  в  навбаре\n    будут не заглушки, а значения, акутуальные на момент запроса\n[~] Император: Все числа теперь показываются с разделителем тысяч\n[~] Настройки  пользователя:   Ко  всем  чекбоксам  добавлены  label   for   на\n    соответствующие  надписи.  Теперь  можно  кликать  на   надпись,   что   бы\n    переключить чекбокс\n[~] Здания: По окончании очереди построек страница автоматически обновляется\n[~] Верфь: Теперь при отмене очереди открывается та же страница верфи (флот или\n    оборона)\n[~] Обзор планеты: При выборе луны в списке колоний её иконка увеличивается  на\n    50%.  Изображение  планеты,  которой  принадлежит  луна,  так  же  остается\n    увеличенным\n[~] Обзор планеты/Обзор Империи: На превьюшку колонии добавлена иконка тележки.\n    Щелчок на неё открывает интерфейс своза ресурсов на данную планету/луну\n[~] Обзор планеты/Обзор Империи: В списке планет полоса  застроенности  планеты\n    перенесена под картинку\n[~] Обзор планеты: Иконка вражеской атаки на превьюшке луны составляет 70%  для\n    лучшей различимости при невыбранной луне\n\n[%] Настройки: Исправлена ошибка с невозможностью ухода в отпуск\n[%] Флоты:  Исправлены   ошибка  \"Не  хватает  топлива\"  при  отправки   миссий\n    \"Колонизация\" и \"Экспедиция\"\n[%] Чёрный Рынок/Торговец ресурсами: закрыт  эксплойт,  позволяющий  обменивать\n    ресурсы на ТМ\n[%] Чёрный   Рынок:     Теперь   невозможно   продать/купить   не-корабль    на\n    соответствующей странице Чёрного Рынка\n[%] Боевка: Корабли теперь не будут увозить отрицательные ресурсы с планеты\n[%] Экономика: Производительность теперь не может быть отрицательной\n[%] Экономика: Исправлена ошибка, когда при отрицательной добыче ресурсы  могли\n    уйти в минус\n[%] Таймер: JS-таймер теперь не будет считать ресурсы меньше 0\n[%] Закрыта дыра, позволяющая поставить  в  очередь  больше  зданий,  чем  есть\n    свободного места на планете\n[%] Устранена ошибка зацикливания перенаправлений при удалении колонии\n[%] Устранена ошибка неначисления опыта/уровня  при  атаках,  если  в  процессе\n    генерации рапорта произошел сбой\n[%] Вселенная: Исправлена ошибка неотображения названий планет с символов \"'\"\n[%] Своз ресурсов: перед свозом ресурсов не пересчитывались данные  о  ресурсах\n    на планетах, поэтому свозилось количество ресурсов меньшее, чем могло\n[%] Сообщения:   Теперь   при   появлении   нового   внутриигрового   сообщения\n    (возвращение флота, шпионаж, отчеты переработчиков итд), счетчик  сообщений\n    реагирует сразу, а не после обновления страницы\n\n[@] Версия БД увеличена до 26. Обновленный дамп\n[@] Теперь админ тоже не может  ходить  по  клиентской  части  при  отключенном\n    сервере во избежание порчи БД при бэкапе или апгрейде\n[@] Таблица  `errors`  влита  в   `logs`.   Новая   таблице   переформирована:\n    добавлены новые  и  переупорядочены  старые  поля  для  удобства  просмотра\n    человеком; добавлено поле с  дампом  переменных  для  дальнейшего  разбора.\n    Старые таблицы сохранены соответственно как `errors_backup` и `logs_backup`\n[@] Изменены коды  операции  со  статистикой.  Раньше  код  102  пересекался  с\n    операцией \"изменение Тёмной Материи\"\n[@] Добавлена   обработка  ситуации,  когда  после  установки  движок   сначала\n    запустили на пустой базе, а только затем залили в неё дамп\n[@] Вселенная: Добавлена обработка исключительной ситуации, когда у планеты нет\n    хозяина: в цикл просмотра системы, в выгрузку  кэша  в  темплейт,  в  самом\n    темплейте\n[@] Счетчик посещений теперь можно отключить из настроек сервера\n[@] Чёрный Рынок/Торговец ресурсами: Переработан  внутренний  алгоритм  работы.\n    Модуль теперь использует коды событий 9xx\n[@] Унифицированы алгоритмы и  файлы  постройки  флота  и  защиты.  Это  должно\n    полностью снять  проблемы  с  отрицательными  ресурсами  после  верфи  и  с\n    постройкой лишних единиц флота/защиты на верфях\n[@] Добавлена защита от выполнения файлов .INC вне основного кода\n\n\n[!] Информацию о предыдущих изменениях можно  посмотреть  в  полном  чейнджлоге\n    /docs/changelog_dev.txt"
  },
  {
    "path": "docs/changelog_dev.txt",
    "content": "Stay out of my shed\n\nОбозначения\n===========\n[!] Нововведение или важное изменение/New features or important change\n[+] Добавлено/New functions\n[-] Убрано/Removed\n[~] Изменено/Changed functions\n[%] Исправление - багфикс/Bugfixes\n[@] Эта информация будет интересна только админам и/или разработчикам/This information is only for administrators and/or developers\n[#] Модуль, не входящий в публичную версию\n[*] ToDo - см. todo.txt & todo.xls\n\n\n[*] Карты на конкретные ауткомы в Экспедициях\n\n[*] https://stackoverflow.com/questions/32378953/keep-the-middle-item-centered-when-side-items-have-different-widths\n        https://jsfiddle.net/wqLezyfe/2/\n\n[*] https://myip.ms/info/whois/<IP ADDRESS>#w\n[*] ну вот есть на карте кнопка ракетного залпа.а рядом кнопку Добавить в закладки. и что бы появлялись там координаты и имя игрока. при нажатии автоматом.\n[*] // TODO Check URL timestamp when checking signature\n    if (INITIAL_PAGE === 'worker' && SN::$gc->request->url->isSigned()) {\n[*] НАСТРОЙКА - покзаывать в списке планет Обзора луны отдельно от планет\n[*] В блоке планеты - передвинуть значок входящего флота вниз. Продублировать значок опасности внизу\n[*] $FIELD_COLOR\n[*] Опция \"Всегда показывать горизонтальный список планет\n    Отцентрировать горизонтальный список для Оперы\n\n\n2021-06-25 12:18:51 - ERROR\narray (\n  'type' => 1,\n  'message' => 'Maximum execution time of 300 seconds exceeded',\n  'file' => '/srv/www/vhosts/alpha.supernova.ws/classes/classCache.php',\n  'line' => 189,\n)\nNULL\n\n\n2021-06-25 12:23:55 - ERROR\narray (\n  'type' => 1,\n  'message' => 'Maximum execution time of 300 seconds exceeded',\n  'file' => '/srv/www/vhosts/alpha.supernova.ws/classes/classCache.php',\n  'line' => 189,\n)\nNULL\n\n\n[*] warning: Flying fleet handler works 3\n    Flying fleet handler works 4.0210 (> 3) seconds - skip rest. Processed 12 events. Last event: mission Экспедиция event Прибытие (1.0681s)\n    Flying fleet handler works 4.7837 (> 3) seconds - skip rest. Processed 4 events. Last event: mission Экспедиция event Прибытие (3.1765s)\n\n    ? Почему так долго\n[*] Stat worked too long - watchdog unlocked\n[*] Duplicate entry ' - когда пытаются ломать. Уменьшить длину поля для УРЛа - всё равно у меня длинного УРЛа не бывает\n[*] ERROR: Unknown column 'subject.raidswin' in 'field list'<br />/* tID 439<br />(DBAL\\db_mysql)DBAL\\db_mysql->doquery() - 'includes/db.php' Line 115<br />doquery() - 'includes/db/db_queries.php' Line 171<br />db_stat_list_statistic() - 'stat.php' Line 109<br /> */ SELECT @rownum:=@rownum+1 AS rank, subject.blitz_player_id as `id`, @rownum as rank_old, subject.raidswin as points, subject.blitz_name as name, subject.* FROM (SELECT @rownum:=800) r, game_blitz_registrations AS subject WHERE subject.user_as_ally is null ORDER BY subject.raidswin DESC, subject.blitz_player_id LIMIT 800,100;<br />\n\n\n2023-03-04 15:32:28\t\tSQL Error\tDeadlock found when trying to get lock; try restarting transaction<br />/* tID 521<br />(DBAL\\db_mysql)DBAL\\db_mysql->doquery() - 'classes/SN.php' Line 336<br />SN::db_query_select() - 'classes/SN.php' Line 432<br />SN::db_get_record_list() - 'classes/Planet/DBStaticPlanet.php' Line 39<br />Planet\\DBStaticPlanet::db_planet_by_gspt_safe() - 'classes/Planet/DBStaticPlanet.php' Line 59<br />Planet\\DBStaticPlanet::db_planet_by_vector() - 'includes/functions/eco_planet_update.php' Line 43<br />sys_o_get_updated() - 'classes/Pages/Deprecated/PageFleet5Gathering.php' Line 57<br />(Pages\\Deprecated\\PageFleet5Gathering)Pages\\Deprecated\\PageFleet5Gathering->flt_build_gathering() - 'classes/Pages/Deprecated/PageFleet5Gathering.php' Line 273<br />(Pages\\Deprecated\\PageFleet5Gathering)Pages\\Deprecated\\PageFleet5Gathering->viewPage5Gathering() - 'fleet.php' Line 240<br /> */ SELECT * FROM game_planets WHERE game_planets.`galaxy` = 8 AND game_planets.`system` = 250 AND game_planets.`planet` = 11 AND game_planets.`planet_type` = 1 FOR UPDATE<br />\n2023-03-04 15:32:29\t\tSQL Error\tDeadlock found when trying to get lock; try restarting transaction<br />/* tID 481<br />(DBAL\\db_mysql)DBAL\\db_mysql->doquery() - 'classes/SN.php' Line 336<br />SN::db_query_select() - 'classes/SN.php' Line 432<br />SN::db_get_record_list() - 'classes/SN.php' Line 631<br />SN::db_que_list_by_type_location() - 'includes/functions/eco_queue.php' Line 409<br />que_get() - 'includes/functions/eco_queue.php' Line 546<br />que_process() - 'includes/functions/eco_planet_update.php' Line 52<br />sys_o_get_updated() - 'common.php' Line 47<br />include() - 'buildings.php' Line 17<br /> */ SELECT * FROM game_que WHERE `que_player_id` = 7240683 AND (`que_planet_id` = 7485628 OR que_planet_id IS NULL) FOR UPDATE<br />\n\n2022-10-23 03:29:11\t\tSQL Error\tYou have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '{{blitz_alliance}} AS subject ON subject.id = sp.id_ally LEFT JOIN game_blitz_st' at line 1<br />/* tID 444<br />(DBAL\\db_mysql)DBAL\\db_mysql->doquery() - 'includes/db.php' Line 115<br />doquery() - 'includes/db/db_queries.php' Line 171<br />db_stat_list_statistic() - 'stat.php' Line 109<br /> */ SELECT @rownum:=@rownum+1 as rownum, subject.id as `id`, sp.total_rank as rank, sp.total_old_rank as rank_old, sp.total_points as points, subject.ally_name as name, subject.ally_tag, subject.ally_members FROM (SELECT @rownum:=0) r, game_blitz_statpoints AS sp LEFT JOIN {{blitz_alliance}} AS subject ON subject.id = sp.id_ally LEFT JOIN game_blitz_statpoints AS sp_old ON sp_old.id_ally = subject.id AND sp_old.`stat_type` = 2 AND sp_old.`stat_code` = 2 WHERE sp.`stat_type` = 2 AND sp.`stat_code` = 1 ORDER BY sp.`total_rank`, subject.id LIMIT 0,100;<br />\n2022-10-25 09:08:37\t\tSQL Error\tYou have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '\\\"' at line 1<br />/* tID 465<br />(DBAL\\db_mysql)DBAL\\db_mysql->doquery() - 'classes/SN.php' Line 336<br />SN::db_query_select() - 'classes/SN.php' Line 432<br />SN::db_get_record_list() - 'classes/SN.php' Line 631<br />SN::db_que_list_by_type_location() - 'includes/functions/eco_queue.php' Line 409<br />que_get() - 'includes/includes/eco_bld_structures.php' Line 115<br />sn_eco_build() - '' Line _UNDEFINED_<br />call_user_func_array() - 'includes/general/general.php' Line 52<br />sn_function_call() - 'includes/includes/eco_bld_structures.php' Line 16<br />eco_build() - 'buildings.php' Line 52<br /> */ SELECT * FROM game_que WHERE `que_player_id` = 7211448 AND (`que_planet_id` = 7269267) AND `que_type` = 6\\\"<br />\n\n\n#wip\n\n???\n SET profiling = 1;\n\nSHOW PROFILES;\n SET profiling = 0;\n\n\n[*] Bypass auth with secret key - for admins, when auth is failing\n\n[*] Flying fleet handler works 3.0368 (> 3) seconds - skip rest. Processed 65 events. Last event: mission Передислокация event Прибытие (0.0887s)\n    Добавить количество оставшихся эвентов? или флотов?\n    Когда берешь флоты/считаешь ивенты изначально - их можно не лочить. Просто потом брать один за другим и считать... наверное\n        Или нет - если кто-то вставил ОЧЕНЬ БЫСТРЫЙ флот\n            Подумать\n    Уменьшить время FFH и уменьшить промежутки между запусками?\n        - Типа, меньше локов?\n        - Надо вообще проверить что там с локами творится\n\n* [*] Проблема с бэкграундом в хроме - посмотреть\n\n* Запрашивать пароль при выходе в отпуск?\n\n[!] Server Instances\n    [*] Use same image/css resources ???\n    [*] Rid off D_SN_HTTP_AVATAR in templates - pass constants in variables\n    [*] Instancing on google servers\n    [*] Direct physical root in config file\n\n    [*] Url to server images - something like cdn.supernova.ws/images.supernova.ws\n\n\n    Отвязать путь/урл от физического пути - или задавать альтернативно в конфигурации\n        Потому что сейчас supernova.sn.ws указывает на beta.sn.ws и так работает\n        Сервер так же пользует для sn.sn.ws путь beta.sn.ws. Но если убрать каталог sn.sn.ws движок (?) сходит с ума и перекидывает на бету\n            Ну или кто-то сходит с ума\n\n\n* Бан по IP в игре\n* ИД игрока в чате по навердению курсора\n  * Возможно - дополнительное меню\n* Переписать кэшер целиком?\n* track all doquery() and replace with statics\n* Переписать все запросы через DbQuery::build()\n* shadowmute - игрок видит свои сообщения, но его никто не видит\n* Ограничение доступа к чату и ЛС в зависимости от ранга\n* Интеграция с форумом\n* Парсинг УРЛов в чате и ЛС - не парсится корректно УРЛы от админа, если УПЛ находится в конце строки и не закрывается пробелом\n    * Очевидно, надо добавить в РЕГЕКП символ окончания строки/линии как признак конка УРЛа\n* Проверить, что там с CI/CD\n\n* !!!!!!!!! 134 элемента грузится на странице чата!!!!!! На 2023-07-03 22:00\n    * Надо компрессировать элементы\n    * Начать с картинок\n        * Начать с меню - оно грузится всегда и везде\n        * В чате - смайлики пожать\n            * Могут быть проблемы с анимированными гифками\n        * В модулях\n            * картинки ивентов\n            * картинки мидалек\n        * Картинки планет (миниатюрные)\n        * Картинки юнитов (миниатюрные)\n        * Иконки\n    * https://medium.com/@robertcooper_rc/animating-a-sprite-sheet-with-css-ca15e2664\n    * sprite from animated gifs and generate css\n      * I am very early in experimenting with CSS sprites, but for me the biggest (and so far the only) benefit in using them is to reduce amount of request the client needs to send to server.\n      * I haven't tried, but I assume that for the same end result (in that optimization context anyway), one could add the animated gif to the HTML inline with data URI scheme: https://en.wikipedia.org/wiki/Data_URI_scheme\n    * https://stackoverflow.com/questions/30231943/smooth-and-fast-sprite-animation-css\n    * https://stackoverflow.com/questions/9139386/animated-gif-vs-spritesheet-js-css\n    * https://stackoverflow.com/questions/10186257/why-not-animated-gif-instead-of-animated-css-sprites\n    * https://stackoverflow.com/questions/37222609/sprite-multiple-gif-files-with-css-info\n    * https://www.w3schools.com/css/css_image_sprites.asp\n    * https://www.geeksforgeeks.org/how-to-use-flex-to-shrink-an-image-in-css/\n    * Offline CSS Sprites Image Generator\n      * 1) Smartsprites 0.3.2 This is a open source PHP CSS sprites Generator Source Code for Download\n      * http://www.tanila.de/smartsprite/index.php\n      *\n* 2) Open Source Java CSS sprites Generator Source Code\n* http://smartsprites.osinski.name/cgi-sys/defaultwebpage.cgi\n* 3) Free PHP Source Code of CCS Sprites Generator from Website-Performace.org\n* https://linux.softpedia.com/get/Internet/HTTP-WWW-/CSS-Sprite-Generator-39417.shtml\n* https://stackoverflow.com/questions/2428053/css-sprites-not-only-for-background-images\n* https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_images/Implementing_image_sprites_in_CSS\n\n* WebP\n  * https://web.dev/serve-images-webp/\n    * Changes\n        * Было:\n            * <img src=\"flower.jpg\" alt=\"\">\n        * Стало:\n            '''html\n            <picture>\n              <source type=\"image/webp\" srcset=\"flower.webp\">\n              <source type=\"image/jpeg\" srcset=\"flower.jpg\">\n              <img src=\"flower.jpg\" alt=\"\">\n            </picture>\n            '''\n  * https://css-tricks.com/using-webp-images/\n* Http2\n  * https://cheapsslsecurity.com/p/how-to-enable-http2/\n  * https://www.google.com/search?hl=en&q=lighttpd+http%2F2+support&oq=lighttpd+http%2F2+support&sourceid=chrome&ie=UTF-8\n\n\n* mod-multiserver: дроп-даун для выбора сервера.\n* объединение очередей, разделение очередей\n* Роботы-пауки:\n  * Отдельный аккаунт под роботов без логина\n  * Отключать чат\n  * Отключать личку\n  * Отключать вывод галактики\n* Антимультик: не пускать в игру, если залогинен аккаунт с того же АйПи/с той же сетки.\n* Режим \"Королевская битва\"\n    * Игроки наачинают в разных частях вселенных\n    * Вселенная уменьшается каждые сутки\n    * Кто последний - тот и выиграл\n    * С разрушением планет, включая столицу ?\n    * Вопросы\n        * Что делать с флотом без планеты? Удалять? Или \"Флот последней надежды\" - долетает и делает, что делает? Или можно перенаправить?\n* Реклама проекта - послать какую-нибудь мелочь блоггерам зарубеж с письмом\n\n\n\n* Флоты в полёте - добавить попап/надпись на \"откуда\" и \"куда\" с названием планеты\n* Добавить в статистику метки \"слабый/сильный игрок\"\n\n\n# WiP\n\nBEFORE PUTTING IT ON GITHUB!!!!\nCheck for module version - die() if it's not compatible with v46a46+\n\n\nTODO: Если пересчёт статы идёт раньше, чем положено - сохранять предыдущие значения и сравнивать с ними новый расчёт, а не с предыдущим расчётом\n\n\n* Не масштабируются попапы от размера фонта!\n\nI have solved this problem using img tags and using the object-fit and object-position properties in my css. Here's a sample of the html and css I used:-\nHTML\n<img src=\"<your image source>\" class=\"sprite-icon sprite-icon-1 \" />\nCSS\n.sprite-icon {\n  height: 20px;\n  width: 20px;\n  object-fit: none;\n}\n.sprite-icon-1 {\n  object-position: 0 0;\n}\n.sprite-icon-2 {\n  object-position: -20px 0;\n}\n\n* Чат моргает, если в Хроме выставлено обнуление кэша - он каждый раз перезапрашивает иконки\n\n* Режим сканирования - без таблиц, без картинок, совсем просто\n\n* Пираты?\n    * Застримлайнить UBE\n\n* TODO\n    * Remove unused menu image icons\n        * NB! Do not forget to copy them to use later as source in Spritify\n    * Switches to disable merging CSS and JS (javascript)\n    * Replace $sn_mvc with class in SN\n        * $sn_mvc['css_filenames'] - for modules to utilize\n    * Skin names should be used as BODY/HTML class names - including parents - to trigger CSS override\n        * Same for templates\n\n* Replace navbar items with sprites\n    * Delete unused navbar images\n        * NB! Do not forget to copy them to use later as source in Spritify\n* Replace smiles with sprites\n\n* В чате не показываются смайлики неактивных ивентов. А должны\n\n\nhttps://www.percona.com/software/database-tools/percona-toolkit\n\nconfig - index is hash\n\n@see changelog.dev.todo.txt\n@see changelog_todo_nocommit.md\n\n* Переделать интерфейс МО на планеты в колонках\n* По-максимуму избавится от полей BLOB/TEXT - особенно в `config`\n\n* Дропнуть во `fleets` индексы:\n    * `fleet_both`\n    * `I_test`\n    * `fleet_id`\nALTER TABLE `ogame`.`game_fleets`\nDROP INDEX `fleet_id`,\nDROP INDEX `fleet_both`,\nDROP INDEX `I_test`;\n\n* Если нет конфигов модулей - сыпится куча ошибок\n\n* ube_report_unit\n    * decimal 64 -> decimal 32 или даже 16-20 - 32 МАЛО!!!\n    * SELECT max(ube_report_unit_attack) FROM `game_ube_report_unit`\n        * 10154002942728 (14)\n    * SELECT max(ube_report_unit_shield) FROM `game_ube_report_unit`\n        * 4002480000000 (13) МАЛО 32!!!\n* SELECT max(unit_level) FROM `game_unit`\n    * 2730432019 (10) 2.730.432.019 2 млрд\n* SELECT\n  max(ube_report_outcome_fleet_resource_lost_metal),\n  max(ube_report_outcome_fleet_resource_lost_crystal),\n  max(ube_report_outcome_fleet_resource_lost_deuterium),\n  max(ube_report_outcome_fleet_resource_dropped_metal),\n  max(ube_report_outcome_fleet_resource_dropped_crystal),\n  max(ube_report_outcome_fleet_resource_dropped_deuterium),\n  max(ube_report_outcome_fleet_resource_loot_metal),\n  max(ube_report_outcome_fleet_resource_loot_crystal),\n  max(ube_report_outcome_fleet_resource_loot_deuterium),\n  max(ube_report_outcome_fleet_resource_lost_in_metal)\n  FROM `game_ube_report_outcome_fleet`\n  * 31929954315500\t24344582856500\t6892749829600\t0\t0\t0\t4230547540717\t6539453840652\t952727336533\t111274170527130 (15)   111.274.170.527.130 111 трлн ube_report_outcome_fleet_resource_lost_in_metal\n* SELECT  max(ube_report_fleet_resource_metal), max(ube_report_fleet_resource_crystal), max(ube_report_fleet_resource_deuterium) FROM `game_ube_report_fleet`;\n    * 10771609702592\t25796158896330\t1905454673066 (14) 25.796.158.896.330 25 трлн ube_report_fleet_resource_crystal\n\n* `confirmation` - чистить\n    * delete FROM `game_confirmations` where create_time < \"2024-01-01\";\n\n* `unit` - `unit_location_type`/`unit_location_id` - развязать, что бы работали констрейнты?\n\n\n* Экстраординарные (экзотические) ядра - можно только найти.\n* // if (defined('DEBUG_SQL_ONLINE'))\n\n* Get all highspots and activities once at start\narray (\n  0 => 'SELECT * FROM `game_festival_highspot_activity` WHERE `highspot_id` = 1445 ORDER BY `start`, `finish`, `id`',\n  1 => 'tID 16',\n  2 => '(DBAL\\\\db_mysql)DBAL\\\\db_mysql->doquery() - \\'includes/db.php\\' Line 81',\n  3 => 'doquery() - \\'modules/core_festival/classes/FestivalHighspot.php\\' Line 190',\n  4 => '(FestivalHighspot)FestivalHighspot->__construct() - \\'modules/core_festival/classes/Festival.php\\' Line 122',\n  5 => '(Festival)Festival->build_highspot() - \\'modules/core_festival/classes/Festival.php\\' Line 102',\n  6 => '(Festival)Festival->__construct() - \\'modules/core_festival/core_festival.php\\' Line 108',\n  7 => '(core_festival)core_festival->__construct() - \\'classes/Modules/ModulesManager.php\\' Line 119',\n  8 => '(Modules\\\\ModulesManager)Modules\\\\ModulesManager->loadModulesFromDirectory() - \\'classes/Modules/ModulesManager.php\\' Line 69',\n  9 => '(Modules\\\\ModulesManager)Modules\\\\ModulesManager->loadModules() - \\'classes/Modules/ModulesManager.php\\' Line 56',\n  10 => '(Modules\\\\ModulesManager)Modules\\\\ModulesManager->__construct() - \\'classes/Core/GlobalContainer.php\\' Line 210',\n  11 => '(Core\\\\GlobalContainer)Core\\\\GlobalContainer->Core\\\\{closure}() - \\'classes/Common/Pimple/Container.php\\' Line 113',\n  12 => '(Common\\\\Pimple\\\\Container)Core\\\\GlobalContainer->offsetGet() - \\'classes/Common/ContainerPlus.php\\' Line 21',\n  13 => '(Common\\\\ContainerPlus)Core\\\\GlobalContainer->__get() - \\'classes/core_auth.php\\' Line 184',\n  14 => '(core_auth)core_auth->__construct() - \\'includes/init.php\\' Line 196',\n  15 => 'require_once() - \\'common.php\\' Line 10',\n  16 => 'require_once() - \\'time_probe.php\\' Line 7',\n)\n* see changelog_todo_festival.txt\n\n* see changelog_dev.deadlock.sql\n\n* Написать в отчётах боя, что при участии в бое члена(-ов) АД-ии лом не выпадает!\n\n#ctv\n\n* Удалить из БД module_festival_39_highspot_796_lock\n* Мигрировать\n    * festival_highspot_puzzle_birthday_698\n    * festival_highspot_puzzle_christmas_tree_1138\n\n\n* https://dev.mysql.com/doc/refman/5.7/en/lock-tables.html#lock-tables-and-transactions\n* The correct way to use LOCK TABLES and UNLOCK TABLES with transactional tables, such as InnoDB tables, is to begin a transaction with SET autocommit = 0 (not START TRANSACTION) followed by LOCK TABLES, and to not call UNLOCK TABLES until you commit the transaction explicitly. For example, if you need to write to table t1 and read from table t2, you can do this:\n    SET autocommit=0;\n    LOCK TABLES t1 WRITE, t2 READ, ...;\n    // FLUSH TABLES WITH READ LOCK;\n    // FLUSH TABLES WITH WRITE LOCK;\n    ... do something with tables t1 and t2 here ...\n    COMMIT;\n    UNLOCK TABLES;\nhttps://thushw.blogspot.com/2010/11/mysql-deadlocks-with-concurrent-inserts.html\n\n\n\nУбрать все unique key - потому что это провоцирует gap_lock?\n\"UNIQUE KEY `I_festival_config_highspot` (`highspot_id`,`festival_id`,`config_name`) USING BTREE\",\n\n\nhttps://www.percona.com/blog/logging-deadlocks-errors/\npt-deadlock-logger u=user,p=password,h=host\npt-deadlock-logger --create-dest-table --dest D=test,t=deadlocks u=root,h=127.0.0.1\npt-deadlock-logger --daemonize --run-time=3000 --dest D=test,t=deadlocks u=root,h=127.0.0.1\n\nhttps://www.percona.com/blog/how-to-deal-with-mysql-deadlocks/\nhttps://rimzy.net/category/pt-deadlock-logger/\nhttps://topic.alibabacloud.com/a/monitoring-deadlocks-with-pt-deadlock-logger_8_8_31094333.html\n\n* Analyze all transactions methods\n\nAddType application/vnd.ms-fontobject .eot\nAddType font/ttf .ttf\nAddType font/otf .otf\nAddType application/x-font-woff .woff\n<IfModule mod_headers.c>\n    <FilesMatch \"\\.(ttf|ttc|otf|eot|woff|svg)$\">\n        Header set Access-Control-Allow-Origin \"*\"\n    </FilesMatch>\n</IfModule>\n\ncrowdin.com - переводчик всякого встроенного в ПО\n\n\n\n* Error in key name - `FK_festival_unit_hispot` instead of `FK_festival_unit_highspot`\n    * $updater->upd_create_table('festival_unit',\n        * \"CONSTRAINT `FK_festival_unit_hispot` FOREIGN KEY (`highspot_id`) REFERENCES `{{festival_highspot}}` (`id`) ON DELETE CASCADE ON UPDATE CASCADE\",\n    * $updater->upd_create_table('festival_unit_log',\n        * \"CONSTRAINT `FK_festival_unit_log_hispot` FOREIGN KEY (`highspot_id`) REFERENCES `{{festival_highspot}}` (`id`) ON DELETE CASCADE ON UPDATE CASCADE\",\n\n* Бан по IP\n    * Добавить в админку на страницу бана возможность банить по IP\n    * Добавить на страницу игрока историю его IP и возможность забанить один/все адреса\n\n* Версионирование отдельно для модулей\n\n\n\n* Расписать блокировку во флотах по функциям конкретных миссий\n\n* Убрать класс db_mysql_v5.php\n    * Заменить на db_mysql в описаниях\n    * Если стоит в конфигурации - заменить на db_mysql\n\n* ВНИМАНИЕ!!! ИЗМЕНЕНИЯ В СИГНАТУРАХ ФУНКЦИЙ НАДО ДЕЛАТЬ НА ВЕТКЕ МОДУЛЕЙ!!!\n    * Пройтись по db_deprecated.php\n    * Разобраться с функциями старта транзакций - вынести их в db_mysql\n\n* Перекинуть переменные транзакций в db_mysql.php\n    * И, возможно, в $debug - что бы там было на случай ошибки\n\n#765 - код ошибки для 46a147\n#832 - код ошибки для 46a147+ 2024-10-23-02-34\n#893 - 149-12 2024-10-24\n\n* Настройки уровня транзакции в базе (?)\n    * Не, в конфиге\n\n* /annonce.php - целая незалинкованная система объявлений!\n\n* Проверить - учитывается ли множитель скорости добычи в Экспедиции при добыче ТМ - должен уменьшать количество пропорционально рейтам\n\n* sys_o_get_updated - перебить все вызовы что бы уже были ИД пользователя и планеты\n    * db_user_by_id() - убрать\n        * SN::db_get_record_by_id - убрать\n        * db_user_list - убрать\n\n\n* Для Gather - балансировка ништяков между игроками\n    * Не давать тому, у кого много\n        * Кроме тех случаев, когда он один на сервере\n            * Или остальные не собирают ништяки\n    * Сделать сначала лог для отладки\n* Капча\n    * Для аякс-вызовов\n        * Не запускать для них капчу\n        * Сделать подпись с родной страницы\n            * С секретным ключем и таймстампом - что бы нельзя было перехватить пакет и хуярить его постоянно и нужно было в браузере рефрешить интерфейс\n        * Энкодить Гет и Пост в УРЛ\n\n* Cron\n    * https://habr.com/ru/articles/255245/\n    * https://code.tutsplus.com/managing-cron-jobs-with-php--net-19428t\n\n* Развоз ресурсов - сделать горизонтальным\n\n* PlayerStatic::getPlayerProduction - прописать правильные коэффициенты конверсии ресурсов\n* admin/planet_compensate.php * Добавить уведомление, что юниты в очереди и нанятые наёмники не будут компенсированы\n* Шадоумьют в чате\n* Поставить количество Удержаний в соответствие со Складом Альянса. Ну и переименовать его\n* На странице выбора планеты сделать отключение функции \"Далее\", если точки отправки и назначения совпадают\n* Переместить записи о ракетах в таблицу флотов. Нужно много работы - что бы ракеты не обрабатывались как флоты!\n* Поменять работу шпионажа:\n    * Сначала - постройки\n    * Потом - ресурсы (?)\n    * Потом - технологии (??)\n    * Потом - оборона (???)\n    * Потом - флоты на орбите (????)\n* Разобраться с масштабированием иконок спрайтов в навбаре\n* На странице отправки флота показывать так же отправленные ресурсы и приписанного Капитана\n* Добавить во Вселенную на пустое место действие \"колонизировать\"\n* Переделать все стандартные сообщения в ЛС на генерацию при просмотре - что бы использовался язык просматривающего, а не того, кто сгенерировал сообщение\n* Добавить на странице \"Флоты в полёте\" на кнопку \"Вернуть флот\" попап, в котором будет идти таймер, когда флот вернется на планету старта, если её нажать\n* Убрать ссылки на планету назначения, старта и владельца планеты назначения из таблицы `fleets`\n* sys_o_get_updated() распотрошить на подфункции\n    * Сделать более мелкие шаги\n    * Обсчитывать производство ресурсов в зависимости от очереди\n        * Юнит построился/разрушился - это может быть производящее здание\n        * Значит надо обсчитать производство на момент окончания постройки/рарушения юнита\n        * Продолжить очередь\n    * Обсчитывать производство на планете с учётом изменений рейтов игры\n        * Логгировать изменения рейтов в отдельную таблицу\n            * Привязывать запись в логах к Фестивалям, что бы можно было создать записи в будущее на хайспоты, а если составитель ошибся и удалил хайспоты - удалить и привязанные рейты\n            * Делать запись при изменении рейтов игры через админ-консоль\n        * Считать по этим реперным точкам производство ресурсов и построек\n    * Возможно, обсчитывать все планеты раз в сутки/неделю/месяц?\n* Не \"прятать\" ресурсы в постройки/юниты/исследования\n    * Это надо как-то придумать, что бы нормально работало прогнозирование остатков ресурсов\n        * Галочка \"строить с переполнением\" - что бы можно было набить очередь, а позже подвести ресурсы?\n* Переписать экспедиции под объекты\n* Версия UBE4.2 - переделать боевую дату под работу с объектами\n* Ошибка в боевом отчёте: если 10 раунд заканчивается полным поражением нападавшего, то результат должен быть \"проигрышь\", а не \"бой закончился ничьёй\", как сейчас\n* Софт-делете сообщений\n* Логгирование результатов экспедиций\n* Расписать по константам\n  const DB_TRANSACTION_SHOULD_NOT_BE = null;\n  const DB_TRANSACTION_SHOULD_BE = true;\n  const DB_TRANSACTION_WHATEVER = false;\n* Расписать doquery()\n* На странице покупок Премиума, Капитанов, Массовы операций - при нажатии на кнопку лочить все остальные\n* В переводах позаменять font color на span class\n    * <font color=\"red\"> => <span class=\"error\">\n    * <font color=\"lime\"> => <span class=\"success\">\n* Переписать TSingleton с использованием __callStatic()\n* Избавиться от глобальных переменных:\n    * $sn_mvc - заодно переделать её в класс\n    * $user\n    * $planet\n* `unit.unit_location_id` - расписать по локациям: `user_id`, `planet_id`, `fleet_id`, убрать `unit_Location_type`\n* Переместить `design/cache` => `cache/css`\n* cache/.gitkeep\n* Обработка на случай если каталога таки не существует\n* //   @import url(\"modules/core_festival/design/templates/OpenGame/march8.min.css?{C_var_db_update}\");\n  //   search for other imports\n* FleetEvent->srcPlanetOwnerId - вообще неправильно и надо переписать!\n\n* При создании списка эвентов флотов - кэшировать уже прочитанные планеты, что бы не читать второй раз прочитанное\n    * Не выбирать планеты > maxplanet - там всё равно ничего нет\n        * Это вообще можно поставить в базовые процедуры\n* FleetDispatch пересекаются?! (\\'fleet_update_run_lock\\', \\'2025-05-07 19:43:28\\') - должен быть до миллисекунд\n    * А вообще - это должен быть флаг?\n* https://dev.mysql.com/doc/refman/5.7/en/timestamp-lookups.html\n    * Либо вешать индексы на все TIMESTAMP поля - либо писать сразу UNIXTIME\n    * И избавляться от всех полей/значений с датой/временем в виде строки...\n* Переделать Vector::__constructor - добавить два статических метода для инициализации вектора из вектора и из флота\n    * Vector -> Coordinates\n\n* Сделать ракетную атаку частью башинга\n* Таймаут на вступление/выход из Альянса. И на создание нового Альянса после роспуска\n\n* Добавить в чате в дропдаун стилей болд италик и страйкаут\n\n* Вообще-то сейчас лучше разгружать код SN от хуйни, а не делать второстепенные изменения.\n    * Так и запишем\n\n\n* Когда будет переход на 7.4:\n    * Заменить mt_rand() на random_int()\n        * https://php.watch/articles/testing-php-rand-functions\n        * https://www.php.net/manual/en/function.random-int.php\n            * !!! It throws an Exception if it could not yield a senescence of numbers with sufficient randomness, and does not fallback to any insecure RNG source.\n* Переход на 8.0\n    * Насыпали сахара: object?->method()\n        * https://www.php.net/manual/en/language.oop5.basic.php#language.oop5.basic.nullsafe\n* 8.1.0\n    * Closure::fromCallable - типа, можно генерить кложуры из методов внутри класса?!\n        * https://www.php.net/manual/en/closure.fromcallable.php\n        * Не, смотреть вот это: https://www.php.net/manual/en/functions.first_class_callable_syntax.php\n\n* SELECT @@tx_isolation;\n* УРЛ на флоты искать в TaskDispatchFleets::task()\n\n37495953 0x23C2491\n7206490 0x6df65a\n\n2025-05-07 21:42:52 6455509\n*** (1) TRANSACTION:\n(1) (Core\\Worker)Core\\Worker->__call() - 'includes/init.php'@267\nREPLACE INTO `delta_config` (`config_name`, `config_value`) VALUES ('fleet_update_last', '2025-05-07 21:42:52')\n*** (2) TRANSACTION:\n(Core\\Worker)Core\\Worker->__call() - 'includes/init.php'@267\nREPLACE INTO `delta_config` (`config_name`, `config_value`) VALUES ('fleet_update_last', '2025-05-07 21:42:52')\n*** WE ROLL BACK TRANSACTION (1)\n\n\n* Deadlock found when trying to get lock; try restarting transaction\n    /* 2025-05-12 04:00:01.597516\n    StatUpdateLauncher::scheduler_process() - 'includes/init.php'@444\n    StatCalculator::sys_stat_calculate() - 'classes/StatUpdateLauncher.php'@72\n    db_user_list() - 'classes/StatCalculator.php'@98 */ SELECT * FROM game_users FOR UPDATE\n* Deadlock found when trying to get lock; try restarting transaction\n  /* 2025-05-12 00:32:32.747069\n  (Core\\Worker)Core\\Worker->__call() - 'includes/init.php'@267\n  call_user_func_array() - 'classes/Core/Worker.php'@51\n  {closure}() - ''\n  (Fleet\\FleetDispatcher)Fleet\\FleetDispatcher->flt_flying_fleet_handler() - 'includes/init.php'@253\n  */ REPLACE INTO `delta_config` (`config_name`, `config_value`) VALUES ('fleet_update_last', '2025-05-12 00:32:32');\n\n\n\n* \"Экстракция ТМ/Компрессия дейтерия\" - шанс превратить часть найденного дейтерия в ТМ\n    * Опция в экспе? Абилка? Юнит?\n* Роллить экспу, даже если флот отзывается из экспедиции более чем через час пребывания\n    * А не будут ли злоупотреблять? Ограничить минимальное время 1 часом\n* Бонус или пити? Специальный триггер по накоплению статистики в экспедиции:\n    * Статистику можно копить по:\n        * Количество проведенных часов - могут злоупотреблять отправляя мелкие флоты для накопления часов, а потом крупный - для гарантированного выигрыша\n            * Способ борьбы - рандомайз момента, когда срабатывает триггер\n        * Количество утраченного металла\n            * Ну тут не вижу злоупотреблений\n        * Количество металла, побывавшего в экспедициях\n        * Масштабировать в зависимости от скорости добычи!\n    * Бонус - удваивать добычу?\n        * Разные майлстоуны - 1.000, 10.000 итд - разные коэффициенты?\n    * Пити:\n        * отменять негативное событие - опять же, что бы не злоупотребляли\n\n* SELECT NOW(6) - microseconds precission, timestamp(6)\n    * You need to be at MySQL version 5.6.4 or later to declare columns with fractional-second time datatypes.\n        * И тест и прод - ОК\n* Перепрятать в класс функции типа\n    * sn_sys_handler_add($functions, $this->manifest['functions'], $this);\n    * sys_handler_add_one($functions, $functionName, $callable, static::class, '');\n* Добавить флаги в описание экспедиций ???:\n    * Может ли быть найден флот\n    * Может ли быть найдена ТМ\n    * Может ли быть потерян флот\n\n* Экспы считаются ОЧЕНЬ долго!\n    * Flying fleet handler works 4.8590 seconds (> 1) - skipping rest. Processed 1 / 58 events. Last event: mission Экспедиция event !TERMINATED BY TIMEOUT! (4.8373s)\n    * Flying fleet handler works 5.2726 seconds (> 1) - skipping rest. Processed 3 / 50 events. Last event: mission Экспедиция event !TERMINATED BY TIMEOUT! (5.2533s)\n    * А это - удержание или экспа внутри?\n        * Flying fleet handler works 5.1873 seconds (> 1) - skipping rest. Processed 6 / 36 events. Last event: mission Удержание event !TERMINATED BY TIMEOUT! (5.1473s)\n    * Выяснить - это ракеты или что вообще?\n\n* https://mariadb.com/kb/en/mariadb-dumpslow/\n\n\n[*] Экспедиция\n    - Новый Наёмник - Экспедитор / Перебежчик (?) - позволяет находить корабли других рас в экспе\n    * Прибавлять количество экспоочков в зависимости от часов (?)\n    * Экспа-квазар: потерять часть (?) флота и привезти больше ТМ\n        * Черная Дыра - сбросить ресурсы и получить ТМ\n            * При виде такого люки ваших кораблей непроизвольно расслабились и все ресурсы улетели в Дыру. Зато получили ТМ (или не получили)\n\n\n* И вообще - объединять Астрокартографию и Колонизацию в одну теху по примеру Огейма - это был идиотизм\n    * Надо разнести обратно - но уже в новой версии\n* Впрочем, вы так активно рассказываете, как у вас много ТМ - что я реально задумался перепроверить свою матмодель.\n    * Чем больше ТМ - тем меньше её будет в экспах ?\n\n* Разрешить отправлять \"Транспорт\" и \"Передислокацию\" на пустые координаты\n    * Тогда все проверки на возможность выгрузки ресурсов/приземлению флота проводить уже в Диспетчере Флотов\n        * ВООБЩЕ=ТО это ВСЕГДА надо проверять! А ВДРУГ Планета поменялась? (телепортация, захват итд)\n\n* Обломки должны быть отдельной сущностью по типу Планеты и Луны\n* Советники - на игрока\n* Губернаторы - на планету, Секретарь - второй губернатор\n* Возврат боевого флота не отменяет башинг? Переписать, кстате\n* Тёмная энергия - производится в одном месте, но доступна везде, где не хватает\n    * Как ЭТО считать?!\n* Позволять упаковывать ресурсы в корабли на орбите?\n    * Специальный перк \"Плюшкин\"?\n    * Специальная технология?\n    * Спеиальный советник?\n    * А игроки не лопнут?\n        * А давать только мелким?\n* Блюющий смайлик вместо кофе - \"Поставить автомат с разливным нескафе\"\n    * Снимать деньги?\n\n#ctv\n\n#ctv MVP3\n\n* Логи экспедиций - и давать ссылку на них в сообщениях\n    * Чистить логи\n* Логи ракетных атак - и давать ссылку на них в сообщениях\n    * Чистить логи\n* Избавиться от require_once(SN_ROOT_PHYSICAL . 'includes/includes/flt_mission_explore.php');\n\n* Проаннотировать в модулях ссылки на функции через @see\n\n#ctv MVP2\n\n* Переключатель в конфигурации - что бы логировались и удачные циклы диспатчера\n    * Ну, посмотреть как там количество IPR\n\n* Буксир - увеличивает скорость других юнитов (?) Может захватывать более крупные корабли в экспе (?)\n\n* Переделать PM - что бы не хранило данные в user и корректно считало количество непрочитанных сообщений\n\n* Проверить описание МПР - возможно это больше не соответствует реальности!\n    * 'description' => 'Межпланетные ракеты уничтожают защиту противника. Уничтоженные межпланетными ракетами оборонительные сооружения больше не восстанавливаются.',\n    * Сделать автоматическую постройку ракет?\n        * За ТМ?\n        * За ММ?\n        * Отдельный Губернатор?\n            * Или добавить в функции MRC_FORTIFIER?\n            * Наверное, лучше отдельный губер\n            * Добавляет Х МПР в очередь? Бесплатно? Сверх лимита обороны?\n\n* Сделать функцию, которая считают рандом по процентам по основанию (1 млн, например) и возвращает [0,1] - нормализированный коэффициент\n    * Использовать её в Экспедициях\n\n#ctv MVP1\n\n* Зающать FleetRowObject в экспедициях\n\n* Конфигурировать макс. количество ТМ через настройки сервера\n    * Возможно, конфигурировать так же остальные шансы\n\n* Переписать `MissionEspionage` с использованием `FleetDispatchEvent` вместо `MissionData`\n\n* Опция настроек \"снять ограничение ширины\" - для \"высоких\" экранов (автоматически?) и для тех, кто любит пошире\n* Мигрировать все опции из таблицы юзеров\n* Добавить картинки к айтемам в ивенте ДР СН\n* Вариант 2: В экспу отправляли флотов больше, чем то позволяют техи - пофиксить как-то\n\n* Подмодули Git - использовать для модулей. Может еще что-то вынести тудой\n    * https://git-scm.com/book/ru/v2/%D0%98%D0%BD%D1%81%D1%82%D1%80%D1%83%D0%BC%D0%B5%D0%BD%D1%82%D1%8B-Git-%D0%9F%D0%BE%D0%B4%D0%BC%D0%BE%D0%B4%D1%83%D0%BB%D0%B8\n    * Нет - проще хостить репо внутри каталога `modules`\n\n* Пройтись по TODO\n\n* Придумать как автоматически обновлять версию модулей\n\n* Заюзать везде array_column()\n\n* Переписать БД под Laravel/Illuminate?\n\n\n\n\n#ctv MVP0\n\n* mailgun\n* ресолвер относительных УРЛов: resolve('phalanx') -> '/phalanx.php'\n    * то же сделать для каталогов изображений и JS\n        * Т.е. позже можно будет ресолвить SN_PATH_AVATAR\n        * И добавить путь для изображений\n        * Т.е. resolve() может вернуть и каталог\n* Вернуть\n    * [~] МПР Теперь можно атаковать ракетами свои же планеты. Таким образом можно избавится от излишних ракет или перехватчиков в шахтах, а так же уничтожать свои защитные сооружения\n* ? Перестало работать отключение чата? Или просто не появляется сообщение?\n* Сделать отдельный флаг \"canBeFoundInExpedition\" - что бы не проверять дополнительные условия, которые вообще могут быть в модулях\n* Починить иконку \"Премиума\"\n* Перенести логгирование запросов в db_sql_query();\n* Перейти на Eloquent?\n\n* Какой-то исход влияет на Экспу\n    [22.05.2025 16:46:02] Сингулярность[БАС] Меркурий> Ну вот что это за хуйня?!\n    Flying fleet handler works 4.9943 seconds (> 1) - skipping rest. Processed 0 IPRs, 1 / 64 events. Last event: mission Экспедиция event !TERMINATED BY TIMEOUT! (4.9560s)\n    [22.05.2025 16:46:10] Сингулярность[БАС] Меркурий> Я же уже переписал Экспедиции!\n    [22.05.2025 16:46:26] Сингулярность[БАС] Меркурий> Нужен лог, что бы понимать, при каком исходе это происходит...\n    [22.05.2025 16:47:28] Сингулярность[БАС] Меркурий> Так-то диспетчер за 1 секунду обычно обрабатывает 100-150 событий\n    [22.05.2025 16:48:05] Сингулярность[БАС] Меркурий> И уже на МПР не спишешь - я их тоже переписал, они не должны тормозить основной луп-луп-залуп игры\n    [22.05.2025 16:54:31] Сингулярность[БАС] Меркурий> Какой-то из исходов грузит движок на 500%\n    [22.05.2025 16:54:37] Сингулярность[БАС] Меркурий> Надо понять - какой\n    [22.05.2025 16:57:42] Сингулярность[БАС] Меркурий> (Ivash[Казаки])Пойми, я когда переписывал экспу всё глазками отсмотрел. Нет там явных косяков. Т.е. там какой-то скрытый косяк, который проявляется при специфических условиях. Возможно даже не связанных с Экспой\n\n* Смерджить таблицы game_festival_unit_log и game_festival_unit\n    * Проверить индексы\n\n* дропнуть:\n    * trunk_submodules\n    * trunk_modules\n    * репо модулей на GitHub\n\n* SELECT sum(length(message)) FROM `game_chat` where `timestamp` < UNIX_TIMESTAMP(\"2025-09-20\") and `timestamp` > UNIX_TIMESTAMP(\"2025-08-20\")\n* https://cloud.google.com/translate/docs/reference/rest\n* https://cloud.google.com/translate#pricing\n\n* Переделать сообщения в чате на span - что бы они нормально копировались из игры\n\n+ пятница 13е\n\n* После ивента собрать:\n    * по логам ТМ - сколько раз летали в экспу\n    * кто был активен в этот период - сколько людей\n    * посчитать коэфициенты и пересчитать шансы нахождения\n\nRewriteRule сам сохраняет GET-параметры, если ты добавишь флаг QSA. (.htaccess)\nRewriteEngine On\n\nRewriteCond %{REQUEST_FILENAME} !-f\nRewriteCond %{REQUEST_FILENAME} !-d\nRewriteRule ^(.+)$ handler.php?path=$1 [L,QSA]\n\nЧто получится\nЗапрос:\n    /foo/bar?x=1&y=2\nПереходит в:\n    handler.php?path=foo/bar&x=1&y=2\nВ PHP:\n    $path = $_GET['path']; // foo/bar\n// остальные параметры тоже доступны\n\nmod_rewrite должен быть включён\n    a2enmod rewrite\nВ конфиге виртуального хоста должно быть:\n    AllowOverride All\nНапример:\n<Directory /var/www/html>\n    AllowOverride All\n    Require all granted\n</Directory>\nБез этого .htaccess не работает вообще.\n\n* Написать утилиту возвращения ТМ за отмену названия.\n* Поменять формулу расчёта награды:\n    * Берется вся сумма найденного\n    * Умножается на количество основных предметов (Тортик/Ёлка)\n    * Вычилсяется доля значимого взноса каждого игрока от 100% (без учета лишних найденных предметов - только по базе)\n    * Выдаётся эта доля\n\nexplain DELETE spe FROM `game_security_player_entry` AS spe LEFT JOIN `game_counter` AS c ON c.player_entry_id = spe.id WHERE c.counter_id IS NULL\nПерепроверить индекс на больших файлах\nCREATE INDEX idx_counter_entry_id_counter_id  ON game_counter(player_entry_id, counter_id);\n* Больше IDшек - возможно, с автодетектом окна\n    * DELETE FROM `game_counter` WHERE `counter_id` IN (41164626\n* TIMESTAMP(6)\n\nСделать проверку на файлы, которые уже не используются (картинки и эмблемы)\n\nНиштяки появляются на странице логина! Надо проверять - залогинен ли текущий пользователь\n\n\n* Плюсомёт\n    * Ставить игроку за нарушение \"плюсы\" типа \"замечания\"\n    * Два плюса - мьют на час\n    * Три плюса - мьют на сутки\n    * Четыре плюса - на неделю\n    * Пять плюсов - на месяц\n    * Плюсы снимаются со скоростью один в месяц\n    * Ну тут еще подумать\n    * может выделять фоном?\n\n* Интегрировать Illuminate\n\n>npm install -g terser cssnano-cli\nnpm warn deprecated q@1.5.1: You or someone you depend on is using Q, the JavaScript Promise library that gave JavaScript developers strong feelings about promises. They can almost certainly migrate to the native JavaScript promise now. Thank you literally everyone for joining me in this bet against the odds. Be excellent to each other.\nnpm warn deprecated\nnpm warn deprecated (For a CapTP with native promises, see @endo/eventual-send and @endo/captp)\nnpm warn deprecated flatten@1.0.3: flatten is deprecated in favor of utility frameworks such as lodash.\nnpm warn deprecated browserslist@1.7.7: Browserslist 2 could fail on reading Browserslist >3.0 config used in other tools.\nnpm warn deprecated svgo@0.7.2: This SVGO version is no longer supported. Upgrade to v2.x.x.\n\nМигрировать на более новый jQuery\n\n* Кажется, можно создать алл с тем же тэгом. что уже существует! Проверить!\n    * Если да - исправить и написать миграцию, которая к более позднему тегу алла добавит \"не\"\n    * Проверить то же по имени алла\n\n[Mon Dec 15 16:45:10.715047 2025] [php7:error] [pid 1550953:tid 1550953] [client 185.250.45.15:35296] PHP Fatal error:  Uncaught Error: Unsupported operand types in /var/www/alpha.supernova.ws/includes/init.php:259\\nStack trace:\\n#0 [internal function]: {closure}()\\n#1 /var/www/alpha.supernova.ws/classes/Core/Worker.php(51): call_user_func_array()\\n#2 /var/www/alpha.supernova.ws/includes/init.php(271): Core\\\\Worker->__call()\\n#3 /var/www/alpha.supernova.ws/common.php(12): require_once('/var/www/alpha....')\\n#4 /var/www/alpha.supernova.ws/index.php(10): require_once('/var/www/alpha....')\\n#5 {main}\\n  thrown in /var/www/alpha.supernova.ws/includes/init.php on line 259\n\n\nFleetDispatchEvent::__construct\n  * Вынести обращения к БД на момент необходимости использовать данные, а не сразу в конструкторе\n\n[детект сходных символов](./docs/similar_characters_detect.md)\n\n#ctv\n\n2025-12-25 16:26:41 46d0\n[!] Project \"SuperNova.WS\" Release 46 \"Совершеннолетие\"\n    Project \"SuperNova.WS\" Release 46 \"Совершеннолетие\"\n    Обновлен docs/changelog.txt\n\n2025-12-21 18:46:54 46a257\n\n[!] Support for overriding skin images via `override.css`\n\n\n2025-12-21 15:03:25 46a255\n\n[~] CSS: misc changes\n\n\n2025-12-16 13:58:06 46a234\n\n[+] FFH now skips FLT_FLEET_ARRIVE events for MT_HOLD/MT_EXPEDITION\n\n\n2025-12-14 17:03:52 46a223\n\n[+] Admin/userlist\n    Added ability to reverse-sort by all sortable fields\n    Now userlist page opens by default sorted descending last login time\n\n2025-12-14 14:27:31 46a221\n\n[@] Code - templates\n    * More robust way to add JS files for compiler\n[@] Replaced JQuery with large version for easier development\n\n2025-10-21 23:34:21 46a172\n[@] Debug\n    Config `debug` option now can be customized\n    * if it set to `1` then `error_reporting` would be set to `E_ALL ^ E_NOTICE ^ E_DEPRECATED ^ E_WARNING`\n    * if it set to different value - then `error_reporting` would be set for this value\n        * Example: `debug` value for `E_ALL ^ E_NOTICE ^ E_DEPRECATED ^ E_WARNING` is `32767 ^ 8 ^ 8192 ^ 2` = 24565\n\n\n2025-10-21 18:30:01 46a171\n[@] Updated debug tools\n\n\n2025-05-15 13:16:27 46a168\n[%] Fleet/Mission/Expedition\n    Fixed error when generation expedition result messages\n\n\n2025-05-15 09:33:09 46a175\n[@] Fleet/Mission/Expedition\n    * Isolated outcomes to separate methods\n    * Removed `includes/includes/flt_mission_explore.php` - we really don't need empty `sn_XXX` function as base hook - there is already a check for this case in `sn_function_call()`\n\n\n2025-05-14 19:50:37 46a166\n[!] Fleet/Mission/Expedition\n    * Refactored mission code to class\n    * Now fleet dispatcher internal messages also contains count of processed IPRs\n    * Mission Explore constants moved to Fleet/Constants\n\n\n2025-05-08 16:29:20 46a165\n[@] Code DBAL: Now lockRecords locks rows not with UNION but by one query per table\n\n[%] Deadlock fixed: sn_fleet_page3() - 'fleet.php'@228: \"SELECT * FROM `game_fleets` WHERE `fleet_end_galaxy` = G AND `fleet_end_system` = S AND `fleet_end_planet` = P AND `fleet_end_type` = T AND `fleet_owner` = FO AND `fleet_mission` IN (1,2,9) AND `fleet_mess` = 0 FOR UPDATE\"\n\n\n2025-05-08 11:23:47 46a164\n[%] Deadlock fixed: flotenajax.php@97: \"SELECT * FROM {{users}} WHERE `id` = X LIMIT 1 FOR UPDATE\"\n\n\n2025-05-06 18:41:30 46a163\n[%] Fleet Dispatcher\n    Fixed error when fleet arrived/returned to deleted planet: planet owner ID rewrote with `0` - current planet \"owner\" so `sys_o_get_updated()` calls becomes invalid\n\n\n2025-05-06 18:10:02 46a162\n[%] Fixed error - FleetDispatchEvent::srcPlanetOwnerId does not filled\n\n\n2025-05-06 17:50:22 46a161\n[~] CSS cacher now caches module's CSS files regardless of supplied extension (`.min.css`, `.css` or no extension)\n\n\n2025-05-06 14:20:56 46a160\n[~] Chat\n    Enchanced URL parsing/replacement in chat messages (for appropriate ACCESS_LEVELS)\n\n\n2025-05-06 09:16:26 46a159\n[%] SQL\n    Fixed `UNION ... FOR UPDATE` syntax to be compatible with MariaDB\n\n\n2025-05-05 12:13:38 46a158\n[!] Language\n    Added translation to Spanish (computer-generated)\n    Added translation to German (computer-generated)\n\n\n2025-05-05 11:56:39 46a157\n[@] Code\n    Fixed some notices and warnings\n\n\n2025-05-05 10:26:37 46a156\n[+] Brand new Fleet Dispatcher to prevent locks with main thread\n    * Rewrote dispatcher code in `classes/Fleet/FleetDispatcher.php`\n        * Added separate class `FleetDispatcherEvent` to handle all fleet event-related stuff\n        * Now for each fleet all related users/planets/fleets are locked\n        * Now missile attacks handled each in own transaction - not in large one. Should reduce probability of deadlocks\n    * Now `sys_o_get_updated()` accepts only user/planet IDs as parameters to reduce probability of index/gap DB lock\n    * Adjusted code to work with new internal API & FleetDispatcher\n        * includes/includes/flt_mission_relocate.php\n        * includes/includes/flt_mission_colonize.php\n        * includes/includes/flt_mission_transport.php\n    * Renamed internal fields and replaced internal representation from array to fields\n        * classes/Fleet/MissionData.php\n    * Moved transaction and locks into main missile loop\n        * includes/functions/flt_mission_missile.php\n\n[@] Code - minor changes\n    * admin/planet_compensate.php\n        * Added locking mechanism\n        * Fixed notices and warnings\n    * Added 2 new debug function: pre() and pred()\n    * Added record lock function to `classes/DBAL/db_mysql.php`\n    * Relocated db-related functions from god-class `classes/SN.php`\n    * Removed unused params from functions in `classes/Planet/DBStaticPlanet.php`\n    * Adjusted code to pass some IDE checks in `classes/Common/Traits/TContainer.php`\n    * Added some phpDoc to `classes/Fleet/Fleet.php`\n    * Adjusted code to work with new internal API\n        * admin/userlist.php\n        * annonce.php\n        * buddy.php\n        * jumpgate.php\n        * classes/Alliance/Alliance.php\n        * classes/Chat/Chat.php\n        * classes/Fleet/DbFleetStatic.php\n        * classes/Fleet/MissionEspionage.php\n        * classes/Fleet/MissionEspionageReport.php\n        * classes/Fleet/MissionExplore.php\n        * classes/Pages/Deprecated/PageFleet5Gathering.php\n        * classes/Pages/Deprecated/PageImperium.php\n        * classes/Pages/Deprecated/PageOverview.php\n        * classes/Planet/Planet.php\n        * classes/Player/PlayerStatic.php\n        * classes/Ube/Ube4_1/Ube4_1Prepare.php\n        * includes/alliance/ali_internal_admin.inc\n        * includes/constants/constants.php\n        * includes/db/db_helpers.php\n        * includes/db/db_queries_users.php\n        * includes/functions/eco_planet_update.php\n        * includes/functions/int_fleet_events.php\n        * includes/functions/rpg_points.php\n        * includes/functions/sys_user.php\n        * includes/functions/uni_functions.php\n        * includes/general/general_validators.php\n        * includes/includes/flt_functions.php\n        * includes/includes/flt_mission_attack.php\n        * includes/includes/flt_mission_explore.php\n        * includes/includes/flt_mission_hold.php\n        * includes/includes/flt_mission_recycle.php\n        * includes/includes/market_info.inc\n        * includes/includes/market_trader.inc\n        * includes/init.php\n        * includes/vars.php\n    * Replaced deprecated strrpos() call with substr() in `classes/Core/Autoloader.php`\n    * Temporaly moved function `RestoreFleetToPlanet` to `includes/functions/_deprecated.php`\n    * Removed unused code\n        * includes/includes/flt_mission_destroy.php\n        * modules/core_festival/classes/FestivalActivityPuzzleShowTree.php\n\n[@] Language\n    * Changed some language variables\n        * language/en/system.mo.php\n        * language/en/tech.mo.php\n        * language/ru/system.mo.php\n        * language/ru/tech.mo.php\n\n\n2025-02-25 13:30:33 46a155\n[%] Admin/Ban\n    Fixed location of ban check\n\n\n2025-02-25 12:29:49 46a154\n[+] Admin/Ban\n    * Basic ban-by-ip facility - table `ban_ip`. Supports IPv4 ranges\n\n[%] HTML\n    Fixed vertical scroll bar on main content cell in latest Chrome versions\n\n\n2025-02-24 13:53:14 46a153\n[@] Code\n    `AwardConstants` now tried to be included in the `init.php` - to allow loading of modules that uses their constants\n\n\n2024-10-26 21:25:39 46a151\n[@] Code\n    * Functions moved from `SN` to `db_mysql`\n        * `db_transaction_start()`\n        * `db_transaction_commit()`\n        * `db_transaction_rollback()`\n        * `db_transaction_check()`\n    * `SN::DB_TRANSACTION_XXX` constants moved `db_mysql::TRANSACTION_LEVEL_XXX`\n    * `SN::TRANSACTION_XXX` constants moved `db_mysql::TRANSACTION_XXX`\n    * Variables moved from `SN` to `db_mysql`: `$db_in_transaction`, `$transaction_id`, `$transactionDepth`\n\n\n2024-10-24 19:13:36 46a150\n[@] Code\n    * `includes/db_deprecated.php` - removed\n    * `classes/DBAL/db_mysql_v5.php` - removed\n    * Addded `pre()` and `pred()` debug functions\n\n\n2024-10-23 01:45:56 46a149\n[~] Logs\n    * In log text \"\\n\" replaced with \"<br />\" on output\n\n\n2024-10-23 01:09:59 46a148\n[@] Code\n    * `$sys_user_logged_in` => `SN::$sys_user_logged_in`\n    * Debug - now prepends sql log filename with current instance DB name\n    * `sys_o_get_updated` - simplified error reporting on empty user\n    * Removed `db_queries_user.php` -> `db_get_user_by_where`\n    * `db_queries_user.php` -> `db_user_by_id` now accepts only userId as first param\n    * Streamlined and formatted code a bit\n\n\n2024-10-21 21:08:03 46a147\n[@] DB\n    * fighting deadlocks\n        * Table `que` - index I_que_planet_id expanded to (`que_planet_id`, `que_player_id`)\n        * Table `planets` - index `id` dropped\n        * Table `users` - index `I_user_id_name` dropped\n\n\n2024-10-21 18:33:01 46a146\n[@] DB\n    * Transaction level for MySQL raised to 'SERIALIZABLE' to reduce deadlocks\n    * Adjusted how SQL queries reported\n\n\n2024-10-21 18:31:30 46a145\n[@] Fleet\n    * Added old fields to Fleet\\Fleet to use in low-level functions\n    * Rewrote fleet lock conditions from `?:` to `if`\n    * Replaces some strings with constants\n    * Some code reformatting\n\n\n2024-10-21 18:25:00 46a144\n[@] Code\n    * Now `DEBUG_SQL_XXX` constants ruled by appropriately named records in `config` table:\n        * `DEBUG_SQL_FILE_LOG` replaces `DEBUG_SQL_ONLINE` - implies `DEBUG_SQL_ERROR` and `DEBUG_SQL_COMMENT_LONG`\n        * `DEBUG_SQL_ERROR` - implies `DEBUG_SQL_COMMENT`\n        * `DEBUG_SQL_COMMENT_LONG` - implies `DEBUG_SQL_COMMENT`\n        * `DEBUG_SQL_COMMENT`\n\n\n2024-10-21 18:21:39 46a143\n[@] Code\n    * Added transaction depth counter into `SN` class\n\n\n2024-10-21 18:16:45 46a142\n[~] Admin\n    * Now in log viewer user options stored in `user.options` parsed for better readability\n\n[@] Code\n    * User options separator `|` move to constant `USER_OPTIONS_SPLIT`\n    * `EVENT_FLT_XXX` constants now are strings for better debug\n\n\n2024-05-24 04:56:07 46a134\n[%] Language\n    * Fixed not loading module l10n files before user init\n\n[@] DB\n    * Dropped unneeded indexes to reduce deadlocks\n        * counter.counter_id\n        * que.que_id\n        * captain.captain_id\n\n\n2024-05-21 14:41:12 46a131\n[@] PHP 7\n    * More PHP7/MariaDB shenanigans\n\n\n2024-04-13 13:04:16 46a127\n[@] PHP 7\n    * Added support for `fastcgi_finish_request()` where it supported\n[@] SQL\n    * Added comments to transaction-related statements - to easier found them later in debug/dump\n    * Added separate table `festival_config` for festival-specific config\n    * Migrated festival config to separate table\n\n\n2024-04-12 08:13:34 46a124\n[~] PHP 7\n    * Fixed minor issues\n\n\n2024-04-08 09:08:32 46a122\n[~] Admin\n    * Adjusted log detail output\n\n\n2024-04-07 14:33:41 46a118\n[~] PHP 7\n    * Fixed no que items on building pages\n    * Fixed que for mass-operations\n    * Fixed other PHP 7 compatibility issues\n\n2024-03-26 17:38:51 46a117\n[~] Fleets\n    * `fleet_dispatch_max_time` renamed to `fleet_update_dispatch_time` for uniformity\n\n\n2024-03-26 17:04:00 46a113\n[~] Fleets\n    * Now length of each fleet dispatch job can be configured via `fleet_dispatch_max_time` config (float). Default: 3 seconds\n    * `GAME_FLEET_HANDLER_MAX_TIME` is obsolete\n\n\n2024-01-22 00:39:08 46a110\n[%] Navbar\n    * Fixed error reposition que in navbar buttons\n\n\n2024-01-22 00:32:52 46a108\n[%] Navbar\n    * Restored links on some navbar buttons\n\n\n2024-01-21 19:38:32 46a105\n[+] Navbar\n    * Navbar now use sprites\n    * This should speed up page loading due to combining multiple standard navbar buttons into one sprite\n\n\n2024-01-21 07:04:39 46a104\n[@] Tools: Spritify update\n    * Adjusted behavior in case if first frame in A-GIF smaller then largest one\n\n\n2024-01-21 06:17:01 46a103\n[@] Tools: Spritify update\n    * Now Spritify honors offset in first frame of A-GIF\n\n2024-01-21 04:44:55 46a102\n[@] Tools: Spritify update\n    * Spritify supports `RESTORE_TO_BACKGROUND_COLOR` disposition method\n\n\n2024-01-21 03:55:30 46a101\n[@] Tools: Spritify update\n    * Now Spritify generates pure CSS animations for extracted A-GIF frames, honoring delay between frames\n\n\n2024-01-21 02:45:25 46a100\n[@] Tools: Spritify update\n    * Animated GIF frame expansion - disposal methods `UNSPECIFIED` and `DO_NOT_DISPOSE`\n\n\n2024-01-21 01:27:46 46a99\n[@] Tools: Spritify update\n    * Basic support for animated GIFs - extract frames into sprite line\n\n\n2024-01-19 17:51:35 46a98\n[@] Tools: Spritify update\n\n\n2024-01-19 16:04:51 46a97\n[@] Tools: Spritify update\n\n\n2024-01-19 11:34:34 46a95\n[@] Modules\n    * Added support for `javascript_filenames` in `sn_mvc`:\n        * Modules' JS now added to new subarray\n        * Now modules' JS can be cached too\n        * This fixed errors with modules' JS loaded too early and not working\n\n[@] CSS\n    * Override jQueryUI images in `_template.css` to make it work with CSS caching\n\n\n2024-01-16 12:28:03 46a91\n[~] Units\n    Support for Festival \"NY-2024\"\n\n\n2024-01-14 06:52:51 46a88\n[@] Tools - `spritify` update\n\n\n2024-01-14 04:31:34 46a86\n[@] HTML\n    JS cacher - merges all JS files into one and caches results\n\n\n2024-01-14 01:45:52 46a85\n[@] Code\n    Streamlined `classConfig` - listed all known config keys with types\n\n\n2024-01-14 01:07:40 46a84\n[@] HTML\n    Rearranged JS code/file include in `_40_js.tpl.html`\n\n[@] Code\n    SnTemplate - refactored a bit\n    Basic support for different environments - @see: .env.ini.example\n\n\n2024-01-12 14:55:46 46a81\n[%] HTML\n    Fixed strange jQueryUI behavior on texteareas in new Chrome\n\n\n2024-01-12 14:09:58 46a80\n[%] News\n    Fixed typo in news rendering engine\n\n\n2024-01-12 03:17:44 46a78\n[~] CSS cacher refactored\n\n[%] Fixed some template error to be compatible with CSS cacher\n\n\n2024-01-12 01:42:07 46a75\n[!] CSS cacher\n    Internal subsystem to compact several CSS files into one file and cache it\n    Updated skins to accomodate CSS cacher\n\n\n2024-01-12 00:05:58 46a73\n[!] Tools - Spritify\n    New tool to create sprite PNG and CSS for it from set of images\n[!] Menu\n    Switched to using sprites as menu icons\n\n\n2024-01-04 02:07:54 46a67\n[@] Code\n    `_SnCacheInternal` class removed from code along with caching players/planets\n    Units cached inside `DbStaticUnit` class\n\n\n2024-01-04 00:58:46 46a65\n[@] Code\n    `_SnCacheInternal::$locator` now used only by LOC_UNIT and LOC_UNIT now does not stored in ::$data\n    Simplified cacher code a little\n\n\n2024-01-03 22:36:43 46a63\n[%] News\n    Now announce editing/copying works again in Chrome\n\n[@] Code\n    Start moving Unit operations from using `SN` or `_SnCacheInternal` objects\n    Misc speedup\n\n\n2024-01-03 19:59:46 46a58\n[%] News\n    Now non-admin users doesn't see announces\n\n\n2023-05-31 16:04:19 46a53\n[~] Юниты\n    Поддержка Фестиваля \"ДР-2023\"\n\n\n2023-05-19 16:17:46 46a49\n[@] Code\n    Replaced part of intermediate function sn_db_XXX and db_XXX with direct calls to SN::$db methods\n\n\n2023-05-18 12:36:49 46a46\n[@] Code - added (disabled now) code to bypass SnInternal cache\n\n\n2023-05-17 11:47:52 46a44\n[@] Code - removed SN::$queries\n\n\n2023-05-14 11:30:37 46a41\n[~] FFH - Added total event amount to FFH timeout message\n\n\n2023-05-13 23:24:17 46a39\n[%] Fixed bug with missing closing slash\n\n\n2023-05-13 19:33:01 46a37\n[%] Fixed problems related to `SN_GOOGLE` constant\n    * Another reminder about maintain maximum back-compatibility...\n\n\n2023-05-13 16:39:53 46a36\n[!] Server Instances. Now several servers can work on one copy of SN engine\n    * Instance-specific files located in `/servers/{domainName}[{_port}]/` folders\n        * `_port` part is optional and used only for servers running on ports other then 80 or 443\n        * {domainName} is SN's domain name\n        * I.e. for `sn.domain.com` folder would be `/servers/sn.domain.com/`\n        * I.e. for `sn.other_domain.com:8080` folder would be `/servers/sn.other_domain.com_8080/`\n    * If SN detects `config.php` file in instance-specific folder it would use it instead of `/config.php`\n    * If SN detectes subfolder `avatars` in instance folder writable by web-server - it would be used for player and Alliance avatars\n        * I.e. if for domain `sn.domain.com` there is folder `/servers/sn.domain.com/avatars` and it writable by web server - it would be used as avatar storage\n[~] Now SN recognize `/avatars` folder in code root as folder for non-instanced servers and prefers it to use before `/image/avatar` folder\n    * It's recommended to move all avatars to this folder because in future `/image/avatar` folder would be deprecated and unsupported\n\n\n2023-05-13 13:36:51 46a35\n[@] Replaced path to avatars with constants\n\n\n2023-05-13 12:24:59 46a33\n[@] GIT: Fixing versioning problem\n\n\n2023-05-13 12:24:15 45d16\n[@] GIT: Fixing versioning problem\n\n\n2023-05-13 12:16:55 46a33\n[@] GIT: Fixing versioning problem\n\n\n2023-05-13 12:01:52 45d15\n[~] DB\n    Драйвер db_mysql_v4 удалён\n    Для db_mysql_v5 `TRANSACTION ISOLATION LEVEL` установлен в `REPEATABLE READ`\n\n2023-05-13 11:23:47 46a32\n[~] Template engine fixes\n    Fixed `error: template->_tpl_load_file()` for module template files loaded on admin page\n    Fixed `error: template->_tpl_load_file()` when module template files tried to load files from core template\n        I.e. alliance manager page tries to load `eco_que` template from core template\n\n2023-05-13 09:42:39 46a23\n[@] Code\n    Немного перераспределил функции для флота внутри кода\n\n2023-02-10 19:39:59 45d3\n[~] Юниты\n    Поддержка Фестиваля \"СНГ-2023\"\n\n\n2022-05-26 07:35:13 46a21\n[~] Юниты\n    Поддержка Фестиваля \"ДР-2022\"\n\n\n2022-01-20 05:37:05 45d2\n[~] Юниты\n    Поддержка Фестиваля \"СНГ-2022\"\n\n\n2021-03-03 13:41:05 46a13\n[~] Личные сообщения\n    Немного приглушен цвет сообщений от Администрации\n\n[#] Модули\n    Поддержка подарков на 8 марта\n\n\n2021-03-02 08:46:06 46a9\n[~] Админка\n    Восстановлено обслуживание таблиц БД, относящихся к безопасности\n\n\n2021-03-02 08:43:10 46a8\n[~] Картинки\n    Немного уменьшены размер некоторых изображений\n\n\n2020-12-11 11:56:23 46a3\n[~] Юниты\n    Поддержка Фестиваля \"СНГ-2021\"\n\n\n2020-08-25 08:45:44 45d2\n[~] Установка/обновление\n    Обновлены SQL-файлы установки\n    Улучшен процесс установки/обновления\n\n\n2020-07-27 12:12:06 45d1\n[!] Документация\n    Переделана в формат MarkDown:\n        - /README -> /README.MD\n        - /docs/install.txt -> /docs/install.md\n        - /docs/install-en.txt -> /docs/install-en.md\n\n[!] Установка/обновление\n    Улучшен процесс установки/обновления\n    Расширен раздел, посвященный проблемам с установкой или обновлением в файлах /docs/install.md и /docs/install-en.md\n\n\n2020-07-27 10:08:44 45d0\n[!] Project \"SuperNova.WS\" Release 45 \"11 years anniversary\"\n    Project \"SuperNova.WS\" Release 45 \"11 years anniversary\"\n    Обновлен docs/changelog.txt\n\n\n2020-07-08 10:35:10 45a118\n[~] Навбар\n    Поддержка кнопки \"Оборона\"\n\n\n2020-06-25 10:30:33 45a113\n[@] Код\n    sn_timer.js: исправлена несовместимость со старыми версиями JS\n\n\n2020-06-21 10:40:09 45a109\n[@] Код\n    CSS:\n        - Добавлена возможность отключать наложение скинов на элементы управления (input, button) у всех дочерних элементов сразу\n    Навбар:\n        - Количество ресурсов в ресбаре теперь всегда выравнивается по правой стороне ячейки\n    Меню:\n        - Раскраска пунктов меню теперь снимается через CSS, а не через JS\n        - Вид пунктов меню кнопки/ссылки теперь переключается через CSS, а не через JS\n        - Кнопка показа/скрытия меню теперь привязывается к самому меню, а не к абсолютным координатам\n    sn_timer.js:\n        - Добавлена поддержка human-readable времени в sn_timer.js\n        - Исправлена ошибка, когда длина бара в таймере отсчёта могла быть > 100%\n\n\n2020-06-18 10:16:27 45a102\n[@] Код\n    Немного откорректирован код для поддержки других темплейтов\n\n\n2020-06-14 09:40:35 45a97\n[~] Юниты\n    Поддержка Фестиваля \"ДР СН-2020\"\n\n\n2020-05-30 11:22:16 45a95\n[%] Чат\n    Исправлена ошибка с утерей фокуса при наборе сообщения дольше 5 секунд\n\n\n2020-05-21 16:39:55 45a94\n[~] Список планет\n    Унифицирован показ статуса планеты (Столица, Луна):\n        - Теперь \"Обзоре планеты\" и \"Империи\" статус планеты показывается соответствующим значком;\n        - На страницах, указанных выше, а так же в меню выбора планеты для Луны Столицы показывается оба значка\n    В \"Обзоре\" теперь название и координаты планеты/луны показываются в самом низу блока планеты\n[@] Темплейт\n    Список планет: темплейт планеты списка вынесен в отдельный файл\n    Империя:\n        - Темплейт ячейки ресурсов вынесен в отдельный файл;\n        - Уменьшен размер создаваемого файла примерно на 5%.\n\n\n2020-05-19 08:08:36 45a88\n[!] Чат\n    Полностью новый чат\n    Встроенная система команд с поддержкой алиасов команд\n    Встроенная система помощи по командам чата - команда /help\n    Добавлен список игроков в чате с дополнительными иконками статуса и командами управления для админов\n    Возможность игрокам управлять своим состоянием видимости в чате - команда /invisible. Администрация сервера (authlevel > 0) всегда видит невидимок\n    Возможность отправлять приватные сообщения другим игрокам - команда /whisper. Приватные сообщения выделяются специальным образом, видны во всех каналах и сохраняются в истории чата. В приватных сообщенях нельзя употреблять форматирование цветом\n    Администраторы имеют возможность запретить игроку писать в чат на определенный срок или вернуть такую возможность - соответственно, команды /mute и /unmute. Запрет распространяется на все каналы и на возможность писать личные сообщения. Соответствующая иконка в списке игроков лишает его права голоса на 1 час\n    Администраторы имеют возможность блокировать и разблокировать игроков прямо из чата - соответственно, команды /mute и /unmute. Иконка в списке игроков банит его на 1 неделю\n    Системные и приватные сообщения выделяются жирным шрифтом\n    Скорость обновления в AJAX части чата регулируется переменной 'chat_refresh_rate'\n    Игроки из онлайн-списка исчезают сразу после выхода из чата - таймаут попадания в список установлен как удвоенный 'chat_refresh_rate'\n    В чате доступен расширенный функционал BBCode\n\n\n2020-05-06 10:37:35 45a87\n[~] Локализация\n    Немного английской локализации\n\n\n2020-05-06 10:29:09 45a86\n[~] Локализация\n    Немного английской локализации\n\n\n2020-05-06 10:10:56 45a85\n[@] Модули\n    Исправлены ошибки включения отсутствующих общих темплейтов\n\n\n2020-04-18 17:49:48 45a79\n[%] Task/Lock\n    Исправлены ошибки взаимодействия разный воркера диспетчера флотов и ЧЛ/ОвК\n\n\n2020-04-18 15:36:05 45a77\n[@] Темплейты\n    Вынесены кнопки навбара в отдельный файл\n[@] Код\n    Поддержка Хайспотов Фестиваля ЧЛ/ОвК\n\n\n2020-02-18 22:18:16 45a74\n[%] Флоты\n    Исправлена ошибка\n\n\n2020-02-18 22:15:57 45a73\n[%] Флоты\n    Исправлена ошибка\n\n\n2020-02-18 22:07:00 45a72\n[%] Флоты\n    Исправлена ошибка\n\n\n2020-02-18 21:00:19 45a71\n[!] Код\n    Новая система рабочих процессов Worker\n\n[!] Флоты\n    Диспетчер флотов теперь использует системы Task+Worker и является неблокирующим\n    Таким образом, теперь нет визуальных задержек на обсчёт флотов (кому-то из игроков раньше не везло каждые 4 секунды)\n    Так же флоты обсчитываются не кусками по 3 секунды, а в пределах, заданным параметром `fleet_update_max_run_time` (30 секунд по умолчанию)\n\n\n2020-02-09 21:08:55 45a70\n[!] Код\n    Новая система задач и блокировок (Task/Lock)\n    Пока используется только для обсчёта летящих флотов\n\n\n2020-02-08 09:14:02 45a65\n[@] Код\n    Поддержка обновления core_festival\n\n\n2020-01-15 08:50:14 45a61\n[+] Код/Скины\n    Добавлена поддержка формата изображений WebP\n\n\n2020-01-12 09:43:34 45a56\n[@] Код\n    Поддержка НГ-2019/2020\n\n\n2020-01-01 14:38:28 45a54\n[%] Локализация\n    Исправлена очепятка в описании плотностей\n\n\n2019-10-28 09:31:45 45a50\n[%] Платежи\n    Исправлена ошибка, когда установлен только один модуль платежей с одним методом платежа типа Generic\n\n\n2019-10-27 15:37:15 45a49\n[@] Код\n    Поддержка модуля `payment_payu_rest`\n\n\n2019-10-17 09:28:49 45a45\n[%] Интерфейс/Результат операции\n    Исправил сползание блока с результатом операции в случае, если есть другой инлайн-блок ниже \"по течению\"\n\n\n2019-10-17 09:18:21 45a44\n[+] Интерфейс/Обзор планеты\n    В экспериментальном порядке дизайн страницы \"Обзор Планеты\" сделан респонзивным.\n    Т.е. на широких экранах элементы страницы - инфа о планете, список планет и список флотов в полете - будут выстроены в три колонки на всю ширину монитора\n    На мобильных устройствах устройствах с узкими дисплеями элементы страницы будут располагаться друг под другом\n\n[%] Интерфейс/Результат операции\n    Наконец-то отследил и убрал все задвоения сообщений о результатах операции\n[%] Интерфейс/Логин\n    Исправлено форматирование\n    Изменена надпись \"Имя игрока или е-мейл\" на просто \"Е-мейл\"\n[%] Интерфейс/Копирайт\n    Исправлен вывод версии патча БД\n[%] Интерфейс\n    Поправлено форматирование на страницах:\n        - Настройки\n        - Император\n        - Управление Альянсом\n\n\n2019-10-08 06:28:37 45a43\n[%] Интерфейс\n    Исправлены ошибки форматирования на страницах: Император, Квест, Чёрный Рынок и некоторые другие\n\n\n2019-10-07 16:30:24 45a40\n[~] Интерфейс/Обзор планеты\n    Немного переверстана страница\n\n\n2019-10-07 16:11:55 45a39\n[%] Интерфейс/Подсказки\n    Исправлена ошибка рендеринга Подсказок вне враппера контента для темплейтов, рендерящих страницу целиком\n\n\n2019-09-30 12:46:46 45a36\n[%] Интерфейс/Обзор планеты\n    Исправлен визуальный глюк\n\n\n2019-09-30 12:36:52 45a35\n[%] Исследования\n    Исправлена ошибка, позволяющая запустить исследование на одной планете в то время, когда на другой планете строится/исследуется Лаборатория или Нанолаборатория при отключенной настройки сервера \"BuildLab\"\n    Исследования блокируются даже если Лаборатория или Нанолаборатория сейчас не строятся, а просто находятся в очереди построек\n\n[@] Код/Темплейты\n    Из хидера страницы извлечены тематические куски в отдельные файлы; стили, подключение JS итд\n    Общие элементы темплейта страницы (хидер, футер, куски внутри хидера, навбар итд) вынесены в отдельную папку\n\n\n2019-09-05 12:12:36 45a31\n[@] Код\n    JS таймер отчета (например - показ ресурсов псевдо-онлайн) теперь может работать по имени класса, а не только по ИД - т.е. управлять сразу несколькими элементами\n    Бенчмарк теперь старается вставить данные о производительности в <body>, а не после\n    Теперь SnTemplate может выводить страницу целиком. Это даёт возможность объединить хидер и футер в один файл\n    SnTemplate::display() теперь закрывает все существующие буффера вывода\n\n\n2019-09-03 16:27:40 45a30\n[@] Код\n    Удалены неиспользуемые файлы\n\n[@] Код/Темплейты\n    Теперь если у темплейта нет родителя или он не существует, то в качестве fallback темплейта используется темплейт по умолчанию\n\n\n2019-09-03 01:44:27 45a26\n[@] Код/Темплейты\n    Добавлена поддержка наследования темплейтов (см. _template.ini):\n        - Поддерживается только один уровень наследования\n        - Поддерживается наследование и дозагрузка _template.css\n    Добавлена поддержка переключения темплейтов (альфа-версия без вывод в интерфейс игроков)\n    Убраны глобальные константы, относящиеся к темплейтам: TEMPLATE_NAME, TEMPLATE_DIR, TEMPLATE_PATH\n    Добавлен метод для добавления JS файлов в темплейт из PHP\n    Класс SnTemplate перенесен в каталог классов и использует автолоадер\n\n\n2019-09-02 20:54:12 45a23\n[~] Платежи/Интерфейс\n    Улучшен вид страниц платежа:\n        - Переработан вывод списков предустановленных валют, методов платежей, списка модулей\n        - Почищен код страниц\n        - Улучшена работа JS\n        - Стили вынесены в файл стилей\n\n[~] Планета/Переименование\n    Теперь при переименовании планеты на странице \"Обзор\" и \"Управление планетой\" имя планеты меняется сразу, а не после повторного обновления страницы\n\n[~] Интерфейс/Результат операции\n    В темплейт заголовка добавлена проверка для избежания двойного вывода\n    Убрано подключение вывода результата на страницах\n        - \"Темная Материя\"/\"Метаматерия\"\n        - \"Ресурсы\"\n        - \"Управление планетой\"\n        - \"Переименование галактики/системы\"\n\n[~] Интерфейс/Подсказки\n    Подсказка теперь подключается при рендеринге страницы, а не в темплейте\n    В темплейт подсказки добавлена проверка для избежания двойного вывода\n    Убрано подключение подсказки на страницах:\n        - \"Переименование галактики/системы\"\n\n[@] Код/JS\n    Объект language теперь имеет метод addLocale для добавления в него строк локализаций\n\n[@] Код/PHP\n    Начат рефакторинг файла includes/template.php:\n        - Создан новый класс SnTemplate, куда перенесена часть функций в виде статических методов\n\n\n2019-08-22 02:38:16 45a21\n[%] Платежи\n    Исправлена ошибка, когда одному платёжному методу соответствует только один модуль\n\n[@] Код/Платежи\n    Рефакторинг кода платежей\n\n\n2019-08-21 20:14:18 45a19\n[!] Модули\n    Изменена система версионирования модулей\n    Теперь в качестве версии модуля используется версия билда, в котором были закоммичены изменения\n\n[~] Платежи\n    Немного переверстана страница выбора способа оплаты\n\n[@] Код/Платежи\n    Рефакторинг кода платежей\n\n\n2019-08-19 17:21:50 45a16\n[~] Альянсы/Рекомендуемые Альянсы\n    Теперь список РА показывается и при простом заходе на страницу Альянса без поиска других Альянсов\n\n\n2019-08-19 17:03:22 45a15\n[+] Альянсы/Поиск\n    В результаты поиска Альянсов и в \"Рекомендуемые Альянсы\" (см. ниже) добавлены две колонки:\n        - Колонка \"Разница в очках\" указывает на разницу между количеством очков у игрока и средним количеством очков на одного игрока в Альянсе. Если она отрицательная - средний игрок в Альянсе имеет больше очков, чем текущий игрок\n        - Колонка \"Рейт\" указывает на соотношение между количеством очков у игрока и средним количеством очков на одного игрока в Альянсе. Если она меньше единицы - средний игрок в Альянсе слабее, чем текущий игрок\n    Для игрока без Альянса на странице поиска Альянса добавлен список \"Рекомендуемые Альянсы\" (далее - РА):\n        - В РА попадают Альянсы, чьё среднее количество очков на игрока в Альянсе не более чем в 5 раз отличается от количества очков текущего игрока\n        - РА сортируется по убыванию модуля разницы очков между количеством очков игрока и средним количеством очков Альянса\n    Нужно отметить, что для начинающих игроков список РА будет, скорее всего, пуст - почти нет активных Альянсов, в которые входят только начинающие игроки\n    Чем дальше играет и развивается игрок, тем больше Альянсов будет в этом списке. Однако, по мере приближения к Топу сервера, этот список будет очевидным образом сужаться\n\n\n2019-08-16 08:06:29 45a14\n[~] ЛС\n    Добавлена иконка игнора в список сообщений\n\n\n2019-08-15 21:10:03 45a13\n[!] Админка/Редактирование планет\n    Переписана и включена админка редактирования планет. Можно редактировать:\n        - Строения\n        - Флот\n        - Оборону\n        - Ресурсы\n    Можно как добавлять юниты, так и удалять их (вводя значения с минусом)\n    На экране просмотра игрока добавлена вкладка со списком планет, клик на которых открыает экран редактирования планеты\n\n[~] Чат/Интерфейс\n    Отключёны автозамена/автокоррекция/итд в строке ввода сообщения\n\n[%] Скины/EpicBlue\n    Добавлена подсветка активного таба в списке табов\n\n\n2019-08-15 18:11:08 45a9\n[%] Игрок\n    playerTimeDiff: Исправлена ошибка в strict mode\n\n\n2019-08-15 00:10:48 45a8\n[!] Личные сообщения\n    Переработан вид сообщений\n    Добавлена функция игнора игроков в ЛС:\n        - В личных сообщениях добавлена возможность добавить игрока в игнор-лист\n        - Сообщения от игроков в игнор-листе не видны в списке ЛС\n        - Убрать игрока из игнор-листа можно на соответствующей вкладке в \"Настройках\"\n\n[@] Код\n    Защита от некорректно объявленного класса-потомка RecordV2\n\n\n2019-08-05 07:28:53 45a3\n[@] Код\n    Глобальные константы PlayerTimeDiff перенесены в константы класса\n\n\n2019-07-30 09:19:03 45a1\n[@] Код\n    Весь код, относящийся к замеру разницы времени, внесён в класс PlayerTimeDiff\n\n\n2019-07-27 08:22:05 44d0\n[!] Project \"SuperNova.WS\" Release 44 \"10 years anniversary\"\n    Project \"SuperNova.WS\" Release 44 \"10 years anniversary\"\n\n\n2019-07-13 15:40:48 44a109\n[~] Платежи\n    Добавлена поддержка отображения приблизительной конечной цены при покупке ММ (не для всех платёжных систем)\n    Немного перевёрстана страница платежей\n\n[@] Код\n    PTL: Добавлен подтип `money` в декоратор `numeric`\n\n\n2019-07-08 10:52:54 44a107\n[%] Платежи\n    Исправлена ошибка при выборе платёжной подсистемы в СН\n\n\n2019-07-08 10:09:17 44a104\n[~] Платежи\n    Поддержка модуля payment_unitpay_form\n\n\n2019-07-04 12:03:06 44a102\n[~] Платежи\n    Поддержка модуля payment_interkassa_form\n\n\n2019-06-25 06:06:10 44a100\n[~] Альянсы\n    Добавлена ссылка на страницу статистики Альянсов - список всех Альянсов и их статистика\n\n\n2019-06-25 05:51:29 44a99\n[~] Контакты\n    Добавлена ссылка на ЛС членов администрации сервера\n    Немного переверстана страница\n\n\n2019-06-07 21:42:31 44a87\n[@] Код\n    Поддержка 10-летия СН\n\n\n2019-05-26 16:46:32 44a84\n[%] Юниты\n    Исправлена ошибка в локализации в предыдущем коммите\n\n\n2019-05-26 16:29:58 44a83\n[~] Юниты\n    Добавлена поддержка юнитов, которые игрок не может построить, но может получить в ходе Фестиваля или других активностей - требование UNIT_CAN_NOT_BE_BUILD\n\n\n2019-03-01 08:56:50 44a73\n[@] Код\n    Улучшена работа с метаинформацией о таблицах в БД\n\n\n2019-03-01 04:44:01 44a71\n[@] Апдейтер\n    Отдельный класс Updater для функций апдейтера\n\n\n2019-02-12 06:34:47 44a41\n[~] Безопасность\n    Небольшой апдейт системы безопасности\n\n\n2019-01-19 13:34:11 44a35\n[+] Флоты/САБ\n    Максимальное количество флотов в САБе ограничено 5\n    Теперь САБ ограничивается по сумме очков игроков:\n        - Ограничение распространяется так же и в большую сторону, т.е. при атаке \"слабыми\" игроками \"сильных\"\n        - В остальном - правила и коэфициенты те же, что и при расчёте сильных/слабых игроков (ака \"нуб-защита\")\n\n\n2018-12-29 15:53:35 44a32\n[@] Код\n    Поддержка СНГ-2019\n\n\n2018-12-28 12:08:14 44a26\n[+] Админка/Активность игрока\n    Красным баром добавлен процент активности в данном часе\n\n\n2018-12-28 10:46:15 44a25\n[!] Админка/Активность игрока\n    Добавлен просмотр активности игрока\n        - Работает только при включённом счётчике посещений\n        - Ссылка - на админской странице просмотра игрока\n        - Просмотреть можно активность с 1 января 2018 года\n        - Интервал просмотра активности - 1 час\n        - Пустой квадратик - активности в этот час не было\n        - Заполненный - была активность. При наведении курсора - указывается час и активность в минутах\n            - Из-за особенностей работы счётчика активность может быть более 1 часа\n\n\n2018-12-22 11:42:20 44a12\n[+] Счётчик\n    Оптимизирована работа счётчика посещений:\n        - Добавлена новая таблица `security_query_strings` для записи параметров запроса\n            - `counter` теперь ссылается на записи в ней, а не на полный URL страницы\n        - Изменена таблица `security_player_entry` - исключён ИД пользователя\n            - `counter` теперь ссылается на записи в ней и не содержит поля с ИД устройства, браузера, IP/прокси\n\n\n2018-12-22 08:27:16 44a10\n[@] Модули\n    Совместимость с модулем 'ad_promo_code'\n\n\n2018-12-21 14:20:19 44a7\n[@] SQL\n    Откат с utf8mb4 к utf8\n\n\n2018-12-21 14:00:41 44a5\n[~] ad_promo_code\n    Поддержка модуля 'ad_promo_code'\n\n[@] Код\n    db_mysql::doquery() - убран неиспользуемый параметр $table\n\n\n2018-11-12 22:51:12 43b2\n[~] Апдейтер\n    Исправлен скрипт установки\n\n\n2018-11-12 08:49:32 43b0\n[!] Project \"SuperNova.WS\" Release 43 \"Double Release\" Beta\n    Project \"SuperNova.WS\" Release 43 \"Double Release\" Beta\n\n\n2018-11-11 03:12:49 43a19.13\n[~] Админка/Альянсы\n    В админке теперь отображается форматирование текстов Альянса\n\n\n2018-11-11 02:45:45 43a19.12\n[~] Админка/Альянсы\n    Ускорено чтение списка Альянсов\n\n[@] Код\n    Класс AccessLogged прокрыт тестами на 100%\n\n\n2018-06-11 20:47:16 43a19.2\n[!] Скины\n    Добавлены полноформатные изображения Наёмников\n\n\n2018-06-11 19:51:26 43a19.1\n[!] Скины\n    Новые изображения Наёмников и Губренаторов в скине supernova-ivash\n    Добавлены большие изображения для Губернаторов\n\n\n2018-06-11 18:58:54 43a19.0\n[!] Скины\n    Новые изображения Наёмников и Губренаторов в скине EpicBlue\n\n\n2018-06-01 06:46:21 43a18.29\n[@] Код\n    Поддержка 9-летия СН\n\n\n2018-05-09 14:34:26 43a18.24\n[~] Игроки/Счётчики\n    Упроядочена работа счётчика игроков онлайн и всего\n\n\n2018-05-04 13:13:49 43a18.23\n[%] Флоты/Транспорт\n    Исправлена ошибка невозможности отправить флот в миссию Транспорт\n\n\n2018-05-04 12:00:45 43a18.22\n[~] Флоты/Шпионаж\n    Кардинально уменьшена эффективность sputnik-ов\n\n\n2018-04-29 19:13:23 43a18.21\n[%] Флоты\n    Исправлена ошибка с отправкой перегруженного флота\n\n[@] Код\n    Небольшой рефакторинг кода\n\n\n2018-04-29 07:28:15 43a18.19\n[@] Код\n    Рефакторинг кода Новостей\n\n\n2018-04-27 14:12:00 43a18.11\n[@] Код\n    Добавлен метод save() в EntityDB - пока без поддержки удаления\n    Переписана отправка флотов с частичным использованием объектов Planet и Fleet\n    Класс RepoV2 теперь не является имплементацией IController. Соответственно - переписаны магические методы как немагические\n\n\n2018-04-27 08:49:24 43a18.10\n[@] Код\n    Функции из includes/db/db_queries_fleet.php переброшены в класс DbFleetStatic\n\n\n2018-04-24 16:17:54 43a18.8\n[@] Код\n    Работа над EntiyDB, Fleet и новым кодом Экспедиции\n\n\n2018-04-24 16:12:45 43a18.7\n[@] Код\n    В обсчёт статистики добавлена чистка пустых записей ACS\n\n\n2018-04-24 15:25:25 43a18.6\n[@] Код\n    Исправлены почти все \"ошибки\" phpStorm в includes/db/db_queries_fleet.php\n\n\n2018-04-24 15:22:08 43a18.5\n[@] Код\n    Трейт TContainer теперь поддерживает табличную трансляцию имён свойств объекта в имена свойств контейнера\n\n\n2018-04-22 13:29:20 43a18.4\n[@] Код\n    Добавлена обработка падения игры по таймауту очистки языкового кэша с использованием xcache\n\n\n2018-04-22 12:43:02 43a18.3\n[~] Флоты/Экспедиции\n    Теперь в Экспедиции не могут быть найдены ивентовые или уникальные мировые корабли\n\n\n2018-04-21 16:19:23 43a18.2\n[@] Код\n    Доработки RepoV2\n    RepoV2 используется на страницах:\n        - Постройки\n        - Ресурсов\n        - Обзор Планеты\n        - Управление Планетой\n    EntityDB:\n        - Шпионаж (через MissionData)\n\n\n2018-04-21 14:08:21 43a18.1\n[@] Код\n    Новые классы для репозитория и хранилища - RepoV2 и StorageV2\n\n\n2018-04-21 12:10:38 43a18.0\n[@] Код\n    Добавлено логгирование фатальных ошибок\n    Работа над новым кодом Экспедиции\n\n\n2018-04-18 11:29:26 43a17.12\n[%] Флоты/Шпионаж\n    Исправлена другая ошибка в вычислении анти-шпионажа\n\n\n2018-04-16 12:59:11 43a17.8\n[+] Админка/Просмотр игрока\n    Добавлена информация об аккаунте игрока\n    Добавлена возможность сменить пароль на аккаунте\n\n[@] Админка/Просмотр игрока\n    Рефакторинг страницы просмотра игрока\n    Убраны почти все подсвеченные phpStorm 'ошибки' SQL (из-за отсутствия поддержки префиксов в phpStorm)\n\n\n2018-04-10 11:29:14 43a17.3\n[%] Флоты/Шпионаж\n    Исправлена ошибка в вычислении анти-шпионажа\n\n\n2018-04-09 09:29:59 43a17.1\n[%] Админка/Настройки\n    Слишком маленькое поле для списка спрятанных игроков обрезало этот список\n\n\n2018-04-08 09:21:12 43a17.0\n[-] Поиск\n    В поиске отключён поиск ботов\n\n\n2018-04-08 09:08:11 43a16.54\n[+] ЛС\n    Админитраторам включены BBCode и поддержка ссылок в отправляемых ими сообщениях\n\n\n2018-04-08 07:50:13 43a16.53\n[~] Игроки/Рендер ников\n    Рендерер званий вынесен в отдельный метод для унификации и использования в остальном коде\n\n\n2018-04-08 06:23:47 43a16.52\n[@] Модули\n    Поддержка core_festival 8a18\n\n\n2018-04-07 14:07:36 43a16.51\n[+] Миссии/Шпионаж\n    Добавлена поддержка анти-шпионажа\n\n\n2018-04-06 07:48:56 43a16.50\n[%] Авторизация\n    Исправлена ошибка с невозможностью зарегестрироваться в игре\n\n\n2018-04-05 21:11:09 43a16.49\n[~] Меню\n    Рамка меню изменена на тонкую\n\n[@] Код\n    Добавлено возобновление таймаута страницы после отработки диспетчера флотов\n    Добавлен отдельный таймаут для выполнения xcache_unset_by_prefix()\n    db_user_count() теперь не считает ботов\n\n\n2018-04-05 19:51:19 43a16.48\n[+] Скины\n    Добавлены картинки технологий для нового ивента\n\n\n2018-04-05 17:39:03 43a16.47\n[@] Код\n    Теперь можно задавать картинки для планет при создании\n\n\n2018-04-05 12:20:21 43a16.46\n[+] Скины\n    Добавлены картинки кораблей для нового ивента\n\n\n2018-04-04 16:49:11 43a16.45\n[!] Админка\n    Добавлена статистика на страницу платежей\n\n\n2018-03-27 22:58:34 43a16.41\n[@] Тесты\n    Исправлены инклюды\n\n\n2018-03-27 08:00:28 43a16.39\n[@] Код\n    Константы разнесены по файлам в includes/constants\n\n\n2018-03-25 21:55:44 43a16.25\n[@] Код\n    Добавлена поддержка модуля admin_balance\n\n\n2018-03-25 12:54:26 43a16.23\n[@] Код\n    Где возможно - вставлены константы USER_BOT_PLAYER итд\n    Прописаны некоторые недостающие поля в classConfig\n    Переделаны player_create() и uni_create_planet() что бы не использовать глобальные переменные\n    В uni_create_planet() добавлена возможность форсировать выбор имени планеты\n\n\n2018-03-25 01:46:34 43a16.18\n[@] Код\n    Еще упрощена и расшита функция eco_get_build_data()\n    Немного переформатирован и почищен код\n\n\n2018-03-24 21:31:51 43a16.16\n[~] Постройки\n    Теперь пункты в списке требований к постройке являются ссылками на статью Новапедии о соответствующем юните/сущности игры\n    Теперь при сносе здания учитываются наличие Губернатора и статус Столицы - снос при Губернаторе и/или на Столице происходит быстрее\n    Теперь время строительства и сноса округляется вверх, а не математически. Это значит, что в среднем в половине случаев время постройки/сноса увеличится на 1 секунду\n\n[@] Код\n    Переработка системы очередей\n    Добавлена таблица для нового хайспота\n    Расшита функция eco_get_build_data(). Часть её функционала вынесена в класс BuildDataStatic, а еще часть сделана хуками\n\n\n2018-03-24 03:14:48 43a16.11\n[@] Код\n    Поддержка изменений в модуле Фестивалей\n\n\n2018-03-23 03:00:04 43a16.8\n[@] Код\n    Поддержка изменений в модуле Фестивалей\n\n\n2018-03-22 20:49:18 43a16.3\n[%] Админка/Добыча игроков\n    Исправлены последствия автозамены по коду. В результате неправильно считало общую добычу. Ручками надо заменять, ручками...\n\n\n2018-03-22 16:28:33 43a16.1\n[~] Админка/Модули\n    Сортировка модулей по активности сделана дефолтной\n\n\n2018-03-22 16:09:47 43a16.1\n[%] Темплейты\n    Принципиально исправлена ошибка, когда скомпилированный код темплейтов не видел $lang - теперь используется доступ к $lang через статик SN\n\n\n2018-03-22 15:50:22 43a16.0\n[-] Темплейты/Меню\n    УБРАНО - Теперь можно добавлять пункты даже в пустое меню\n\n\n2018-03-22 15:05:34 43a15.30\n[!] Вёрстка\n    Сделан более универсальный патч, который снимет потенциальные проблемы с исчезающим скроллом в Сркщьу 65 на любых экранах\n    Соответственно - исправлена ошибка с пропаданием скролла в режиме Сканирования Вселенной на маленьких экранах в Сркщьу\n\n[+] Вселенная\n    Перевёрстана панель выбора Галактики/Системы:\n        - Теперь она будет масштабироваться в зависимости от размера экрана\n        - Ну и на вид стала поприятнее\n    Теперь имена Игроков/Альянсов в случае длинных названий не \"ломаются\" по статусам, а остаются на одной строке\n\n\n2018-03-22 14:02:10 43a15.29\n[!] Админка/Модули\n    Новый экран со списком модулей, доступных в игре и их статусе\n    Есть сортировка по имени модуля и по его активности\n\n[+] Админка/Добыча игроков\n    Добавлена колонка с суммарной производительностью в металле\n    На экран добавлены сортировки\n\n[~] Темплейты/Меню\n    Теперь можно добавлять пункты даже в пустое меню\n[~] Админка/Меню\n    Если в админском меню не стоит AUTH_LEVEL - прописывается высший (AUTH_LEVEL_ADMINISTRATOR = 3)\n\n\n2018-03-22 10:46:30 43a15.27\n[@] Код\n    В модулях из манифеста в свойства класса вынесены $active и $installed\n    Почищен неиспользуемый код\n\n\n2018-03-21 15:06:05 43a15.22\n[%] Юнит-тесты\n    Исправлены юнит-тесты\n\n\n2018-03-21 14:28:18 43a15.20\n[@] Код\n    Раскидал часть классов по неймспейсам\n\n\n2018-03-21 13:09:38 43a15.18\n[@] Код\n    Раскидал часть классов по неймспейсам\n\n\n2018-03-21 12:36:55 43a15.16\n[@] Код\n    Рефакторинг кода\n\n\n2018-03-21 10:24:54 43a15.14\n[%] Конфигурация\n    Исправлена очепятка в коде удаления ключа\n\n\n2018-03-21 09:58:35 43a15.12\n[@] Код\n    Рефакторинг кода\n    Исправлен потенциальный E_NOTICE в классе SnBootstrap\n    В класс \\classConfig добавлен метод __unset() который удаляет переменную из базы\n\n\n2018-03-21 04:06:14 43a15.9\n[@] Код\n    Рефакторинг кода\n\n\n2018-03-21 00:06:40 43a15.6\n[+] Модули\n    Добавлена поддержка MVC-опций в модулях (подмассив 'mvc' манифеста)\n\n[~] Флоты/Интерфейс\n    Теперь при нехватке места в трюме под топливо на странице выбора миссии выводится соответствующее сообщение\n[~] Вселенная/Планеты\n    Упрощена генерация имени планеты\n    Так же учитывается статус Столицы при генерации имени планеты\n\n[@] Код\n    При удалении пользователя теперь:\n        - Удаляются юниты игрока\n        - Не удаляются сообщения, связанные с пользователем - они будут почищены при обслуживании\n        - ВРЕМЕННО не удаляется Альянс, в котором пользователь был последним игроком\n    ВРЕМЕННО При обслуживании не удаляются пустые Альянсы\n    Заменено 'fleet_update_skip' => PAGE_OPTION_FLEET_UPDATE_SKIP\n    Добавлена опция PAGE_OPTION_ADMIN для обозначения страницы как админской до её загрузки\n    Админские страницы теперь не вызывают обсчёт флота (используется опция MVC PAGE_OPTION_ADMIN)\n    Функция DeleteSelectedUser() стала методом в Player\\PlayerStatic\n    Метод Universe::fleetsReturn() для отзыва всех флотов с указанной локации\n\n\n2018-03-20 16:52:45 43a15.4\n[@] Код\n    Класс Modules\\Modules переименован в Modules\\ModulesManager\n    Небольшой рефакторинг предыдущего кода\n\n\n2018-03-20 01:39:16 43a15.2\n[%] Модули\n    Добавлен отсутствующий файл класса Modules\\Manager\n\n\n2018-03-19 23:34:15 43a15.0 - Рефакторинг подсистемы модулей\n[!] Модули\n    Переписана и отрефакторена подсистема управления модулями\n    Добавлены новые методы в базовый класс модулей sn_module\n    Класс sn_module:\n        - Убрана зависимость от полей манифеста модуля:\n            - 'load_order' => getLoadOrder(), self::M_LOAD_ORDER\n    Класс Modules\\Manager:\n        - В этот класс вынесен функционал управления модулями из класса sn_module\n        - Включает в себя так же функционал убраных глобальных переменных $sn_module и $sn_module_list\n    Убрана поддержка модулей из одного файла и модулей без структуры sn_modules\n    sn_sys_load_php_files() - убран блок для $modules = true;\n\n[@] Код\n    Индекс 'require' заменен на P_REQUIRE или TPL_BLOCK_REQUIRE - в соответствии с контекстом\n\n\n2018-03-18 16:14:41 43a14.19\n[@] Код\n    Добавлены классы поддержки чейн-коллов Pimp и Hooker, а так же производный класс SnPimp\n    В GlobalContainer добавлено свойство $pimp\n\n\n2018-03-17 19:32:23 43a14.18\n[%] Альянсы/Дипломатия\n    Исправлены и уточнены сообщения, отправляемые в ЛС при принятии предложений об изменении отношений между Альянсами\n\n\n2018-03-17 18:45:38 43a14.17\n[+] Админка/Настройки\n    Добавлена возможность настраивать опции смены имени игроком\n\n[~] Игрок/Настройки\n    Теперь если смена имени игрока (ника) запрещена настройками сервера - то так и пишется в Настройках Игрока\n[~] Квесты\n    Добавлены разделители тысяч к целям и наградам квестов\n\n[@] Код\n    Добавлена временная функция декодирования строк JSON с фоллбэком на unserialize для старых строк\n\n\n2018-03-16 23:58:18 43a14.12\n[~] Картинки\n    Еще чутка пережаты картинки\n[~] Звуки\n    Немного пережаты звуки\n\n\n2018-03-16 09:19:00 43a14.10\n[@] Код\n    Поддержка модулей player_award 0d5 и core_festival 7a3\n\n\n2018-03-16 09:18:07 43a14.9 core_festival 7a3\n[!] Хайспот/8 Марта\n    Полностью переработан код админки и награждения\n    Теперь так же в админке начисляется ММ и выдаются Памятные Знаки Кавалеров\n\n\n2018-03-16 09:12:57 43a14.8 player_award 0d5\n[+] 8 марта\n    Разделены награды за количество ММ и за персон для Кавалеров и Дам\n    Добавлены все нужные изображения медалей\n\n\n2018-03-14 14:55:23 43a14.5\n[@] Код\n    Поддержка player_award 0d4\n\n\n2018-03-14 14:49:35 43a14.4 player_award 0d4\n[+] 8 марта\n    Универсальные медали и памятные знаки для ивента 8 марта\n\n\n2018-03-13 20:05:09 43a14.3\n[!] Настройки\n    Рефакторинг кода и переработка дизайна страницы настроек\n\n\n2018-03-13 12:12:39 43a14.2 menu_customize 0d9\n[%] Настройки\n    Исправлена ошибка, не дающая сохранить настройки\n\n\n2018-03-12 22:46:54 43a14.1\n[@] Код\n    Класс classSupernova переименован в SN\n\n\n2018-03-12 17:39:41 43a13.39\n[~] О сервере\n    В футере и на странице информации о сервере к версии движка теперь добавляется номер патча БД\n\n\n2018-03-12 17:03:26 43a13.38\n[~] Админка/Меню\n    Название уровня члена АД-ии (Модератор, Оператор итд) теперь является ссылкой на возврат в игру\n\n\n2018-03-12 16:16:59 43a13.36\n[~] Экономика/Исследования\n    Изменено название страницы на 'Исследование технологий', что бы не было путаницы между пунктами меню \"Исследования\" и \"Технологии\"\n    Добавлена ссылка на закладку списка Технологий в Новапедии\n\n\n2018-03-12 15:13:17 43a13.35\n[~] Новости\n    Цвет ссылки \"Подробнее...\" изменен на ярко-голубой для лучшей читаемости\n\n\n2018-03-12 15:05:46 43a13.34\n[@] Вёрстка\n    Хак для Google Chrome v65, возвращающий горизонтальную полосу прокрутки на широких экранах (например, \"Империя\" и \"Массовые Операции\")\n\n\n2018-03-12 13:23:10 43a13.33\n[@] БД\n    Увеличен размер поля `value` в таблице `player_options` до 16000 символов - перестали влезать настройки меню, гггг\n\n\n2018-03-11 21:07:04 43a13.32\n[@] Код\n    Немного переделан автолоадер\n\n\n2018-03-11 20:55:53 43a13.31\n[@] Код\n    Немного переделан автолоадер\n\n\n2018-03-11 20:34:00 43a13.29 player_login_token 0a8 - Струя удачи\n[!] Струя удачи\n    Струя удачи:\n        - Теперь при входе в игру несколько дней подряд количество токенов, получаемых игроком в день, медленно увеличивается - это и называется \"струя удачи\"\n        - Если игрок не входит в игру хотя бы один - \"струя\" иссякает и количество токенов в день опять падает до 1\n        - Максимальное количество токенов, которые можно получить в день - 6\n    Сильно переработан попап с оповещением о получении токена. Теперь в нём выводится практически вся доступная информация по токену\n    Расширена и переработана подсказка игрока на странице\n\n\n2018-03-11 17:18:10 43a13.28\n[@] Код\n    Автолоадер:\n        - Теперь по одному пути может быть несколько неймспейсов;\n        - Теперь можно загружать классы с любыми неймспейсами из произвольных мест файловой системы:\n            - При регистрации пути в автолоадере задаётся классовый префикс;\n            - Этот префикс игнорируется в неймспейсе класса при генерации имени файла;\n            - Классовый префикс может быть как полным, так и частичным;\n            - Например, при задании префикса \"Module\" и пути \"folder\" класс \"Module\\Test\\Sub\" будет грузится из каталога \"folder\\Test\\Sub\"\n    Опции игрока:\n        - Добавлена возможность форсирования загрузки опций из БД, минуя кэш в памяти\n    Добавлено несколько констант времени и его форматирования\n    Поддержка player_login_token 0a8\n\n\n2018-03-11 12:08:38 43a13.26\n[@] Код\n    Код инициализации автолоадера вынесен в конструктор класса sn_module\n\n\n2018-03-07 12:00:47 43a13.24\n[%] Апдейтер\n    Исправлена ошибка повторного обновления таблицы для core_festival\n\n\n2018-03-07 09:23:41 43a13.23\n[@] Код\n    Поддержка core_festival 7a0\n\n\n2018-03-07 09:19:00 43a13.21 core_festival 7a0\n[#] core_festival 7a0\n    [!] Хайспот/8 Марта\n        Добавлена возможность Кавалеру открыть свой ник\n        Добавлена возможность добавить текстовое поздравление к подарку\n        В списках подарков у Дам и Кавалеров каждый подарок теперь выводится отдельно\n        В списке полученных подарков у Дам:\n            - Выводится поздравление, если оно есть;\n            - Выводится игровой ник подарившего, если он решил его открыть;\n        В списке отправленных подарков у Кавалеров:\n            - Выводится значок (А) после имени Дамы, если подарок был сделан анонимно, т.е. без раскрытия ника;\n            - Выводится поздравление, если оно есть;\n        Теперь когда Кавалер дарит подарок, Даме отправляется личное сообщение с уведомлением. Сообщение содержит:\n            - Подаренную и начисленную (с учётом подарка от АД-ии) суммы ММ;\n            - Имя Кавалера, если он решил его раскрыть;\n            - Текст поздравления, если он есть\n\n\n2018-03-05 22:06:27 43a13.20\n[@] Код\n    Поддержка player_login_token 0a6\n\n\n2018-03-05 22:04:24 43a13.19 player_login_token 0a6\n[!] Алгоритм\n    Изменён алгоритм генерации таблицы призов:\n        - На порядки увеличено количество получаемых кораблей;\n        - Очень значительно увеличены шансы выпадения Артефактов, а так же доступность Артефактов по уровням игроков;\n    В целом, очень значительно увеличилась средняя ценность приза\n\n[%] Артефакты\n    Исправлено неправильное отображение количества Артефактов при выигрыше - они всегда выигрываются по 1 штуке\n\n\n2018-03-05 13:43:56 43a13.17\n[@] Код\n    Класс PlayerLevelHelper теперь всегда выдаёт полную таблицу для всех уровней игроков, а не только для тех, что есть в БД\n\n\n2018-03-05 13:39:34 43a13.16\n[!] Админка/Добыча игроков\n    Страница просмотра общей добычи игроков\n\n[@] Код\n    Рендерер ников теперь проверяет наличие соответствующих полей в массиве игрока\n\n\n2018-03-03 08:43:49 43a13.14\n[!] Столица\n    Теперь на Столице скорость строительства зданий, флота и обороны повышена в два раза\n    Теперь на Столице скорость добычи ресурсов повышена в два раза\n    Добавлена индикация Столицы в имени планеты иконкой ♕ &#9813;\n        - В списке планет в навбаре\n        - На странице \"Обзор планеты\"\n        - На странице \"Империя\"\n        - На странице \"Вселенная\"\n\n[+] Вселенная/Луны\n    Добавлена индикация Луны в имени планеты иконкой ☽ U+263D &#9789;\n        - В списке планет в навбаре\n        - На странице \"Обзор планеты\"\n        - На странице \"Империя\"\n\n\n2018-03-01 15:22:32 43a13.13\n[@] Дизайн\n    Добавлен стиль для модуля chat_advanced\n\n\n2018-03-01 15:18:13 43a13.11 chat_advanced 6a4\n[+] Респонзивный дизайн\n    Улучшено отображение чата для экранов разных размеров\n    Добавлено автоматическое скрытие правой панели (списка онлайн игроков) на мелких экранах с возможностью показать список онлайн\n\n\n2018-03-01 11:31:14 43a13.10\n[~] Чат\n    Оптимизированы CSS-стили\n\n\n2018-03-01 10:53:41 43a13.9\n[~] Чат\n    Оптимизированы CSS-стили\n    Вынесен JS-код из темплейта\n\n\n2018-03-01 09:17:46 43a13.8\n[@] Дизайн\n    Добавлен стиль для модуля chat_advanced\n\n\n2018-03-01 09:15:05 43a13.7\n[%] Чат\n    Возвёрнуты назад скобки к вставляемому нику при ссылке на ник\n\n\n2018-03-01 08:15:48 43a13.6\n[~] Чат\n    Доработан вид сообщений в чате:\n        - Выровнены время сообщения, ник (и иконки) и само сообщение\n        - Уменьшен размер передаваемых данных для приходящих сообщений\n        - Улучшен генерируемый HTML-код\n\n[~] Воинские звания\n    Немного изменены иконки званий младшего офицерского состава (9-11 уровень)\n\n\n2018-03-01 08:14:09 43a13.5\n[%] Вселенная\n    Исправлено задвоение значка \"Отпуск\"\n\n\n2018-02-28 09:44:39 43a13.3\n[+] Воинские звания\n    Добавлен дополнительный уменьшенный комплект иконок специально для ников - для большей отзывчивости интерфейса\n    На иконки воинских званий в никах добавлены всплывающие подсказки с названиями званийи и уровнем\n\n[+] Админка/Список игроков\n    К никам игроков добавлены иконки званий, отпуска, ДР и наград\n    Уменьшен размер страницы\n\n\n2018-02-28 07:29:37 43a13.2\n[~] Картинки\n    Еще немножко уменьшены размеры картинок\n\n\n2018-02-28 04:51:31 43a13.0 - Воинские звания\n[!] Воинские звания\n    В игру добавлены уровни игроков и военные звания для каждого уровня\n    Уровни присваиваются в зависимости от общего количества очков игрока\n    Если в настройках сервера отключён показ статистики для команды сервера, то все члены команды имеют 20й уровень и соответствующее звание вне зависимости от реального количества очков\n    Звания показываются перед ником игрока везде, где это имеет смысл\n    Так же картинка звания показывается на странице профиля игрока (ака \"Император\")\n    Всего в игре 21 звание - от Курсанта (уровень 0) до Генералиссимуса (уровень 20)\n    Посмотреть полный список званий и соответствующих им уровней можно кликнув на картинку звания на странице Император\n\n[~] Игроки/Ники\n    Вид ников во всей игре унифицирован с новым видом на странице \"Статистика\"\n\n\n2018-02-27 09:33:47 43a12.12\n[~] Игроки/Статистика\n    Изменен порядок вывода иконок для имени игрока\n\n\n2018-02-27 09:31:55 43a12.11\n[~] Игроки/Статистика\n    Убрана отдельная колонка для ссылки на страницу игрока. Теперь клик на нике является такой ссылкой\n    Добавлено выделение названия Альянса как ссылки\n    Изменен порядок вывода иконок для имени игрока\n\n\n2018-02-27 08:58:10 43a12.10\n[%] Игроки/Ники\n    Исправлена ошибка некорректного рендеринга ников\n\n\n2018-02-27 08:32:46 43a12.8\n[+] Апдейтер\n    Добавлен механизм патчей: проверка существования патча и его регистрация\n    Отформатирован код\n\n[~] Игроки/Ники\n    В рендерере ников функции serialize()/unserialize() заменены на функции json_encode()/json_decode()\n    Добавлена возможность сортировки элементов ника в произвольном порядке\n\n\n2018-02-18 16:12:58 43a12.7\n[@] Код\n    В бутстрап добавлено время на инициализацию игры при холодном кэше\n\n\n2018-02-18 15:56:13 43a12.6\n[%] Игроки\n    Исправлена ошибка расчёта максимального уровня Игрока в игре\n\n[@] Код\n    Продолжение разработки нового кода для Экспедиции\n    Новый класс Common\\OutcomeManager для выбора одного варианта из массива возможных вариантов с разными шансами на появление\n\n\n2018-02-17 21:13:29 43a12.5\n[+] Симулятор\n    Добавлен чекбокс \"Симуляция\", отключающий фактор случайности и гарантирующий повторяемость результатов Симулятора\n\n\n2018-02-15 14:01:10 43a12.4\n[+] Симулятор\n    Теперь при создании Луны указывается количество обломков, которое образовалось бы без создания Луны\n\n[~] Артефакты\n    Артефакты класса \"Крюк\" теперь не меняют количество обломков на орбите - т.е. Луна появляется без траты обломков\n\n[%] UBE\n    Исправлена ошибка с нулевым размером Луны в отчётах\n\n\n2018-02-13 12:31:38 43a12.2\n[+] Вселенная/Луны\n    Теперь при создании лун учитывается текущая скорость добычи ресурсов:\n        - Например, для сервера со скоростью добычи x10 стоимость создания луны так же удесятеряется\n        - Исправлена древняя ошибка, из-за которой луна после боя могла получится больше, чем на неё потрачено ресурсов\n    Унифицированы алгоритмы создания лун после боя, через Артефакты, через Админку итд\n\n[@] Код/UBE\n    Продолжен рефакторинг кода UBE\n\n\n2018-02-13 09:05:46 43a12.1\n[%] UBE\n    Исправлена ошибка\n\n\n2018-02-13 07:10:16 43a12.0\n[@] Код/UBE\n    Сворачивание процедур UBE в классы (UBEv4.1)\n\n\n2018-02-13 05:18:54 43a11.9\n[+] Список планет\n    В списке планет на иконках построек/верфи/обороны добавлено количество активных слотов в очереди\n\n\n2018-02-05 13:03:26 43a11.8\n[!] Темплейты\n    Добавлен класс Ptl\\PtlVariableDecorator для форматирования текста в блоках по разметке темплейта\n        - В блоках вызовы prettyNumberStyledDefault() заменены на форматирование внутри темплейтов;\n\n\n2018-02-01 10:46:35 43a11.7\n[@] Код\n    Рефакторинг страницы \"Империя\" и связанного с ней кода\n\n\n2018-02-01 09:28:23 43a11.6\n[@] Код\n    Рефакторинг страниц \"Обзор Планеты\" и \"Империя\" и связанного с ней кода\n\n\n2018-02-01 08:12:31 43a11.5\n[%] Обзор Планеты\n    Исправлена ошибка с неразделением флотов в списке летящих на текущую и другие планеты\n\n\n2018-02-01 07:41:38 43a11.4\n[%] Обзор Планеты\n    Исправлена ошибка с непоявлением количества входящих ресурсов в некоторых случаях\n\n\n2018-01-25 11:08:40 43a11.3\n[@] Код\n    Рефакторинг страницы \"Обзор Планеты\" и связанного с ней кода\n\n\n2018-01-25 05:28:57 43a11.2\n[@] Код\n    Небольшой рефакторинг Губернаторов\n\n\n2018-01-08 16:36:57 43a11.0\n[@] Код\n    Функционал найма Губернаторов вынесен в отдельный класс\n\n\n2018-01-01 21:21:28 43a10.23\n[%] Флот\n    Исправлена отладка в коде\n\n\n2017-12-26 19:50:26 43a10.22\n[%] Новости\n    Убрана кнопка \"Добавить новость\" для не-администрации\n\n\n2017-12-19 00:30:38 43a10.21\n[+] BBCode\n    Добавлена поддержка BBCode [news=XXX] для вставки прямой ссылки на Новость\n    Добавлена поддержка сокращённого URL news://XXX для вставки прямой ссылки на Новость\n[+] Чат\n    Добавлена трансляция в сообщении полного URL с ссылкой на новость в BBCode\n    Добавлена трансляция в сообщении сокращённого URL news://XXX в BBCode\n\n\n2017-12-18 23:45:31 43a10.19\n[+] Новости\n    Добавлена возможность ссылаться на отдельную новость:\n        - Ссылка на новость \"прячется\" под датой новости;\n        - При открытии - открывается только одна указанная Новость;\n        - Под заголовком страницы добавляется ссылка \"Все новости\" для возврата к полному списку новостей;\n\n\n2017-12-18 23:00:28 43a10.17\n[+] Новости\n    Добавлена листалка на страницу Новостей. Теперь можно посмотреть Новости за всю историю игры!\n    Добавлен заголовок страницы \"Новости\"\n    Форма добавления новости спрятана под кнопку \"Добавить новость\" - что бы не мозолила АД-ии глаза\n\n\n2017-12-17 13:44:13 43a10.12\n[~] Изображения\n    Пережаты изображения без потерь качества\n\n\n2017-12-13 16:56:27 43a10.0\n[@] Код\n    Добавлен хелпер для рассчёта уровней игроков \\Player\\PlayerLevelHelper\n\n\n2017-12-13 13:35:46 43a9.19\n[@] Код\n    Добавлен механизм дополнительного рендеринга элементов страницы через $template_result[TEMPLATE_EXTRA_ARRAY]\n\n\n2017-12-13 10:37:53 43a9.18\n[~] Флоты\n    Теперь статус \"Новый игрок\" рассчитывается с учётом скорости добычи ресурсов\n[~] Модули\n    Поддержка player_login_token 0a0\n\n[@] Код\n    Альфа-версия класса MissionExplore\n\n\n2017-12-06 15:33:17 43a9.15\n[@] Код\n    Небольшой рефакторинг и еще один юнит-тест для подсистемы бонусов\n\n\n2017-12-04 04:02:37 43a9.13\n[@] Код\n    Функция sn_module_get_active_count() перемещена в sn_module::sn_module_get_active_count()\n\n\n2017-12-04 03:48:38 43a9.11\n[@] Код\n    Из файла general.php часть функций вынесены в отдельные файлы и классы\n\n\n2017-12-04 01:18:00 43a9.8\n[@] Код\n    Переписана работа BonusAtomXXX и связанных классов\n\n\n2017-12-03 23:25:54 43a9.7\n[@] Код\n    Юнит-тесты для части механизма бонусов\n\n\n2017-12-03 02:00:47 43a9.5\n[@] Код\n    В описании юнитов 'bonus_type' заменено на P_BONUS_TYPE, 'bonus' заменено на P_BONUS_VALUE\n\n\n2017-12-02 17:29:26 43a9.4\n[@] Код\n    Класс BonusDescription развернут в семейство классов BonusAtomXXX\n    Переработан класс BonusListAtom (бывш BonusDescriptionList)\n\n\n2017-12-02 09:54:39 43a9.3\n[@] Код\n    Альфа-версия поддержки авторасчёта бонусов\n    Механизм бонусов используется для расчёта скоростей игры, добычи и флота\n\n\n2017-12-01 03:13:30 43a9.0\n[@] Код\n    Добавлен плейсхолдер Bonus\\BonusCatalog\n\n\n2017-11-28 12:43:36 43a8.8\n[+] Админка\n    Доработана админка Альянсов\n\n[@] Код\n    AllianceStatic разобран на классы Alliance\\*\n\n\n2017-11-28 03:07:30 43a8.7\n[+] Сообщения\n    При просмотре сообщений оставлена лишь кнопка \"Удалить отмеченные сообщения\" ввиду бессмысленности остальных опций\n    Если нет отмеченных сообщений кнопка \"Удалить...\" неактивна\n    При просмотре сообщений добавлена кнопка \"Показать все\" - для показа всех сообщений в категории\n\n\n2017-11-26 07:11:02 43a8.6\n[~] Обучение\n    Обучение по умолчанию отключено\n\n\n2017-11-26 06:54:14 43a8.5\n[~] Награды/Бессмертный\n    Лимит для получения статуса \"Бессмертный\" увеличен до 200.000 ММ\n\n\n2017-11-26 06:45:10 43a8.4\n[~] Обновление\n    Количество ММ для награды Бессмертного теперь берется из конфигурации\n\n\n2017-11-26 06:40:25 43a8.3\n[+] Награды/Бессмертный\n    Теперь не меняется дата/время установки статуса после повторных начислений ММ\n    Теперь так же при установке статуса начисляется памятный знак\n    Памятный знак будет начислен всем Бессмертным при апдейте\n\n\n2017-11-26 05:22:37 43a8.2\n[+] Платежи\n    В платежи добавлена листалка\n\n\n2017-11-26 00:07:35 43a8.1\n[!] Сообщения\n    Добавлено листание сообщений по страницам. Каждая страница вмещает по 10 сообщений\n    На странице просмотра сообщений в дроп-дауне выделены цветом удаление всех сообщений категории и всех личных сообщений\n    Убрано удаление неотмеченных сообщений. Теперь просто отметить мультигалочкой (галочкой в заголовке) все сообщения на странице и удалить их\n\n\n2017-11-25 19:30:17 43a8.0\n[@] Код\n    Добавлена класс-пейджинатор для текстовых sql-запросов\n\n\n2017-11-24 05:07:29 43a7.16\n[#] Поддержка core_festival 6a8\n\n\n2017-11-04 20:30:11 43a7.11\n[%] Вселенная\n    Исправлена ошибка неудаления проэкспайрившихся лун\n\n\n2017-10-31 00:52:03 43a7.0\n[@] Код\n    Из класса classSupernova в отдельный класс _SnCacheInternal вынесены все операции с внутренним кэшем\n\n\n2017-10-30 20:33:48 43a6.18\n[+] Админка\n    Добавлен емейл игрока в список игроков (для auth_level 3)\n    Добавлен статус отпуска игрока в список игроков (для auth_level 3) и сортировка по этому полю\n\n[~] Обслуживание\n    Обслуживание теперь так же не удаляет админские сообщения\n\n[%] Миссии/Шпионаж\n    Исправлена ошибка, если не найдена игрок-отправитель флота или цель/хозяин цели Шпионажа\n[%] Код\n    Скорректировано несколько инклюдов для вызова из Админки - пути к файлам расширены до полных\n\n\n2017-10-24 06:15:11 43a6.15\n[~] Навбар\n    Добавлена поддержка изменения ТМ/ММ во время работы страницы\n\n\n2017-10-20 05:23:19 43a6.6\n[~] Чат\n    Поддержка модуля чата\n\n\n2017-10-19 20:26:27 43a6.5\n[~] Админка/Перепаковка счётчиков посещений (СП)\n    Теперь выводится результат перепаковки\n    Теперь считается количество обработанных записей\n    Теперь неизменённые записи не записываются в БД\n    Добавлен счётчик обработанных и пропущенных записей\n    Добавлено новое условие прерывания обработки - если разница между ID обработанных записей меньше размера пакета\n    Добавлено дополнительное время на операцию удаления записей\n\n\n2017-10-18 09:27:27 43a6.1\n[~] Админка/Перепаковка счётчиков посещений (СП)\n    Теперь не пакуются хиты свежее трёх недель\n    Теперь так же считаются общие хиты за визит\n    Теперь записи удаляются и обновляются пакетом\n    Теперь корректно обрабатываются визиты, находящиеся на стыке пакетов\n\n\n2017-10-17 09:49:24 43a6.0\n[!] Админка/Перепаковка счётчиков посещений (СП)\n    См. страницу \"Утилиты\". Работает на машинах с 2Gb+ под PHP\n\n[~] Админка/Обслуживание\n    Очистка СП от записей несуществующих пользователей\n\n[@] Код\n    \\DBAL\\DbMysqliResultIterator и метод db_mysql->selectIterator()\n\n\n2017-10-16 08:00:38 43a5.3\n[%] Интерфейс/Строительство\n    В подсказке по изменению производительности шахт убрано смещение в строке с текущим уровнем шахты\n    Изменена индикация бонусов за уровни Астрокартографии\n\n\n2017-10-15 11:54:26 43a5.0\n[@] Код\n    Рефакторинг /includes/functions/eco_get_planet_caps.php -> \\Meta\\Economic\\ResourceCalculations\n\n\n2017-10-13 00:55:41 43a4.9\n[@] Код\n    Рассчитываемое поле 'caps' планеты теперь нигде не используется\n\n\n2017-10-12 23:07:52 43a4.7\n[%] Миссии/Шпионаж\n    Исправлена ошибка обнаружения флота при вероятности 0%\n    Удалён неиспользуемый код\n\n\n2017-10-12 16:18:03 43a4.6 - Переработка шпионажа\n[~] Миссии/Шпионаж\n    Миссия переписана с нуля\n    Теперь в таблице `messages` сохраняются данные, а не HTML-код:\n        - Значительно уменьшен объем данных, сохраняемых в БД;\n        - Увеличено количество информации в БД;\n        - Рендеринг сообщения осуществляется непосредственно при просмотре, а не при сканировании;\n        - Уменьшено время на обсчёт миссии (см.пред.пункт);\n    Исправлена ошибка, при которой флоты с шансом обнаружения выше 100% не уничтожались\n    Теперь на электронную почту не отправляются полные шпионские отчёты, а лишь уведомление о сообщении типа \"шпионаж\" (без различия входящих и исходящих)\n\n\n2017-10-12 12:07:40 43a4.5\n[@] Код\n    Рефакторинг \\Fleet\\MissionEspionage\n\n\n2017-10-12 10:03:45 43a4.4\n[@] Код\n    Рефакторинг /includes/includes/flt_mission_spy.php -> \\Fleet\\MissionEspionage\n\n\n2017-10-11 12:51:50 43a4.3\n[@] БД\n    Добавлен признак json-кодирования `message_json` в таблице `messages`\n\n\n2017-10-10 11:21:44 43a4.2\n[~] Сообщения\n    Добавлены разделители тысяч в количестве юнитов в шпионском отчёте\n\n\n2017-10-08 17:34:41 43a4.0\n[@] Код\n    ЛС - рефакторинг: \\messages.php -> \\Deprecated\\PageMessages\n\n\n2017-10-04 08:44:41 43a3.15\n[%] Админка/Альянсы\n    Исправлена передача Альянса\n\n\n2017-10-04 08:37:41 43a3.14\n[@] Код\n    Рефакторинг ActiveRecordAbstract\n\n\n2017-10-04 02:57:26 43a3.12\n[@] Код\n    Функция pretty_number() убрана из кода\n\n\n2017-10-04 01:59:42 43a3.9\n[@] Код\n    Вызов pretty_number() убран из базового кода\n\n\n2017-10-04 01:39:39 43a3.8\n[@] Код\n    Вызов pretty_number() убран почти отовсюду\n\n\n2017-10-04 01:21:05 43a3.6\n[%] Интерфейс/Ресурсы\n    Исправлена невозможность смены ядра планеты\n\n[@] Код\n    Вызов pretty_number() заменён на prettyNumberStyledCompare()\n\n\n2017-10-04 00:30:00 43a3.5\n[@] Код\n    Вызов pretty_number() заменён на prettyNumberStyledDefault() там, где было простое стилизирование чисел от 0\n\n\n2017-10-03 23:58:20 43a3.4\n[@] Код\n    Вызов pretty_number() заменён на HelperString::numberFloorAndFormat() там, где было простое форматирование чисел\n\n\n2017-10-02 22:47:16 43a2.26\n[@] Код\n    PTL:\n        - Добавлен класс \\phpbb_hook из phpBB3 для поддержки работы PTL (из phpBB3 /includes/hooks/index.php)\n        - Прописан phpDoc для хинтов класса\n\n\n2017-10-02 22:36:38 43a2.25\n[@] Код\n    PTL - Вызов eval() инкапсулирован в отдельный метод \\template()->evaluate() для облегчения игнора предупреждений в code sniffers\n\n\n2017-10-02 22:13:00 43a2.24\n[@] Код\n    PTL - Исправлена ошибка неправильного ре-рендера при отключённом минификаторе\n\n\n2017-10-01 17:43:27 43a2.22\n[~] Империя\n    Добавлен новый уровень цветового кодирования процентов производства. Теперь схема выглядит так:\n        - Голубой - 100% производства\n        - Зеленый - 90-80%%;\n        - Жёлтый - 70-50%%;\n        - Оранжевый - 40-10%%;\n        - Красный - 0%;\n    Теперь фон дроп-дауна так же подсвечивается в зависимости от процента производства по вышеописанной схеме\n    Добавлено цветовое кодирование для процентов в дропдауне - см. выше. Цвет букв более яркий для лучшего чтения на фоне текущего производства\n\n[@] Код\n    /includes/pages/imperium.php -> \\Deprecated\\PageImperium с оптимизацией и рефакторингом\n\n\n2017-10-01 13:51:33 43a2.18\n[@] Git\n    Отладка хуков\n\n\n2017-10-01 13:25:49 43a2.16\n[@] Код\n    Добавлен класс \\Meta\\Economic\\EconomicHelper\n        - Метод ::getResourcesExchange() - бывш. general.php::get_resource_exchange()\n        - Юнит-тесты\n\n\n2017-10-01 11:45:30 43a2.15\n[~] Проверка верcии движка\n        - /ajax_version_check.php преобразован в класс \\Tools\\VersionCheckerDeprecated\n        - В init.php переделан вызов версион-чекера\n\n\n2017-09-30 10:41:09 43a2.10\n[@] Код\n    Флоты/Своз ресурсов:\n        - /includes/includes/flt_page5.inc - преобразован в класс \\Deprecated\\PageFleet5Gathering.php\n        - Из темплейта fleet5.tpl.html извлечён JS-код\n        - Рефакторинг\n\n\n2017-09-30 07:56:16 43a2.8\n[~] Интерфейс/Наёмники+Планы\n    Добавлен список требований при покупке перманентных Наёмников\n    Отключены элементы покупки при неудовлетворённых требованиях по юнитам (в случае перманентных Наёмников)\n    Убрано дублирование сообщения об ошибках\n\n\n2017-09-30 05:53:35 43a2.4\n[@] Код\n    \\Deprecated\\PageMercenary - рефакторинг\n\n\n2017-09-30 05:42:07 43a2.3\n[@] Код\n    /includes/includes/mrc_mercenary.php - преобразован в класс \\Deprecated\\PageMercenary\n\n\n2017-09-30 05:13:36 43a2.2\n[@] Код\n    /galaxy.php - Еще упрощён код\n    /includes/functions/qst_quest.php - Рефакторинг кода\n\n\n2017-09-25 11:11:39 43a2.0 - Улучшение Обзора Империи\n[+] Империя\n    В тайтл страницы (заголовок окна браузера) добавлено название страницы \"Обзор Империи\"\n    Добавлены разделители тысяч ко всем количествам юнитов: имеющихся на планете, стоящих в очереди, летящих с флотами\n    К количеству ресурсов на планете и производству добавлена индикация ресурсов на летящих на планету флотах\n\n\n2017-09-25 04:15:45 43a1.33\n[@] Код\n    /galaxy.php\n        - Убраны все замечания от IDE;\n        - Оптимизирована работа с Лунами;\n        - Оптимизирована работа с обломками;\n        - Уменьшено количество холостых циклов;\n    /includes/pages/imperium.php\n        - Рефакторинг\n    /overview.php\n        - Небольшой рефакторинг для лучшей совместимости с Империей\n\n\n2017-09-24 23:52:53 43a1.28\n[@] Код\n    Флоты:\n        - serialize/unserialize для usedfleet заменено на пару json_encode/json_decode\n        - /includes/include/flt_page1.php:127 - закомментирована как неиспользуемая\n              //'ships'           => str_rot13(base64_encode(serialize($ships))),\n\n\n2017-09-24 22:50:09 43a1.26\n[@] Код\n    /admin/admin_metamatter.php:81 - Исправлена ошибка сравнения вместо использования empty()\n\n\n2017-09-24 22:35:54 43a1.25\n[@] Код\n    /admin/admin_locale.php:122 - more strict string sanitizing\n        //fwrite($file_handler, \"'\" . str_replace(array(\"\\\\\", \"'\"), array('\\\\\\\\', \"\\\\'\"), $string_value[$lang_id]) . \"',\");\n        fwrite($file_handler, \"'\" . addslashes($string_value[$lang_id]) . \"',\");\n    ActiveRecordAbstract\n        - Fixed phpDoc\n        - Replaced direct field name 'id' with aliased class constant\n            - :273\n                //$recordId = $this->id;\n                $recordId = $this->{self::ID_PROPERTY_NAME};\n            - :310\n                //$this->id = $this->dbLastInsertId();\n                $this->{self::ID_PROPERTY_NAME} = $this->dbLastInsertId();\n        - :460 - Fixed loose comparsion\n    HelperString: removed debug code\n\n\n2017-09-24 19:31:43 43a1.24\n[%] Статистика/Обсчёт\n    Исправлено неправильное определение длительности последней операции\n\n\n2017-09-24 19:15:32 43a1.23\n[%] Код\n    Добавил отсутствующие файлы\n\n\n2017-09-24 19:12:30 43a1.22\n[~] Интерфейс/Окно сообщений\n    Теперь при отключении сервера не показывается навбар - не производится лишних расчётов/обращений к БД\n[~] Бенчмарк\n    Добавлена дата и время начала операции, для которой проводится бенчмарк\n[~] Статистика/Обновление\n    Теперь при сбое всех стандартных способов уведомления о занятости сервера выдаётся стандартная простая заглушка\n\n[@] Код\n    classConfig: В аннотацию добавлены некоторые переменные для поддержки со стороны IDE\n    classPersistent: Немного документации для метода pass()\n    /includes/db/db_queries.php: Удалена часть запросов, относящихся исключительно к подсчёту статы\n    Статистика:\n        - Дефолтное минимальное время между запусками статы вынесено в константу STATS_RUN_INTERVAL_MINIMUM;\n        - Функции по обработке статистики вынесены в классы:\n            - Статический класс StatUpdateLauncher, который управляет запуском обновления;\n            - Статический класс StatCalculator - расчёт статистики\n\n\n2017-09-24 12:57:56 43a1.21\n[@] Код\n    ActiveRecords:\n        - Переупорядочены расположения методов в классах\n        - Добавлены юнит-тесты на базовую функциональность ActiveRecords\n\n\n2017-09-24 12:54:26 43a1.20\n[@] Код\n    Отладка - исправлена невозможность сохранения дампа при наличии closure в любом уровне аргументов бэктрейса\n\n\n2017-07-12 05:26:44 43a1.18\n[@] Код\n    Работа над ActiveRecords\n\n\n2017-06-28 09:17:53 43a1.17\n[@] Код\n    Класс AccessLogged и тесты для него\n\n\n2017-06-24 07:46:31 43a1.15\n[@] Код\n    Простенькая глобальная шина событий EventBus\n\n\n2017-06-19 17:03:45 43a1.13\n[@] Код\n    ActiveRecord->defaultValues():\n        - Выставляет для отсутствюущих полей значения по умолчанию;\n        - Значения по умолчанию берутся из схемы таблицы в БД;\n        - Поддерживает CURRENT_TIMESTAMP для типа поля `timestamp` - будет установлено в SN_TIME_SQL;\n        - Поля со свойством auto_increment игнорируются;\n        - Используется в insert(), update() и fromProperties()\n\n\n2017-06-19 16:09:10 43a1.12\n[@] Код\n    Класс TableSсhema - получение схемы таблицы\n\n\n2017-06-19 12:17:46 43a1.11\n[@] Код\n    Работа над ActiveRecord\n\n\n2017-06-17 15:35:00 43a1.3\n[@] Код\n    ActiveRecordStatic переделан в обычный ActiveRecord\n\n\n2017-06-16 13:20:43 43a1.2\n[@] Код\n    Начата переделка ActiveRecordStatic в нормальный ActiveRecord\n\n\n2017-06-16 13:15:07 43a1.1\n[~] Админка/Альянсы\n    Доработано форматирование\n\n\n2017-06-15 23:18:03 43a1.0\n[%] Админка/Альянсы\n    Исправлена очепятка\n\n\n2017-06-15 21:19:53 43a0.16\n[@] Код\n    Патч для устранения в разнице работы basename() под Windows и Linux\n\n\n2017-06-15 20:48:17 43a0.15\n[!] Админка/Альянсы\n    Добавлена страница для просмотра списка Альянсов\n    Добавлена страница для просмотра отдельного Альянса:\n        - Дамп записи Альянса;\n        - Список игроков с рангами, онлайном, отпуском, баном;\n        - Передача Альянса другому игроку;\n        - Цветовое кодирование Главы:\n            - Зеленый - активен на протяжении 30 дней;\n            - Красный - неактивен.\n\n\n2017-06-15 06:59:23 43a0.13\n[@] Код\n    Убрана global $skip_fleet_update\n\n\n2017-06-15 06:43:59 43a0.11\n[@] Код\n    Перенес константы CACHE_xxx в класс кэшера\n\n\n2017-06-15 06:32:06 43a0.9\n[@] Код\n    classPersistent: добавлен метод pass(), заставляющий читать/писать следующую переменную из/в БД\n\n\n2017-06-15 04:44:57 43a0.8\n[@] Код\n    Новый класс FleetDispatcher: инкапсулированы функции FFH\n    Bootstrap: назначения констант вынесены из класса в init.php - для поддержки phpStorm-ом\n    Новый класс Watchdog: набор методов для \"слежения\" за переменными конфигурации и проверка условий по дельте времени\n\n\n2017-06-13 12:37:53 43a0.3\n[-] Апдейтер\n    Удалены апдейты по релиз 39 включительно\n\n[@] Код\n    Класс ActiveRecordStatic: добавлено семейство методов find()\n    \\DBAL\\DbQuery::insert() теперь является protected. Тесты переписаны соответственно\n\n\n2017-06-12 14:59:17 43a0.2\n[!] PHP\n    Для работы СН требуется PHP не ниже 5.5\n\n[@] Код\n    Убрано использование sys_refresh_tablelist()\n\n\n2017-06-12 14:32:20 43a0.0\n[@] Код\n    Добавлен класс DBAL\\Schema()\n\n\n2017-06-12 09:24:47 42c0\n[!] Project \"SuperNova.WS\" Release 42 \"8th anniversary\" Release Candidate\n    Обновлён файл docs/changelog.txt\n\n\n2017-06-11 11:11:21 42a30.0\n[@] Код\n    Упорядочена процедура инициализации\n\n\n2017-06-09 21:51:47 42a29.15\n[~] Флоты/Менеджер флотов\n    Ещё расширено диагностическое сообщение в логах\n\n\n2017-06-09 21:30:37 42a29.14\n[~] Флоты/Менеджер флотов\n    Теперь время работы менеджера флотов вычисляется непосредственно с начала его работы, а маскимальное время работы уменьшено до 3 секунд (регулируется константой GAME_FLEET_HANDLER_MAX_TIME)\n    Так же расширено диагностическое сообщение в логах\n\n\n2017-06-08 17:03:55 42a29.6\n[@] Код\n    Поддержка core_festival 5a4.0+\n\n\n2017-06-04 19:55:31 42a29.4\n[+] Админка/Изменение ММ\n    Полностью переписано начисление ММ\n    Теперь можно изменять ММ так же по ID или имени игрока\n    Теперь перед изменением ММ можно посмотреть, что будет изменено - и лишь потом подтвердить изменения\n\n\n2017-06-04 17:08:48 42a29.3\n[+] Админка/ИзменениеТМ\n    Переписано изменение ТМ\n\n[@] Код\n    Класс ExceptionSnLocalized:\n        - Локализует сообщения об эксцепшнах при вызове getMessageLocalized() - использует $lang\n        - Может включать вызов sprintf() для внедрения данных по шаблону. Данные указываются четвертым параметром при вызове конструктора\n\n\n2017-06-04 14:12:30 42a29.1\n[~] Админка\n    Пункт меню \"Тёмная материя\" стал заголовком\n\n\n2017-06-04 14:07:27 42a29.0\n[~] Админка\n    Теперь при неудачной попытке начислить ТМ/ММ назад в форму возвращается причина начисления\n\n\n2017-06-04 02:54:37 42a28.42\n[@] Код\n    Добавлена функция sign()\n\n\n2017-06-02 22:51:45 42a28.29\n[@] Код\n    Поддержка Хайспота \"Чёрная Луна\"\n\n\n2017-05-30 14:18:22 42a28.24\n[~] Флоты/САБ\n    Переверстана страница подбора игроков в САБ\n[~] Вселенная/Переименование\n    Переверстана страница переименования Галактик/систем\n\n\n2017-04-15 00:47:15 42a28.17\n[%] UBE/Боевой отчёт\n    Исправлено отсутствие локализации для BBcode в боевом отчёте\n\n\n2017-04-14 21:53:31 42a28.16\n[~] UBE/Боевой отчёт\n    Добавлен BBCode для вставки в чат ссылки на бой\n\n\n2017-04-14 14:15:19 42a28.13\n[~] Меню\n    Добавлена опция настройки логотипа сервера в меню - опция 'menu_server_logo' в таблице `config`:\n        - Пустое значение - использовать логотип по умолчанию;\n        - В остальных случаях строка трактуется, как относительный путь к картинке (от корня игры), например - 'design/images/supernova.png'\n        - Опция 'menu_server_logo_disabled' позволяет отключить логотип в меню\n    Добавлена опция отключения названия сервера в меню - опция 'menu_server_name_disabled' в таблице `config`:\n    Добавлена опция отключения даты запуска в меню - опция 'menu_launch_date_disabled' в таблице `config`:\n\n\n\n2017-04-14 00:53:28 42a28.12\n[~] PTL\n    Доббавлены хинты к PHP-коду, выводящему значения в рендерённом темплейте\n    Переменные конфига теперь выводятся через объект свойство classSupernova::$config\n    Для вывода переменных конфига используется ArrayAccess для синтаксически-корректной записи\n\n[@] Код\n    classCache теперь реализует интерфейс ArrayAccess\n[@] Тесты\n    Добавлены PTL-тесты для префикса {C_xxx}\n\n\n2017-04-13 02:17:19 42a28.9\n[#] core_festival v4a4.0\n    Поддержка core_festival v4a4.0\n\n[@] Код\n    Добавлены методы в HelperArray\n\n\n2017-04-03 15:28:13 42a28.3\n[%] Сообщение\n    Исправлено задвоение информации об отправке сообщения\n\n\n2017-04-03 14:59:07 42a28.2\n[~] Альянсы\n    Добавлены более понятные сообщения об ошибках при попытках сменить имя или тэг Альянса на уже существующие в игре\n\n\n2017-04-03 14:39:57 42a28.1\n[@] Код\n    Теперь при загрузке стандартных CSS проводится проверка на физическое наличие файлов:\n        - Если есть минифицированный CSS - грузится он\n        - В противном случае проверяется наличие оригинального файла\n        - Если файлов вообще нет - соответствующая строка не добавляется в хидер страницы\n\n\n\n2017-03-28 14:18:41 42a28.0\n[!] Биржа ресурсов\n    Базовая вёрстка страницы + часть JS-функционала\n\n[@] Код\n    JS:\n        - Пространство имён \"sn\" для дальнейшего использования\n        - Базовый функионал объекта локализации sn.l() и sn.l.unitName()\n        - Рекурсивный подсчёт стоимости юнита/пака стэкируемых юнитов - sn.eco.getPriceIn() и sn.eco.getCostIn()\n            - Кэширование результатов для дальнейшего использования\n        - События/обработчики для ainput - ainputEnable и ainputDisable\n\n\n2017-03-22 16:04:24 42a27.15\n[@] Код\n    Рефакторинг messageBox-ов\n    Рефакторинг рендеринга хидера\n\n\n2017-03-22 10:58:31 42a27.14\n[-] Обзор Планеты\n    Отключена ранняя загрузка\n\n\n2017-03-21 21:26:06 42a27.13\n[-] Строительство\n    Отключена ранняя загрузка на странице строительства\n\n\n2017-03-21 19:55:11 42a27.11\n[%] Строительство\n    Исправлена ошибка задвоения юнитов\n\n\n2017-03-21 18:44:26 42a27.6\n[!] Ранняя загрузка\n    Включена на страницах overview.php и buildings.php\n\n[@] Код\n    Добавлена возможность выводить стандартные элементы страницы (меню и навбар) перед выполнением основного кода:\n        - Это даёт возможность браузеру начать загрузку остальных элементов страницы во время выполнения основного кода;\n        - Ранний вывод заголовка возможна не всегда и требует поддержки со стороны серверной части;\n        - Буфферизация вывода должна быть отключена как в PHP, так и в настройках веб-сервера;\n        - Должна быть отключена компрессия в PHP;\n        - Метод подключения PHP к веб-серверу должен поддерживать стриминг вывода;\n        - Код страницы не должен менять заголовки. В частности - не делать редиректы средствами PHP;\n        - Title страницы нужно задать заранее в описании через $sn_mvc['pages'][<имя страницы>][PAGE_OPTION_TITLE];\n        - Для включения - выставить $sn_mvc['pages'][<имя страницы>][PAGE_OPTION_EARLY_HEADER] в true;\n    Теперь _error-404.php возвращает всегда статус 200\n\n\n2017-03-21 13:08:57 42a27.4\n[@] Код\n    Обновлена версия ION Sound до 3.0.8\n    Добавлена полная версия библиотеки в docs/extra\n    Включено кэширование звуков в браузере\n\n\n2017-03-21 12:43:36 42a27.3\n[@] Код\n    Темплейт _page опять разбит на header и footer\n    Результаты операции в структуре result теперь выводятся стандартными механизмами, а не хаком\n    Вывод ADV_SEO_SCRIPT перенесен выше по коду, что бы он не тормозил дальнешую загрузку\n        - Яндекс и Гугль теперь используют https протокол - их скрипты должны ВСЕГДА грузится с префиксом https://\n    Звуки теперь загружаются\n    Добавлен служебный файл _error-404.php - на него можно сделать редирект для обработки ошибок 404. Его вывод подходит так же для CSS и JS файлов\n\n\n2017-03-19 14:11:31 42a27.2\n[#] player_award\n    Поддержка player_award v0с7\n\n\n2017-03-11 20:09:51 42a26.15\n[@] Код\n    Добавлено новое поле `skins` в таблицу `users` на замену убраному полю `dpath`\n\n\n2017-03-06 00:43:16 42a26.4\n[#] core_festival\n    Поддержка core_festival v4a0.0\n\n[@] Код\n    Теперь в навбаре можно программно добавлять кнопки в основной ряд кнопок\n\n\n2017-03-06 00:40:41 42a26.3\n[#] core_festival 4a0.0\n    (!) Highspot \"8 Марта\"\n        Добавлен Ивент \"8 Марта\":\n            - При включённом Ивенте в навбаре в конце основного блока кнопок появляется новая кнопка, ведущая на страницу Ивента\n            - На странице Ивента игрок с указанным в профиле мужским полом может подарить метаматерию игроку-женщине\n            - Подарок будет снят со счёта игрока-мужчины и зачислен на счёт игрока-женщине\n            - При зачислении игроку-женщине будет начислено на 25% больше ММ\n            - Минимальное количество снимаемой метаматерии - 20.000 ММ\n            - Игрок-мужчина может увидеть кому он делал подарки, сколько было списано со счёта и сколько было начислено\n\n    (~) Фестивали\n        Фестиваль считается активным только если имеет хоть один активный хайспот\n\n\n2017-03-05 02:37:20 42a26.2\n[~] Квесты\n    В списке квестов к фильтру добавлен вариант \"Все, кроме выполненных\"\n\n\n2017-03-04 17:09:11 42a26.1\n[+] Квесты\n    На страницу просмотра квестов добавлен фильтр квестов по статусу:\n        - Состояние фильтра запоминается между визитами на страницу квестов\n\n[@] Код\n    Квесты:\n        - CSS-код вынесен в _template.css\n    Добавлен интерфейс IPage\n    Добавлена проверка на разрешенные экшны на страницах\n\n\n2017-03-04 13:03:43 42a26.0\n[!] Квесты\n    Полностью переделан интерфейс квестов\n\n\n2017-03-04 11:49:35 42a25.26\n[+] Квесты\n    Новый статус квеста - \"Начат\":\n        - Квест отмечается как \"Начат\", если был построен хоть один юнит из условий квеста;\n        - Отметка выставляется в момент завершения постройки первого юнита\n        - Уже построенные юниты не учитываются - нужно построить хотя бы один юнит, что бы сменить статус квеста\n        - Количество начатых квестов отображаетя в навбаре на кнопке квестов желтым цветом\n\n\n2017-03-04 10:26:45 42a25.24\n[@] Рефакторинг\n    PHP Strict - устранены нотисы вызова AjaxController::view() в /includes/general.php\n    Расшит метод classSupernova::db_query на 4 разных\n    Некоторые устаревшие функции доступа к БД заменены операциями через \\DBAL\\DbQuery\n\n\n2017-03-03 21:40:35 42a25.20\n[@] Рефакторинг\n    PHP Strict - устранены нотисы в BBCodeParse.php\n\n\n2017-03-03 14:21:48 42a25.19\n[~] Навбар\n    Ресурсбар:\n        - Цветовое кодирование к количеству производимой энергии в ресурсбаре\n        - Цветовое кодирование к попапам в ресурсбаре\n        - Отдельный попап для энергии, выводящий потребление\n\n[@] Код\n    В JS добавлены аналоги Tools::fillPercentStyle():\n        - Number.prototype.spanByValue()\n        - Number.prototype.spanByMaximum()\n    В TPL добавлены аналоги Tools::fillPercentStyle():\n        - _number_percent_class.tpl.html\n        - _number_color_value.tpl.html\n        - _number_color_maximum.tpl.html\n\n\n2017-03-01 16:41:39 42a25.18\n[%] Навбар\n    Исправлена ошибка отображения производимой энергии\n\n\n2017-03-01 16:32:18 42a25.17\n[~] Навбар\n    В ресурсбаре улучшена индикация заполнения складов:\n        - Если количество ресурсов больше объема склада (> 100%) - размер склада будет выведен красным\n        - Если ресурсов <= 100%, но > 90% - оранжевым\n        - <= 90%, но > 75% - желтым\n        - <= 75%, но > 50% - синим\n        - <= 50% - зеленым\n        - И, наконец, если склада нет и количество ресурсов на планете равно нулю - размер склада будет выведен белым\n\n[@] Код\n    Добавлена универсальная функция цветового кодирования Tools::fillPercentStyle()\n\n\n2017-03-01 11:40:45 42a25.14\n[@] Рефакторинг\n    В админке все проверки доступа заменены вызовом AdminCheckLevel()\n    Убраны все упоминания глобальной переменной $parse\n\n\n2017-03-01 11:27:52 42a25.12\n[@] Пакет\n    Отдельный каталог extra в подкаталоге docs для разного\n\n\n2017-03-01 10:46:46 42a25.11\n[@] Рефакторинг\n    Убрана глобальная переменная $phpbb_root_path\n\n\n2017-02-28 13:09:26 42a25.8\n[@] Рефакторинг\n    Еще убраны лишние вызовы функции parsetemplate()\n    Убран неиспользуемый код и соответствующие темплейты\n        - admin/changelog.php - не работал\n        - admin/messall.php - не работал\n        - admin/paneladmina.php - не используется\n\n\n2017-02-28 12:51:45 42a25.7\n[+] Альянсы\n    Включена отключенная ранее опция рассылки сообщений членам Альянса - кнопка \"Послать сообщения всему Альянсу\" в блоке \"Управлеение Альянсом\"\n    Переверстаны некоторые страницы\n[+] Админка\n    Переверстаны страницы:\n        - Администрирование чата\n        - Утилиты\n\n[@] Рефакторинг\n    Убраны почти все лишние вызовы функции parsetemplate()\n    Убран неиспользуемый код и соответствующие темплейты\n        - changelog.php - не работал\n        - admin/add_research.php - не работал из-за смены формата хранения юнитов\n        - admin/del_research.php - не работал из-за смены формата хранения юнитов\n        - admin/deletuser.php - просто не работал\n\n\n2017-02-28 09:39:20 42a25.4\n[%] Симулятор\n    В верхней строке теперь не выводится надпись \"Код доступа\"\n\n[@] Рефакторинг\n    Упрощёна сигнатура функции display() до двух параметров. Соотвественно изменены вызовы\n    Объединены файлы темплейта _global_header и _global_footer в _page. Вывод данных производится в общий темплейт\n    Изменены некоторые SQL-запросы для совместимости с подсветкой синтаксиса phpStorm\n    Удалены куски неиспользуемого кода\n\n\n2017-02-27 21:52:44 42a25.2\n[@] Код\n    Добавлен класс-эмулятор текущего пользователя TheUser - для замены $dpath\n    Рендерер ников использует скины для получения иконок ДР, пола и отпуска\n    Из кода убрано использвание $dpath - остался только в базе\n\n\n2017-02-27 14:26:36 42a25.1\n[%] Навбар\n    Исправлена ошибка показа в попапе ресурсбара неправильного объёма склада при ОЧЕНЬ больших складах\n\n\n2017-02-27 14:15:14 42a25.0\n[~] Навбар\n    Теперь в ресурсбаре Альянсов не показывается ненужная строка ёмкости хранилищ\n\n\n2017-02-25 16:18:54 42a24.10\n[@] Код\n    Убран неиспользуемый класс skin\n\n\n2017-02-25 16:16:02 42a24.9\n[!] Админка\n    Рефакторинг страницы просмотра информации об игроке\n\n\n2017-02-25 14:09:30 42a24.7\n[!] Темплейты\n    Класс PTLTag:\n        - Замена элементов темплейта их значением\n        - Формат: {<prefix>{<text>|[elementID]|...}>\n            - <prefix> используется для идентификации. Должен быть отрезан перед передачей в PTLTag\n            - <text> - текст\n            - [elementID] - название элемента в квадратных скобках. Поддерживаются следующие элементы:\n                - Корневые значения {VAR} -> [VAR]\n                - Переменные темплейта из $DEFINE - {$VAR} -> [$VAR]\n                - Блоковые переменные на текущем уровне - {block.VAR] -> [block.VAR]\n            - Количество и комбинации текста и названий элементов могут быть любыми:\n                - На примере тэга {I_xxx}: {I_unit_[unit.ID]_red_[UNIT_SIZE]}\n            - Сейчас поддерживается в тэгах {R_xxx} и {I_xxx} - см. примеры ниже\n\n    Новый тег косвенной адресации {R_[XXX]}:\n        - Позволяет в рантайме выводить значение из элемента, чьё имя генерируется во время исполнения темплейта\n        - Пример:\n            - Пусть у нас в темплейте есть тэг {R_[RENDER]}\n            - Пусть во время исполнения корневой элемент 'VAR' равен 'VALUE';\n            - Пусть во время исполнения корневой элемент 'RENDER' равен '{VAR}'\n            - Тогда во время компиляции сгенерируется исполнимый код, который во время исполнения темплейта проделает следующее:\n                - Возьмет значение переменной 'RENDER', т.е. '{VAR}'\n                - Динамически скомпилирует код для вывода переменной '{VAR}'\n                - Исполнит его и выведет значение элемента 'VAR', т.е. в нашем конкретном случае - выведет 'VALUE'\n        - Тэг надо использовать с осторожностью, избегай вывода переменных, полученных напрямую от пользователя\n        - Содержимое элемента может быть любым тэгом из поддерживаемых темплейтом: {$VAR}, {block.VAR}, {D_xxx}, {I_xxx} итд\n\n    Тэг изображения {I_xxx} теперь работает через класс PTLTag\n        - Пример:\n            - Пусть в skin.ini есть записи\n                s_black_moon = \"planeten/small/s_black_moon.jpg\"\n                black_moon = \"planeten/black_moon.jpg\"\n            - Пусть в темплейте есть директива <!-- DEFINE $BLACK = 'black_moon' -->\n            - Тогда тэг {I_[$BLACK]} выведет '<HTTP путь к текущему скину>planeten/black_moon.jpg'\n            - А вот тэг {I_s_[$BLACK]} выведет '<HTTP путь к текущему скину>planeten/small/s_black_moon.jpg'\n        - В квадратных скобках можно использовать любые имена элментов, поддерживаемых PTLTag\n\n\n2017-02-23 13:56:39 42a24.4\n[!] Темплейты\n    Начата переделка темплейтов для поддержки мультитемплейтов\n\n\n2017-02-22 17:31:05 42a24.1\n[!] Вёрстка\n    Перевёрстан базовый темплейт:\n        - Переделан на DIV-ах\n        - Меню, навбар и дополнения (новости, заметки итд) подключаются теперь в _global_header\n\n\n2017-02-22 12:26:27 42a23.17\n[+] Навбар\n    В старом ресурсбаре теперь так же работают всплывающие окна с подсказками\n[+] Обзор планеты\n    В список ресурсов на планете добавились всплывающие окна с подсказками\n\n\n2017-02-22 10:53:11 42a23.13\n[+] Навбар\n    Полностью переверстан новый ресурсбар\n    Улучшена поддержка IE в старом/новом ресурсбарах\n\n\n2017-02-22 04:48:50 42a23.10\n[@] Код\n    Упрощены CSS-стили набара\n\n\n2017-02-22 04:02:48 42a23.8\n[@] Код\n    В темплейтах навбара настройки планетбара вынесены в темплейтные переменные\n\n\n2017-02-22 01:46:23 42a23.6\n[@] Код\n    Теперь при обмене ресурсов используется код RPG_MARKET_EXCHANGE = 35 вместо RPG_MARKET = 6\n\n\n2017-02-21 02:05:54 42a23.5\n[@] Код\n    Унифицирован код набаровских кнопок с очередями, а так же упрощена вёрстка и CSS\n\n\n2017-02-21 00:33:30 42a23.4\n[@] Код\n    Отформатирован код кнопок навбара\n\n\n2017-02-21 00:24:49 42a23.3\n[@] Код\n    Навбар\n        - Кнопки с очередями (Исследования, Постройки, Верфь) вынесены из темплейта навбара в отдельные файлы\n        - Отформатирован HTML-код навбара\n\n\n2017-02-20 22:12:58 42a23.1\n[%] Строительство\n    Исправлена ошибка на экране исследований при которой не включалась кнопка \"Исследовать\" на планетах у которых закончилось свободное место\n\n\n2017-02-20 21:54:36 42a23.0\n[~] Навбар\n    Новости навбара вынесены в отдельный темплейт\n    Заметки навбара вынесены в отдельный темплейт\n\n\n2017-02-19 12:14:23 42a22.4\n[~] Админка/Статистика регистраций\n    Добавлено количество аккаунтов - т.е. без учёта чисток\n    Для детализации по дням месяца теперь показывается день недели\n\n\n2017-02-19 11:44:28 42a22.3\n[~] Регистрация\n    Теперь новый игрок начинает игру со всеми прочитанными новостями\n\n\n2017-02-19 08:33:17 42a22.2\n[+] Вселенная\n    В режиме сканирования теперь работают попапы на лунах/планетах/итд\n\n[%] Админка\n    Исправлена ошибка при попытке имперсонации в несуществующего пользователя\n\n\n2017-02-18 21:38:25 42a22.1\n[!] Админка\n    Добавлена страница с балансом материи\n\n\n2017-02-18 17:06:17 42a22.0\n[!] Вселенная\n    Добавлен режим сканирования Вселенной:\n        - Вход в режим осуществляется нажатием кнопки \"Включить режима сканирования\"\n        - Выход - нажатием кнопки \"Выйти из режима сканирования\"\n        - Вид Вселенной переключается на минималистический\n        - Убираются лишние вертикальные отступы в ячейках\n        - В режиме сканирования отключаются: меню, навбар, подсказки\n        - Так же в режиме сканирования отключаются рамки вокруг таблиц, если они были включены в Настройках\n\n\n2017-02-18 15:14:37 42a21.13\n[~] Навбар\n    По умолчанию показ ёмкости в ресубаре теперь включен. Опция в настройках отключает показ ресурсов\n\n\n2017-02-18 15:05:57 42a21.12\n[+] Навбар\n    В настройках добавлена опция показа ёмкости складов в ресурсбаре. Опция действует как в новом, так и в старом ресурсбарах\n\n\n2017-02-18 13:33:58 42a21.11\n[@] Код\n    Убран неиспользуемый класс userOptionsOld\n\n\n2017-02-18 09:54:34 42a21.10\n[%] Флоты\n    В отправке флотов на странице выбора точки назначения переписан JS - изменения в ресурс-баре почему-то дали кидать тут ошибку\n\n[@] Код\n    Объявления функций classSupernova::db_changeset_xxx сделаны static - убраны лишние notice\n\n\n2017-02-18 01:46:46 42a21.9\n[+] Интерфейс\n    Теперь так же работает перетаскивание элементов тапом на тач-скринах\n    В частности - теперь можно поменять положение окна Советника на мобильных устройствах\n\n\n2017-02-18 01:36:03 42a21.8\n[%] Новости\n    Исправлена сломанная кнопка \"Показать текст новости\"\n\n\n2017-02-18 00:56:33 42a21.7\n[+] Навбар\n    Вместо букв-маркеров ресурсов (М, К, Д итд) используются иконки\n    Новый планетбар используется по умолчанию\n    В \"Настройки\" добавлена опция включения старого планетбара\n\n\n2017-02-17 12:47:37 42a21.6\n[+] Навбар\n    В новый планетбар добавлена поддержка вертикального расположения в навбаре\n    Добавлены разделители тысяч в попапе нового планетбара\n    Включен стандартный попап для энергии в новом планетбаре\n\n\n2017-02-17 11:11:55 42a21.5\n[+] Навбар\n    Верстка: металл+кристалл и дейтерий+энергия/тм объединены в блоки - так красивее работает адаптивный дизайн\n    CSS и JS из темплейта вынесены в соответствующие файлы\n\n\n2017-02-17 09:26:42 42a21.4\n[%] Флоты\n    На странице флотов в полёте исправлена ошибка, когда в таблице флотов вместо общего количества показывался список активных экспедиций\n\n\n2017-02-17 09:25:10 42a21.3\n[+] Навбар\n    Добавлена обратная совместимость со старыми ресурсбарами (вертикальным и горизонтальным)\n    Добавлена поддержка показа ресурсов Альянса\n\n\n2017-02-16 06:41:04 42a21.0\n[!] Навбар\n    Новый ресурсбар (количество ресурсов на планете/в Альянсе) - теперь встроенный в навбар\n    Выводится количество ресурсов на планете, а для электроэнергии - баланс (т.е. производство минус потребление)\n    При наведении курсора мыши на ячейку с ресурсом (металл, кристалл, дейтерий) появляется попап, в котором указывается:\n        - Полное название ресурса;\n        - Количество ресурсов на складе;\n        - Размер склада;\n        - Заполнение склада в процентах\n    При открытом попапе клик на ячейку закроет попап\n    Так же попап открывается при клике/тапе на ячейке - для мобильных устройств\n    Планетбар переверстан с использованием flex и поддерживает адаптивный дизайн\n\n[@] Код\n    Заменена библиотека jQuery UI - добавлены виджеты Menu и ToolTip\n\n\n2017-02-15 23:19:01 42a20.21\n[!] Админка\n    Небольшой тест функиональности phpBB Template Engine в \"Утилитах\"\n\n[~] PTL\n    Теперь PTL при отсутствии переменных LA_xxx выводит полное название переменной с префиксом, т.е. 'LA_xxx' вместо 'xxx'\n\n[%] Админка\n    Исправлена работа форсированного обновления с нуля\n\n[@] Код\n    Теперь заголовок окна может передаваться в переменной темплейта PAGE_TITLE\n\n\n2017-02-15 13:13:35 42a20.19\n[~] Обучение\n    Теперь окно обучения при перезагрузке страницы не \"прыгает\" из правого нижнего угла, а сразу открывается в нужном месте\n\n\n2017-02-15 12:33:27 42a20.17\n[!] bbCode\n    Переписана работа с bbCode\n    Класс BBCodeParser переделан в динамический\n    Работа со списком смайликов и bbCode вынесены в новый класс Design\n    Из класса classSupernova убран теперь неиспользуемый массив $design\n    Базовые смайлики и bbCode вынесены в vars.php\n\n\n2017-02-15 09:50:16 42a20.16\n[%] bbCode\n    Исправлена ошибка с нерабочими bbCode в чате у обычных игроков\n\n\n2017-02-14 19:37:02 42a20.13\n[%] bbCode\n    Исправлена ошибка с неинициализацией bbCodeParser\n\n\n2017-02-14 17:13:45 42a20.11\n[~] Обновление\n    Пересоздана таблица `text`\n\n\n2017-02-14 16:30:41 42a20.9\n[+] Обучение\n    Текст и заголовок Обучения теперь могут использовать bbCode\n\n\n2017-02-14 15:42:59 42a20.8\n[%] Документация\n    Добавлен утерянный readme.html\n\n\n2017-02-14 13:55:31 42a20.7\n[%] Обучение\n    Исправлена ошибка показа блока при полном отсутствии туториала\n\n\n2017-02-14 13:43:06 42a20.5\n[@] Код\n    Убрано использование функции cht_message_parse() в модулях\n    Убрано использование функции sys_bbcodeParse()\n\n\n2017-02-14 13:17:28 42a20.3\n[@] Код\n    Убрано использование функции cht_message_parse() в основном коде\n\n\n2017-02-14 12:49:40 42a20.2\n[+] bbCode\n    Новый bbCode - [urlw=URL]text[/urlw] - разворачивается в активную ссылку, которая переодит по URL в текущем окне\n    Новый префикс \"faq://link.html\" - разворачивается в активную ссылку на \"link.html\" в ЧаВо (FAQ) - если ЧаВо сконфигурировано в настройках сервера\n    Активная ссылка на боевой отчёт теперь открывается в текущем окне\n    Для bbCode [c] появилась полная версия [color]\n    Теперь URL боевого отчёта с текущего сервера автоматически преобразуется в активную ссылку на боевой отчёт\n    Теперь ссылки bbCode, открывающиеся в новом окне, подчёркиваются двойной линией\n\n[@] Код\n    Добавлена автоинициализация статической части класса через статический метод _constructorStatic() (с ОДНИМ подчёркиванием!)\n    Добавлен класс BBCodeParser\n\n\n2017-02-14 10:39:35 42a20.1\n[~] Новости\n    Переделан вывод результатов опроса для того, что бы не перекрывать всплывающие окна\n\n\n2017-02-14 10:21:32 42a18\n[~] Обучение\n    Локализованы строки в JS Обучения\n    Убран доступ к опции PLAYER_OPTION_TUTORIAL_WINDOWED - статус хранится на стороне игрока\n\n\n2017-02-13 13:44:18 42a17\n[+] Обучение\n    Теперь обучение можно открыть в окне\n        - По умолчанию окно открывается в правом нижнем углу страницы\n        - Окно прилеплено и не меняет местоположение при скролле\n        - Окно можно перемещать по странице, таская его за заголовок\n        - Статус обучения (на странице в навбаре или в окне), а так же положение окна на экране сохраняется в куках отдельно на каждом устройстве\n    Теперь можно задать ID первого текста из таблицы `text` в обучаловке - опция 'tutorial_first_item' в таблице `config`\n    Теперь при сбросе обучения так же сбрасывается статус \"обучение завершено\"\n[+] Лицензия\n    Добавлен файл docs/credits.txt для списка используемых материалов и соответствующих копирайтов\n\n[@] Код\n    Добавлена библиотека JS для поддержки кукесов\n\n\n2017-02-13 09:31:36 42a16\n[~] Обучение\n    Добавлены локализации кнопок - ru, en\n\n\n2017-02-12 19:11:37 42a15\n[+] Обучение\n    Первая рабочая версия Обучения\n    Работающие кнопки \"Вперед\", \"Назад\", \"Закончить\"\n    Подгрузка новых текстов через AJAX\n\n[@] Код\n    Базовый контроллер AjaxController\n    Скелетные классы Storage и Repository\n    Первая итерация класса PageTutorial\n    Первая итерация классов для энтити Text\n    Вынесена часть кода из рендера навбара в отдельные функции\n\n\n2017-02-10 04:46:42 42a12\n[@] Код\n    Добавлены классы AccessAccessors\n\n\n2017-02-10 01:27:02 42a11\n[@] Код\n    Добавлен интерфейс IContainer\n    Добавлены классы Invoker, AccessorMagic и AccessorsV2 с тестами\n\n\n2017-02-08 23:00:13 42a10\n[!] Обучение\n    Добавлен блок \"Обучение\" в навбар:\n        - Заголовок с кнопкой \"Закрыть\" (действует только на текущей странице)\n        - Основной текстовой блок с картинкой Советника\n        - Блок с кнопками\n        - Футер\n    В \"Настройки\" добавлены опции:\n        - Новые опции располагаются на вкладке \"Интерфейс\", подвкладка \"Обучение\"\n        - Опция отключения Обучения\n        - Опция показа Обучения во всплывающем окне (popup)\n\n[~] Новости\n    Переделана кнопка \"Закрыть\" в блоке новостей - вынесены стили в CSS, а обработчик нажатия - в JS\n\n\n2017-02-07 16:57:17 42a8\n[@] Код\n    Добавлены классы: DBAL\\DbQuery, HelperArray и Validators, а так же тесты для них и для Pimple\n\n\n\n2017-02-07 13:34:05 42a6\n[#] Модули\n    {~} Документация\n        Файлы с документацией переименованы в соответствии с нзваниями модулей\n    {%} misc_radio 2c4\n        Исправлена ошибка незагрузки минифицированного дизайна\n    {@} Код\n        Модули переписаны на использование $manifest['mvc']['pages']\n        Во все модули добавлено свойство $versionCommitted - автозаменяемое значение, указывающее на версии, в которой был сделан коммит модуля\n\n[@] Код\n    Добавлен код для отдельной регистрации страниц модулей\n\n\n2017-02-07 12:25:29 42a2\n[@] Код\n    Данные страниц перенесены из $sn_data в $sn_mvc\n    Из файлов страниц описания MVC занесены в vars.php\n\n\n2017-02-07 09:43:45 42a0\n[~] Новости\n    На обзоре страницы теперь можно ограничивать показываемые новости так же по времени публикации:\n        - Настройка game_news_overview_show в таблице config устанавливает давность новости;\n        - Задаётся в секундах. По умолчанию - новости давнее 2 недель (1209600 секунд) не показываются;\n        - При установке в 0 показывает все новости;\n\n\n2017-02-07 08:08:51 41d0 Project \"SuperNova.WS\" Release 41\n[!] Project \"SuperNova.WS\" Release 41 \"Festival batch fleet navbar\"\n\n\n2017-02-03 18:05:28 41b2\n[~] Инсталляция\n    Обновлены SQL-файлы до версии 41\n\n\n2017-02-03 16:10:49 41b1\n[-] Апгрейд\n    Апгрейд с очень старый версий движка (СН версии 36 и ниже) больше не поддерживается\n\n\n2017-02-03 15:52:44 41b0 Project \"SuperNova.WS\" Release 41 Beta\n[!] Project \"SuperNova.WS\" Release 41 \"Festival batch fleet navbar\" Beta\n    Обновлён файл docs/changelog.txt\n\n[~] Друзья\n    Теперь можно отправлять пустой запрос на дружбу (хотя кому и зачем это может понадобиться - непонятно)\n[~] МПР\n    Теперь можно атаковать ракетами свои же планеты. Таким образом можно избавится от излишних ракет или перехватчиков в шахтах, а так же уничтожать свои защитные сооружения\n[~] Флоты\n    Время возвращения флота из САБа теперь равно времени полёта на САБ, а не чистому времени полёта флота\n\n\n2016-10-29 10:43:27 41a60.19\n[@] Код\n    Папка с классами перемещена в корень движка - автолоадер изменен соответствующим образом\n    Переименованы файлы с классами для полной поддержки PSR-4 автолоадера\n\n\n2016-10-08 19:18:12 41a60.5\n[@] Код\n    Поддержка модуля interface_batch_operations\n\n\n2016-09-26 09:33:39 41a9.7\n[%] Строительство\n    Исправлены ошибка с иногда не срабатывающей кнопкой \"Уничтожить строение\"\n\n\n2016-09-25 19:26:24 41a9.5\n[%] Авторизация\n    Исправлена очепятка в запросе автогенерации имени\n\n[@] Код/JS\n    Обновлена библиотеека Ion Sound до версии 3.0.7\n\n\n2016-09-18 20:55:05 41a9.3\n[%] Строительство\n    Исправлена ошибка с неправильным отображением характеристики кораблей/обороны на страницах постройки\n\n\n2016-09-18 18:02:18 41a9.2\n[+] Дизайн\n    Снова оптимизированы картинки более совершенной версией оптимизатора\n\n\n2016-09-18 11:32:58 41a8.31\n[%] Строительство\n    Исправлена ошибка с неизменяемыми остатками в таблице ресурсов при постройке флота/обороны\n\n\n2016-09-18 10:22:47 41a8.30\n[%] Строительство\n    Исправлены нерабочие кнопки \"+\" и \"-\" в интерфейсе строительства\n\n\n2016-09-17 23:32:18 41a8.29\n[%] Заметки/Закладки\n    Исправлена невозможность редактирования текста Заметки в новых версиях Хрома\n\n\n2016-09-09 01:23:12 41a52.88\n[~] Постройка\n    Немного переработан темплейт построек для соответствия изменениям в JS (см. ниже)\n    Так же для уменьшения количества \"ошибок\" - формально некорректный синтаксис из-за использования PTL\n\n[@] Код/JavaScript\n    В основном коде заменены deprecated функции jQuery:\n        - bind(), live() и delegate() на on();\n        - unbind() - на off();\n        - В этот раз - честно-честно!\n    В класс Math добавлены функции-аналоги PHP-шных intval() и floatval() и функции округлений\n        - Эти функции всегда возвращают только числовые значения, а NaN преобразуют в 0\n        - В некоторых местах parseFloat() и parseInt() заменены на новые функции;\n    sn_format_number():\n        - Функция оптимизирована и разбита на две части\n        - Первая - считает вид числа;\n        - Вторая - выдаёт соответствующий cssClass для расцветки;\n        - При сохранении обратной совместимости (deprecated по факту) теперь можно менять сразу класс элемента, без вставки <span /> в DOMик;\n        - Добавлена соотвествующая функция-враппер elementPrettyNumber().\n    Постройка:\n        - Все значения от PHP теперь пропускаются через новые функции;\n        - Оптимизирована работа разных кусков JS и улучшен код;\n        - Убрано обращение к document;\n    ...а так же всякие мелкие оптимизации.\n\n\n\n2016-06-05 20:07:31 41a8.2\n[~] Модули\n    Поддержка Хайспота \"День Рождения СуперНовы\"\n\n\n2016-03-17 21:40:22 41a6.4\n[@] Константы\n    Поддержка медалей и мемориалий на ивент 8 марта 2016 года\n\n\n2016-01-26 21:56:35 41a3.4\n[%] Вселенная\n    Исправлено именование/переименовывание Галактики/Вселенной, когда они отличаются от текущих\n\n\n2016-01-25 22:41:42 41a3.3\n[+] Заметки/Закладки\n    Исправлена ошибка, делающая удаление Закладок невозможным при выборе некоторых диапазонов\n    Изменения на основной странице Закладок:\n        - Заголовок и текст закладки выделяется цветом важности. Отдельный словесный маркер важности убран за ненадобностью;\n        - Название объекта в космосе, на который указывает закладка, отображается сокращённо - (П) для Планеты, (Л) для Луны и (О) для Поля обломков\n        - Статус \"прилепленной\" закладки показывается иконкой, а не надписью \"Прилеплена\";\n        - Дата в списке закладок перенесена в заголовок;\n        - Увеличена максимальная ширина таблицы с закладками - для обладателей широкоформатных мониторов;\n        - На маленьких экранах если заголовок не влазит по ширине - он будет разнесен на нужное количество строк;\n        - Диапазоны выбора какие закладки удалять в верхней и нижней части синхронизированы. Т.е. выбор диапазона в верхнем элементе приводит к изменению диапазона в нижнем элементе - и наоборот\n    Редактирование/создание закладки:\n        - При выборе важности закладки меняется цвет заголовка, текста и самого выбранного элемента;\n        - Если в закладке пустой текст и заголовок, но указана хотя бы одна координата (галактика, система или планета) - закладка будет сохранена;\n\n\n2016-01-23 19:52:52 41a3.2\n[+] Флоты/Подбор флота\n    Названия характерстик на миниатюрах кораблей заменены иконками\n    Название корабля теперь выделяется голубым цветом\n    Добавлены настройки (пункт меню \"Настройки\", вкладка \"Интерфейс\", подвкладка \"Флоты\"), на которых можно:\n        - Включить \"старый режим\" - без картинок и с выводом количества кораблей в отдельном столбце;\n        - Отключить показ каждой характеристики корабля отдельно: т.е. скорости, ёмкости трюмов, потреблении;\n\n\n2016-01-22 23:38:06 41a3.1\n[~] Флоты/Подбор флота\n    Выделены цветом названия кораблей (желтый) и количество на орбите (зеленый\n\n\n2016-01-22 22:49:10 41a3.0 - Страница подборки флота\n[!] Флоты/Подбор флота\n    Переверстана страница подбора кораблей во флот:\n        - Уменьшена ширина списка кораблей для удобства мобильных пользователей;\n        - Миниатюра корабля:\n            - Вместо названия корабля и скорости полёта поставлена миниатюра корабля;\n            - Клик или тап на миниатюре переадресует на страницу о подробной информации корабля;\n            - Миниатюра корабля приведена к общему стандарту - название корабля в верхней строке и количество кораблей на орбите в нижней строке (вместо отдельной колонки);\n            - Так же на миниатюре корабля выводится: скорость полёта, расход топлива и ёмкость трюмов;\n            - Если корабль не может покинуть орбиту планеты/луны (Солнечный Спутнки, ТОП итд), то вместо характеристик выводится надпись \"Спутник\";\n        - В верхней части списка кораблей продублированы все кнопки. Теперь не обязательно листать до низа страницы, что бы быстро поднять все корабли или свезти ресурсы;\n        - Настройки сортировки перенесены в самое начало страницы - по аналогии с другими страницами;\n    Теперь при отсутствии свободных слотов для нового флота:\n        - Скрываются кнопки массового выбора кораблей, перехода на следующую страницу и своза ресурсов (поскольку слотов под своз всё равно нету);\n        - Скрывается мультиэлемент выбора кораблей;\n        - Миниатюры кораблей выводятся в большем размере - что бы легче читались характеристики.\n\n\n2016-01-17 15:43:49 41a2.0 - Новая страница настроек сервера\n[!] Админка/Настройки\n    Полностью переверстана таблица настроек сервера - со вкладками и на div-ах!\n\n\n2016-01-14 09:57:12 41a1.0 - Изоляция таблицы `fleets`\n[!] Код\n    Изолирован код, обращающийся к таблице `fleets`\n\n[~] Флоты/Минифлот\n    Доработан заголовок минифлота\n\n\n2015-12-26 00:54:08 41a0.45\n[%] Дизайн\n    Исправлены \"скачки\" вертикального скроллера при скролле до самого низа страницы\n    Исправлен черный фон на экране логина в некоторых браузерах\n\n\n2015-12-24 12:54:12 41a0.43\n[!] Дизайн\n    Общий responsive бэкграуд Блица для всех скинов\n    Responsive бэкграунд при входе в игру в обоих режимах (СН/Блиц)\n\n\n2015-12-24 09:36:20 41a0.42\n[~] Навбар\n    Твик, который должен решить проблемы в некоторых мобильных Хромах\n\n\n2015-12-23 21:11:30 41a0.37\n[+] Навбар\n    Поддержка добавочных кнопок вверху навбара\n\n\n2015-12-22 23:03:44 41a0.33\n[%] Навбар\n    Исправлена ошибка \"прыжков\" ресурс-навбара в вертикальной ориентации\n\n\n2015-12-22 15:30:58 41a0.31\n[+] Навбар\n    Ресурсный навбар:\n        - Теперь ресурсный навбар может быть сделан вертикальным\n        - \"Настройки\", раздел \"Интерфейс\", вкладка \"Панель навигации\", опция \"Вертикальная панель ресурсов\"\n        - При этом ресурсный навбар \"прижимается\" сбоку от основного навбара - полезно игрокам с широкими мониторами\n    Добавлены поясняющие надписи к кнопкам\n    Немного переделан навбар - больше флексбоксов богу флексбоксов!\n\n\n2015-12-22 12:25:50 41a0.26\n[~] Император\n    Улучшена читаемость страницы, немного уменьшена HTML-выдача\n\n[@] Код\n    Теперь модули могут добавлять свои CSS-файлы и конструкции в заголовок\n\n\n2015-12-22 01:22:18 41a0.20\n[~] Дизайн\n    Опять сделан прозрачным основной фон навбара\n    Скин supernova-ivash: добавлены стили для TD/TH\n    Реформат:\n        - Страница партнерской программы;\n        - Страница управления Альянсом;\n\n2015-12-22 00:00:32 41a0.17\n[+] core_festival\n    Еще поддержка хайспота Gather\n\n\n2015-12-21 06:06:09 41a0.12\n[+] core_festival\n    Добавлено больше поддержки хайспота Gather\n\n\n2015-12-21 03:41:22 41a0.10\n[+] core_festival\n    Добавлена поддержка хайспота Gather\n\n[@] Код\n    Исправлен двойной вызов MVC('model', '')\n\n\n2015-12-20 02:12:59 41a0.7\n[@] Код\n    Унифицирован вызов MVC-хуков. Теперь их потенциально можно роутить в базовом варианте\n    Вьюшки с IN_ADMIN в модулях теперь корректно выдают страницу с обрамлением админки\n\n\n2015-12-19 04:12:25 41a0.5\n[~] Навбар\n    Добавлена поддержка ивента ЁГ\n\n\n2015-12-19 04:03:10 41a0.4\n[@] БД\n    Добавлена поддержка ивентов типа Puzzle - сбор целого по кусочкам\n\n\n2015-12-18 06:43:00 41a0.3\n[%] Дизайн\n    Исправлена очепятка в EpicBlue/skin.ini\n\n\n2015-12-18 05:01:09 41a0.1\n[@] БД\n    Добавлена поддержка фестивалей\n\n\n2015-12-18 01:35:35 41a0.0\n[%] Настройки\n    Исправлена ошибка появления подвкладки с настройками интерфейса при первом открытии страницы\n\n\n2015-12-18 00:32:57 40d0 Project \"SuperNova.WS\" Release 40\n[!] Project \"SuperNova.WS\" Release 40 \"RD auth planet universe que sn_timer\"\n    Release\n\n\n2015-12-17 23:51:12 40c1.3\n[!] Планета\n    Прогресс застройки:\n        - Код рендеринга застройки планеты унифицирован с кодом страницы Планета/Управление\n        - Изменено цветовое кодирование прогресса застройки:\n            - Зеленый цвет - свободно не менее 50% секторов;\n            - Желтый цвет - свободно меньше 50%, но не меньше 25% секторов;\n            - Оранжевый цвет - свободно меньше 25%, но не меньше 10% секторов;\n            - Красный цвет - свобдно менее 10% секторов - стоит подумать о постройке Терраформера или расширении планеты\n    Теперь отображаются бонусные и максимальные уровни текущего Губернатора (если они есть)\n    Добавлена кнопка \"Улучшить или сменить Губернатора\" если Губернатор уже есть на планете\n    JS-код вынесен в отдельный файл\n    Вынесены CSS-стили\n\n[+] Планета/Управление\n    Теперь отображаются бонусные и максимальные уровни текущего и нанимаемых Губернаторов (если они есть)\n    Добавлена иконка помощи для каждого из нанимаемых Губернаторов\n    Вынесены CSS-стили\n[+] Дизайн/Картинки\n    Иконка информации (чёрная буква \"i\" в синем круге) заменена на иконку помощи (белый вопрос в синем круге с белой окантовкой)\n[+] Строительство\n    В подробном описании юнита добавлена ссылка на статью в Новапедии - иконка помощи на изображении юнита\n\n[@] Код\n    CSS:\n        - Добавлены классы абсолютного позиционирования\n        - Добавлены флекс-контейнеры\n\n\n2015-12-17 02:10:53 40c1.0\n[!] Планета/Управление\n    Страница переверстана\n    Разделены формы смены типа ядра, переноса Столицы, телепортации и покидания колонии что бы исключить любое возможное ложное срабатывание\n    Добавлены подтверждения на перенос Столицы, Телепортацию и покидание колонии\n\n\n2015-12-14 08:18:40 40c0.4\n[+] Наемники/Чертежи\n    Изображение юнита:\n        - Название юнита перенесено на саму картинку\n        - Добавлен текущий и бонусный уровни - теперь бонусы к Наемникам/Чертежам видны и без найма\n        - Для юнитов, покупаемых на время, под уровень подложен прогресс-бар, ширина которого указывает на количество оставшегося времени действия юнита\n        - Прогресс-бар имеет цветовое кодирование:\n            - Зеленый цвет - >= 50% общего времени найма;\n            - Желтый цвет - < 50%, но >= 25% общего времени найма;\n            - Оранжевый цвет - < 25%, но >= 10% общего времени найма;\n            - Красный цвет - < 10% общего времени найма\n\n\n2015-12-14 05:20:24 40c0.3\n[@] Платежи\n    Получение курса теперь производится через функцию get_exchange_rate() в которой предусмотрено получение стоимости ММ через перекрываемую функцию get_mm_cost()\n    Класс платежей для получения курса использует get_exchange_rate()\n\n\n2015-12-14 04:20:27 40c0.2\n[@] Отладка\n    Добавлен подсчёт времени, затраченный движком на query и fetch_assoc\n    Добавлен вывод точки вызова в pdump - для лучшей отладки и поиска, кто же это срёт в вывод\n\n\n2015-12-06 16:05:00 40c0.0 Project \"SuperNova.WS\" Release Candidate 40RC0\n[!] Project \"SuperNova.WS\" Release Candidate 40RC0 \"RD auth planet universe que sn_timer\"\n    Обновлён файл docs/changelog.txt\n\n\n2015-12-06 15:26:44 40b1.2\n[%] Апдейтер\n    Исправлены ошибки апдейта\n\n\n2015-12-06 15:10:58 40b1.0\n[!] Апдейтер\n    Обновлен файл docs/supernova.sql\n    Обновлен файл docs/supernova-data.sql\n    Добавлено удаление лишних индексов из таблицы `planets`\n    Версия БД увеличена до 40-й\n    Версия релиза увеличена до 40-й\n\n\n2015-12-04 23:07:47 40b0.36\n[#] skins/immi\n    (%) Исправлены ошибки в скине\n\n\n2015-12-04 23:04:18 40b0.35\n[%] Скины\n    Исправлена ссылка supernova-ivash на самого себя\n    Исправлены неправильные пути к иконкам навбара\n\n\n2015-12-04 22:45:41 40b0.34\n[!] Скины\n    supernova-ivash: удалены дубликаты картинок, которые есть в родительском скине\n    EpicBlue: удалены дубликаты картинок\n\n\n2015-12-04 22:42:58 40b0.33\n[#] skins/immi\n    (!) Скин-предок для immi - теперь supernova-ivash\n        Удалены картинки-дубликаты, имеющиеся в родителе\n\n\n2015-12-04 20:56:20 40b0.32\n[+] Скин/supernova-ivash\n    Пережаты изображения планет малого размера в стандартное для них разрешение 88x88\n\n\n2015-12-04 17:04:32 40b0.30\n[~] Очередь построек\n    На общей панели постройки количество юнитов/уровень в текущем слоте отображается на новой строке\n\n\n2015-12-04 16:28:03 40b0.29\n[~] Дизайн/Изображения\n    Добавлены новогодние смайлики\n\n\n2015-12-04 16:25:43 40b0.28\n[~] Дизайн/Изображения\n    Еще немного пожаты некоторые глобальные изображения\n\n\n2015-12-04 15:46:39 40b0.27\n[@] Класс skin\n    Оптимизация-2\n\n\n2015-12-04 14:46:52 40b0.26\n[@] Класс skin\n    Оптимизация-2\n    Заглушка _NO_IMAGE компилируется однократно при инициализации скина\n    Теперь каждый скин может иметь собственную заглушку _NO_IMAGE\n\n2015-12-04 12:21:12 40b0.24\n[+] BBCode\n    Добавлен новый макрос 'faq://' - разворачивается в УРЛ ЧаВо, если он прописан в 'url_faq' конфигурации\n\n[@] Класс skin\n    Оптимизация-1\n\n\n2015-12-04 10:49:47 40b0.23\n[@] Класс skin\n    Теперь в контейнере вдобавок к HTTP-пути к картинке хранится и скомпилированная строка при указании параметров;\n    При запросе параметризированного тэга у родителя запрашивается только HTTP-путь к файлу, а параметры применяются по правилам скина;\n    Теперь всегда корректно применяются параметры тэга {I_xxx|yyy}\n    Проверки file_exists() заменены на is_file()\n    Добавлено изображение-заглушка _NO_IMAGE в случае отсутствия изображений\n    Переписана логика компиляции изображения\n\n\n2015-12-04 08:41:53 40b0.22\n[@] Класс skin\n    Добавлена проверка на физическое существование картинки. Если её не существует - идёт откат на родительские данные\n\n\n2015-12-04 07:36:04 40b0.21\n[!] Скин/supernova-ivash\n    Добавлены цветные кнопки в навбар. Большое спасибо Ivash@Alpha\n\n\n2015-12-04 07:00:25 40b0.20\n[@] Код\n    На странице Построек и в Очереди используются изображения юнитов, назначенные в skin.ini\n    Минифицирован build_unit.js - размер уменьшен на 30%\n\n\n2015-12-03 20:32:42 40b0.19\n[!] Дизайн\n    Минифицированы CSSы: глобальные и в скинах EB и SN-I\n[@] JS\n    Минифицированы глобальные JS-файлы. Оригинальные версии лежат рядом\n\n\n2015-12-03 20:22:13 40b0.18\n[#] skins/immi\n    (!) Оптимизированы изображения\n        Минифицирован CSS\n\n\n2015-12-03 18:35:26 40b0.16\n[!] Дизайн/Изображения\n    Оптимизированы изображения в скинах EP и SN-I. Как и раньше - средний выигрыш более 20%\n\n\n2015-12-03 12:22:00 40b0.15\n[~] Дизайн/Скины\n    В скинах 'EpicBlue' и 'supernova-ivash' расширения файлов-картинок приведены в соответствие с их реальным типом\n\n\n2015-12-03 10:52:55 40b0.14\n[!] Дизайн/Изображения\n    Оптимизированы изображения в каталоге design/ - в среднем размеры изображений уменьшены на 20%\n\n\n2015-12-02 17:04:58 40b0.11\n[+] Настройки\n    Вкладка \"Интерфейс\" теперь содержит подвкладки \"Общие\", \"Настройки меню\" (бывашя вкладка), \"Панель навигации\" и \"Вселенная\"\n\n\n2015-12-02 14:36:17 40b0.10\n[+] Навбар\n    Теперь в настройках навбара (пункт меню \"Настройки\", вкладка \"Интерфейс\", раздел \"Панель навигации (вверху экрана)\") можно так же отключить кнопку метаматерии\n\n\n2015-12-02 13:32:16 40b0.9\n[%] Метаматерия\n    Исправлена ошибка, когда при покупке Альянсовских плюшек (Чертежи, Наёмники итд) списывалась ММ с аккаунта покупающего\n\n\n2015-12-01 10:20:15 40b0.7\n[+] Дизайн/Скины\n    supernova-ivash: Добавлена плашка навбара ММ\n\n\n2015-12-01 09:25:55 40b0.6\n[+] Дизайн/Скины\n    EpicBlue: Прописаны пути для плашек в навбаре\n    supernova-ivash: Плашки в навбаре заменены на подходящие по тону (кроме плашки ММ)\n[+] skin.ini\n    Добавлена поддержка HTTP-пути от корня СН, например - '/design/images/navbar_hangar.png' станет 'http://localhost/supernova/design/images/navbar_hangar.png' если HTTP-корень СН в 'http://localhost/supernova/'\n\n[~] Дизайн/Вёрстка\n    Навбар: Адреса картинок (src) заменены с путей на имена {I_xxx}\n\n\n2015-11-30 21:16:51 40b0.4\n[~] Страница постройки\n    Добавлено разрежение ячеек в подтаблицы цены и допинформации о юните\n    Добавлено выделение четных рядов другим фоном в подтаблице допинформации\n[~] Навбар\n    Увеличена прозрачность фонов под цифрами-индикаторами\n\n\n2015-11-30 13:41:19 40b0.3\n[~] Новости\n    Добавлен отступ от предыдущего элемента при выводе новостей на страницах игры\n    При выводе на страницах игры используется тонкая рамка\n\n\n2015-11-30 13:32:07 40b0.2\n[~] Новости\n    Кнопки действия над новостью (для Администрации) перенесены из отдельных колонок под саму новость\n    Толстая рамка вокруг опроса заменена тонкой\n\n\n2015-11-30 13:03:10 40b0.1\n[!] Навбар\n    Настройки навбара (пункт меню \"Настройки\", вкладка \"Интерфейс\", раздел \"Панель навигации (вверху экрана)\"):\n        - Теперь можно так же отключить:\n            - Кнопку исследований\n            - Кнопку флотов в полёте\n            - Кнопку экспедиций\n        - По умолчанию кнопка исследований теперь имеет стандартную ширину. Вернуть старый вид можно включив опцию \"Широкая кнопка исследований (старый вид)\"\n    Если на планете есть очередь зданий она выводится на кнопке планеты аналогично Исследованиям и Ангару. При этом количество занятых и свободных слотов не выводится\n    Имена планет в дропдауне переключения приведены к стандартному виду [координаты] (тип) Имя\n    Навбар переверстан под flex-box - теперь корректно переносятся кнопки на маленьких экранах\n    Все инлайн-стили вынесены в _template.css\n\n\n2015-11-30 10:01:14 40b0.0 Project \"SuperNova.WS\" Release 40 Beta\n[!] Project \"SuperNova.WS\" Release 40 Beta \"RD auth planet universe que sn_timer\"\n    Бета-версия 40-го релиза доступна для тестирования\n    В changelog.txt внесены все изменения по коммит \"2015-11-29 05:07:31 40a19.24\", включительно\n    Не забываем после обновления чистить папку 'cache'\n    Взять бету можно по ссылке https://github.com/supernova-ws/SuperNova/archive/master.zip\n    За новыми версиями модулей обращаться по личным каналам. Лучше всего - в скайп supernova.ws. Изменений в модулях до релиза не будут и текущие модули так же пойдут на релизе\n\n\n2015-11-29 05:07:31 40a19.24\n[~] Админка/Обслуживание\n    Добавлена чистка очередей на покинутых и удалённых планетах\n[%] Админка/Обслуживание\n    Исправлено сохранение боевых отчётов из списка \"Лучшие бои\" при чистке таблицы `ube_report`\n    Исправлено отсутствие отчёта при выполнении обслуживания\n\n\n2015-11-29 01:09:13 40a19.22\n[~] Меню\n    Добавлена иконка для пункта меню \"Лучшие бои\"\n\n\n2015-11-28 06:30:27 40a19.21\n[#] info_best_battles 1b0\n    (!) Модуль \"Лучшие бои\"\n        Добавляет в меню новый пункт \"Лучшие бои\", который открывает соответствующую страницу\n        Для каждого боя выводится:\n            - Порядковый номер в таблице;\n            - Дата и время боя;\n            - Общее количество обломков в пересчете на металл;\n            - Ссылка на просмотр соответствующего боевого отчёта;\n        На странице выводится 50 лучших боёв\n        Бои сравниваются по общему количеству обломков в пересчете на металл\n        В таблицу попадают только бои, которые произошли не ранее 2-х суток от текущей даты, т.е.:\n            - 2015-11-28 в 00:11:30 будут доступны бои, произошедшие до 2015-11-26 00:00:00 (не включая полуночь);\n            - Бои, происшедшие 2015-11-26 появятся в списке лучших боёв 2015-11-29 ровно в 00:00:00 (если, конечно, образовалось больше обломков, чем у худшего из лучших боёв);\n            - Это сделано специально, что бы дать всем заинтересованным сторонам собрать лом с орбиты;\n        Лучшие бои не удаляются из базы данных во время процедуры технического обслуживания\n\n[~] Боевой отчёт\n    Добавлен заголовок на страницу\n    Ссылки на планеты приведены к стандартному виду - [координаты] (тип) Имя\n    Улучшена вёрстка\n\n\n2015-11-26 13:31:33 40a19.20\n[!] Акции\n    Теперь на странице информации \"О сервере\" выводится стандартное значение рейтов и значение рейтов в рамках акции\n    Теперь во время акций с увеличением скорости добычи ресурсов:\n        - Размеры хранилищ не увеличиваются - это вызывало много вопросов у новичков\n        - Добыча И потребление энергии НЕ ИЗМЕНЯЮТСЯ - т.е. не надо строить дополнительные энергомощности или хранить избыток\n        - Добыча в экспедиции не изменяется, как и было задумано\n\n[@] Код\n    Немного форматирования\n\n\n2015-11-26 12:16:05 40a19.19\n[~] Платежи\n    Немного доработана страница платежей\n\n[~] Вселенная\n    В обеих вариантах вселенной относительные ссылки на иконки действий заменены абсолютными - может это поможет решить проблему на некоторых мобильных телефонах\n\n\n2015-11-24 20:15:52 40a19.18\n[~] Реклама\n    Изменены условия показа рекламного блока:\n        - Реклама не показывается игрокам, которые играют меньше недели\n        - Реклама не показывается игрокам, которые взяли Премиум-аккаунт\n\n[@] Код\n    Переформатирован файл vars_menu.php\n    Добавлен фейковый файл констант для поддержки в phpStorm констант из модулей - их теперь можно вынести из основного кода\n\n\n2015-11-24 00:12:16 40a19.17\n[~] Дизайн/Темплейты/OpenGame\n    Максимальная ширина таблицы юнитов установлена в 100em\n\n\n2015-11-23 04:24:40 40a19.13\n[@] Код\n    Добавлены phpDoc в includes/template.php\n\n\n2015-11-19 22:02:27 40a19.12\n[+] Очередь постройки\n    Добавлено диалоговое окно с подтверждением при полной очистке очереди\n    Добавлено диалоговое окно с подтверждением при использовании Артефакта\n\n\n2015-11-19 16:31:53 40a19.11\n[+] Очередь постройки\n    Прогресс-бары теперь можно отключить - пункт меню \"Настройки\", вкладка \"Интерфейс\", опция \"Отключить прогресс-бары\"\n    Теперь не рендерится очередь постройки, если контейнер для неё невидим - например, в навбаре\n[+] Планета\n    Добавлены прогресс-бары к общей информации об очередях\n[+] Навбар\n    Добавлены прогресс-бары к плашке исследований\n\n\n2015-11-19 14:09:41 40a19.10\n[!] Очередь постройки\n    Список юнитов в очереди переверстан на дивах с использованием flex-box\n    В очередь постройки добавились прогресс-бары:\n        - Прогресс-бар постройки текущего юнита в текущем слоте очереди:\n            - Цвет - зеленый\n            - Выводится под таймером обратного отсчёта постройки текущего юнита\n            - Учитывается ПОЛНОЕ время постройки юнита - т.е. при перезагрузке страницы время продолжит идти\n            - Показывает процент завершения постройки текущего юнита - т.е. увеличивается со временем\n            - Виден во всех очередях постройки на первом слоте в очереди и в навбаре в плашке верфи\n        - Прогресс-бар постройки всех юнитов в текущем слоте:\n            - Цвет - синий\n            - Выводится под количеством юнитов в текущем слоте (для зданий, очевидно, выводится всегда - здания строятся поштучно)\n            - Показывает оставшееся количество юнитов к постройке в текущем слоте - т.е. уменьшается со временем\n            - Виден во всех очередях постройки на всех слотах\n        - Общий прогресс-бар очереди:\n            - Цвет - болотный;\n            - Выводится под общим таймером обратного отсчёта всей очереди\n            - Учитывается ОСТАВШЕЕСЯ время всех слотов в очереди - т.е. при перезагрузке страницы отсчёт стартует с начала\n            - Показывает поставшееся время до завершения всей очереди - т.е. уменьшается со временем\n            - Виден на странице постройки и в навбаре в плашке верфи\n    Оптимизирована работа таймера очередей, так что новые прогресс-бары не должны сказаться на быстродействии\n\n[+] Страница постройки\n    Превьюшки юнитов теперь занимают максимум ширины - на широкоформатных мониторах станет меньше вертикального скроллинга\n    Улучшилось отображение вертикальной очереди при включенных рамках\n    Крайне рекомендуется использовать вертикальную очередь на широкоформатных мониторах - пункт меню \"Настройки\", вкладка \"Интерфейс\", опция \"Вертикальные очереди\"\n[+] Дизайн/Скины/EpicBlue\n    Заменены изображения Терраформера и Большого Планетарного Шита\n    Изображения для Нанолаборатории, МИС, Астрокартографии, Гордыни, ТОПа, ПЗ, Малого Планетарного Щита, МПР и Перехватчика приведены к стилю остальных картинок\n    За проделанную работу - особая благодарность игроку 4apaeff@Alpha\n\n\n2015-11-16 10:17:40 40a19.9\n[+] Страница постройки\n    Настройки сортировки внесены в основную таблицу\n    Таблица стоимости постройки всегда выровнена по левому краю - так уменьшается количество \"скачков\" при переходе между юнитами\n    В Верфи/Обороне галочка \"Автоконвертация\" перенесена над количеством юнитов - так исключена вероятность нежелательного нажатия при игре на устройствах с тач-интерфейсом\n    В очереди построек ссылки \"Очистить очередь\", ссылка Артефакта (\"Наностроитель\"/\"Эвристический чип\") и \"Отменить последнее\" сделаны кнопками. Это выделит данные элементы, а так же уменьшит количество ложных нажатий\n[~] Юниты\n    \"Хранилища\" переименованы в \"склады\" - так короче. Уточнено и расширено описание хранилищ\n\n\n2015-11-16 08:46:37 40a19.8\n[~] Обзор Планеты\n    Добавлены координаты и название планеты в виде заголовка страницы\n    Убрано уведомление \"У вас Х новых сообщений\"\n[~] Заметки\n    Продублирована группа элементов для удаления заметок в самом верху страницы\n\n[@] Код\n    Убрано предупреждение при пустом списке JS-файлов на странице\n    Немного подчищен код\n\n\n2015-10-30 23:11:23 40a19.7\n[!] Хэлоуин\n    Поддержка ивента \"Хэллоуин-2015\"\n\n\n2015-10-30 21:43:22 40a19.6\n[!] Хэлоуин\n    Поддержка ивента \"Хэллоуин-2015\"\n\n\n2015-10-30 19:09:01 40a19.5\n[!] Хэлоуин\n    Поддержка ивента \"Хэллоуин-2015\"\n\n[+] Темплейты\n    Поддержка централизованного добавления JS-файлов и кода в заголовок страницы\n    Поддержка global_override.css для всего сервера\n\n\n2015-10-29 00:16:48 40a19.4 - Дизайн v2 - Phase 20\n[!] Дизайн\n    Переделаны картинки ВебМани на странице платежей при выборе метода платежа\n    Скин supernova-ivash: доработан CSS для перекрытия наследованных цветов\n\n\n2015-10-28 21:58:26 40a19.1 - Дизайн v2 - Phase 19\n[!] Дизайн\n    Выделены скинозависимые цвета в _template.css\n\n\n2015-10-28 21:01:55 40a19.0 - Дизайн v2 - Phase 18\n[!] Дизайн\n    body font-size установлен в 1em вместо 1rem - это должно устранить проблему с превым открытием странице в Хроме с его долбанутой системой кэширования CSS\n    OpenGame/_template.css:\n        - Из скинов вынесены стили для стандартной рамки;\n        - Добавлены правила для обработки заголовков основной части страницы через [table_title]::before (то, что сейчас является PAGE_TITLE);\n        - Часть skin-related цветов вынесена в \"подвал\";\n\n\n2015-10-27 20:41:51 40a18.15\n[~] Отправка флотов\n    Выбор точки назначения: рамки вокруг точек назначения заменены на тонкие\n\n\n2015-10-27 19:04:14 40a18.11 - Редизайн страницы \"Технологии\"\n[!] Технологии\n    Редизайн страницы:\n        - Вкладки на каждый вид технологий;\n        - Блочная, а не табличная вёрстка;\n        - Гибкая вёрстка в зависимости от размера экрана\n        - Добавлены картинки юнитов;\n        - Списки \"Требуется\" и \"Предоставляет\" теперь являются ссылками - можно сразу перейти на просмотр описания требуемых/предоставляемых технологий;\n    Новый дизайн страницы должен подойти любому игроку - начиная от мобильных пользователей и заканчивая владельцами широкоформатных экранов: он динамичен, масштабируем, информация организована удобнее и сама страница занимает меньше экранного пространства\n    Табличный дизайн можно включить в \"Настройках\" вкладка \"Интерфейс\" опция \"Страница Технологий в виде таблицы (старый вид)\"\n\n[~] Настройки\n    Настройки, которые имеют только один чекбокс приведены к общему виду: сначала идёт чекбокс, затем - описание. Это улучшает читаемость и удобство таблицы, а в некоторых случаях - уменьшает вертикальный размер страницы \"Настроек\"\n[~] Дизайн/Скины\n    EpicBlue: Добавлено изображение для ресурса \"Метаматерия\". Соответственно - оно появилось и во всех остальных скинах;\n    supernova-ivash:\n        - Выставлен цвет заполненной части у слайдера;\n        - Добавлены рамки-изображения к попапам;\n        - Исправлена ошибка непоказа в некоторых случаях изображения Эсминца;\n\n\n2015-10-25 23:06:39 40a18.10\n[%] Скины/supernova-ivash\n    Исправлен индекс картинки Ракетной шахты в Постройках\n\n\n2015-10-25 22:40:58 40a18.9\n[%] Планета/Управление\n    Исправлена ошибка покидания колонии\n\n\n2015-10-25 21:23:43 40a18.8\n[+] Планета/Управление\n    Добавлено подтверждение на покупку сектора\n\n\n2015-10-25 20:00:49 40a18.7\n[+] Вселенная\n    В новой Вселенной добавилось выделение поля обломков цветной рамкой в зависимости от количества ресурсов\n    В обоих видах Вселенной (старый и новый) изменилась цветовая кодировка обломков: добавились новые градации и изменились границы переключения цветов на более интуитивные:\n        - Без фона/рамочки - менее 1.000 единиц обломков;\n        - Зеленый фон/рамочка - не менее 1.000 и не более 1.000.000 единиц;\n        - Желтый - не менее 1.000.000 и не более 1.000.000.000 единиц;\n        - Оранжевый - не менее 1.000.000.000 и не более 1.000.000.000.000 единиц;\n        - Красный - не менее 1.000.000.000.000 единиц;\n\n[~] Постройки/Исследования\n    Немного увеличены размеры иконок на превьюшках юнитов. Так же сами иконки масштабируются при изменении размеров шрифта как внутренними методами СН, так и средствами браузера\n\n\n2015-10-25 17:00:13 40a18.6 - Дизайн v2 - Phase 17\n[!] Дизайн\n    Добавлено подтверждение на покупку сектора\n    Оптимизированы CSS-файлы\n    Попапы обзавелись тонкими рамочками (там где их не было и при включенных рамках)\n    Отмеченные чекбоксы теперь подсвечиваются зеленым - в старой Опере и браузерах на основе WebKit\n    Перекрашены псевдокнопки - теперь они не отличаются по стилю от конопк jQuery-UI\n    В новом виде Вселенной убраны подчеркивания на кнопках Игрока и Альянса\n    Исправлена ошибка переименования планеты, когда к спецсимовлам добавлялся слэш\n\n\n2015-10-24 19:34:00 40a18.2 - Дизайн v2 - Phase 16\n[!] Дизайн/Скины/skin.ini\n    Поддержка позднего связывания в темплейтах для PTL-структуры DEFINE, например {I_[$PLANET_GOVERNOR_ID]}\n    Поддержка skin.ini:\n        - gebaeude\n            - DEFINE в Обзоре Планеты и Управлении Планетой\n        - planeten\n            - Список планет (Империя и Планета);\n            - Вселенная (старая, новая и общий блок, включая попапы)\n            - Навбар - кнопка текущей планеты\n        - menu_empire_emperor.png - {dpath}icons/menu_empire_emperor.png\n            - Настройки\n            - Поиск\n            - Статистика\n            - Вселенная (везде)\n        - Меню\n            - Все иконки, включая иконки с путями\n\n    Замена большой рамки маленькой:\n        - Управление планетой (вокруг Губернаторов);\n        - Вселенная - все попапы. Рамка сделана полупрозрачной;\n\n\n2015-10-24 17:22:34 40a18.1 - Дизайн v2 - Phase 15\n[!] Дизайн/Скины/skin.ini\n    Поддержка skin.ini:\n        - gebaeude\n            - Артефакты;\n            - Наёмники;\n            - Отправка флотов - миниатюры кораблей во флоте;\n            - Список планет (Империя и Планета);\n            - Управление планетой (Губернаторы);\n    Замена большой рамки маленькой:\n        - Управление планетой (вокруг Губернаторов);\n\n[@] Код\n    Удалён каталог \".unused\" вместе с содержимым\n\n\n2015-10-24 17:08:58 40a18.0 - Дизайн v2 - Phase 14\n[!] Дизайн/Скины/skin.ini\n    В подсистему скинов добавлена поддержка файла конфигурации \"skin.ini\"\n    Постройки/исследования (buildings_build) поддерживает \"skin.ini\":\n        - В превьюшках юнитов;\n        - В большом изображении юнита;\n    Очередь юнитов (eco_queue) поддерживает \"skin.ini\" в иконках очереди\n    Новапедия (novapedia) поддерживает \"skin.ini\" в изображениях юнита\n\n\n2015-10-23 18:04:40 40a17.16\n[%] Дизайн\n    Исправлена ошибка из-за которой в некоторых конфигурациях сбрасывается внутренний размер шрифта\n\n\n2015-10-23 00:44:20 40a17.14 - Дизайн v2 - Phase 13\n[!] Дизайн\n    Обновлены рамки в скинах \"EpicBlue\" и \"immi\"\n    Добавлена поддержка тонких рамок в скин \"supernova-ivash\"\n\n\n2015-10-22 22:27:24 40a17.10 - Дизайн v2 - Phase 12\n[!] Дизайн\n    Добавлена возможность отключения рамки в скин \"immi\"\n\n\n2015-10-22 22:21:49 40a17.9 - Дизайн v2 - Phase 11\n[!] Дизайн\n    Исправлена ошибка с размером лун в списке планет на странице \"Обзор Планеты\"\n    Добавлена возможность отключения рамок в скин \"supernova-ivash\"\n\n\n2015-10-22 21:43:30 40a17.8 - Дизайн v2 - Phase 10\n[!] Дизайн\n    Страница строительства корректно масштабируется с рамками и без\n    Отправка флотов:\n        - Страница выбора миссии при отправке флотов корректно масштабируется с рамками и без\n        - Миниатюры кораблей теперь масштабируются вплоть до 1700 пикселов по ширине - на больших экранах в одну строку влазит больше кораблей\n        - Корявый указатель направления миссии \"=>\" заменен на красивую стрелочку с подписью типа миссии\n    В скине EpicBlue добавлены тонкие рамки к планет-бару (количество ресурсов на планете сразу под навбаром) и на страницу строительства - вокруг сортировки, блока дополнительной информации и блока стоимости строительства\n\n\n2015-10-22 18:55:04 40a17.7 - Дизайн v2 - Phase 9\n[!] Дизайн\n    Переработано встроенное масштабирование страницы\n\n\n2015-10-22 17:47:44 40a17.6 - Дизайн v2 - Phase 8\n[!] Дизайн\n    Элементы ввода (строки и текстовые области) теперь не \"прыгают\" при нажатии клавиши \"пробел\" во время набора\n    В \"Настройки\" на вкладке \"Интерфейс\" добавлена опция \"Отключить рамки у таблиц\", которая убирает рамки-изображения у элементов дизайна\n    Слайдер:\n        - Увеличен размер слайдера, ручки слайдера и отступ - для облегчения использования мобильными пользователями\n        - Ручка слайдера теперь не \"прыгает\" при нажатии на неё курсором мыши\n\n\n2015-10-22 14:37:58 40a17.5 - Дизайн/Скины v2 - Phase 7\n[!] Дизайн\n    В global.css оставлен только boilerplate. Все остальные стили перенесены в _template.css\n    Первичное упорядочивание _template.css\n    Везде, где имеет смысл, \"px\" заменены на \"em\"\n    При увеличении масштаба средствами СН иконки-спрайты нормально центрируются\n    Восстановлено выделение цветом отпускника\n    Восстановлен жолтый цвет заполненной части слайдера\n    Перенарезана рамка с более точными позициями\n\n[~] Локализация\n    Кэширование локализаций теперь может быть отключено в таблице 'config' установкой параметра 'locale_cache_disable' в 1\n    Апдейтер теперь сбрасывает кэш языков при обновлении\n\n[~] Флоты/Выбор миссии\n    Теперь при выборе миссии вместо \"=>\" меняется иконка\n\n\n2015-10-21 22:58:01 40a17.3 - Locale v3 - Phase 2\n[!] Локализация\n    Языковой фоллбэк на уровне строк - откат на другую локализацию, если не найдена строка в текущей\n\n\n2015-10-21 19:28:56 40a17.2 - Locale v3 - Phase 1\n[!] Локализация\n    Все файлы локализации в модулях работают через $a_lang_array\n\n[@] Код\n    Добавлены константы для рас\n\n\n2015-10-21 18:18:16 40a17.0 - Locale v3 - Phase 0\n[!] Локализация\n    Добавлена поддержка кэширования строк локализации во внешнем кэше. Это позволило уменьшить объем процесса на пользователя примерно на 300кб минимум\n    Все файлы локализации в базовом движке работают через $a_lang_array\n    Класс locale:\n        - Конструктор теперь грузить дефолтный язык на старте;\n        - Многалогав;\n\n[~] Меню\n    Отключен фрагмент JS-кода, относящийся к модулю\n\n[~] Настройки\n    Улучшено поведение игры при переключении языков\n\n\n2015-10-20 16:58:02 40a16.14\n[~] Дизайн\n    Скин EpicBlue - добавлена рамка\n    Скин supernova-ivash - улучшена работа с рамками\n\n\n2015-10-20 15:53:00 40a16.12\n[~] Дизайн/Скины/supernova-ivash\n    Добавлено обрамление (рамка) ко всем элементам\n\n\n2015-10-20 10:57:53 40a16.11\n[~] Дизайн/Темплейты/OpenGame\n    Улучшена разметка\n\n\n2015-10-19 18:37:54 40a16.8\n[%] Обзор Вселенной\n    Исправлена неработа ракетной атаки при вызове через кнопку быстрого действия\n\n\n2015-10-19 18:24:57 40a16.7\n[~] Меню\n    Ускорена работа меню в приложениях\n\n[%] Обзор Вселенной\n    Исправлена неработа фаланги при вызове через кнопку-название планеты\n\n\n2015-10-19 16:28:51 40a16.6\n[+] Обзор Вселенной\n    В \"Настройках\" на вкладке \"Интерфейс\" в разделе \"Вселенная\" добавлена возможность отключить кнопку \"Послать колонизатор для основания колонии на позиции номер Х\"\n\n\n2015-10-19 16:17:29 40a16.5\n[%] Список планет\n    Исправлена неработающая кнопка \"Свезти ресурсы\" в списке планет (страницы \"Планета\" и \"Империя\")\n\n\n2015-10-19 16:06:08 40a16.4\n[%] Обзор Вселенной\n    Исправлена ошибка отображения кнопки колонизации\n\n\n2015-10-19 15:52:41 40a16.2 - Вселенная - Phase 3\n[!] Обзор Вселенной\n    Полный редизайн вида Обзора Вселенной. Изменений настолько много, что нет смысла их тут описывать все - проще посмотреть самому\n    Те, кому не нравится новый вид - могут включить старый вид в \"Настройках\", вкладка \"Интерфейс\", галочка 'Использовать старый вид \"Обзора Вселенной\"'\n    Изменения в обоих видах \"Вселенной\":\n        - При выборе \"Ракетной атаки\" страница скроллируется на форму запуска ракет. Ей добавлена толстая красная рамка, что бы визуально выделить среди остальных элментов;\n        - После анализа востребованности фишек, убраны быстрые действия \"Просмотреть место игрока в статитстике\" и \"Добавить игрока в друзья\". Действия по-прежнему доступны в попапе игрока, однако быстрые кнопки использовались настолько мало, что было принято решение убрать эти опции;\n    Изменения в старом виде Вселенной:\n        - Быстрые действия на планету/аккаунт сделаны кнопками для облегчения судьбы наших мобильных друзей;\n\n\n2015-10-19 10:42:39 40a16.0 - Вселенная - Phase 0\n[!] Вселенная\n    Возможность выбора старого и нового вида Вселенной\n    Доработки старого интерфейса\n\n\n2015-10-18 22:42:01 40a15.13\n[+] Вселенная\n    Переработаны попапы игрока и Альянса аналогично кнопкам миссий\n\n[~] Верфь/Оборона\n    Чекбокс автоконвертации больше не скиннится как кнопка\n\n\n2015-10-18 21:12:08 40a15.12\n[+] Вселенная\n    Добавлена кнопка ракетной атаки в планетарные попапы\n    Кнопки в планетарных попапах выглядят аккуратнее\n    При выборе ракетной атаки страница автоматически скроллируется на форму ракетной атаки\n    При активации AJAX-действия (отправка ракеты, отправка шпионов итд) в центре экрана пишется результат совершения действия\n    Значительно уменьшен размер генерируемой страницы Вселенной:\n        - С иконок ракетной атаки убран код onclick и перенесен в jQuery-обработчики\n        - Кнопки миссий в планетарном попапе переделаны на button_pseudo, a onclick-код вынесен в jQuery-обработчики\n\n[+] JS\n    Существенно расширены возможности обработчика HTML-аттрибута \"go\". Теперь он может отправлять флоты с миссиями и открывать УРЛ-ы в новом окне\n\n\n2015-10-18 18:23:45 40a15.11\n[~] Вселенная\n    Изменен вывод названия планеты/луны в попапе\n\n\n2015-10-18 17:33:14 40a15.10\n[%] Новости\n    Исправлена ошибка двойного экранирования служебных символов в вопросах опроса\n\n\n2015-10-18 17:17:07 40a15.8\n[~] Навбар\n    На кнопку очереди верфи добавлена индикация количества оставшихся в очереди слотов\n\n[~] JS\n    Оптимизирована работа функции popup_show()\n    Убрано слежение за позицией курсора. Это должно улучшить работу страницы на слабых машинах\n\n\n2015-10-17 14:49:34 40a15.6\n[~] Админка/Обзор\n    Первыми в списке всегда выводятся игроки и только потом - Альянсы\n\n\n2015-10-17 14:46:32 40a15.5\n[+] Админка/Обзор\n    Пункт меню \"Обзор\" в админке теперь использует тот же код, что и список пользователей - за исключением того, что \"Обзор\" показывает только игроков онлайн и показывает Альянсы\n    Таким образом теперь в пункте меню \"Обзор\" можно использовать весь функционал страницы \"Список пользователей\": сортировку, просмотр информации о пользователе, имперсонейт итд\n\n[@] Код\n    Увеличино дефолтное значение размера системы до 16 планет\n\n\n2015-10-17 10:47:46 40a15.3\n[%] Верфь/Оборона\n    Исправлена ошибка невозможности постройки чего-либо на Верфи/Обороне при отключенной в Настройках кнопке автоконвертации\n\n\n2015-10-17 09:47:57 40a15.1\n[%] Новости\n    Исправлен вывод дробного количества процентов проголосовавших\n\n\n2015-10-17 09:41:46 40a15.0\n[!] Новости\n    Общий редизайн блока новостей\n    Добавлена индикация общего количества проголосовавших\n    Результаты опроса:\n        - Бар, показывающий процент проголосовавших, выводится под самим ответом\n        - К каждому варианту ответа добавилась индикация процента проголосовавших\n\n[~] Скины/supernova-ivash\n    Добавлен нормальный промежуточный цвет фона между TH и TD. В частности, активно используется в новостях\n    Для новостей выставлены цвета скина\n[~] Оборона/Верфь\n    На кнопкочекбокс автоконвертации так же распространяется действие опции \"Скрыть кнопку автоконвертации\" в \"Настройках\"\n\n[%] Чёрный Рынок/Обмен ресурсов\n    Исправлена ошибка невозможности обмена, когда количество ТМ на аккаунте было менее двухкратной стоимости обмена\n\n\n2015-10-17 06:52:57 40a14.11\n[~] Меню\n    Восстановлен прогресс-бар исчерпания премиума в соответствующем пункте меню\n    В стандартном режиме обычный пункт меню занимает всю площадь строки - как и меню-заголовок\n    Улучшено отображение метки \"Свежие\" в пункте меню \"Новости\"\n    Режим \"низкие пункты меню\":\n        - Восстановлен функционал подсветки пункта, на котором сейчас находится курсор мышки\n        - Подсвечивается весь пункт меню, а не только ссылка\n        - Клик срабатывает на всей площади пункта меню, а не только на ссылке\n\n\n2015-10-16 19:20:05 40a14.9\n[~] Планета\n    Исправлена ошибка позиционирования попапа с летящими флотами\n\n\n2015-10-16 14:55:04 40a14.8 - Скины v2 - Phase 6\n[~] Скины\n    Вид элемента <textarea> приведен к общему виду\n    Исправлена ошибка на странице \"Верфь\" и \"Оборона\"\n\n\n2015-10-16 14:09:38 40a14.6\n[%] Скины\n    Исправлен вид кнопки \"Показать пароль\" в Хроме\n\n\n2015-10-16 10:01:07 40a14.5 - Скины v2 - Phase 5\n[#] skins/immi\n    [!] В игру добавлен скин immi\n\n\n2015-10-16 09:29:54 40a14.3 - Скины v2 - Phase 4\n[+] Скины/supernova-ivash\n    Переделаны CSS-стили для jQuery-UI в цветовую гамму скина\n\n\n2015-10-16 07:11:47 40a14.2 - Скины v2 - Phase 3\n[~] Скины\n    Закончено выделение общего кода из CSS-файлов скина и перенос его в CSS-файл темплейта\n    Добавлено масштабирование фона в скин supernova-ivash\n    Улучшено масштабирование фона в скин EpicBlue\n\n\n2015-10-16 04:42:24 40a14.1 - Скины v2 - Phase 2\n[~] Скины\n    Часть общей информации перенесена из скиновых CSS-файлов в общий CSS-файл темплейта\n    Добавлен фикс jQueryUI для работы tabs с тэгом <base>\n    Временно отключен checkator - до выяснения проблем совместимости с jQUI.tabs\n\n\n2015-10-16 02:57:28 40a14.0 - Скины v2 - Phase 1\n[!] Скины\n    Обновлен вид чекбоксов и радиобоксов\n    Переделан вид стандартных элементов управления (кнопок итд) под новый дизайн\n    Унифицирован вид jQueryUI элементов с кастомными элементами в скине EpicBlue\n    Переделаны кнопки на странице построек для поддержки нового дизайна\n    jQueryUI проапгрейжен до версии 1.11.4\n    Теперь не грузится дополнительно jQuery-UI CSS скина - все стили скина должны быть в skin.css\n    Отключена поддержка серверных CSS - задача модификации стандартного скина решается клонированием в новый скин\n\n\n2015-10-15 07:26:21 40a13.17 - authV4 Alpha v4.8.9\n[+] Авторизация\n    Класс auth_local\n        - Возвзращена поддержка древних кук. Видимо - не все сконвертировали...\n\n\n2015-10-14 19:59:43 40a13.16\n[+] Новости\n    Переработан интерфейс Опроса\n    Теперь под кнопкой \"Показать текст новости\" скрываются параграфы (два CRLF подряд), а не предложения (жесткий перевод строки - 1 CRLF)\n    Кнопка \"Показать текст новости\" использует новый стиль\n    Кнопка закрытия новости теперь машстабируется вместе с интерфейсом\n\n\n2015-10-14 12:21:49 40a13.15\n[@] Код\n    Немного почищен и оптимизирован код\n\n\n2015-10-14 11:56:28 40a13.14\n[!] JavaScript\n    Заметно ускорена начальная загрузка страниц за счёт избавления от element_cache\n[!] Чёрный рынок\n    Улучшен интерфейс заглавной страницы\n    Обмен ресурсов:\n        - Переверстана страница Обмена Ресурсов. Теперь ею должно быть удобнее пользоваться мобильным пользователям, а так же меньше вероятность совершить неправильную операцию\n        - Добавлено подтверждение при обмене ТМ на ресурсы\n\n[~] Меню\n    Теперь при наведении курсора на пункт меню он подсвечивается\n[~] Флоты/Отправка флотов\n    Улучшен вид кнопок выбора миссий\n\n[-] Симулятор\n    Убрана отладочная галочка \"Симуляция\" - симулятор всегда работает в режиме симуляции\n\n\n2015-10-14 05:00:20 40a13.12 - authV4 Alpha v4.8.8\n[-] Авторизация\n    Класс auth_local\n        - Убрана поддержка древних кук - за 5 месяцев кто играет уже сконвертировал себе куки в новый формат;\n\n\n2015-10-14 04:02:35 40a13.11\n[~] Чёрный рынок\n    Немного переделан интерфейс заглавной страницы\n\n\n2015-10-14 03:06:41 40a13.10\n[#] chat_advanced 5c6\n    (@) Код\n        $microtime заменен на SN_TIME_MICRO\n\n[~] Таймеры\n    Объявление таймера в темплейте:\n        - Свойства таймера 'active' и 'start_time' теперь устанавливается в sn_timer_prepare() - соответственно они убраны из обновления\n        - Тип таймера объявляется через соответствующую константу TIMER_xxx\n\n[@] Код\n    PHP\n        - Во всём коде $microtime заменён на SN_TIME_MICRO\n    JavaScript\n        - localTime переименован в timeBrowser\n        - Убрана переменная TIME_NOW\n    Из темплейтов убрана переменная TIME_NOW\n    Почищен и упорядочен код\n\n2015-10-14 01:35:55 40a13.8\n[+] Таймеры\n    PHP-код замера разницы во времени вынесен из general.php в отдельный класс playerTimeDiff\n    Добавлен алгоритм замера разницы в часах по GMT-времен браузера и сервера\n\n[~] Вёрстка\n    Изменены директивы viewport для лучшей поддержки мобильных устройств\n[~] Апдейтер\n    В апдейтере в некоторых местах empty() заменены на !isset() согласно логике апдейта\n\n[@] Разное\n    В бенчмарк добавлено время рендеринга страницы в браузере, измеряемое через JavaScript\n    В основном коде $microtime заменён на SN_TIME_MICRO\n\n\n2015-10-13 04:15:35 40a13.7\n[%] BBCode\n    Исправлена ошибка парсинга строки, состоящей только из УРЛ и начинающейся с http://\n    Исправлена ошибка парсинга префикса sn:// в составе BBCode 'url'\n\n\n2015-10-13 04:02:45 40a13.6\n[#] menu_customize 0d5\n    (!) Настройки/Меню\n        Все настройки меню вынесены в отдельный таб \"Настройка меню\"\n        Настройки элементов меню сгруппированы по категориям\n\n[~] Меню\n    Весь CSS-код вынесен из темплейта меню в CSS-файл темплейта\n\n\n2015-10-12 02:15:45 40a13.4\n[+] Интерфейс/Меню\n    Для недовольных новым стандартным меню - в Настройки добавлена опция \"Низкие пункты меню\", которая возвращает меню его прежний вид. Опция СОВМЕСТИМА с настройкой \"БОЛЬШИЕ ПУНКТЫ МЕНЮ\"\n\n\n2015-10-12 23:12:53 40a13.1\n[#] menu_customize 0d4\n    (+) Поддержка редизайна меню\n        Опция \"Показать пункты меню в виде кнопок\" переименована в \"БОЛЬШИЕ КНОПКИ В МЕНЮ\"\n        Добавлена опция \"Белые надписи на кнопка меню (отключить цвета)\"\n\n\n2015-10-12 23:08:18 40a13.0\n[!] GIT\n    До этого момента уже некоторое время не учитывался часовой пояс в PHP-CLI на локальной машине и, соответственно, в хуках GIT-a. Значит с какого-то времени и до сейчас даты даны стоят неправильные\n    TODO - ИСПРАВИТЬ ДАТЫ. Проверить, когда же был переход на новую версию PHP\n\n[!] Интерфейс/Меню\n    Редизайн пунктов меню\n    Теперь в стандартном режиме пункты меню выглядят, как кнопки. Однако при этом являются ссылками - т.е. позволяют открывать страницы в новом окне\n    Настройке \"Показывать пункты меню в виде кнопок\" заменён на \"БОЛЬШИЕ КНОПКИ В МЕНЮ\" и теперь просто увеличивает размер пунктов меню - удобно для мобильных устройств\n    Добавлена настройка \"Белый надписи на кнопках меню...\" убирает раскраску пунктов меню, оставляя выделенными только категории\n    Немного переупорядочены пункты меню так, что бы уменьшить количество категорий и не было неактивных заголовков в категориях\n\n\n2015-10-12 15:20:11 40a12.8\n[~] JS/sn_timer\n    Дополнительные оптимизации по скорости для Хрома\n\n\n2015-10-12 02:34:59 40a12.7\n[%] JS/sn_timer\n    Исправлена глупая, глупая алгоритмическая ошибка\n\n\n2015-10-12 01:07:26 40a12.6\n[+] Навбар\n    Если в почтовом ящике есть сообщения - общее количество соощений в навбаре мягко мерцает\n\n\n2015-10-11 22:34:54 40a12.5\n[!] Интерфейс/Очередь построек\n    На странице \"Обзро планеты\" для каждой очереди добавлена дата и время её завершения\n    Функционирование таймера для слота с 1-секундным временем строительства восстановлено, как и было ранее - т.е. фактически всегда будет написано 00:00:01\n    Добавлены эффекты анимации к списку юнитов в очереди:\n        - При начале строительства нового юнита таймер на миниатюре мигает один раз;\n        - При окончании строительства всех юнитов в слоте - слот исчезает;\n        - Отключить анимацию эффектов в игре можно в пункте меню \"Настройки\", вкладка \"Интерфейс\", галочка \"Отключить эффекты анимации в игре\"\n\n\n2015-10-11 13:26:35 40a12.3\n[%] JS/sn_timer\n    Исправлена ошибка, не учитывающая, что в JS время - в тысячных секунды\n\n\n2015-10-11 10:17:18 40a12.2\n[!] JS/sn_timer\n    Теперь таймеры хорошо переносят кнопки \"Назад\"-\"Вперед\" в браузерах - время почти не сбивается\n    Еще оптимизирован код\n    Добавлены патчи для работы на разных нестандартных браузерах\n    Устранены все найденные ошибки\n\n\n2015-10-11 07:58:53 40a12.1\n[!] JS/sn_timer\n    Промежуточный коммит для исправленной версии sn_timer\n\n\n2015-10-10 18:47:24 40a12.0\n[!] JS/sn_timer\n    Весь DOM-код переписан с использованием jQuery. Более активно используется кэширование\n    Убраны лишние обсчёты невидимых элементов\n    Полностью переписан код, относящийся к очередям\n    Оптимизированы и другие части таймера\n    Новый код максимально оптимизирован (20-50% ускорение). Но заметно это будет лишь на слабых устройствах - на мощных компьютерах абсолютные значение выигрыша исчисляются микросекундами\n\n[!] Интерфейс/Очередь построек\n    Переделана Очередь построек\n    Теперь время до постройки одного юнита в первом слоте пишется на самом юните\n    \"Общее время\" теперь индицируется сверху\n    Оптимизирована выдача HTML-кода - меньше использование свойства style\n    Добавлена поддержка нового кода sn_timer\n\n[!] CSS\n    Первый этап оптимизации - унифицированы CSS-стили на темплейте OpenGame для скинов EpicBlue и supernova-ivash\n    Немного уменьшены итоговые размеры CSS-файлов\n\n\n2015-10-10 10:19:29 40a11.8 - authV4 Alpha v4.8.7\n[!] Авторизация\n    Еще немного работы для поддержки auth_vkontakte\n\n[@] Код\n    Обсчёт статистики\n        - Теперь обсчёт статистики запускается не AJAX-запросом, а в ходе init.php. Это должно улучшить отзывчивость игры\n    По умолчанию - отключена запись полных УРЛов в таблицу счётчиков. Включить её можно в конфигурации, установив security_write_full_url_disabled в 0\n    Теперь в таблице `security_url` сохраняются пути без учета SN_ROOT_RELATIVE\n    Назначения констант вынесены из classSupernova - так опять будет нормально работать autocomplete в IDE по этим константам\n\n\n2015-10-10 03:56:31 40a11.7 - authV4 Alpha v4.8.6\n[!] Авторизация\n    Добавлен базовый класс авторизации auth_abstract\n        - auth_local теперь является потомком auth_abstract;\n        - публичные методы и свойства вынесены в auth_abstract;\n        - provider_id вынесен из $manifest в отдельное свойство;\n\n\n2015-10-09 22:31:21 40a11.6 - authV4 Alpha v4.8.5\n[!] Авторизация\n    Доделана Имперсонация для поддержки ММ на аккаунте\n    Почищен код\n\n\n2015-10-08 02:57:35 40a11.3 - Немного Мобильной И Широкоформатной Любви\n[+] Флоты/Отправка флотов\n    Выбор миссий на странице выбора миссий сделан иконками-пиктограммами. Это улучшит юзабилити для игроков на мобильных устройствах\n    Для миссии \"Колонизация\" введено цветовое кодирование количества колоний:\n        - Красный - количество колоний больше максимально возможного количества (например, вследствие исчерпания срока Премиум-аккаунта или окончания специальных акций)\n        - Оранжевый - количество колоний равно максимальному количеству. Нельзя колонизировать ни одной новой планеты;\n        - Желтый - количество колоний на 1 меньше максимального количества. Текущая миссия заблокирует возможность дальнейшего расширения;\n        - Зеленый - количество колоний на 2 и более единиц меньше максимального количества. Можно спокойно запускать текущую миссию.\n    Улучшено отображение состава флота на больших и маленьких экранах\n    Теперь при отсутствии кораблей на планете на экране подбора флота доступна кнопка \"Свезти ресурсы\"\n    Весь автономный JS-код вынесен в отдельный файл - это уменьшило размер страницы и улучшило отзывчивость\n\n[~] Темплейт/OpenGame\n    Триггеры на переключение по медиа-запросам выставлены в rem. Это должно улучшить работу на мобильных устройствах\n\n[@] Код/JS\n    Нельзя полагаться на document.ready() из-за возможных проблем с загрузками сторонних скриптов (например - Яндекс.Метрика). Поэтому теперь соответствующие действия активируются насильно по загрузке футера\n\n\n2015-10-02 10:35:46 40a11.2\n[~] Админка\n    Добавлены разделители разрядов в сообщение о начислении игроку ТМ и ММ через админку\n\n\n2015-10-02 08:46:41 40a11.1\n[~] Обзор Планеты\n    В расчёте миниатюр планет и иконок на них транзакции теперь делаются per-planet, а не глобально по всем планетам. Это должно существенно улучшить отзывчивость для остальных игроков\n[~] Империя\n    Транзакции теперь делаются per-planet, а не глобальные по всем планетам. Это должно существенно улучшить отзывчивость для остальных игроков\n\n\n2015-10-01 22:43:09 40a11.0\n[#] interface_batch_operations v0a1\n    [~] Транзакции\n        Транзакции теперь делаются per-planet, а не глобальные по всем планетам\n        Это должно существенно улучшить отзывчивость для остальных игроков - за счёт небольшого ухудшения производительности для пользователей масс-фишек\n\n[~] Флот/Своз ресурсов\n    Транзакции теперь делаются per-planet, а не глобальные по всем планетам\n    Это должно существенно улучшить отзывчивость для остальных игроков - за счёт небольшого ухудшения производительности для пользователей своза ресурсов\n\n\n2015-09-29 22:43:38 40a10.31\n[~] Навбар\n    Изменена иконка Верфи в навбаре - теперь на стандартной плашке!!!\n\n\n2015-09-25 11:41:46 40a10.30 - authV4 Alpha 4.8.2\n[!] Авторизация/ММ на аккаунте\n    Исправлены ошибки в модулях платежей robokassa и xsolla\n\n\n2015-09-25 10:06:05 40a10.28 - authV4 Alpha 4.8.1\n[!] Авторизация/ММ на аккаунте\n    Класс sn_module_payment:\n        - Исправлена ошибка начисления ММ;\n    Код почищен\n\n\n2015-09-24 11:39:37 40a10.25 - authV4 Alpha 4.8\n[!] Авторизация/ММ на аккаунте\n    МетаМатерия:\n        - Теперь ММ является свойством аккаунта, а не записи игрока;\n        - Так же статус \"Бессмертного\" поднят на уровень аккаунта;\n    Класс Account:\n        - Старая функция mm_points_change() внесена в виде метода metamatter_change() - с соответствующими переделками\n        - Добавлена проверка на существование нужных таблиц в БД - пригодится в ЕА для выявления критических проблем с миграцией;\n    Платежи:\n        - Полностью переписан базовый абстрактный класс платежей;\n        - Полностью переписаны модули платежей robokassa, webmoney, xsolla;\n        - Унифицированы огромные куски кода как в связке абстрактный класс/модули так и между модулями;\n        - Теперь всегда создаётся запись в таблице `payment` при начале платежа. Это позволит адекватно оценивать восстребованность разных платёжных систем;\n        - Теперь в платежные системы передаётся количество покупаемой ММ с учётом бонуса;\n        - Добавлена защита на случай совершения покупок на границе акций. В этом случае могли не совпадать заявленные суммы покупки/ММ с актуальными на момент попытки начисления ММ и могла возникнуть ошибка;\n        - Унифицирована защита от двойных зачислений;\n    Админка:\n        - Переработано начисление ММ с учётом изменений;\n    Код:\n        - ACCOUNT_PROVIDER_BASIC -> ACCOUNT_PROVIDER_NONE;\n        - Убран статус PAYMENT_STATUS_TEST, а так же переработана работа с тестовыми платежами;\n    Разное:\n        - Исправлена ошибка в Хроме с невозможностью сменить аккаунт на странице выбора имени игрока;\n        - Еще немного логов богу логов!\n\n\n2015-09-18 12:42:51 40a10.24 - authV4 Alpha 4.7\n[!] Авторизация\n    Полностью работает impersonate на уровне пользователя\n    Класс PlayerToAccountTranslate:\n        - Из auth вынесен функционал работы с таблицей трансляции игроков в аккаунты\n    Класс core_auth (бывший auth):\n        - Класс переименован в core_auth для поддержания единообразия в именовании модулей;\n        - Почти все методы и поля сделаны не-статическими;\n        - Проставлены нужные visibility для всех методов и полей;\n        - Методы db_player_name_exists() и db_player_get_max_id() вынесены в глобальные функции;\n\n\n2015-09-18 08:32:37 40a10.23 - authV4 Alpha 4.6\n[!] Авторизация\n    Регистрация теперь двухэтапная:\n        - На первом этапе вводится емейл и пароль. Регистрируется аккаунт\n        - На втором этапе игроку предлагается выбрать имя, под которым он будет виден в игре. По умолчанию - это название почтового ящика без домена. Регистрируется новый игрок\n        - Новые аккаунты регистрируются с емейлом в качестве имени аккаунта\n    Логин теперь двухэтапный:\n        - На первом этапе проверяется имя/емейл игрока. Аутентифицируется аккаунт\n        - На втором этапе аккаунт авторизируется - выбирается игрок, который привязан к данному аккаунту и по нему выставляются права доступа. Если игрока нет - предлагается зарегестрировать нового\n        - Теперь игрок может входить в игру по имени аккаунта или емейлу\n    Класс auth:\n        - Для второго этапа авторизации добавлена форма выбора имени игрока - модель и вид в core_auth\n        - auth::$account теперь выставляется в выбранный аккаунт\n        - Убраны неиспользуемые куски кода\n    Классa auth_local:\n        - В класс перенесены методы password_reset_send_code() и password_reset_confirm() - они теперь являются частью процесса логина\n    Настройки:\n        - Добавлена индикация имени текущего аккаунта (логина) на страницу \"Настройки\", вкладка \"Интерфейс\"\n        - Полностью работает смена пароля\n        - Полностью работает проверка пароля (например, при покидании колонии)\n    Класс Confirmation:\n        - Работа с подтверждениями вынесены в отдельный класс Confirmation\n        - Подтверждения работают на том же уровне, что и аккаунт\n    Темплейт логина переделан под 'em'\n    Переработан код для поддержки двухэтапной авторизации\n    Работает имперсонейт на вход, но на выход вообще выкидывает из игры\n\n[~] Флоты/Отправка кораблей\n    Добавлены вертикальные границы между миниатюрами кораблей\n\n[~] Навбар\n    Убрана горизонтальная зеленая линия над общим количеством ТМ в случае, если на аккаунте нет ММ и/или ММ не подключена в игре\n\n\n2015-09-17 09:16:45 40a10.22 - authV4 Alpha 4.51\n[!] Авторизация\n    Избавились от F_ACCOUNT\n    Избавились от $data в auth_local\n    Удалён неиспользуемый код в auth и auth_local\n\n[~] БД\n    `account_name` в `account` теперь уникальное\n    Добавлены поля для ЕММ в `account`\n\n\n2015-09-15 05:23:56 40a10.21 - authV4 Alpha 4.5\n[!] Авторизация\n    Полнофункциональный класс Account\n        - Хранит все данные об аккаунте\n        - Все индивидуальные операции над аккаунтом вынесены из провайдера в методы Account\n        - Методы разделены на public и protected\n    Класс RequestInfo\n        - Из провайдера вынесены методы записи счетчика и системы безопасности\n    Класс auth_local\n        - Теперь является универсальным local/central классом\n        - Упорядочены методы, разделены на public и protected, убраны неиспользуемые методы\n\n[~] Модули\n    Конфиг модуля может быть в другом каталоге - например, в общем конфиге приложения\n\n\n2015-09-10 09:58:58 40a10.20\n[%] Вселенная/Планеты\n    Исправлена очепятка в коде определения типа ядра колонии, из-за которой не бралось реальной значение Астрокартографии у игрока\n\n\n2015-09-07 21:11:48 40a10.19\n[~] Режим Рубилова\n    Уточнено уведомление о провале захвата в победном бое, не закончвшемся одним раундом\n\n\n2015-09-05 17:07:15 40a10.17\n[+] UBEv4\n    Добавлена поддержка Режима Рубилова в отчётах\n\n\n2015-09-05 16:14:04 40a10.16 - authV4 Alpha 4.2 - auth_central\n[!] Авторизация\n    Избавились от F_LOGIN_STATUS в классах\n    Избавились от auth::$hidden\n    $user_id_to_provider теперь свойство auth\n    $auth_level_max_local теперь свойство auth\n    Почищен и немного переупорядочен код\n\n\n2015-09-03 21:28:08 40a10.15 - authV4 Альфа3.3 - подготовка к auth_central\n[!] Авторизация\n    Игрок теперь прописывается автоматически на все авторизированные аккаунты\n    Максимальный auth_level всех игроков на авторизированных аккаунтах теперь является свойством auth\n    Домен, путь, имя куки и секретное слово для куки теперь берутся из свойств auth_local\n    Больше логов богу логов!!!\n\n[!] Код/БД\n    Префикс таблиц теперь является свойством db_mysql, так что методы не обращаются к внешним сущностям\n    Список таблиц теперь хранится в db_mysql\n    Исправлена ошибка реконнекта при живой БД (апдейтер вызывал)\n\n\n\n2015-09-02 01:00:17 40a10.14 - authV4 Альфа3.2\n[!] Авторизация\n    auth использует свою собственную БД\n    auth_local использует свою собственную БД\n\n\n2015-09-01 23:39:51 40a10.13 - DB redo Phase 4\n[!] Код/БД\n    Низкоуровневые функции SQL вынесены в отдельные драйвера\n        - Добавлена возможность выбора драйвера SQL\n        - Название драйвера задается в config.php в поле 'sn_driver' массива $dbsettings\n        - В стандартном комплекте поставляются два драйвера - db_mysql_v5 и db_mysql_v4\n        - db_mysql_v5 использует более новый интерфейс ext/mysqli\n        - db_mysql_v4 использует интерфейс ext/mysql, который объявлен устаревшим и не будет использоваться начиная с PHP 5.5.0\n        - По умолчанию используется db_mysql_v5. В случае проблем с PHP у хостера можно попробовать использовать db_mysql_v4\n        - Подробнее о разнице в интерфейсах MySQL API можно прочитать тут: http://php.net/manual/en/mysqlinfo.api.choosing.php\n        - Так же можно написать свой драйвер по примеру предоставленных. Результаты его методов должны эмулировать поведение MySQL\n        - Драйвера находятся в каталоге /includes/classes/\n    В auth_local избавились от F_INPUT\n\n[!] Код/СН\n    Куски инициализации вынесены в classSupernova. Не то, что бы им там было место - но теперь init.php немного разгружен\n    Настройки из файла конфигурации теперь хранятся в classSupernova\n\n\n2015-09-01 18:07:06 40a10.12 - DB redo Phase 3\n[!] Код/БД\n    Убран $link в заголовках функции\n    Причесан код и переупорядочены функции\n\n\n2015-09-01 16:42:38 40a10.11 - DB redo Phase 2\n[!] Код/БД\n    Упорядочен код инициализации и реакция на ошибки БД при инициализации\n    Убрано использование $link в коде\n\n\n2015-09-01 14:36:37 40a10.10 - DB redo Phase 1\n[!] Код/БД\n    Изоляция БД-код в отдельном объекте\n\n\n2015-08-31 12:34:21 40a10.8\n[%] Вселенная/Планеты\n    Исправлена ошибка вычисления диаметра планеты\n\n\n2015-08-30 15:04:04 40a10.6 - authV4 Альфа3\n[!] Авторизация\n    Изолированы БД-запросы в отдельных методах\n    Удалены функциональные дубликаты методов\n    Часть полей из $data перенесены в свойства классов\n\n\n2015-08-29 15:24:46 40a10.5 - authV4 Альфа2\n[!] Авторизация\n    Информация о запросе (IP, браузер, УРЛ итд) вынесены в отдельный класс RequestInfo\n    Работает смена пароля\n    Добавлен, но не проверен код смены емейла\n    Добавлен, но не проверен код проверки пароля\n    Немного почищен код\n\n\n2015-08-28 00:41:31 40a10.4\n[~] Авторизация\n    Добавлены сообщения для ошибок при регистрации игрока\n    Почищен код\n\n\n2015-08-27 19:38:58 40a10.2\n[+] Партнерская программа\n    Теперь когда аффилейт (игрок, приглашенный реферралом) покупает МетаМатерию, то реферрал (игрок, пригласивший аффилейта) получает 20% от купленной ММ в виде Тёмной Материи (в отличии от 10% при получении аффилейтом ТМ)\n\n\n2015-08-27 19:14:05 40a10.0 - authV4 Альфа\n[!] Авторизация\n    В очередной раз переписана система авторизации. Цель - подготовка к вводу в игру Единого Аккаунта\n    Информация игрока (таблица `users`) отвязана от информации о логине/пароле\n    \"Имя игрока\" в игре и \"Логин\" (aka \"Имя аккаунта\") теперь разные вещи\n    Работает, но не оттестировано:\n        Вход в игру\n        Регистрация в игре (имя игрока автоматически выбирается равным имени аккаунта)\n        Сброс пароля\n    Не работает:\n        Смена имени ИГРОКА\n        Смена пароля\n        Смена вторичного емейла\n        Верификация вторичного емейла\n    Не реализовано:\n        Смена имени АККАУНТА\n        AUTH_LEVEL аккаунта (под вопросом необходимость реализации)\n        Смена основного емейла\n        Верификация основного емейла\n        ...и многое, многое другое!\n\n\n2015-08-22 18:24:26 40a9.10\n[+] Код\n    Поддержка захвата планет в РР\n\n\n2015-08-21 11:24:35 40a9.9\n[~] Навбар\n    Счетчики сообщений теперь располагаются не в линию, а по углам иконки сообщений:\n        - Левый верхний - личные сообщения;\n        - Правый верхний - сообщения от Альянса;\n        - Левый нижний - сообщения от Администрации;\n        - Правый нижний - общее количество сообщений.\n    Таким образом на мобильных устройствах будет гораздо проще выбрать нужный тип сообщений\n\n\n2015-08-21 10:47:52 40a9.8\n[%] Планета/Управление\n    Исправлена нерабочая автоконвертация ММ при покупке секторов\n\n\n2015-08-20 04:47:33 40a9.7\n[+] Чёрный рынок/Обмен ресурсов\n    Добавлена поддержка автоконвертации ММ на страницу\n\n\n2015-08-20 00:48:25 40a9.6\n[~] Метаматерия\n    Улучшено логгирование автоконвертации\n\n\n2015-08-20 00:26:08 40a9.5\n[+] Планета/Управление\n    Добавлена поддержка автоконвертации ММ на страницу\n\n\n2015-08-20 00:03:59 40a9.4\n[%] Метаматерия\n    Исправлена ошибка в работе автоконвертации\n\n\n2015-08-19 22:28:30 40a9.2\n[%] Метаматерия\n    Исправлена ошибка при покупке через Иксоллу\n\n\n2015-08-19 22:10:44 40a9.0 - Автоконвертация ММ в ТМ\n[!] Метаматерия\n    Теперь не нужно вручную конвертировать ММ в ТМ - конвертация осуществляется автоматически при нехватке ТМ. Это упростит обращение с ММ для новых игроков\n    Переработан интерфейс покупки Метаматерии с учётом добавления в игру автоконвертации\n\n\n2015-08-19 05:28:59 40a8.12\n[%] Авторизация\n    Исправлена ошибка необновления \"онлайн\" статуса игрока\n\n\n2015-08-19 04:37:02 40a8.9\n[%] Вселенная/Планеты\n    Исправлена ошибка, когда стартовой планете не назначалось изображение\n\n\n2015-08-19 03:56:24 40a8.8\n[+] Навбар\n    В навбар добавлена кнопка \"Планета\". Клик на неё откроет страницу \"Обзор Планеты\". В правом нижнем углу кнопки показывается количество колоний у игрока и максимально возможное количество колоний\n    В навбар добавлена кнопка \"Верфь\". Клик на неё откроет страницу \"Верфь\". При наличии юнитов в очереди Верфи на кнопке будет отображено:\n        - Название юнита в текущем слоте очереди (т.е. корабль, который сейчас строится)\n        - Количество оставшихся юнитов в текущем слоте\n        - Время, оставшееся до окончания постройки текущего корабля в текущем юните\n        - Общее время до окончания постройки всех юнитов в очереди\n    Добавлена возможно отключить в навбаре следующие кнопки:\n        - \"Планета\"\n        - \"Верфь\"\n        - \"Квесты\"\n    Отключение кнопок происходит в пункте меню \"Настройки\", вкладка \"Интерфейс\", раздел \"Навигационная панель\"\n\n\n2015-08-19 01:50:06 40a8.7\n[%] Авторизация\n    Исправлено отсутствие вывода информации об ошибках при логине/регистрации/сбросе пароля\n\n\n2015-08-18 15:01\n[#] menu_customize 0d3\n    (%) Пустые пункты меню\n        Теперь пустые пункты меню не показываются в меню при включенном модуле настройки меню\n\n\n2015-08-18 12:18:38 40a8.6 - Дальнейшая подготовка к режиму \"Рубилово\"\n[~] Интерфейс\n    В режиме \"Рубилово\" из навбара убрана иконка исследований, а из Обзора Планеты - данные об очередях постройки и обороны\n\n\n2015-08-18 11:11:53 40a8.4\n[+] Админка\n    В настройки сервера вынесены:\n        - Стартовый размер хранилищ на планете\n        - Стартовое количество ресурсов на планете\n\n\n2015-08-18 10:31:07 40a8.3 - Дальнейшая подготовка к режиму \"Рубилово\"\n[+] Поддержка модуля game_skirmish\n    Для неуправляемых добывающих юнитов (P_MINING_IS_MANAGED = false) не показываются дропдауны выбора производительности\n\n\n2015-08-18 10:07:54 40a8.0 - Подготовка к режиму \"Рубилово\"\n[+] Поддержка модуля game_skirmish\n    Добавлена возможность отключать интерфейсы Построек, Обороны и Исследований\n    Добавлена поддержка 'grants' в интерфейсы на страницах постройки и \"Технологии\"\n    Добавлена возможность получить сразу весь список юнитов в локации из db_get_unit_by_location() по $unit_id = 0\n\n\n2015-08-13 18:20:58 40a7.1\n[~] Код\n    Кодировка файла includes/classes/core_classes.php изменен на UTF-8\n    Добавлен список блокировки родительских записей для поддержки LOC_FLEET\n    Добавлено сохранение NULL в db_changeset как NULL в MySQL\n    Добавлена новая отладочная функция print_rr()\n\n\n2015-08-06 18:24:16 40a7.0 - Планеты. Фаза 2\n[!] Вселенная/Планеты\n    Пересчитана математическая модель типов ядер с учётом пожеланий игроков. Поменялось ВСЁ: шансы нахождения определенных типов ядер, добыча ресурсов, цены на смену типа ядра итд\n    Теперь тип ядра, находимого в колонизации, ограничивается уровнем Астрокартографии:\n        От 0 до 5 - могут быть найдены только планеты с типами ядер \"Стандарт\", \"Водяной лёд\", \"Камень\", \"Руда\"\n        От 6 до 10 - так же могут быть найдены планеты с типами ядер \"Метановый лёд\", \"Силикат\", \"Оливин\n        11 и больше - так же могут быть найдены планеты с типами ядер \"Водородный лёд\", \"Кристалл\", \"Металл\"\n    Это сделано для облегчения игры для новичков и уравнивания стартовых условий\n    Например, если первая же найденная колония имеет тип ядра \"Водородный лёд\" - новый игрок получает сильное пенальти на развитие планеты (планеты \"Раритетного\" класса очень сложно застраивать сам-один и для нормальной застройки требуется транспорт других типов ресурсов с остальных планет)\n\n[+] Новапедия\n    Переписана статья о типах ядер в соответствии с текущим положением дел\n\n[~] Интерфейс/Империя\n    Цветовое кодирование типов ядер приведено в соответствие с описанием. Теперь чем \"тревожнее\" цвет, тем реже этот тип ядра можно найти при колонизации, т.е. красный цвет означает самый редкий (Раритетный) класс ядер , оранжевый - Редкий класс, желтый - Продвинутый класс, и, наконец, белый - стандартное ядро\n\n\n2015-08-06 09:43:22 40a6.6\n[~] Код\n    Код почищен от отладочных строк\n    В основном коде конструкции create_function заменены на лямбда-функции\n\n\n2015-08-06 08:36:28 40a6.5\n[!] Код\n    Реализован класс хранения многомерных массивов в плоской хэш-таблице\n[!] Игроки/Настройки\n    Полностью переписана система хранения настроек игроков с использованием класса ArrayAccessMultidimensional (старые настройки времен xnova пока хранятся в стиле xnova)\n\n[+] Интерфейс/Строительство\n    На страницы постройки (исследования, здания, верфь, оборона) добавлена возможность сортировки\n    Для каждой из вышеперечисленных страниц настройки сортировки сохраняются отдельно\n    Добавлена поддержка стандартной сортировки в обратном порядке\n\n[-] Интерфейс/Метаматерия\n    Убрана ненужная блок с курсами и скидками: цена теперь всегда выводится в выбранной валюте, а все скидки прописаны в блоке покупок кнопками\n\n\n2015-08-04 05:49:22 40a6.4\n[~] Админка/Логи\n    Ссылка на чистку логов теперь так же удаляет записи об обслуживании БД\n\n\n2015-08-04 05:40:38 40a6.3\n[~] Админка/Логи\n    Добавлена ссылка на удаление из логов записей об обновлении движка и статистики (неактуальную после успешно проведенных обновлений)\n\n\n2015-08-04 05:28:26 40a6.2\n[~] БД\n    Добавлена поддержка форсированной блокировки таблиц\n\n\n2015-08-03 15:05:26 40a6.0 - Планеты. Фаза 1\n[!] Вселенная/Планеты\n    !!!ВНИМАНИЕ!!! Полностью переписана с нуля генерация планет! Теперь она очень сильно отличается от оГеймовской!\n    Размер планеты:\n        Теоретические размеры планет составляют: минимальный - 30 секторов, максимальный - 330 (бывают только при наличии в системе планет-странников)\n        На стандартных слотах (с 1-го по 15-й) кардинально увеличен средний размер планеты - как за счёт пересчёта минимального и максимального количества секторов, так и за счёт исправления архитектурной ошибки xnova\n        Диаметр планеты теперь вычисляется по более вразумительной формуле\n    Типы ядер:\n        Полностью переработаны типы ядер\n        Пересчитана добыча всех типов ядер с учётом экономической эффективности и собранной статистики по типам планет у игроков (Нерф? Кто сказал - \"НЕРФ\"?!. Наоборот!)\n        Переработаны и переименованы старые типы ядер:\n            Во всех старых типах ядер увеличена добыча\n            \"Лёд\" переименован в \"Метановый лёд\" и улучшена добыча\n            \"Камень\", \"Силикат\" и \"Руда\" - улучшена добыча\n            \"Металл\" переименован в \"Оливин\". Его добыча находится на уровне старого типа \"Тяжелый металл\"\n            Ядра типа \"Тяжелый металл\" мигрированы в тип \"Оливин\" (с улучшением добычи)\n        Добавлены 4 новых типа ядра:\n            \"Водяной лёд\" - приблизительный аналог \"Камня\" или \"Руды\" для водорода\n            Три новых типа ядра - \"Водородный лёд\", \"Кристалл\" и \"Металл\" (новая версия) - с охренительной добычей соответствующих типов ресурсов\n        Десять типов ядер четырех классов:\n            \"Базовый\" класс - тип ядра \"Стандарт\"\n            \"Продвинутый\" - \"Водяной лёд\", \"Камень\", \"Руда\"\n            \"Редкий\" - \"Метановый лёд\", \"Силикат\", \"Оливин\n            \"Раритетный\" - \"Водородный лёд\", \"Кристалл\", \"Металл\"\n        Смена типа ядра:\n            Стоимость смены типа ядра теперь не зависит от текущего - можно недорого поэкспериментировать как минимум с продвинутым классом ядер, а то и с редкими\n            Стоимость смены типа ядра теперь зависит от типа планеты\n            В среднем - смена ядра планеты стала ГОРАЗДО выгоднее (в пересчете затраченной ТМ на полученный прирост производительности)\n        ПРИМЕРНО половина планет во Вселенной имеет улучшенный тип ядра (или водяной лёд, или камень, или руда), еще треть имеют тип ядра \"Стандарт\". Остаток приходится на долю остальных типов ядер\n    Температура:\n        Изменены диапазоны, в пределах которых меняется максимальная температура планеты\n        Разница между максимальной и минимальной температурами теперь не фиксирована (40 градусов в xnova), а определяется случайным образом в определенном диапазоне\n        Диапазон разницы температур зависит от места планеты в системе\n        !!!ВНИМАНИЕ!!! У всех старых планет (колонизированных до установки патча) минимальная температура установлена равной максимальной температуре для того, что бы изменения патча не повлияли на добычу\n    Добавлена поддержка \"планет-странников\":\n        Планета-странник - ранее блуждавшая по галактике экзопланета, захваченная тяготением звезды\n        Планеты-странники находятся за 15-м слотом в системе (если в настройках игры включен размер системы более 15 планет)\n        Хотя планета-странник может быть достаточно тёплой, ожидать околозвёздных температур в сотни градусов Цельсия от неё не стоит. А вот получить промороженную насквозь планету - вполне реально\n        Планеты-странники имеют огромный разброс по всем параметрам - соответственно и типов таких планет несчётнок множество\n        Получить граничные значения размера планеты можно только на планете-страннике\n        По умолчанию размер системы увеличен до 16 планет (ПРОВЕРЬТЕ ВАШИ ЗАМЕТКИ/ЗАКЛАДКИ НА ЭКСПЕДИЦИЮ!)\n    Переработано распределение планет по системе:\n        Естественно, средняя температура планеты падает на расстоянии от звезды (т.е. по мере увеличения\n        Теперь бесполезно искать планеты с ледяным ядром возле звезды или планеты с металлическим ядром на внешних границах системы\n        Кристаллические ядра встречаются практически по всей системе\n        Крупные планеты группируются в середине системы. Соответственно - на внешней и внутренней границах системы планеты более мелкие\n        Как и с размером планет - температурные диапазоны уменьшаются от центра системы к её границам\n    Изменена цветовая кодировка типа ядер на странице \"Империя\":\n        Белый - стандартное ядро\n        Красный - ядро \"Продвинутого\" класса\n        Оранжевый - \"Редкий\" класс\n        Желтый - \"Раритетный\" класс\n\n\n2015-06-21 03:41:00 40a5.3\n[#] player_award 0c4\n    (+) День Рождения СН 2015\n        Добавлены памятные знаки Дня Рождения СН\n\n\n2015-06-16 07:43:01 40a5.0\n[+] Авторизация\n    Базовая система имперсонации для новой авторизации\n\n\n2015-06-07 16:02:37 40a4.15\n[~] Ивенты\n    Добавлена поддержка удаления, перекрытия и расширения исходов в Экспедиции\n    Добавлена поддержка изменения максимального количества Экспедиций\n\n\n2015-06-07 13:16:42 40a4.14\n[~] Ивенты\n    Поддержка ивента \"Звезда родилась\"\n\n\n2015-06-01 18:09:23 40a4.13\n[+] Новости\n    Теперь если новость содержит больше 1 параграфа, то остаток новости скрывается под кнопкой \"Показать текст новости\"\n\n[+] BBCode\n    Добавлен новый тип ссылок: \"sn://\" доступный тем же, кому доступны обычные ссылки. Он развертывается в полный URL к корню игры, т.е. если игра стоит по адресу \"http://your_domain.tld/your_path/\", то \"sn://overview.php\" будет развернуто в \"http://your_domain.tld/your_path/overview.php\"\n\n\n2015-05-29 08:43:57 40a4.11\n[~] Ивенты\n    Поддержка Дня Защиты Детей\n\n\n2015-05-27 20:20:48 40a4.10\n[~] Дизайн\n    Небольшие изменения для поддержки RD - в основном в чате\n\n\n2015-05-26 14:11:32 40a4.9\n[~] Навбар\n    Переименованы кнопки-иконки, что бы уменьшить вероятность блокировки ад-блокерами\n\n\n2015-05-26 11:42:33 40a4.8\n[~] JS\n    Исправлена несовместимость с IE - использование зарезервированного только в IE слова `parent` в качестве названия переменной\n\n\n2015-05-24 14:08:41 40a4.6\n[~] Меню\n    Поддержка иконок меню, расположенных в любом месте движка\n\n\n2015-05-23 19:10:00 40a4.5\n[~] Страница построек/исследований\n    На странице постройки кораблей и обороны добавлено форматирование максимального количества юнитов для обычной постройки и постройки с автоконвертированием\n\n\n2015-05-23 18:56:14 40a4.4 - Автоконвертация Phase 2\n[!] Страница построек/исследований\n    На странице постройки кораблей и обороны добавлена возможность постройки с автоконвертацией\n    В строке максимального количества через слэш показывается максимально доступное количество юнитов при автоконвертации\n    Для включения режима автоконвертации необходимо отметить галочку \"Автоконвертация\". При этом все элементы страницы работают так же, как и раньше, однако максимальное количество юнитов к постройке становится равно максимально доступному количеству юнитов с учётом автоконвертации\n    Автоконвертация щадяща: даже если при включенной галочке автоконвертации поставить на постройку не больше максимального количества юнитов, доступных без автоконвертации - ТМ снята не будет и конвертация ресурсов производится не будет\n    Автоконвертация переводит ресурсы исходя из заказанного количества юнитов - даже если в очередь может стать меньше. Это сделано специально для того, что бы можно было однократно воспользоваться автоконвертацией, а затем просто пополнять очередь новыми юнитами - с уже сконвертированных в правильном отношении ресурсов\n\n\n2015-05-23 15:31:11 40a4.3\n[~] Код\n    Неиспользуемые файлы кода и темплейта постройки ангара и исследований перемещены в подкаталог .unused\n\n\n2015-05-23 10:15:21 40a4.2\n[~] Страница построек/исследований\n    Добавлено форматирование чисел в автоконвертацию\n\n\n2015-05-23 10:08:08 40a4.1\n[~] Страница построек/исследований\n    Увеличено количество информации, выводимой в лог ТМ при автоконвертации\n\n\n2015-05-23 09:55:41 40a4.0 - Автоконвертация Phase 1\n[!] Страница построек/исследований\n    На странице постройки зданий и исследования технологий добавлена возможность постройки с автоматической конвертацией ресурсов (далее просто - \"автоконвертация\")\n    Автоконвертация доступна если для постройки/исследовния не хватает какого-то конкретного типа ресурсов, однако есть излишек других ресурсов, которые можно сконвертировать на ЧР в недостающий ресурс\n    Конвертируются только планетарные ресурсы - металл, кристалл и дейтерий. ТМ автоматически не конвертируется\n    Сразу после конвертации недостающих ресурсов постройка/исследование ставится в очередь\n    Для предотвращения случайных срабатываний при нажатии на кнопку автоконвертации выскакивает дополнительное окно с подтверждением\n    Автоконвертация ресурсов - платная. Стоимость операции составляет утроенную цену одной конвертации на ЧР или 3.000 ТМ, если ручной обмен на ЧР бесплатен\n    Отключить кнопку автоконвертации можно в \"Настройках\", вкладка \"Интерфейс\", чекбокс \"Скрыть кнопку автоконвертации\"\n\n\n2015-05-18 11:25:35 40a3.11\n[~] Responsive Design\n    Очередные изменения для лучшей поддержки мобильных устройств\n\n[~] Чат\n    Теперь при маленьком размере экрана строка ввода сообщений переносится на отдельную строку - что бы было больше места для текста\n    Теперь при маленьком размере экрана текст сообщения переносится на отдельную строчку, а при совсем крохотном - так же на отдельную строчку переносится ник\n    При переносе ника/сообщения на отдельную строчку для лучшей читаемости блоки разделяются линией и перед сообщением делается отступ\n\n[~] Страница построек/исследований\n    RD: На больших экранах блоки покупки/уничтожения теперь выстраиваются в один ряд с информацией о стоимости постройки\n    Теперь если при постройке зданий нет свободных секторов - превьюшки затеняются и выводится соответствующее сообщение\n    При постройке зданий/исследовании технологий теперь показывается уровень, который будет строится/исследоваться\n    При постройке зданий/исследовании технологий кнопка постройки/исследования теперь отключается, если операция невозможна\n\n\n2015-05-18 00:25:13 40a3.10\n[%] Локализация\n    Исправлена en/infos.php\n\n\n2015-05-17 04:55:54 40a3.8\n[~] Заметки\n    Теперь в пределах одной категории важности заметки дополнительно сортируются в порядке убывания по координатам и типу планеты\n\n\n2015-05-17 03:26:45 40a3.7\n[!] Ребрендинг\n    \"Сверхновая\" стала \"СуперНовой\"\n\n[~] RD\n    Улучшена поддержка мобильных устройств\n    Добавлен хак для отключения хромо-андроидовского FontBusting (не очепятка)\n\n\n2015-05-17 02:01:00 40a3.6\n[~] Страница построек/исследований\n    Переработана вёрстка блока информации о юните\n    Убрана кнопка \"Спрятать/Показать дополнительную информацию\"\n    Добавлен патч для форсирования отрисовки блока допинфы для некоторых браузеров\n\n[~] Флот/Отправка флота\n    Миниатюры кораблей уменьшены в размере\n\n[~] Масштабирование\n    Масштабирование теперь работает с %, а не с px\n\n\n2015-05-16 07:25:27 40a3.5\n[%] Страница построек/исследований\n    Еще убраны найденные \"прыжки\"\n\n\n2015-05-16 03:37:27 40a3.3 - Корабли во флоте при отправке\n[!] Флот/Отправка флота\n    Теперь при отправке флота на страницах \"Выбор точки назначения\", \"Выбор задания\" и \"Флот отправлен\" выводятся:\n      - Состав флота в виде картинок с названиями и количеством;\n      - Точка отправления флота;\n      - (если доступно) Точка назначения флота;\n      - (если доступно) Время и срок прибытия в точку отправления и назначения.\n    На странице \"Выбор точки назначения\" при выборе планеты/заметки/боевого союза точка назначения флота меняется соответственно\n\n[+] Флот/Флоты в полёте\n    Добавлена возможность массового отзыва флотов\n\n[+] Флот/САБ\n    При создании/присоединении к САБу теперь видна дополнительная информация о флоте, к которому присоединяется САБ: состав флота, откуда и куда направляется флот, а так же дата и оставшееся время до прибытия/возвращения\n    Теперь при нажатии кнопки \"Боевой союз\" автоматически создается САБ и в него добавляется текущий игрок. При этом ему не отсылается в данном случае лишнее сообщение\n    Название САБА теперь имеет вид \"САБ <ID>\", где <ID> - назначаемый игрой идентификатор\n\n[~] Страница построек/исследований\n    Исправлены брекпоинты в медиа-запросах, что бы не появлялся скроллер при включенной опции \"Вертикальные очереди построек\"\n    По умолчанию - дополнительная информация о юнитах включена. \"Прыжки\" форматированию будут устранены в следующих патчах\n\n[%] Локализация\n    Убран отладочный текст из описания ТЭ\n\n\n2015-05-15 22:40:10 40a3.1 - Responsive design, FTW! Phase 2\n[~] Страница построек/исследований\n    Количество превьюшек теперь динамически меняется в зависимости от разрешения экрана от 3 до 7 в одном ряду\n    Дополнительная информация о юнитах (боевые характеристики кораблей, баланс производства на рудниках, количество колоний/слотов экспедиции у Астрокартографии итд) теперь по умолчанию скрыта. Увидеть её можно нажав на кнопку \"Показать дополнительную информацию\"\n    На больших экранах (там, где вмещается не менее 6 превьюшек в ряд) таблица с дополнительной информацией располагается справа от описания юнитов\n    Убраны скачкообразные изменения размеров блока информации о юните при mouseover курсора мыши на превьюшках\n    Исправлены все замеченные проблемы верстки при разных разрешениях\n\n\n2015-05-15 07:51:28 40a3.0 - Responsive design, FTW! Phase 1\n[!] Responsive design\n    Responsive design (автоматическое изменение верстки в зависимости от размеров экрана) не совместим с функцией \"Масштабирование\". Используйте либо одно, либо другое\n\n[!] Страница построек/исследований\n    Страница переверстана для поддержки responsive design\n\n[!] Мультиэлемент выбора чисел\n    Мультиэлемент переверстан для лучшей поддержки масштабирования и для поддержки Responsive design\n\n[~] Исследования\n    Гравитехнология для исследования теперь требует минимум 6-го уровня Гипертехнологии\n    ЗС теперь не требует напрямую Гипертехнологии для постройки\n    СН теперь не требует напрямую Гипертехнологии для постройки\n\n\n2015-05-15 01:39:07 40a2.4\n[%] Планета\n    Исправлена ошибка отображение кнопки \"Управление\" в глюкобаге\n\n\n2015-05-13 21:12:11 40a2.2\n[+] Админка/Платежи\n    Добавлена ссылка на отправку письма игроку из списка платежа\n\n\n2015-05-07 04:39:51 40a2.1\n[%] Список планет\n    Восстановлена иконка своза ресурсов на неактивной планете\n\n\n2015-05-06 23:57:27 40a2.0 - Масштабирование\n[!] Масштабирование\n    Переработана система масштабирования. Теперь она делается не через JS, а через CSS установкой базового размера шрифта. Это улучшило результат масштабирования\n    Базовый размер шрифта теперь запоминается в куке и в настройках пользователя. При загрузке страницы он берется сначала из куки и только если кука не установлена - из настроек\n\n[+] Навбар\n    Кнопки-картинки в навбаре теперь масштабируются в соответствии с базовым размером шрифта\n\n[+] Список планет\n    Список планет переделан для поддержки масштабирования\n    Под полосу застройки и очередь на планете добавлена подложка с фоном\n    На каждой планете теперь даже при отсутствии очередей выводятся полупрозрачные иконки, при клике на которые можно сразу перейти к строительству зданий, кораблей и обороны\n    Иконка своза ресурсов на луну появляется только при выборе луны\n\n[+] Очередь\n    Очередь переделана для поддержки масштабирования\n\n[+] Планета\n    Исправлен вид шкалы застройки\n\n[+] Планета/Управление\n    Исправлен вид шкалы застройки\n    Картинки Губернаторов теперь масштабируются в зависимости от базового размера шрифта\n    \"Вернутся к обзору\" теперь стало кнопкой\n\n[+] Империя\n    Страница переделана для поддержки масштабирования\n    Размеры ячеек с количеством/уровнем юнитов теперь не наползают одна на другую и не обрезаются\n\n[+] Страница строительства\n    Страница переделана для поддержки масштабирования\n    Корректно масштабируются все элементы в блоке информации, в частности - кнопка \"Построить\", требования в описании юнита, картинка юнита итд\n    Переделан блок превью юнита:\n      - масштабируются поля под иконки (сами иконки пока не масштабируются);\n      - количество ресурсов не закрывает название ресурсов;\n      - все элементы выровнены друг относительно друга и не наползают один на другой;\n      - масштабируется рамка вокруг выбранного элемента.\n\n[+] Подсказки\n    Подсказка переделана для поддержки масштабирования\n\n[+] Настройки\n    Настройки переделаны для поддержки масштабирования\n\n[+] Вселенная\n    Вселенная переделана для поддержки масштабирования\n    Корректно масштабируются изображения планет, лун и обломков\n    Корректно масштабируются в попапах картинки планеты, луны и обломков\n    Корректно масштабируются иконки: в \"Обозначениях\", в заголовке и теле таблицы\n\n\n2015-05-05 23:28:02 40a1.17\n[%] Интерфейс/Масштабирование\n    Исправлена ошибка масштабирования, когда масштаб применялся к стандартному размеру шрифта\n\n\n2015-05-05 23:18:14 40a1.16\n[%] Админка/Обслуживание\n    Исправлена очепятка в запросе\n\n\n2015-05-05 22:56:40 40a1.15\n[~] Навбар\n    Добавлена кнопка \"Норма\" для нормализации шрифта\n    Кнопки увеличения/уменьшения шрифта переехали в строку со временем\n    \"Локальное время\" переименовано во \"Время у игрока\", \"Серверное время\" - во \"Время на сервере\"\n\n\n2015-05-05 22:05:52 40a1.12\n[!] Интерфейс/Масштабирование\n    Добавлена экспериментальная фишка - увеличение и уменьшение шрифта\n\n\n2015-05-05 19:34:40 40a1.11\n[+] Планета\n    Часть функционала \"Управления планетой\" вынесена на основной экран \"Планеты\":\n      - Переименование\n      - Смена типа ядра\n    Ссылка \"Управление\" переделана в кнопку\n\n\n2015-05-05 17:16:02 40a1.10\n[%] Блиц\n    Исправлена ошибка создания игроков\n\n\n2015-05-04 15:08:54 40a1.8\n[%] Авторизация\n    Исправлен выход из отпуска\n\n\n2015-05-04 14:23:40 40a1.7\n[%] Авторизация\n    Исправлены нерабочие баны и отпуски. Нет, это не причина Амнистии - это повод. Сейчас уже всё работает\n\n\n2015-05-04 02:42:15 40a1.6\n[~] Обновление\n    Отключено логгировние служебных запросов апдейтера\n\n[~] Обслуживание\n    Добавлено удаление юнитов без планет\n    Добавлено удаление пустых юнитов\n    Добавлено удаление стандартных записей логов (обсчёт статистики, маинтенанс, апдейт) более чем недельной давности\n\n[~] Статистика\n    Все записи обсчёта статистики теперь проходят под кодом 191 - LOG_INFO_STAT_PROCESS\n\n\n2015-05-04 01:08:42 40a1.5\n[@] Код\n    Отключено логгирование в авторизации\n\n\n2015-05-04 00:58:09 40a1.4\n[#] chat_advanced 5c5\n    (%) Прилепленный чат\n        Исправлена невозможность отлепить прилепленный чат в некоторых браузерах\n\n\n2015-05-04 00:09:02 40a1.2 - Настройки игрока, Фаза 2\n[#] menu_customize 0d2\n    (%) Кнопка Скрыть/Показать меню\n        Исправлены проблемы с позиционированием кнопки\n\n[@] Код - Настройки игрока\n    Настройки игрока полностью переписаны с использованием объекта userOptions\n    Убраны неиспользуемые теперь функции\n      - player_load_option()\n      - player_save_option_array()\n      - player_save_option()\n\n\n2015-05-03 23:03:52 40a1.0 - Настройки игрока, Фаза 1\n[!] Настройки игрока\n    Переписана с нуля работа с настройками игрока\n    Большая часть полей с настройками вынесены в отдельную таблицу player_options - уменьшен размер записи в таблице `users`\n    Настройки теперь кэшируются write-through - уменьшено количество обращений к БД\n    Закрыта возможность сменить имя игрока с использованием зарещенных символов\n\n\n2015-05-03 12:49:23 40a0.25\n[%] Игрок/Создание\n    Исправлена ошибка незаписи системы и планеты игрока в таблицу `users`\n\n[%] Авторизация\n    Исправлено неправильное сообщение при вводе некорректного пароля\n\n\n2015-05-02 20:13:45 40a0.24\n[%] Авторизация\n    Багфиксы по результатам тестирования\n\n\n2015-05-02 18:55:20 40a0.22\n[~] Авторизация\n    При сбросе пароля письмо с новым паролем отправляется в ЛС\n\n\n2015-05-02 18:24:15 40a0.17\n[%] Планета/Управление\n    Исправлена ошибка удаления планеты\n\n\n2015-05-02 17:38:46 40a0.14\n    [%] Авторизация\n    Багфиксы по результатам тестирования\n\n\n2015-05-02 17:23:07 40a0.10\n[%] Авторизация\n    Исправлены странные символы в письмо о сбросе пароля\n\n\n2015-05-02 17:12:54 40a0.9\n[%] Авторизация\n    Исправлена неправильная ссылка на подтверждение пароля\n\n\n2015-05-02 17:07:29 40a0.8\n[%] Авторизация\n    Багфиксы по результатам тестирования\n\n\n2015-05-02 15:40:47 40a0.7\n[%] Обновления\n    Исправлена очепятки в обновлении\n\n\n2015-05-02 15:19:36 40a0.5\n[#] player_premium 3c3\n    (+) Ивенты\n        Поддержка бонусных уровней Премиума по ивентам\n\n[#] unit_captain 3b1\n    (%) Исправлена ошибка дублирования записей Капитанов в таблице units\n\n[!] Админка\n    adm_user_analyze.php - утилита для отслеживания пользователей-ботов\n\n\n2015-05-02 15:11:07 40a0.1 - Авторизация, Фаза 3\n[!] Авторизация\n    В третий раз переписана авторизация\n    Теперь авторизация модульная\n    Убран юзернейм из куки, однако оставлен код для совместимости\n    Больше сообщений о возможных ошибках. Сообщения об ошибках стали более понятными\n\n[@] Код\n    Большое количество изменений для совместимости с новым модулем авторизации\n    Подробнее - см. лог гитхаба\n\n\n2015-04-19 23:46:50 40a0.0 - Авторизация, Фаза 2\n[!] Авторизация\n    Переписана система авторизации - логин отделен от внутриигрового имени\n    Логин, пароль и системный емейл пользователя вынесены в отдельну таблицу `account`\n    Длина кода подтверждения для сброса пароля увеличена до 9 символов - усилена секретность и уменьшена вероятность коллизий\n    При регистрации запрещены символы \", ', \\ в имени аккаунта\n    При смене пароля новый пароль так же сбрасывается в личную почту в сообщении от Администрации\n\n[+] Настройки игрока\n    Отделен логин от игрового имени\n    Добавлены кнопки \"Показать пароль\" ко всем парольным полям\n\n[~] Апдейтер\n    Код апдейтов по 36-ю версию включительно вынесены в старый файл\n\n\n2015-04-17 13:45:23 39d2\n[~] Апдейтер\n    Повышена устойчивость работы движка при смене префикса в конфигурации\n    Повышена устойчивость работы системы логгирования\n\n2015-04-17 07:11:07 39d0 Project \"SuperNova.WS\" Release 39 \"2014 annual joint operation report\"\n[!] Project \"SuperNova.WS\" Release 39 \"2014 annual joint operation report\"\n[!] ВНИМАНИЕ! Требуется версия PHP >= 5.3.2\n[!] Рад представить вам очередной релиз СуперНовы. Перед вами - результат более чем года работы. Фактически, в этом мегапатче объединены целых ПЯТЬ релизов:\n      1. \"Зима-2014\" - системный релиз, где было проведена гигантская работа по упорядочиванию внутренних механизмов работы:\n        - Все юниты были отвязаны в БД от записей планет и пользователей;\n        - Вся работа с юнитами была переписана с нуля;\n        - С нуля была написана подсистема очередей;\n        - Разделены очереди кораблей и обороны;\n        - Полностью переписаны квесты;\n        - Добавлено глубокое сквозное кэширование, что позволило в 2-4 раза увеличить скорость работы движка;\n        - ...и многое, многое другое!\n      2. \"Весна-Лето 2014\" - багфиксы и небольшие оптимизации релиза З-2014;\n      3. \"Лето-Осень 2014\" - \"мобильнутый релиз\". Было сделано множество улучшений и усовершенствования для большего удобства игры с мобильных устройств (насколько это возможно без полной переделки темплейта и всех скинов);\n      4. \"Зима 2014-2015\" - \"бета релиз\". В начале зимы была выпущена бета 39-го релиза, но потом как-то всё закрутилось - новый год, фишки к НГ, ивенты к НГ, просто фишки, просто ивенты - и объем кода, написанного после беты неожиданно сам начал тянуть на отдельный релиз\n      5. \"Март-апрель 2015\" - \"бета релиз\". В середине февраля наконец-то было решено завязать с добавлением фишек и просто пофиксить баги... Очнулся я только в середине апреля. В игре появились звук, опросы, переработан код таймера, была добавлена пачка улучшений, пофикшены баги и переписана авторизация...\n    В общем, тут я решил все-таки выпустить 39й релиз as-is и сделать стабильную ветку\n    Общий объем проделанной работы можно оценить по количеству коммитов в GitHub. Ну или хотя бы по чейнджлогу релиза, приведенному ниже\n\n\n2015-04-17 06:21:50 39b15.23\n[~] Авторизация/Регистрация\n    Добавлена подсказка по вводу емейла\n\n[@] Код\n    Функции, относящиеся к пользователям, вынесены в /includes/functions/sys_user.php\n    Создание пользователя вынесено в отдельную функцию\n\n\n2015-04-17 03:23:08 39b15.22\n[%] Флоты/Ракетные атаки\n    Исправлена выдача отчета \"нет защиты\" при ракетной атаки даже тогда, когда защита есть\n\n\n2015-04-17 02:48:52 39b15.21\n[%] Флоты/Экспедиция\n    Исправлено PHP предупреждение в flt_mission_explore()\n\n\n2015-04-16 08:54:11 39b15.20\n[~] Авторизация/Регистрация\n    Запоминается имя сервера, на котором зарегестрировался игрок\n\n\n2015-04-16 01:16:31 39b15.19\n[~] Админка/Обслуживание\n    Восстановлена работа упаковщика логов ТМ\n\n\n2015-04-16 01:01:08 39b15.17\n[%] Апдейтер\n    Исправлена очепятка в запросе\n\n\n2015-04-15 21:26:51 39b15.16\n[~] Апдейтер\n    Доработана процедура апдейта\n\n\n2015-04-15 19:49:29 39b15.15\n[#] player_award 0c3\n    (+) Блиц-сервер\n        Добавлены медали за 1-2-3 места в 0-м раунде Блица\n        Добавлен памятный знак за участие в 0-м раунде Блица\n\n\n2015-04-15 18:38:25 39b15.13\n[~] Блитц\n    В кнопку Блица добавлено количество зарегестрировавшихся игроков в текущем раунде\n\n\n2015-04-15 15:49:17 39b15.12\n[%] Настройки игрока\n    Исправлено несохранение настроек ЛС\n\n\n2015-04-15 06:08:20 39b15.11\n[~] Блиц-сервер\n    Добавлена индикация текущего режима на кнопку Блица\n    Добавлена индикация цены на страницу регистрации\n\n\n2015-04-15 04:50:08 39b15.10\n[+] Блиц-сервер\n    Добавлена поддержка раундов\n    Добавлена поддержка подачи заявок на участие за ММ и возвращения взноса при отзыве заявки\n\n[%] Админка/Просмотр пользователя\n    Исправлена ошибка при пустой таблице браузеров\n\n\n2015-04-14 20:09:19 39b15.9\n[~] Обзор Вселенной\n    Теперь на уничтоженной планете/луне не всплывает попап\n\n\n2015-04-14 15:24:26 39b15.8\n[~] Чёрный Рынок\n    Каждый тип операции теперь имеет свой уникальный код\n\n\n2015-04-14 15:08:44 39b15.6\n[@] Код\n    Исправлены очепятки в запросах с SN_TIME_NOW\n\n\n2015-04-14 14:57:55 39b15.5\n[%] Авторизация/Регистрация\n    Исправлены нерабочие кнопки \"Показать пароль\"\n\n\n2015-04-14 14:52:27 39b15.4\n[%] Система\n    Код вывода уведомления о наличии плохих запросов внесен в шатдаун-функцию\n\n\n2015-04-14 14:45:40 39b15.2\n[~] Код\n    Добавлена функция sn_version_compare - обёртка над PHP-шной для работы с видом версий СН\n\n\n2015-04-14 02:37:16 39b15.1 - Авторизация, Фаза 1\n[!] Авторизация\n    Дальнейшие изменения в процедуре авторизации\n    При вводе кода подтверждения на сброс пароля теперь удаляется только этот код\n    Теперь при сбросе пароля происходит автологин - не надо самому логиниться с новым паролем\n\n\n2015-04-13 02:57:18 39b15.0 - Авторизация, Фаза 0\n[!] Авторизация\n    Добавлена соль к паролям. Новые пароли теперь будут просолены\n    Теперь при смене пароля игроку не надо логиниться заново - при смене пароля так же изменяется кука\n\n\n2015-04-11 19:52:41 39b14.10\n[~] Статистика\n    Теперь устаревшая статистика удаляется по дням, а не по количеству обсчётов\n    Настраивается количество дней в таблице `config` переменная 'stats_history_days' (по умолчанию - 14 дней)\n\n\n2015-04-11 16:39:43 39b14.9\n[~] Меню\n    Добавлены два новых поля к записям меню:\n      - AUTH_LEVEL - меню видно только игрокам с уровнем доступа не меньше указанного;\n      - DISABLED - если установлено в 1 - меню не рендерится\n    Переработан код для основных пунктов меню без использования $sn_menu_extra\n\n\n2015-04-11 14:36:37 39b14.7\n[~] Чёрный Рынок\n    Добавлена настройка, отвечающая за заполнение ЧР кораблей, если там не осталось ни одного корабля\n    Она находится в таблице `config` переменная 'eco_stockman_fleet_populate' (по умолчанию - включена)\n\n\n2015-04-11 14:22:33 39b14.6\n[#] Модули\n    В модулях $time_now заменено на SN_TIME_NOW\n\n\n2015-04-11 14:17:51 39b14.4\n[%] Обзор Империи\n    Теперь правильно показывает остаток обороны/флотов в очереди\n\n[@] Код\n    Оптимизировано количество вызовов функции sys_o_get_updated()\n    В основном коде $time_now заменено на SN_TIME_NOW\n\n\n2015-04-11 11:46:48 39b14.1\n[@] Код\n    Простенький DB abstraction layer: замена прямых вызовов mysql на враппер\n    Так же убрано использование подстроки mysql в переменных, строках локализации итд\n\n\n2015-04-10 03:57:59 39b13.1\n[%] Авторизация/Логин\n    Исправлена проблема пропадающих элементов на странице логина\n\n\n2015-04-10 02:03:18 39b13.0\n[!] Счетчик посещений\n    Переделан счетчик посещения так, что бы таблица занимала меньше места\n\n\n2015-04-09 19:57:47 39b12.9\n[+] Метаматерия\n    Переработана страница покупки ММ\n    Курсы валют и бонусы оптовой покупки сведены в одну ячейку\n    Игрок теперь может выбрать валюту по-умолчанию\n    Валюта игрока будет использована во всех расчетах вместо базовой валюты сервера, т.е. при расчете стоимости ММ, стоимости пакетов итд\n    На каждом пакете теперь указывается его стоимость в валюте игрока\n\n\n2015-04-08 15:05:09 39b12.7\n[%] Блиц-сервер\n    Исправлена ошибка появления кнопки Блиц-сервера даже при отключенной регистрации\n\n\n2015-04-06 12:43:19 39b12.6\n[~] Блиц-сервер\n    Кнопка Блиц-сервера выровнена по правому краю навбара, что бы было удобнее нажимать на планетарный дропдаун\n\n\n2015-04-05 16:18:07 39b12.5\n[%] Статистика\n    Исправлено пропадание иконок Мира и РО\n\n\n2015-04-05 07:50:45 39b12.4\n[+] Блиц-сервер\n    Добавлена возможность просмотра статистики по результатам блиц-сервера - ссылка \"Посмотреть статистику Блиц-сервера\" на странице Блиц-сервера\n    Показывается статистика только для первых 100 игроков\n\n\n2015-04-05 06:01:27 39b12.3\n[+] Блиц-сервер\n    Добавлен код для награждения победителей\n    Добавлена индикация Альянса игрока в таблице участников\n\n[~] Статистика\n    Настройки страницы статистики (типа статистики, Игроки/Альянсы итд) теперь передаются в строке браузера - теперь можно легко обмениваться ссылками на конкретную страницу статистики\n\n\n2015-04-04 13:32:47 39b12.2\n[~] Блиц-сервер\n    В экспорт/импорт результатов добавлен ID игрока на Блице\n\n\n2015-04-04 10:25:52 39b12.1\n[+] Блиц-сервер\n    Добавлен механизм импорта результатов\n\n\n2015-04-04 10:15:23 39b12.0 - MOAR BLITZ!\n[+] Блиц-сервер\n    Добавлен механизм экспорта результатов\n\n\n2015-04-04 09:13:35 39b11.8\n[+] Блиц-сервер\n    Добавлен режим раскрытия соотвествий аккаунтов на странице регистраций заявок\n\n\n2015-04-03 17:17:10 39b11.7\n[%] Меню\n    У кнопки \"Скрыть/Показать меню\" исправлен неработающий тип \"Обычная\"\n\n\n2015-04-03 15:25:33 39b11.6\n[#] menu_customize 0d1\n    (~) Кнопка Скрыть/Показать меню\n        Кнопка перенесена справа от меню и зафиксирована на своей позиции\n\n[~] Меню\n    Поддержка нового вида кнопки \"Скрыть/Показать меню\"\n\n\n2015-04-02 15:59:32 39b11.3 - Сортировка кораблей\n[+] Флоты/Отправка флотов\n    На странице подбора кораблей во флот добавлена опция сортировки списка кораблей\n    Возможна следующие виды сортировки: \"Стандартная\", \"По названию\", \"По скорости\", \"По количеству\" и \"По ID\" - по возрастанию характеристики\n    Так же возможна инверсная сортировка - по убыванию характеристики\n    Выбор вида и хода сортировки осуществляется дропдауном и галочкой в нижней части таблицы\n    Сделанный выбор сохраняется в настройках пользователя и затем используется при следующих открытиях той же страницы\n    Изменения вида или хода сортировки перегружает страницу\n\n\n2015-04-02 15:59:32 39b11.3\n[%] Опросы\n    Исправлена подсказка при создании опроса (3 дня -> 1 день)\n\n\n2015-03-31 07:22:08 39b11.2\n[~] Опросы\n    Теперь по умолчанию опрос открыт в течении 1 суток\n\n[%] Опросы\n    Исправлена ошибка, не позволявшая полностью использовать возможности strtotime() в дате окончания опроса\n\n\n2015-03-31 06:57:47 39b11.1\n[+] Опросы\n    Добавлена индикация срока опроса\n    Улучшена верстка\n\n\n2015-03-30 23:36:46 39b11.0 - Опросы\n[!] Опросы\n    Добавлена возможность проводить опросы/голосования\n    Опросы прикрепляются к новостям - один опрос на одну новость\n    Можно копировать новости с опросами\n    Можно редактировать новости с опросами, но при этом потеряются текущие результаты опроса\n    Поддерживается произвольное количество ответов, но не менее двух\n    Можно устанавливать срок действия опроса, используя синтаксис PHP-функции strtotime() или просто задавая дату окончания опроса (по серверному времени). По умолчанию опрос действует 3 дня\n    Игрок может выбрать 1 вариант ответа из списка\n\n\n2015-03-24 19:10:37 39b10.2\n[#] chat_advanced 5c2\n    (+) Звук\n        Добавлено звуковое уведомление при получении сообщения в чате\n\n\n2015-03-24 19:03:24 39b10.0 - Звуки Му\n[!] Звуки\n    Добавлена библиотека для поддержки звуков ion.sound © 2014 Денис Инешин лицензия http://ionden.com/a/plugins/licence.html\n    По умолчанию звуки отключены. Для включения нужно на странице \"Настройки\" (вкладка \"Интерфейс\") поставить галочку \"Включить звуки в игре\" и сохранить изменения\n    Работа звуков в устаревших и/или мобильных версиях браузеров НЕ ГАРАНТИРУЕТСЯ!\n\n[+] Чат\n    Добавлен звук при получении нового сообщения в чате\n\n\n2015-03-22 17:50:38 39b9.10\n[%] Админка/Обслуживание\n    Исправлен запрос к таблице messages\n\n\n2015-03-22 16:26:49 39b9.9\n[~] Таймеры\n    Доработан код таймера. Теперь он корректно работает в некоторых часовых поясах, в которых ранее не работал\n\n[@] Код\n    Теперь приобретение Губернатора записывается в БД с собственным кодом и комментарием\n    Расстояние между галактиками вынесено в таблицу config, переменная uni_galaxy_distance (20000 по умолчанию)\n\n\n2015-03-22 09:51:31 39b9.8\n[+] Таймеры\n    Переработан код JS-таймеров\n    JS-таймер опять устойчив к навигации между страницами (кнопки \"вперед-назад\" в браузере, обновление, переход на ту же страницу с чтением её из кэша), т.е. результаты работы таймера будут корректны при навигации\n\n\n2015-03-21 21:47:41 39b9.7\n[~] Апдейтер\n    Переупорядочен код работы с полями dark_matter_total, metamatter_total и immortal\n\n[@] Код\n    Исправлено вычисление полей metamatter_total и dark_matter_total. Теперь от них не отнимаются отрицательные значения\n\n\n2015-03-21 10:54:52 39b9.6\n[~] Блиц-сервер\n    Ссылка на БЦ теперь подчеркнута\n\n\n2015-03-21 10:44:18 39b9.5\n[~] Блиц-сервер\n    На странице приема заявок на Блиц-сервер добавлена ссылка на БЦ\n\n\n2015-03-21 01:22:08 39b9.4\n[~] Блиц-сервер\n    Добавлено удаление ничьих планет перед заполнении Вселенной\n\n\n2015-03-21 01:15:53 39b9.3\n[%] Блиц-сервер\n    Исправлена ошибка заполнения Вселенной\n\n\n2015-03-21 01:06:28 39b9.2\n[+] Блиц-сервер\n    Добавлена генерация логинов и паролей для заявок игроков\n    Добавлено заполнение Вселенной по сгенерированным логинам и паролям\n\n[~] Меню\n    Переделано меню, что бы на месте отключенных пунктов не было пустого места\n[~] Альянсы/Управление\n    Альянс теперь можно передать любому участнику - а не только заместителю главы\n\n[%] Альянсы/Управление\n    Исправлена невозможность включить прием заявок после его отключения\n    Исправлена ошибка попадения в список кандидатов на передачу Альянса игроков не из Альянса\n\n\n2015-03-20 06:27:27 39b9.1\n[+] Блиц-сервер\n    Теперь в режиме Блиц-сервера:\n      - Отключен механизм Альянсов и убраны соответствующие пункты меню;\n      - Отключен поиск по серверу и убран пункт меню;\n      - Отключена регистрация;\n\n    Добавлен режим регистрации заявок на игру на Блиц-сервере:\n      - Включается установкой переменной game_blitz_register в таблице config в 1;\n      - Установка переменной в 2 закрывает регистрацию;\n      - При включении этого режима в самом верху страницы появляется кнопка, ведущая на страницу регистрации.\n    На странице регистрации игрок может:\n      - Посмотреть список зарегестрировавшихся игроков;\n      - Подать регистрацию на игру;\n      - Отозвать свою заявку.\n\n\n2015-03-20 04:34:49 39b9.0 - Блиц-сервер\n[!] Блиц-сервер\n    Добавлены отдельные картинки для экрана логина и бэкграунды для режима Блиц-сервера\n    Добавлен \"Блиц-сервер\" в режим выбора режима работы СН\n\n\n2015-03-18 03:40:56 39b8.5\n[~] Админка/Обслуживание\n    Теперь удаляются все сообщения (кроме личных и альянсовских) старше 4 недель у всех игроков\n    Теперь удаляются все сообщения (кроме личных и альянсовских) любой давности у игроков, неактивных более 4 недель\n\n[%] Статистика\n    Теперь для новых аккаунтов при первом обсчете статистики в изменении места показывается \"*\", а не \"-(новое место)\"\n\n\n2015-03-18 01:57:35 39b8.4\n[+] Обслуживание/Регламент аккаунта\n    Теперь из отпуска выводятся аккаунты, бывшие в отпуске более 5 недель, а не 9, как раньше\n    Теперь удаляются спящие аккаунты, неактивные более 8 недель\n\n[@] Код\n    Подготовка к новому регламенту по Бессмертным\n\n\n2015-03-15 13:02:31 39b8.3\n[%] Заметки/Закладки\n    Можно делать закладки на слот Экспедиции\n\n\n2015-03-14 20:34:26 39b8.2\n[%] Император\n    Исправлена формула вычисления высоты графика\n    Добавлена анимация смены высоты графика\n\n\n2015-03-14 20:06:06 39b8.1\n[%] Император\n    Убран вывод отладочной информации\n\n\n2015-03-14 20:00:15 39b8.0 - График статистики\n[!] Император\n    Теперь на странице показывается статистика изменения основных показателей игрока за прошедшие 2 недели (меньше - если не накоплена нужная статистика)\n\n[+] Статистика\n    Теперь в базе хранится статистика за 2 недели\n\n\n2015-03-11 03:36:23 39b7.2\n[~] Обновление\n    Добавлено metamatter_total = 1 тем, у кого есть статус \"Бессмертный\" и metamatter_total == 0\n\n\n2015-03-11 03:30:45 39b7.1\n[%] Обновление\n    Исправлена ошибка вычисления metamatter_total\n\n\n2015-03-11 03:16:16 39b7.0 - Новый регламент аккаунта\n[!] Обслуживание/Регламент аккаунта\n    Новый регламент распространяется на все аккаунты, без учёта статуса Бессмертного\n    Изменен регламент для аккаунтов, находящихся в отпуске более 8 недель и не находящиеся в блокировке. Теперь такие аккаунты при Обслуживании выводятся из отпуска\n    Изменен регламент для аккаунтов, неактивных более 4 недель (I-шки). Теперь для таких аккаунтов:\n    - Отключается получение сообщений, кроме личных, Альянсовских и административных;\n    - Отключается производство на планетах;\n    - Очищаются все очереди (строительства, исследования, обороны, верфи итд) без компенсации.\n\n\n2015-03-09 22:24:44 39b6.34\n[!] Пакетные операции\n    Поддержка пакетных операций\n\n\n2015-03-09 02:44:56 39b6.33\n[%] Статистика\n    Исправлена очепятка, из-за которой в очках по ресурсам не учитывались ресурсы, находящиеся в очередях\n\n\n2015-03-09 01:58:09 39b6.32\n[%] Альянсы\n    Исправлен прием игрока в Альянс, чей тег или название содержат спецсимволы\n[%] Админка/ТМ\n    Исправлена ошибка начисления ТМ игрокам, чье имя начинается со знака \"-\"\n\n\n2015-03-08 01:54:11 39b6.31\n[#] player_award 0c2\n    (~) Кавалер-2015\n        Изменено изображение памятного знака\n\n\n2015-03-08 01:00:22 39b6.29\n[#] player_award 0c1\n    (+) Кавалер-2015\n        Памятный знак за подарок Даме в ходе ивента \"8 марта 2015 года\"\n\n\n2015-03-06 19:45:00 39b6.27 - Пол и половые принадлежности\n[+] Игрок/Пол\n    Для исключения блокировки корпоративными фаерволлами и прочим, \"sex\" заменен на \"gender\"\n    Добавлена возможность выбрать пол в \"Настройках\"\n\n\n2015-03-03 16:41:59 39b6.26\n[~] Статистика/Интерфейс\n    Теперь ники игроков и названия Альянсов не переносятся на вторую строку\n\n\n2015-03-03 16:33:15 39b6.25\n[+] Игроки/Отпуск\n    Добавлена индикация аккаунтов, находящихся в отпуске. Такие аккаунты отмечаются специальной иконкой в нике и надписью \"В отпуске\" на странице \"Император\"\n\n[~] Флоты/Отправка\n    При подборе кораблей во флот теперь показывается актуальная скорость каждого корабля\n    Добавлены разделители тысяч к количеству кораблей во второй колонке\n\n\n2015-02-22 13:34:21 39b6.22\n[~] Планета/Управление\n    Уточнена надпись - какой пароль нужно ввести для сноса колонии\n\n\n2015-02-22 12:35:22 39b6.21\n[#] chat_advanced 5c1\n    (~) Сообщения\n        Сообщения в чате переверстаны опять на таблицы. Ха-ха-ха\n\n\n2015-02-22 01:02:59 39b6.19\n[#] player_award 0c0\n    (+) Орден Спонсора\n        Добавлены Ордена Спонсора 5-й, 6-й и 7-й степеней - соответстенно \"Алмазный Спонсор\", \"Тёмный Спонсор\" и \"Метаспонсор\"\n        Уменьшены размеры картинок\n\n\n2015-02-21 23:32:46 39b6.16\n[#] chat_advanced 5c0\n    (~) Сообщения\n        Изменена верстка сообщений в чате. Новая вёрстка дает лучше форматированный текст при операциях Copy-Paste\n\n\n2015-02-19 20:50:44 39b6.13\n[#] unit_captain 3b0\n    (~) Стоимость Капитана снижена до 20.000 ТМ\n\n\n2015-02-19 20:47:41 39b6.11\n[~] Планета\n    Добавлено отображение бонусных уровней Губернатора\n\n\n2015-02-16 13:06:07 39b6.9\n[#] chat_advanced 5b6\n    (+) Теперь на замьюченом игроке в попапе видна причина мьюта\n    (%) Исправлена незапись причины мьюта в БД\n\n\n2015-02-15 18:13:15 39b6.6\n[+] Админка/Обслуживание\n    Добавлена чистка общих логов. Из таблицы `logs` при обслуживании удаляются записи, сделанные ранее 1 числа три месяца назад\n\n\n2015-02-15 18:05:25 39b6.5\n[~] Админка/Обслуживание\n    Небольшие изменения запросов для большей устойчивости работы при повторных запусках\n\n\n2015-02-15 17:29:42 39b6.4\n[%] Обновление\n    Исправлены очепятки\n\n\n2015-02-15 17:22:33 39b6.3\n[~] Обновление\n    Добавлен пересчет начального баланса ТМ в таблице log_dark_matter\n\n[%] Админка/Обслуживание\n    Исправлены очепятки в SQL-запросах\n\n\n2015-02-15 16:28:55 39b6.2\n[+] Админка/Обслуживание\n    Добавлена упаковка логов транзакций Тёмной Материи. Записи, сделанные ранее 1 числа три месяца назад, пакуются в одну запись от 1 числа указанного месяца с суммой всех транзакций за период упаковки\n    Добавлена агрегирование статистики онлайна игроков. Записи, сделанные ранее 1 числа три месяца назад, агрегируются в записи с интервалом по 10 минут со средним арифметическим онлайна за указанный интервал\n    Добавлена чистка game_watchlist и stats_hide_player_list от несуществующих пользователей\n\n\n2015-02-12 13:43:58 39b6.1\n[%] Сообщения\n    Исправлена очепятка в темплейте\n\n\n2015-02-12 13:35:56 39b6.0\n[+] Сообщения\n    Теперь при отправке сообщения в ответ на личное сообщения, доступна история переписки. Она показывается под формой создания сообщения\n    В истории показываются в порядке написания сообщения от собеседника игроку и неудаленные сообщения от игрока собеседнику, но не более 20 сообщений\n    Так же добавлена автоматическая замена нескольких подряд префиксов ответов (\"RE:\") на один\n\n\n2015-02-11 13:52:34 39b5.10\n[#] menu_customize 0d0\n    (~) Исправлена несовместимость с пермачатом в модуле chat_advanced\n\n[#] chat_advanced 5b5\n    (~) Ссылка на историю чата вынесена в заголовок\n    (%) Пермачат\n        Исправлена ошибка с неработающим ресайзингом элементов пермачата\n\n\n2015-02-11 13:32:31 39b5.7\n[~] Интерфейс\n    Ссылки, открывающие дополнительные окна, теперь подчеркиваются двойной линией\n\n\n2015-02-09 16:26:14 39b5.6\n[%] Заметки\n    Исправлена ошибка с невозможностью сохранить луну в качестве точки назначения\n    Исправлена ошибка, когда при редактировании заметки точка назначения сбрасывалась в \"планету\"\n\n\n2015-02-09 16:08:42 39b5.5\n[%] Флоты/САБ\n    Исправлена невозможность присоеденить флот к САБу\n\n\n2015-02-09 14:06:35 39b5.4\n[~] Обзор Вселенной\n    Ячейка для кнопки колонизации теперь не включает ячейки действия\n\n\n2015-02-09 13:09:50 39b5.3\n[+] Обзор Вселенной\n    Восстановлена работа настройки \"Время показа подсказок\" в разделе \"Вселенная\" на вкладке \"Интерфейс\"\n    Настройка даёт возможность задать задержку между наведением курсора мыши на элемент в Обзоре Вселенной и появлением попапа\n    По умолчанию задержка выставлена в 500 миллисекунд (0,5 секунды)\n    Значение \"0\" означает \"использовать задержку по умолчанию\". Для фактического отключения задержки можно использовать небольшие значения, например, \"1\"\n    Задержка действует только при наведении курсора - при клике на элементе попап появляется сразу\n\n[-] Настройки/Замер времени\n    Отключено использование window.performance при замере времени - не все браузеры адекватно возвращают соответствующие значения\n\n\n2015-02-01 20:36:32 39b5.1\n[!] Настройки\n    Таймеры в JavaScript теперь не зависят от того, был ли произведен замер или нет - нужные данные вычисляются на клиентской стороне по данным с сервера\n    Фактически, это означает, что все таймеры теперь всегда будут корректно работать - не взирая на правильность/неправильность часовых поясов, \"сбитых\" часов итд\n    Замер времени сохраняется для рендеринга времени на стороне сервера (дата/время сообщений, чат, новости итд)\n\n\n2015-02-01 15:19:11 39b5.0\n[!] Настройки\n    Замеры времени делаются индивидуально для каждого устройства\n    Теперь время не будет сбиваться при переходе с устройства на устройство вне зависимости от разницы настроек во времени/часовых поясах\n    Там, где есть соответствующая поддержка, при замере времени учитываются задержки при передаче данных. Особенно актуально это для мобильных игроков с нестабильным каналом\n\n\n2015-01-31 05:49:49 39b4.4\n[%] Админка/Локализация\n    Восстановлена работа интерфейса локализации в админке\n\n\n2015-01-23 19:45:42 39b4.2\n[%] BBCode\n    Исправлена ошибка с тэгами [s] и [u]\n\n\n2015-01-23 10:06:29 39b4.1\n[%] Обзор Вселенной\n    Исправлена ошибка отсутствия места игрока в попапе\n\n\n2015-01-23 09:38:50 39b4.0 - Переработка Обзора Вселенной\n[!] Обзор Вселенной\n    Страница сильно переработана\n    Полностью переделана работа с попапами:\n      - Изменен принцип позиционирования попапов - теперь они по минимуму закрывают информацию от пользователя, а так же стараются не вылазить за границы окна\n      - Устранены ошибки с позиционированием попапов у большинства мобильных пользователей - в отдельных браузерах ошибка может сохранится из-за некорректной реализации в браузере масштабирования\n      - Теперь попапы открываются не только при наведении мышки, а и по клику. Повторный клик на той же ячейке закрывает попап для удобства мобильных пользователей. Впрочем, \"мышисты\" тоже могут этим пользоваться\n      - Расширены области срабатывания попапов для удобства мобильных пользователей\n      - Передеалн попап игрока: в нём сдублированы все возможности, которые дают иконки. Так что теперь иконки можно отключать для экономии места на экране без потери функциональности\n    Везде, где это имело смысл, ссылки изменены на кнопки для удобства мобильных пользователей\n    Заменены иконки шпионажа и ракетной атаки - теоретически их теперь не должны блокировать рекламорезки на мобильных устройствах\n    На пустой позиции в системе теперь высвечивается большая кнопка \"Колонизировать...\"\n    JS-код переписан с использованием jQuery и большая его часть вынесена в отдельный файл\n    Переверстана страница с активным использованием CSS - размер итоговой страницы уменьшен на несколько десятков % - в зависимости от населенности системы и активности флотов в ней\n    Кнопка \"Перейти\" в выборе Галактики/Системы вынесена вправо от элементов листания Вселенной\n    Исправлена ошибка появления отрицательных цифр в попапе обломков, если на планете не хватает дейтерия для отправки переработчиков\n\n[-] Закладки\n    Удалена таблица Закладок, код и темплейт\n\n[~] Заметки/Закладки\n    При редактировании заметки иконки подтверждения и отмены изменений разнесены в вертикальной плоскости\n[~] Темплейты\n    Все темплейты теперь используют общую иконку для отправки писем из /design/images\n\n\n2015-01-22 11:12:20 39b3.2\n[~] Флоты\n    Убран переход на следующую страницу при отправке флота при выборе координат нажатием кнопки\n\n\n2015-01-22 02:34:51 39b3.1\n[%] Заметки/Закладки\n    Добавлен код для миграции Закладок в Заметки\n\n\n2015-01-22 02:16:22 39b3.0 Объединение Заметок и Закладок\n[!] Заметки/Закладки\n    Теперь в \"Заметках\" можно добавлять координаты во Вселенной и тип объекта (Планета, Луна, Поле обломков)\n    Таблица на второй странице отправки флота теперь берет данные из Заметок, выбирая только записи, у которых все три координаты планеты отличны от нуля. В качестве текста используется заголовок Заметки. Работает сортировка по приоритету\n    Ввиду полной бессмысленности дублирования функционала, \"Закладки\" убраны из игры. Все существуещие в игре \"Закладки\" перенесены в \"Заметки\" вместе с комментариями\n    В Заметке теперь может быть пустым либо заголовок, либо текст - но не оба одновременно\n    На странице редактирования теперь только важность Заметки выделяется цветом, а не вся Заметка, как раньше\n    Теперь Заметку можно сделать прилепленной. Такие Заметки будут отображаться на всех страницах игры под навбаром сразу после Новостей в отдельной таблице. Клик на заголовке переведет на страницу редактирования Заметок\n    Клик на координатах в обычных и прилепленных Заметках откроет страницу \"Вселенная\" в указанной галактике и системе\n\n[~] Флоты\n    На второй странице отправки флота список текущих планет и список Заметок с координатами переделаны в кнопки для удобства мобильных пользователей\n[~] Аффилейты\n    Добавлена прямая ссылка\n[~] Админка/Список игроков\n    Добавлена подсветка всей строки при наведении курсора для облегчения операций с аккаунтами\n\n\n2015-01-17 13:14:53 39b2.0\n[+] Авторизация\n    Данные об user_agent и user_proxy вынесены в отдельную таблицу со справочником\n[+] Админка/Информация об игроке\n    Отформатированы все даты и числа (там, где это имеет смысл)\n\n[~] BBCode\n    Устаревшие HTML-тэги <u> и <s> заменены на <span style>\n    Исправлена ошибка добавления в конец URL закрывающего BBCode при парсинге чистых URL\n\n\n2015-01-12 05:50:10 39b1.6\n[~] Движок\n    Обновление информации о статусе пользователя производится после проверки на отключение сервера - что бы не было блокировок\n\n\n2015-01-12 02:29:58 39b1.2\n[@] ТМ\n    Добавлено и заполнено поле dark_matter_total в таблице `users`. Поле так же изменяется при начислении ТМ внутренними механизмами движка\n\n\n2015-01-09 20:58:29 39b1.1\n[~] Флоты/Менеджер летящих флотов\n    Доработан таймер-сторожок зависания флотов\n\n\n2015-01-08 04:58:24 39b1.0\n[~] Флоты/Менеджер летящих флотов\n    Переписан менеджер летящих флотов для избежания зависаний\n    Интервал обсчёта флотов теперь задается в таблице `config` переменной 'fleet_update_interval'\n    Так же теперь обновление флотов не производится во время отключений сервера\n\n[@] class cache\n    Переписан кэшер для поддержки блокировок на уровне БД\n[@] TimeProbe\n    Отключен запуск флотов\n\n\n2015-01-02 02:49:29 39b0.16\n[*] Поиск\n    Отключён поиск планет. Я вообще с трудом понимаю, зачем он был изначально сделан в игре...\n\n\n2014-12-28 14:25:02 39b0.12\n[@] БД\n    Добавлен индекс в таблицу `unit` для ускорения работы ивента\n\n\n2014-12-28 14:02:49 39b0.11\n[~] Флоты\n    Поддержка моратория на агрессивные миссии\n\n\n2014-12-27 20:16:37 39b0.9\n[~] Флоты/Экспедиция\n    Поддержка дополнительных событий в Экспедиции\n\n\n2014-12-24 19:01:42 39b0.8\n[#] player_premium 3c2\n    (+) Ивенты\n        Поддержка скидок на Премиум по ивентам\n\n[@] Код\n    Поддержка скидок на Премиум по ивентам\n\n\n2014-12-18 13:58:02 39b0.5\n[@] Код: Флоты\n    Вычисление расстояния между вселенными вынесено в отдельную функцию\n\n\n2014-12-13 06:58:13 39b0.4\n[@] Код: Флоты\n    Поддержка ресурсов в составе флота: проверка на перегрузку, на отрицательное количество ресурсов\n\n\n2014-12-12 08:04:30 39b0.2\n[+] Флоты\n    Проверка качества отправляемого флота на предмет наличия орбитальных структур (СС, ТОП, \"Лень\")\n    Поддержка внешней активации транзакции при отправке флота - для пакетной обработки массовой отправки со страницы своза ресурсов\n\n\n2014-12-07 14:53:35 39b0.1\n[%] JS\n    Исправлена ошибка в JS\n\n\n2014-12-07 14:27:52 39b0.0 Project \"SuperNova.WS\" Release 39 Beta\n[!] Project \"SuperNova.WS\" Release 39 Beta \"2014 annual joint operation report\"\n    Рад сообщить, что для тестирования доступна бета-версия 39-го релиза!\n    Перед вами - результат более чем года работы. К сожалению, планируемая цель данного релиза - ввод в строй модуля КК - не достигнута. Однако, этот релиз делается именно для того, что бы КК появился\n    Цель выпуска 39-го релиза именно в текущем состоянии - проверить работу подсистем движка на остальных хостингах. Очень многое было переписано, переделано и написано с нуля для поддержки КК. На своём сервере я отладил работу всех новых изменений и дополнений, однако теперь нужно провести более широкий тест\n    Качество кода - \"good enough\". У меня он работает стабильно несколько месяцев, т.е. вполне можно ставить на свои основные сервера. Примерно через неделю-две я планирую сделать полноценный релиз. Возможно в этот раз - с пропуском Release Candidate, поскольку эта бета, фактически, им является. Однако могут возникнуть определенные НЮАНСЫ при работе на других серверах. Поэтому - бета\n    Обновление документации по игре будет подготовлено к релизу. Пока же можно читать технический чейнджлог\n    Не забываем после обновления чистить папку 'cache'\n    Взять бету можно по ссылке https://github.com/supernova-ws/SuperNova/archive/master.zip\n    За новыми версиями модулей обращаться по личным каналам. Лучше всего - в скайп supernova.ws\n\n\n2014-12-07 14:20:35 39a21.4\n[!] Инсталляция\n    Добавлен специальный режим отключения сервера \"Инсталляция и конфигурация\". В этом режиме игра стартует после инсталляции (логин по умолчанию - admin, пароль - admin). Так же его можно включить в админке или выставив в таблице `config` в записи 'game_disable' значение 4\n    Отличие данного режима отключения от остальных в том, что нём доступны страницы login.php и logout.php\n    Основное назначение режима - настройка сервера после инсталляции движка и аварийное восстановление после сбоев в работе сервера\n\n[+] JS\n    Защита от двойного старта JS-скриптов sn_global.js и sn_timer.js\n    Код замера времени перенесен из хидера в sn_global.js\n\n\n2014-12-01 18:07:24 39a21.3\n[+] Планета/Управление\n    Переверстана страница. Теперь разные типы элементов разделены на группы и упорядочены так, что бы исключить случайное нажатие на мобильных устройствах\n    Тип ядра:\n     - Увеличена высота кнопки\n     - Количество ТМ, нужной для операции, теперь форматируется с группировкой тысяч и кодируется цветом\n    Телепорт:\n     - Увеличена высота кнопки\n     - Количество ТМ, нужной для операции, теперь форматируется с группировкой тысяч и кодируется цветом\n    Губернаторы: вынесены в отдельный блок\n    Перенос столицы:\n      - Сделано подтверждение на перенос столицы;\n      - Увеличена высота кнопки и кнопка теперь отключается, если перенос невозможен\n      - Количество ТМ, нужной для операции, теперь форматируется с группировкой тысяч и кодируется цветом\n[+] Планета\n    Ссылка \"Переработать\" сделана кнопкой. Она всегда показывается, если на планете есть переработчики. При этом, если обломков нет на орбите - кнопка неактивна\n[+] Покупка секторов\n    Более подробный лог траты ТМ\n\n\n2014-12-01 14:17:13 39a21.2\n[+] Наёмники/Чертежи\n    Лог ТМ при найме/покупке Чертежа теперь пишется на языке текущего пользователя. Так же в него пишется стоимость и срок найма\n    В лог ТМ теперь пишется запись об увольнении Наёмника с детальной информацией\n\n\n2014-12-01 13:07:32 39a21.1\n[+] Император\n    Переработана страница \"Император\"\n\n\n2014-11-28 07:36:32 39a21.0\n[!] Метаматерия\n    Изменены скидки за оптовую покупку. Теперь небольшие сумм покупок дают большую скидку, чем раньше\n\n[+] Система\n    Поддержка акций\n\n[%] Флоты/Своз ресурсов\n    Исправлено PHP-предупреждение, если у игрока только одна планета\n[%] Админка\n    Исправлен вылет админки в попытке обсчитать флоты (симптомы - белый экран на любой из страниц админки)\n[%] Регистрация\n    Исправлено PHP-предупреждение в редиректоре регистрации reg.php\n\n[@] Код\n    Определение скоростей (игры/добычи/флота) вынесены в отдельные перекрываемы функции\n\n\n2014-11-23 20:08:50 39a20.18\n[!] Система\n    Переделана работа отключения сайта. Теперь различаются источники блокировки сайта, для каждого из которых выводится своё сообщение:\n      - Блокировка из админки. Игроки в причине блокировки видят то, что введено в настройках сайта;\n      - Блокирование из статистики - своё сообщение;\n      - Блокирование из обновления - своё сообщение;\n      - Блокирования при первой инсталляции до окончания настройки\n    Текущий режим отображается для игроков соответствующим сообщением, автовыбором соответствующего пункта в \"Настройках\" в админке и красным сообщением вверху странице в админке же\n    Администратор может насильно отменить режимы блокировки, устанавливаемые статистикой и обновлением - однако делать это крайне не рекомендуется\n\n[+] Статистика\n    Теперь во время расчёта статистики сайт отключается\n[+] Админка/Статистика\n    Опять переделана процедура расчёта статистики\n    Теперь указывается не интервал расчёта статистики, а \"расписание\", т.е. конкретное время запуска в привязке к текущему времени. Ниже будет подробнее объяснено на примерах\n    Формат расписания изменился и теперь выглядит так:\n      <время запуска>[,<время запуска>...]\n      <время запуска>: [ГГГГ:[ММ:[ДД:[ЧЧ:[ММ:[СС]]]]]]\n    Пустые параметры приравниваются к нулю. Лидирующий ноль укзаывать не обязательно. Т.е. записи: \"0000:00:00:00:30:00\", \"0:30:0\" и \"30:\" - равноценны. Примеры:\n      - \"00:00:27:00\" означает \"запуск в 27 минут каждого часа\", т.е. в 00:27:00, 01:27:00, 02:27:00 итд;\n      - \"04::\" означает \"запуск в 4 утра каждого дня\"\n      - \"01::,17:15\" означает \"запуск в 1 утра каждого дня и в 17 минут 15 секунд каждого часа\", т.е. каждый день в 00:17:15, 01:00:00 (это сработало дополнительное расписание), 01:17:15, затем в 02:17:15, 03:17:15, 04:17:15 итд;\n      - \"1:4:30:00\" означает \"Запуск 1 числа каждого месяца в 04:30 утра\", т.е. 1 января в 04:30:00, 1 февраля в 04:30:00, 1 марта в 04:30:00 итд;\n      - \"2015:1:1:00:00:00\" означает \"Однократное срабатывание 1 января 2015 года ровно в полночь\"\n\n[~] Метаматерия\n    Пересчитано по логам общее количество метаматерии, пришедшее на аккаунт для более точной работы системы очистки старых аккаунтов\n\n[%] Статистика\n    Исправлена ошибка с неправильным указанием времени запуска статистики\n\n\n2014-11-23 14:10:19 39a20.17\n[%] Меню\n    Исправлена ошибка вёрстки при индикации свежей новости в соответствующем пункте\n\n\n2014-11-23 12:30:47 39a20.16\n[%] Флоты/САБ\n    Исправлена ошибка многократного обсчёта САБа\n[%] Меню\n    Исправлена ошибка применения настроек меню при отключенном модуле menu_customize\n\n[@] Темплейты\n    В рендерере темплейтов отключен вывод ошибки когда не существует файла с указанным темплейтом - это нужно для корректной работы условного INCLUDE\n\n\n2014-11-23 10:51:10 39a20.14 - Пункты меню в виде кнопок\n[#] menu_customize 0c1\n  (+) Пункты меню как кнопки\n      Добавлена возможность выводить пункты меню в виде кнопок для большего удобства мобильных пользователей\n      Эта возможность доступна при базовой настройке меню: чекбокс \"Показывать пункты меню в виде кнопок\" в \"Настройках\", вкладка \"Профиль\", раздел \"Настройка меню\"\n  (@) Темплейт\n      Все настройки из общего темплейта перенесены в соответствующий файл темплейта модуля\n\n[#] player_premium 3c1\n    (~) Меню\n        Поддержка пунктов-кнопок\n    (~) Вёрстка\n        Изменена вёрстка для корректной работы страницы при отключенном минификаторе\n\n[+] Пункты меню как кнопки\n    Поддержка новой фичи модуля menu_customize\n\n\n2014-11-22 18:36:54 39a20.10\n[%] Планета\n    Убран давно ненужный вызов jQuery.noConflict()\n\n\n2014-11-22 18:24:09 39a20.9\n[#] menu_customize v0c0\n    (+) Администрация сервера имеет возможность менять/прятать все пункты меню даже без премиум-аккаунта\n    (~) Теперь настройки меню записываются не AJAX-post, а как скрытый элемент формы, что делает настройку меню работоспособной в андроид-приложении и FireFox\n    (~) Кнопка \"Показать/Спрятать все пункты\" сделана именно кнопкой, что должно облегчить её использование мобильным пользователям\n    (~) Кнопка \"Открепить меню\" сделана неперемещаемой\n\n\n2014-11-22 16:40:22 39a20.5\n[~] Админка/Начисление ТМ/ММ\n    Редактирование сообщения в логе\n\n\n2014-11-22 16:34:49 39a20.4\n[+] Админка/Начисление ТМ\n    Восстановлен поиск по ID игрока\n    Убран поиск по планете\n    Теперь при начислении ТМ через админку в комментариях пишется причина начисления и кто произвел начисление\n[+] Админка/Начисление ММ\n    Восстановлен поиск по ID игрока\n    Теперь при начислении ММ через админку в комментариях пишется причина начисления и кто произвел начисление\n\n[~] Меню\n    Переверстано меню, что бы корректно работать без минификатора\n\n\n2014-11-20 17:47:07 39a20.1\n[#] player_premium v3c0\n    (!) Требуется СН 38a20.0\n    (!) Модуль переписан\n        Добавлена возможность продления Премиума и Апгрейда на более высокий уровень\n        В интерфейс страницы добавлена развернутая таблица с ценами на все комбинации уровня премиума и срока действия\n        При имеющемся премиуме дополнительно в таблице выводится информация о базовой стоимости премиума\n        Добавлена защита от двойного срабатывания при обновлении страницы\n        Базовая стоимость премиум-аккаунта уменьшена с 25.000 ТМ до 20.000 ТМ\n        Добавлен +6 премиум\n\n\n2014-11-18 14:17:59 39a19.0 - Настраиваемые менюшки!\n[#] menu_customize v0a0\n    (!) Первая версия\n        Требуется СН 38a19.0\n    (!) Базовая настройка меню\n        Новый раздел \"Настройки меню\" в \"Настройках\"\n        Настраивается вид кнопки \"Спрятать/Показать меню\". Она может быть:\n          - \"прилепленная\", т.е. всегда находится в левом верхнем углу экрана - даже когда страница скроллируется\n          - \"обычная\" - т.е. находится в левом верхнем углу СТРАНИЦЫ и скроллируется как нормальный элемент\n          - \"скрытая\" - т.е. кнопка не показывается. В этом случае недоступна кнопка \"Прикрепить/Открепить меню\". Так же при выборе этого варианта сбрасывается флаг открепления меню\n        Настраивается поведение кнопки \"Спрятать/Показать меню\":\n          - можно настроить скрытие меню при наведении курсора на кнопку \"Спрятать меню\"\n          - можно настроить показ меню при наведении курсора на кнопку \"Показать меню\"\n        Настраивается вид и поведение откреплённого меню:\n          - можно настроить скрытие откреплённого меню при выводе курсора за его пределы\n          - можно настроить показ откреплённого меню поверх всех элементов\n    (!) Расширенная настройка меню\n        Для игроков с премиум-аккаунтом доступна пользовательская сортировка пунктов меню и скрытие отдельных пунктов (с определенными ограничениями в зависимости от уровня премиума, см. ниже)\n        Режим расширенной настройки меню включается при нажатии кнопки \"Настроить пункты меню\". При этом все остальные настройки становятся недоступными\n        У элементов, которые можно менять местами, слева появляется значок в виде двойной стрелки вверх-вниз. Такой пункт можно схватить курсором мышки и перетащить на новое место\n        У элементов, которые можно скрыть, справа появляется значок глаза. Клик на нём меняет видимость пункта меню. Белый глаз - пункт меню будет виден. Черный перечеркнутый глаз - пункт меню будет скрыт\n        После окончания настройки нужно нажать кнопку \"Сохранить настройки пунктов\". Настройки пунктов будут сохранены, а страница перегружена. При этом все остальные изменения в настройках сохранены НЕ БУДУТ\n        Так же нажатием кнопки \"Сбросить настройки пунктов меню\" можно вернуть меню первоначальный вид\n        Если скрыты какие-то пункты, то в самом низу меню появляется дополнительный пункт \"Показать скрытые\", который покажет скрытые пункты меню\n        Возможности по настройке меню зависят от уровня премиум-аккаунта игрока:\n          - нет премиум-аккаунта: доступны только базовые настройки\n          - Премиум 1-го уровня: игрок может менять местами пункты меню, кроме системных (название и логотип сервера, пункты \"Как играть\", \"Настройки\", \"Выход\" и логотип движка). Игрок не может скрывать пункты меню\n          - Премиум 2-го уровня: игрок может менять местами все пункты меню. Игрок может скрывать пункты меню, кроме системных\n          - Премиум 3-го уровня: игрок может менять местами и скрывать любые пункты меню\n\n[~] Интерфейс\n    Теперь текст кнопок и вводимые данные в элементах ввода имеют жирный шрифт\n\n[@] Код\n    Добавлен функционал чтения/записи настроек пользователя в отдельную таблицу\n\n\n2014-11-11 23:36:15 39a18.14\n[-] Меню\n    Временно отключен функционал показывания/скрытия меню по событиям mouseenter и mouseleave\n\n\n2014-11-11 23:19:28 39a18.13\n[~] Чат\n    Скруглены углы в попапах и в админском элементе ввода сообщений\n    Уменьшены размеры кнопок со смайликами, а так же уменьшен размер соответствующего попапа\n\n[-] Смайлики\n    Добавлены новый смайлик:\n      :accordion:\n    Убраны смайлики из-за их больших размеров, которые сильно портят форматирование\n      :tratata: :maniac: :bayan:\n\n\n2014-11-11 22:51:05 39a18.12\n[+] Смайлики\n    Добавлены новые смайлики:\n      :ban: :bayan: :censored: :contract: :facepalm: :help: :hmm: :maniac: :panic: :poke: :pray: :whistle:\n    Заменены лучшими версиями (в основном - без подкладки белого \"креста\") смайлики:\n      :clap: :coffee: :nea: :popcorn: :rose: :quote: :shout: :sorry: :spiteful: :ups:\n\n\n2014-11-10 14:52:37 39a18.11\n[%] Интерфейс\n    Исправленна ошибка появления кнопок chat_advanced при открытии открепленного главного меню\n\n[+] jQuery\n    Добавлены виджеты Droppable и Sortable\n\n\n2014-11-10 14:04:38 39a18.9\n[#] chat_advanced v5b3\n    (~) Интерфейс\n        Для мобильных клиентов отключено обработка события mouseleave для меню - в них она вызывала тормоза\n\n[@] Темплейты\n    {-path_prefix-} заменен на {D_SN_ROOT_VIRTUAL}\n    Добавлена глобальная переменная SN_GOOGLE - в темплейт и JS\n\n\n2014-11-10 13:48:39 39a18.7\n[!] Империя\n    Переверстана страница \"Империя\" с активным использованием CSS и jQuery\n    При идентичном виде и идентичной функциональности, на тестовом примере (15 планет при средней застройке) выигрышь в размере составил порядка 45% (~41 кб) при уже включенном минификаторе!\n    При большем количестве объектов или более плотной застройке, выигрышь может быть еще больше\n\n\n2014-11-10 11:05:55 39a18.6\n[#] chat_advanced v5b2\n    (~) Интерфейс\n        Панель элементов переверстана на чистом CSS без участия JS для расчёта размера элемента ввода - размер строки для сообщения теперь меняется динамически при ресайзе окна браузера\n        Вывод сообщений переписан-таки на DIV-ах. В результате опять уменьшился объем передаваемых данных\n\n\n2014-11-10 07:04:48 39a18.4\n[%] Меню\n    Исправлена ошибка с нескроллируемым меню\n\n\n2014-11-10 05:03:35 39a18.3\n[#] misc_radio v2c2\n    (@) JS и CSS\n        Файлы JS и CSS модуля маркируются датой и временем последнего обновления - для форсирования обновления браузерами при изменении этих файлов\n\n[#] chat_advanced v5b1\n    (@) JS и CSS\n        Файлы JS и CSS модуля маркируются датой и временем последнего обновления - для форсирования обновления браузерами при изменении этих файлов\n\n\n2014-11-10 04:55:03 39a18.0 - Крадущаяся мышка, затаившееся меню\n[!] Меню\n    Появилась возможность спрятать меню разово или однократно - до обновления страницы или перехода на другую страницу\n      - Возможность спрятать меню однократно бывает полезна, например, в окне чата, что бы увеличить его площадь или в окне \"Империя\", что бы больше информации влезло на экран\n      - Для того, что бы однократно спрятать меню нужно навестись мышкой или тапнуть по кнопке \"Спрятать меню\" в левом верхнем углу экрана. При этом кнопка изменится на \"Показать меню\"\n      - Что бы вернуть меню достаточно навестись мышкой или тапнуть по кнопке \"Показать меню\"\n    Так же можно спрятать меню на постоянной основе тем самым, увеличивать полезную площадь страницы\n    Для того, что бы постоянно спрятать меню, нужно нажать кнопку \"Открепить меню\" в самом верху меню. В открепленном режиме меню имеются следующие особенности:\n      - Статус открепленного меню запоминается в куках устройства, т.е. для каждого устройства открепление меню настраивается отдельно\n      - При каждом следующем открытии страницы меню будет сразу в спрятанном состоянии (см. выше)\n      - Кнопка \"Открепить меню\" меняется на кнопку \"Закрепить меню\"\n      - Что бы вернуть стандартное поведение меню достаточно нажать на кнопку \"Закрепить меню\"\n      - Что бы воспользоваться, нужно навестись мышкой или тапнуть на кнопке \"Показать меню\". При этом при выходе курсора мышки за пределы меню оно автоматически скрывается\n    Особенно полезна возможность скрытия меню будет для устройств с маленькими экранами - телефонов и мелкоразмерных планшетов\n    Однако она так же может оказаться полезной и для пользователей обычных компьютеров, благо из-за функционала кнопки \"Показать меню\" привычки в работе с меню практически не нужно менять\n\n[@] JS и CSS\n    Все файлы JS и CSS в основном коде маркируются датой и временем последнего обновления - для форсирования обновления браузерами при изменении этих файлов\n    Информация из global.css, относящяяся к темплейту OpenGame перенесена в CSS темплейта\n\n\n2014-11-09 22:44:59 39a17.11\n[#] payment_xsolla v1a0\n    (!) Стартовый релиз для СН 35a17.0\n        Полностью реализован протокол \"Shopping Cart 3.0 Xsolla\" - команды 'check', 'pay' и 'cancel'\n        Поддержка режима тестирования\n        Конфигурация отдельным файлом config.php в каталоге модуля. Если конфигурация недоступна - модуль отключается\n        Поддержка выбора способа платежа xSolla\n        Генерик-плательщик xSolla\n        Хочу отметить часть методов платежа, добавленных к уже существующим:\n          - мобильные платежи: через SMS, со счета мобильного, сервия ZONG от PayPal, со счета Киевстар\n          - платежные системы: PayPal, EasyPay\n          - банковские переводы: Приват24, Сбербанк Онлайн, Банк24 Национальный кредит\n          - терминалы: EasyPay, Ibox, Терминалы Украины, Терминалы России\n          - кредитные карты American Express, JCB, UnionPay\n        Список далеко не полон - на сайте xSolla можно выбрать десятки других способов оплаты\n\n\n[#] chat_advanced v5b0\n    (!) ЭКСПЕРИМЕНТАЛЬНАЯ ФИШКА\n        Добавлен код для устранения проблем с потерей фокуса строки ввода под некоторыми браузерами (в частности - IE 11, возможно поможет и на некоторых мобильных устройствах)\n        Фишка реализована грязным хаком, поэтому если возникнут проблемы с работой чата - необходимо срочно об этом сообщить\n    (~) Интерфейс\n        Смайлики теперь центрированы в своих кнопках и по вертикали\n        Уменьшен размер кнопок смайликов и попапа со смайликами\n\n[#] payment_robokassa v3c3\n    (+) Добавлен generic-метод RoboKassa\n\n\n2014-11-09 08:04:30 39a17.5\n[#] chat_advanced v5a1\n    (~) Интерфейс\n        Подогнан размер поля для ввода текста\n        Раздвинуты элементы панели ввода: кнопка выбора цвета, кнопка смайла итд\n        Небольшой визуальный тюнинг\n\n\n2014-11-09 05:57:11 39a17.3 - Больше красоты в чат!\n[#] chat_advanced v5a0\n    (!) Администрирование\n        Теперь можно забанить игрока из чата без РО. Для этого сразу после срока бана надо добавить восклицательный знак. Например, так:\n          /ban id 10 7d! Бан без РО\n        Переписано меню бана и мьюта - сроки бана и мьюта стали кнопками\n        Во всплывающее меню для бана/мьюта добавлено поле для ввода причины бана/мьюта. По умолчанию при мьюте поле пустое, а при бане заполнено стандартной причиной \"Заблокирован из чата\"\n        Во всплывающем меню для мьюта добавлен чекбокс \"Забанить без РО\" с соответствующим функционалом. По умолчанию галочка включена\n        И поле причины, и чекбокс бана без РО при открытии меню выставляются в значение по умолчанию. Это сделано специально для уменьшения вероятности ошибиться\n    (!) Интерфейс\n        Изменено позиционирование попапов и подсказок с тем, что бы они не перекрывали вызывающий их элемент\n        Так же немного изменены сами попапы для лучшей читаемости\n        Смайлик, открывающий попап со смайликами, теперь сам стал кнопкой\n        Цвет фона в поле ввода сообщения изменен на черный\n        Выпадающий список с выбором цвета заменен на кнопку, при нажатии на которую выскакивает попап с вариантами выбора цвета\n        Выбранный цвет текста сразу же отражается в поле ввода сообщения, давая возможность увидеть, как будет выглядеть сообщение в игре\n    (%) Смайлики\n        Исправлена ошибка, когда на кнопке смайлика её часть оставалась неактивной и некликабельной\n        Исправлена ошибка неправильных кодов для смайликов \":)\" и \":(\"\n    (%) Администрирование\n        Исправлена неработа некоторых диапазонов продолжительности мьюта/бана - в частности, \"y\" и \"w\"\n\n\n2014-11-09 01:54:11 39a17.0\n[!] Платежи\n    Подготовка к подключению модуля xSolla\n    Платежи теперь соблюдают порядок, назначенный им в module\n    Заменены почти все картинки платежей на более качественные\n    Если методов оплаты более 6 - остальные сворачиваются и прячутся с возможностью в дальнейшем развернуть\n    Добавлено 17 новых методов платежей и картинок к ним\n\n\n2014-11-07 22:14:27 39a16.15\n[#] chat_advanced v4b4\n    (~) Смайлики\n        Уменьшен размер выдачи: попап переделан на jQuery и вынесены стили в CSS, а так же убран лишний внутренний элемент\n        Улучшено позиционирование попапа, а сам попап стал выше и шире для удобства мобильных пользователей\n        Иконки заменены кнопками - для удобства мобильных пользователей\n        Клик в попапе вне иконки закрывает попап - для удобства мобильных пользователей\n    (~) Администрирование\n        Добавлено больше сроков для команд мюьта и бана в меню администрирования\n        Код меню администрирования теперь не рендерится для обычных игроков\n    (%) Исправлена странная ошибка с &amp; не транслирующимся в символ амперсанда\n\n\n2014-11-07 07:18:23 39a16.11\n[#] chat_advanced v4b2\n    (%) История\n        Исправлен внешний вид Истории чата\n\n\n2014-11-07 07:12:28 39a16.9\n[~] Рендерер ников\n    Изменен порядок иконок в нике\n\n\n2014-11-07 05:15:30 39a16.8\n[+] Чат\n    Добавлена поддержка нового рендерера ников\n    Теперь если по какой-либо причине отключается модуль chat_advanced, то в чате не видны личные и информационные сообщения\n\n\n2014-11-07 04:56:30 39a16.7\n[#] chat_advanced v4b1\n    (+) Список сообщений\n        Еще уменьшена выдача списка сообщений\n\n\n2014-11-07 02:02:18 39a16.4 - chat_advanced v4\n[#] chat_advanced v4b0\n    (!) Требуется СН 39a16.0+\n    (!) Функционал списка сообщений и онлайн-листа переписан на CSS и jQuery\n    (!) Администрирование\n        Полностью переписаны функции администрирования\n        На кнопках бана и мьюта добавлен попап с выбором сороков бана\n        На кнопка аньмюта при наведении курсора появляется соответствующая подсказка - что позволяет её отличить от кнопки мьюта\n        При операциях бана, мьюта и анмьюта чётко указывается, к какому пользователю будет применена команда. Это позволит исключить случайные промахи при обновлении онлайн-листа\n        ID пользователя перенесен из тултипа в онлайн-лист - так что его теперь легко увидеть\n        При бане из чата в бан-листе проставляется соответствующая причина\n        Иконки бана и мьюта теперь не показываются на аккаунты, которые выше игрока по иерархии\n    (!) Список сообщений\n        Переверстан на таблицах. Сами ебитесь со своими div-ами!\n        Очень сильно оптимизирован вывод сообщений по размеру\n        Теперь вторая и последующие строки многострочных сообщений выравнены по первой строке, а не переносятся на следующую\n        Корректно выравнены иконки в нике относительно надписей\n        Благодаря новому парсеру, корректно выводятся URL-ы, стоящие сразу за символами \")\", \"]\" и \"}\"\n    (!) Список онлайна\n        Очень сильно оптимизирован вывод списка по размеру - даже без минимайзера на каждой строке выигрышь составляет более 0,5 кб!\n        При наведении на статус мьюта в списке онлайна сразу появляется подсказка с именем пользователя и сроком мьюта\n    (%) Чат корректно работает с никами, содержащими символы \"'\", \" \", \"\\\", \"/\"\n    (@) Добавлена поддержка компактизированных ников - соответственно в БД уменьшен размер таблицы с сообщениями\n\n[#] player_award v0b0\n    (!) Требуется СН 39a16.0+\n    (-) Убрана неработающая ссылка в админском меню\n    (@) Поддержка новой версии движка\n\n[#] player_race v2d3\n    (!) Требуется СН 39a16.0+\n    (@) Поддержка новой версии движка\n\n[!] Рендерер ников\n    Полностью переписан рендерер ников\n    Добавлена поддержка компактизированных скин-независимых ников\n    Расширена возможность отключать части ника\n    Иконки теперь имеют фиксированные положения, не зависимые от порядка загрузки модулей\n\n[+] BBCode\n    Теперь парсер понимает URL-ы, стоящие сразу за символами \")\", \"]\" и \"}\"\n\n\n2014-11-06 12:00:27 39a15.5\n[~] Новости\n    Теперь статус новости (например, \"СВЕЖАЯ\") пишется перед датой, а не перед текстом новости\n\n[%] Настройки\n    Исправлена невозможность снять галочку \"Задать вручную разницу во времени\"\n[%] Страница покупки ММ\n    Исправлены очепятки\n\n\n2014-11-05 00:48:58 39a15.4\n[+] Настройки\n    На вкладке \"Профиль\" добавлена возможность вручную выставить разницу между серверным и клиентским временем. Для этого нужно выставить галочку \"Задать вручную разницу во времени\", ввести разницу во времени в секундах и сохранить изменения\n\n[%] Платежи\n    Исправлены перепутанные иконки МТС и Мегафона\n\n\n2014-10-25 03:58:25 39a15.3\n[+] Платежи\n    Добавлены картинки для большинства поддерживаемых методов платежей\n    Выбор фиксированного пакета при покупке ММ теперь сразу переводит на следующую страницу\n    Добавлена индикация стоимости выбранного количества ММ в валюте сервера на всех этапах платежа\n    Переупорядочены доступные методы платежей - от наиболее используемых к наименее используемым\n    Немного переформатирована страница платежей\n    Исправлено вычисление рассчётной стоимости ММ в рублях\n\n\n2014-10-25 01:36:52 39a15.0 - Немного любви для Администраторов\n[!] Админка/Информация об игроке\n    Добавлена базовая страница с информацией об игроке. Она доступна только Администраторам и выше. Перейти на неё можно кликнув по ИД или нику игрока на странице \"Список игроков\"\n    В настоящий момент страница является чуть облагороженным дампом соответствующей записи в таблице users без возможности редактирования\n\n[~] Админка/Список игроков\n    Добавлена колонка \"Активен\", показывающее прошедшее время с момента прошлой активности игрока\n    При наличии модуля платежей появляется колонка с общим количеством купленной игроком ММ\n    Убрана колонка \"Е-Мейл\" - эту и другую иноформацию об игроке теперь можно посмотреть на отдельной странице\n    В колонке \"Рефералы\" подколонки \"Игроки\" и \"ТМ\" выравнены по правому краю\n    Добавлены разделители тысяч в количество ТМ, заработанной рефералами\n    Колонки переупорядочены для большего удобства\n    Уменьшен размер страницы примерно на 40-60%%\n\n\n2014-10-24 17:37:31 39a14.9\n[%] Платежи\n    Исправлена ошибка при выборе метода только с одним провайдером\n\n\n2014-10-23 18:48:40 39a14.8\n[%] Реферральная программа\n    Исправлена ошибка, при которой баннер не показывался без регистрации\n\n\n2014-10-23 18:17:53 39a14.7\n[~] Реферральная программа\n    Теперь в простых ссылках указывается не УРЛ, а имя сервера\n\n\n2014-10-23 17:25:18 39a14.6\n[!] Меню\n    Новые авторские иконки: уменьшенного размера, оптимизированные, оригинальные\n\n[~] Платежи\n    Банковские карты перенесены выше электронных платежей\n\n\n2014-10-23 16:40:50 39a14.4\n[~] Реферральная программа\n    Изменены УРЛы на действительные (с reg.php на login.php)\n[~] Сообщения\n    \"Написать сообщение\" теперь стало отдельной кнопкой\n\n[%] Вселенная\n    Исправлена ошибка отображения информации об игроках с определенными никами\n[%] Сообщения\n    Исправлена ошибка невозможности отправки сообщений игроками с определенными никами\n\n2014-10-23 07:29:31 39a14.3\n[#] payment_robokassa v3c0\n    Требуется СН 39a14.1+\n    Модуль переписан под новую систему платежей\n\n[#] payment_webmoney v3c0\n    Требуется СН 39a14.1+\n    Модуль переписан под новую систему платежей\n\n[!] Платежи\n    Полностью переделан интерфейс системы платежей\n\n\n2014-10-23 00:13:50 39a14.0 - Багфиксы и патчи\n[~] Интерфейс/Строительство\n    Теперь здание можно удалить даже если требования к постройке не удовлетворены. Т.е. теперь здания можно удалять в любом порядке и не нужны соответствующие Планы\n[~] Интерфейс\n    К элементам textarea не применяются теперь стили jQuery - применение стиля в Хроме делало текст невидимым\n\n[%] Партнерская программа\n    Исправлена регистрация по партнерским ссылкам - теперь правильно регистрируются привлеченные игроки\n[%] Интерфейс/Строительство\n    Убрана надпись NaN/NaN в конце требования к юниту, когда требованием является определенный Родной Мир\n    Исправлена ошибка, позволяющее строить на луне/планете запрещенные к постройке здания\n[%] Интерфейс/Император\n    Добавлен пробел между \"У вас\" и количеством сообщений\n[%] Альянсы\n    Исправлена ошибка отправки многострочных сообщений\n\n[@] Код\n    Исправлено предупреждение в /includes/classes/supernova.php line 125\n\n\n2014-10-22 21:26:13 39a13.7\n[#] adm_user_stats\n    Блокировка по расчету недельных данных уменьшена до 1 недели - хотя данные и не совсем адекватные, однако лучше видеть не совсем адекватные данные, чем никаких\n    Добавлен рассчет % для активных (активность < 1 дня) и спящих (активность < 1 недели) пользователей\n\n\n2014-09-09 01:47:22 39a13.2\n[!] Интерфейс/Покупка юнитов\n    Страница постройки кораблей и обороны:\n      1. В списке юнитов вместо остатка при постройке 1 юнита показывается цена постройки 1 юнита: красным - если не хватает ресурса, желтым - если хватает ресурса на 1 юнит, зеленым - если после постройки юнита еще остаются ресурсы\n      2. В описании в таблице стоимости юнита цена и остаток ресурсов теперь меняются динамически с учетом количества выбранных для постройки юнитов\n      3. В описании юнитов под таблицей стоимости теперь показывается максимальное количество юнитов, которое можно построить с имеющимися ресурсами\n      4. Теперь при вводе корректного количества юнитов и нажатии кнопки \"Enter\" юниты ставятся в очередь\n    При входе на страницу первый элемент для отображения деталей выбирается по порядку отображения, а не по ID. Например, на Верфи теперь отображается Легкий Истребитель, а не Супертранспорт, как раньше\n[!] Мультиэлемент ввода чисел\n    Полностью переписан мультиэлемент:\n      1. Максимальное использование jQuery\n      2. Убраны устаревшие функции bind(), live() и delegate()\n      3. Уменьшено количество обработчиков\n      4. Ускорена работа мультиэлемента\n      5. Использование спецтега <ainput> и вставка мультиэлемента на его место методами jQuery\n      6. Теперь при входе в ячейку если значение в ней отлична от нуля, то значение выбирается (как в операционных системах)\n    Исправлена ошибка, когда после нажатия курсором на кнопку \"+\" или \"-\", а затем отпускание кнопки мыши вне соответствующего элемента не отключал действие кнопки\n    Новый мультиэлемент работает на страницах: постройки флота и обороны; набора кораблей во флот; черный рынок - покупка и продажа кораблей\n\n\n2014-09-07 06:14:51 39a13.1\n[%] Обзор планеты\n    Исправлена ошибка с неработающими таймерами\n\n\n2014-09-07 05:29:01 39a13.0 - Мобильнутый интерфейс\n[!] Интерфейс\n    Все строки ввода данных, чекбоксы и кнопки теперь используют jQueryUI в тех браузерах, в которых он работает\n    Переработано множество страниц для совместимости с новым видом интерфейса, сделано огромное количество мелких правок - так что даже не буду пытаться их все перечислить\n\n[~] Интерфейс/Покупка юнитов\n    Если к юниту нет требований - требования не показываются вообще\n    Уменьшен на 1 размер шрифта требований\n    На странице технологий отображается \"Время исследования\", а не \"Время строительства\"\n    Теперь на странице постройки флота/обороны кнопка \"Построить\" блокируется, если не выбрано количество юнитов\n[~] Симулятор боя\n    Переверстан интерфейс симулятора\n\n[%] Админка\n    Исправлена ошибка в SQL-запросе, которая не давала работать спискам планет\n\n\n\n2014-09-06 23:11:56 39a12.3\n[%] JS\n    Откат на jQueryUI 1.8.7 - в новой версии какие-то несовместимости с кодом СН, дающие СТРАШНЫЕ баги на странице логина\n    На всякий случай добавлены полные исходники работающей версии библиотеки\n[%] Мультиэлемент ввода чисел\n    Исправлена ошибка \"NaN\" при вводе первого нечислового символа\n    Установлена фиксированная ширина кнопок. Это сделало вид элементов аккуратнее. Ну, и заодно - исправило некрасивость на странице отправки флотов\n\n\n2014-09-06 21:54:21 39a12.1\n[!] Мультиэлемент ввода чисел\n    Элемент переделан под jQueryUI.button(). Выглядит получше и более дружественнен к мобильным пользователям (читай - больше по размеру)\n    Переверстан под таблицы из-за странного поведения float div в некоторых сценариях. Пока ребята из Вилларибо верстают сайт дивами....\n    Исправлен баг, когда нажатие на кнопки \"+\" или \"-\" при отключенном мультиэлементе меняли числовое значение\n\n[+] Интерфейс/Покупка юнитов\n    Переверстана страница. Теперь она более дружелюбна к мобильным пользователям:\n      1. Уменьшено количество юнитов в ряду до 4-х\n      2. Переверстана панель детальной информации о юните\n      3. Благодаря пп. 1 и 2 удалось на 20% уменьшить ширину страницы - теперь она практически не отличается по ширине от навбара и гораздо удобней для просмотра на узких экранах\n      4. Кнопка постройки юнита сделана через jQueryUI - стала толще и красивее. Кнопка \"Удалить\" осталась такой же мелкой - для исключения случайных промахах на мелких экранах\n    Еще допилена таблица бонусов. Теперь если количество бонуса с прошлого уровня не изменилось - общее число бонуса подсвечивается желтым, а не зеленым. Таким образом, быстрый взгляд на таблицу дает полное представление об изменениях бонуса по уровням: зеленый цвет - положительные изменения, красный цвет - отрицательные, желтый цвет - нет изменений\n    Пример:\n      1. Возьмем опять же Астрокартографию 2-го уровня\n      2. Раньше в третьей строке таблицы бонусов (Ур 3) количество экспедиций (1) подсвечивало зеленым\n      3. Теперь в той же строке количество экспедиций (1) подсвечивается желтым - поскольку новых экспедиций на третьем уровне Астрокартографии не появится\n      4. Опять же - проще посмотреть в интерфейсе, чем объяснять\n\n[@] JS\n    jQueryUI обновлен до самой свежей версии v1.11.1 и в него включены все виджеты\n\n\n2014-09-06 17:58:47 39a12.0\n[+] Интерфейс/Покупка юнитов\n    Теперь в описании юнита показывается не кумулятивная разница бонусов с текущим уровнем, а инкрементальная. Так гораздо лучше виден эффект перехода от уровня к уровню юнита, что позволяет лучше планировать своё развитие.\n    Пример:\n      1. Пусть есть Астрокартография 2-го уровня\n      2. Раньше четвертая строка таблицы бонусов (Ур 4) показывала разницу в +2 колонии. Это была кумулятивная разницу с текущим 2-м уровнем. Т.е. +1 колония за 3 уровень (значение в строке Ур 3) и +1 колония за четвертый уровень - итого +2 колонии\n      3. Теперь четвертая строка будет показывать разницу в +1 колонию. Т.е. разницу между 3-м и 4-м уровнем Астрокартографии, которая и есть +1 колония. Значение в третьей строке (Ур 3) останется по-прежнему +1\n      4. На самом деле - изменение выглядит в интерфейсе горзадо проще и интуитивнее, чем его объяснение\n    Теперь на странице покупки юнитов показываются требования для покупки, а так же их выполнение\n    Теперь ссылка на покупку юнита изменяется в контексте страницы: здания и боевые юниты \"строятся\", технологии - \"исследуются\", Наемники - \"нанимаются\"\n\n[~] Документация\n    В пример конфигурационного файла добавлено уведомление о необходимости использования разных префиксов для нескольких копий СН на одном сервере\n\n[%] Интерфейс/Строительство\n    Исправлена ошибка, когда уровень/количество юнита в описании под изображением \"замерзал\" и не обнулялся при выборе юнита с 0 уровнями/количеством\n\n\n2014-08-26 12:57:47 39a11.9\n[#] menu_applications_button 1с0\n    Иконки-ссылки для загрузки приложений под Android, Windows 8.1, Windows Phone 8\n    Иконки располагаются в каталоге модуля\n\n\n2014-08-25 03:06:19 39a11.6\n[@] Код\n    Поставлены заглушки для будущей поддержки GeoIP\n    Теперь модули могут добавлять пункты в админское меню\n\n\n2014-08-24 20:56:18 39a11.5\n[#] chat_advanced 3a4 Смайлики\n    Исправлен пустой список смайликов\n\n\n2014-08-24 20:06:59 39a11.2\n[#] chat_advanced 3a3\n    В чате теперь доступен расширенный функционал BBCode (см. ниже)\n\n\n2014-08-24 19:57:09 39a11.0 Расширение функционала BBCode\n[+] BBCode\n    Расширен функционал BBCode - добавлена поддержка уровня автора сообщений при парсинге\n    Для пользователей с разным authlevel досутпны разные BBCode. В частности, Администраторам (authlevel 3) теперь доступны следующие возможности:\n      1. Автоматическое преобразование URL-ов в ссылки\n      2. BBCode [url=HREF]text[/url]\n      3. BBCode [c] может использоваться с любыми цветами в формате [c=#XXXXXX]text[/c], где #XXXXXX - HTML-код цвета\n[+] Новости\n    В новостях теперь доступен весь функционал BBCode (см.выше)\n    Теперь по умолчанию галочка \"Разослать новость всем игрокам\" отключена\n[+] Чат\n    В чате теперь доступен расширенный функционал BBCode (см.выше)\n\n\n2014-08-23 17:04:43 39a10.20\n[%] Обслуживание\n    Исправлена несовместимость Обслуживания с новым кодом аутентификации\n\n\n2014-08-23 16:47:19 39a10.18\n[%] UBE\n    Исправлена ошибка, при которой при потерях в возвращающемся флоте неправильно отображалось количество кораблей\n\n\n2014-08-23 16:37:03 39a10.17\n[~] Режим отпуска\n    Теперь на странице отпуска не отображаются: меню, новости, навбар... По прежнему блокируется вход на любые страницы игры. Отдыхать - так отдыхать!\n\n\n2014-08-23 16:22:25 39a10.16\n[%] Миссии/Шпионаж\n    Исправлена ошибка добавления в отчёт эффективного уровня юнитов, а не базового\n\n\n2014-08-23 15:56:19 39a10.14\n[%] Код\n    Убраны BOM-префиксы в исходниках - таким образом, восстановлена работа создания баннера на странице \"Заработай ТМ\" и, собственно, сама генерация баннера\n\n\n2014-08-23 15:20:58 39a10.13\n[~] Интерфейс/Исследования\n    На странице \"Исследования\" при выборе Астрокартографии в подробном описании добавлена таблица, показывающая увеличение количества экспедиций и колоний при апгрейде технологии\n[~] Интерфейс/Империя\n    Добавлено количество текущих/максимальных экспедиций\n\n[%] Флоты/Колонизация\n    Исправлена ошибка, позволяющая колонизировать на 1 планету больше возможного количества\n\n\n2014-08-15 05:02:31 39a10.12\n[~] Авторизация\n    Убрано автозаполнение аналогичных ячеек при наборе данных - как возможная причина дублирования символов на некоторых андроид-устройствах\n\n\n2014-08-14 18:07:14 39a10.11\n[%] Локализация\n    В текстах экспедиционная технология заменена Астрокартографией\n\n\n2014-08-14 15:24:27 39a10.10\n[~] Авторизация\n    Добавлены страницы редиректа старых адресов reg.php и lostpassword.php\n\n\n2014-08-10 07:59:30 39a10.9\n[%] Платежные модули\n    Исправлена несовместимость платежных модулей с новой системой аутентификации\n\n\n2014-08-09 22:21:39 39a10.6\n[!] misc_player v2c1\n    Новый  HTML5/SWF плеер, совместимый с подавляющим большинством устройств\n    Теперь определение мобильныого устройства и переключение плеера на HTML5 версию работает корректно\n    Поправлены CSS-стили под поддержку бОльшего количества браузеров\n    Обновлен плейлист\n\n\n2014-08-09 21:30:58 39a10.3\n[~] Метаматерия\n    Немного изменен алгоритм начисления ММ\n\n\n2014-08-08 04:50:49 39a10.2\n[+] Авторизация\n    Добавлена отправка письма с логином и паролем при регистрации\n    Исправлены некоторые ошибки при авторизации\n\n\n2014-08-07 01:02:13 39a10.0 - Авторизация и статистика\n[!] Авторизация\n    Полностью переписана с нуля система авторизации в игре\n    Все операции системы авторизации проводятся в init.php - дальше по коду передаются только результаты авторизации. Это дает возможность позже добавить плагины для авторизации во внешних сайтах\n    Страницы логина, регистрации и восстановления пароля сведены в одну и сделаны более дружественными для мобильных пользователей:\n    При восстановлении пароля отсылается цифровой код, что облегчает ввод с экранной клавиатуры\n    Код восстановления высылыается не чаще раза в час и действует 1 сутки\n\n[+] Статистика\n    Статистика переработана, что бы потреблять меньше памяти в процессе\n    Размер памяти, резирвируемый под процесс PHP при обсчете статистики, можно менять переменной 'stats_php_memory' в таблице `config`. Синтаксис - такой же, как и в php.ini. Значение по умолчанию - 1024M\n    Минимальный интервал обсчета задается переменной 'stats_minimal_interval' в таблице `config`. Значение по умолчанию - 600 секунд\n[+] Скины\n    Добавлена поддержка адаптивности скинов для мобильных пользователей\n    В настоящий момент - фон адаптируется ступенчато под размеры экрана или вовсе отключается (для устройств с небольшим размером экрана)\n\n\n2014-07-06 14:29:21 39a9.5\n[~] Статистика\n    Добавлено принудительное увеличение памяти в процедуру расчета статистики\n\n\n2014-07-06 14:24:44 39a9.4\n[%] Альянсы\n    Исправлена ошибка создания Альянса\n\n\n2014-06-29 05:08:12 39a9.3\n[%] Админка\n    Исправлена невозможность запуска менеджера летящих флотов из админки\n\n\n2014-06-29 04:33:56 39a9.2\n[!] Обзор Империи\n    Размер страницы уменьшен на 35%-50%\n[!] Обзор планеты\n    Размер страницы уменьшен на 5%-20%\n[!] Скины\n    При разрешении экрана менее 1224 пикселов фон либо не грузится вообще (страница входа/регистрации) или грузится облегченная версия фона если разрешение экрана выше 768 пикселов (скины EpicBlue и supernova-ivash)\n    Это сделано для более быстрой работы на мобильных устройствах и маломощных компьютерах. Фишка работает только в перечисленных скинах и/или на указанных страницах\n    Так же, если размер изображентия планеты невелик, то грузится файл с меньшим разрешением. В неподдерживаемых скинах картинки планет могу отсутствовать вовсе\n\n\n2014-06-28 22:34:51 39a9.0 - КК, Фаза 10. И опять не КК-шная, мобильно-оптимизаторская\n[!] Страница построек\n    Размер страницы построек уменьшен на 25%-35%. Например, на тестовом прогоне размер уменьшился с 82676 до 64582 байт. И это со включенным минификатором!\n\n\n2014-06-28 18:10:08 39a8.21\n[~] Интерфейс\n    Везде, где возможно, поля ввода для логина и пароля ограничены 32 символами\n[~] Настройки игрока\n    Добавлена возможность ввести основной емейл - если он еще не введен\n    Добавлены подсказки, объясняющие различие между основным и вторичным емейлом\n\n[%] Настройки игрока\n    Исправлена ошибка в заголовке таблицы на странице \"Уведомления\", если на сервере включена отправка емейлов\n\n[@] Код\n    Обработка летящих флотов вынесена из common.php в init.php\n    Часть кода из init.php вынесена в отдельный файл init/init_functions.php\n    Убрана переменная $IsUserChecked\n[@] Верстка\n    Добавлено подключение CSS, специфичного для темплейта - _template.css - из корня темплейта\n    Основная часть страницы теперь центрируется не через <center>\n\n\n2014-06-26 19:16:42 39a8.20\n[+] Локализация\n    Добавлена подсистема сбора информации об употреблении строк локализации в коде. Включается переменной \"server_locale_log_usage\" в таблице `config`\n\n\n2014-06-24 19:08:49 39a8.18\n[~] Чат\n    В списке онлайна Администраторы сервера теперь всегда идут первыми\n\n\n2014-06-20 20:02:18 39a8.13\n[+] Локализация\n    Добавлена поддержка вариантов языка - типа, en-US и en-UK\n\n[~] Логин/Регистрация/Восстановление пароля\n    Переделан выбор языков\n\n\n2014-06-15 09:35:00 39a8.12\n[-] Темплейты/OpenGame\n    Удалены неисользуемые картинки в каталоге img\n\n[@] Веб-сервер\n    Добавлены файлы .htaccess\n\n\n2014-06-14 15:46:33 39a8.11\n[~] Меню\n    Рекламная ссылка перенесена в самый низ меню\n\n\n2014-06-14 14:58:21 39a8.8\n[~] Строительство\n    Восстановлен редирект на новую страницу после постройки\n[~] Меню\n    Ссылка на движок открывается в новом окне и на странице с описанием движка\n\n[%] Альянсы\n    Исправлена ошибка\n\n\n2014-06-11 19:40:28 39a8.7\n[~] Меню\n    В стандартных скинах убраны \"скачки\" меню в процессе рендеринга страницы\n\n\n2014-06-11 14:49:17 39a8.6\n[%] Настройки\n    Восстановлены потерянные настройки \"День рождения\" и \"Замерить разницу времени...\" (вкладка \"Профиль\"), а так же выбор языка игры (вкладка \"Интерфейс\")\n\n\n2014-06-10 13:08:15 39a8.5\n[%] Верфи и оборона\n    Исправлена странная ошибка в некоторых случаях не позволяющая ввести некоторые количества юнитов\n\n\n2014-06-10 12:26:22 39a8.4\n[%] Верфи и оборона\n    Исправлена ошибка невозможности постройки юнитов с ограничением на максимальное количество (например - щиты и ПЗ)\n\n\n2014-06-10 07:27:57 39a8.3\n[!] Настройки\n    Переделана страница настроек под табы\n    Добавлена возможность отключать колонок \"Статистика игрока\" и \"Информация об игроке\" в обзоре Вселенной\n\n[+] Верфи и оборона\n    Добавлена информация о боевых характеристиках (для всех юнитов) и скоростных характеристиках (для кораблей)\n\n[%] Верфи и оборона\n    Исправлена ошибка непоказывания 0 юнитов в описании юнитов\n\n\n2014-06-09 15:48:57 39a8.2 Верфи и оборона\n[!] Верфи и оборона\n    Интерфейс верфи и обороны унифицирован с интерфейсом постройки зданий\n    Теперь при постановке в очередь юнитов больше, чем максимальный размер стэка очереди (2000 по умолчанию) в очередь ставятся подряд несколько стэков - до тех пор, пока не будет поставлено в очередь нужное количество юнитов или пока не закончатся свободные слоты в очереди\n    Восстановлен функционал старого интерфейса - при срабатывании ограничений (по количеству юнитов, по размеру ракетной шахты, при нехватке ресурсов итд) в очередь ставится максимально возможное количество юнитов с учётом ограничений\n    Унифицированный интерфейс особенно хорошо смотрится с вертикальной очередью построек (для экранов с разрешением свыше 1000 пикселов по горизонтали). Вертикальная очередь построек включается в \"Настройках\" соответствующим чекбоксом\n\n\n2014-06-07 17:37:43 39a8.0 - КК, Фаза 9. Не КК-шная\n[!] Исследования\n    Интерфейс исследований унифицирован с интерфейсом постройки зданий\n    Премиум-аккаунт добавляет 1 слот в очереди исследований за каждый уровень Премиума\n\n\n2014-06-05 10:51:13 39a7.24\n[%] Логин\n    Исправлена ошибка входа в игру, если текущей планетой является удаленная по Обслуживанию планета\n\n\n2014-06-02 00:24:28 39a7.23\n[%] Миссии/Экспедиция\n    Исправлена ошибка нахождения Солнечного Спутника в Экспедиции\n\n\n2014-06-01 18:58:42 39a7.22\n[%] Контакты\n    Исправлена ошибка вывода всех игроков\n\n\n2014-06-01 16:28:24 39a7.20\n[%] Миссии/Экспедиция\n    Исправлены нерабочии экспедиции\n[%] Миссии/Атака\n    Исправлено несохранение потерянных юнитов при ничье или выигрыше обороняющегося\n    Исправлена ошибка при нападении на удаленную планету\n[%] Миссии/Колонизация\n    Исправлена ошибка с \"вечным\" колонизатором\n\n\n2014-05-18 04:59:46 39a7.18\n[%] Капитаны\n    Исправлена очепятка\n\n[@] Код\n    Функции преобразования intval() для идентификаторов БД bigint(20) в $supernova заменены на idval() - внутри которой пока floor()\n\n\n2014-05-17 08:36:14 39a7.16\n[+] Капитаны\n    Теперь при отправке флота виден уровень Капитана на планете и его скиллы\n\n[%] Капитаны\n    Исправлена ошибка отправки Капитана с флотом\n\n2014-05-16 06:42:34 39a7.13\n[~] Отладка\n    Доработана отладка\n\n\n2014-05-15 06:58:50 39a7.12\n[%] Фаланга\n    Исправлена ошибка\n\n\n2014-05-15 06:37:24 39a7.11\n[%] Капитаны\n    Исправлена ошибка\n\n\n2014-05-14 22:10:08 39a7.9\n[%] Миссии/Ракетная атака\n    Исправлено увеличение Перехватчиков в шахтах вместо уменьшения при атаке\n\n[%] Стандартный чат\n    Исправлена ошибка вывода бенчмарка в AJAX-выдачу\n\n[@] Код\n    К LOC_QUE добавлены родители в LOC_PLANET по que_planet_id_origin и que_planet_id\n\n\n2014-05-13 18:09:32 39a7.8\n[%] Флоты/Своз ресурсов\n    Исправлена ошибка из-за которой ресурсы на планете не уменьшались при свозе\n\n\n2014-05-13 05:56:44 39a7.6\n[%] Альянсы\n    Исправлена ошибка с показом количества игроков в Альянсе\n[%] Премиум\n    Исправлена ошибка с отображением даты окончания Премиума\n[%] Флоты/САБ\n    Исправлена невозможность добавить игрока в САБ\n\n\n2014-05-12 21:26:52 39a7.4\n[!] Админка/Записи логов\n    Определение дедлоков и добавочная информация для их диагностики\n    Для получения добавчной инофрмации о дедлоках пользователь MySQL, под которым запускается игра, должен иметь право MySQL PROCESS\n\n[%] Регистрация\n    Исправлена ошибка уровня Warning\n\n[@] Код\n    Доработка сообщений о дедлоке для более лёгкой читаемости\n\n\n2014-05-12 16:52:30 39a7.3\n[!] Код\n    Исправлены дедлоки (?)\n    Две новые директивы отладки в init.php:\n      DEBUG_SQL_COMMENT - включает комментирование SQL-запросов\n      DEBUG_SQL_ONLINE - включает лог SQL-запросов в таблицу `logs`. Так же подразумевает DEBUG_SQL_COMMENT\n\n[~] Чёрный рынок\n    При полностью пустом списке б/у кораблей в продаже он пополняется случайным образом\n\n[%] Статистика\n    Исправлен запуск статистики при каждом входе админа в админку\n\n\n2014-05-11 22:39:53 39a7.2\n[@] Отладка\n\n\n2014-05-11 16:50:06 39a7.0 - КК, Фаза 8. Опытно-квестовая\n[!] Квесты\n    Переписан механизм квестов\n    Квесты включены\n    Исправлен баг неполного начисления награды, когда одновременно выполняются более одного квеста\n[!] Опыт\n    Включен опыт за постройку зданий и исследование технологий\n[!] Отпуск\n    Переделана процедура ухода в отпуск\n    Выход в отпуск включен\n[!] Очереди\n    Теперь при ошибке постановки в очередь выдается соответствующее сообщение с подробным объяснением причины ошибки\n\n\n2014-05-11 13:18:26 39a6.15\n[!] Код\n    cSN::db_get_record_by_id() теперь использует cSN::db_get_record_list()\n\n[%] Миссии/Переработка\n    Теперь переработчики собирают ресурсы\n[%] Обновление\n    Исправлена редкая возможность запуска двух обновлений\n\n\n2014-05-11 11:18:24 39a6.14\n[!] Код\n    changeset v1 - добавлена поддержка условий WHERE типа $field => $value. Обратно совместимо с v0\n    db_chаngeset_condition_compile()\n    Улучшенный лог SQL-запросов\n    Переработаны функции cache_ и их использование\n\n\n2014-05-11 06:22:18 39a6.12\n[%] Артефакты\n    Исправлена работа наностроителя\n\n[%] Альянсы\n    Исправлена ошибка Исследований Альянса\n    Исправлена ошибка покупки Планов Альянса\n\n\n2014-05-10 20:13:37 39a6.10\n[!] Ускорение\n    В три раза ускорение при в 3 раза меньшей памяти! (по сравнению с 39a5.4)\n[!] Код\n    db_get_list() теперь автоматически блокирует родителей любого уровня вложенности - быстро и аккуратно\n    Получаем очереди по локации через $supernova\n\n[%] Миссии/Шпионаж\n    Исправлена ошибка\n\n\n2014-05-10 13:21:39 39a6.9\n[%] Наемники\n    Исправлена ошибка при покупке Наемника\n\n\n2014-05-10 03:32:11 39a6.6\n[!] Код\n    db_changeset_apply() теперь работает с методами $supernova\n    После que_get() теперь первый элемент выбирается независимо от индексации - можно использовать нормально очереди с методами $supernova\n\n[~] Артефакты\n    На Эвристический чип и Наностроитель добавлена защита от случайного срабатывания. Теперь они не срабатывают, если осталось меньше 1 минуты для постройки\n\n\n2014-05-09 19:26:29 39a6.3\n[!] Код\n    Доступ к `unit` использует методы $supernova\n\n\n2014-05-09 18:06:15 39a6.2\n[!] Код\n    Унифицироваы процедуры базовых операций с записями из любых таблиц\n    Доступ к `users` использует процедуры $supernova\n    Доступ к `planets` использует процедуры $supernova\n\n\n2014-05-08 07:11:45 39a6.0 - КК, Фаза 7. Оптимизация\n[!] Админка\n    Отключены: панель админа, компенсация планет - может что-то еще\n    В начислении ТМ и ММ поиск производится только по имени пользователя\n    Убрана возможность начислять ТМ по планете\n[!] Код\n    Базовые операции доступа к `users` внесены в $supernova\n\n\n2014-05-07 19:07:22 39a5.8\n[!] Код\n    Все обращения к таблице `que` вынесены в отдельный файл\n\n\n2014-05-05 19:49:32 39a5.6 Багфиксы\n[%] Код\n    Исправлено множество багов\n\n\n2014-05-05 17:35:11 39a5.4\n[!] Код\n    Все обращения к таблице `unit` вынесены в отдельный файл\n\n\n2014-05-04 23:52:56 39a5.3\n[%] Логин\n    Исправлена проблема с логином\n\n\n2014-05-04 23:20:44 39a5.2\n[%] Движок БД\n    Исправлена очепятка выборки ИД пользователя по имени\n\n\n2014-05-04 22:09:41 39a5.0 - КК, Фаза 6. Нудная фаза\n[!] ВНИИМАНИЕ! ДАННЫЙ ПАТЧ ЯВЛЯЕТСЯ ПРОМЕЖУТОЧНЫМ! В НЁМ ВСЁ ЕЩЕ НЕ РАБОТАЮТ НЕКОТОРЫЕ ФИШКИ (СМ. ПРЕДЫДУЩИЙ ПАТЧНОУТ)\n[!] Код\n    Все обращения к таблице `planets` вынесены в отдельный файл\n    Все обращения к таблице `users` вынесены в отдельный файл\n    В запросы \"START TRANSACTION\", \"COMMIT\" и \"ROLLBACK\" заменены вызовами соответствующих функций\n\n[%] Движок\n    Исправлены какие-то ошибки\n\n\n2014-05-03 04:02:05 39a4.3\n[!] Код\n    В основном коде запросы \"START TRANSACTION\", \"COMMIT\" и \"ROLLBACK\" заменены вызовами соответствующих функций\n\n[%] Вселенная\n    Исправлено отображение нового количества ракет после межпланетной атаки\n[%] Админка\n    Ссылка \"Компенсировать\" возвращена на своё место в меню\n[%] Код\n    Исправлено предупреждение Warning: Invalid argument supplied for foreach() in includes/db.php on line 365\n\n\n2014-05-02 20:30:58 39a4.2 Багфиксы\n[!] Кстати, сейчас в Верфи и Обороне строится только первое из выбранных сооружений. Забыл написать\n\n[%] Движок\n    Пофикшены все найденные баги патча 39a4.0\n\n\n2014-05-02 09:59:28 39a4.0 - КК, Фаза 5. Отвязная фаза\n[!] Требуется версия PHP >= 5.3.2\n[!] ВНИИМАНИЕ! ДАННЫЙ ПАТЧ ЯВЛЯЕТСЯ ПРОМЕЖУТОЧНЫМ! В НЁМ НЕ РАБОТАЮТ НЕКОТОРЫЕ ФИШКИ\n    Отключены квесты\n    Отключено начисление опыта за постройки/исследования\n    Не отсылаются сообщения очередей\n    В админке отключена \"Панель админа\" и \"Редактирование планеты\"\n    При ошибках при добавлении постройки/исследования/корабля/обороны в очередь игра останавливается и выдается сообщение об ошибке. Что бы продолжить игру - вернитесь на предыдущую страницу. Если сообщение соответствует ошибке - сообщать об этом не надо!\n    Может заметно ухудшиться отзывчивость движка. Это нормально - код будет еще оптимизироваться\n[!] Очереди\n    С нуля написана универсальная подсистема очередей. Очереди отвязаны от записей пользователей и планет в БД\n    При обновлении очереди построек на планетах будут сконвертированы в новый формат. Очереди верфи и обороны будут обнулены, а стоимость юнитов в очереди - возвращена на планету\n    Теперь очереди полностью независимые от записей пользователя и планеты\n    Полностью разделены очереди кораблей и обороны. На обзор планеты добавлена индикация очереди обороны. На картинки планет в обзоре планеты и обзоре Империи добавлена иконка активной очереди обороны (щит)\n[!] Юниты\n    Постройки, корабли и оборона отвязаны от записей пользователей и планет в БД\n    Все наличные постройки, корабли и оборона сконвертированы в новый формат\n[!] Статистика\n    Полностью переделан расчёт статистики\n    Скорость расчёта статистики заметно увеличена (при одновременном увеличении количества объектов для обсчёта!). Чем больше игроков и чем активнее игра - тем больше выигрышь в скорости обсчета. Ускорение обсчета на типичном сервере составляет от 10 раз и выше\n    Теперь полностью учитываются все юниты всех типов, включая корабли в полёте\n    Теперь в статистике по ресурсам полностью учитываются все ресурсы: на планетах, вложенные в очереди строительства/верфи/обороны/исследования, находящиеся на флотах в полёте. Так же учитывается наличная ТМ (ММ не учитывается)\n    Теперь при расчете статистики Альянса так же учитывается юниты Альянса и ресурсы в банке\n    Теперь расчитывается и отображается изменение места Альянса во всех типах статистики\n    Исправлена очепятка, из-за которой вообще не учитывался дейтерий\n    Все вышеуказанные изменения приведут к однократной перетусовке в статистике и росту абсолютного значения всех видов статистики\n[!] Код\n    Фактически, всё, связанное с юнитами и очередями полностью переписано\n\n[+] Рекорды\n    Для флотов и обороны теперь показывается суммарное количество юнитов на всех планетах и лунах. Флоты в полёте по-прежнему не учитываются\n\n[~] Меню\n    Изменены иконки пунктов меню Верфь, Оборона и Флоты в полете\n\n[%] Очередь верфи\n    Исправлена ошибка индикации оставшихся юнитов в стаке при постройке последнего в стаке юнита\n[%] Альянсы\n    Исправлено незаполнение имени user_as_ally тэгом при создании Альянса\n[%] Очередь исследований\n    Наконец-то локализована и, вроде, исправлена редкая ошибка с \"зависанием\" времени исследования в оффлайне\n[%] Артефакты\n    Исправлена редкая ошибка, когда АКК мог пропасть при неудачной попытке развернуть его на полной планете\n[%] Обслуживание сервера\n    Убраны предупреждение на экране оповещения об обслуживании при включенных E_WARNING\n\n[@] Код\n    Убраны все прямые обращения к P_NAME, оставшиеся изолированы\n    SetSelectedPlanet() переписан полностью\n\n\n2014-04-22 13:58:44 39a3.7\n[~] Платежи\n    Добавлена поддержка WMB - белорусских рублей на WebMoney\n    Основной валютой сервера по умолчанию установлен доллар США (USD). Скорректированы курсы всех валют\n[~] О сервере\n    Добавлена индикация режима взаимодействия игроков с 1 IP (мультиаккаунтов)\n\n\n2014-04-14 15:25:14 39a3.6\n[%] Экономика\n    Исправлена ошибка обсчета планеты\n\n\n2014-04-12 15:33:17 39a3.5\n[+] ЧР/Обмен ресурсов\n    Строка ввода заменена на мультиэлемент\n\n\n2014-04-12 13:45:47 39a3.3\n[~] Флоты/Своз ресурсов\n    Теперь в расчете строки ИТОГО учитывается ёмкость трюмов флота, т.е. показывается реальное количество ресурсов, которые будут свезены по текущим данным\n    Код расчёта переписан на jQuery\n\n[@] Код\n    Теперь в sys_o_get_updated() при выборке планеты FOR UPDATE сразу же блокируется и юзер. Это чуть замедлит работу, но позволит избежать дедлоков при работе с планетами и юзерами\n\n\n2014-04-12 11:23:30 39a3.2\n[~] Отправка флота\n    Кнопка 1/(доступных экспедиций) теперь появляется только если не дублирует кнопки 1/(максимальное количество экспедиций) и \"Все корабли\"\n\n\n2014-04-12 11:05:24 39a3.1\n[%] Флоты/Менеджер летящих флотов\n    Исправлена ошибка неактуальной информации о планете при шпионаже/атаке планет неактивных игроков\n\n[@] Код\n    Исправлено предупреждение в uni_coordinates_valid()\n\n\n2014-04-11 06:41:10 39a3.0 - КК, Фаза 4 - Под коп под топ\n[!] UBEv4.1\n    Изменена процедура обсчета боя. Теперь щиты считаются индивидуально для каждого корабля\n    Это значит, что теперь практически невозможно провести бой без потерь с обеих сторон\n\n\n2014-04-09 12:25:48 39a2.10 - Продвинутый мультиэлемент ввода данных\n[+] Темплейт\n    Существенно доработан функционал мультиэлемента выбора количества (строка ввода со слайдером и кнопками \"-\" и \"+\")\n    Добавлены кнопки \"0\" и \"М\" - соответственно устанавливающие значение поля в 0 и в максимальное значение\n    Теперь можно не кликать несколько раз подряд для увеличения/уменьшения значения в ячейке, а достаточно зажать кнопку мышки - количество будет изменятся автоматически\n    Изменение данных при зажатой мышке проводится с ускорением - чем дольше держать кнопку мышки, тем быстрее будет изменятся значение в строке ввода\n    ВНИМАНИЕ! В отдельных браузерах страницу, возможно, прийдется перегрузить несколько раз подряд, что бы новый функционал полноценно заработал!\n\n[~] Отправка флота\n    Добавлена кнопка 1/X, где X - количество неотправленных экспедиций. Появляется только если X > 1 и X < максимального количества экспедиций\n    В подбор кораблей добавлен новый функционал мультиэлемента\n\n\n2014-04-09 10:17:15 39a2.9\n[%] Миссии/Переработка\n    Исправлена ошибка из-за которой переработчики не привозили ресурсы\n\n\n2014-04-08 23:50:53 39a2.8\n[+] Меню\n    В раздел \"Правила игры\" добавлена ссылка на подробную документацию к игре. Файл по ссылке открывается в новом окне\n\n[%] Миссии/Экспедиция\n    Исправлена ошибка обсчёта события Экспедиции по прибытию в неизведанную территорию, а не после окончания срока Экспедиции\n    Исправлена ошибка двойного обсчёта одной и той же Экспедиции\n[%] Миссии/Уничтожение\n    Испрвлена ошибка дублирование отчетов в ЛС\n\n[@] Разное\n    Теперь можно отключить защиту от взаимодействия аккаунтов с одним IP выставив в таблице `config` параметр `game_multiaccount_enabled` в 1\n\n\n2014-04-08 13:24:24 39a2.7\n[~] Артефакты\n    Добавлены временные картинки для Крюков\n\n\n2014-04-08 10:51:44 39a2.6 - Луны на продажу!\n[!] Артефакты\n    Новый тип Артефакта: Крюк. Он телепортирует астероид из ближайшего метеоритного пояса и запускает его на орбиту планеты, создавая таким образом луну. Доступны три вида Крюков: Малый, Средний и Большой. Малый создает луну минимального диаметра (1100 км), Большой - максимального диаметра (8999 км), а Средний - луну случайного диаметра (от 1100 до 8999 км)\n    Изменена логика работы Наностроителя и Эврестического Чипа. Теперь они уменьшают время соотвественно текущего исследования в Империи и постройки/разрушения текущего строения на планете/луне в два раза (если до окончания процесса осталось больше часа) или моментально заканчивают процесс (если до окончания осталось не более 1 часа)\n    В цене Артефакта добавлен разделитель тысяч\n\n\n2014-04-08 08:29:13 39a2.4\n[%] Навбар\n    Исправлена ошибка неправильной страницы строительства при переключении планеты на странице, отличной от страницы построек\n[%] Альянсы\n    Исправлена ошибка с возможностью сделать Альянс пустым именем/тэгом/титулом главы\n\n\n2014-04-07 00:24:34 39a2.2\n[#] unit_captain 3a0\n    (!) Требуется СН 39a2.0+\n    (~) Вынести работу с Капитаном при возвращении флота в модуль Капитанов\n\n\n2014-04-07 00:02:05 39a2.0 - КК, Фаза 3: Менеджер летящих флотов\n[!] Флоты/Менеджер летящих флотов\n    Бета-версия нового менеджера летящих флотов (МЛФ)\n    Новый МЛФ должен гарантировать полное отсутствие дедлоков\n    Все мисиии переписаны для поддержки нового МЛФ\n    Из основного кода удалена поддержка модуля Капитанов\n\n[+] Флоты/Шпионаж\n    Включён \"Имперский шпионаж\"\n    Уровень Имперского шпионажа (УИШ) - это сумма уровней Шпиона и Шпионской технологии с учётом всех доступных бонусов, но без учёта количества спутников-шпионов\n    Если УИШ шпионящего больше или равен УИШ шпиониемого, то в отчете будут видны так же Имперские Технологии\n\n[@] Локализация\n    Добавлена переменная активного языка в classLocale\n    Добавлено уведомление о критической ошибке при попытке вызвать функции локализации с $lang не в виде класса\n[@] БД\n    При старте уровень транзакций сессии устанавливается в REPEATABLE_READ для меньшей зависимости от настроек сервера\n    Добавлена возможность установливать уровень отдельной транзакции в sn_start_transaction()\n    В doquery() запрос теперь обрабатывается функцией trim()\n\n\n2014-04-05 18:07:11 39a1.3\n[~] Темплейт\n    В темплейте включён рекламный блок и логотип СН\n\n[%] Навбар\n    Исправлена ошибка переключения планеты в истории чата\n\n\n2014-03-28 21:11:10 39a1.2\n[@] В модулях убраны обращения к $sn_data\n\n\n2014-03-28 20:31:43 39a1.0 - КК, Фаза 2: $sn_data\n[@]  Все  оставшиеся  обращения  к  $sn_data  в  основном  коде   заменены   на\nget_unit_param()\n\n\n2014-03-26 14:46:41 39a0.6\n[@] Обслуживание\n    Изменена процедура Обслуживания\n\n\n2014-03-22 00:48:56 39a0.0 - КК, Фаза 1: Группы\n[@] Все обращения к $sn_data['groups'] заменены на sn_get_groups()\n\n\n2014-03-03 18:28:35 38d0 Project \"SuperNova.WS\" Release 38 \"Admin astro expo news bugfix\"\n[!] Project \"SuperNova.WS\" Release 38 \"Admin astro expo news bugfix\"\n\n\n2014-02-22 04:38:08 38c0 Project \"SuperNova.WS\" Release Candidate 38RC0 \"Admin astro expo news bugfix\"\n[!] Project \"SuperNova.WS\" Release Candidate 38RC0 \"Admin astro expo news bugfix\"\n\n\n2014-02-20 16:32:16 38a11.1\n[%] Обзор Империи\n    Добавлен патч для устранения несовместимости глюкобага со стандартами W3C\n\n\n2014-02-19 02:37:18 38a11.0\n[+] Обзор Империи\n    Оптимизирован HTML-код страницы. В среднем в минифицированном состоянии выигрышь составил порядка 6 кб на 1 планету/луну. Чем больше объектов в Империи и чем больше типов юнитов - тем больше выигрышь\n    Строка таблицы с координатами перемещена под строку с названием планет для унификации вывода\n    Строка с количеством секторов убрана - она дублирует информацию на иконке планеты\n\n\n2014-02-17 20:32:36 38a10.25\n[-] Скин\n    Убран скин sn_space_blue\n\n\n2014-02-15 16:13:54 38a10.23\n[~] Новости\n    Добавлена информация о публикаторе новости\n    Изменено отображение новости\n    Добавлена вторичная сортировка новостей по ID\n\n[%] Настройки\n    Исправлено отображение статуса удаления аккаунта\n\n\n2014-02-15 15:06:17 38a10.22\n[~] Чёрный рынок\n    Переписан на использование result вместо message()\n\n[@] Интерфейс\n    message() теперь работает через PTE-объект\n\n\n2014-02-15 13:56:36 38a10.21\n[~] Меню\n    Пункты меню \"ЧаВо\", \"Форум\" и \"Правила игры\" открываются в новых окнах\n[~] Админка/Обслуживание\n    Теперь сразу после обслуживания происходит обновление статистики - для устранения разрывов в местах игроков, которые могут появится из-за удаления старых аккаунтов\n    Убраны операции очистки таблиц, дублирующие работу констраинтов\n\n[%] Боевой отчет\n    Устранено появление строки \"Дата и время\" для симулированных отчетов при ненулевой разнице клиентского и серверного времени\n[%] Настройки\n    Исправлена индикация режима защиты планет Администрации\n\n\n2014-02-11 10:50:00 38a10.18 - Радио \"Космос\"\n[#] misc_radio 0a0\n    (!) Начальная ревизия\n\n\n2014-02-10 19:56:03 38a10.15\n[%] Статистика\n    Исправлено постоянное обновление статистики\n\n\n2014-02-10 19:35:39 38a10.14\n[%] Статистика\n    Исправлена ошибка неправильного времени в графе \"Следующее обновление\"\n[%] Поиск\n    Исправлена ссылка на страницу статистики для ранка 1000+\n    Убрана ссылка на страницу статистики для неучаствующих в подсчете аккаунтов (например - Адмиинистрации сервера)\n\n[@] Расписание\n    Изменен формат расписания. Теперь он определяет интервал запуска задачи и имеет вид:\n      Г-М-Д Ч:И:С\n    где Г, М, Д, Ч, И, С - соответственно длина интервала в годах, месяцах, днях, часах, минутах и секундах\n    Значения левее первой значащей цифры можно не указывать. Например, \"0-0-1 0:0:0\" можно записать как \"1 0:0:0\" и это будет означать \"запустить задачу раз в сутки\"\n    Нулевые значения можно опустить. Например, предыдущий интервал можно записать так же в виде \"1 ::\". Обращаю внимание на пробел между \"1\" и \":\"! Пробел - значащий разделитель и его опускать в данном случае нельзя, потому что интервал \"1::\" будет истолкован как \"запустить задачу раз в час\"!\n\n\n2014-02-09 01:14:56 38a10.12\n[%] Флоты/Отправка флотов\n    Исправлена невозможность отправить флот в Удержание при подборе кораблей через пункт меню \"Флот на орбите\"\n    Усилена защита от отправки флотов в нетранспортную миссию с ресурсами\n[%] Обзор Вселенной\n    Исправлена ошибка пропадения иконок ракетной атаки и шпионажа у планет игроков, неактивных более одной недели\n\n2014-02-08 22:51:37 38a10.11\n[@] БД\n    Изменена таблица `payment`\n\n\n2014-02-08 22:27:22 38a10.10\n[+] Новости\n    Теперь свежие новости показываются на всех страницах залогиненного пользователя\n    Теперь для того, что бы скрыть свежие новости не обязательно открывать страницу новостей - достаточно кликнуть на кнопку \"Закрыть\" в правом верхнем углу списка новостей\n\n[~] Статистика\n    Теперь на странице статистики показывается так же время следующего обновления\n    Время предыдущего и следующего обновления учитывает разницу между локальным и серверным временем\n[~] Навбар\n    Если страница открыта во фрейме (например, при прикреплении чата) в под навбаром появляется ссылка \"Обновить страницу\", при нажатии которой страница по фрейме будет обновлена\n\n[%] Сообщения\n    Исправлена ошибка в сообщениях, если указан неправильный класс сообщений\n\n\n2014-02-07 12:59:19 38a10.8\n[#] player_award 0a2\n    (+) Бессмертный\n        Добавлен памятный знак \"Бессмертный\"\n        Знак начисляется автоматически при покупке хотя бы одной единицы ММ\n        Статус \"Бессмертного\" означает сохранение аккаунта при автоматической чистке БД (Админка/Обслуживание)\n\n\n2014-02-06 00:34:11 38a10.2\n[#] player_award 0a0\n    (!) Первая версия\n        Требуется СН 38a10.0+\n        Раздел \"Награды и достижения\" на странице Императора, видимый всем\n        Поддержка орденов, медалей, памятных знаков, вымпелов, бэйджей\n        Орден Спонсора четырех степеней - в комплекте\n        Планка Ордена Спонсора в нике везде, где допускаются иконки\n        Отдельный тип опции\n\n\n2014-02-06 00:30:02 38a10.0\n[#] player_award\n    Подддержка модуля\n\n\n2014-02-04 03:39:05 38a9.18\n[~] Меню\n    Переформатировано меню - убраны дублирующиеся пункты, ЧаВо перенесено вверх\n\n\n2014-02-03 18:17:11 38a9.17\n[~] Флоты\n    При максимальном количестве экспедиций (Х) более одной доступна новая кнопка на странице подбора флота - \"1/X\"\n\n\n2014-01-25 17:24:28 38a9.16\n[~] Вселенная\n    Добавлены картинки-плейсхолдеры для аватара/лого Альянса/миниатюры планеты\n\n[%] Альянсы\n    Исправлена ошибка \"налазания\" длинного внешнего текста на логотип Альянса\n\n\n2014-01-25 00:19:57 38a9.15\n[~] Скин/sn_space_blue\n    Обновлены файлы скина\n\n\n2014-01-25 00:08:29 38a9.9\n[@] Код\n    Убрана ошибка уровня PHP_STRICT в классах кэширования\n\n\n2014-01-24 16:02:44 38a9.8\n[!] Админка/Начисление ТМ\n    Переработана страница под phpBB3\n[!] Админка/Начисление ММ\n    Переработана страница под phpBB3\n\n\n2014-01-21 23:53:56 38a9.7\n[~] Система\n    Удалена неактуальная таблица rw\n\n\n2014-01-21 23:44:01 38a9.6\n[#] chat_advanced 2c2\n    [%] Чат\n        Исправлена ошибка с центровкой сообщений в чате\n\n\n2014-01-21 23:11:41 38a9.3\n[#] chat_advanced 2c1\n    (+) История чата\n        Добавлены кнопки листания на страницу вперед/назад, на первую/последнюю страницы истории\n    (+) Миничат\n        Теперь можно изменять соотношение фреймов, отведенных под основной экран и миничат. Для этого нужно потянуть за разделитель между фреймами\n\n[%] Системное\n    Исправлена ошибка статистической системы\n\n\n2014-01-21 18:13:03 38a9.1\n[%] Обзор Империи\n    Исправлена ошибка появления в таблице строчек с 0 юнитов/уровней\n[%] Админка/Обслуживание\n    Исправлена ошибка\n\n\n2014-01-19 21:48:26 38a9.0 - Переписывание админки\n[!] Админка/Список сообщений\n    Страница полностью переписана\n[!] Админка/Флоты в полёте\n    Страница полностью переписана. Процедуры унифицированы с пользовательской частью\n[!] Админка/Обзор\n    Страница полностью переписана\n[!] Админка/Добавить луну\n    Страница полностью переписана\n[!] Админка/Записи система логов\n    Серъезно переработана страница\n\n[%] Вселенная\n    Исправлена ошибка невозможности атаковать ракетами/шпионить слабого игрока i-шку\n    Добавлены тэги nowrap к таблице планет что бы не было переносов названий Альянсов с пробелами\n    Поправлены мелкие шероховатости\n[%] Альянсы/Интерфейс\n    Исправлена несовместимость с новой системой локализации\n\n\n2014-01-19 15:40:44 38a8.15\n[#] player_race_units 2c1\n    (%) Исправлена несовместимость с новой системой локализации\n[#] unit_hope 2c1\n    (%) Исправлена несовместимость с новой системой локализации\n\n\n2014-01-19 03:13:29 38a8.11\n[@] Локализация\n    Методы локализации инкапсулированы в класс и при работе с объектами вызовы процедур редиректят в методы объекта\n\n\n2014-01-19 01:47:26 38a8.10\n[~] Локализация\n    Поддержка модулей с юнитами\n\n\n2014-01-19 01:19:47 38a8.1\n[-] Локализация\n    Удален перевод на узбекский язык из-за отвратительного качества перевода\n\n\n2014-01-19 01:18:26 38a8.0\n[@] Локализация\n    Подготовка к новой системе локализации\n\n\n2014-01-18 19:30:15 38a7.9\n[#] player_race 2d0\n    (!) Добавлена поддержка СН 38a7.8+\n    (+) Марс\n        Марсиане так же получают +1 уровень к Астрокартографии\n    (+) Родные миры\n        Теперь родной мир можно выбрать сразу на странице \"Родные миры\"\n\n\n2014-01-18 19:08:42 38a7.8\n[%] Меню\n    Небольшой фикс\n\n\n2014-01-18 19:06:34 38a7.7\n[!] Админка/Меню\n    Меню в админке переделано на динамическое - по типу меню игроков\n\n\n2014-01-18 17:25:05 38a7.6\n[+] Страница игрока\n    Добавлена возможность просмотреть страницу игрока (ака \"Император\")\n    Для этого нужно кликнуть на иконку \"Император\", которая доступна:\n      1. На странице статистики\n      2. В результатах поиска\n      3. В Обзоре Вселенной\n\n[~] Обзор Вселенной\n    Переработан экран обзора Вселенной\n\n\n2014-01-18 16:12:52 38a7.5\n[+] Интерфейс/Количество колоний\n    Теперь видно текущее и максимальное количество колоний:\n      1. В Обзоре Империи (первая колонка, вторая строка)\n      2. На странице выбора кораблей во флот при переходе на неё из Обзора Вселенной с миссией \"Колонизация\"\n      3. На странице выбора миссии \"Колонизация\"\n\n\n2014-01-18 15:34:15 38a7.4\n[~] Админка/Локализация\n    Добавлены строки\n\n\n2014-01-17 18:21:28 38a7.3\n[%] Админка/Обновление\n    Исправлен  постоянный  запуск  обновления  при  работе  из-под   админского\n    аккаунта\n\n\n2014-01-17 18:04:17 38a7.2\n[~] Миссии/Экспедиции\n    Теперь максимальная стоимость находимых кораблей при событии \"Найден флот\" МЕНЬШЕ, чем стоимость самого дорогого из посланных кораблей. Другими словами - посланная ЗС никогда не найдет ЗС, а только корабли, дешевле её\n[~] Локальное время\n    Теперь замер разницы между клиентским и серверным временем производится автоматически каждый час\n[~] Админка/Обновление\n    Теперь для запуска обновления из админки используется гораздо более безопасная проверка по AUTHLEVEL пользователя, а не по HTTP_REFERRER вызывающей страницы\n[~] Админка/Обслуживание\n    Теперь используется LOCK TABLES вместо START TRANSACTION для исключения дедлоков\n\n[%] Плотность\n    Исправлена очепятка в описании типов ядер\n[%] Тёмная материя\n    Исправлена ширина страницы\n[%] Метаматерия\n    Исправлена ширина страницы\n[%] Обзор Империи\n    Теперь когда возвращается/строится корабль/юнит, которого нет на орбите/планете, правильно показывается соответствующая строка в Обзоре\n\n\n2013-12-26 11:54:26 38a7.0 - Экспедиции. Фаза 3 - Опыт и уровни\n[+] Миссии/Экспедиции\n    Теперь за регулярные полёты в экспедиции начисляется экспедиционный опыт\n    За набор экспедиционного опыта начисляются экспедиционные уровни\n    При получении каждого нового уровня начисляется 1.000 ТМ\n    Количество опыта для получения уровня - геометрическая прогрессия с первым членом 10 и показателем 1,05. Ниже дается для ориентировки небольшая таблица: в первой колонке - экспедиционный уровень, во второй - количество опыта для перехода на следующий уровень, в третьей - общее количество экспедиционного опыта для перехода на следующий уровень. Собственно, таблица:\n        1      10        10\n        2      10        20\n        3      11        31\n        4      11        42\n        5      12        54\n        6      12        66\n        7      13        79\n        8      14        93\n        9      14       107\n       10      15       122\n       15      19       209\n       20      25       321\n       25      32       465\n       30      41       650\n       35      52       887\n       40      67     1.189\n       45      85     1.575\n       50     109     2.070\n       75     369     7.530\n      100   1.252    26.052\n      150  14.361   301.323\n      200 164.691 3.458.217\n    Посмотреть текущее количество экспедиционного опыта, текущий уровень и необходимое количество для перехода на следующий уровень можно на странице \"Император\"\n\n\n2013-12-24 10:03:09 38a6.12\n[~] Платежи\n    Добавлены адреса серверов реального/предполагаемого в сообщение об ошибке платежа\n\n\n2013-12-22 16:10:33 38a6.8\n[~] Локализация\n    Обновлена локализация для поддержки sn_space_blue\n\n\n2013-12-22 15:11:36 38a6.5\n[%] Технологии/Астрокартография\n    Исправлена ошибка учета столицы, как колонии\n\n\n2013-12-22 14:45:20 38a6.2\n[%] Технологии/Астрокартография\n    Исправлена ошибка в требованиях к кораблю \"Колонизатор\"\n\n\n2013-12-22 14:37:55 38a6.1\n[%] Технологии/Астрокартография\n    Исправлена ошибка NAN в навбаре вместо 0 при отсутствующих технологиях Астрокартографии\n\n\n2013-12-22 05:03:56 38a6.0 - Экспедиции. Фаза 2 - Астрокартография\n[!] Технологии/Астрокартография\n    Экспедиционная технология и Колонизационная технология заменены одной технологией Астрокартографии\n    Стоимость имеющихся уровней устаревших технологий возвращена на главную планету игрока, а сами технологии удалены\n    Устаревшие технологии, находящиеся в исследовательской очереди игроков, удалены, а их стоимость возвращена на планету, где была запущена технология\n    По умолчанию убрано ограничение на максимальное количество колоний\n    Уровень развития Астрокартографии влияет на:\n      1. Максимальное количество колоний\n      2. Максимальное количество экспедиций\n      3. Максимальное время отправки флота в экспедицию\n\n\n2013-12-21 15:41:26 38a5.5\n[~] Метаматерия\n    Добавлено поле для общего количества полученной метаматерии - из всех источников\n\n\n2013-12-21 14:48:48 38a5.4\n[+] Навбар\n    Новая иконка Метаматерии в навбаре\n\n\n2013-12-21 14:33:55 38a5.3\n[~] Экспедиции\n    Добавлены строки локализации для локалей EN и UZ\n\n\n2013-12-21 14:31:32 38a5.2\n[~] Экспедиции\n    Исправлена ошибка расчета найденного количества кристалла и дейтерия - теперь правильно учитываются ресурсы\n\n\n2013-12-21 14:23:57 38a5.1\n[+] Экспедиции\n    Максимальная длительность Экспедиции теперь зависит от уровня Экспедиционной технологии - 1 час за каждый уровень на скорости Экспедиции х1\n    Изменена индикация времени Экспедиции для поддержки скорости Экспедиции <> 1\n\n[~] Удержание\n    Длительность удержания теперь находится в промежутке от 1 до 12 часов\n\n[%] Экспедиции\n    Исправлена ошибка неначисления найденного флота\n\n\n2013-12-21 03:20:09 38a5.0 - Экспедиции. Фаза 1 - полная переделка\n[!] Полностью переписаны Экспедиции\n    Экспедиции теперь планово-прибыльные, т.е. полеты в экспу одним и тем же флотом в среднем будут приносить прибыль, а не убытки, как раньше\n    Количественные результаты Экспедиций (нахождение флота, ресурсов, ТМ) теперь привязаны к стоимости отправляемого флота. Т.е. чем дороже флот в пересчете на ресурсы - тем больше будет найдено в экспедиции\n    Экспедиции стали средне- и высокоуровневым контентом - существуют минимальные размеры флотов, которые вообще имеет смысл посылать в Экспедиции. Меньшие флоты попросту не будут ничего привозить (см.ниже). Хотя, например, фармить ресурсы транспортами можно прямо со старта игры - учитывая плановую прибыльность обновленных Экспедиций это вполне имеет смысл, особенно \"шахтерам\"\n    Теперь время нахождения флота в Экспедиции влияет на шанс найти что-либо в процессе миссии - как на положительный шанс, так и на отрицательный\n    Изменены шансы происходящих событий (отношения расчитаны для обновленной Экспедиции в 1 час):\n      1. Шанс того, что в Экспедиции не произойдет ничего, увеличен примерно в 2,5 раза\n      2. Шанс потери флота уменьшен почти в 20 (!) раз\n      3. Шанс нахождения ресурсов увеличен на треть\n      4. Шанс нахождения флота уменьшен на треть\n      5. Шанс нахождения ТМ увеличен на треть\n    Изменено количество находимых в Экспедиции ништяков и оно теперь привязано к общей стоимости флота:\n      0. Количество ништяков может быть \"Нормальным\", \"Большим\" и \"Очень большим\". Соответственно меняются возможное количество находимых ништяков (идея упёрта с Огейма)\n      1. Количество ништяков отбалансированно для достижения планово-прибыльного характера Экспедиции и для компенсации изменения шанса происходящих событий\n      2. Количество находимых ТМ теперь вариабельно. Максимальное количество находимых ТМ - 10.000. Коэфцициент пересчета стоимости флота в ТМ зависит от курса ТМ (который, в свою очередь, зависит от скорости добычи ресурсов). Чем он выше - тем больше кораблей надо на шанс получения 1 ТМ. Для ориентировки - на х1 нужно запустить 10 эсминцев для получения 1 ТМ\n      3. Качество найденного флота теперь очень сильно зависит от качества исследовательского флота: невозможно найти корабли, дороже самого дорогого корабля в Экспедиции. Максимальная стоимость флота зависит от скорости добычи на сервере\n      4. Качество найденных ресурсов теперь вариабельно. В среднем находится 50% металла, 37,5% кристалла и 12,5% дейтерия. Однако в частном случае доли ресурсов могут варьироваться в очень широких пределах. Максимальное количество находимых ресурсов так же привязано к скорости добычи\n\n\n2013-12-18 07:02:13 38a4.20\n[@] Код\n    $time_now теперь определяется из $microtime\n    Новая процедура определения локальных путей для поддержки PHP 5.3+\n\n\n2013-12-14 12:11:30 38a4.19\n[~] Template/OpenGame\n    Добавлена возможность подгрузки серверных CSS для скинов\n\n\n2013-12-10 00:39:13 38a4.17\n[~] admin/Sypex Dumper\n    Sypex Dumper обновлен до версии 2.0.11\n\n\n2013-12-09 23:48:34 38a4.16\n[~] Скин/sn_space_blue\n    Обновлены файлы скина, в т.ч. для модулей\n\n\n2013-12-01 19:26:00 38a4.5\n[#] chat_advanced 2a4\n  (!) Требуется СН 38а4.4+\n  (+) Перманентный чат\n      Теперь чат прикрепляется кликом по ссылке \"Прикрепить\" под списком  онлайна\n      пользователей\n      Теперь прикрепленный чат можно открепить - ссылка \"Открепить\"  под  списком\n      онлайна\n      Теперь можно прикреплять  так  же  чат  Альянса.  Одновременно  может  быть\n      прикреплен только один чат - общий или Альянса\n  (+) Смайлики\n      Теперь смайлики размещены в отдельном попапе и не  занимают  лишнее  место.\n      Вызвать попап можно кликнув на смайлик слева от строки ввода сообщения\n  (+) Таймаут\n      Теперь при отключении чата по таймауту можно  обновить  окно  чата/миничата\n      соответсвующей ссылкой, которая появляется вместо строки ввода сообщения\n      Теперь таймаут так же убирает список игроков онлайн\n  (~) Игроки онлайн\n      Убрана  дублирующаяся  надпись  \"Игроки  онлайн\",  а   количество   игроков\n      перенесена в заголовок списка онлайна\n\n\n\n2013-11-30 22:16:03 38a4.3\n[#] chat_advanced 2a3\n    Изменен алгоритм смены фокуса, что бы окно  миничата  не  мешало  работе  в\n    основном окне\n\n\n2013-11-30 21:31:40 38a4.0 - Перманентный чат\n[#] chat_advanced 2a1\n    Требуется СН 38а4.0+\n    Реализован перманентный общий чат через iframe. Чат  делается  перманентным\n    при нажатии ссылки \"Фрейм\" на экране чата\n\n\n2013-11-30 18:28:32 38a3.20\n[@] Темплейты\n    Меню и навбар могут быть отключены параметрами в темплейте\n\n\n2013-11-30 14:28:51 38a3.19\n[@] general.php\n    sys_get_param_phone()\n\n\n2013-11-28 08:06:06 38a3.18\n[%] Флоты\n    Исправлена ошибка при отправке Капитана с миссией \"Транспорт\"\n\n\n2013-11-27 17:29:20 38a3.17\n[+] Смайлы\n    Добавлен смайл :sarcasm:\n\n\n2013-11-24 15:50:57 38a3.14\n[~] Служебное\n    Добавлена функция получения параметра в виде SQL-даты\n\n\n2013-11-24 10:40:40 38a3.13\n[%] Постройки\n    Исправлена ошибка отображения остатка ресурсов при входящих флотах\n\n\n2013-11-23 17:02:16 38a3.12\n[!] Админка/Платежи\n    В админку добавлен просмотр  платежей  с  фильтрами  (доступно  только  при\n    существующих и работающих платежных модулях)\n\n\n2013-11-21 13:29:23 38a3.9 - Интеграция нового скина\n[~] Темплейты\n    Изменена поддержка нескольких темплейтов\n\n\n2013-11-20 05:54:24 38a3.2 - Интеграция нового скина\n[!] Код\n    Добавлена поддержка нескольких темплейтов\n\n\n2013-11-17 23:56:40 38a3.1 - Интеграция нового скина\n[~] Локализация\n    Добавлены изменения для поддержки нового скина\n\n\n2013-11-16 16:55:19 38a3.0 - Интеграция нового скина\n[!] Интеграция нового скина\n\n\n2013-11-13 17:17:28 38a2.2\n[!] Админка/Метаматерия\n    Добавлена возможность начислить игроку ММ из админки\n\n[%] Платежи\n    Исправлена ошибка неначисления ММ по платежам\n\n\n2013-11-13 01:13:01 38a2.1\n[!] Метаматерия\n    Добавлены новые файлы для поддержки ММ\n\n\n2013-11-13 00:38:11 38a2.0 - Метаматерия. Вторая итерация\n[!] Метаматерия. Вторая итерация\n    Полностью переписаны модули платежей (см. ниже)\n    В игру добавлен модуль \"Ресурс Метаматерия\"\n    Метаматерия - новый  тип  ресурсов,  который  можно  приобрести  только  за\n    реальные деньги. Таким образом отделяются ресурсы, которые можно приобрести\n    внутри игры и ресурсы, которые можно только  купить.  Это  нужно  в  первую\n    очередь для добавления услуг и сервисов, которые требуют от движка платежей\n    в реальных деньгах - например, СМС-информирование об атаках\n    Так же это позволит добавить в игру больше возможностей для  взаимодействия\n    игроков, не опасаясь сильного дисбаланса  от  такого  взаимодействия  и/или\n    смещения экономики игры в сторону pay-2-win\n\n[@] Модули платежей\n    Модули полностью переписаны\n    Максимальная  унификация   модулей   -   все   общие   части   вынесены   в\n    модуль-родитель\n    Все сообщения внутри модуля генерируются во внутренних кодах СН\n    Добавлена подсистема конвертации внутренних кодов в коды  платежный  систем\n    (там, где это имеет смысл)\n    Все модули переделаны под работу с Метаматерией, а не ТМ\n\n\n2013-11-09 17:45:24 38a1.0 - Метаматерия. Первая итерация\n[!] Метаматерия\n    Первая итерация\n\n\n2013-11-07 21:27:28 38a0.6\n[%] Ресурсы\n    Исправлена невозможность сменить % производства шахт\n\n\n2013-11-07 20:49:56 38a0.5 - chat_advanced v1d4\n[#] chat_advanced v1d4\n    Интерфейс\n      Теперь ники подчеркиваются цветом ника и не  подчеркиваютя  иконки  (если\n      таковые есть в оформлении)\n\n[@] Рендер ников\n    Добавлена опция, позволяющая присвоить нику дополнительные CSS-классы\n\n\n2013-11-07 19:47:29 38a0.3 - chat_advanced v1d3\n[#] chat_advanced v1d3\n    Клавиатура\n      По \"Ctrl+Enter\" теперь так же отсылаются сообщения\n    Whisper\n      Команда \"/w\" теперь корректно работает с никами, в которых есть  пробелы.\n      Для этого ник нужно заключить в двойные  кавычки.  Подсказка  по  команде\n      изменена соответствующим образом\n      Изменен формат вывода шепота: \"(от кого) -> (кому)> (сообщение)\"\n      Теперь клик на имени собеседника в списке  сообщений  так  же  добавит  в\n      строку текущего сообщения команду \"/w <имя адресата> \". Так  будет  легче\n      переписываться с игроками, находящимися вне чата\n    Интерфейс\n      Все неявные элементы, клик по которым совершает  какое-либо  действие  на\n      странице  (например,  ник  в  списке  онлайна)  выделены  соответствующим\n      образом\n\n\n2013-10-29 10:56:05 38a0.1\n[~] Отпуск\n    Минимальный срок отпуска и таймаут  на  следующий  отпуск  уменьшены  до  1\n    недели\n\n\n2013-10-28 01:11:13 38a0.0\n[~] Отпуск\n    Минимальный срок отпуска увеличен до 2 недель\n    Введен таймаут на следующий отпуск - 2 недели с момента выхода из предыдущего отпуска\n\n\n2013-10-20 13:36:01 37d0 Project \"SuperNova.WS\" Release 37\n[!] Project \"SuperNova.WS\" Release 37 \"Year of Work\"\n\n\n2013-10-16 00:30:19 37c5\n[%] Рекорды\n    Исправлена ошибка на странице рекордов\n\n\n2013-10-15 20:38:50 37c4\n[+] Экономика/Плотность планеты\n    Новый алгоритм расчета цены изменения типа ядра планеты\n    Матрица цен теперь несимметрична: уменьшать плотность планеты дешевле, чем увеличивать\n    Матрица цен теперь кумулятивна: теперь переход через несколько типов ядра стоит как сумма переходов по одному типу\n    Матрица цен теперь сбалансирована: больше нет переходов со стоимостью, различающейся на два-три порядка\n\n[~] Артефакты\n    Добавлена ссылки на статьи Новапедии - открывается по клику на названии Артефакта\n    Выделены подчеркиваниями элементы-ссылки\n\n[%] Рекорды\n    Исправлена неработа рекордов если список скрытых пользователей пуст\n\n[@] Код\n    Почищен код от лишнего\n\n\n2013-10-14 23:21:26 37c3\n[%] Постройки\n    Исправлено неправильное отображение прироста производства ресурсов в информации о шахтах на планетах с нестандартным типом ядра\n\n\n2013-10-14 22:50:49 37c2\n[+] Ресурсы\n    Теперь на странице ресурсов можно изменить тип ядра планеты\n\n[%] Постройки\n    Исправлено неправильное отображение производства ресурсов в информации о шахтах на планетах с нестандартным типом ядра\n\n\n2013-10-13 23:15:27 37c1\n[%] Документация\n    Исправление обнаруженных неточностей\n\n\n2013-10-13 22:55:59 37c0 Project \"SuperNova.WS\" Release 37 Release Candidate 0\n[!] Project \"SuperNova.WS\" Release 37 RC0 \"Year of Work\"\n\n\n2013-10-13 15:18:47 37a13.13\n[+] Экономика/Плотность планеты\n    Теперь возможно сменить тип ядра планеты. Возможность доступна на экране управления планетой (Обзор планеты -> Управление)\n    Стоимость смены высчитывается динамически и зависит от того, насколько сильно отличается текущий тип ядра от желаемого\n\n[~] Новапедия\n    Переработана статья о плотности планеты с учётом изменившейся концепции\n\n\n2013-10-01 23:29:49 37a13.11\n[#] player_premium 1d3\n    Добавлена  индикация  остатка  времени  Премиума  в  пункт  меню   в   виде\n    прогресс-бара с цветовым кодированием:\n      Зеленый - осталось не менее 50% времени пермиума\n      Желтый - осталось меньше 50%, но не менее 25%\n      Оранжевый - осталось меньше 25%, но не менее 10%\n      Красный - осталось меньше 10%\n      Цвет фона - нет Премиума\n\n\n2013-09-29 20:55:32 37a13.9\n[%] Настройки\n    Исправлен мегабаг с нерабочей страницей Настроек Пользователя\n\n\n2013-09-29 04:31:44 37a13.8\n[#] player_premium 1d2\n    Добавлена индикация уровня премиума в меню\n\n[+] Строения\n    В очередь построек добавлена возможность использовать Наностроитель  -  при\n    наличии Артефакта на складе\n[+] Исследования\n    В очередь построек добавлена возможность использовать Эвристического чипа -\n    при наличии Артефакта на складе\n\n[~] Артефакты\n    Исправлена индикация неправильного уровеня здания в сообщении  об  успешном\n    применении Наностроителя\n\n\n2013-09-28 21:38:56 37a13.6\n[+] ТМ/Платежи\n    Добавлена индикация внутренних курсов системы\n    Теперь большую часть информационных элементов на странице можно свернуть\n\n\n2013-09-28 17:01:05 37a13.5\n[~] Обновление\n    В сообщение об обновлении сервера добавлена ссылка для Команды Сервера\n\n\n2013-09-28 16:49:21 37a13.4\n[~] Обновление\n    Теперь обновления можно запустить только из админ-консоли\n    Переделано обновление 37a13.2 что бы не вызывать отбоя\n\n\n2013-09-28 15:50:08 37a13.2\n[~] Обзор Планеты\n    Добавлено отображение типа ядра планеты\n[~] Обзор Империи\n    Добавлено отображение типа ядра планеты с цветовым кодированием:\n      Зеленый - тип ядра встречается очень часто\n      Желтый - тип ядра встречается часто\n      Оранжевый - тип ядра встречается редко\n      Красный - тип ядра встречается очень редко\n\n\n2013-09-28 15:01:07 37a13.1\n[~] Код/БД\n    Артефакты перенесены из таблицы игроков в таблицу юнитов\n    Удалены лишние поля Технологий из таблицы игрока\n\n\n2013-09-25 23:21:34 37a13.0\n[!] Экономика/Плотность планеты\n    Добавлен новый параметр планеты - плотность. Он определяет химический состав геосферы планеты и влияет на добычу ресурсов на ней\n    Плотность планеты лежит в диапазоне от 850 до 9250 кг/м3. Новые планеты имеют нормальное распределение\n    Существует 7 классов плотности - с уникальным набором коэфициентов добычи для каждого класса:\n      Ледяные планеты (<2000 кг/м3) - встречаются очень редко: очень низкая добыча металла, очень низкая добыча кристаллов, очень высокая добыча дейтерия\n      Силикатные планеты (2000=3250 кг/м3) - встречаются редко: очень низкая добыча металла, очень высокая добыча кристаллов и еще хорошая добыча дейтерия.\n      Каменные планеты (3250-4500 кг/м3) - встречаются часто: хорошая добыча металлов, высокая добыча кристаллов и низкая добыча дейтерия.\n      Стандарнтые планеты (4500-5750 кг/м3) - встречаются очень часто: хорошая добыча металлов, хорошая добыча кристаллов и хорошая добыча дейтерия.\n      Железнорудные планеты (5750-7000 кг/м3) - встречаются часто: очень хорошая добыча металлов, низкая добыча кристаллов и низкая добыча дейтерия.\n      Металлические планеты (5750-7000 кг/м3) - встречаются редко: отличная добыча металлов, низкая добыча кристаллов и низкая добыча дейтерия.\n      Тяжелометаллические планеты (>7000 кг/м3) - встречаются очень редко: великолепная добыча металлов, очень низкая добыча кристаллов и очень низкая добыча дейтерия.\n    Стартовая планета имеет плотность 5500 кг/м3 и принадлежит к 4-му классу плотности. Все луны имеют плотность 2500 кг/м3 и принадлежат ко 2-му классу плотности\n    Плотность планеты указывается на странице Обзора Планеты\n\n[+] Новапедия\n    Добавлена статья про плотность планеты\n\n\n2013-09-24 19:21:40 37a12.6\n[%] Регистрация\n    Исправлена ошибка регистрации\n\n\n2013-09-24 19:09:31 37a12.3\n[%] Регистрация\n    Исправлена ошибка регистрации\n\n\n2013-09-24 10:44:10 37a12.2\n[%] Обзор Империи\n    Исправлена несовместимость с модулем unit_captain\n\n\n2013-09-24 10:26:17 37a12.1\n[+] Обновление\n    Теперь на время обновления сервер отключается\n\n\n2013-09-24 10:14:25 37a12.0\n[+] Обзор Империи\n    Добавлена возможность управления производством шахт со страницы Обзора Империи\n    Дроп-дауны в колонке \"ИТОГО\" выставляют соответствующие проценты производства для зданий соответствующего типа сразу на всех планетах\n    Кнопки \"Сохранить\" продублированы в заголовке каждого типа юнитов и действуют сразу на всю страницу\n\n[~] Обзор Империи\n    Включен реальный перерасчет данных о планетах. Теперь очереди построек/флотов и количество ресурсов будут обновлятся в реальном времени\n\n[%] Ресурсы\n    Исправлено сообщение о делении на 0 при отсутствии складов\n\n\n2013-09-23 20:50:48 37a11.1\n[~] Админка\n    По умолчанию включена смена пользователем своего имени за ТМ\n\n\n2013-09-23 20:46:22 37a11.0\n[!] Настройки пользователя\n    Максимальная длина имени пользователя уменьшена до 32 символов\n    Добавлена  возможность  изменения  имени  пользователя  за  ТМ.   Стоимость\n    изменения - 100.000 ТМ\n    Игра сохраняет историю изменения имени пользователя. Только бывший владелец\n    может при желании вернуть себе старое имя - опять же за ТМ\n    Поиск по имени так же производится по старым именам. В случае, если  старое\n    имя  пользователя  соответствует  критериям  поиска,  в  результаты   будет\n    добавлена  еще  одна  строка,  в  которой   будет   указано   текущее   имя\n    пользователя, а после него в скобках  и  выделенное  цветом  -  старое  имя\n    пользователя. Никто не спрячется от своей истории!\n\n[~] Админка\n    Переменная настроек сервера 'game_user_changename' отвечает за  возможность\n    смены имени пользователя самим пользователем:\n      0 - смена имени запрещена\n      1 - смена имени разрешена и свободна\n      2 - смена имени разрешена, но стоит ТМ. Стоимость смены имени  указана  в\n      переменной 'game_user_changename_cost' (100.000 ТМ по умолчанию)\n\n\n2013-09-23 00:35:01 37a10.3\n[~] Артефакты\n    Теперь  после  операций  по   покупке/применению   Артефакта   страница   с\n    соответствующим списком открывается на последнем Артефакте\n[~] Сообщения\n    Теперь если есть URL перехода после сообщения есть возможность  перейти  на\n    соответствующую  страницу  по  ссылке  \"Продолжить\"  под   сообщением,   не\n    дожидаясь таймаута\n\n[%] Артефакты\n    Исправлена ошибка в работе Эвристического Чипа на луне\n    Исправлена очепятка в описании Эвристического Чипа\n\n\n2013-09-22 23:26:00 37a10.2\n[~] Постройки\n    Теперь на луне можно строить Нанофабрику\n\n\n2013-09-22 23:06:39 37a10.1\n[%] Артефакты\n    Добавлены картинки Артефактов\n\n\n2013-09-22 22:35:19 37a10.0\n[!] Артефакты\n    Добавлены два новых Артефакта: \"Эвристический чип\" и \"Наностроитель\"\n    Артефакты уменьшают на 1 час соответственно время текущего  исследования  и\n    время постройки/уничтожения текущего здания на текущей планете\n    Если  оставшееся  время  исследования/постройки/уничтожения  меньше  одного\n    часа, то Артефакт обнуляет время. Разница не переходит на следующий слот  в\n    очереди\n    Стоимость эвристического чипа составляет 20.000 ТМ\n    Стоимость наностроителя составляет 5.000 ТМ\n\n\n2013-09-21 10:09:33 37a9.33\n[#] payment_robokassa 0a1 - модуль платежей через агрегатора RoboKassa\n\n\n2013-09-21 09:10:40 37a9.30\n[~] ТМ/Платежи\n    Предварительная поддержка индикации обменных курсов\n\n\n2013-09-17 00:59:37 37a9.29\n[~] ТМ/Платежи\n    Изменена система бонусов за оптовые покупки ТМ:\n      от 50.000 ТМ - бонус 2% к количеству ТМ\n      от 100.000 ТМ - бонус 4% к количеству ТМ\n      от 200.000 ТМ - бонус 7% к количеству ТМ\n      от 250.000 ТМ - бонус 11% к количеству ТМ\n      от 375.000 ТМ - бонус 15% к количеству ТМ\n      от 500.000 ТМ - бонус 22% к количеству ТМ\n      от 750.000 ТМ - бонус 33% к количеству ТМ\n      от 1.000.000 ТМ - бонус 44% к количеству ТМ\n      от 1.250.000 ТМ - бонус 55% к количеству ТМ\n\n\n2013-09-16 22:16:19 37a9.28\n[#] payment_webmoney 0a4 - модуль приема платежей на кошельки WebMoney\n    Поддержка нескольких кошельков с разными валютами\n    Поддержка SUCCESS_URL\n\n\n[!] ТМ/Платежи\n    Понижена в 2,5 раза цена ТМ. Теперь за 1 гривну можно купить 2500 ТМ\n    Изменена система скидок при оптовой покупке. При покупке:\n      от 100.000 ТМ - бонус 5% к количеству ТМ\n      от 250.000 ТМ - бонус 10% к количеству ТМ\n      от 500.000 ТМ - бонус 20% к количеству ТМ\n      от 1.000.000 ТМ - бонус 30% к количеству ТМ\n      от 2.500.000 ТМ - бонус 40% к количеству ТМ\n      от 5.000.000 ТМ - бонус 50% к количеству ТМ\n    Размер лота (шага покупки) установлен в 2500 ТМ\n    Добавлена поддержка модулей с более чем одним количеством шагов при покупке\n    Добавлена поддержка мультивалютности\n    Добавлена поддержка SUCCESS_URL в платежных системах\n\n\n2013-09-16 20:34:58 37a9.25\n[~] ТМ/Платежи\n    Список доступных цен и список  скидок  строится  теперь  по  данным  модуля\n    sn_payment\n\n\n2013-09-15 17:29:00 37a9.23\n[~] ТМ/Платежи\n    Расширен функционал платежной страницы\n\n\n2013-09-15 16:17:01 37a9.22\n[~] ТМ/Платежи\n    Расширен функционал платежной страницы\n\n\n2013-09-15 11:36:48 37a9.18\n[~] ТМ/Платежи\n    Расширен функционал платежной страницы\n\n\n2013-08-24 20:05:48 37a9.17\n[%] Админка/Обновление\n    Исправлена ошибка удаления покинутых планет\n\n\n2013-07-21 21:17:06 37a9.14\n[%] Симулятор\n    Исправлено ошибочное увеличение уровней Технологий/Наемников/Губернатора на порядок\n\n\n2013-07-21 09:40:21 37a9.13\n[~] Исследования\n    Изменен алгоритм рассчета эффективного уровня лаборатории и необходимого времени исследования при настройке сервера \"Строить лабораторию во время исследования: Нет\"\n    Теперь при идущем исследовании блокируется постройка/уничтожение нано- и/или лабораторий на все планетах\n    Теперь блокируется попытка начать исследование на планете, где идет постройка/уничтожение нано- и/или лабораторий\n    Однако возможно начать исследование на другой планете. В таком исследовании не будут участвовать все планеты где происходить модификация нано- и/или лабораторий. При этом по окончании постройки/уничтожения время исследования не пересчитывается\n\n[%] Меню\n    Исправлена смена названия пункта меню \"Настройки\" на \"Опции\" при заходе на страницу Альянса\n[%] Вселенная\n    Исправлен показ места в статистике и показ кнопки-ссылки на статистику для скрываемых из статистики пользователей (по умолчанию к таким пользователям относится команда сервера)\n\n\n2013-07-21 05:36:01 37a9.12\n[~] Обновление\n    Ускорена процедура обновления\n\n\n2013-07-21 05:17:43 37a9.11\n[+] Симулятор\n    Добвлена поддержка Фортификатора для защищающегося флота\n\n[%] Экспедиции\n    Исправлена ошибка при которой можно было отправить экспедиций больше максимального количества\n[%] Вселенная\n    Исправлена ошибка отправки флота дальше, чем позволяет запас топлива\n[%] Боевой отчет\n    Исправлена ошибка открытия неправильной системы во Вселенной при клике на координаты в отчете\n[%] Симулятор\n    Исправлена ошибка при которой не учитывались уровни Адмиралов\n\n\n2013-07-15 23:39:34 37a9.10\n[~] Обновление\n    Ускорена процедура обновления\n\n\n2013-07-15 23:30:40 37a9.9\n[~] Админка\n    Временно ограничен доступ  к  некоторым  админским  страницам  всем,  кроме\n    админов\n\n\n2013-07-09 15:27:42 37a9.8\n[~] Чат\n    Добавлены смайлики\n\n\n2013-07-07 17:21:20 37a9.7\n[-] Шпионаж\n    Временно отключен вывод технологий при шпионаже\n\n\n2013-07-07 10:49:22 37a9.5\n[~] Чат\n    Добавлены смайлики\n\n\n2013-07-06 20:10:42 37a9.3\n[~] Интерфейс\n    Добавлена подсветка ника уровня \"Разработчик\"\n\n\n2013-07-06 19:53:44 37a9.1\n[~] Админка\n    Добавлен еще один уровень прав доступа \"Разработчик\"\n\n\n2013-07-06 18:12:51 37a9.0\n[~] Обновление\n    Добавлены констраинты в некоторые таблицы\n\n\n2013-07-04 21:51:35 37a8.25\n[~] Локальное время\n    Повышена устойчивость механизма к ошибкам на стороне клиента:  неправильный\n    часовой пояс, неправильные настройки DST  в  операционной  системе,  сильно\n    отстающие/спешащие часы итд\n\n\n2013-07-03 20:45:56 37a8.24\n[~] Исследования\n    Включен расчет статистики по исследованиям (не путать с рассчетом опыта!)\n\n\n2013-07-01 16:17:44 37a8.23\n[%] Исследования\n    Исправлена ошибка начисления слишком большого количества XP при наличии бонусов к технологии\n\n\n2013-07-01 03:40:43 37a8.22\n[%] Обзор Империи\n    Исправлен вывод производства энергии. Теперь в случае недостатка энергии вместо \"0\"/\"-1\" показывается реальная недостача энергии\n\n\n2013-07-01 03:12:55 37a8.21\n[~] Исследования\n    Включены квесты на исследования\n    Награда в ресурсах за выполнение квеста пока начисляется на главную планету\n    Награда за уже выполненные квесты будет начислена при следующем изменении уровня соответствующей технологии\n\n\n2013-07-01 02:31:20 37a8.20\n[~] Исследования\n    Включено начисление опыта за исследования\n\n\n2013-07-01 01:48:30 37a8.19\n[%] Локальное время\n    Исправлены ошибки расчета длительности построек в sn_timer.js\n\n[@] Код\n    Изменены некоторые SQL-запросы\n\n\n2013-05-08 03:06:09 37a8.18\n[~] Рекорды\n    В несколько раз ускорена страница Рекордов\n\n\n2013-05-07 12:45:19 37a8.17\n[~] Обзор Империи\n    Уменьшено время рендера страницы за  счет  перехода  на  симуляцию  обсчета\n    планет\n\n[@] Код\n    Добавлен простенький бенчмарк\n\n\n2013-05-07 04:10:52 37a8.16\n[~] Термоядерная электростанция\n    Теперь ТЭС не использует ресурсы со склада,  а  оперирует  только  балансом\n    производства дейтерия. Т.е. ТЭС работает только при  положительном  балансе\n    производства дейтерия И генерации энергии  одновременно.  Это  сделано  для\n    того, что бы оставленная \"без присмотра\" ТЭС с  отрицательным  балансом  по\n    дейтерию не выжрала весь ресурс со склада\n    Как следствие - ТЭС не отключается при положительном  балансе  производства\n    дейтерия и энергии, даже если количество дейтерия на планете равно  0.  Это\n    упростит своз ресурсов с планет, на которых энергия генерируется только  на\n    ТЭС\n[~] Страница \"Ресурсы\"\n    Теперь при эффективности добычи ресурсов менее 100%  вместе  с  актуальными\n    значениями добычи в ячейку добавляется рассчетное значение добычи в круглых\n    скобках. Это упростит балансировку производсва при недостатке ресурсов\n[~] Обновление производства ресурсов\n    Убрана задержка в обновлении информации о производстве ресурсов\n\n[%] Сообщение\n    Исправлена ошибка отправки сообщения об окончании строительства  на  верфи.\n    Теперь сообщение отправляется один раз, а не каждый  раз,  когда  на  верфи\n    строится юнит\n\n\n2013-05-03 15:59:10 37a8.15\n[~] Обзор Империи\n    Теперь не показываются \"пустые\" строчки для юнитов, которых нет в Империи\n    Для производства ресурсов и складов используется структура 'caps' планеты\n    Правильно считается общее количество полей на всех объектах Империи\n[~] Производство ресурсов\n    Естественное производство дает 100% ресурсов даже при недостатке энергии\n\n\n2013-05-03 14:08:48 37a8.13\n[~] Обновление\n    Изменено условие в старом патче для совместимости с новыми версиями\n\n\n2013-05-01 08:49:57 37a8.12\n[%] Рекорды\n    Исправлено отображение рекордов по технологиям\n\n\n2013-04-30 09:57:21 37a8.11\n[%] Админка\n    Исправлена невозможность бана\n\n\n2013-04-30 01:13:49 37a8.10\n[~] Обновление\n    Ускорено обновление для 37a8.7\n\n\n2013-04-29 05:05:52 37a8.8\n[%] Исправлена ошибка обновления\n\n[@] Обновление\n    Удалена колонка `que` из таблицы `users`\n\n\n2013-04-29 03:17:35 37a8.7 - ОЧЕРЕДЬ!\n[!] В этой версии не работает:\n    1. Квесты на исследования\n    2. Начисление экспы и ТМ за исследования\n    3. Не приходят ЛС по завершению исследования\n    4. Могут не работать рекорды по технологиям и статистика по технологиям\n[!] Очередь\n    Обновленная система очереди\n\n[%] Локализация/Английский\n    Исправлены сообщения боевого отчета\n\n[@] Код\n    Исследования и очередь исследований перенесены в соответствующие таблицы\n\n\n2013-04-20 21:49:14 37a8.6\n[@] Код\n    premium перенесен из таблицы powerup в таблицу unit\n    Расчеты уровня премиума вынесены в модуль\n\n\n2013-04-20 20:01:30 37a8.4\n[@] Код\n    infos.php теперь использует прямое обращение к production юнита и подмассиву modifiers\n    eco_bld_structures.php теперь использует обращение к подмассиву modifiers\n    mercenaries и plans перенесены из таблицы powerup в таблицу unit\n\n\n2013-04-13 17:22:24 37a8.2\n[@] Топбар\n    Исправлено отображение объема складов\n\n\n2013-04-13 16:32:59 37a8\n[@] Ресурсы\n    Исправлено отображение загрузки складов\n\n\n2013-04-13 16:15:17 37a7 Переработка системы бонусов #1 - Энергокризис!\n[!] Экономика\n    Изменен  алгоритм  расчетов  бонусов  добычи  ресурсов.  Список   изменений\n    приводится ниже:\n    1. Бонусы на добычу ресурсов улучшают так же базовую добычу на планете\n    2. Бонусы на добычу ресурсов так же увеличивают  потребление  сопутствующих\n    ресурсов - дейтерия (для Термоядерной Электростанции) и энергии  (для  всех\n    остальных шахт)\n    3. Бонусы на добычу ресурсов улучшают так же выработку энергии на спутниках\n    4.  Недостаток  энергии  влияет  на  базовую  добычу  -  т.е.  она   теперь\n    масштабируется в зависимости эффективности производства\n\n[@] Код\n    Константа MAX_OVERFLOW исключена из кода\n    Обработан eco_get_planet_caps и связанные процедуры\n\n\n2013-04-07 10:14:18 37a6.5\n[%] Локализация/Русский\n    Исправлены некоторые очепятки\n\n\n2013-04-07 09:55:05 37a6.3\n[%] Чёрный Рынок\n    Исправлена невозможность продать/купить ТОП на ЧР\n\n\n2013-04-06 18:09:29 37a6.1\n[+] Админка/Список игроков\n    Добавлены  две  колонки  со  сведениями  о  реферралах  игрока:  количестве\n    привлеченных игроков и количество заработанных ими ТМ\n\n\n2013-04-03 07:20:19 37a6.0\n[@] MVC\n    $sn_i18n['pages'] -> $sn_mvc['i18n']\n\n\n2013-04-03 03:25:19 37a5.8\n[#] Локализация/Узбекский\n    Добавлены переводы на узбекский для модулей: chat_advanced, player_premium,\n    player_race, player_race_units, unit_captain и unit_hope\n\n[+] Локализация/Узбекский\n    Добавлен перевод на узбекский от Акмалжона Мусаева\n\n\n2013-03-27 13:25:01 37a4.3\n[~] Скины\n    Заменена картинка \"Черетеж ТОП\"\n\n[@] Код\n    Добавлена функция вычисления случайного числа, распределенного нормально\n\n\n2013-03-13 19:43:31 37a4.2\n[@] Обслуживание\n    Процедура обслуживания теперь так же удаляет боевые отчеты  UBE  старше  60\n    дней\n\n\n2013-03-13 17:32:13 37a4.1\n[#] chat_advanced 1d0\n    Клик на имени игрока в списке онлайна теперь всегда добавляет команду  \"/w\"\n    в начало сообщения - а не в конец, как ранее\n    Скорость обновления  в  AJAX  части  чата  теперь  регулируется  переменной\n    'chat_refresh_rate'\n    Теперь игроки из онлайн-списка  исчезают  сразу  после  выхода  из  чата  -\n    таймаут попадания в список установлен как удвоенный 'chat_refresh_rate',  а\n    не как 'chat_timeout' ранее и вычисляется по дополнительному полю, а не  по\n    `chat_player_activity` как ранее\n\n\n2013-03-12 23:10:57 37a3.21\n[%] Флоты\n    Исправлена ошибка при приглашении в САБ самого себя\n\n\n2013-03-12 23:04:37 37a3.20\n[%] Флоты\n    Исправлено ошибочное сообщение \"неисследованное пространство\"  в  заголовке\n    страницы\n\n\n2013-02-23 05:09:26 37a3.10\n[#] ТОП (Тяжелая Орбитальная Платформа) - unit_hope 0b0\n    Требуется СН не ниже 37a3.9\n    Тяжелая Орбитальная Платформа (ТОП) - специальный юнит, предназначенный для борьбы с тяжелыми и сверхтяжелыми кораблями. Особенно эффективен против Уничтожителей\n    ТОП является спутником - т.е. строится  на  верфи  и  находится  на  орбите планеты/луны\n    Для постройки требуется Чертеж (цена 50.000 ТМ)\n\n[~] Новапедия\n    Теперь  при  просмотре  стационарных  кораблей  (спутников  и   орбитальных\n    платформ) не показываются ненужные характеристики двигателей\n\n[@] Юниты\n    Добавлены константы и столбцы таблиц для ТОП\n\n\n2013-02-23 02:51:29 37a3.9\n[%] unit_captain 0d0\n    Исправлена ошибка при отсылке Капитана в экспедицию\n\n\n2013-02-11 15:57:49 37a3.6\n[@] Модули\n    Изменен алгоритм слияния массивов переменных в модулях\n\n\n2013-01-27 13:47:37 37a3.4\n[%] Время\n    Исправлены ошибки расчета времени в JS\n\n\n2013-01-27 10:40:55 37a3.3\n[%] Навбар\n    Исправлена ошибка с перепутанным серверным и локальным временем\n\n\n2013-01-27 10:12:06 37a3.2\n[%] Навбар\n    Исправлено смещение надписи в индикаторе исследования влево\n[%] Настройки пользователя\n    Названия групп настроек отцентрированы\n\n\n2013-01-27 09:53:58 37a3.1\n[%] Новости\n    Исправлена ошибка генерации локальной даты новости\n\n\n2013-01-27 09:44:53 37a3\n[#] Расширенный чат - chat_advanced v1c2\n    Требуется СН не ниже 37a3\n    Добавлена поддержка локального времени в чат и историю чата\n    Теперь можно использовать команды  при  выбранном  цвете  сообщения.  Ранее\n    такие команды не воспринимались системой чата\n    Произведена замена цветов для лучшей читаемости сообщений: red  ->  maroon,\n    blue -> cyan\n    Цвет green оставлен для пользвателей, а подтверждающие системные  сообщения\n    используют цвет lime - как и в остальном интерфейсе сервера\n    Системные и приватные сообщения теперь выделяются жирным шрифтом\n\n[~] Стандартный чат\n    Произведена замена цветов для лучшей читаемости сообщений: red  ->  maroon,\n    blue -> cyan\n\n\n2013-01-27 08:45:40 37a1\n[!] Локальное (клиентское) и серверное время\n    Изменена процедура замера разницы между  локальным  и  серверным  временем.\n    Теперь она производится не каждый раз при обращении к серверу, а один раз и\n    сохраняется в БД. При заметном изменении разницы  можно  заново  произвести\n    эту  операцию,  установив  галочку  \"Замерить   разницу   между   локальным\n    (клиентским) и серверным временем\"  на  странице  настроек  пользователя  и\n    сохранив настройки. Замер будет произведен  при  следующем  открытии  любой\n    страницы игры\n    Теперь вместо локального или серверного времени одновременно показывается и\n    локальное, и серверное время в следующих местах:\n    1. В навбаре - часы реального времени\n    2. При отправке флота на экране выбора точки назначения - в  графе  времени\n    прибытия и возвращения флота\n    3. При отправке флота на экране подтверждения отправки -  в  графе  времени\n    прибытия и возвращения флота\n    Теперь вместо серверного времени показывается локальное в следующих местах:\n    1. В событиях навбара (флоты и экспедиции)\n    2. В новостях\n    3. На экране флотов в полете\n    4. На экране обзора планеты в списке летящих флотов\n    5. В чате и истории чата\n    6. В боевых отчетах\n    7. В сообщениях\n\n[~] Навбар\n    Переформатирован навбар для добавления локального и серверного времени\n    Теперь в событиях навбара (флоты и экпедиции) показывается тип  объекта,  к\n    которому относится событие (планета или луна)\n\n[@] Переписаны некоторые процедуры fleet.js на использование jQuery\n\n\n2013-01-07 20:26:36 37a0.0\n[%] Админка/Список пользователей: Убрана отладка\n\n\n2013-01-04 12:23:36 36d1\n[~] Регистрация: Пароль теперь указывается в отдельной строке\n[~] Восстановление пароля: Новый пароль теперь указывается в отдельной строке\n\n\n2013-01-04 12:11:49 36d0.5\n[~] Админка/Бан:  Теперь  срок  бана  инкрементальный  -  каждый   новый    бан\n    увеличивает срок блокировки, а не заменяет его\n[~] Админка/Список игроков: Для забаненных игроков при наведении на ячейку бана\n    выскакивает тултип с ником забанившего, датой и причиной бана\n\n[%] Наемники: Исправлена ссылка на дерево развития\n[%] Чат: Исправлена ошибка появленя всех сообщений пользователя  в  чате  после\n    его удаления\n[%] Отпуск: Исправлено сообщение об ошибке на загрушке отпуска\n\n\n2013-01-03 19:45:44 36d0.4\n[%] Админка/Обслуживание: Исправлена ошибка обслуживания базы\n\n\n2013-01-03 19:40:29 36d0.3\n[%] Админка/Обслуживание: Исправлена ошибка обслуживания базы\n\n\n2012-12-30 18:02:46 36d0 36d0 - Project \"SuperNova.WS\" Release 36\n[!] Project \"SuperNova.WS\" Release 36 \"UBEv4 captains chat Happy New Year 2013!!!\"\n\n\n2012-11-16 14:03:38 36a1.12\n[@] Код\n    Почищен код\n\n2012-11-09 21:39:16 36a1.11\n[%] Чёрный Рынок\n    Исправлена несовместимость с Opera 12.x\n\n\n2012-11-04 16:56:30 36a1.7\n[%] UBEv4\n    Исправлен механизм \"sneak defense\" при Удержании\n    Исправлена  генерация  случайного  числа  для  проверки   взрыва   ЗС   при\n    Уничтожении\n    Шанс уничтожения луны теперь всегда лежит в пределах 1%-99%\n    Исправлен возврат флота при взрыве СН в миссии Удержание\n    Убрана отладка из кода UBEv4\n\n\n2012-11-03 16:32:57 36a1.6\n[~] Исследования\n    Добавлен патч для медленных MySQL серверов\n\n\n2012-11-03 12:02:30 36a1.4\n[~] Чат\n    Переписана работа с сообщениями - опять отображаются спецсимволы HTML\n\n\n2012-11-03 11:08:38 36a1.3\n[~] Чат\n    Добавлены иконки для состояний игроков\n\n\n2012-11-01 18:05:41 36a1.2\n[~] Чат\n    Небольшое изменение в смайликах\n\n\n2012-11-01 15:38:07 36a1.1\n[~] Чат\n    Список смайликов теперь  генерируется  автоматически  из  всего  доступного\n    списка\n    При открытии окна чата курсор позиционируется в строку набора сообщения\n    Реформатирование HTML-кода страницы чата\n[~] UBEv4\n    Отключена отладка запросов боя\n\n[%] Чёрный Рынок\n    Исправлена уязвимость в Скупщике лома\n[%] Навбар\n    Исправлена ошибка смены планеты на preMVC-страницах\n\n\n2012-10-24 00:48:17 36a1.0\n[~] Чат\n    Переделан в preMVC\n    Три файла чата интегрированы в один\n\n[@] Код\n    Поддержка опций движка в глобальном объекте $supernova\n    Поддержка опций на страницах в подмассиве 'pages'\n\n\n2012-10-18 17:52:01 36a0.34\n[~] Чат\n    В чате Альянса в нике участника теперь не указывается Альянс\n\n[%] Обзор Империи\n    Исправлена ошибка смещения фона для производящих зданий\n\n\n2012-10-15 14:47:04 36a0.33\n[%] Флоты/Фаланга\n    Исправлена ошибка сканирования пустого места во Вселенной\n\n\n2012-10-15 14:38:59 36a0.32\n[~] Новости\n    На странице Обзора планеты добавлена подсказка как закрыть окно со  свежими\n    новостями\n\n\n2012-10-13 19:12:11 36a0.31\n[~] UBEv4\n    Изменен расчет поля  обломков.  Раньше  все  100%  обломков  с  кораблей  и\n    выброшенных за борт ресурсов оказывались в поле обломков. Теперь на  орбите\n    оказывается от 30% до 70% выброшенных за борт ресурсов  и  от  20%  до  40%\n    обломков кораблей.  В  детерминированном  симуляторе  процент  обломков  на\n    орбите всегда равен 30%, а обломки, выброшенные из трюма всегда  составляют\n    50% от потерь\n\n\n2012-10-12 21:37:52 36a0.30\n[%] Флоты\n    Исправлена ошибка при отправке ракет\n\n\n2012-10-12 15:23:42 36a0.27\n[~] Документация\n    Добавлено описание модуля Капитанов в changelog.txt\n\n\n2012-10-12 15:12:48 36a0.26\n[#] Модуль \"Капитаны\"\n    Требуется СН не ниже 36a0.24\n    Капитан - это опытный командующий, который летает с флотами и за счет более тонкого управления флотами улучшает эффективные характеристики всех кораблей\n    Найм и управление Капитанами осуществляется через пункт меню \"Капитаны\" (сразу под \"Наемниками\")\n    Каждый Капитан привязан к определенной планете или луне. Нельзя иметь двух Капитанов на одном небесном теле. Капитан, летящий с флотом, все равно считается привязанным к планете\n    Капитана можно перевозить с одной планеты на другую миссией \"Передислокация\". При этом на время перелёта Капитан считается привязанным сразу к обоим планетам - стартовой и финишной\n    С флотом можно отправить только одного Капитана\n    При гибели флота Капитан так же погибает. Под \"гибелью флота\" подразумевается уничтожение всех кораблей флота. Это верно как для атакующих флотов, так и для флотов, стоящих в удержании\n    Капитан на планете не участвует в защите планеты при атаке - этим занимается Фортификатор. Зато при полном уничтожении всего планетарного флота такой Капитан не погибнет\n    За каждый выигранный простой бой (САБы и миссия \"Уничтожить\" не считаются) Капитан атакующего флота получает 1 пункт опыта. За \"победы\" над неактивными игроками опыт не начисляется. Так же не начисляется опыт, если бой закончился выигрышем атакующего за 1 раунд\n    Капитаны всегда улучшают характеристики кораблей своего флота - даже если участвуют в бою, за который они не получат опыта: атака на неактивных игроков, удержание, САБ, уничтожение луны и т.д.\n    При наборе определенного количества опыта Капитан получает новый уровень. Чем выше уровень - тем больше опыта нужно для получения следующего уровня\n    Повышение в уровне дает возможность улучшать умения Капитанов. Каждый уровень умений дает 1% к базовому значению соответствующей характеристики\n    Умения Капитана включают бонусы к щитам, броне и атаке\n    Уровни Капитана вкладываются в умения один раз и навсегда - поэтому заранее тщательно планируйте развитие своего Капитана\n\n\n2012-10-12 14:12:32 36a0.25\n[~] Флоты\n    Изменены ограничения на отправку Шпионов. Их можно посылать  в  одиночку  в\n    миссии Шпионаж, Передислокация и Транспорт. Во все остальные миссии Шпионов\n    тоже можно отсылать - но только в сопровождении других кораблей\n\n\n2012-10-11 16:15:31 36a0.21\n[+] Меню\n    Редизайн меню\n    Изменен порядок расположения пунктов\n    Добавлены иконки\n\n\n2012-10-11 13:36:48 36a0.20\n[~] Скины\n    Добавлены иконки меню в скины EpicBlue и supernova-ivash\n\n\n2012-10-11 13:34:53 36a0.19\n[~] Новости\n    Количество новостей ограничено 20-ю самыми свежими\n[~] Меню\n    Высота пункта меню увеличена до 16 пикселов\n    Размер иконки теперь ограничен 14 пикселами в высоту\n\n[@] Код\n    Переформатирован HTML-код обзора планеты\n\n\n2012-10-10 18:13:02 36a0.18\n[~] Обзор Империи\n    Уровни Капитанов указываются в списке юнитов\n    На заднем фоне ячейки с уровнем выводится прогресс-бар развития Капитана с цветовым кодированием:\n      Пустая ячейка - Капитан не нанят, либо только что получил уровень\n      Красный прогресс-бар - до следующего уровня осталось больше 50% опыта\n      Оранжевый - не меньше 50% опыта, но меньше 80%\n      Желтый - не меньше 80% опыта\n      Зеленый - в следующем бою Капитан получит новый уровень\n[~] Флоты в полёте/Обзор планеты\n    В списке флотов для своих флотов если во флоте есть Капитан перед количеством кораблей во флоте высвечивается \"*\", а в попапе состава показывается его уровень\n\n[%] UBEv4\n    Исправлена ошибочная попытка записать информацию о башинге при уничтоженной луне\n[%] Альянсы\n    Исправлена надпись при отправке письма членам Альянса\n\n\n2012-10-10 15:01:27 36a0.17\n[%] Обзор Империи\n    Исправлена пропажа индикации количества строящихся зданий\n\n\n2012-10-10 14:02:11 36a0.16\n[!] Модули\n    Поддержка модуля \"Капитаны\"\n\n[%] Навбар\n    Исправлена ошибка неправильного цветового кодирования остатков энергии в планетбаре\n[%] UBEv4\n    Исправлено появление дробных значений при расчете атаки в раунде\n\n\n2012-10-09 19:40:07 36a0.15\n[~] Навбар\n    Изменена разметка навбара, что бы его не перекашивало в случае вывода блока информации до него\n[~] UBEv4\n    Атака давно неактивных игроков не приносит рейдового опыта\n\n[%] UBEv4\n    Исправлена ошибка расчета бонусов игроков и флотов\n    Исправлена ошибка исчезновения части флота, которая не пострадала в бою, когда во флоте есть какие-то потери\n    Исправлена ошибка записи в таблицу при Удержании с РМФ\n\n[@] БД\n    В таблицу флотов добавлены идентификаторы планет старта/назначения\n\n\n2012-10-07 12:56:38 36a0.14 UBEv3 clearence\n[~] UBEv4\n    Отключена отладка боевого движка в симуляторе\n    Удалены файлы и неиспользуемые функции старого боевого движка\n\n\n2012-10-07 12:39:21 36a0.13\n[%] UBEv4\n    Исправлена ошибка вычисления количества лута с планет\n    Исправлены перепутанные строки локализации в боевом отчете для добычи\n[%] Апдейтер\n    Добавлен отсутствующий файл с апдейтами БД с версий по 32ю включительно\n\n\n2012-10-05 19:09:10 36a0.12\n[%] UBEv4\n    Исправлена ошибка потери части флота при количестве типов кораблей во флоте\n    более 1\n\n\n2012-10-05 17:32:51 36a0.11\n[%] Просмотр отчета\n    Исправлена ссылка \"Мои отчеты\"\n\n\n2012-10-04 20:47:49 36a0.10\n[~] Чат\n    Добавился новый BBCode \"s\" - зачёркнутый текст\n\n[%] UBEv4\n    Исправлена ошибка с отображением дейтерия в обломках в письмах\n\n[@] Код\n    Все ссылки на старый файл отчетов rw.php заменены на новые\n\n\n2012-10-04 10:50:33 36a0.9\n[@] Документация\n    Обновлен конвертер txt2html\n\n\n2012-10-04 10:17:47 36a0.8\n[~] UBEv4\n    В отчете координаты планет теперь являются ссылками на Вселенную\n\n\n2012-10-04 10:06:08 36a0.7\n[@] Модули\n    Устранена ошибка на случай, если модуль не возвращает темплейта. Такого  не\n    должно происходить в нормальной ситуации\n[@] Код\n    UBEv4 добавлен в транк\n\n\n2012-10-03 21:15:44 35d0 - Project \"SuperNova.WS\" Release 35\n[!] Project \"SuperNova.WS\" Release 35 \"MVC race reparse teleportation recycle\"\n\n\n2012-10-03 02:15:37 36a0.2\n[@] Код\n    Рефакторинг UBEv4\n\n\n2012-10-02 23:44:32 36a0.1\n[~] Симулятор\n    Добавлена запись отчетов в режим случайного боя\n    Добавлена проверка чтения/записи\n\n\n2012-10-02 22:14:31 36a0.0 - Introducing UBEv4\n[!] UBEv4\n    Написанн с нуля боевой движок и боевые отчеты\n\n    Особенности подготовки к бою:\n    1. Бой теперь считается не по $time_now, а по времени прилета  флота  -  на\n    случай, если бой сильно отложенный. Например, при сбоях движка  или  низкой\n    активности  сервера.  Так  будут  отработаны  корректно  все  удержания   в\n    правильное время\n\n    Особенности хода боя:\n    1. Броня не регенерируется между раундами\n    2. Если броня упала  ниже  75%  -  корабль  имеет  шанс  взорваться  равный\n    проценту от общего здоровья\n    3. Новый механизм боя: подлов атакующего или sneak defense. Если в САБе и в\n    удержании участвуют флоты одного и того же  игрока,  то  прилетающие  флоты\n    этого игрока будут сражаться на стороне защитника. Аккуратно смотрите, кого\n    приглашает в САБ. Хе-хе\n\n    Особенности подведения итогов боя:\n    1. Если в бою участвует хотя бы один флот Админов с любой стороны - лом  не\n    выпадает ни с кого!\n    2. Возвращение обломков с оборонных сооружений не производится\n    3. В миссии  \"Уничтожить\"  шанс  уничтожения  флота  от  взрыва  одного  из\n    кораблей при попытке уничтожить луну теперь так же  зависит  от  количества\n    гравидвигателей во флоте - чем их больше, тем шанс выше\n    4. В миссии \"Уничтожить\" корабли могут взорваться даже в  случае  успешного\n    уничтожения  луны.  Как  и  раньше,  подрыв  кораблей   с   гравидвигателем\n    уничтожает весь флот\n    5. Теперь в рейдовый опыт засчитываются исключительно одиночные  атаки.  Ни\n    \"Удержание\", ни \"САБ\" не засчитывается. Т.е. вообще не засчитываются -  вне\n    зависимости от результата боя\n    6. Количество свободных полей на луне зависит от её размера и  определяется\n    по формуле Размер/1000 с округлением вверх до целого\n\n    Боевой отчет теперь состоит из трёх частей: \"Основная  информация  о  бое\",\n    \"Боевые потери\" и лог раундов\n    \"Основная информация о бое\" показывает:\n    1. Время проведения боя (если доступно)\n    2. Место боя (если доступно) - координаты планеты, её тип и имя\n    3. Результат боя (выигрыш атакующего, ничья, проигрыш атакующего)\n    4. Обломки на орбите\n    5. Шанс образования луны и результат такой попытки\n    6. (Для миссии  \"Уничтожить\")  Состояние  кораблей  с  гравидвигателями  по\n    итогам боя. Шанс уничтожения луны оставшимися кораблями и  результат  такой\n    попытки. Шанс взрыва кораблей и итог миссии\n\n    Раздел \"Боевые потери\" показывает:\n    1. (На планетах) Количество восстановленных боевых сооружений\n    2. Общие потери боевых единиц каждого из участвующих в бою игроков. Если  у\n    одного игрока участвовало в бою несколько флотов - будут показаны суммарные\n    потери по всем флотам. Это верно для всех параметров в  этом  разделе.  Для\n    планетарной обороны в потери не включаются восстановленные единицы\n    3. (В случае победы атакующих) Количество ресурсов, вывезенных  с  планеты.\n    Для  планеты  это  будет  положительное  число,  для  атакующих  флотов   -\n    отрицательное\n    4. (Для флотов) Количество ресурсов  потерянных  из-за  уменьшения  емкости\n    трюмов вследствии уничтожения части флота. Эти ресурсы рассматриваются  как\n    \"боевые потери\" - они  плюсуются  к  обломкам  на  орбите  и  к  потерям  в\n    пересчете на ресурсы\n    5. Общие потери в пересчете на ресурсы. Включает стоимость боевых единиц на\n    момент боя, вывоз с планеты и ресурсы, потерянные из-за уменьшения трюмов\n    6. Общие потери в ресурсах в пересчете на металл по курсу Черного Рынка  на\n    момент проведения боя. Писькомерка для сравнения\n\n    \"Лог раундов\" показывает результаты расчета каждого раунда для всех флотов\n    1.  Показывает  координаты  и  тип  планеты,  с  которой  прилетели   флоты\n    атакующих/защитников\n    2.  Расширено  количество  информации  о  боевых  подраздеениях   Добавлена\n    информация о \"Пробое\" и \"Уроне\". \"Пробой\" - атака, которая пришлась на щиты\n    и была ими поглощена (или пропущена - см. ниже). \"Урон\"  -  атака,  которая\n    пришлась на броню\n    3. Цветовое кодирование информации о подразделениях:\n       Зеленый означает, что вся атака в раунде поглощена щитами\n       Желтый - часть атаки пробила щиты (\"пробой\") и нанесла урон по броне, но\n       при этом ни одна боевая единица не уничтожена\n       Оранжевый - один или более боевых единиц уничтожено\n       Красный - все оставшиеся боевые единицы уничтожены в этом раунде\n       Число  в  скобках  в  столбце  потерь  -   количество   боевых   единиц,\n       взорвавшихся в раунде из-за фатальных повреждений\n\n    Доработан симулятор для поддержки изменений в UBEv4:\n    1. Стандартный режим работы симулятора - полная определенность  результатов\n    в зависимости от начальной конфигурации (галочка \"Симуляция\" включена)\n    2. Добавлен второй режим работы  -  недетрминированный  симулятор  (галочка\n    \"Симуляция\" отключена). В этом режиме работы  проводится  полная  симуляция\n    боя (включая образование луны) с применением генератора случайных  чисел  -\n    т.е. так, как происходил бы обычный бой. В  этом  режиме  результаты  могут\n    сильно отличаться от симуляции к симуляции. Так же в этом режиме происходит\n    запись боевого отчета с результатом симуляции в БД\n    3. В  стандартном  режиме  если  шанс  образования  луны  больше  1  всегда\n    образуется луна со средним размером для данного шанса\n\n\n2012-10-03 20:59:09 35c11\n[%] Альянсы\n    Исправлена ошибка неудаления тэга Альянса у игрока при его исключении\n\n\n2012-10-02 22:22:31 35c10\n[~] Инициализация\n    Изменен алгоритм определения корневого каталога СН. Теперь движок корректно\n    работает в каталогах-симлинках\n\n\n2012-09-22 21:17:22 35c9\n[~] Поиск\n    Добавлена подсказка\n    Убраны игроки-Альянсы в результатах поиска по игрокам/планетам\n\n\n2012-09-22 17:43:53 35c8\n[%] Вселенная\n    Исправлена ошибка невозможности отправки переработчиков на  поле  обломков,\n    если общая емкость переработчиков от количества ресурсов в поле  составляет\n    менее 1% (отображается как 0% в попапе)\n\n\n2012-09-22 00:54:26 35c6\n[!] Поиск\n    Полностью переписан поиск\n    Добавлена сортировка по Альянсу, имени игрока, имени планеты\n    Настройка сервера \"Скрывать ссылки на  ЛС\"  теперь  распространяется  и  на\n    результаты поиска\n\n[~] Статистика\n    Немного оптимизирован темплейт статистики\n\n[%] Статистика\n    Исправлена ошибка непозиционирования статистики Альянсов\n\n\n2012-09-21 11:37:48 35c5\n[%] Исследования\n    Исправлено возможный двойной возврат ресурсов при отмене исследования\n\n\n2012-09-20 15:16:16 35c4\n[~] Админка/Утилиты\n    В шифрование паролей добавлен генератор паролей\n\n\n2012-09-20 03:33:09 35c3\n[+] Смайлы\n    В чат добавлены следующие смайлы: nea, ups, quote, shout, sorry, spiteful\n\n[%] Альянсы\n    Исправлена проблема с отсылкой сообщений всему Альянсу\n[%] Корабли\n    Исправлено нулевое потребление некоторых юнитов (в частности  -  шпионского\n    зонда)\n[%] Локализация\n    Исправлено описание фаланги\n\n\n2012-09-17 18:33:12 35c2\n[!] Документация\n    Вся документация сконвертирована в UTF-8\n\n\n2012-09-15 17:23:52 35c0 - Project \"SuperNova.WS\" Release 35 Release Candidate 0\n[!] Project \"SuperNova.WS\" Release 35 RC0 \"MVC race reparse teleportation recycle\"\n\n[!] readme.txt сконвертирован в UTF8\n\n2012-09-15 14:27:02 35b5\n[%] Админка\n    Исправлена ошибка удаления игрока\n\n\n2012-09-13 16:19:25 35b4\n[%] Меню\n    Ссылка на планетарные постройки теперь не меняет название в зависимости  от\n    страницы\n\n\n2012-09-09 21:06:49 35b2\n[~] Меню\n    Под логотип сервера в ALT вместо 'supernova.ws' подкладывается имя сервера\n\n\n2012-09-09 20:14:51 35b1\n[%] Вселенная\n    Исправлена ошибка с перебросом по координатам [1:1:1] при выборе планеты из\n    выпадающего меню\n\n\n2012-09-09 20:01:06 35b0\n[%] Рекорды\n    Исправлено отображение расовых кораблей\n[%] Альянсы\n    Теперь нельзя сделать дипломатическое предложение своему же Альянсу\n[%] UBEv3\n    Правильно считается количество ресурсов для атакующего для  кораблей  с  ID\n    более 300\n[%] Симулятор\n    Исправлено отображение расовых кораблей\n\n\n2012-09-09 01:43:33 35a12.23\n[~] Меню\n    Добавлена прямая поддержка CSS-стилей для элементов меню\n\n\n2012-09-09 01:17:57 35a12.22\n[~] Меню\n    Немного изменен темплейт для гарантированного перекрытия стиля  элемента  в\n    случае если есть свойство LINK\n\n\n2012-09-09 00:53:06 35a12.21\n[+] Меню\n    Добавлена возможность добавления иконки к пункту меню.  Иконки  берутся  из\n    подкаталога 'icons' текущего скина\n\n[%] Локализация\n    В описании зачем нужна ТМ исправлена ссылка на страницу премиума\n\n[@] Меню\n    Стандартное меню вынесено из файла template.php в includes/vars_menu.php\n    Парсер меню теперь понимает вложенные конструкции и константы для типа меню\n    'lang' - т.е. конструкции вида 'info[STRUC_MINE_METAL][description]'\n\n\n2012-09-07 18:42:38 35a12.18\n[@] Альянсы\n    Страница и её инклюдесы избавлены от использования $parse\n\n\n2012-09-07 05:47:07 35a12.16\n[!] Новапедия\n    Полностью написана с нуля страница информации о юнитах\n    Теперь  в  Новапедии  показываются  требования  для  постройки/исследования\n    юнита\n    Теперь для корабля показываются данные для всех типов  двигателей,  которые\n    возможно на него установить\n\n[~] Тёмная Материя\n    Немного переработан интерфейс страницы\n\n[%] UBEv3\n    Исправлена ошибка с определением кораблей с  ID  >  300  как  защиты  и  их\n    восстановлением. Ошибка проявляется только в сторонних модулях\n\n\n2012-09-05 19:48:21 35a12.15\n[@] Подтверждение авторства для JetBrains\n    Псевдоним: Gorlum\n    Емейл: supernova.ws@gmail.com\n\n\n2012-09-05 07:44:06 35a12.13\n[~] Друзья\n    Немного изменен темплейт\n\n\n2012-09-05 07:06:33 35a12.10\n[!] Друзья\n    Страница друзей написана с нуля\n    Теперь подробно сообщается  обо  всех  ошибках  и  результатах  операций  с\n    заявками\n    В личную почту отправляются сообщения по  приходу,  принятию  и  отверганию\n    заявки, а так же при разрыве дружеских отношений\n    Цветовое кодирование статуса друга: зеленый - онлайн, желтый -  бездействие\n    от 5 до 15 минут, оранжевый - оффлайн, красный - оффлайн более суток\n\n\n2012-09-04 03:54:38 35a12.9\n[~] Документация\n    Изменен файл /docs/html/developer.html\n\n\n2012-09-04 00:19:47 35a12.7\n[~] Путь к иконкам пола в  рендерере  темплейтов  сделан  относительным,  а  не\n    абсолютным\n\n\n2012-09-03 20:17:48 35a12.6\n[%] Регистрация\n    Исправлена очепятка в отсылке сообщения о регистрации\n\n\n2012-09-03 14:44:27 35a12.5\n[%] Заметки\n    Добавлен новый файл темплейта. Убраны устаревшие файлы\n\n\n2012-09-03 01:35:52 35a12.4 - Note rewrote\n[!] Заметки\n    С нуля написаны заметки. Что еще сказать?\n\n[+] Планетарные врата\n    Интерфейс переделан по примеру страницы \"Флоты на орбите\"\n\n[~] Регистрация\n    Изменено сообщение при регистрации игрока\n[~] Чат\n    Изменено форматирование сообщений в чате\n\n[@] Код\n    Теперь в doquery() префикс {{table}} не используется и не обрабатывается\n    Страницы login.php, phalanx.php переписаны без использования $parse\n    JS: В объявлениях скриптов все конструкции  language=\"javascript\"  заменены\n    на type=\"text/javascript\"\n    Теперь is_id так же проверяет, что бы ИД не был отрицательным\n[@] MVC\n    Частичная поддержка структуры MVCv2 в init.php\n    Добавлена поддержка анонимных MVC-страниц в common.php\n    Добавлена поддержка MVC-страниц на страницы логина/регистрации\n\n\n\n2012-09-02 07:36:16 35a12.3\n[@] Код\n    Страница Контакты переписана в preMVC без использования $parse\n    Страницы  ТМ,  летящих  флотов,  обзора  планеты,  ресурсов,  симулятора  и\n    статистики переписаны без использования $parse\n\n\n2012-09-02 04:33:33 35a12.0\n[!] Модули\n    Поддержка ali_ally_player 12a0\n    Поддержка player_premium 1b0\n\n[@] Код\n    sn_function_call теперь корректно отрабатывает несуществующие функции\n    Добавлена функция sn_get_groups()\n    eco_bld_tech.php теперь не использует $sn_data\n    Оптимизирован код Альянсов\n    Убраны все $GLOBALS\n[@] MVC\n    index.php избавился от устаревшего кода обработки параметра 'module'\n\n\n2012-09-01 19:26:41 35a11.7\n[+] Статистика\n    В  раздел  \"Статистика  и  рекорды\"  серверных  настроек  добавлена   опция\n    \"Скрывать  ссылки  на  ЛС\".  При  её  включении  в  таблице  статистики  не\n    показывается URL на создание личного сообщения игрокам\n\n\n2012-08-31 23:46:53 35a11.6\n[~] Флот\n    Полностью локализована страница приглашения в САБ\n    Все страницы (fleet0-fleet5) переписаны без использования $parse\n[~] Настройки\n    Страница переписана без использования $parse\n[~] Император\n    Страница переписана без использования $parse\n\n[%] Флот\n    Теперь невозможно пригласить в САБ игрока, на которого летит этот САБ\n\n\n2012-08-30 18:08:06 35a11.0 - Techtree rewrite\n[!] Дерево технологий\n    Полностью переписано дерево технологий (бывш. techtree.php)\n    Рядом с названиями юнитов там, где это имеет смысл, отображаются их  уровни\n    в Империи/на текущей планете\n    Теперь вместо полного уровня с учетом бонусов отображаются отдельно базовые\n    уровни и отдельно бонус к ним\n    Добавлена  поддержка  дополнительных  требований  к  строительству   юнитов\n    (например - модуля расовых юнитов)\n\n[~] Обзор Империи\n    Теперь юниты всегда групируются согласно их принадлежности.  Например,  при\n    подключении модуля расовых юнитов они добавляются в категорию \"Флот\", а  не\n    как ранее в конце таблицы\n[~] Исследования\n    Добавлена индикация бонусных уровней (например, от  премиума)  на  страницу\n    исследований\n[~] Наемники\n    Добавлена индикация бонусных уровней (например, от  премиума)  на  страницу\n    наемников\n[~] Стили\n    Цвет бонусов изменен с \"yellow\" на \"gold\" - это даст  возможность  отличать\n    их от, например, прибывающих на планету юнитов\n[~] Локализация\n    К эффектам Технолога и  Фортификатора  добавлена  информация  о  добавлении\n    слотов к очередям\n\n[@] MVC\n    Все страницы, переделанные под MVC, перемещены в /includes/pages\n[@] Меню\n    Заменены типы элементов меню на \"lang\" там, где это было возможно\n\n\n2012-08-30 03:42:05 35a10.0\n[!] Чат: Переписан чат\n    Чат теперь инкрементальный - с сервера передается не всё содержимое чата, а\n    только новые сообщения. Чат  корректно  работает  когда  у  игрока  открыто\n    несколько окон с чатом\n    Исправлена проблема со скроллированием чата в Chrome v20+\n    Теперь при отключении чата по таймауту содержимое окна не  стирается,  а  в\n    него добавляется соответствующее сообщение. Так же прячутся элементы ввода:\n    выбор цветов, строка сообщения, кнопка \"Отправить\" и панель смайлов\n    Основное окно чата переписано под preMVC\n    Новый код чата (как JS, так и PHP) заметно компактнее, аккуратнее и быстрее\n    старого\n\n2012-08-29 15:57:46 35a9.25\n[!] Статистика\n    Теперь можно управлять появлением игроков  в  статистике  и  рекордах.  Для\n    этого на странице настроек сервера появились дополнительные настройки.  Они\n    размещаются в разделе \"Статистика и рекорды\"\n    Отключение настройки \"Прятать админов\" добавит в статистику и рекорды  всех\n    пользователей с  authlevel > 0. По умолчанию она включена\n    Настройка \"Прятать игроков\" позволяет указать  через  запятую  перечень  ID\n    игроков, которые не будут участвовать в статистике и  рекордах.  Это  может\n    быть полезно для создания NPC - ботов или  игроков,  которые  исполняют  их\n    роли\n    Так  же  в  этот  раздел  вынесена  настройка  расписания   автоматического\n    обновления статистики. ВНИМАНИЕ!!! КРАЙНЕ НЕ РЕКОМЕНДУЕТСЯ МЕНЯТЬ  ЗНАЧЕНИЕ\n    ПО УМОЛЧАНИЮ!!!\n\n[+] Реклама\n    Добавлена возможность управлять мета-тегами 'description' и 'keywords'  без\n    редактирования темплейта! Их содержимое хранится в таблице `config` в полях\n    `adv_seo_meta_description` и `adv_seo_meta_keywords` соответственно\n\n\n2012-08-28 11:51:04 35a9.24\n[~] Отпуск: Обработка отпуска  происходит  теперь  до  обработки  информации  о\n    планете - что бы не показывалась планетарная активность\n\n\n2012-08-28 01:40:28 35a9.23\n[~] Скины/EpicBlue: СН и все расовые корабли приведены к цветовой гамме скина\n\n\n2012-08-28 01:30:01 35a9.22\n[~] Обзор  Империи:  В  колонку  \"ИТОГО\"  добавлена  сумма  по  строящимся    и\n    прибывающим на планеты юнитам\n\n\n2012-08-23 21:30:30 35a9.21\n[%] Флоты: Исправлена ошибка отправки корабля,  если  его  ID  больше  300  или\n    меньше 200\n\n\n2012-08-23 12:18:34 35a9.20\n[~] Скины/EpicBlue: Картинки \"Лени\" и  \"Зависти\"  приведены  к  цветовой  гамме\n    скина\n\n\n2012-08-21 17:11:05 35a9.17\n[%] Вселенная\n    Исправлена ошибка коммита 35a9.15\n    Добавлена иконка статистики\n\n\n2012-08-21 15:54:14 35a9.15\n[~] Вселенная\n    Шаблоны попапов легенды, планет, лун, обломков, игроков и альянсов вынесены\n    из JS-скрипта в шаблон страницы\n    В попап легенды добавлены расшифровки для иконок действия\n    В попапе планеты показывается её диаметр\n    В попапе луны миссия  \"Уничтожить\"  показывается  только  если  на  текущей\n    планете игрока есть ЗС\n    Из попапа игрока убраны ссылки - все, что можно было  сделать  по  ссылкам,\n    можно теперь\n    Добавлено новая  иконка  действия  -  \"Статистика\".  Её  тултип  показывает\n    статистику игрока\n    Расширена подсказка\n[~] Статистика\n    Теперь переход по определенной позиции  (например  со  страницы  Вселенной)\n    скроллирует страницу сразу на эту позицию\n    Немного уменьшен размер страницы статистики\n\n\n2012-08-20 13:05:16 35a9.13\n[+] Шкурки: Добавлены картинки для расовых юнитов в шкурки EpicBlue и SN-Ivash\n\n\n2012-08-19 21:20:50 35a9.12\n[!] Поддержка модуля player_race_units 0b0\n    Теперь  движок  может   работать   с   неограниченным   количеством   типов\n    кораблей-переработчиков\n[!] Переработка: Полностью переделана работа с полем обломков\n    Полностью переписан алгоритм запуска переработчиков\n    В  попапе  вместе  с  абсолютными  теперь  показываются   и   относительные\n    значениями в процентах\n    В попапе добавилось три строки:\n    1. Строка \"В полете\" показывает емкость трюмов переработчиков пользователя,\n    которые уже летят на данное поле\n    2. Строка \"На орбите\" показывает емкость переработчиков на  орбите  текущей\n    планеты или луны\n    3. Строка \"К переработке\" показывает сумму двух предыдущих строк\n    На  основном  экране  Вселенной  к  иконке  обломков  добавлена   индикация\n    процентного значения из строки \"В полете\". Она имеет цветовое кодирование:\n    1.  Зеленый  цвет  означает,  что  прибывающие   флоты   игрока   полностью\n    переработают поле обломков на ресурсы\n    2.  Желтый  цвет  означает,  что  к   полю   летит   некоторое   количество\n    переработчиков, которых не хватит что бы целиком переработать  обломки,  но\n    на  текущей  планете  есть  достаточно  переработчиков,  что  бы  полностью\n    обработать поле\n    3. Оранжевый означает, что к полю летит флот иргока с  переработчиками,  но\n    их не хватит на полную обработку обломков, даже  включая  те  корабли,  что\n    находятся на орбите\n    4.  Красный  цвет  значит,  что  к  полю  обломков  не  летит   ни   одного\n    переработчика игрока\n\n[~] Вселенная\n    Убрано количество летящих флотов - эта информация есть в навбаре\n    Полностью переписана работа AJAX-части,  отвечающей  за  отправку  шпионов,\n    переработчиков и ракет\n    Количество переработчиков теперь включает все виды кораблей, которые  могут\n    перерабатывать обломки\n[~] Обзор планеты\n    Переписана процедура отсылки переработчиков\n    Теперь выводится результат отсылки переработчиков\n\n[%] Обзор планеты\n    Добавлена проверка на уровень губернатора при его отображении - если  вдруг\n    при прямых манипуляциях в базе у планеты есть ИД губернатора,  но  нет  его\n    уровня. В нормальных условиях такого произойти не может\n\n[@] Библиотека \"tw-sack.js\" больше не используется - она заменена на jQuery\n\n\n2012-08-18 13:48:16 35a9.11\n[%] Вселенная: Исправлена ошибка отправкой МПР и  отображением  планеты-цели  в\n    форме отправки\n\n\n2012-08-17 04:55:13 35a9.10 - Road to race units\n[!] МПР - изменение алгоритма ракетного удара\n    Алгоритм  ракетного  удара  теперь  не  привязан  к   численным   значениям\n    идентификаторов юнитов и сильно оптимизирован по скорости\n    Теперь при атаке МПР учитываются  щиты  оборонных  сооружений.  Это  должно\n    слегка  уменьшить  эффективность  ракет  и  повысить   живучесть   защитных\n    сооружений с большим количеством щитов\n    Теперь при ракетном ударе рандомизируются параметры атаки, брони и щитов  у\n    соответствующих юнитов. Границы такие же, как и для сражений  флотов  -  от\n    80% до 120%\n    В результате изменений в алгоритме существенно повысилась живучесть ПЗ  при\n    ракетном ударе\n    Добавлена поддержка усиления залпа для МПР\n\n[~] Вселенная: Интерфейс запуска ракет использует группу защитных сооружений, а\n    не хард-кодед перечень, как было раньше\n\n[@] Система: Численные значения для защитных сооружений и ракет заменены  везде\n    на константы\n\n\n2012-08-16 22:40:07 35a9.9\n[%] СуперНова: Исправлена очепятка в требованиях\n\n\n2012-08-16 17:11:13 35a9.8\n[!] Поддержка модуля player_race_units 0a2\n\n[~] Флот: небольшой ребаланс кораблей\n    СуперНова - атака уменьшена на  порядок.  При  этом  боевая  эффективность\n    корабля изменилось незначительно, благодаря изменению коэфициента  усиления\n    залпа. Немного уменьшилась эффективность против наземной обороны  и  легких\n    кораблей и увеличилась эффективность против средних кораблей\n    Бомбардировщик: понижена эффективность против ионных орудий  и  повышена  -\n    против плазменных\n\n\n2012-08-15 08:54:58 35a9.7\n[@] Из информации о боевых юнитов убраны ненужные данные о единичных усилениях\n\n\n2012-08-15 08:44:44 35a9.6\n[@] Из файла vars.php выделены три  отдельных  файла  со  структурами,  боевыми\n    юнитами и всеми остальными\n    Так же добавлена дополнительная служебная информация для  того,  что  бы  в\n    симуляторе не пропадали защитные сооружения  при  добавлении  новых  юнитов\n    через модули\n    Убраны неиспользуемые данные \"скорострела\"\n[@] Исправлена очепятка в названии константы технологии ионного двигателя\n\n\n2012-08-13 10:45:03 35a9.5\n[!] Поддержка модуля player_race_units 0a1\n\n[~] UBEv3: Улучшена поддержка залпового огня\n[~] Новапедия: Улучшено отображение информации о кораблях и обороне\n\n[@] Убран неиспользуемый код \"скорострела\"\n\n\n2012-08-12 20:45:49 35a9.3\n[!] Поддержка модуля player_race_units 0a0\n\n\n2012-08-09 20:52:38 35a9.1\n[~] Изменена процедура инициализации и загрузки  модулей  для  совместимости  с\n    некоторыми хостингами\n\n\n2012-08-06 01:01:20 35a8.24\n[%] Исправлена ошибка с неправильным вычислением время полета флота до цели\n\n\n2012-08-06 00:49:18 35a8.23\n[%] Исправлена ошибка с досрочным выходом из отпуска\n\n\n2012-08-03 23:43:57 35a8.21 Required module\n[@] Модули\n    Поддержка  дерева  зависимости  модулей  -  теперь  можно  делать   модули,\n    зависящие от других модулей\n    Автоматическая загрузка зависимых модулей в правильном порядке\n\n\n2012-08-02 22:20:39 35a8.16\n[~] Чат\n    Увеличена длина поля для ника в чате\n\n\n2012-08-02 14:12:06 35a8.12 - player_race\n[#] Модули: Расы\n    Шесть фиксированных рас: земляне, луниты, меркурианцы, венериане, марсиане,\n    республиканцы\n    Иконка расы отображается в чате,  в  статистике,  в  попапе  информации  об\n    игроке во Вселенной и на странице Императора. Удержание курсора над иконкой\n    расы вызывает тултип с её названием. Клик - открывает страницу с  описанием\n    всех рас\n    Каждая раса имеет собственные бонусы. Бонусы рас действуют сразу  же  после\n    выбора родного мира - не нужно, например, исследовать техи, что бы получить\n    к ним бонус\n    Раса выбирается после регистрации на странице Императора\n    Первый выбор расы производится бесплатно, каждая смена расы  стоит  100.000\n    ТМ\n    Описание текущей расы доступно на странице Императора. Там же  есть  ссылка\n    на описание всех рас в игре с указанием их символов\n\n\n2012-08-01 11:20:18 35a8.10\n[@] Содержимое переменной $template_result автоматически загружаетя в  темплейт\n    в файле index.php\n[@] Файл темплейта _result_message автоматически  подгружается  при  рендеринге\n    темплейта, если в структуре переменных темплейта есть массив 'result'\n\n\n2012-07-31 14:56:39 35a8.8\n[%] Локализация: Небольшая правка опечаток в русской локализации\n\n[@] Модули\n    Теперь можно указывать  в  качестве  страницы  загрузки  файла  локализации\n    пустое множество '' - файлы в этом массиве будут загружаться всегда\n[@] Инициализация\n    Изменена процедура инициализации  -  модули  теперь  грузятся  до  проверки\n    наличия  страниц.  Это  сделано  на  случай,  если  модуль  добавляет  свои\n    собственные страницы как, например, модуль Премиума и модуль Рас\n\n\n2012-07-31 03:24:39 35a8.7\n[~] Император\n    Со страницы убраны баннер и юзербар\n    Страница переделана в preMVC\n\n[@] Темплейты\n    Рендерер страницы теперь подхватывает заголовок страницы, если  он  есть  -\n    переменная PTL {PAGE_HEADER}\n[@] Почти везде из текста убраны ссылки на переменную  $GLOBALS  для  поддержки\n    рефакторинга в IDE\n\n\n2012-07-29 13:58:10 35a8.6\n[@] js_safe_string()  теперь  так  же  корректно  работает  с  Линуксовыми    и\n    Маковскими переводами строк\n\n\n2012-07-29 13:16:01 35a8.5\n[%] Настройки: Исправлена ошибка с прибавлением на планетах большого количества\n    ресурсов и невозможностью включить РО\n\n\n2012-07-28 21:45:05 35a8.4\n[@] js_safe_string() теперь корректно работает со строками,  где  есть  перевод\n    строки\n\n\n2012-07-27 15:48:08 35a8.3 - Name renderer\n[!] Рендерер имен\n    Добавлен механизм рендеринга имени пользователя\n    Чат, статистика, Вселенная и страница Императора  теперь  используют  общий\n    механизм рендеринга имени пользователя\n\n\n2012-07-27 07:53:18 35a8.1 - preMVC\n[!] MVC\n    Базовая поддержка MVC - встроенная система моделей и видов\n\n[+] Модули\n    Система модулей переписана с учетом базовой поддержки MVC\n    Автоматическая загрузка языков\n\n[~] Настройки пользователей: страница переделана в preMVC\n\n\n2012-07-24 09:00:18 35a8.0\n[@] Доработана система темплейтов\n    Теперь gettemplate() является кумулятивной и  может  принимать  в  качестве\n    параметров уже существующий темплейт - новый файл будет присоединен\n\n\n2012-07-23 21:42:09 35a7\n[~] HTTPS: Теперь СН нормально работает и по HTTPS протоколу\n\n\n2012-07-21 17:39:31 35a6 - Empire Overview optimization\n[+] Обзор Империи: Значительно оптимизирован HTML-код\n    Размер HTML-кода уменьшен на величину от 30% и в отдельных случаях до  80%.\n    Среднему игроку оптимизация даст уменьшение размера загружаемого  файла  на\n    40-50%% (включает так же выигрышь от оптимизации Списка планет - см.ниже)\n[+] Список планет: Значительно оптимизирован HTML-код\n    В Списке планет убрана иконка, отвечающая за исследование вследствие полной\n    бессмысленности\n\n[%] Вселенная: Исправлена опечатка в легенде\n\n\n2012-07-17 16:25:29 35a5.1\n[%] Исправлены недочеты в Телепортации и Переносе столицы\n\n\n2012-07-17 16:06:28 35a5 - Capital transfer\n[!] Перенос столицы  -  новая  возможность,  доступна  на  странице  управления\n    планетой\n    Теперь любая планета может быть назначена столицей\n    Стоимость переноса столицы по умолчанию составляет 25.000 ТМ. Она  задается\n    в таблице `config` переменной 'planet_capital_cost'\n\n\n2012-07-17 15:26:48 35a4 - Planet teleportation\n[!] Телепортация планеты - новая возможность, доступна на  странице  управления\n    планетой\n    Телепортация может производится только на свободное место - там, где нет ни\n    планет, ни лун, ни обломков, включая уничтоженные объекты\n    Телепортация перемещает  в  новые  координаты  планету  вместе  с  флотами,\n    находящимися на орбите планеты\n    Если у планеты есть луна - она  так  же  перемещается  в  новые  координаты\n    вместе с флотами\n    Телепортация  невозможна,  если  в  окрестностях  планеты   есть   какая-то\n    активность флотов (т.е. есть флоты, имеющие в  качестве  точки  отправления\n    или назначения саму планету, луну или поле обломков)\n    После телепортации  необходимо  выждать  некоторое  время  перед  следующей\n    телепортацией -  нарушенная  метрика  пространства  вокруг  планеты  должна\n    нормализироваться\n    Стоимость телепортации и таймаут перед следующим прыжком задаются в таблице\n    `config` соответственно переменными 'planet_teleport_cost' (по умолчанию  -\n    50.000 ТМ) и 'planet_teleport_timeout' (по умолчанию - 1 сутки)\n\n\n2012-07-06 21:39:43 35a3\n[%] Альянсы: Исправлена ошибка отображения полей информации Альянса при наличии\n    лого и отсутствии внешнего текста Альянса\n\n\n2012-07-06 20:55:25 35a2\n[%] Локализация: Исправлена ошибка чтения информации о локализации\n\n\n2012-06-30 16:20:51 35a1\n[%] Фаланга: Теперь нельзя сканировать удаленную планету\n\n\n2012-06-29 14:03:05 35a0\n[+] Добавлен пункт меню \"Тёмная материя\"\n\n[~] На странице \"Тёмная материя\" дополнен список возможного использования ТМ  и\n    проставлены ссылки на соответствующие страницы\n\n\n2012-06-16 14:36:40 34d0 - Project \"SuperNova.WS\" Release 34\n[!] Project \"SuperNova.WS\" Release 34 \"Happy Birthday Supernova! 3rd anniversary\"\n\n\n2012-06-14 14:11:27 34c1 - Research slowing down\n[%] Исследования\n    Устранена ошибка  в  формуле  рассчета  скорости  исследования  технологии.\n    Теперь корректно рассчитывается время исследования для игроков и  Альянсов,\n    а так же корректно обрабатывается случай,  когда  у  игрока  нет  ни  одной\n    лаборатории\n    После исправления время исследования увеличится чуть более, чем в два  раза\n    для игроков с МИС,  а  для  игроков  без  МИС  -  упадет  на  один  уровень\n    лаборатории\n\n\n2012-06-13 14:07:36 34c0.2\n[%] Исправлено отображение цветов в планетбаре\n\n\n2012-06-13 13:26:49 34c0.1\n[%] Исправлен апдейтер\n\n\n2012-06-12 17:54:48 34c0 - Project \"SuperNova.WS\" Release 34 Release Candidat\n[!] Project \"SuperNova.WS\" Release 34 RC0 \"Happy Birthday Supernova! 3rd anniversary\"\n[!] Юбилей: 3 года назад 11 июня 2009 года был запущен первый сервер того,  что\n    превратилось в Проект \"СуперНова\"\n\n\n2012-06-12 17:48:44 34b3 - Fuel economy\n[+] Корабли/Технологии\n    Бонус к скорости полета кораблей теперь вычисляется относительно требуемого\n    уровня технологии двигателя. При равной технологии пользователя бонус равен\n    нулю, при  отличной  -  разнице  уровней  между  требованиями  постройки  и\n    пользовательской умноженной на бонус  двигателя.  Если  уровень  технологии\n    пользователя  меньше,  чем  требуемый  уровень  (например,  для   кораблей,\n    купленных на Черном  Рынке),  то  корабль  получает  пенальти  к  скорости,\n    вычисляемое аналогично, но не более 95%\n    Пример.  Бомбардировщик  требует  Ионный  двигатель  6-го  уровня.  Базовая\n    скорость  полета  корабля  -  4.000.  Каждый  уровень   технологии   Ионных\n    двигателей дает 20% к скорости полета. Таким образом:\n      * При  технологии   Ионных  двигателей  8-го   уровня   скорость   полета\n        Бомбардировщика составит:\n          4.000 * (1 + (8 - 6) * (20 / 100)) = 4.000 * (1 + 2 * 0,2) = 5.600\n      * При технологии 6-го уровня - 4.000\n      * При технологии 3-го уровня\n          4.000 * (1 + (3 - 6) * (20 / 100)) = 4.000 * (1 - 3 * 0,2) = 1.600\n      * Без технологии пенальти к уровню будет равно 120%,  поэтому  вступит  в\n        силу ограничение:\n          4.000 * (1 + (0 - 6) * (20 / 100)) = 4.000 * (1 - 0,95) = 200\n    Технологии двигателей теперь  так  же  влияют  на  расход  топлива.  Каждый\n    уровень, выше требуемого, уменьшает расход  топлива  на  10%  от  бонуса  к\n    скорости за уровень, но не больше чем 50% от расхода. Каждый уровень,  ниже\n    требуемого - увеличивает расход на 20% от бонуса.\n    Например, для Бомбардировщика каждый уровень Ионного двигателя,  ниже  6-го\n    будет увеличивать расход  топлива  на  4%  до  12%  при  полном  отсутствии\n    технологии. Каждый уровень, выше 6-го будет уменьшать расход топлива на 2%,\n    вплоть до 25-го уровня,  где вступит в силу ограничение.\n\n\n2012-06-12 14:14:00 34b2 - Premium Info\n[~] Черный Рынок/Инфотрейдер: Добавлена информация об уровне премиума\n\n\n2012-06-10 16:08:48 34b1 - Premium Structures\n[!] Премиум\n    Премиум аккаунт  теперь  дает  бонус  к  уровням  Фабрики  роботов,  Верфи,\n    Нанофабрики, Лаборатории, Нанолаборатории и к складам ресурсов\n\n[+] Постройки/Здания\n    На превьюшках зданий и в информационной панели  дополнительно  отображаются\n    бонусные уровни - включая таблицу расчета производства\n[+] Ресурсы\n    Выводятся бонусные уровни зданий\n\n[~] Юниты/Гравитационная технология\n    Изменены   требования   и   цена:   теперь   для   исследования   требуется\n    Энергетическая  технология  12  уровня,  а  цена  исследования   составляет\n    100.000.000 металла, 100.000.000 кристаллов и 50.000.000 дейтерия\n\n[@] Модули\n    Конструктор  теперь  поддерживает  загрузку  индексированныъ  элементов   в\n    многоуровневые массивы  типа  sn_data,  включая  использование  констант  в\n    качестве индексов. Подробнее см. в \"sn_module.php\"\n\n\n2012-06-09 18:24:48 34b0\n[!] Файл \"/docs/release.txt\" приведен в соответствие с актуальной информацией\n\n\n2012-06-04 15:46:11 34a20.2\n[%] Сообщения: Исправлена ошибка при попытке отправить сообщение без адресата\n\n\n2012-05-31 00:35:52 34a20.1 - Buy a sector everywhere!\n[~] Покупка секторов на планете\n    Теперь сектора можно купить в нескольких  местах:  в  \"Обзоре  планеты\",  в\n    \"Управлении планетой\" и на экране строительства зданий\n\n\n2012-05-29 01:06:04 34a20 - Buy a sector\n[!] Покупка секторов на планете\n    Теперь можно за ТМ докупать дополнительные сектора на планете - один сектор\n    за раз, максимальное количество секторов не ограничено\n    Стоимость сектора для планеты -  геометрическая  прогрессия  с  количеством\n    секторов в качестве номера члена, БС = 1000 и Ф = 1.01\n    Ориентировочная стоимость покупи  1  сектора  на  планете  составляет:  для\n    планеты размером 100 секторов - 2678  ТМ,  150  секторов  -  4404  ТМ,  163\n    сектора - 5013 ТМ, 200 секторов - 7244 ТМ, 250 секторов  -  11913  ТМ,  300\n    секторов - 19493 ТМ, 330 секторов - 26508 ТМ\n[!] Строительство\n    Минимальное время постройки/исследования юнита уменьшено до 1 секунды\n\n[~] Новости: Заголовок \"Новости\" таблицы актуальных  новостей  теперь  является\n    ссылкой - клик на него раскрывает список всех новостей\n[~] Обзор планеты\n    Переформатирован вывод статуса Ворот для луны\n    Убран таймер исследований вследствие его полной бессмысленности\n\n\n2012-05-28 22:32:16 34a19 - Premium for a term\n[!] Модули: Поддержка player_premium 1a2\n    Премиум теперь можно покупать на разные сроки\n\n[@] Классы\n    Новый метод  'assign_recursive'  класса  \"template\"  -  позволяет  в  одном\n    операторе заполнить  как  переменные  темплейта,  так  и  блоки  -  включая\n    вложенные\n\n\n2012-05-27 14:24:23 34a18.1\n[%] Исправлена ошибка отображения Альянсов в статистике игроков и рекордах\n\n\n2012-05-27 04:44:46 34a18\n[!] Статистика\n    Полностью переписана страница вывода статистики игроков и Альянсов\n    Она теперь использует PTE\n    Полностью переписаны алгоритмы работы страницы\n    Для игроков добавлены отображение следующих  видов  статистики:  \"Проведено\n    боев\", \"Выиграно боев\", \"Проиграно боев\", \"Уровень за постройки\",  \"Уровень\n    за исследования\", \"Уровень  за  рейдерство\".  В  качестве  исходных  данных\n    используется информация из записей игроков (т.е. актуальная  информация  на\n    момент просмотра статистики), поэтому изменение для данных типов статистики\n    всегда будет равно 0\n\n[@] Скины\n    Теперь движок подгружает файл  /design/css/global_server.css  .  Этот  файл\n    может использоваться для добавления специфичных глобальных стилей  сервера.\n    Он грузится после global.css и, следовательно, может перекрывать глобальные\n    стили  \"по  умолчанию\".  Однако  он  грузится  после   скинового   CSS   и,\n    следовательно, будет  перекрыт стилями скина\n    Добавлены  классы  \".same_alliance\"  и  \".same_player\"  для   выделения   в\n    статистике соответственно Альянса игрока и самого игрока\n\n\n2012-05-25 23:30:30 34a17.4\n[@] Апдейтер: Исправлена ошибка, приводящая к закицилванию апдейтера\n\n2012-05-24 19:41:34 34a17.3\n[%] Исследования: Исправлена ошибка расчета времени для исследований Альянса  в\n    случае, если Альянсу известна технология МИС\n\n\n2012-05-21 19:09:53 34a17.2\n[~] Альянсы: Исправлена локализация\n\n[%] Модули:    Исправлен   алгоритм   установки   глобальных   переменных   при\n    инициализации модуля\n\n\n2012-05-20 11:27:47 34a17.1\n[~] Чат: Ник премиумного аккаунта выделяется (по умолчанию - желтым цветом)\n\n[@] Скины\n    Выделение Администрации и  премиумных  аккаунтов  проводится  через  стили.\n    Соответственно,  в  основной  скин  добавлены  стили  классов  .nick_admin,\n    .nick_operator, .nick_moderator и  .nick_premium.  Естественно,  они  могут\n    быть перекрыти в скинах\n\n\n2012-05-20 03:54:52 34a17\n[#] Премиумный аккаунт\n    Добавлен интерфейс покупки премиумного аккаунта\n    Поддержка модуля player_premium 1a0\n\n[~] Офицеры: Академик, Фортификатор, Инженер\n    Изменена логика работы Академика, Фортификатора и Инженера. Все они  влияют\n    на скорость постройки юнитов, однако раньше  зависимость  была  практически\n    экспоненциальная. Вдобавок слишком большой бонус от Академика в  Альянсе  в\n    сочетании с полностью  прокачанным  Наемником  у  игрока  мог  приводить  к\n    артефактам в работе исследований\n    Теперь бонус указанных офицеров - это процент увеличения скорости постройки\n    соответствующих  юнитов,  а  не  процент,  на  который  уменьшается   время\n    постройки. Т.е. это - слагаемое в знаменатели дроби. Если  говорить  совсем\n    просто: 100% бонуса от офицера уменьшают время постройки юнита  в  2  раза,\n    200% - в три раза, 300% - в четыре раза и так далее\n    В связи с данными изменениями  сняты  ограничения  на  максимальные  уровни\n    Фортификатора и Инженера. Кроме того, бонус Академика увеличен с 5% до 10%,\n    а его максимальный уровень - до 30\n\n[%] Интерфейс\n    Исправлена ошибка с отсутствием скина в боевых отчетах и отчете симулятора\n[%] Исправлена ошибка вычисления стоимости Наемников\n\n[@] Скины\n    В базовый CSS перенесено цветовое кодирование чисел  и  сообщений  (ошибка,\n    предупреждение итд). При желании они  могут  быть  перекрыти  в  CSS-файлах\n    стилей\n[@] Модули\n    Теперь в манифесте модуля можно задавать  список  констант,  которые  будут\n    автоматически назначены при его инициализации\n    Теперь  в  манифесте  модуля  можно  задавать  список  переменных,  которые\n    автоматически заменят (в случае обычных переменных) или дополнят (в  случае\n    одноуровневых массивов) соответствующие глобальные переменные.  Специальный\n    механизм гарантирует корректную работу с константами в таких  переменных  и\n    массивах - даже  тех,  которые  были  только  назначены  при  инициализации\n    модуля\n    Теперь  при  инициализации  модуля  в  цепочку  вызовов  функций  корректно\n    инсталлируется оригинальная основная функция из движка\n\n\n2012-05-15 18:23:49 34a16.2\n[%] НоваПедия\n    Исправлено описание ракетной шахты\n\n2012-05-12 00:46:27 34a16.1\n[~] Меню\n    Пункты \"Технология\" и \"Квесты\" перемещены в раздел \"Информация\"\n\n[@] Скины\n    Изменена  организация  CSS-файлов.  Файл   \"formate.css\"   переименован   в\n    \"skin.css\". К нему присоединен в конце файл  \"default.css\".  Таким  образом\n    сохранена последовательность загрузки стилей и при  этом  все  стили  скина\n    находятся теперь в одном файле\n    Изменена система раскраски меню. Теперь каждому пункту  меню  присваиваются\n    присваиваются собственные аттрибуты HTML ID и CLASS.  КРАЙНЕ  рекомендуется\n    производить раскраску меню через аттрибут  ID  (см.  пример  в  formate.css\n    скина EpicBlue). Список ID элементов меню  можно  узнать  либо  в  браузере\n    (используя  функцию  \"Inspect  Element\"  или  аналогичную),  либо  в  файле\n    /includes/template.php, функция tpl_render_menu(), переменная $sn_menu\n    supernova-ivash: Скин приведен в соответствие с текущим положением дел\n\n\n2012-05-05 23:50:46 34a16\n[#] Премиумный аккаунт\n    Базовая поддержка основных функций модуля\n\n[+] MVC: Базовая поддержка MVC\n\n[@] Усовершенствована поддержка \"цепи перекрытий\":  теперь  можно  протаскивать\n   сквозь цепь результат вычислений, модифицируя его на каждом шагу.\n[@] Меню: Новый тип элемента 'lang' - для  поздней  подстановки  локализованных\n    строк\n[@] Модули: sn_module\n    Теперь модули корректно поддерживают работу с цепью перекрытий\n    Теперь модули могут вставлять свои пункты в основное меню\n\n\n2012-05-05 18:30:14 34a15.2\n[%] Фаланга: Исправлена уязвимость в отправке флота\n\n\n2012-05-02 14:17:21 34a15.1\n[~] Меню\n    Исправлена ошибочная ссылка в пункте \"Чертежи\"\n\n\n2012-05-02 03:24:30 34a15\n[+] Губернаторы\n    Теперь Фортификатор добавляет +1 слот к  очереди  постройки  оборонительных\n    сооружений за каждый уровень. Вследствие этого фактор Губернатора измененен\n    с 1.00 до 1.25. Текущие Фортификаторы остались без изменений\n    Теперь Инженер добавляет +1 слот к очереди постройки кораблей и  зданий  за\n    каждый уровень. Кроме того, бонус к скорости строительства зданий  увеличен\n    до 10%. Вследствие этого максимальный уровень уменьшен до 8,  БС  увеличена\n    до 500, фактор увеличен до 1.65. У нанятых инженеров уровень понижен в  два\n    раза с округлением в большую сторону. В целом  это  означает,  что  Инженер\n    стоит дешевле в пересчете на эффективный уровень.\n\n[@] Модули\n    Добавлена поддержка \"цепи перекрытий\"\n[@] Файлы\n    Расширение файлов локализации изменено с  \".mo\"  на  \".mo.php\"  для  лучшей\n    поддержки в различных IDE\n[@] Локализация\n    В дополнение к стандартным путям  \"language/<ISO2>/<domain>.mo.php\"  теперь\n    так же  поддерживаются  пути  вида  \"language/<domain>_<ISO2>.mo.php\".  Это\n    сделано для упрощения структуры подкаталогов в модуле\n[@] Меню\n    Меню теперь является динамическим\n[@] Очереди\n    Упразднена  константа  MAX_BUILDING_QUEUE_SIZE.   Теперь   размер   очереди\n    построек зданий и верфи/обороны задается переменными из таблицы `config`  -\n    соотвественно 'server_que_length_structures' и  'server_que_length_hangar'.\n    По умолчанию их значения равны 5\n\n\n2012-05-01 19:50:03 34a14.8\n[%] Локализация/EN: Исправлены очепятки\n\n\n2012-05-01 19:45:06 34a14.7\n[~] Воплощение: Теперь при Воплощении  в  забаненного  персонажа  администратор\n    автоматически развоплощается\n\n\n2012-04-27 22:34:26 34a14.6\n[~] Меню\n    Добавлена дата запуска сервера (под логотипом). Для уже запущенных серверов\n    она  равна  дате  запуска  апдейта.  Она  хранится  в  таблице  `config`  в\n    переменной \"server_start_date\"\n\n\n2012-04-21 19:45:02 34a14.5\n[%] Флоты\n    Исправлена  мгновенная  скорость  полета  если  для   двигателей   кораблей\n    соответствующие технологии еще не исследованы (например - корабль куплен на\n    ЧР или найден в экспедиции)\n\n\n2012-04-21 18:43:29 34a14.4\n[%] Артефакты\n    Исправлена ошибка, когда АКК мог быть развернут на  планете  с  уже  идущим\n    строительством\n\n\n2012-04-19 23:37:11 34a14.3\n[~] Скины: Обновлен скин supernova-ivash\n\n\n2012-04-14 23:37:24 34a14.2\n[%] Верфи: Доработан фикс 34a13.1\n\n\n2012-04-10 20:05:50 34a14.1\n[%] Чертежи: Исправлена ошибка, делающая чертежи временными при включении ВН\n\n\n2012-04-08 18:02:30 34a14\n[~] Покупка ТМ\n    Изменена структура таблицы `payment`\n    Отдельный метод для подсчета бонуса ТМ\n    Настройка бонусов в свойстве $bonus_table класса sn_module_payment\n\n\n2012-04-08 12:46:40 34a13.3\n[%] Статистика: Исправлен warning в коде расчета статистики\n\n\n2012-04-05 20:06:20 34a13.2\n[~] День Рождения: Подарок начисляется в ДР, а не на следующий день\n\n\n2012-04-04 16:06:16 34a13.1\n[%] Верфь\n    Исправлена ошибка с отрицательными ресурсами на планете\n\n\n2012-04-04 01:32:45 34a13 Fleet Events Iteration 1\n[~] События флотов\n    Фаланга теперь показывает входящие и исходящие ракетные атаки\n    Индикатор атаки на планету теперь так же реагирует на ракетные атаки\n\n[@] События флотов\n    Первая итерация новой системы событий флотов\n\n\n2012-04-02 12:25:39 34a12.2\n[%] Чертежи: Исправлена ошибка расчета стоимости Чертежа для Альянса\n\n\n2012-03-31 13:52:43 34a12.1\n[%] Исследования: Исправлена ошибка в исследованиях Альянса\n\n\n2012-03-31 13:41:56 34a12\n[!] Админка: Параметры MySQL сервера\n    В админку на страницу утилит  добавлен  вывод  информации  о  настройках  и\n    параметрах MySQL сервера\n[!] Админка: Шифрование пароля\n    Полностью переписана утилита шифрования пароля в MD5\n\n\n2012-03-31 10:39:39 34a11.13\n[%] Исправлена ошибка исправления .11\n\n\n2012-03-31 02:14:16 34a11.12\n[%] Locale/EN: Fixed typos and missed strings\n\n\n2012-03-31 02:04:07 34a11.11\n[%] Исследования: Исправлена ошибка повторного начисления ресурсов  при  отмене\n    исследования\n\n\n2012-03-30 22:46:25 34a11.10\n[~] Навбар: Изменена иконка исследований\n\n\n2012-03-30 02:46:25 34a11.9\n[%]  Наемники/Чертежи:  Добавлен  перевод  строки  в  сообщения   о   недостаче\n     ресурсов/неудовлетворении требований\n\n\n2012-03-29 22:40:38 34a11.8\n[%] Верфи\n    Сообщение \"no resources\" заменено на соответстующую локализацию\n\n\n2012-03-29 22:20:48 34a11.7\n[~] Админка: Редактирование планет\n    При выбранной планете невозможно изменить её ID для  исключения  перезаписи\n    информации о текущей планете\n\n\n2012-03-29 02:58:56 34a11.6\n[~] Чертежи/Наемники\n    Теперь при недостачи ТМ видна цена чертежей/наемников\n    Цветовое кодирование цены: красный - не хватает ТМ, зеленый - хватает\n    Кнопка \"Купить/Нанять\" исчезает, если не хватает ТМ для покупки\n\n\n2012-03-29 00:48:06 34a11.5\n[%] Рекорды: Чертежи больше не видны\n\n[@] Константы типов юнитов приведены к единому формату \"UNIT_xxx\"\n[@] Всем юнитам прописаны типы\n\n\n2012-03-29 00:01:38 34a11.4\n[%] Чертежи: Исправлена ошибка требований Чертежа  10го  уровня  для  постройки\n    гипетранспорта\n[%] Технологии: Исправлена косметическая ошибка вывода уровня Чертежа  при  его\n    отсутствии\n\n\n2012-03-28 23:46:14 34a11.3\n[~] Навбар: Виджет экспедиций теперь ведет на страницу отправки флотов\n\n\n2012-03-28 22:00:16 34a11.2\n[%] Наемники: Исправлены описания Наемников и Губернаторов\n\n\n2012-03-28 21:47:45 34a11.1\n[~] Навбар: Обновлена иконка своих флотов\n\n\n2012-03-28 19:23:59 34a11\n[!] Чертежи\n    \"Чертеж\" - это  программный  пакет,  дающий  доступ  к  производсту  юнитов\n    определенного типа. Доступны следующие чертежи:\n    1. Здания: термоядерная электростанция\n    2. Корабли: супертранспорт, гипертранспорт, Звезда Смерти, \"СуперНова\"\n    3. Защитные постройки: планетарная защиат\n    Чертеж  покупается  на  Империю,  после  чего  указанный  юнит  доступен  к\n    производству на всех планетах\n    Чертеж является перманентым\n    Чертежи  заменяют  Наемников  в  требованиях к постройке\n    Наемники \"Разрушитель\" и \"Ассасин\" сконвертированы соответственно в \"Чертеж\n    ЗС\" и \"Чертеж СН\". Остальные четрежи надо покупать самостоятельно\n\n\n2012-03-28 18:22:00 34a10.3\n[~] Интерфейс/Навбар: Заменены иконки\n\n\n2012-03-27 17:10:12 34a10.2\n[%] Локализация:  Исправлены  непрвильные  строчки  локализации  на   страницах\n    Артефактов и Наемников\n\n\n2012-03-26 22:52:24 34a10.1\n[%] Верфи: Исправлена ошибка, позволяющая использовать больше  слотов  очереди,\n    чем позволено движком\n\n\n2012-03-25 11:24:12 34a10\n[+] Покупка ТМ\n    Добавлена поддержка методов 'GET' и 'POST' в системах платежа\n\n\n2012-03-25 08:46:41 34a9.2\n[+] Локализация: Добавлена система отката языков в случае, если включаемый файл\n    не найден в указанном месте движка/для указанного языка\n\n[~] Продажа ТМ: Доработан дизайн страницы. Добавлена информация о бонусах\n\n[%] Админка/Редактор локализаций\n    Добавлена поддержка констант SNC_VER_ в домене 'admin'\n\n\n2012-03-25 07:29:05 34a9.1\n[@] Модули\n    Автоматическая загрузка конфигурации модуля из файла\n\n\n2012-03-25 06:36:08 34a9\n[#] Админка: Редактирование характеристик планеты\n    Модуль admin_planet_edit_extra v1c0\n    В  админке  можно  менять  основные   характеристики   планеты:   название,\n    изображение, размер, температуру, губернатора  и  его  уровень,  количество\n    обломков на орбите\n\n[#] Покупка ТМ: Модуль платежной системы XSolla\n    Модуль payment_xsolla_currency v1b0\n    Реализован протокол \"Виртуальная валюта\" (без отката платежей)\n    Поддержка плагина PayStation\n\n[~] Админка/Редактирование планеты\n    Добавлено форматирование количества юнитов/ресурсов на планете\n    Поддержка admin_planet_edit_extra v1c0\n\n[@] Модули\n    Теперь можно перекрывать функции методами из класса\n    Автоматическая загрузка и регистрация модулей\n    Автоматическое перекрытие функций методами модуля из $manifest\n\n[@] Подсказки: Можно задавать ширину  подсказки  для  согласования  с  основной\n    страницей\n\n\n2012-03-25 01:14:37 34a8.1\n[%] Покупка ТМ\n    Исправлена ошибка расчета бонусов при оптовой покупке\n    Поддержка payment_xsolla_currency v1b0\n\n\n2012-03-25 00:10:06 34a8\n[!] Покупка ТМ (требуется модуль платежной системы)\n    Интерфейс покупки ТМ\n    Поддержка payment_xsolla_currency v0a0.2\n\n[@] Прототипы классов модулей и модулей покупки\n\n\n2012-03-22 20:54 34a7.3\n[%] Альянсы: Исправлена ошибка  незасчета  уровня  технологии  при  составлении\n    списка доступных к постройке юнитов на верфи (корабли и оборона)\n[%] Экономика/Строительство: Исправлен ошибка багоюз медленных  соединений  при\n    постройке зданий\n\n\n2012-03-22 19:56 34a7.2\n[%] Локализация/EN: Исправлены очепятки, спасибо tedk28\n\n\n2012-03-19 09:42 34a7.1\n[%] Админка: Закрыты защитой по authlevel не закрытые ранее файлы\n\n\n2012-03-19 02:50 34a7 - Iconized Navbar\n[!] Навбар: Полностью переработан навбар\n    Вся информация теперь выводится поверх кликабельных иконок  с  всплывающими\n    подсказками\n    Добавлена информация о текущих исследованиях пользователя\n\n\n2012-03-19 00:45 34a6.1\n[@] txt2html: Генерирует более валидный HTML с кодировкой и языком\n\n\n2012-03-19 00:16 34a6 - Base payment module support\n[!] Система: Добавлена базовая поддержка платежных модулей:  таблица  платежей,\n    базовые настройки, строки локализации и константы\n\n[~] Система: Усовершенствован способ определения корневого каталога игры\n\n\n2012-03-18 08:18 34a5- Rocket Science\n[!] Верфь: Полностью переписана работа верфи\n\n[~] Очередь верфи\n    Теперь так же показывается количество юнитов, когда юнит один в очереди\n[~] Ракеты: небольшой ребаланс ракет\n    Емкость шахты увеличена до 12-и, а размер межпланетной ракеты  увеличен  до\n    3-х. Таким образом на один уровень шахты теперь влазит 12 перехватчиков или\n    4  ракеты  (вместо  10  перехватчиков  и  5   ракет,   как   было   ранее),\n    а количество ракет к перехватчикам на уровень шахты увеличилось до  3  к  1\n    вместо 2 к 1. Мощность ракет осталась прежней\n\n[%] Очередь верфи и исследований: Исправлена ошибка с индикацией конца  очереди\n    - \"undefined\" вместо \"Очередь пуста\"\n\n[@] Юниты: Добавлена дополнительный аттрибут \"max\" ко всем юнитам и  его  общая\n    проверка в eco_get_build_data()\n\n\n2012-03-18 00:50 34a4.2\n[~] День рождения: Добавлена иконка ДР в обзор Вселенной\n\n\n2012-03-17 21:18 34a4.1\n[~] День рождения: Добавлена подсказка с датой рождения  на  иконку  тортика  в\n    статистике\n\n\n2012-03-16 03:14 34a4 - Happy Birthday!\n[!] День Рождения: Добавлена подсистема подарков на ДР игрока\n    Игрок может ввести свой ДР на своей странице настроек. ДР вводится один раз\n    и  после  этого  не  может  быть  изменен.  Дата  проходит  валидизацию   в\n    соответствии с серверными настройками формата даты\n    Игрок с ДР на текущую дату будет отмечен специальной иконкой в статистике\n    Амдинистратор сервера может назначить количество  ТМ  в  подарок  на  ДР  в\n    настройках (опция \"Подарок игроку на день  рождения\").  Если  это  значение\n    установлено в 0 - подарки отключены.\n    Выдача подарков происходит один раз в сутки  всем  игрокам,  день  рождения\n    которых находится не далее чем в \"Ретро-рождение\"  дней  от  текущей  даты.\n    При этом подарки выдаются только игрокам,  которые  на  момент  выдачи  уже\n    имели введенную дату рождения. Движок гарантированно начислит подарки  даже\n    если  ДР  пришелся  на  день  неактивности   сервера   (неисправность   или\n    обслуживание).\n    Такая система выбрана с одной стороны - что бы не обидеть игроков в  случае\n    проблем с сервером, а с другой стороны - что  бы  избежать  злоупотреблений\n    (например - ввести послезавтрашнюю дату ДР, на следующий день  получить  ТМ\n    за \"прошлый ДР\", а через день -  еще  и  за  \"нынешний\".  Такой  вариант  в\n    выбранной системе начисления подарков не прокатит)\n\n\n\n2012-03-14 21:55 34a3.2\n[%] Обзор планеты: Исправлена ошибка, когда на лунах не показывалось количество\n    ресурсов во входящих флотах\n[%] Фаланга: Исправлена дурацкая очепятка\n\n\n2012-03-12 14:05 34a3.1\n[%] Реверт предыдущего коммита\n\n\n2012-03-11 21:49 34a3\n[~] Флоты\n    На странице выбора миссии таблица загрузки ресурсов по умолчанию отключена\n    Добавлено дополнительное сообщение при совпадении планеты отправки и пункта\n    назначения\n    Добавлено дополнительное сообщение при попытке отправить незагруженный флот\n    с миссией \"Транспорт\"\n    Добавлено дополнительное сообщение при попытке отправить флот с ресурсами в\n    миссию, отличную от миссий \"Транспорт\", \"Передислокация\" и \"Колонизация\"\n\n[%] Меню: Исправлена ссылка с пункта \"Статистика\"\n[%] Флоты: Исправлена ошибка, дающая возможность отправить флот с  ресурсами  в\n    миссии \"Атака\" или \"Удержание\"\n\n\n2012-03-10 00:42 34a2.3\n[~] Админка: После штатного Развоплощения (т.е. из меню, а не при ошибке  и  не\n    из игрока в отпуске) Администратора возвращает на страницу списка игроков\n\n[%] Админка: Скорректирована работа Развоплощения на пользователе в отпуске\n\n\n2012-03-10 00:10 34a2.2\n[@] Тестирование GIT hooks\n\n\n2012-03-10 00:04 34a2.1\n[@] txt2html: Обновлен\n\n\n2012-03-09 23:36 34a2\n[~] Альянсы: Немного оптимизирован код страницы Управления Альянсом\n\n\n2012-03-09 22:18 34a1.3\n[@] Тестирование GIT hooks\n\n\n2012-03-09 22:10 34a1.2\n[@] txt2html: Подключен к хукам GIT\n\n\n2012-03-09 18:40 34a1.1\n[%] Статистика: Исправлено неотображение пола игрока при просмотре статистики с\n    экранов логина/регистрации\n[%] Админка/Вполощения: Исправлена ошибка с невозможностью  Развоплотиться  при\n    Воплощении в игрока в отпуске\n[%] Локализация/EN: Исправлены очепятки\n\n\n2012-03-08 22:47 34a1\n[!] Админка: Воплощение\n    Теперь можно Воплотиться в любого игрока, посмотреть  игру  его  глазами  и\n    поуправлять игрой его... эээ... интерфейсом!\n    Воплощение доступно только Администраторам сервера\n    Воплотиться можно только в игрока меньшего уровня - т.е. нельзя Воплотиться\n    в такого же Администратора\n    Вложенные  Воплощения   недопустимы:   нельзя   Воплотиться,   будучи   уже\n    Воплощенным в кого-то. Развоплотитесь сначала\n    Для Воплощения используйте соответствующую иконку в \"Списке игроков\"\n    При Воплощении изменяется только  onlinetime  пользователя.  Вся  остальная\n    информация (IP, User-agent итд) сохраняется\n    Если на аккаунте игрока есть ошибки, то попытке Воплощения  будет  выведено\n    сообщение об ошибке, которое увидел бы игрок  на  вашем  месте.  Обновление\n    страницы вернет вас в ваш аккаунт\n    Для Развоплощения используйте соответствующий пункт меню или \"Выход\"\n    ВНИМАНИЕ! Перед использованием Воплощения почистите куки в браузере!  Из-за\n    изменений в работе кукесов кэш браузера может содержать дубликаты куков\n[!] Админка: Список игроков\n    Полностью переписан \"Список игроков\" с использованием PTE\n    Список переписан с использованием PTE\n    Сокращено количество строк локализации\n    Альянсы-игроки больше не выводятся в списке\n    Пишется полный срок бана\n    Для мультиаккаунтов подсвечиваются все адреса с одинаковым IP и  в  скобках\n    добавляется количество игроков с таким же адресом\n    Теперь невозможно удалить  игрока  того  же  уровня  -  для  предотвращения\n    разборок между членами команды одного уровня\n\n[~] Куки: Доработан  механизм  сессий  на  кукесах  для  корректной  работы  из\n    любого каталога (например - модули или админка)\n\n\n2012-03-08 15:47 34a0\n[%] Своз ресурсов: Исправлена ошибка со смещением цветового кодирования емкости\n    транспортов на одну строку вниз\n[%] Локализация/EN: Исправлены очепятки\n\n[@] Документация: README преобразован в UTF8\n[@] Добавлена компенсация  работы  механизма  Magic  Quotes.  Подробнее  -  см.\n    /docs/install.txt, подраздел \"Magic Quotes\"\n\n\n2012-03-08 01:17 33d0 - Project \"SuperNova.WS\" Release 33\n[!] Project \"SuperNova.WS\" Release 33 \"Women Day v2012!\"\n\n\n2012-03-07 22:00 33c2\n[~]  Логин/Регистрация:  Данные   по   серверу   (размер,   скорость,   онлайн)\n    сгруппированы в один блок и теперь видны так же на странице регистрации\n\n[%] Логин: Исправлено неправильное количество игроков онлайн\n[%] Статистика: В  подсчет  и  отображение  статистики  внесены  дополнительные\n    модификации для того,  что  бы  предотвратить  подсчет  Альянсов-игроков  в\n    статистике обычных игроков\n\n\n2012-03-07 21:25 33c1\n[~] Здания: Алгоритм расчета максимальной ширины откачен до предыдущей  версии.\n    Слишком долго работал текущий\n[~] Статистика: Теперь полностью отрабатывается переход со ссылок Вселенной  на\n    страницу статистики: правильно выбирается тип статистики  (игрок/Альянс)  и\n    страница статистики, корректно отрабатываются дропдауны (выбирается  именно\n    текущий тип/страница статистики).\n    Cписок статистики скроллируется либо до выбранного объекта - если позволяет\n    размер страницы, либо максимально вниз, если размер страницы не позволяет\n    Выбранный объект отмечается знаком \">\" в столбце рангов\n    Все числа в ячейках отцентрированы по правому краю\n\n[%] Рекорды: Исправлена проблема с участием в рекордах удаленных планет\n[%] Локализация/EN: Исправлены очепятки\n\n\n2012-03-06 16:54 33c0.2\n[%] Исправлены очепятки в документации и локализациях\n\n\n2012-03-06 00:47 33c0.1\n[%] Исправлены очепятки в документации и локализациях\n\n\n2012-03-04 23:50 33c0 - Project \"SuperNova.WS\" Release 33 Release Candidate\n[!] Project \"SuperNova.WS\" Release 33 RC \"Temporary Mercenary Aliplayers Avatar\n    Rename VerCheck\"\n\n[@] ВНИМАНИЕ!!! В этой версии скорректирована работа партнерской  программы,  а\n    так же повторно применен патч для масштабирования ТМ -  для  тех  серверов,\n    где он не был применен  раньше.  Если  вы  вносили  изменения  в  настройки\n    партнерской системы или начисления ТМ - сверьте текущие настройки в таблице\n    \"config\" с эталонными настройками в конце файла /docs/sql/supernova.sql!\n\n[~] Партнерская программа\n    Скорректирована  работа  партнерской  программы.  Теперь  она  работает   в\n    точности так, как написано в /docs/readme.txt\n\n[~] Интерфейс\n    Убрано автоматическое обновление страницы при окончании очереди\n\n[%] Безопасность\n    Исправлена работа  с  куками.  Некорректно  работали  кукесы  на  серверах,\n    где движок был установленн не в корневой каталог\n\n[@] БД: Обновлен дамп БД\n[@] Апдейтер: Апдейтер работает более стабильно\n\n\n\n2012-03-04 08:29 33a32 - Aliplayer V9\n[#] Модули: Поддержка Aliplayer V9\n    Поддержка чёрного рынка Альянса\n    Расширенная поддержка счета Альянса в ресурсбаре\n    Поддержка модуля на странице \"Мировые константы\"\n\n[@] Модули:  Изменена  процедура  подключения  модуля.  Теперь  не  нужен  файл\n    начальной  загрузки  -  вместо  него  используется  файл с именем  модуля и\n    расширением \"php\"\n    Для  корректной  работы  существующих  модулей  достаточно  удалить   файлы\n    \"*_bootstrap.php\" в каталоге \"/modules\".\n\n\n2012-03-03 22:53 33a31.3\n[%] Создание юнитов: Исправлена ошибка, когда  члену  Альянса  не  засчитывался\n    бонус Альянса при составлении списка доступных к постройке юнитов\n\n\n2012-02-29 22:01 33a31.2\n[@] Модули: Загрузчики модулей теперь располагаются в каталоге /modules, а не в\n    /modules/_functions\n[~] Чат: Тэг  Альянса  после  имени  игрока  теперь  указывается  в  квадратных\n    скобках, а адресат сообщения - в круглых. Сделано для унификации  написания\n    тэга Альянса в движке\n\n\n2012-02-29 17:20 33a31.1\n[%] Флоты: Исправлена уязвимость, позволяющая дублировать флоты\n\n\n2012-02-26 22:09 33a31\n[@] Сильно переработана организация файлов PHP\n    Многие процедуры поменяли свое местоположение\n    Множество файлов теперь не грузятся  автоматически  при  старте  движка,  а\n    грузятся лишь по потребности. В частности - все файлы  миссий  подгружаются\n    только в менеджере летящих флотов,  а  сам  менеджер  грузится  только  при\n    потребности в обработке флотов.  Кроме  того,  боевой  движок  подгружается\n    только в симуляторе и при обсчете боев (Миссии \"Атака\" и \"Уничтожить\")\n\n\n2012-02-26 15:49 33a30.1\n[~] Логин/Регистрация\n    Исправлено форматирование - копирайт больше  не  съезжает  влево,  основной\n    блок страниц отцентрирован\n    В строке \"Новичек\" теперь показываются только игроки\n\n\n2012-02-26 06:59 33a30\n[!] Админка: Регистрация сервера\n    Регистрация сервера нужна для  ряда  запросов  к  серверу  обновлений.  При\n    регистрации передается минимум информации,  необходимой  для  идентификации\n    сервера:\n    1. Полный URL сервера - т.е. HTTP-адрес  и  подкаталог  сервера.  Например:\n       http://myserver.com/myfolder/.    Это    необходимо    для     первичной\n       идентификации сервера. Полный путь необходим для того, что бы  различать\n       несколько копий СуперНовы, установленных на одном IP или домене.\n    2. Внутреннее название сервера. Используется для подстановки в сообщения.\n    Зачем  вообще  регистрировать  свой  сервер?  В  будущем   планируется  ряд\n    возможностей, которые буду доступны только зарегистрированным  серверам.  В\n    их число входит (отсортированы по запланированным срокам реализации):\n    1. Автоматическое получение чейнджлога\n    2. Автоматизированное обновление движка\n    3. Участие в рейтинге серверов\n    4. Багрепорты от администраторов серверов\n    5. Чат для администраторов серверов\n    6. По запросу - удаленная диагностика сервера\n    7. ...и многое, многое другое\n    Зачем регистрировать свой сервер прямо сейчас?\n    1. Запросы от администраторов  зарегестрированных  серверов  имеют  больший\n       приоритет при диагностике проблем и обработке багрепортов.\n    2. При регистрации кроме индивидуального ключа серверу выдается  уникальный\n       идентификационный номер,  который  будет  использоваться  при  первичной\n       сортировке серверов. Чем раньше  будет  зарегистрирован  сервер  -  тем,\n       например, выше он будет в общем каталоге серверов...\n\n\n2012-02-25 17:40 33a29.3\n[~] Флоты\n    На страницу своза ресурсов добавлено количество ресурсов на планете\n\n[@] Админка: Проверка версии\n    Добавлена обработка неподдерживаемых ответов от сервера\n\n\n2012-02-24 18:15 33a29.2\n[~] Навбар\n    На страницу настроек пользователя  добавлена  опция  постоянного  включения\n    планетарного навбара\n[~] Флоты\n    Планетарный навбар включен на странице выбора кораблей\n\n\n2012-02-24 17:48 33a29.1\n[~] Флоты: Добавлено количество дейтерия на планете на страницу выбора кораблей\n    и страницу выбора точки назначения\n[~] Навбар\n    Планетарный навбар сделан горизонтальным\n    Из планетарного навбара убрана колонка летящих флотов\n\n[@] Файлы из /includes/fleet и /includes/market перенесены в /includes/includes\n\n\n2012-02-24 01:15 33a29 - Navbar Rework\n[+] Навбар: Переработан навбар\n    Из навбара убрано количество текущих ресурсов на  планете/в  альянсе  -  их\n    присутствие на большей части экранов не имело практического смысла при том,\n    что отнимало драгоценное вертикальное пространство страницы.\n    Там, где сведения о количестве  ресурсов  на  планете  смысл  имеет  -  они\n    добавлены (см. ниже)\n\n[~] Обзор планеты: Добавлено текущее количество ресурсов  на  планете,  текущий\n    размер хранилищ, а так же - количество ресурсов на прилетающих флотах\n[~] Черный Рынок/Скупщик кораблей и Продавец б/у кораблей\n    Добавлено количество текущих ресурсов на планету\n[~] Модуль \"Альянсы-игроки\" - v6\n    Добавлено количество  текущих  ресурсов  в  таблицу  перечислений  ресурсов\n    Альянсу\n    Модуль необходимо обновить до версии v6+ для работы с SN v33a29+!\n[~] Альянсы\n    Добавлен заголовок на страницу Альянсов с тэгом Альянса\n    Ссылка на управление Альянсом/игроками перенесена в самую верхнюю таблицу\n[~] Экономика/Строительство\n    На всех экранах строительства (здания, флот, оборона) к  основному  навбару\n    прибавляется  дополнительный  с  перечнем  текущих  ресурсов  на  выбранной\n    планете\n\n[@] Темплейты\n    Расширение файлов темплейтов изменено с \".tpl\" на \".tpl.html\" для  большего\n    удобства разработки\n    Теперь при использовании директивы <!--  INCLUDE  -->  НЕ  НУЖНО  указывать\n    расширение подключаемого файла\n\n\n2012-02-21 01:26 33a28.1 - Menu rework\n[~] Меню: Переработано меню\n\n\n2012-02-20 23:38 33a28 - Automatic version check\n[~] Админка: Проверка версии\n    На страницу настроек сервера добавлена опция автоматической проверки версии\n    (по умолчанию - отключена).\n    1. При автоматической проверке передаются те же самые анонимные данные, что\n       и  в  случае  ручной  проверки.   Однака   сама   проверка   выполняется\n       автоматически по расписанию.\n    2. Период проверки версии задается в секундах в таблице  config  переменной\n       server_updater_check_period. По умолчанию период проверки равен 24 часам\n       (раз в сутки)\n    Результат и время последней проверки теперь выводится в левом меню\n    Результат и время последней проверки так же выводится на странице настроек\n    Предусмотрено цветовое кодирование результатов проверки (как в левом  меню,\n    так  и  в  настройках):  зеленый  -  обновление  необязательно,  желтый   -\n    желательно обновить движок, оранжевый -  крайне  рекомендуется  обновление,\n    красный - ошибка проверки версии\n\n\n2012-02-20 02:33 33a27 - Planet column setup\n[+] Обзор планеты: Настраиваемое количество колонок в списке планет\n    На странице настроке пользователя можно  указать,  сколько  колонок  должно\n    быть в списке планет - пункт \"Количество колонок в списке планет\" в разделе\n    \"Настройки интерфейса\"\n    Можно выставить количество колонок в 0 и  указать  максимальное  количество\n    рядов с списке - см. соответствующий пункт там же.  В  этом  случае  движок\n    рассчитает количество колонок исходя из этого  числа.\n    Обращаю внимание - указывается именно максимальное количество  рядов!  Т.е.\n    если у игрока 6  планет,  а  количество  рядов  указано  5,  то  количество\n    необходимых колонок для того, что бы число рядов не привысило 5 будет равно\n    двум. Соответственно, список планет будет сформирован в виде  двух  колонок\n    по три ряда. Если же колоний будет 12 - список планет будет  выглядеть  как\n    таблица три колонки по четыре ряда.\n    Данная особенность связана с построением списка планет -  слева  направо  и\n    сверху вниз. Естественно, не составило  бы  никакого  труда  сделать  вывод\n    списка сверху вниз, а затем справо налево - это было бы даже легче.  Однако\n    при выбранном способе  сохраняется  пользовательская  сортировка  планет  -\n    более \"важные\" колонии всегда будут \"выше\" в списке\n\n\n2012-02-18 19:23 33a26 - Skin 'xnova' removal\n[!] Скины: Удален скин 'xnova'\n    Из дистрибутива игры удален скин 'xnova' из-за занимаемого им размера\n    Скачать скин можно с основного сайта проекта по ссылке\n    http://supernova.ws/files/skins/supernova-skin-xnova.zip\n    либо с SourceForge по ссылке\n    http://sourceforge.net/projects/supernova-ws/files/skins/\n\n\n2012-02-18 19:23 33a25 - Version check\n[@] Админка: Проверка версии\n    Проверка версии выполняется исключительно в ручном режиме по нажатию кнопки\n    \"Проверить версию\". При этом передаются только анонимные данные  -  текущая\n    версия БД, номер релиза и версия игры\n    При проверке движок обращается через HTTP на сервер  проверки  версий.  При\n    установленом CURL он будет использован.  Если  CURL  не  установлен,  будет\n    осуществлена попытка  получить  версию  через  file_get_contents(). Поэтому\n    убедитесь, что если  CURL  установлен  -  он  правильно  сконфигурирован  и\n    подключен к PHP, а если CURL не установлен - в PHP разрешается обращаться к\n    внешним сайтам через соответствующую функцию\n    Результат проверки - рекомендация движка о необходимости обновления текущей\n    версии игры\n[@] БД: Доработан дамп. Теперь по умолчанию новосоздаваемый игрок имеет мужской\n    пол - так же, как и дефолтный администраторский аккаунт\n\n\n2012-02-17 18:42 33a24.3 - Statistic with sex\n[+] Статистика: Показывается пол игрока\n\n\n2012-02-16 00:06 33a24.2\n[%] Альянсы: Исправлена ошибка выхода из Альянса - у игрока  не  затирался  тэг\n    Альянса\n[%] Локализация: Исправлено опечатка в en/market.mo\n\n\n2012-02-15 16:42 33a24.1\n[@] Апдейтер: Изменено условие апгрейда Наемников до отдельной таблицы\n\n\n2012-02-15 00:40\n[%] Альигроки больше не учитываются в онлайн-статистике\n\n\n2012-02-13 04:54\n[~] Наемники: Отключены отладочные малые сроки найма ВН\n\n\n2012-02-13 04:48\n[!] Иконка сайта: Новая иконка сайта! Мегареспект ув.Помощнику Ivash!\n\n\n2012-02-13 04:38 33a24 - Minify!\n[!] Темплейты: Минификатор\n    ВНИМАНИЕ! ЭТО - ЭКСПЕРИМЕНТАЛЬНАЯ ФИШКА! ИСПОЛЬЗУЙТЕ ЕЁ  НА  СВОЙ  СТРАХ  И\n    РИСК!\n    Минификатор уменьшает размер генерируемого движком HTML-кода  путем  замены\n    нескольких  идущих  подряд  пустых  символов  (перевод  строки,  табуляция,\n    пробел) одним символом пробела.\n    Минификатор умеет сжимать HTML и встроенный JS-код. Для JS-кода он  так  же\n    удаляет однострочные комментарии.\n    Минификатор работает на уровне темплейтов и если включено  кэширование,  то\n    минификатор вызывается  только  один  раз  при  компиляции  кода  и  дальше\n    кэшируется   уменьшенный   скомпилированный   темплейт,    что    исключает\n    необходимость в повторном вызове минификатора. Этим он  выгодно  отличается\n    от  минификаторов, работающих на уровне сессии через ob_hanler()\n    В  среднем  по  сайту  минификатор   дает   выигрыш   порядка   7-8%%   при\n    незначительном падении производительности.\n    По  умолчанию  минификатор  отключен.  Включить  его  можно  в  админке   в\n    настройках сервера - пункт \"Минификатор темплейтов\"\n\n\n2012-02-12 21:37 33a23.1\n[~] Чёрный Рынок: Письма от Продавца  Информации  всегда  приходят  в  почтовый\n    ящик - даже если у игрока отключено получение  шпионских  отчетов.  Мистика\n    какая-то!\n\n\n2012-02-12 21:11 33a23 - Infotrader\n[!] Чёрный Рынок: Продавец информации\n    На Чёрном Рынке доступна новая услуга: продажа информации.\n    Информация об игроке: текущие уровни активных Наемников\n\n\n2012-02-12 15:40 33a22.2\n[~] Чёрный Рынок\n    Редизайн основного экрана и экрана обмена ресурсов\n    Откуда взялась эта странная надпись? Очень странно...\n\n[@] Чёрный Рынок: Почищены языковые файы от неиспользуемых строк\n\n\n2012-02-12 13:40 33a22.1\n[%] UBE: Исправлена ошибка неучета бонуса  Альянсов  в  бою.  ВНИМАНИЕ!  Бонусы\n    Альянса и от Наемников по-прежнему не будут видны в логе боя!\n\n\n2012-02-11 22:08 33a22 - Log rename\n[~] Вселенная:   Все  действия  по  переименованию  галактик  и  систем  теперь\n    записываются в лог - код события 104\n\n[@] Админка: Редизайн интерфейса просмотра логов (бывш. \"Ошибки\")\n\n\n2012-02-11 03:50 33a21 - Allie's research que indication\n[+] Альянсы:  На  страницу  информации  об  Альянсе  для  участников  добавлена\n    индикация очереди исследований\n\n[~] Исследования: Если исследования проводит  Альянс  это  видно  по  заголовку\n    страницы\n\n[%] Альянс: Бонус от ресурсов теперь отображаются правильно - \"Не доступен\"\n\n[@] Модули: Если не найден INCLUDE файл темплейта  в  каталоге  модуля,  движок\n    пытается подключить аналогичный файл из каталога основного темплейта\n\n\n2012-02-10 19:19 33a20.2\n[!] ВНИМАНИЕ!!! PHP 5.3.1 содержит баг, который делает невозможной  полноценную\n    работу СН начиная с v33a12! Обновите  PHP,  или  сделайте  откат  до  более\n    ранней версии PHP, или используйте предыдущую версию СН.\n    Описание бага: https://bugs.php.net/bug.php?id=50394\n\n[%] Исправлена пропажа топбара в PHP 5.3.x\n\n[@] Среда разработки изменена на WAMP Server 2.2. Конфигурация:\n    MySQL 5.1.41\n    Apache 2.2\n    PHP 5.2.9-2 + xCache 1.3.2\n\n\n2012-02-10 01:40 33a20.1\n[@] Новости: Изменения в редактировании новости\n    При редактировании новости галочка рассылки новости по умолчанию отключена\n    При  редактировании  новости  не  изменяется  глобальное  время   написания\n    последней новости -  т.е.  отредактированная  новость  не  включает  список\n    последних новостей на обзоре планеты\n[@] Файлы: Удалены неиспользуемые файлы faq.php, faq1.php, faq2.php\n\n\n2012-02-10 01:17 33a20 changelog - now with HTML taste!\n[+] Документация: Добавлен /docs/changelog.html - чейнджлог в формате html\n\n\n2012-02-09 20:11 33a19\n[%] Исследования: При отмене исследования ресурсы возвращаются  не  на  текущую\n    планету, а на ту, с которой были взяты\n\n\n2012-02-09 10:52 33a18.3\n[~] JS: Восстановлена расцветка чисел\n\n\n2012-02-09 00:19 33a18.2\n[~] Список планет: Клик на иконке  летящего  союзного  флота  теперь  открывает\n    сраницу \"Флоты в полете\"\n\n[%] Исследования: Исправлена ошибка исследования\n\n[@] Оптимизирована eco_get_build_data()\n[@] Файл todo.txt заменен на todo.xls\n\n\n2012-02-08 01:56 33a18.1\n[~] Исследования: Награда за квесты на исследование теперь  всегда  начисляется\n    на основную планету игрока\n\n[%] Исследования: Исправлена ошибка  начисления  награды  за  исследовательские\n    квесты исследования в виде металла/кристалла/дейтерия\n[%] Админка: Исправлена ошибка создания квеста\n\n\n2012-02-07 02:10 33a18 - Indicating Ally Res\n[+] Альянсы: В топбар добавлено отображение ресурсов Альянса\n\n[%] Альянсы:  Исправлена  ошибка  отображения  списка  ресурсов  при  просмотре\n    Альянса сторонним игроком\n\n\n2012-02-06 00:21 33a17 - Ally Tech\n[#] Альянсы:  Альянсы  теперь  могут  рекрутировать  Наемников  и  исследовать\n    технологии!\n    1. Каждый Альянс имеет счет с ресурсами металл/кристалл/дейтерий/ТМ\n    2. Член Альянса может перевести ресурсы на счет Альянса. Сделать это  можно\n       на главной странице Альянса в разделе \"Ресурсы Альянса\".  Там  же  можно\n       увидеть состояние счета Альянса и бонусы, предоставляемые Альянсом  (см.\n       ниже)\n    3. Ресурсы со счета Альянса могут расходоваться только  на  нужды  Альянса.\n       Вывод ресурсов со счета Альянса невозможен\n    4.  Владелец  Альянса  с  его  счета   может   исследовать   технологии   и\n       Рекрутировать Наемников - соответственно пункты \"Технологии  Альянса\"  и\n       \"Наемники Альянса\" на странице управления Альянсом\n    5. После достижения минимально необходимого размера Альянса (10 человек  по\n       умолчанию, задается  в  таблице  `config`  записью  'ali_bonus_members')\n       каждый член Альянса получает бонус к своим Наемникам и технологиям\n    6. Значение бонуса зависит от количества игроков в Альянсе и вычисляется по\n       формуле: Бонус = round(уровень  технологии  или  Наемника  /  количество\n       игроков),  где round() - операция математического округления.\n    7. Бонусы   от  Наемников  и  Технологий  так  же  действуют  при  проверки\n       требований  к  постройкам/исследованиям.  Например:  игрок   состоит   в\n       Альянсе, дающем бонус  +2  к  Лазерной  технологии,  а  его  собственный\n       уровень технологии равен 4. Эффективный уровень технологии этого  игрока\n       равен 6. Это  означает,  что  находясь  в  Альянсе  он  имеет  доступ  к\n       исследованию Ионной  технологии  (требуется  ЛТ  5-го  уровня)  и  может\n       строить Тяжелый Лазер (требуется ЛТ 6-го уровня). Очевидно, если  бы  он\n       не находился в Альянсе, эти постройки были бы заблокированы\n    8. При исследовании технологии уровень лаборатории равен количеству игроков\n       в Альянсе на момент начала  исследования.  Активные  исследования  видны\n       членам Альянса на странице информации\n    Примеры:\n      1. Альянс из 10 человек купил Технологию 4 уровня. Бонус = round(4/10)  =\n         round(0,4) = 0\n      2. Альянс из 10 человек купил Технологию 7 уровня. Бонус = round(7/10)  =\n         round(0,7) = 1\n    Выбранная механика бонусов  Альянса  призван  обеспечить  достижение  сразу\n    нескольких целей:\n    1. Исключить злоупотребление фишкой,  когда  2-3  игрока  формируют  Альянс\n       исключительно для получения бонусов\n    2. Активизировать межальянсную активность: бонусы от ресусов Альянса  можно\n       получить только начиная с определенного количества участников. Ну и  чем\n       больше игроков в Альянсе, тем больше у него ресурсов\n    3. Усилить лояльность игроков к  Альянсу  -  при  выходе  (или  выгоне)  из\n       Альянса игрок теряет  все  бонусы  и  (самое  неприятное)  все  ресурсы,\n       пожертвованные  в Альянс\n    4. Исключить появление  мегаальянсов:  чем  больше  игроков  -  тем  больше\n       ресурсов они могут пожертвовать, но тем меньше  бонусов  получит  каждый\n       отдельный игрок\n    5. Слабые игроки в сильных  Альянсах  получают  доступ  к  end-game  юнитам\n       (если, конечно, глава Альянса  решит  потратить  ТМ  на  соответствующих\n       Наемников) и бонус в развитии\n    6. Сильные игроки смогут  поднять  эффективные  уровни  Технологий  даже  в\n       больших Альянсах. Например, если в Альянсе 15 человек, то исследовать 15\n       уровень технологии всем Альянсом будет  проще  и  дешевле,  чем  каждому\n       игроку отдельно\n    7. То же самое распространяется и на Наемников. При этом только Альянс дает\n       возможность получить эффективный уровень Наемников больше максимального\n\n[%] Ракетная атака: Исправлена ошибка, из-за которой ракеты били в  десять  раз\n    слабее\n\n\n2012-02-05 21:07 33a16.2\n[#] Альянсы: Альянсам добавлены счета в металле, кристалле и  дейтерии. Правила\n    пополнения  аналогичны счету в ТМ: класть можно, забирать - нельзя\n\n\n2012-02-05 20:54 33a16.1\n[@] Промежуточный коммит. Переписан интерфейс исследований для поддержки модуля\n    Альянсов-игроков\n\n\n2012-02-05 04:41 33a16\n[!] Исследования: Очередь исследований перенесена с планет на пользователя\n    1. Исследования теперь могут производится даже на планетах  со  строящимися\n       лабораториями/нанолабораториями\n    2.  В  случае  отсутствия  МИС  для  проведения   исследования   выбирается\n       лаборатория  с  максимальным  эффективным   уровнем   (т.е.   с   учетом\n       нанолабораторий)\n    При апдейте все идущие исследования  будут  перенесены  в  пользовательскую\n    очередь\n\n[+] Исследования: Добавлены подробные  сообщения  об  ошибке  в  случае,  когда\n    технология не может быть исследована (нехватка ресурсов,  неудовлетворенные\n    требования итд)\n\n[@] Авторизация: Добавлена проверка на попытку входа от лица Альянса\n[@] Исследования: Исследования теперь завернуты в  транзакции  -  это  позволит\n    избежать  злоупотреблений  связанных  с  частым  обновлением  страницы   на\n    медленных соединениях\n\n\n2012-02-05 00:52 33a15.2\n[@] Убраны pass-by-reference в eco_bld_structures.php\n\n\n2012-02-04 22:18 33a15.1\n[%] Регистрация: Исправлена ошибка, когда игрок мог создать  аккаунт/планету  с\n    пробелами в начале/в конце\n\n\n2012-02-04 21:00 33a15\n[~] Альянсы: Изменено отображение логотипа на странице Альянса\n\n[%] Альянсы: Исправлена ошибка при отправке письма группам Альянса\n[%] Альянсы: Исправлена ошибка с неправильным отображением звания главы Альянса\n\n\n2012-02-04 20:19 33a14.4\n[@] Убраны pass-by-reference в qst_quest.php и flt_mission_attack.php\n\n\n2012-02-04 20:11 33a14.3\n[%] Наемники: Исправлена ошибка с невозможностью найма ПН\n\n[@] Убраны pass-by-reference в template.php\n\n\n2012-02-04 16:12 33a14.2\n[%] Исправлены ошибки в работе Альянса\n\n\n2012-02-04 16:12 33a14.1\n[%] Исправлены ошибки предыдущего коммита\n\n\n2012-02-04 04:27 33a14\n[#] Альянсы: Альянсы теперь могут покупать Наемников!\n    1. Каждый Альянс имеет собственный счет ТМ\n    2. Член Альянса может перевести ТМ на счет Альянса\n    3. ТМ со счета Альянса могут расходоваться только на нужды Альянса.  Нельзя\n       переводить ТМ на счет игрока\n    4. Владелец Альянса может покупать Наемников со счета Альянса\n    5. После достижения минимально необходимого количества (по умолчанию -  10,\n       задается в таблице `config`  записью  'ali_bonus_members')  каждый  член\n       Альянса получает бонус к своим Наемникам, равный уровню Наемника Альянса\n       деленного на количество членов Альянса, математически округленный\n    Примеры:\n    1. Альянс из 10 человек купил Наемника 4 уровня.\n        Бонус = round(10/4) = round(0,4) = 0\n    2. Альянс из 10 человек купил Наемника 7 уровня\n        Бонус = round(10/7) = round(0,7) = 1\n    Данный подход призван обеспечить достижение сразу нескольких целей:\n    1. Исключить злоупотребление фишкой,  когда  2-3  игрока  формируют  Альянс\n    исключительно для получения бонусов\n    2. Активизировать межальянсную активность - чем больше игроков, тем  больше\n    ТМ\n    3. Усилить значение лояльности Альянсу - при выходе (или выгоне) из Альянса\n    игрок теряет все бонусы и, самое неприятное - все пожертвованные  в  Альянс\n    ТМ\n    4. Исключить появление мегаальянсов - чем  больше  игроков,  тем,  конешно,\n    больше ТМ они могут пожертвовать - но тем  меньше  бонусов  получит  каждый\n    отдельный игрок\n    5. Слабые игроки в сильных  Альянсах  получают  доступ  к  end-game  юнитам\n    (если,  конечно,  глава  Альянса  решит  потратить  ТМ  на  соответствующих\n    Наемников)\n\n[~] Наемники:   При  найме  постоянных  наемников  показывается  ровно  столько\n    уровней, на сколько хватает ТМ\n[~] Альянсы: Теперь список членов Альянса распознает права просматривающего без\n    захода в админскую  часть,  поэтому  из  админки  убран  пункт  \"Управление\n    участниками\"\n\n\n2012-02-03 22:58 33a13\n[+] Наемники: Временных наемников теперь можно  увольнять  до  истечения  срока\n    найма. ВНИМАНИЕ! При увольнении наемников вся портаченная на найм ТМ  будет\n    утеряна!\n\n[~] Альянсы:  Количество  игроков  в  Альянсе  теперь   изменяется  сразу   при\n    изменении, а не при апдейте статистики/обслуживании сервера\n\n[%] Админка: Устранена ошибка в модуле  обслуживания,  приводившая  к  удалению\n    записей игроков для Альянса\n\n[@] Движок: Количество игроков теперь не включает игроков-Альянсов\n[@] Локализация: Все строки локализации, относящиеся к  наемникам,  вынесены  в\n    файл mrc_mercenary.mo\n\n\n2012-01-29 04:27 33a12\n[!] Модульность: Базовая  поддержка  модульности  -  динамически  перекрываемые\n    функции. Подробное описание по использованию динамического перекытия см.  в\n    разделе \"Модульная система\" файла /docs/readme.txt\n\n[%] Наемники:  Исправлена  ошибка  начисления  процентных  бонусов:  в  случаях\n    многократных бонусов происходило  начисление  бонуса  на  бонус,  а  не  на\n    базовое значение. Это приводило к получению завышенных бонусов  -  например,\n    на Адмирале и Навигаторе\n\n[@] Статистика: файл с функциями  статистики  /functions/sys_stat_functions.php\n    перемещен в /includes/includes/sys_stat.php\n\n\n2012-01-27 03:58 33a11\n[+]  Постройки:  Вертикальная  очередь  построек.   Включается   в   настройках\n    пользователя в секции \"Настройки интерфейса\"\n\n[~] Вселенная: При создании новой планеты из имени планеты  исключен  знак  \"№\"\n    для более корректной работы функций PHP\n\n[@] Строительство: Файлы с функциями строительства  зданий/исследований/обороны\n    перенесены в каталог /includes/includes и теперь  подключаются  только  при\n    вызове соответствующих страниц. Это  позволило  заметно  сократить  размеры\n    кода в памяти сервера\n\n\n2012-01-23 15:52 33a10\n[+] Скины: Добавлена возможность перекрыть дефолтные стили  элементов  jQueryUI\n    (файл /design/css/jquery.css) стилями, специфическими для скина. Для  этого\n    в корневой каталог скина  нужно  положить  файл  jquerу.css  с  настройками\n    стилей элемента. Сгенерировать файл под свою тему можно на сайте jQuery  по\n    ссылке: http://jqueryui.com/themeroller/\n\n[~] Скины: EpicBlue имеет собственное оформление элементов jQueryUI\n\n[@] JS: Обновлен jQuery до версии 1.7.1. Обновлен jQuery-UI до версии 1.8.17\n\n\n\n2012-01-22 07:39 33a9\n[+] Вселенная: На попапе игрока отображается его текущее звание в Альянсе\n[+] ЧР/Торговец ресурсами: Теперь  можно  поменять  ТМ  сразу  на  все  ресурсы\n    (опция \"Все ресурсы\" в дропдауне выбора ресурсов). При этом вводимая  сумма\n    будет разделена на три части и на  каждую  из  этих  третей  будет  куплено\n    соответствующее количество ресурсов по курсу. Стоимость такой операции -  в\n    три раза больше базовой стоимость обмена\n[+] Квесты: Теперь в награду за исполнение квеста можно одновременно ставить до\n    четырех видов ресурсов\n\n\n2012-01-14 04:34 33a8\n[!] Вселенная: Галактики и системы могут иметь собственные названия!\n    1. По умолчанию галактики и системы не имеют собственных названий\n    2. Увидеть текущее имя галактики или системы можно на странице \"Вселенная\"\n    3.  Назвать  галактику  или  систему  можно   по   ссылке   \"Переименовать\"\n    соответственно  возле  координат  галактики   или   системы   на   странице\n    \"Вселенная\"\n    4. Именование галактики или системы имеет  соотвествующую  стоимость  -  по\n    умолчанию 10000 ТМ для галактики и 1000 ТМ  для  системы  -  т.н.  \"базовую\n    стоимость  именования\".  Изменить  базовую  стоимость  именования  можно  в\n    настройках  сервера.  Игроки  могут  видеть   текущую   базовую   стоимость\n    именования на странице \"Мировые константы\"\n    5.  При  именовании  галактики  или  системы  игрок  может  назначить  цену\n    именования. Минимальная цена именования равна базовой стоимости именования\n    6. При переименовании уже именованной галактики или системы,  игрок  должен\n    уплатить ранее назначенную  стоимость  именования  плюс  базовая  стоимость\n    именования. Таким образом,  если  первый  игрок  назначил  цену  именования\n    системы в 2500 ТМ, а базовая цена именования системы составляет 1000 ТМ, то\n    игрок, желающий переименовать ту же систему должен уплатить не меньше  3500\n    ТМ. Таким образом более высокая цена именования  галактики  или  системы  в\n    определенной степени защищает объект от переименования\n    7. Как было сказано в п.2, имя галактики и системы видны всем игрокам\n\n\n2012-01-09 03:53 33a7\n[!] Наемники: Добавлена поддержка временных Наемников  (ВН).  Осталась  так  же\n    поддержка постоянных Наемников (ПН). Тип Наемников выбирается в  настройках\n    сервера\n    1. ВН (как следует из названия) не являются постоянными,  а  нанимаются  на\n    определенный срок. По истечению срока Наемник исчезает\n    2. В режиме ВН отсутствует понятие \"веток развития\" и  для  найма  доступны\n    сразу все Наемники. Соответственно не отображаются требования  к  Наемникам\n    на странице \"Технологии\"\n    3. Базовая цена покупки ПН в режиме ВН становится ценой  найма  на  базовый\n    период найма (БПН). По умолчанию он равен одному среднекаелндарному  месяцу\n    (30 дней, 2592000 секунд). Изменить БПН можно на странице настроек сервера\n    4. Предусмотрена система скидок/наценок  в  зависимости  от  срока  покупки\n    Наемника.  Настройки  содержатся  в  массиве  $mrc_hire_discount  в   файле\n    /officer.php.  Индекс  элемента  -  количество  секунд  найма,  значение  -\n    коэффициент скидки. \"1\" означает, что  на  данный  интервал  найма  нет  ни\n    наценки, ни скидки и при пересчете на количество секунд в БПН его стоимость\n    будет в точности равна стоимости БПН.  Если  число  меньше  единицы  -  это\n    означает скидку; больше единицы - наценку\n    5.  После  рекрутирования  Наемника  невозможно  изменить  его  уровень  до\n    окончания срока действия найма\n    6. Режим Наемников отображается на странице \"Мировые константы\"\n    При переключении режима Наемников следует учитывать следующие особенности:\n    1.  При  включении  ВН  все  постоянные  Наемники  будут  преобразованы  во\n    временные со сроком действия равному БПН. В случае  необходимости  изменить\n    БПН нужно СНАЧАЛА его изменить, а затем переключать режим работы Наемников\n    2. После включения ВН изменение базового интервала найма не влияет  на  уже\n    рекрутированных Наемников, а влияет только на цену нового найма\n    3.  При  отключении  ВН  все  активные  на  этот  момент   Наемники   будут\n    преобразованы в постоянные - вне зависимости от того,  на  какой  срок  они\n    были наняты и сколько времени осталось до срока истечения найма. Информация\n    о сроках найма при этом теряется\n    4.  При  отключении  ВН  активизируются   ограничения   по   рекрутированию\n    Наемников, однако уже нанятые Наемники останутся активными и  будут  влиять\n    на игру вне зависимости от того, может  игрок  их  купить  или  нет.  Такой\n    способ переключение выбран для исключения потери ТМ, вложенных  игроками  в\n    Наемников\n    Переработана  страница  рекуртирования  Наемников:\n    1. Добавлена поддержка временных Наемников\n    2. Стоимость найма  отображается  динамически  в  зависимости  от  текущего\n    режима Наемников, выбранного уровня и срока найма\n    3. В режиме ПН видны все наемники - даже  недоступные  (с  соответствующими\n    пояснениями)\n    4. В режиме ПН можно нанимать сразу несколько уровней Наемников\n\n[~] Админка: Добавлены label for для всех чекбоксов\n\n\n2012-01-06 15:09 33a6\n[!] Альянс: Добавлена поддержка серверных логотипов  Альянсов.  Логотипы  могут\n    быть  загружены  с  локального  диска  на  странице  управления   Альянсом.\n    Поддерживаются файлы форматов JPG, GIF и PNG размером до 200КБ. Загруженные\n    картинки будут отмасштабированы до размеров 128х128\n    Логотип отображается на странице информации об Альянсе и в  попапе  Альянса\n    на странице \"Вселенная\"\n\n[+] Настройки: Отображение логотипов Альянсов и аватаров  игроков  на  странице\n    \"Вселенная\" может быть отключено в настройках игроков\n\n\n2012-01-05 03:26 33a5\n[!] Аватары: Добавлена поддержка серверных аватаров игроков. Аватары могут быть\n    загружены  с  локального   диска   на   странице   настроек   пользователя.\n    Поддерживаются файлы форматов JPG, GIF и PNG размером до 200КБ. Загруженные\n    картинки будут отмасштабированы до размеров 128х128\n    Аватар отображается на странице \"Император\" и в попапе игрока  на  странице\n    \"Вселенная\"\n\n[+] Пол: Добавлено отображение пола игрока на странице \"Император\" и  в  попапе\n    игрока на странице \"Вселенная\"\n\n[@] Аватары: Для корректной  работы  подсистемы  аватаров  в  PHP  должен  быть\n    корректно настроен временный каталог и движку должна быть разрешена  запись\n    в каталог /images/avatar\n    Максимальный размер аватара настраивается  в  таблице  `config`  переменные\n    avatar_max_width и avatar_max_height\n[@] Пол: В скины добавлена иконка пола в подкаталог \"images\" скина.  Файлы  для\n    мужского  и  женского  пола  называются  соответственно  \"sex_male.png\"   и\n    \"sex_female.png\". Встроенные скины обновлены автоматически\n\n\n2012-01-04 23:12 33a4\n[~] Экономика: При расчете  времени  постройки  юнитов  учитывается  не  только\n    количество ресурсов, но и  их  качество.  Время  постройки  нормированы  по\n    дейтерию, т.е. постройки с большей долей  низкоуровневых  ресурсов  строятся\n    быстрее\n\n\n2012-01-04 14:32 33a3\n[~] Чат: Теперь при таймауте чата скрипт поллинга полностью прекращает работу\n\n\n2012-01-04 12:08 33a2\n[%] Партнерка: Исправлено отображения количества начисленных ТМ\n\n\n2012-01-04 11:41 33a1\n[~] Император: Теперь на  страницы  показывается  так  же  очки  за  ресурсы  и\n    исследования. Немного переформатирована таблица статистики\n[~] Статистика:  Оптимизирован   алгоритм   подсчета   статистики.   Отключено\n    обновление \"очков планеты\"\n\n[@] $sn_data['groups']['prod'] => $sn_data['groups']['factories']\n\n\n2012-01-03 11:11 33a0\n[~] Постройки: Информация о  постройках  кэшируется  при  заходе  на  страницу.\n    Ширина таблицы построек устанавливается на максимальную из  возможных.  Это\n    предотвращает \"баян\" - прыжки ширины таблицы построек\n\n\n2011-09-19 17:16 32d0\n[!] Project \"SuperNova.WS\" Release 32 \"Happy New Year v2012!\"\n\n\n2011-12-30 10:04 32c1\n[%] Флоты: Исправлена уязвимость, позволяющая передавать флоты  другим игрокам.\n    Ненавижу хНову!\n\n\n2011-09-19 17:16 32c0\n[!] Project \"SuperNova.WS\" Release 32 RC \"Happy New Year v2012!\"\n\n[@] DB: Обновлен дамп БД до версии 32\n\n\n2011-12-24 15:12 32a16\n[%] Исправлена ошибка с расчетом ранга в статистике из 32a15\n[%] Исправлена ошибка с расчетом времени постройки строений из 32a15\n\n\n2011-12-21 13:37 32a15\n[~] Рекорды: Переписана страница рекордов\n\n[%] Рекорды: Исправлена ошибка, когда в списке  рекордсменов  появлялись  члены\n    команды сервера\n\n[@] PTE: Парсер темплейтов теперь понимает конструкции вида  {L_tech[D_CONST]},\n    которые будут развернуты в $lang['tech'][CONST]\n[@] eco_get_build_data() возвращает  время  постройки  в  отдельном  подмассиве\n    RES_TIME, а не в массивах действий BLD_CREATE/BLD_DESTROY\n[@] Числовые идентификаторы для строений заменены константами STRUC_xxx\n\n\n2011-12-16 21:20 32a14\n[%] Исправлена уязвимость, позволяющая быстрыми многократными  постройками  или\n    отменами добиться увеличения ресурсов\n\n\n2011-12-12 04:24\n[~] Статистика:  Статистика  теперь  считается  не  в  транзакции,  что  бы  не\n    блокировать игроков. Сохраняется статистика за 10 дней\n\n\n2011-12-12 04:05 32a13\n[~] Экономика:  Увеличена  базовая  добыча  шахты  кристаллов  с  20  до   32.\n    Соответственно увеличено энергопотребление с 10 до 16\n\n[%] Друзья: Исправлена надпись с неправильной кодировкой при ответе  на  письмо\n    друга/кандидата\n\n\n2011-12-09 21:30 32a12\n[~] Верфь: Обновлен интерфейс верфи\n[~] Верфь: Ограничено максимальное количество строящихся юнитов на верфи\n\n\n2011-12-03 22:26 32a11\n[~] Скины: Обновлен скин supernova-ivash\n\n\n2011-11-27 06:08 32a10\n[+] Постройки:  На   ресурсогенерящие   постройки   добавлена   подсказка   по\n    производительности\n\n[~] Экономика: Производство и потребление электроэнергии теперь  масштабируется\n    согласно скорости добычи\n\n\n2011-11-27 01:34 32a9\n[~] Постройки: Из темплейта постройки задний яваскрипт вынесен в отдельный файл\n[~] Постройки: Увеличено  место  для  картинки  здания  до  150  пикселей.  Это\n    предотвратит \"скачки\"  описания  постройки  при  перемещении  выделения  на\n    иконках\n\n\n2011-11-25 17:11 32a8\n[%] Статистика: Исправлены ошибки в работе статистики\n[%] Админка: Исправлена ошибка самопроизвольного сброса  флага  масштабирования\n    хранилищ\n\n\n2011-11-20 14:45 32a7\n[~] Фаланга: Исправлена ошибка с неправильными названиями полей в iraks\n\n\n2011-11-13 20:50\n[!] Исправлена работа с большими числами:\n      Все числовые значение в HTTP-запросах трактуются как  числа  с  плавающей\n      запятой. Все идентификаторы передаются как строки\n      Все идентификаторы в  БД  являются  BIGINT(20).  Соответствующим  образом\n      переконфигурированы (добавлены или изменены) FOREIGHN KEYS\n      Переработаны  все  таблицы,  что  бы  исключить  переполнение  при  любом\n      разумном сценарии  использования  движка  (скажем,  вплоть  до  скоростей\n      x1000000)\n\n[%] Флоты: Исправлена ошибка при загрузки в  трюмы  одного  ресурса  более  чем\n    PHP_MAX_INT\n\n\n2011-11-06 20:24 32a6\n[~] Постройки: Теперь при невозможности постройки юнита не затемняется название\n    и текущий уровень юнита\n[~] Постройки: В описании юнитов разнесены ссылки на  постройку  и  уничтожение\n    юнита во избежании случайного выбора не того действия\n\n[~] Постройки: Ссылки на создание и  уничтожение  юнитов  раскрашены  в  цвета,\n    согласно  CSS  (по  умолчанию:  зеленый  для  создания,   красный   -   для\n    уничтожения)\n\n[%] Постройки: Исправлена ошибка  с  невозможностью  выбора  превьюшки  здания,\n    которое нельзя построить\n[%] Постройки: Исправлена ошибка с невозможностью уничтожить  постройку,  когда\n    не хватает ресурсов на создание постройки, но хватает на её уничтожение\n\n\n2011-10-24 17:02 32a5\n[~] Флоты: Теоретическая емкость флота увеличена до BIGINT(20) по каждому  типу\n    ресурсов\n\n\n2011-10-24 01:01 32a4\n[~] Скины: Обновлен скин СН/Иваш\n\n\n2011-10-22 15:38\n[~] Постройки: На лунах из списка доступных построек убрана ТЭ\n\n\n2011-10-22 15:33 32a3\n[%] Альянсы: Исправлена ошибка с неотображением тэга  у  членов  новосозданного\n    Альянса\n\n\n2011-10-22 15:06 32a2\n[%] Вселенная: Исправлена ошибка с запуском  отрицательного  количества  юнитов\n    через AJAX\n\n\n2011-10-08 14:51 32a1\n[~] Постройки:  Показываются  все  возможные  строения  на  планете/луне.  Если\n    невозможно осуществить постройку - показывается причина\n    Иконки  информации  и   своза   ресурсов   поменялись   местами,   что   бы\n    соответствовать  расположению  иконки  своза  ресурсов  на  списке   планет\n    (Обзор/Империя)\n\n\n2011-10-03 19:08 32a0\n[~] Скины: СН/Иваш - заменен бэкграунд в ячейках заголовка таблицы\n\n\n2011-09-30 11:10 31d0\n[!] Project \"SuperNova.WS\" Release 31 \"Artifact governors edit localized inflation\"\n\n\n2011-09-28 22:32 31c4\n[+] Император: Добавлено отображение исследовательских уровня, текущего опыта и\n    опыта, необходимого для перехода на следующий уровень\n\n[%] Император: Исправлена ошибка отображения  опыта  для  следующего  уровня  -\n    показывался опыт, необходимый для набора текущего уровня\n\n\n2011-09-27 13:54\n[%] Локализация:  Восстановлены  названия  кораблей  в  описаниях  двигательных\n    технологий\n\n\n2011-09-25 18:09 31c3\n[!] Скины: Новый скин \"supernova-ivash\". Автор - Ivash\n\n[+] Новапедия: Включена страница информации для Артефактов. Добавлены  описания\n    всех стандартных ресурсов на русском и английском языках, а так же включена\n    страница информации для них\n\n[%] Исправлен  неработающий   просмотр   бана   при   входе   заблокированного\n    пользователя\n\n\n2011-09-23 00:22 31c2\n[%] Технологии: Исправлена ошибка с исследованием Гравитехи  при  отрицательном\n    балансе энергии\n\n\n2011-09-22 17:15\n[%] Отладка: Файл отладки перекодирован в UTF8\n\n\n2011-09-21 22:14 31c1\n[%] Технологии: Исправлена ошибка с невозможностью исследования  Гравитационной\n    техонологии\n\n\n2011-09-19 17:16 31c0\n[!] Project \"SuperNova.WS\" Release 31 RC \"Artifact governors edit localized inflation\"\n\n\n2011-09-18 13:37\n[%] Бой: Исправлена ошибка с Гипертранспортом\n\n\n2011-09-17 22:32\n[~] Верфь: Заменена картинка Гипертранспорта на аналог в другой палитре\n\n\n2011-09-16 23:46\n[~] Здания: Теперь при количестве нужных ресурсов для постройки больше 1ккк  на\n    превьюшках постройки показывается укороченное название ресурсов\n\n\n2011-09-16 22:58\n[~] Админка: Редактирование планеты теперь показывает юниты только если выбрана\n    планета для редактирования\n\n\n2011-09-15 23:04 31a19\n[!] Флот: Новый корабль - Гипертранспорт. Предназначен для  ТОП  игроков  и/или\n    скоростных Вселенных\n\n\n2011-09-15 12:17\n[%] Артефакты: МАКК разворачивает робофабрику 4го уровня - согласно описанию\n\n\n2011-09-14 21:34\n[~] Технологии:  Пересмотрены  технологии.  Устранены  противоречия  в  ветках\n    развития  (например,  ионный   двигатель   можно   было   исследовать   без\n    технологии). Технологии переупорядочены в более логичном порядке\n\n\n2011-09-12 16:05\n[%] Здания: Исправлена невозможность постройки терраформера\n\n\n2011-09-12 12:39\n[%] Шпионаж: Ссылка в отчете на  просканированную  планету  теперь  переключает\n    галактику и систему\n\n\n2011-09-12 12:31\n[%] Исследования: Исправлена  ошибка  бесплатного  исследования  Гравитационной\n    технологии\n\n\n2011-09-11 18:22 31a18\n[+] Строительство/Здания: В ссылке на уничтожение здания указывается количество\n    требуемых ресурсов и время\n\n[-] Новапедия: Со страницы информации о зданиях убрана  ссылка  на  уничтожение\n    здания\n\n[@] Перераспределены функции между файлами\n\n\n2011-09-11 13:03\n[~] Фаланга: Изменение алгоритма - фаланга теперь не  видит  флоты,  летящие  с\n    луны. Так же фаланга не видит флоты, летящие с заданием \"Удержание\"\n[~] Локализация: Ни один из встроенных языковых доменов больше не  нуждается  в\n    ручном редактировании после использования редактора локализаций\n\n\n2011-09-11 00:35 31a17\n[~] Фаланга:  Исправлен  и  переработан  алгоритм  работы  фаланги.  Добавлено\n    исключение при полете флота с луны:\n      Особый случай: флот летит с луны A --> B\n       a) флот виден только на B, но не на луне A\n       b) возвращающийся на луну флот не виден нигде\n\n\n2011-09-10 13:49\n[~] Губернаторы: Эффект Фортификатора  приведен  в  соответствие  с  описанием.\n    Теперь  он  улучшает  не  технологии  хозяина  планеты,  а  непосредственно\n    характеристики флота и обороны\n\n\n2011-09-08 22:21 31a16\n[!] Тёмная Материя: За исследования начисляются опыт,  за  опыт  -  уровни,  за\n    уровни - ТМ. Таблица необходимого опыта  для  набора  уровней  эквивалентна\n    таблице опыта  за  постройки  (см.  /docs/readme.txt).  За  каждый  уровень\n    начисляется 1000 ТМ\n\n\n2011-09-07 21:36 31a15\n[+] Экономика: Новая  настройка  сервера  \"Масштабировать  склады  от  скорости\n    добычи\". Настройка доступна в общих настройках сервера  в  разделе  \"Прочие\n    параметры\". По умолчанию возможность отключена\n\n[%] Локализация/EN: Исправлены некоторые очепятки\n\n[@] Админка/Настройки:  Состояние  все  чекбоксов  (включен/выключен)   теперь\n    определются в темлейте\n\n\n2011-09-05 22:28\n[%] Шпионаж: Исправлена ошибка в сообщении об детектировании шпионажа\n\n\n2011-09-04 18:25\n[%] Локализация/EN: Исправлены ошибки с двумя l в \"Metall\" и \"Crystall\"\n\n\n2011-09-04 01:06 31a14\n[~] Шпионаж: Оптимизирована процедура генерации шпионского рапорта\n\n[%] Шпионаж: Корректно выставляется время шпионского рапорта\n\n\n2011-09-03 01:42\n[~] Партнерка: Добавлено  ограничение  по  минимальному  количеству  ТМ,  после\n    которого   начинается   начисление   бонусов   реферралу    -    переменная\n    \"rpg_bonus_minimum\" в таблице \"config\"\n[%] Партнерка: Исправлена ошибка в количестве начисляемых бонусов\n\n\n2011-09-01 15:39\n[~] Вселенная: При обнаружении  планеты  с  отсутствующим  пользователем  в  БД\n    планета удаляется с отсрочкой 24 часа\n\n\n2011-08-31 23:24\n[%] Локализация: Исправлена ошибка локализации отношения Альянсов\n\n\n2011-08-31 23:08\n[%] Новапедия: Убрана отладка на кораблях\n[%] Император: Изменена нелокализованная строка\n\n\n2011-08-31 03:15 31a13\n[!] Артефакты: Добавлены Артефакты - Автономный  Колонизирующий  Комплекс  трех\n    уровней\n\n\n2011-08-30 23:25\n[%] Локализация:  Добавлен  патч  для  серверов  с   неправильно   настроенной\n    кодировкой в HTTP-заголовке\n\n\n2011-08-30 02:19\n[~] Артефакты: При нехватке  ТМ  для  покупки  Артефакта  выводится  не  только\n    уведомление, но и цена Артефакта\n\n\n2011-08-30 02:00\n[~] Новости: В \"Обзоре планеты\" выводятся только  непрочитанные  новости  и  не\n    более, чем указано в настройках сервера (по умолчанию - 3)\n\n\n2011-08-30 01:40 31a12\n[!] Артефакты: Добавлена подсистема Артефактов - редких объектов с  уникальными\n    свойствами. Артефакты можно купить за ТМ. Артефакты являются одноразовыми -\n    после  использования  Артефакт  исчезает.  Некоторые  Артефакты   настолько\n    мощные,  что  их  количество  в  одной  Империи  ограничено.  Использование\n    некоторых  Артефактов  привязано  к  планетам  -  т.е.  их   эффект   будет\n    распространятся  только  на  эту   планету.   Эффекты   других   Артефактов\n    распространяются на всю Империю. Особо  мощные  Артефакты  могут  оказывать\n    влияние на солнечную систему, галактику или даже Вселенную\n    Новый Артефакт - \"Большой Адронный Колайдер\"\n\n\n2011-08-28 22:29\n[%] Экспедиции: Исправлена ошибка неначисления ТМ\n\n\n2011-08-28 18:45 31a11\n[!] Экономика: ТМ смасштабирована в отношении 1 к 1000. Т.е. весь приход  ТМ  и\n    все цены в ТМ увеличены в 1000 раз. Так же отмасштабированы награды текущих\n    квестов\n\n[+] Вселенная: В попап Альянсов добавлен его ранг\n\n[~] Статистика: Изменен  расчет  статистики.  Теперь  в  тратах  каждый  ресурс\n    считается согласно курсу обмена.  Таким  образом  игроки  с  более  ценными\n    ресурсами получат больше очков\n[~] Поиск: Оптимизированы запросы поиска\n    При поиске Альянса поиск подстроки происходит одновременно в имени и тэге\n\n[@] БД: В таблице `users` убраны неиспользуемые поля. Изменены типы  нескольких\n    полей на более логичные. Добавлено поле `ally_tag`\n    В таблицы `users` и `alliance` добавлены поля с текущими значениями ранга и\n    очков статистики\n\n\n2011-08-24 23:22 31a10\n[!] Локализация: Добавлены копирайты в файлы локализации\n\n\n2011-08-24 23:13\n[!] Редактор локализаций: Оптимизирована работа редактора\n    Редактор теперь корректно работает с константами внутри файлов локализаций.\n    Список  файлов,  требующих  ручного  редактирования   после   использования\n    редактора теперь включает только \"options\" и \"system\"\n    Добавлена возможность добавления и удаления строк локализации\n[!] Локализация: Все файлы локализации пропущены через редактор и  приведены  к\n    одному виду\n\n\n2011-08-24 06:56 31a9\n[!] Локализация:  В  админке  добавлен  редактор  локализаций  -  пункт   меню\n    \"Локализация\" в разделе \"Утилиты\". Логика работы редактора следующая:\n    1. Выбор пункта  меню  \"Локализация\"  открывает  выбор  т.н.  \"домена\"  для\n    редактирования. Домен - это совокупность строк локализации,  относящихся  к\n    отдельному   аспекту   игры.   Домен   эквивалентен   языковому   файлу   с\n    соответствующим именем\n    2.  После  выбора  домена  и  подтверждения  выбора  открывается   страница\n    редактирования строк локализации. Открытие больших  файлов  может  занимать\n    существенное время - поэтому запаситесь терпением\n    3. После редактирования строк локализации и подтверждения редактор  создаст\n    файлы \"<имя домена>.mo.new\" в каждой папке языка\n    4. Файлы .mo.new имеют приоритет перед обычными  .mo  файлами  локализации.\n    Т.е. если в одном языковом каталоге присутствуют оба типа файлов,  редактор\n    загрузит для редактирования .mo.new\n    5. Для того, что бы движок  подгрузил  новый  файл  локализации,  требуется\n    изменить его расширение с .mo.new  на  .mo.  Обычно  это  перезапишет  файл\n    текущий файл локализации - поэтому следует заранее  сделать  его  резервную\n    копию\n    6. ВНИМАНИЕ! Следует соблюдать осторожность при  замене  старых  файлов  на\n    новые! Редактор разрешает идентификаторы констант в их  реальное  значение,\n    не  сохраняет  комментарии  и  игнорирует  дополнительный  код   в   файлах\n    локализации - включая подстановку значений в infos.mo! В результате простая\n    перезапись файлов может нарушить нормальную работу  подсистемы  локализации\n    движка!   Для   отдельных   файлов   может   потребоваться   дополнительное\n    редактирование \"ручками\" и добавление  исполнимого  кода  из  оригинального\n    файла!\n    7. Следующие домены используют дополнительный  PHP-код  и  требуют  ручного\n    вмешательства при редактировании: fleet, infos,  login,  market,  messages,\n    options, system\n    8.  Следующие  домены   используют   константы   alliance,   tech,   quest.\n    Рекомендуется ручное редактирование этих файлов, однако оно не требуется\n\n\n2011-08-23 15:18\n[%] Чат: Исправлена кодировка чата\n\n\n2011-08-23 14:41 31a8\n[!] Локализация: СуперНова использует  кодировку  UTF-8  при  работе  с  БД  и\n    рендере HTML-страниц. Таким образом поддерживаются любые наборы символов\n\n\n2011-08-23 00:01 31a7\n[!] Локализация: Язык и кодировка теперь зависят от загруженного языка\n\n\n2011-08-22 22:57 31a6.1\n[+] Наемники: Добавлено подтверждение при  покупке  губернатора,  отличного  от\n    текущего. Добавлена защита от повторной покупки губернатора  при  случайном\n    обновлении страницы\n\n\n2011-08-22 21:50 31a6\n[+] Наемники: Фортификатор дает хозяину планеты бонус 10% за каждый  уровень  к\n    атаке, броне и щитам при обороне\n\n\n2011-08-22 21:04 31a5.1\n[~] ТМ: Изменения в ТМ текущего игрока отображаются сразу по факту\n\n[%] ТМ: Исправлена ошибка вычета  ТМ  при  найме  губернаторов,  которая  могла\n    привести к отрицательному количеству ТМ у игрока\n\n[@] ТМ: Изменение ТМ в переменной $user производится в теле rpg_points_change\n\n\n2011-08-22 00:28\n[~] Список планет: Добавлена всплывающая подсказка на иконку губернатора\n\n\n2011-08-21 23:01 31a5\n[+] Новости: Добавлена лента новостей на  страницу  обзора  планеты.  Выводятся\n    только 3 последние свежие новости\n\n[@] Новости:  На  странице  новостей,  странице  Императора  и  обзоре  планеты\n    рендерятся одной процедурой и используют один  темплейт.  Индикатор  свежих\n    новостей теперь  ориентируется  на  дату  просмотра,  а  не  на  количество\n    новостей\n[@] ТМ: Исправлена лишняя ошибка в логах, если количество изменяемой ТМ равно 0\n    (например, при бесплатном рынке или офицерах)\n\n\n2011-08-21 21:39\n[@] Апдейтер: Доработан апдейтер, что бы предотвратить зацикливание\n[@] Админка: В \"Утилиты\" добавлена  возможность  форсировать  только  последний\n    апдейт\n\n\n2011-08-21 21:20\n[~] Губернаторы: Надпись \"Нет губернатора\" изменена на \"Нанять губернатора\"\n\n\n2011-08-21 20:20 31a4\n[+] Список  планет:  На  изображения  планет   добавлен   индикатор   текущего\n    губернатора и его уровень\n[~] Управление планетой: На лунах убран губернатор \"Технолог\", как  не  имеющий\n    смысла\n\n\n2011-08-21 18:29 31a3.1\n[!] Наемники: Произведены следующие изменения в наемниках (бывш. \"офицеры\"):\n      1. Академик опять стал общеимперским наемником\n      2. Максимальный уровень карго-мастера увеличен до 20\n      3. Защитник упразднен, а его функции переданы Фортификатору (см. ниже)\n      4. Реорганизованы ветки развития наемников\n        \"Шахтерская\" теперь выглядит как \"Карго-мастер\" - \"Шпион\" -  \"Академик\"\n        - \"Разрушитель\"\n        \"Атакерская  \"  теперь  выглядит  как  \"Адмирал\"  -   \"Координатор\"   -\n        \"Навигатор\" - \"Ассасин\"\n    Произведены следующие изменения в губернаторах:\n    1. Новый губернатор \"Технолог\" объединяет функции Геолога и  Энергетика\n       БС = 1, Фактор = 1.06, стоимость 20 уровня - 31 ТМ, не имеет ограничения\n       по уровню. С 5-го уровня позволяет строить  термоядерную  электростанцию\n    2. Новый губернатор \"Инженер\" объединяет функции Архитектора и Конструктора\n       БС = 0.5,  Фактор  =  1.25,  стоимость  на  10-м  уровне  -  10  ТМ,  на\n       макисмальном 15-м - 63 ТМ\n    3. \"Фортификатор\" так же исполняет для планеты функции Защитника\n       БС = 2, Фактор = 1, стоимость максимального 8-го уровня - 16 ТМ. С  3-го\n       уровня позволяет  строить  Планетарную  защиту\n\n\n2011-08-20 04:08 31a3\n[!] Наемники:  Произведено  разделение  наемников  на  \"просто  наемников\"   и\n    \"губернаторов\".   \"Просто   наемники\"   обладают   всеимперским    бонусом.\n    \"Губернаторы\" обладают локальным планетарным бонусом  и  должны  покупаться\n    индивидуально на каждую планету\n    Система губернаторов cбалансирована следующим образом:\n    1.  В  губернаторы  произведены  следующие  офицеры:   Геолог,   Энергетик,\n    Архитектор, Конструктор, Академик, Фортификатор\n    2. Вся ТМ за бывших офицеров возвернута игрокам\n    3. Балансировка губернаторов проведена из  расчета  на  \"среднего  игрока\",\n    имеющего 3 планеты. Такие игроки не получат  пенальти  при  оснащении  всех\n    планет  губернаторами.  Понятно,  что  общая   производительность   Империи\n    уменшитя, однако это те жертвы, на  которые  я  готов  пойти.  В  целом  же\n    изменение направлено на уменьшение среднего  количества  ТМ  у  игроков.  В\n    особенности - у топов и саб-топов\n    4. Стоимость наемников теперь рассчитывается по формуле:\n      БС * (Фактор ^ Уровень), где\n      БС - базовая стоимость наемника\n      Фактор - заранее заданная величина\n      ^ - операция возведения в степень\n      Уровень - новый уровень наемника\n    5. Оставшиеся офицеры-наемники перебалансированы с учетом изменений\n    Губернаторы изменены следующим образом:\n    1. Геолог. БС = 1, Фактор = 1.1. Базовый губернатор  и  -  самый  полезный.\n    Доступен игрокам практически с самого начал - БС составляет всего 1 ТМ.  Не\n    имеет ограничений по уровню - поэтому имеет такой высокий Фактор\n    2. Энергетик. БС = 1, Фактор = 1.025. Специфический губернатор,  нужный  на\n    планетах с очень низкой температурой.  Откровенно  говоря  -  слабополезен,\n    поэтому очень дешев. И поэтому же не имеет ограничений по уровню\n    3. Архитектор. БС = 0.6, Фактор = 1.2. Чрезвычайно  полезен  для  застройки\n    новой колонии. Незаменим для начинающих игроков.  После  отстройки  колоний\n    скорее всего будет заменен на другого офицера - в зависимости от назначения\n    планеты. Весьма доступен из-за низкой БС,  однако  не  дает  топам  особого\n    преимущества  из-за  высокого  Фактора.  Ограничен  15-м  уровнем\n    4. Конструктор. БС = 0.2, Фактор = 1.3. Позволяет начинающим игрокам  после\n    отстройки колоний быстрее  производить  стартовый  флот.  Что  бы  избежать\n    злоупотреблений наемника топами имеет очень высокий Фактор. Ограничен  15-м\n    уровнем\n    5. Академик. БС = 0.2, Фактор = 1.3. Аналогично Конструктору  имеет  низкую\n    стартовую цену что бы помочь начинающему игроку  исследовать  технологии  и\n    очень высокий Фактор, что бы воспрепятствовать  топам  использовать  низкую\n    цену. Ограничен 15-м уровнем\n    6. Фортификатор. БС = 2, Фактор =  1.  Офицер  для  \"поплавков\".  Т.е.  для\n    топов, имеющих возможность делать \"поплавки\". Характеристики  -  временные.\n    Планирутся добавление бонуса к боевым характеристикам  защитных  сооружений\n    и, возможно, планетарного флота. Ограничен 8-м уровнем\n\n\n2011-08-19 16:08\n[~] Постройки: Оптимзировано удаление построек из очереди\n\n\n2011-08-17 02:07 31a2\n[!] Межгалактические Врата: Интерфейс Врат  вынесен  на  отдельную  страницу  и\n    доступен с Обзора планеты (куда вынесен таймер готовности врат)\n    Все таймеры врат заменены на sn_timer\n\n\n2011-08-16 23:16\n[@] Новапедия: InsertJavaScriptChronoApplet перенесен в infos.php. Основной код\n    вынесен из функции в тело файла\n\n\n2011-08-15 01:23 31a1\n[%] Флоты: Добавлен патч для mysql-серверов со  включенным  STRICT_TRANS_TABLES\n    при отправке флотов\n\n\n2011-08-13 11:31\n[~] Вселенная: Исправлена ошибка отправки ракет\n\n\n2011-08-09 14:40\n[%] Админка: Исправлена ошибка неначисления ТМ  при  вводе  имени  планеты  или\n    пользователя\n\n\n2011-08-08 03:24\n[%] Флоты: Исправлена ошибка со свозом ресурсов\n\n\n2011-08-08 03:10\n[%] Флоты: Исправлены ошибки с отправкой флота\n\n\n2011-08-08 02:11\n[~] Луна: Имя создаваемой луны теперь не такое длинное\n\n\n2011-08-08 00:53 31a0\n[+] Обзор  планеты/Обзор  Империи:  Таймер  очереди  строительства  зданий  под\n    иконкой планеты теперь переключается на  следующее  здание  в  очереди  при\n    окончании строительства текущего. Ранее таймер  показывал  только  прогресс\n    постройки первого здания в очереди\n[+] Инфо/Флот: Показываются текущие характеристики корабля (с учетом  наемников\n    и технологий)\n\n[@] SYS: Устаревшие функции заменены актуальными аналогами:\n      int_buildCounter => tpl_parse_planet\n      GetTargetDistance, GetMissionDuration, GetFleetConsumption =>\n        flt_travel_data\n      GetShipConsumption, get_ship_speed => get_ship_data\n      GetFleetMaxSpeed => flt_fleet_speed\n[@] SYS: В описании структуры кораблей (vars.php) данные о двигателях  вынесены\n    в  отдельный  массив  'engine'.  Теперь  можно   указывать   неограниченное\n    количество двигателей для апгрейда корабля\n\n\n2011-08-07 18:13 30d4\n[%] Локализация: Исправлена ошибка загрузки не-дефолтного языка\n\n\n2011-08-06 18:10\n[!] Документация: Добавлен файл с английской версией  инструкции  по  установке\n    /docs/install-en.txt (google translated)\n\n\n2011-08-06 15:01 30d3\n[!] Локализация:  Выбранный  при  регистрации  язык  сохраняется  в  настройках\n    пользователя. Все страницы, доступные до  входа  в  игру,  показываются  на\n    выбранном языке\n\n\n2011-08-06 14:01 30d2\n[!]  Локализация:  Доступен  выбор  языка  до  входа  в  игру:   на   страницах\nрегистрации, логина, восстановления забытого пароля\n\n[%] Сообщения:  Исправлена  ошибка  отправки  сообщений  от  системы  на  mySQL\n    серверах со STRICT_TRANS_TABLES\n\n\n2011-08-05 13:08 30d1\n[%] Отправка флотов: Исправлена ошибка на mySQL серверах со STRICT_TRANS_TABLES\n\n\n2011-08-05 01:45 30d0\n[!] Project \"SuperNova.WS\" Release 30 \"Emailing phalanx research antiSSF\"\n\n\n2011-08-02 21:35\n[@] Обслуживание: Корректно пересчитывается количество аккаунтов в БД\n\n\n2011-07-27 01:48 30c0\n[!] Project \"SuperNova.WS\" Release 30 \"Emailing phalanx research antiSSF\" RC0\n\n\n2011-07-24 14:27 29d25\n[~] Вселенная:  Теперь  при  создании  луны  с  орбиты  списывается  количество\n    обломков, из которых сформировалась луна\n\n\n2011-07-24 00:25\n[@] Общие: Устаревшие массивы  $pricelist,  $resources,  $reslist,  $sn_groups,\n    $CombatCaps заменены в коде на $sn_data\n\n\n2011-07-23 23:05\n[@] Общие: Устаревшая функция SYS_mysqlSmartEscape заменена на соответствующие\n\n\n2011-07-23 19:15 29d24\n[!] АнтиРМФ: Если флот атакующего уничтожен за один раунд, то:\n      1. Атакующий не получает отчета о бое\n      2. Флоты, находящиеся в удержании так и остаются на орбите\n[+] Сообщения: Изменена процедура  генерации  писем  с  уведомлением  о  боевом\n    отчете следующим образом:\n      1. Если бой  закончился  за  один  раунд  проигрышем  атакующего,  то  он\n         получает сообщение о том, что связь с флотом прервалась и не  получает\n         никакой дополнительной информации (включая ссылку на боевой отчет)\n      2. Теперь все участники боя (включая членов  САБа  и  хозяинов  флотов  в\n         удержании) получают одинаковые письма (кроме случая, описанного в п.1)\n      3. Уведомление о бое всегда содержит потери атакующих  и  оброняющихся  и\n         сведения о поле обломков\n      4. Сведения о вывозе ресурсов с планеты добавляются в  отчет  только  при\n         выигрыше атакующих\n      5. Уведомления теперь корректно окрашиваются  для  всех  участников  боя:\n         красным, если участник проиграл, зеленым - если выиграл, оранжевым - в\n         случае ничьи\n      6. Все числа в уведомлении теперь форматируются\n[~] Боевой отчет: Если в результате боя появилась луна, то в отчет  пишется  её\n    имя, а не имя планеты, на орбите которой она была создана\n\n\n2011-07-23 16:17 29d23\n[+] Чат:  Боевые  отчеты  теперь  преобразуются  в  ссылки.   Из   соображений\n    безопасности работают только ссылки на текущем сервере. По клику на  ссылку\n    боевой отчет открывается в новом окне\n\n\n2011-07-23 15:28 29d22\n[+] Исследования: Полностью переписан интерфейс Лаборатории\n    Очередь исследований приведена к стандартному виду\n    Обработка очереди исследований теперь производится  при  каждом  обновлении\n    страницы, а не только при входе в интерфейс Лаборатории\n\n\n2011-07-22 21:05 29d21\n[+] Исследования: Используется PTL и sn_timer\n[@] Разное: Часть функций помечена как deprecated и должны быть заменены\n\n\n2011-07-22 19:09\n[!] Исследования: Переработан интерфейс страницы исследований\n\n\n2011-07-22 17:13\n[%] Верфь: Ограниченные в количестве  постройки  (щиты,  ПЗ  и  ракеты)  теперь\n    отображают точное количество юнитов, которое можно построить,  а  не  общее\n    количество, исходя из ресурсов на планете\n\n\n2011-07-16 23:47 29d20\n[+] Админка/Список планет: \"Список планет\", \"Список лун\" и  \"Активные  планеты\"\n    используют один и тот же код и шаблон. Список планет теперь показывает  тип\n    планеты, хозяина планеты (имя и ИД), а для лун - родительскую планету и  её\n    ИД\n[~] Админка/Бан: По умолчанию включена галочка РО и выставлен срок бана в 3 дня\n    Бан и разбан объединены в один пункт меню и на одну страницу интерфейса\n[~] Админка/Меню: Объединены разделы \"Планета\" и \"Луна\" в раздел \"Вселенная\"\n    Под названием сервера добавлены часы серверного времени\n\n[%] Сообщения: Исправлены ошибки в CSS\n\n\n2011-07-16 19:23 29d19\n[+] Сообщения:  Изменена  цветовая  кодировка  сообщений.\n    Категории  сообщений переупорядочены\n    Добавлена  возможность  очистить  сообщения  определенной   категории,   не\n    открывая их - на случай переполнения  почтового ящика\n    Добавлена подсказка\n\n[@] Сообщения: Переработаны файлы локализации\n\n\n2011-07-16 19:23 29d18\n[%] Обзор планеты: Исправлено зацикливание обновления страницы после  окончания\n    исследования\n\n\n2011-07-16 19:15\n[~] Офицеры: Уровень офицеров указывается и при максимальном уровне\n\n\n2011-07-15 23:04\n[~] Верфь: Небольшая доработка интерфейса\n[~] Флоты: Переупорядочен список кораблей\n[~] Новапедия: Отредактировано короткое описание большого транспорта\n\n\n2011-07-15 22:29 29d17\n[!] Админка: С нуля создан интерфейс редактирования юнитов/ресурсов на  планете\n    - пункт меню  \"Редактировать\"  в  разделе  \"Планета\".  Он  доступен  членам\n    команды начиная с Оператора (authlevel=2) и выше\n\n\n2011-07-15 19:56 26d16\n[!] Фаланга: Новый алгоритм работы фаланги. Алгоритм работы - почти оффовский:\n    1. Показываются все флоты, летящие от сканируемой планеты или же к ней\n    2. Полет A --> B\n       a) скан B => можно увидеть время прибытия флота\n       b) скан A => можно увидеть время возвращения флота (но не  его  прибытия\n       на B)\n    3. Возвращение B --> A\n       a) скан B => не видно ничего\n       b) скан A => виден возвращающийся флот\n    4. Особый случай: задание \"Передислокация\" A --> B\n       a) флот виден только на B, но не на A\n       b) после отзыва флот нигде не виден\n    5. Полнота информации о флотах зависит от уровня шпионажа (см. ниже)\n[+] Обзор планеты/Фаланга: На  количество  отображаемой  информации  о  летящих\n    чужих флотах влияет эффективный уровень шпионажа (технология+наемник):\n    Меньше 4 - нет информации о летящем флоте\n    4 и выше - видно общее количество кораблей во флоте и везет ли флот ресурсы\n    6 и выше - виден качественный состав флота - т.е. сколько групп кораблей во\n    флоте и сколько кораблей в каждой группе\n    8 и выше - видно точное количество ресурсов в трюмах кораблей\n    10 и выше - виден количественный состав флота\n\n\n2011-07-15 17:40\n[%] sn_timer: Исправлена ошибка подсчета общего времени постройки\n\n\n2011-07-15 17:17\n[@] Настройки: Константа  VACATION_TIME  перенесена  в  таблицу  config  (опция\nplayer_vacation_time). Добавлена опция player_delete_time\n\n\n2011-07-14 03:46 V29d15\n[+] Верфь: Вид очереди построек обновлен. Теперь они выглядят  так  же,  как  и\n    очередь  постройки  зданий.\n    Добавлена возможность удалить последний добавленный элемент из очереди\n    Кнопка \"Построить\" дублируется возле каждого юнита. Функционал  сохранен  -\n    по её нажатию будут построены все выбранные юниты\n[+] Обзор планеты: Вид очереди построек верфи  и  очереди  исследований  теперь\n    аналогичен очереди построек зданий\n\n[@] sn_timer: Таймер корректно работает с очередью, в которой количество юнитов\n    больше 1\n\n\n2011-07-13 15:25 V29d14\n[+] Сообщения:  Теперь  настройки  автоматических  уведомлений   включены   по\n    умолчанию для новых игроков\n\n[%] Локализация/Английский: Исправлены баги в локализации\n\n\n2011-07-13 15:05 V29d13\n[%] Вселенная: Исправлен баг с невозможностью отсылки переработчиков\n\n\n2011-07-13 14:37\n[%] События флота: Исправлен баг с отображением события  \"Прибытие  флота\"  для\n    миссий \"Удержание\" и \"Исследование\" когда флот уже достиг точки  назначения\n    и приступил к выполнению миссии\n\n\n2011-07-12 02:52\n[%]  События  флота:  Исправлен  баг  с  неотображением  названия  планеты  для\n    миссий \"Колонизация\" и \"Исследование\"\n\n\n2011-07-12 02:42\n[~]  Настройки:  Включение  защиты  планет  от   атак   доступно   только   для\n     Администраторов во избежание злоупотреблений\n\n\n2011-07-12 02:16 V29d12\n[+] Фаланга: Переписан вывод фаланги с использованием  функций  СН.  Теперь  он\n    выглядит аналогично списку событий флота на странице \"Обзор планеты\"\n\n\n2011-07-11 02:46\n[%] Верфь: Исправлен баг показа  всех  кораблей,  а  не  только  доступных  для\n    постройки\n\n\n2011-07-10 23:48 V29d11\n[~] Верфь: Переверстан интерфейс Верфи и Обороны\n\n\n2011-07-08 02:30 V29d10\n[+] Обновление: Добавлена возможность форсировать обновление в случае проблем с\n    автоматическим   обновлением.    Возможность    доступна    в    интерфейсе\n    Администратора, пункт меню \"Утилиты\"\n\n\n2011-07-07 21:46 V29d9\n[%] Обновление: Изменен автоапдейтер для корректной конверсии записей ТМ\n\n\n2011-07-07 00:26 V29d8\n[~] Админка: Убрана отладка со страницы сохранения настроек\n\n[%] Обслуживание: Исправлена ошибка при обслуживании таблицы Альянсов\n\n\n2011-07-05 16:45 V29d7\n[~] Обновление: Теперь не логгируются запросы к схеме БД при обновлении\n\n[%] Мировые константы: Убрана отладка\n[%] Обновление: Добавлено имя БД к запросам информации о ключах таблицы\n[%] ТМ: Исправлена ошибка создания записей в логе ТМ\n\n\n2011-07-05 16:14 V29d6\n[%] SQL: Исправлена ошибка с индексами в таблице users\n\n\n2011-07-04 18:50 V29d5\n[~] Обновление:  Ускорен   автоапдейтер   при   повторном   запуске   на   уже\n    сконвертированных таблицах\n\n\n2011-07-04 18:31 V29d4\n[+] Антибашинг: Добавлена возможность настройки системы антибашинга на страницу\n    настроек сервера\n[+] Мировые константы: Добавлена информация о текущих настройках антибашинга\n\n\n2011-07-03 20:25 V29d3\n[~] Отладка: Теперь при отключенной  глобальной  отладке  не  генерируется  лог\n    запросов\n[~] Обновление: Автоапдейтер на время работы отключает отладку вне  зависимости\n    от глобальных настроек\n[~] Обновление: Оптимизирована работа апдейтера для апгрейда с 25-28 релизов\n[~] Обновление: Апдейтер использует собственные процедуры запросов к БД\n\n[%] Настройки:  Исправлена  ошибка  сохранения   настроек,   возникающая   при\n    определенных настройках MySQL\n\n[@] Логи: Изменения Тёмной Материи  вынесены  из  глобального  лога в отдельную\n    таблицу. Это  существенно  облегчило  поиск  неисправностей  на  сервере  и\n    подозрительных действий пользователей. Старые записи перенесены в отдельную\n    таблицу\n[@] БД: Все существующие таблицы сконвертированы в UTF8\n[@] БД: Все существующие таблицы переведены на InnoDB\n\n\n2011-07-03 17:45 V29d2\n[%] Вселенная:  Исправлена  ошибка  создания  новой  планеты  при  регистрации,\n    возникающая при определенных настройках MySQL\n\n\n2011-07-03 16:13 V29d1\n[!] Сообщения:  Добавлена  возможность  пересылки  личных  сообщений  на  емейл\n    игрока. Возможность включается администратором сервера в настройках - опция\n    \"Разрешить  пересылку  ЛС  на  email\".  После  этого  в  настройках  игрока\n    появляются дополнительные опции для всех категорий входящих сообщений\n\n\n2011-06-26 17:44 V29d\n[!] Project \"SuperNova.WS\" Release 29 \"Quest messaging\"\n\n\n2011-06-27 04:09\n[%] Флоты: Исправлена очепятка рапорта миссии \"Уничтожение\"\n\n\n2011-06-26 17:44 V29c4 Project \"SuperNova.WS\" Release 29 Release Candidate 4\n[~] Шпионаж: Изменены сообщения при уничтожении разведфлота\n\n\n2011-06-26 16:52 V29c3 Project \"SuperNova.WS\" Release 29 Release Candidate 3\n[%] Настройки: Исправлена  ошибка,  позволяющая  уйти  в  отпуск  когда  что-то\n    строится или исследуется на неосновной планете\n\n\n2011-06-26 16:28 V29c2 Project \"SuperNova.WS\" Release 29 Release Candidate 2\n[%] Бой: В отчете правильно указывается количество захваченных ресурсов\n[%] Бой: Исправлены предупреждения, выдающиеся в случае,  когда  какой-либо  из\n    флотов пустой\n\n\n2011-06-26 13:16 V29c1 Project \"SuperNova.WS\" Release 29 Release Candidate 1\n[~] Вселенная:  Во  всплывающем  меню  на  поле  обломков  добавлена  индикация\n    количества летящих переработчиков игрока\n\n[%] Вселенная: Исправлены ошибка  индикации  на  поле  обломков  чужих  летящих\n    переработчиков  и  ошибка   невозможности   послать   переработчики   через\n    всплывающее меню, если уже на то же поле летят чужие переработчики\n\n[@] Автоапдейтер: Исправлен апдейтер\n[@] SQL: Исправлен дамп БД\n\n\n2011-06-25 23:49 V29c0 Project \"SuperNova.WS\" Release 29 Release Candidate\n[!] Project \"SuperNova.WS\" Release 29 Release Candidate\n\n\n2011-06-23 00:27\n[%] Альянсы: Исправлены ошибки при роспуске Альянса и выходе игрока из Альянса\n\n\n2011-06-22 23:50\n[%] Альянсы: Исправлена невозможность исключить игрока из Альянса\n\n\n2011-06-21 13:40\n[%] Флоты: Исправлена ошибка с невозможностью просканировать игроков\n\n\n2011-06-21 00:09 V28.1a16\n[~] Мировые  константы:  Добавлен  вывод  информации  о  разешении  прокачки  и\n    разрешении удержания на слабом соаловце\n\n[@] Настройки:   Добавлена  опция  сервера,  разрешающая  удержание  на  слабом\n    со-аловце. Опция \"Разрешить  удержание  на  слабом  соаловце\"  на  странице\n    настроек сервера\n[@] Настройки: Теперь флаг  прокачки  по  умолчанию  отключен  и  его  действие\n    инвертировано: невыставленный флаг ЗАПРЕЩАЕТ  прокачку,  а  выставленный  -\n    разрешает. Таки образом, по умолчанию прокачка (отправка транспортов  более\n    сильному   игроку)   отключена,   а   соответствующая   опция   равна    0.\n    Соответствующие изменения  автоматически  внесены  в  базу  данных,  однако\n    рекомендуется проверить настройки после обновления\n[@] Апдейтер: Исправлена ошибка добавления внешних ключей к таблице users\n\n\n2011-06-19 02:33 V28.1a15\n[~] Обзор планеты: Теперь если планет больше  5,  то  они  показываются  в  две\n    колонки\n\n[%] Обзор планеты: Луна теперь не показывается в списке планет справа -  как  и\n    должно быть\n\n\n2011-06-19 01:48\n[%] Настройки: Теперь при включении режим отпуска игрок уходит в  отпуск  сразу\n    же, а не при следующем обращении к браузеру\n\n\n2011-06-18 15:37 V28.1a14\n[~] Навбар: Клик на индикаторе сообщений Администрации, Альянса или от  другого\n    игрока сразу открывает просмотр соответствующих сообщений\n\n\n2011-06-11 19:02\n[%] Админка: Исправлена уязвимость подмены User Agent\n\n\n2011-06-11 14:11 V28.1a13\n[~] Флоты+Сообщения: Приглашение в САБ теперь  относится  к  категории  \"Боевые\n    отчеты\", а не к категории \"Сообщения от игроков\"\n\n[%] Флоты: Исправлена ошибка, позволяющая атаковать в САБе более слабого игрока\n\n\n2011-06-11 13:16 V28.1a12\n[%] Новости: Исправлена ошибка форматирования новостей при отсылке игроку\n\n[@] Флоты: Добавлена возможность отключения запрета  прокачки  транспортировкой\n    ресурсов от более слабого игрока более сильному. Опция \"Запрет прокачки\" на\n    странице настроек сервера\n\n\n2011-06-07 00:02 V28.1a11\n[+] Сообщения: Добавлена новый класс сообщений \"Сообщения Администрации\". К ним\n    относятся:\n    1. Сообщения системы квестов\n    2. Новости сервера\n    3. Сообщения Администрации\n[+] Сообщения:  Восстановлена  функциональноксть  класса  сообщений  \"Сообщения\n    очереди построек\". К ним относятся:\n    1. Уведомления о  завершении  исследований.  Уведомление  высылается  после\n       входа на страницу исследований\n    2. Уведомления об окончании работы верфи на планете. Уведомление высылается\n       по окончании очереди строительства Верфи\n    3. Уведомление об окончалии строительных работ на  планете  (постройка  или\n       разрушение здания). Уведомление высылается пакетно в  полуавтоматическом\n       режиме.  Это  означает,  что  сообщение  генерируется  каждый  раз   при\n       обращении к планете (сканирование шпионажом  или  игроком,  переключение\n       активной планеты игроком итд). При  этом  в  сообщение  указываются  все\n       изменения, произошедшие на момент обращения\n\n\n2011-06-03 22:11 V28.1a10\n[%] Сообщения:  Исправлена  ошибка  выбора  неправильной  записи  игрока   при\n    просмотре настроек автоматических уведомлений\n\n\n2011-06-03 13:56\n[%] Флоты:  Исправлена  ошибка  обработки  миссии  \"Транспорт\"  если   планета\n    назначения не существует\n\n\n2011-06-03 02:09\n[@] SQL: Исправлен SQL-файл\n\n\n2011-06-02 03:32 V28.1a9\n[!] Readme: Добавил раздел  \"Благодарности\".  Желающие  вычеркнуть  себя  могут\n    написать мне об этом в личку\n\n[+] Боевой отчет: Локализован\n\n[~] Боевой отчет: Добавлено количество  убитых  корблей  в  предыдущем  раунде.\n\n[%] Сообщения:  Исправлена   ошибка   с   невозможностью   получить   сообщения\n    определенных классов\n\n\n2011-05-31 00:47 V28.1a8\n[!]  Сообщения:  Настройка  автоматических  уведомлений.  Теперь  в  настройках\n    пользователя можно отключить получения определенных категории сообщений.  В\n    этот список входят: Шпионские отчёты, Военные отчёты,  Отчеты  переработки,\n    Прибытие флота, Отчёты экспедиций, Сообщения очереди построек.\n[!] Настройки: Полностью переписана система настроек пользователя\n\n[~] Локализация: Немного оптимизирована локализация\n\n[%] JS: Исправлена ошибка в js_timer, приводящая к некорректной работе  таймера\n    после таймера с типом \"date&time with delta\"\n\n[@] Настройки: Изменена подсистема дополнительных настроек пользователя\n[@] Системное: sys_get_param_int_val теперь так же  обрабатывает  непрописанные\n    чекбоксы - те, которые возвращают \"on\"  и  \"off\".  Для  таких  значение  он\n    возвращаеет соответственно \"1\" и \"{$default}\"\n\n\n2011-05-23 02:30 V28.1a7\n[%] Вселенная: Исправлена ошибка  с  неправильным  наложением  иконки  летящего\n    флота, когда флоту отдана команда \"Обратно\"\n[%] Обзор планеты: Исправлена ошибка с добавлением лишнего события, когда флоту\n    отдана команда \"Обратно\"\n[%] Вселенная: Исправлена возможность  появления  фантомных  лун.  Существующие\n    фантомные луны удалены\n\n\n2011-05-23 01:19\n[%] Сообщения:  Исправлен  глюк,  подставляющий  в  отправителя  сообщения  имя\n    адресата\n[%] Сообщения: Исправлен глюк, не дающий увидеть  кому  пишется  сообщение  при\n    переходе на страницу создания сообщений по ссылке с ИД игрока\n\n[@] Update: Изменен апдейтер так, что бы не  выдавать  ошибки  при  апгрейде  с\n    версии <26. Так же введена конфигурация времени блокировки базы апдейтером\n[@] Update: Добавлена процедура валидизации  таблицы  игроков  по  имени  и  ИД\n    Альянса. Так же добавлены constraint на эти поля\n[@] GIT: GIT теперь так же игнорирует SQL-файлы в каталоге бэкапа\n\n\n2011-05-19 10:41\n[%] Антибашинг: Исправлена ошибка в подсистеме  техобслуживания  из-за  которой\n    удалялись нужные записи из таблицы башинга и расчет волн сбрасывался в ноль\n\n\n2011-05-18 22:38 V28.1a6\n[!] Сообщения: Полностью переписана  система  сообщений.  В  системе  произошли\n    следующие изменения:\n    1. Можно писать письма любому игроку! Форма создания нового  письма  теперь\n    корректно обрабатывает  имена  игроков,  введенных  в  строку  \"Кому\".  Эта\n    возможность доступна из списка  категорий  сообщений  по  ссылке  \"Написать\n    сообщение\" в самом низу таблицы категорий\n    2. В списке писем теперь работает чекбокс в заголовке. Клик на нем приведет\n    к выбору всех сообщений. Повторный клик - к снятию всех отметок\n    3. Добавлен дополнительный диапазон для удаления сообщений - \"Все сообщения\n    данной категории\". Внимание! В категории \"Все сообщения\" его выбор приведет\n    к полной очистке почтового ящика!\n    4. При  первом  открытии  страницы  создания  нового  сообщения  больше  не\n    выскакивают угрожающие красные надписи\n    5. Счетчик  сообщений в  навбаре  работает  без  задержек. Т.е. если  игрок\n    перешел  в  категорию  с  непрочитанными  сообщениями,  счетчик   изменится\n    соответствующим образом  сразу  же  после  перехода,  а  не  при  следующей\n    загрузке страницы\n    6. Множество мелких стилистических доработок\n    7. Оптимизированы алгоритмы работы подсистемы сообщений, а так же  почти  в\n    два раза уменьшен объем  передаваемой  информации  от  клиента  к  серверу.\n    Особенно это заметно при удалении большого количества сообщений\n\n\n2011-05-16 02:08\n[~] Империя: Переформатирована страница Империи\n\n\n2011-05-15 20:59\n[%] Квесты: Исправлена ошибка редактирования квестов с многострочным описанием\n\n\n2011-05-15 20:13 V28.1a5\n[~] Квесты: Теперь в  качестве  награды  можно  назначать  и  простые  ресурсы:\n    металл, кристаллы и дейтерий\n\n\n2011-05-15 18:00 V28.1a4\n[~] Флоты: Переоформлена страница своза ресурсов. Теперь так же считается сумма\n    выбранных для своза ресурсов по каждой планете\n\n\n2011-05-14 01:34\n[~] Флоты: Переоформлены страницы отправки флота\n[~] Чёрный Рынок: Переоформлены страницы ЧР\n\n\n2011-05-14 00:34 V28.1a3\n[+] Интерфейс: Новый тип  сортировки  планет  -  по  общему  количество  полей.\n    Учитываются терраформеры (на планетах) и лунные базы (на лунах)\n\n[~] Список планет: Изменена цветовая  кодировка  полосы  застройки:  зеленый  -\n    менее 50% застройки, желтый - не меньше 50% и меньше 80%,  оранжевый  -  не\n    меньше 80% и меньше 100%, красный - 100% застройки\n[~] Список планет: На полосы застройки добавилось  застроенное  и  максимальное\n    количество полей на планете\n[~] Список планет: Сортировка учитывается в списке планет на  страницах  \"Обзор\n    планеты\" и \"Империя\"\n\n\n2011-05-12 21:14 V28.1a2\n[+] Империя: Добавлено цветовое кодирование для производящих структур.  Уровень\n    производства, выставленный на странице \"Ресурсы\",  кодируется  цветом  фона\n    соответствующей ячейки: зеленый -  100%,  желтый  -  80-90%%,  оранжевый  -\n    70-50%%, красный - 40-10%%, цвет  фона  -  0%  или  структура  не  является\n    производящей. Пропорционально уровню производительности  меняется  и  длина\n    кодированной полоски\n\n\n2011-05-11 19:03 V28.1a1\n[%] Альянсы: Исправлена ошибка невозможности выхода из Альянса\n\n\n2011-05-10 22:47 V28d\n[!] Project \"SuperNova.WS\" Release 28\n\n\n2011-05-10 20:25\n[%] Башинг: Исправлена ошибка с неправильной записью времени башинга\n\n\n2011-05-08 15:47\n[+] Квесты: Добавлены критерии по постройке кораблей и обороны\n[~] Навбар: По клику на  счетчике  флотов  и  экспедиций  открывается  страница\n    флотов в полете\n\n\n2011-05-07 03:07 V28.1a0\n[!] Квесты: Добавлена подсистема квестов\n     1. Администратор сервера может самостоятельно добавлять  новые  квесты\n     2. В настоящий момент доступны квесты на постройку зданий  и  обороны,  на\n     исследование и на постройку кораблей (триггер срабатывает при  наличии  на\n     планете Х кораблей одного типа)\n     3. Наградой является ТМ. Доступ к конструктору квестов  осуществляется  из\n     меню \"Квесты\" страницы администрирования. Создание квестов доступно только\n     Администратору сервера (auth_level = 3)\n     4.  Игрок  может  просматривать  список  доступных  квестов  и  их  статус\n     (выполнен или не выполнен)\n     5. По выполнению квеста игроку высылается письмо с уведомлением\n     6. Общее количество  и  количество  выполненных  квестов  видно  игроку  в\n     навбаре\n     7. Администратор может посмотреть выполненные квесты игрока  по  ссылке  в\n     его профиле (Поиск через админпанель)\n     8.   Игроки,   превысившие   условия   квеста,    автоматически    получат\n     вознаграждение при следующей проверке на  критерии  выполнения.  Например,\n     если целью квеста является постройка шахты 10го уровня, то  при  постройке\n     шахты  выше  9го  уровня  на  любой  планете   игрок   получит   квестовое\n     вознаграждение. То же самое верно и  по  отношению  к  уничтожению  шахты.\n     Однако, если при уничтожении шахты её уровень окажется ниже 10го, то игрок\n     вознаграждения не получит, хотя он уже и имел шахту 10го уровня\n\n\n2011-05-04 19:00 V28c6\n[%] Альянсы: Исправлена невозможность подать заявку на вступление в Альянс\n\n\n2011-05-04 04:30 V28c5\n[%] Башинг: Исправлена ошибка расчета времени летящих флотов\n\n\n2011-05-04 04:30 V28c4\n[+] Альянсы: Переработана страница информации об Альянсе. Теперь  внутренняя  и\n    внешняя страница генерируются одним файлом и используют один темплейт.  Как\n    следствие - появился список отношений Альянса на странице информации для не\n    членов Альянса\n\n\n2011-05-03 12:23 V28c3\n[%] Обслуживание: Исправлена ошибка системы обслуживания  при  очистке  таблицы\n    башинга\n\n\n2011-05-03 12:23 V28c2\n[%] Альянсы: Убрана отладка заявок\n\n\n2011-05-03 11:57\n[%] Альянсы: Исправлена ошибка создания Альянса\n\n\n2011-05-03 00:40\n[%] Альянсы: Исправлена ошибка удаления Альянса (добавлено ON DELETE CASCADE)\n\n\n2011-05-03 00:27 V28c1\n[%] Админка: Исправлены сообщения \"Page not found\" в формах\n\n\n2011-05-02 23:25 V28c0\n[%] Альянсы/Дипломатия: Исправлена ошибка с рассылкой писем членам Альянсов при\n    принятии предложения изменения отношений\n\n\n2011-05-02 22:16 V28c Project \"SuperNova.WS\" Release 28 Release Candidate\n[!] Антибашинг: Добавлена система защиты от башинга. Защита не  дает  отправить\n    больше флотов и волн, чем указано в правилах.\n    Настройки по умолчанию - в течении 24 часов 3 волны по 3 атаки не более  30\n    минут между атаками в одной волне.  Настроить  систему  защиты  от  башинга\n    можно через таблицу `config` - группа параметров fleet_bashing_*. Установка\n    параметра fleet_bashing_attack  в  0  означает  полное  отключение  системы\n    защиты.\n      1. Атакой считаются миссии: \"Атака\",  \"Совместная  атака\"  и  \"Уничтожить\n      луну\". Миссия \"Ракетная атака\" атакой не считается\n      2. Учитываются флоты в полете. Т.е. если игрок уже запустил две  волны  и\n      еще одна находится в полете - он больше не сможет запускать флоты.\n      3. Атаки засчитываются по факту - т.е.  если  полностью  отменить  волну,\n      находящуюся в полете, игрок сразу же  сможет  послать  на  планету  новые\n      флоты, не дожидаясь возвращения волны\n      4. Атаки учитываются  вне  зависимости  от  результата  (выигрыш,  ничья,\n      проигрыш)\n      5.  При  САБе  атака  засчитывается  ВСЕМ  нападающим  -  дабы   избежать\n      \"карусели\", т.е. когда несколько игроков по  очереди  запускают  САБы,  а\n      остальные к ним присоединяются\n      6. Все флоты одного игрока в одном САБе считаются как один флот\n      7. Если Альянсы находятся в  отношении  \"Война\",  защита  от  башинга  не\n      работает\n      8. Объявление войны не требует согласия. Это означает, что когда Альянс А\n      предложил  Альянсу  Б  отношение  \"Война\",  это  предложение  принимается\n      автоматически и отношения устанавливаются сразу для обоих Альянсов\n      9. Выход из состояния войны требует согласия обоих сторон\n      10. Выход из состояния войны обратной  силы  не  имеет!  Т.е.  если  было\n      объявлено перемирие  когда  планеты  одного  из  Альянсов  находятся  под\n      атакой, то флоты все равно долетят и совершат нападение  -  какое  бы  ни\n      было новое отношение между Альянсами  (если,  конечно,  атакующий  их  не\n      отзовет)\n      11.  А  вы  знаете  почему   такой   относительно   простой   вещи   (40+\n      человекочасов) нет на Оффе? А что бы денежки снимать за разбан!\n[+] Альянсы: К Дипломатии добавлено отношение \"Мир\".  Рекомендуется  выставлять\n    этот статус после заключения пакта о ненападении. С точки зрения движка оно\n    ничем не отличается от \"Нейтралитета\" и нужно для информирования остального\n    игрового  сообщества  о  неких  устных  договоренностях  -  буде  в   таком\n    информировании возникнет нужда. Альянсы вольны следовать или  не  следовать\n    данной рекомендации, а так же решать - хотят они  оповестить  Вселенную  об\n    изменении своих отношений или нет\n[+] Альянсы: Нельзя сделать предложение текущих отношений  (т.е.  если  Альянсы\n    находится в отношениях \"Война\" нельзя опять предложить отношение \"Война\")\n\n[~]  Произведено  разделение  между  \"Релизом\"   и   \"Версией\"   в   интерфейсе\n    пользователя. \"Релиз\" - это крупное обновление движка, выкладываемое в виде\n    одного файла в общий доступ. Версия - небольшое  обновление,  недостаточное\n    для  смены  номера  релиза.  Подробнее  об  этом  можно  прочесть  в  файле\n    /docs/html/developer.html\n\n[%] Флоты: Исправлено неудаление САБа, если все  флоты  САБа  получили  команду\n    \"Обратно\"\n\n[@] Разработка: добавлен каталог '.local' для облегчения  разработки.  Файлы  в\n    этом каталоге игнорируются GIT-ом, но при этом корректно подключают внешние\n    файлы для обработки и выполнения\n[@] Разработка:  добавлена  процедура  sn_db_perform().  Отныне  для   вставки\n    одиночных записей следует использовать только её. См. файл db.php\n[@] Система:  Добавлена  процедура  ежедневного  обслуживания:  чистка  таблицы\n    башинга, чистка таблицы САБ\n\n\n2011-04-30 22:54 V27a20\n[!] Альянсы:  Добавленв  подсистема  дипломатии  (главная  внутренняя  страница\n    Альянса, таблица \"Дипломатия\"). Глава Альянса может начинать  переговоры  и\n    принимать  предложения  от  других  Альянсов.  Отношения  между   Альянсами\n    включают:\n      1. Нейтралитет. Отношение по умолчанию\n      2. Война.\n    Что  бы  изменилось  отношения  между  Альянсами,  другая  сторона   должна\n    потвердить предложение об изменении по ссылке \"Переговоры\", доступной главе\n    Альянса. Там же можно сделать предложение об  изменении  отношений  другому\n    Альянсу\n\n\n2011-04-28 23:48 V27a19\n[~] Чат: Код смайла WINK изменен с \";)\" на \":wink:\"\n\n\n2011-04-28 23:33\n[~] Чат: Добавлена трансляция в смайлы сочетания \":)\" - :smile\n\n\n2011-04-28 23:27\n[%] Чат: Исправлена ошибка парсинга смайлов \":(\" и \";)\"\n\n\n2011-04-28 23:14\n[%] Чат: Исправлена ошибка с двойным экранированием символов\n\n\n2011-04-28 23:04\n[+] Император: Добавлена дата регистрации игрока: \"Император [Имярек] с [дата]\"\n\n\n2011-04-28 21:47 V27a18\n[%] SN_TIMER:  Исправлена  ошибка  в  JS,  при  которой  рассинхронизировались\n    счетчики флотов/экспедиций в навбаре с реальным состоянием дел\n\n[@] Админка: Исправлено ложное  срабатывание  системы  определения  взлома  при\n    обновлении пользователем страницы  \"Флоты  в  полете\"  сразу  после  отдачи\n    команды \"Обратно\" последнему из текущих флотов.  Так  же  в  предупреждение\n    теперь логгится состав флот, который пытался вернуть пользователь\n\n\n2011-04-28 10:26 V27a17\n[~] Список  забаненных:  Полностью  переписан.  Список  теперь  сортируется  по\n    возрастанию даты бана - последние забаненные появляются  в  начале  списка.\n    Добавлено отображение разбанов\n[@] БД: Изменена структура таблицы банов banned\n\n\n2011-04-24 00:06\n[%] Статистика: Исправлено неправильно отображение даты  последнего  обновления\n    статистики при просмотре статистики Альянсов\n\n\n2011-04-24 00:00\n[%] Альянсы: Исправлены ошибки редактирования информации Альянсов\n\n\n2011-04-23 23:28\n[%] Чат: Исправлено непоказывание Альянса в чате\n\n\n2011-04-23 21:23 V27a16\n[!] Чат: Полностью  переписан  внутренний  чат.\n      1.  Полностью  переписана  JS-часть.  В  частности  -  AJAX-вызовы   теперь\n      осуществляются  через jQuery\n      2. Добавлена  заплатка  для  корректной  работы  чата  в  глюкофоксе\n      3. Добавлена защита на стороне клиента от слишком частых обновлений\n      4. Полностью переработана PHP-часть чата\n      5. Корректно показывает заголовок в истории  чата  -  \"общий  чат\"  и  \"чат\n      альянса\" соответственно\n      6. История чата теперь грузиться в виде нормальной страницы СН, а не в виде\n      \"обмылка\", как раньше\n      7. Содержимое языкового файла chat.mo отфильтровано и влито в system.mo\n      8. Множество других добавлений и усовершенствований\n\n\n2011-04-22 01:16 V27a15r1\n[+] Свезти ресурсы: Добавлено отображение количества необходимых  ресурсов  при\n    переходе по кнопке \"Свезти ресурсы\" со страницы постройки\n\n\n2011-04-22 00:57 V27a15\n[+] Свезти ресурсы: Добавлен JS-счетчик  общего  количества  свозимых  ресурсов\n    с учетом чекбоксов\n\n\n2011-04-21 23:45\n[~] Флоты: Страница 0 -  перемещена  кнопка  \"Дальше\"  на  одну  строку  вверх.\n    Добавена кнопка \"Свезти ресурсы\"\n\n\n2011-04-16 20:58 V27a14\n[%] Навбар: Переписан  алгоритм  генерации  списка  событий  флотов/экспедиций.\n    Теперь корректно обрабатывается миссия \"Передислокация\"\n\n\n2011-04-14 22:47 V27a13\n[%] Фаерфокс: Исправлен ВНЕЗАПНЫЙ отказ глюкобага отправлять сообщения  в  чат.\n    Тормозилла - так поддерживать! Ибо то, что висит можно только поддерживать.\n\n\n2011-04-14 18:58 V27a12\n[%] Интерфейс: Исправлен текста в попапах\n\n\n2011-04-10 17:43\n[~] HTML: Исправлен хидер, что бы быть W3C-compliant\n[%] НоваПедия:  Исправлена  очепятка  из-за  которой  в  списке  кораблей   на\n    химических двигателях не показывался переработчик\n\n\n2011-04-10 16:51\n[%] Империя: Исправлена ошибка отображения маскимального  количества  полей  на\n    луне\n\n\n2011-04-10 16:18 V27a11\n[+] Список планет: Справа от иконки планеты добавлены три колонки, показывающие\n    процент производительности шахт и  синтезаторов:  серый  -  шахта  металла,\n    голубой - синтезатор кристаллов, фиолетовый -  синтезатор дейтерия.  Высота\n    колонки пропорциональна проценту производства, а  фон  кодирует  диапазоны:\n    желтый - 80-90%, оранжевый - 50-70%%, красный - меньее 50%. На высоту и фон\n    колонки влияет ИСКЛЮЧИТЕЛЬНО процент производства, выставленный на странице\n    \"Ресурсы\"\n[+] Империя: Цифра производства ресурсов теперь  кодируется  цветом  аналогично\n    фону колонки прозиводства ресурсов (см. выше)\n\n[%] НоваПедия: Исправлено описание гипердвигателя\n\n\n2011-04-08 23:49\n[~] НоваПедия: \"Ракетный двигатель\" переименован в \"Химический\", а \"Импульсный\"\n    - в  \"Ионный\".  Для  них  полностью  изменено  описание.  Так  же  изменено\n    соответствующе описание кораблей.\n\n\n2011-04-08 23:07 V27a10\n[%] Экономика: Исправлен глюк с невычитанием дейтерия при отрицательном балансе\n[%] JS: Исправлен глюк в скрипте таймера из-за которого  не  отсчитывало  назад\n    ресурсы при переполненных складах\n\n\n2011-04-05 02:54 V27a9\n[+] Навбар: Количество флотов и экспедиций в полете  теперь  интерактивно:  оно\n    автоматически  изменяется  в  соответствие  с  происходящими  событиями   -\n    прибытие, возвращение  и  окончание  миссии  флота  (как  они  должны  были\n    произойти  на  момент  загрузки  страницы).  При   наведении   курсора   на\n    соответствующую ячейку всплывает подсказка с описанием ближайшего события\n\n\n2011-04-05 00:42\n[~] Новости: Чекбокс \"Разослать новость игрокам\" включен по умолчанию\n\n\n2011-04-05 00:36 V27a8\n[+] Навбар:  Добавлено  количество  флотов  и  экспедиций  в  полете  и  всего.\n    Переработана ячейка сообщений\n\n\n2011-04-03 05:42 V27a7\n[~] Рефакторинг кода\n\n\n2011-04-03 01:22 V27a6\n[~] Индексы технологий заменены константами\n[%] Ракеты: Исправлена ошибка в процедуре ракетной атаки - технологии  щитов  и\n    брони были перепутаны местами\n\n\n2011-03-30 09:30 V27a5\n[%]  Флоты:  Теперь  при  возврате  последнего  флота  в  САБе  САБ   корректно\n    уничтожается\n\n\n2011-03-28 01:12 V27a4\n[~] Админка: Проставлены права доступа к отдельным страницам в  зависимости  от\n    уровня.\n    1. Модератор (authlevel=1) имеет доступ к  следующим  страницам:  overview,\n    activeplanet, banned, changelog, planetlist, statbuilder, tools, md5enc.\n    Он может: видеть список игроков онлайн и  их  активность,  видет  список  и\n    активность планет,  вручную  обновлять  статистику,  банить  и  разбанивать\n    пользователей\n    2. Оператор (authlevel=2) дополнительно имеет доступ к следующим страницам:\n    add_building,  add_def,  add_money,   add_moon,   add_research,   add_ship,\n    del_building,  del_def,  del_money,   del_research,   del_ship,   moonlist,\n    showfliyingfleets.\n    Дополнительно к функциям  модератора  он  может:  добавлять  и  убирать  на\n    планетах здания, корабли, защиту, ресурсы; добавлять и  убирать  технологии\n    игрока; видеть все луны и добавлять луны к планетам; видеть и редактировать\n    флоты в полете\n    3.  Администратор   (authlevel=3)   имеет   доступ   ко   всем   страницам,\n    включая     delete_user,     admin_darkmatter,     errors,     maintenance,\n    maintenance_ajax,   messagelist,    messall,    admin_chat,    paneladmina,\n    planetcompensate,  settings, userlist.\n    Дополнительно к функциям оператора он  может:  добавлять  и  убирать  ТМ  у\n    игроков; видеть полный  список  игроков  с  IP-адресами;  удалять  игроков;\n    запускать  процедуру  обслуживания  БД;  просматривать  и  удалять   личные\n    сообщения; просматривать и удалять сообщения чата; просматривать и  удалять\n    сообщения системы логов; изменять права пользователей;  изменять  настройки\n    игры; возмещать игроку стоимость затрат на планету\n[~] Админка: Введена дополнительная защита от взлома. Теперь член команды  игры\n    не может назначить кому-либо уровень доступа, равный  или  больший  своего.\n    Таким образом через админку невозможно  назначить  второго  Администратора.\n    Однако это можно проделать напрямую в БД\n\n\n2011-03-26 21:35 V27a3\n[~] Чат: Ники модераторов (auth_level=1) и  операторов  (auth_level=2)  в  чате\n    теперь тоже выделяются.  По  умолчании  соответственно  зеленым  и  красным\n    цветом\n\n\n2011-03-25 23:00 V27a2\n[+] Экономика: Изменена схема работы МИС. Теперь МИС работает следующим образом:\n      1. По каждой планете вычисляется эффективный уровень исследования (ЭУИ) =\n      уровень лаборатории / (0,5 ^ уровень нанитки)\n      2. Планеты сортируются по эффективному уровню\n      3. Отсекаются планеты с уровенм лаборатории, недостаточным для проведения\n      данного исследования\n      4. Выбирается верхние (уровень МИС + 1) планет в списке и суммируется ЭУИ этих планет\n      5. Получившееся число подставляется в формулу вычисления времени исследования\n      Следствия:\n      1. Нанолаборатория теперь увеличивают эффективность лаборатории только на\n      той планете, на которой они расположены\n      2. Время исследования теперь одинаково на  всех  планетах.  На  некоторых\n      планетах чуть больше, на некоторых - чуть меньше, но в среднем  -  лучше,\n      чем было раньше\n      3. Имеет смысл держать только (уровень МИС + 1) планет  с  лабораториями.\n      Остальные просто не будут подключаться.\n      3.1. Примечание к следствию 3 - собственно, так было и раньше - все равно\n      исследование могло проводиться только на одной планете\n\n\n2011-03-23 00:15 work\n[~] Вселенная: Стартовая планета теперь имеет температуру 0/40\n\n\n2011-03-19 00:44 work\n[~] Экономика: Дописан пересчет времени построек в очереди\n\n\n2011-03-18 00:16 V27a1\n[~] Экономика: Изменена формула расчета производительности ЭС. Она  нормирована\n    по максимальной температуре. Норма - 40 градусов.\n\n\n2011-03-17 23:16\n[+] Новости: Добавлена возможность массовой рассылки новости всем игрокам\n\n\n2011-03-16 23:07\n[+] Обзор Империи: Добавлена температура планеты. В колонке ИТОГО - минимальная\n    и максимальная среди всех температур\n\n\n2011-03-16 09:49\n[%] Рапорты: На странице просмотра  рапортов  максимальное  количество  в  поле\n    ввода кода выставлено в 32\n\n\n2011-03-16 08:29\n[~] Экономика: Энергетическая технология теперь не дает дополнительный бонус  к\n    производительности электроэнергии\n\n\n2011-03-15 23:35\n[~] Экономика: Изменена выработка энергии. Производство энергии на термоядерной\n    электростанции теперь считается по формуле оффа:\n       30 * [Э] * (1,05 + [Е] * 0,01) ^ [Э]\n    где Э - уровень электростанции, Т - уровень энергетической технологии\n[~] Экономика: Энергия считается более аккуратно\n[~] Вселенная: Правильно считается минимальная и максимальная температура луны\n\n\n2011-03-15 20:59\n[~] Экономика: Изменена выработка энергии.  Во-первых  -  модификатор  скорости\n    игры теперь не действует на энергию. Во-вторых - температура планеты влияет\n    на выработку солнечных электростанций\n\n\n2011-03-15 16:01\n[+] Интерфейс:  В  списках  летящих  флотов  к  количеству  кораблей  во  флоте\n    добавляется в конце знак \"+\" если флот везет ресурсы\n\n\n2011-03-15 15:08\n[-] Флоты: Удалена ссылка на редактирование закладок со  страницы  1  -  теперь\n    закладки можно редактировать через левое меню\n\n\n2011-03-15 00:10 V27\n[!] Project \"SuperNova.WS\" Release 27\n\n[+] ЧаВо: В настройках сервера  добавлена  возможность  задать  URL  для  ЧаВо.\n    Добавлен соответствующий пункт в левое меню\n\n[~] Логин: Переработаны  меню  страниц  логина,  регистрации  и  восстановления\n    забытого пароля. Теперь оно одинаково  для  всех  страниц  и  кроме  старых\n    пунктов дополнительно включает блок ссылок логин/регистрация/восстановление\n    пароля, ссылку на FAQ, ссылку на новости сервера  (к  ним  теперь  разрешен\n    доступ незалогиненных/забаненных пользователей).\n\n[~] Интерфейс: Теперь если в настройках сервера отсутсвует какой  либо  из  URL\n    (адрес форума, ссылка на правила, ссылка на FAQ), то соответствующие пункты\n    меню и ссылки скрываются или не подсвечиваются. В дампе БД по умолчанию все\n    URL идут пустыми\n[~] Интерфейс: Переработана страница ТМ. Теперь если в конфигурации отсутствует\n    URL с подробностями покупки ТМ - информация о возможности покупки просто не\n    выводится\n\n[%] Безопасность:  Исправлена  ошибка  невозможности  доступа   незалогиненных\n    пользователей к статистике, контактам итд\n[%] Реклама: Исправлена ошибка несохранения  параметров  рекламного  блока  при\n    перезапуске сервера\n\n[@] БД: Версия БД увеличена до 27. Обновлен дамп\n\n\n2011-03-14 21:42 V26e22\n[!] Боевой отчет: Добавлена страница для просмотра боевого отчета по его  коду.\n    В меню в раздел \"Информация\" добавлена соответствующая ссылка\n\n[~] Локализация: Слияние файлов viewreport.mo => system.mo\n\n[~] Навбар: Убран разделитель между  часами  реального  времени  и  количеством\n    игроков онлайн\n\n[%] Локализация: Исправлена ошибка неполной локализации в  случае,  когда  язык\n    игрока отличается от языка сервера по умолчанию\n[%] Локализация: Исправлена ошибка  локализации  надписи  \"ИТОГО\"  на  странице\n    Обзора Империи\n\n\n2011-03-14 04:04 V26e21\n[!] Закладки: Полностью переписана система закладок. Теперь закладки хранятся в\n    отдельной таблице и не захламляют данные пользователя. Полностью переделано\n    редактирование закладок\n\n[~] Флоты: Немного изменил страницу 1 отправки флотов - теперь закладки, базы и\n    боевые союзы выводятся бок-о-бок в три колонки\n\n\n2011-03-14 01:50\n[~] Новости: Строка \"Ссылка на подробности\" вынесена в файлы языков\n[~] Закладки: \"ссылки\" везде заменены на \"закладки\"\n\n\n2011-03-13 17:04\n[~] Империя: При  входе  на  страницу  Империи  информация  о  планетах  теперь\n    обновляется и в БД\n\n\n2011-03-13 16:41 V26e20\n[!] Включена возможность выбора языка для пользователей\n\n[+] Админка: Язык игры по  умолчанию  теперь  выбирается  из  списка  доступных\n    языков\n\n[~] Локализация:  Удалены  файлы:  activate.mo,  active.mo,  changepassword.mo,\n    mip.mo, functions.mo, index.mo, lang_info.cfg, news.mo, player.mo\n[~] Локализация: Переименован файл lang_main.php в language.mo\n[~] Локализация: Слияние файлов\n      banned.mo => system.mo\n      contact.mo => system.mo\n      credit.mo => system.mo\n      records.mo => system.mo\n\n\n2011-03-13 13:08 V26e19\n[~] Локализация: Обновлена английская локализация (c) madmax1991\n\n[%] Админка: Исправлены ссылки на главной странице панели администратора\n\n\n2011-03-12 15:11\n[~] Локализация: Обновлен файл локализации en/alliance.mo (c) madmax1991\n\n\n2011-03-10 21:09\n[%] Вселенная: Корректно выводится сообщение при попытке нападения на игрока  в\n    отпуске\n\n\n2011-03-09 20:36 V26e17\n[%] Админка: Исправлена баннерилка\n\n\n2011-03-09 12:14 V26e16\n[!] Добавлен английский перевод (с) madmax1991\n\n\n2011-03-08 22:08 V26e15\n[~] \"Крейсер\" переименован в \"Эсминец\", а \"Линкор\" - в \"Крейсер\"\n\n[-] Из кода убрана английская локализация - большая её часть все равно была на\n    русском, а остальное не соответствовало реальности\n\n[@] Массив $lang['tech_rc'] заменен $lang['tech'] ввиду их полной идентичности\n[@] Все идентификаторы кораблей заменены на константы\n\n\n2011-03-08 16:56 V26e14\n[~] Альянсы: Страницы \"Настройка прав  доступа\",  \"Список  участиков  Альянса\",\n    \"Поиск Альянса\", \"Создание Альянса\", \"Управление заявками\"  переделаны  под\n    PTE\n\n[%] Альянсы: Исправлена ошибка вывода заявки  на  странице  управления  заявок.\n    Теперь если в заявке есть переводы строк, то заявка корректо форматируется\n[%] Альянсы: На странице внешней информации об  Альянсе  исправлена  ссылка  на\n    страницу заполнения заявки\n\n\n2011-03-07 22:56 V26e13\n[~] Регистрация: Пароль теперь так же указывается на странице  пост-регистрации\n    - на случай, если письмо с паролем не дойдет до адресата\n\n\n2011-03-07 22:48\n[%] Добавлен хак для FF при отключении отпуска\n\n\n2011-03-07 19:59 V26e12\n[%] Исправлена ошибка при регистрации и восстановлении забытого пароля\n\n\n2011-03-07 00:56 V26e11\n[~] Админка: Процедура  обслуживания  БД  теперь  удаляет  только  сообщения  с\n    неизвестным адресатом и сообщения, старше 30 дней\n\n\n2011-03-06 23:27\n[%] Флот: Исправлена ошибка дублирования списка САБов\n\n[@] В процедуру апдейта добавлена очистка старого списка САБов\n\n\n2011-03-06 23:09 V26e10\n[%] Флот: исправлена ошибка неудаления пустого САБа после атаки\n\n\n2011-03-06 21:35 work\n[~] Почищены лишние файлы\n\n\n2011-03-06 20:16 work\n[%] Исправлена ошибка v26e9 с невозможностью залогиниться в игру\n\n\n2011-03-06 02:46 v26e9\n[~] Все {{table}} в запросах заменены на соответствующие названия таблиц\n\n\n2011-03-06 02:08 work\n[~] Изменена система слежения за  игроками.  Теперь  не  логгируются  неопасные\n    запросы (SELECT, START TRANSACTION,  COMMIT,  ROLLBACK).  При  логгировании\n    запроса так же записывается стандартный набор переменных\n\n\n2011-03-06 01:53 work\n[~] Переменная dpath по максимуму убрана из кода\n\n\n2011-03-06 00:09 work\n[~] Проверка на наличие лога неправильных запросов перенесена из  /overview.php\n    в /common.php\n[~] Упрощены алгоритмы подключения файлов\n[~] Проверка на права в админском разделе вынесена в /common.php\n[~] Переменная   $ugamela_root_path   заменена    на    $sn_root_virtual    и\n    $sn_root_physical\n\n\n2011-03-05 22:41 V26e8\n[!] Движок:  Теперь  СН  может  размещаться  на  веб-сервере  на  любом  уровне\n    вложенности каталогов\n[%] Флот: исправлена ошибка создания САБа в случае, когда летит максимум флотов\n\n\n2011-03-04 02:31 V26e7.3\n[%] Исправлены ошибки переноса в подкаталоги в админке: флоты в полете, ошибки,\n    обслуживание   БД,   пересчет   статистики,   обзорная   страница,   список\n    пользователей. Так же восстановлены картинки в нескольких местах Альянса, в\n    редактировании новостей, в списке друзей\n\n\n2011-03-03 22:14 V26e7.2\n[%] Флоты в полете:  Исправлена  ошибка  отображения  количества  экспедиций  в\n    полете\n\n\n2011-03-02 23:34 V26e7.1\n[%] Исправлены ошибки переноса в подкаталоги в  админке:  редактирование  чата,\n    перезагрузка конфигурации, MD5-шифрование\n\n\n2011-03-02 23:20\n[%] Исправлены ошибки переноса в подкаталоги: баннеры  в  партнерке,  настройки\n    пользователя, баннеры  Императора,  генерация  баннера,  ссылки  на  боевые\n    отчеты, Вселенная - отправка переработчиков и фаланга, смайлики в чате\n\n\n2011-02-27 20:17\n[!] Теперь можно ставить СН в подкаталоги доменов, т.е. СН  корректно  работает\n    по УРЛ типа http://domain.com/folder\n\n\n2011-02-27 03:02 V26e7\n[+] Чат: Изменилось выделение сообщеник команды сервера. Теперь выделяется  ник\n    и  в  сообщениях  можно  использовать  все  стандартные  цвета.  Переменная\n    конфигурации     chat_admin_msgFormat      заменена      на      переменную\n    chat_admin_highlight. В ней можно использовать  HTML  коды.  Место  вставки\n    ника обозначается как '$1' - см. пример в БД\n\n\n2011-02-27 02:19 V26e6\n[!] Интерфейс: Разнесены по разным страницам  отправка  флота  и  информация  о\n    флотах в полете. Теперь флот отправляется через пункт меню \"Флот на орбите\"\n    раздела \"Планета\", а информация о летящих флотах доступна через пункт  меню\n    \"Флоты в полете\" раздела \"Империя\"\n\n[~] Меню: Константы очередей берутся напрямую через D_\n\n\n2011-02-26 22:00\n[~] Экономика/Ресурсы: По многочисленным просьбам добавлена колонка \"В час\"\n\n\n2011-02-26 11:20 V26e5\n[~] Убрана запись сообщения в логи о постройке ПЗ/отмене очереди\n[%] Закрыта уязвимость к передаче неправильных ИД кораблей на странице флота\n\n\n2011-02-26 10:36 V26e4\n[%] Добавлена проверка на корректное время Экспедиций и Удержания\n[%] Теперь корректно выводится сообщени об ошибке при попытке отправить флот на\n    несуществующую планету\n\n\n2011-02-23 16:43 V26e3\n[%] Добавлена проверка на корректный процент производства на странице \"Ресурсы\"\n\n[@] Теперь можно отключить защиту слабых игроков, сбросив game_noob_factor в 0\n[@] Удалены  записи  конфигурации   'noobprotection',   'noobprotectionmulti',\n    'noobprotectiontime'\n\n\n2011-02-17 02:07 V26e2\n[%] Исправлено  \"PHP  Warning:  Invalid  argument  supplied  for  foreach()  in\n    /includes/functions/FormatCR.php on line 116\"\n[%] Исправлено  \"PHP  Warning:  Invalid  argument  supplied  for  foreach()  in\n    /includes/fleet/flt_page1.inc on line 14\"\n[%] Исправлено  \"PHP  Warning:  Invalid  argument  supplied  for  foreach()  in\n    /includes/fleet/flt_page2.inc on line 22\"\n[%] Исправлено  \"PHP  Warning:  Invalid  argument  supplied  for  foreach()  in\n    /includes/functions/flt_mission_explore.php on line 159\n\n\n2011-02-16 17:25 V26e1\n[%] Исследования: Теперь невозможно исследовать технологии во  время  постройки\n    лаборатории или нанолаборатории\n[@] Из дампа убраны NOT NULL для полей типа text\n[@] В файле db.php проверка на таблицу errors заменена на таблицу logs\n[@] В классе debug исправлено сообщение  о  невозможности  записать  событие  в\n    таблицу\n[@] flt_flying_fleet_handler: исправлены obsolete вызовы  функций  с  передачей\n    параметров по ссылке в тексте вызова, а не в объявлении функции\n[@] update.php:  разбил  несколько  запросов   на   подзапросы   для   лучшего\n    восстановления в случае сбоя во время апдейта\n\n\n2011-02-13 19:31 V26e0\n[~] SQL: По умолчанию в дампе счетчик посещений - отключен, а игра - включена\n\n\n2011-02-10 22:18 V26e\n[%] Вселенная: Исправлена ошибка  с  неправильной  ссылкой  на  экспедицию  при\n    количестве планет в системе не равном 15\n\n\n2011-02-07 13:02 V26d\n[!] Project \"SuperNova.WS\" Release 26\n\n\n2011-02-06 15:25 V26c7.3\n[@] Предельно упрощена time_probe.php - теперь возвращается только timestamp\n[@] Убраны двойные объявления констант INSTALL и INSIDE\n\n\n2011-02-05 04:59 V26c7.2\n[%] Настройки: Исправлена очепятка, не дающая уйти в отпуск\n\n\n2011-02-04 15:04 V26c7.1\n[@] Добавлена защита от выполнения файлов .INC вне основного кода\n\n\n2011-02-04 23:23 V26c7\n[~] ЧР: При попытке уйти в  отпуск  теперь  выдается  отдельное  сообщение  при\n    летящих флотах и отдельное сообщение при постройке на планетах\n[~] ЧР: унифицирован алгоритм ППК и ПК\n\n\n2011-02-04 15:30 work\n[~] Чёрный Рынок: Переработаны Продавец  Подержанных  Кораблей  и  Переработчик\n    Кораблей. Оптимизированы алгоритм и логика работы. Код готов к унификации\n\n\n2011-02-04 10:36 work\n[~] Чёрный Рынок: Торговец ресурсами - Добавлена защита от повторного обмена\n[%] Чёрный Рынок: Торговец ресурсами - Закрыт эксплойт, позволяющий  обменивать\n    ресурсы на ТМ\n[@] Чёрный Рынок: Торговец ресурсами - Оптимизирован алгоритм и логика работы\n[@] Теперь в  debug->error  и  debug->warning  можно  предавать  дополнительные\n    переменные для дампа\n[@] Удален /raketenangriff.php\n[@] sys_o_get_updated теперь не блокирует запись пользователя при симуляции\n\n\n2011-02-03 17:21 V26c6\n[~] Свезти ресурсы: Галочки  в  колонке  \"ВСЕГО\"  теперь  не  влияют  на  набор\n    вывозимых  ресурсов, а используются только для облегчения выбора\n\n\n2011-02-03 16:29\n[~] Свезти ресурсы: Добавлены колонки \"ВСЕГО\" - общее  количество  ресурсов  на\n    планете и \"Трюмы\" - общая грузоподъемность транспортного флота  с  цветовым\n    кодированием\n[~] Свезти ресурсы: Теперь можно отдельно выбирать типы ресурсы, которые  нужно\n    свезти\n[@] Добавил в темплейты подстановку констант PHP (префикс D_)\n\n\n2011-02-03 10:39 V26c5\n[%] Исправлена ошибка с невозможностью  отправить  экспедицию  при  достаточном\n    количестве дейтерия для полета\n\n\n2011-02-03 10:39 V26c4\n[%] Исправлена ошибка в менеджере летящих флотов, приводившая  к  неправильному\n    расчету количества вывозимых ресурсов с планеты\n\n\n2011-02-03 00:13\n[~] Сообщения: В навбаре теперь отображаются дополнительно количество сообщений\n    от других игроков  и  количество  сообщений  от  членов  альянса.  Цветовое\n    кодирование зависит от скина и такое же, как на странице сообщений\n\n\n2011-02-02 22:19 V26c3.2\n[%] Чёрный Рынок: Исправлена ошибка в работе Торговца Ресурсами\n\n\n2011-02-02 22:08\n[~] ЧР: Обмен ресурсов завернут в транзакцию\n\n\n2011-02-02 21:02\n[%] Чёрный   Рынок:   Теперь   невозможно   продать/купить   не-корабль    на\n    соответствующей странице Чёрного Рынка\n\n\n2011-02-02 01:25 V26c3\n[~] Оптимизирован кэшер летящих флотов. Теперь он лочит планеты и пользователей\n    только при использовании данных в первый раз, а не при сканировании флотов\n\n\n2011-02-02 00:38 V26c2\n[%] Боевка: Корабли теперь не будут увозить отрицательные ресурсы с планеты\n[%] Экономика: Производительность теперь не может быть отрицательной\n[%] Таймер: JS-таймер теперь не будет считать ресурсы меньше 0\n\n\n2011-02-01 23:56\n[@] Исправлена ошибка с пустой таблицой планет на странице администратора\n\n\n2011-02-01 23:41 V26c1\n[%] Исправлена ошибка, когда при отрицательной  добыче  ресурсы  могли  уйти  в\n    минус\n\n\n2011-02-01 23:16\n[@] Счетчик посещений теперь можно отключить из настроек сервера\n\n\n2011-02-01 23:05 V26c0\n[~] Вселенная: Теперь отображаются  все  планеты  в  зависимости  от  настройки\n    game_max_planet, а не 16 штук\n[~] Вселенная: Теперь все планеты и луны  в  системе  вынимаются  из  БД  одним\n    запросом\n[~] Вселенная: Теперь все флоты, летящие в данную систему в  настоящий  момент,\n    вынимаются из БД одним запросом\n[~] Империя: Убран лишний вызов flt_get_fleet_to_planet - она уже вызывается из\n    tpl_parse_planet\n[~] Обзор  планеты:  Убран  лишний  вызов  flt_get_fleet_to_planet  -  она  уже\n    вызывается из    tpl_parse_planet\n\n\n2011-02-01 21:26\n[!] Project \"SuperNova.WS\" Release Candidate 26\n\n\n2011-02-01 21:16\n[~] Восстановление пароля: Теперь  в  восстановлении  пароля  участвует  адрес,\n    указанный при регистрации\n\n\n2011-02-01 19:13\n[%] Убрал назад добавление в шпионаж - шпионы и так отлично работали\n\n\n2011-02-01 10:31\n[@] flt_can_attack: Добавлена проверка на корректность цели миссии \"Уничтожить\"\n\n\n2011-02-01 02:33\n[@] Переписана процедура обслуживания полностью на пакетные команды\n[%] Альянсы: Исправлена очепятка в названии таблицы\n\n\n2011-01-31 02:04\n[%] Интерфейс/Список планет: Иконка вражеской атаки на луне составляет 70%  для\n    лучшей видимости\n\n\n2011-01-31 00:44\n[~] mysql_fetch_array() заменена на mysql_fetch_assoc()  везде,  где  это  было\n    возможно\n\n\n2011-01-31 00:34\n[~]  Все  результаты  аггрегатных  функций  MySQL  (sum,   count,   min,   max)\n    поименованы. В doquery() mysql_fetch_array заменена на mysql_fetch_assoc. В\n    общем случае это сократит объем для хранения данных PHP в полтора-два раза.\n    Соответственно, меньше будет использоваться памяти xCache\n\n\n2011-01-30 23:56 V26b6\n[+] Скрипт обновления статистики завернут в транзакции.  Это  дало  50  кратное\n    увеличение скорости исполнения\n\n[@] В таблицу `planets` добавлен индекс i_parent_planet\n[@] В  таблице  `messages`  удалены  старые  индексы  и  вместо  них  добавлены\n    `i_time`, `i_owner_time`, `i_sender_time`\n[@] Из overview.php убраны два лишних запроса\n\n\n2011-01-30 18:12\n[!] changelog.txt разделен на пользовательский и девелоперский. Все изменения в\n    процессе  разработки  новой  версии  вносятся  в   changelog_dev.txt.   При\n    подготовке релиза в changelog.txt переносятся только  финальные  изменения\n[@] Убрал все остатки старого менеджера флотов\n\n\n2011-01-30 17:17\n[~] В навбаре время и количество ресурсов  прописано  в  темплейте.  Теперь  на\n    медленных соединениях до конца загрузки страницы в навбаре будут  не  тупые\n    заглушки, а значения, акутуальные на момент запроса\n\n\n2011-01-30 16:57 work\n[@] Добалвена таблица аттрибутов миссий. Флот-кэшер теперь кэширует  только  те\n    планеты, которые используются в миссии\n\n\n2011-01-30 15:34 work v26b5\n[@] Добавлена  блокировка  от  одновременного  запуска  нескольких  менеджеров\n    летящих флотов\n\n\n2011-01-30 14:30 work v26b4\n[~] Добавлена  обработка  ситуации,  когда  после  установки  движок   сначала\n    запустили на пустой базе, а только затем залили в неё дамп\n\n\n2011-01-29 10:25 work v26b3\n[@] Флоты: нормально обрабатывается ситуация, если не  существует  планеты  или\n    пользователя   для   места   назначения    флота    (пользователь    удален\n    администратором или системой чистки БД)\n\n\n2011-01-29 10:25\n[%] Закрыта дыра, позволяющая поставить  в  очередь  больше  зданий,  чем  есть\n    свободного места на планете\n\n\n2011-01-28 21:43 work\n[@] Вселенная: Добавлена обработка исключительной ситуации, когда у планеты нет\n    хозяина: в цикл просмотра системы, в выгрузку  кэша  в  темплейт,  в  самом\n    темплейте\n\n\n2011-01-28 21:43 work v26b2\n[%] Исправлена ошибка  невозможности  отправить  переработчики  через  страницу\n    \"Флот\"\n[%] Флоты: Исправлена ошибка в кэшере,  приводившая  к  многократной  обработки\n    одного и того же флота\n\n\n2011-01-28 00:08 work\n[%] Офицеры: Шпион теперь работает :)\n\n\n2011-01-27 16:32 work v26b1\n[@] Теперь кэшер рекеширует только нужные данные и только когда они  нужны.  Не\n    кэширует планеты и пользователей для пустых/вернувшихся флотов, не кэширует\n    пользователей для несуществующих планет. Проверка на наличие данных в  кэше\n    проверяется перед обработкой каждого события\n[@] Исправлена ошибки очистки кэша после обработки функции -  удалялись  лишние\n    записи\n\n\n2011-01-27 02:32 work\n[%] Не кэшировались заново планеты и юзера, если  у  флота  был  эвент  в  этом\n    таймслоте\n\n\n2011-01-27 01:49 work v26b\n[@] Изменены коды  операции  со  статистикой.  Раньше  код  102  пересекался  с\n    операцией \"изменение Тёмной Материи\"\n\n\n2011-01-27 01:37 work\n[@] Версия БД увеличена до 26\n[!] Таблица  `errors`  влита  в   `logs`.   Новая   таблице   переформирована:\n    добавлены новые  и  переупорядочены  старые  поля  для  удобства  просмотра\n    человеком; добавлено поле с  дампом  переменных  для  дальнейшего  разбора.\n    Старые таблицы сохранены соответственно как `errors_backup` и `logs_backup`\n[+] В отладчик добавлен режим работы  с  отключенной  БД.  В  этом  режиме  все\n    сообщения и ошибки выдаются на страницу\n[~] Теперь админ тоже не может  ходить  по  клиентской  части  при  отключенном\n    сервере во избежание порчи БД при бэкапе\n\n\n2011-01-26 19:03 work v26a8\n[+] Полностью переписана работа алгоритма миссии  \"Шпионаж\".  От  старого  кода\n    осталась только генерация рапорта\n[@] Все небоевые миссии теперь используют на входе кэшированные данные\n\n\n2011-01-26 01:45\n[~] В навбаре теперь при загрузке страницы показываются ресурсы с разделителями\n    тысяч\n[~] Император: Все числа теперь показываются с разделителем тысяч\n\n\n2011-01-26 02:47 work v26a7\n[%] Устранена ошибка зацикливания перенаправлений при удалении колонии\n[%] Устранена ошибка неначисления опыта/уровня  при  атаках,  если  в  процессе\n    генерации рапорта произошел сбой\n[%] Изменена структура таблицы rw  что  бы  не  возникало  ошибки  при  вставке\n    рапорта с существующим rid\n[@] Обработка возврата флотов работает в теле flying_fleet_handler\n[@] MT_COLONIZE использует кэшированные значения\n[@] MT_HOLD возвращает флаги кэширования и не нуждается в кэше вообще\n[@] Переделаны  обработчики  миссий  для  возвращения  флагов   рекеширования:\n    MT_ATTACK, MT_AKS, MT_DESTROY\n\n\n2011-01-25 15:52 work v26a6\n[@] Переделаны  обработчики  миссий  для  возвращения  флагов   рекеширования:\n    MT_SPY\n[@] Исправлена ошибка обработки CACHE_FLEET\n\n\n2011-01-25 15:52 work v26a5\n[@] Переделаны  обработчики  миссий  для  возвращения  флагов   рекеширования:\n    MT_COLONIZE и MT_RECYCLE\n\n\n2011-01-25 16:00\n[%] Галактика: Исправлена ошибка неотображения названий планет с символов \"'\"\n\n\n2011-01-25 15:52 work\n[@] Переделаны  обработчики  миссий  для  возвращения  флагов   рекеширования:\n    MT_TRANSPOSRT\n\n\n2011-01-25 09:56 work v26a4\n[@] Переделаны  обработчики  миссий  для  возвращения  флагов   рекеширования:\n    MT_EXPLORE и MT_RELOCATE\n\n\n2011-01-25 02:04 work v26a3\n[@] Написана система кэширования данных для обработчика  флотов.  Пока  она  не\n    интегрированна  в  обработчик,  а   просто   написана   и   протестирована.\n\n\n2011-01-24 09:32\n[%] Своз ресурсов: перед свозом ресурсов не пересчитывались данные  о  ресурсах\n    на планетах, поэтому свозилось количество ресурсов меньшее, чем могло\n\n\n2011-01-24 09:32 work v26a2\n[~] Изменил версию\n\n\n2011-01-24 13:34 work commit\n[@] Модифицировал PlanetResourceUpdate, что бы  она  возвращала  массив,  а  не\n    использовала передачу по ссылке. Переименовал её в sys_o_get_updated\n\n\n2011-01-24 09:32 work commit\n[%] Сообщения:  Теперь  при  появлении   нового   сообщения   у   пользоватея,\n    отправленного ему игрой (возвращение флота, шпионаж, отчеты  переработчиков\n    итд), счетчик сообщений реагирует сразу, а не после обновления страницы\n\n\n2011-01-23 22:42 work commit\n[@] Файл GetMaxConstructibleShips.php удален, как неиспользуемый\n[@] Функция CheckPlanetUsedFields перенесена в /includes/functions.php\n[@] Функция GetMaxConstructibleElements перенесена в /includes/functions.php\n[@] Функция IsElementBuyable перенесена в /includes/functions.php\n[@] Функции  sys_user_options_pack  и  sys_user_options_unpack  перенесены   в\n    /includes/functions.php\n[@] Функция RestoreFleetToPlanet перенесена в\n    /includes/functions/FlyingFleetHandler.php\n[@]  Функция  ali_internal_admin_rights  перенесена  из  отдельного   файла   в\n    /includes/alliances/ali_internal.inc\n[@] Функция SetSelectedPlanet теперь  в  случае  несуществующей  current_planet\n    перебрасывает на $user['id_planet']. Сама функция перемещена из  отдельного\n    файла в /includes/functions.php\n[@] Новая функция sys_get_updated вместо PlanetResourceUpdate. Отличия:  вместо\n    массива $planet_row принимает либо ID колонии, либо её координаты  с  типом\n    (луна или планета). Вместо  очереди  возвращает  массив  из  актуальных  на\n    текущий момент записей пользователя, планеты и очереди на планете. При этом\n    новая функция производит извлечение записей из  БД,  если  параметр  $simul\n    истинен. В перспективе все вызовы PlanetResourceUpdate  будут  заменены  на\n    вызовы sys_get_updated с целью оптимизаций обращений к БД\n[@] PlanetResourceUpdate заменена на sys_get_updated в файлах:\n    /includes/functions/coe_missile_attack.php\n    /common.php\n    /imperium.php\n\n\n2011-01-23 16:33 work commit\n[@] Изменен   алгоритм   обработки   миссий    \"Шпионаж\",    \"Транспорт\"    и\n    \"Передислокация\".  Теперь  при  отсутствии  планеты-цели   флот   корректно\n    обрабатывается\n\n\n2011-01-22 16:49 work commit\n[@] Переработан алгоритм строительства  в  подготовке  к  подключению  верфи  и\n    исследований\n[@] В $lang['info'] добавлено поле effect для описания функцонала юнитов\n\n\n2011-01-22 01:25\n[~] Теперь по окончании очереди построек страница автоматически обновляется\n\n\n2011-01-21 19:04\n[%] Флоты: Исправлена ошибка \"Не хватает топлива\" при отправлении флота  не  на\n    100% скорости\n\n\n2011-01-21 18:49\n[%] Флоты: Исправлена ошибка \"Не  хватает  топлива\"  при  отправлении  флота  с\n    миссией \"Колонизация\"\n\n\n2011-01-21 00:03\n[@] Немного изменен процесс автологина  в  попытке  избавится  от  некорректных\n    SQL-запросов\n\n\n2011-01-20 22:34\n[~] Список планет: Добавлена иконка тележки и на луну\n\n\n2011-01-20 22:27\n[~] Настройки  пользователя:  Ко  всем  чекбоксам  добавлены  label   for   на\n    соответствующие  надписи.  Теперь  можно  кликать  на   надпись,   что   бы\n    переключить чекбокс\n\n\n2011-01-20 21:16\n[~] Список планет: Полоса застроенности планеты перенесена под картинку.\n[~] Обзор планеты/Список планет: При выборе луны  её  иконка  увеличивается  на\n    50%, а изображение планеты,  которой  принадлежит  луна,  так  же  остается\n    увеличенным\n[~] Список планет: Добавлена иконка тележки. Щелчок на неё открывает  интерфейс\n    своза ресурсов на данную планету\n\n\n2011-01-20 15:05\n[!] Унифицированы алгоритмы и  файлы  постройки  флота  и  защиты.  Это  должно\n    полностью снять  проблемы  с  отрицательными  ресурсами  после  верфи  и  с\n    постройкой лишних единиц флота/защиты на верфях\n[~] Теперь при отмене очереди на верфи открывается та же страница верфи\n\n\n2011-01-20 12:24 Project \"SuperNova.WS\" Release 25\n[+] Project \"SuperNova.WS\" Release 25\n\n\n2011-01-20 12:24 V25c2\n[%] Здания: Теперь ссылки \"Построить Уровень\" и \"Уничтожить Уровень\" в описании\n    здания отображаются только если здание действительно  можно  построить  или\n    уничтожить\n\n\n2011-01-20 00:21 V25c1\n[+] Свезти  ресурсы:  Добавлена  дополнительная  колонка  \"Время\",  в  которой\n    указывается  время  полета  флота  для  перевозки  максимально   возможного\n    количества ресурсов\n\n\n2011-01-19 20:46 V25c\n[%] Верфь/Защита: Исправлена ошибка, которая могла приводить  к  отрицательному\n    количеству ресурсов  на  планете  и  к  дублированию  защитных  сооружений,\n    имеющих ограничение по количество (планетарных щитов и ракет)\n\n\n2011-01-19 01:26 V25beta2\n[+] Свезти ресурсы: Теперь в форме выбора планет видно количество  ресурсов  на\n    каждой планете.\n[~] Свезти ресурсы: Координаты и имя колонии разделены на две колонки\n[@] Обновлен дамп БД до версии 25\n\n\n2011-01-19 01:26 V25beta1\n[+] Мировые константы: Добавлена информация о версиях компонентов сервера\n[+] Текущая версия движка теперь выводится в футере вместе с копирайтом\n\n\n2011-01-19 00:57 V25beta\n[~] Версия БД увеличена до 25\n\n\n2011-01-19 00:52\n[+] Файл readme.txt приведен в соответствие с реальным состоянием движка\n\n\n2011-01-18 23:34\n[!] Переписана  с  нуля  система  отпусков.  Теперь  работает  серверная  опция\n    \"Отключить режим отпуска\".\n[+] Настройки: Переформатирован блок \"Управление  профилем\":  добавлен  таймер,\n    указывающий минимальный срок отпуска;  добавлено  пояснение  по  отключению\n    профиля\n[-] Настройки: Убрано поле  IPCheck,  поскольку  этот  параметр  все  равно  не\n    работал\n[@] Все поля в таблице `users`, относящиеся к режиму отпуска, заменены на  поле\n    `vacancy`. Все функци -  на  sys_user_vacancy.  Проерка  на  режим  отпуска\n    производится сразу в файле common.php. Отключить режим отпуска на  странице\n    можно  установив  глобальную  переменную  $skip_ban_check  в   true   перед\n    подключением   common.php.   Переменная   конфигурации   urlaubs_modus_erz,\n    отвечающая за отключение режима отпуска на сервере переименована в\n    user_vacation_disable\n\n\n2011-01-18 18:12\n[~] Кнопка \"Свезти ресурсы\" перенесена под расписание движения флотов\n[~] Исправлена ошибка неотображения планеты в списке на  вывоз  ресурсов,  если\n    колония только одна\n\n\n2011-01-18 16:01\n[%] Исправлена  ошибка  при  колонизации  планеты  игроком,  в  имени  которого\n    содержится апостроф. Это так же устраняет зависание флотов, если в  очереди\n    событий есть задание \"Колонизация\" от такого игрока\n\n\n2011-01-18 15:26\n[@] Добавлены  дополнительные  проверки  в  систему  безопасности.  Это  должно\n    полностью убрать два вида с неправильными  запросами  тип  \"SELECT  *  FROM\n    sn_fleets  WHERE  fleet_owner=\"   и  \"SELECT   *   FROM   sn_fleets   WHERE\n    (fleet_start_galaxy = AND fleet_start_system = AND ...[итд])\"\n\n\n2011-01-17 22:22 Свезти ресурсы\n[!] Флоты: Добавлена новая возможность - свезти ресурсы с остальных  планет  на\n    текущую. Возможность доступна из левого меню \"Флоты\" через  кнопку  \"Свезти\n    ресурсы\"\n[@] Доработана функция flt_can_attack. Добавлены обработка следующих  ситуаций:\n    ATTACK_WRONG_SPEED,  ATTACK_NO_RESOURCES,  ATTACK_NO_FUEL,   ATTACK_NO_ACS,\n    ATTACK_ACS_MISSTARGET, ATTACK_ACS_TOO_LATE\n[@] Написана функция flt_send_fleet\n\n\n2011-01-17 17:55\n[%] Вселенная: Исправлена ошибка отображения названий с символами \"\\\" и \"\"\"\"\n\n\n2011-01-15 22:22 Ребалансировка офицеров\n[!]  Перебалансированы  офицеры.  Характеристики  чуть-чуть  изменены.  Кое-где\n    добавлены дополнительные уровни. Основное новвоведение - офицеры разбиты на\n    две независимые подветки - \"шахтерскую\" и \"рейдерскую\". \"Шахтерская\"  ветка\n    начинается с Геолога и  включает  Энергетика,  Карго-мастера,  Архитектора,\n    Шпиона, Координатора. \"Рейдерская\" ветка начинается с Адмирала  и  включает\n    Конструктора,  Академик,  Фортификатор,  Защитника,  Навигатора.   Открытие\n    второго офицера в ветке требует 5 уровней  первого  офицера,  а  каждого  -\n    следующего - по 5 уровней двух предыдущих (каждый раз пары разные).\n    Топ-офицеры Разрушитель и Ассасин кроме полного  открытия  своей  подветки\n    (\"рейдерской\" и \"атакерской\"  соответственно)  требуют  открытия  соседней\n    ветки примерно до половины\n\n\n2011-01-15 20:08 Project \"SuperNova.WS\" Release 24.2\n[+] Project \"SuperNova.WS\" Release 24.2\n\n\n2011-01-15 17:53\n[~] Экономика: Теперь нельзя ставить в очередь занятые  здания  (лаборатории  -\n    при текущем исследовании, верфь - при постройке кораблей или  защиты).  Так\n    же нельзя запустить постройку кораблей/защиты при наличии в очереди верфи и\n    нельзя запустить исследование при наличии в очереди любого типа лаборатории\n[%]  Экономика/Здания:  Исправлена  ошибка,  позволяющая  строить  здания,  для\n    которых не выполнены требования постройки\n[%] Экономика/Здания: Исправлена ошибка,  позволяющая  строить  лаборатории  во\n    время исследования при отключенном BuildLabWhileRun\n[%] Экономика: Исправлена ошибка, позволяющая апгрейдить  верфь  при  постройке\n    кораблей\n[%] Экономика: Исправлена ошибка, позволяющая строить корабли во время  стройки\n    на верфи\n[@] Функция IsTechnologieAccessible заменена на eco_can_build_unit\n[@] Функции CheckFleetSettingsInQueue  и  CheckDefSettingsInQueue  заменены  на\n    функцию eco_hangar_is_building\n[@] Код очищен от использования полей b_building и b_building_id\n\n\n2011-01-15 15:50\n[@] В  .giattributes  добавлены  директивы  для  исключения  .gitattributes  и\n    .gitignore из архива\n\n\n2011-01-15 15:18\n[@] Каталог /docs/coding-guidelines переименован в /docs/html\n[@] /docs/readme_dev.txt переформатирован в /docs/html/developer.html\n\n\n2011-01-15 15:07\n[~] Из global.css в отдельный файл вынесены опции для IE. Это позволило сделать\n    CSS-код W3C-валидизируемым\n\n\n2011-01-14 15:51\n[~] Здания: Добавлена заглушка против особенно  тупых  пользователей  -  теперь\n    после обработки команды (постройка/удаление/очистка очереди  итд)  страница\n    редиректится на страницу построек с пустым запросом. Это должно убрать  все\n    возможные глюки с нажатием кнопки Refresh (F5)\n\n\n2011-01-14 15:23\n[+] Симулятор: Добавлена поддержка Адмирала\n\n\n2011-01-14 15:02\n[~] Обзор Империи: Уровни сносимых зданий выделяются в списке  уровней  красным\n    цветом\n\n\n2011-01-14 14:49\n[%] Исследования: Исправлена  ошибка,  позволявшая  исследовать  технологии  во\n    время постройки лаборатории при отключенной опции \"Строить  лабораторию  во\n    время исследования\"\n\n\n2011-01-14 14:23\n[~] Постройка/Здания: Модифицирован код темплейта для совместимости с Хромом\n\n\n2011-01-14 13:55\n[~] Вселенная: Добавлен статус для игрока \"Начинаюшщий игрок\" в зоне абсолютной\n    нубзащиты (по умолчанию меньше 5000 очков)\n[~] Вселенная: Статус активности игрока теперь выделяется стилем текста: жирный\n    - активный игрок, обычный - игрок неактивный больше  7  дней,  наклонный  -\n    игрок неактивный больше 28 дней\n\n\n2011-01-14 09:58\n[%]  Интерфейс/Инфобар: Баланс энергии опять отображается красным цветом\n\n\n2011-01-14 09:47\n[%] Обзор планету/Управления: Исправлена ссылка \"Вернуться к обзору\"\n\n\n2011-01-14 09:18\n[~] Очередь построек:  Перенесена  номер  строящегося  уровеня  с  середины  на\n    верхнюю границу иконки\n\n\n2011-01-14 09:08\n[%] Постройки: Исправлена конструкция JS что бы её воспринимал долбанный IE\n\n\n2011-01-14 08:54\n[%] Корабли: Исправлена ошибка RR - у СуперНовы в  требованиях  для  постройки\n    был только один офицер. Добавлены требования по технологиям\n\n\n2011-01-14 07:51\n[%] Поиск: Исправлена ошибка  RR,  не  дающая  возможность  искать  игрока  вне\n    Альянсов\n\n\n2011-01-13 23:12\n[~] Строительства/Здания: Добавлена возможность  удалять  последнее  здание  из\n    очереди\n\n\n2011-01-13 19:31\n[~] Мировые константы: Добавлено слово \"Скорость\"  к  соответствующим  пунктам.\n    Добавлен пробел между \"Базовая добыча\" и названием ресурса\n\n\n2011-01-13 19:09\n[%] Строительства/Здания:   Исправлена   ошибка   в   начислении   опыта   за\n    строительство. Опыта давалось в 100 раз больше, чем должно было\n\n\n2011-01-13 18:51\n[@]  Чат:  Добавлена  возможность  настраивать  таймаут  чата  через  интерфейс\n     Настроек сервера  (раздел  \"Настройки  чата\"/\"Таймаут  по  неактивности\").\n     Значение \"0\" означает, что таймаут отключен (не рекомендуется, потому  что\n     при  неправильных  настройках  сервера  большое  одновременное  количество\n     запросов может привести к DoS)\n\n\n2011-01-13 18:05\n[%] САБ: Исправлена ошибка, когда  можно  было  отправить  флот  более  быстрый\n    корабль с заданием \"Атака\". Теперь при выборе  САБа  доступно  единственное\n    задание: \"Совместная атака\"\n\n\n2011-01-13 15:42\n[%] Строительство/здания: Общее время постройки теперь  в  конце  строительства\n    обнуляется\n\n\n2011-01-13 12:55\n[%] Исправлена ошибка неначисления опыта при постройке зданий\n\n\n2011-01-13 09:36\n[~] Чёрный  рынок/Тоговец  ресурсами:  Добавлена  обработка  попытки  обменять\n    нулевое количество ресурсов. Раньше просто списывались ТМ, теперь  выдается\n    сообщение об ошибке\n[%] Чёрный Рынок/Тоговец ресурсами: Исправлена уязвимость, позволявшая  указать\n    отрицательное количество ТМ и получить прибавление ТМ у  пользователя.  Код\n    атаки 305 в таблице logs\n\n\n2011-01-13 08:28\n[~] Информация/Верфь:   Изменена   индикация   эффекта   скорострела.   Теперь\n    высчитывается и показывается реальный скорострел (через поле 'amplify')\n[%] Информация/Защита: Исправлена ошибка RR с  неотоображением  скорострела  от\n    юнитов\n[%] Информация: Исправлена опечатка RR \"Планетарный защита\"\n\n\n2011-01-11 23:06\n[~] Империя: Изменено форматирование по желанию пользователей\n\n\n2011-01-11 22:35\n[%] Империя: Исправлена ошибка форматирования количества прилетающих кораблей\n\n\n2011-01-11 22:24\n[+] Файл readme.txt приведен в соответствие с реальным состоянием движка\n\n\n2011-01-11 22:17 Project \"SuperNova.WS\" Release 24.1\n[@] Обновлен дамп supernova.sql до версии 24\n\n\n2011-01-11 21:09\n[%] CSS: Исправлена очепятка в CSS\n\n\n2011-01-11 11:04\n[-] Строительство: Полностью убран из игры старый строительный интерфейс\n\n\n2011-01-10 15:46\n[~] Офицеры:  Фортификатор  дает  теперь  уменьшает  время  постройки  защитных\n    сооружений на 10% за уровень\n[~] Офицеры:  Описания  офицеров,  ускоряющих  постройки,  изменены  на  более\n    корректные - вместо \"-X%  к  скорости  постройки\"  стало  \"-X%  ко  времени\n    постройки\"\n\n\n2011-01-10 15:41\n[%] Офицеры: Исправлена ошибка c Фортификатором: вместо того, что  бы  ускорять\n    постройку защиты он её замедлял\n\n\n2011-01-10 15:19\n[%] Офицеры: Исправлена ошибка с нерабочими Шпионом и Координатором\n\n\n2011-01-10 04:00\n[@] В целях защиты сервера возможность  для  членов  команды  (authlevel  >  0)\n    сбрасывать пароль через страницу \"Забытого пароля\" заблокирована\n\n\n2011-01-10 03:58\n[~] Империя: Переработано форматирование названия строящегося  здания,  что  бы\n    таблицу сильно не распирало\n\n\n2011-01-10 02:39 Project \"SuperNova.WS\" Release 24\n\n\n2011-01-10 02:18\n[+] Добавлено отображение общего времени на постройку всех юнитов в очереди\n\n\n2011-01-10 01:08\n[@] update.php: Процедуры апдейта до  версии  20  переписаны  с  использованием\n    добавленных позже процедур и функций\n\n\n2011-01-10 00:56\n[@] Изменен алгоритм вызова функции апгрейда БД\n\n\n2011-01-09 22:30\n[~] Офицеры: добавлена информация о бонусах офицера на страницу найма\n[~] Офицеры: добавлена информация о бонусах офицера на страницу информации\n[~] Изменен копирайт\n\n\n2011-01-09 06:23\n[%] Исправлена ошибка при колонизации с луны с приданным флотом или ресурсами -\n    флот/ресурсы не появлялись на новой колонии\n\n\n2011-01-09 05:08\n[%] Исправлена проблема с кодировками в письмах подсистемы \"Забыл пароль\"\n\n\n2011-01-09 04:53\n[%] Исправлена ошибка в предыдущем коммите, не дающая отсылать новый пароль\n\n\n2011-01-09 04:43 \"Я забыл пароль!\"\n[!] Полностью переписана  система  восстановления  забытого  пароля:  добавлена\n    процедура подтверждения емейла перед сбросом  пароля;  добавлена  процедура\n    генерации случайного пароля.\n\n\n2011-01-09 01:09\n[@] Следующие изменения в функциях:\n    ElementBuildListQueue - убрана\n    GetRestPrice => /includes/functions/ResearchBuildingPage.php\n    IsOfficierAccessible.php => /officer.php\n    IsVacationMode - убрана\n    SetNextQueueElementOnTop - убрана\n    StoreGoodsToPlanet => /includes/functions/MissionCaseTransport.php\n\n\n2011-01-09 01:09\n[@] Убран неиспользуемый файл /fleetback.php. Его функционал  уже  перенесен  в\n    файл /fleet.php\n\n\n2011-01-09 01:03\n[@] Функция CreateOnePlanetRecord переименована в uni_create_planet\n[@] Функции   uni_create_planet   и   uni_create_moon   перенесены   в   файл\n    /includes/functions/uni_functions.php\n\n\n2011-01-09 00:46\n[~] Название луны теперь генерируется в виде \"Луна планеты <Навзание планеты>\"\n[@] CreateOneMoonRecord: функция переименована  в  uni_create_moon.  Из  вызова\n    убран неиспользуемый параметр $MoonID, изменено следование  двух  последних\n    параметров\n\n\n2011-01-09 00:12\n[@] Функция CheckIfIsBuilding \"вшита\" в файл /options.php\n[@] Убраны следующие функции и соответствующие им файлы: InsertBuildListScript,\n    InsertCounterLaunchScript, CheckPlanetBuildingQueue,\n    UpdatePlanetBatimentQueueList\n\n\n2011-01-08 23:49\n[@] Убрана часть функций, отвечающих за старую очередь построек\n\n\n2011-01-08 23:41\n[@] Функция BuildFleetEventTable перенесена в файл phalanx.php\n[@] Функция BuildFlyingFleetTable \"вшита\" в файл admin/ShowFlyingFleets.php\n\n\n2011-01-08 23:21\n[@] Убрана  функция  INT_myPrettyNumber.  Её  функционал  полностью  вынесен  в\n    функцию pretty_number. Последняя так же переработана\n\n\n2011-01-08 22:53\n[@] Существенно уменьшено количество файлов в подкаталоге  /includes/functions:\n    функции, относящиеся к одному смысловому блоку, объединены в один файл\n\n\n2011-01-08 20:35\n[@] БД версии 23\n\n\n2011-01-08 20:05\n[%] Офицеры: Исправлена ошибка неотображения портретов офицеров\n\n\n2011-01-08 19:56\n[%] Исправлена ошибка неотображения иконок летящих  своих/враждебных  флотов  в\n    списке планет на страницах Обзор Империи и Обзор планеты\n\n\n2011-01-08 19:50\n[@] Исправлена ошибка запуска процедуры обслуживания\n\n\n2011-01-05 08:50\n[%] Баннеры: Исправлена ошибка, требовавашая авторизации при запросе баннера\n\n\n2011-01-03 22:32\n[@] Добавлено дополнительное поле с DEFAULT-value в SQL-файлы\n\n\n2011-01-03 17:53\n[%] Обзор планеты: Исправлена ошибка, не дающая возможность покинуть колонию\n\n\n2011-01-03 17:34 ПкВН-6.УС-3.Здания.RC3\n[~] Обзор планеты: индикатор строительства зданий и таймер строительства в списке\n    планет справа теперь использует новую систему очередей\n[~] Обзор Империи: индикатор строительства  зданий  и  таймер  строительства  в\n    списке планет и лун теперь использует новую систему очередей\n[~] Обзор Империи: Зеленые цифры  изменения  уровня  зданий  теперь  используют\n    новую систему очередей. Так же  корректно  показывается  постройка/удаление\n    зданий\n[@] update.php: Добавлен конвертер старой очереди в новый формат\n\n\n2011-01-02 01:06\n[%] Вселенная: Исправлена ошибка с исчезновением луны из  Вселенной,  внесенная\n    последним коммитом\n\n\n2011-01-01 15:19\n[%] Обзор планеты: Исправлена несовместимость с PHP 5.2.2\n\n\n2011-01-01 15:04\n[%] Вселенная: Исправлено отображение названий с апострофами (')\n\n\n2010-12-30 14:27 ПкВН-6.УС-3.Здания.RC2\n[!] Полностью рабочая очередь построек зданий\n[-] \"Обзор планеты\": Временно отключены счетчики и индикаторы построек в списке\n    планет\n[-] \"Обзор Империи\": Временно отключены счетчики и индикаторы построек в списке\n    планет\n\n\n2010-12-28 15:57 ПкВН-6.УС-3.Здания.RC1\n[+] Постройки: Добавлена возможность очистки очередей\n[+] Постройки: Локализованы все новые строчки\n[%] Интерфейс/Здания:  Исправлена  ошибка  отображения  иконок  \"Построить\"  и\n    \"Разрушить\" на превьюшках юнитов\n\n\n2010-12-28 04:11\n[@] Обновлен Sypex Dumper до версии 2.0.8 Release\n\n\n2010-12-28 03:58 ПкВН-6, УС-3 (нет, это не новый фильм Михалкова)\n[~] Переверстан блок информации о здании на странице зданий\n[@] Темплейт отображения новой очереди вынесен в отдельный файл\n[~] Теперь корректно вычисляется количество свободных  секторов  на  планете  с\n    учетом событий в очереди зданий (постройка или уничтожение)\n\n\n2010-12-23 10:35 ПкВН-5, bugfix\n[%] Добавлена проверка на время строительства в процедуру обработки очереди\n[%] Время разрушения теперь не может быть меньше 1с, а время строительства - 2с\n[%] Количество строящихся юнитов теперь не может быть меньше 1\n\n\n2010-12-14 01:33 Подготовка к введению наемников-5, унификация строительств-2\n[@] Полностью переписана очередь строительств. Все  очереди  хранятся  в  одной\n    строке и парсятся универсальным обработчиком\n[@] Полностью переделано отображение очередей\n\n\n2010-12-05 22:41 Подготовка к введению наемников-4, унификация строительств\n[@] Оптимизирован код в includes/functions/rpg_points.php\n[@]  Процедуры,   относящиеся   к   очереди   строений   перенесены   в   файл\n    BatimentBuildPage\n[@] Переписана проццедура CheckPlanetUsedFields\n[@] Начата унификация  кода  для  строительства/покупки.  Оптимизация  страницы\n    покупки зданий. Новый менеджер очередей\n\n\n2010-12-03 14:32\n[~] Переработана страница управления планетой\n[@] Переименованы процедуры, относящиеся к начислению ТМ\n\n\n2010-12-03 12:51\n[~] Верхняя панель: Убраны названия ресурсов  -  они  всплывают  при  наведении\n    мышкой на иконки. Это уменьшило высоту верхней панели на две строки\n[%] Интерфейс/Строительство: Исправлено пропадание кнопки  \"Удалить  здание\"  с\n    превьюшек\n[@] Часть ссылок на ресурсы заменена на константы\n[@] Подготовка к замене eval() на  процедуры  -  вычисления  производительности\n    шахт вынесены из строковых переменных\n\n\n2010-12-02 15:28 Подготовка к введению наемников-3\n[!] Добавлена подсистема губернаторов. Пока функционирует не полностью\n[~] Обзор планеты: Добавилось отображение текущего губернатора\n[~] Обзор планеты/Управление: Переверстана. Добавился список губернаторов\n[@] Описание  юнитов  теперь  хранится  в  файле  languages/*/infos.mo,  а  не\n    разбросано по файлам fleet.mo и tech.mo\n[@] mrc_modify_value теперь принимает в качестве параметра и планету. Это нужно\n    для поддержки губернаторов. Соответствующим  образом  изменены  все  вызовы\n    функции\n\n\n2010-12-02 03:37\n[%] Интерфейс/Строительство: Исправлено пропадание кнопки  \"Удалить  здание\"  с\n    превьюшек при полной застройке планеты\n\n\n2010-12-01 15:57 Подготовка к введению наемников (временных офицеров)\n[~] Офицеры переименованы в соответствии с будущими функциями\n[@] Все текущие офицеры отвязаны от цифровых идентификаторов и  названий  полей\n[@] Идентификаторы офицеров задаются в includes/constants.php\n[@] Поля в БД определяются по константам и задаются в файле includes/vars.php\n[@] Параметры офицеров (стоимость, величина бонуса, тип бонуса) теперь задаются\n    в includes/vars.php, а не жестко закодированы в коде\n[@]  Значения,  на  которые  влияют  офицеры  (скорость   производства,   объем\n    хранилища,  количество  флотов  итд)  теперь  обсчитываются   в   отдельной\n    процедуре mrc_modify_value.\n\n\n2010-11-06 16:07\n[%] Вселенная/Ракеты: Исправлена опечатка, не  позволявшая  атаковать  ракетами\n    отдельно выбранное здание\n\n\n2010-11-05 08:21\n[@] Добавлена  система  логов  попытки  изменить  количество  ТМ  за  пределами\n    специально  обученной  функции.  Это  должно  устранить  любую  возможность\n    манипуляции ТМ\n\n\n2010-11-05 08:13\n[%] Исправлена уязвимость к SQL-injection в модуле редактирования закладок\n\n\n2010-11-02 00:58\n[~] Офицеры: Переработана страница офицеров\n[~] Офицеры: Изменены названия и описания некоторых офицеров\n[@] Изображения офицеров теперь хранятся в скинах\n[@] Страница офицеров теперь использует PTE\n[@] Теперь  название  и  описание  офицеров  на  странице  найма  берется   из\n    информационной таблицы. Это означает,  что  описание  и  название  офицеров\n    теперь будет одинаковым на странице информации и странице найма. Больше  не\n    нужно изменять описание офицеров в двух местах\n\n\n2010-10-31 12:46\n[@] Переписан алгоритм  работы  автоматического  обновления  статистики. Теперь\n    невозможно запустить два экземпляра обновления одновременно\n\n\n2010-10-30 22:56\n[@] Теперь в причине отключения сервера нельзя использовать HTML  -  тэги  HTML\n    вырезаются при сохранении\n\n\n2010-10-30 21:57\n[%] Строительство/Оборона:  Исправлена   ошибка,   когда   при   одновременной\n    постановке МПР и ПРО в очередь  можно  было  построить  больше  ракет,  чем\n    емкость ракетной шахты\n\n\n2010-10-30 21:45\n[%] Альянсы: Исправлена ошибка, когда при существующей форме запроса  приема  в\n    Альянс кандидату показывалась стандартная заглушка\n\n\n2010-10-30 21:36\n[~] Торговец ресурсами: Теперь  при  смене  типа  ресурсов,  на  которые  будет\n    произведен обмен, все ячейки ресурсов обнуляются\n\n\n2010-10-30 21:31\n[~] Верхняя панель: Количество ресурсов и склад теперь окрашиваются  в  красный\n    цвет когда количество ресурсов больше или равно емкости склада (а не просто\n    больше, как раньше)\n\n\n2010-10-30 21:16\n[%] Обзор планет: Исправлена ошибка  индикации  отосланных  переработчиков  при\n    использовании ссылки \"Переработать\"\n[~] Изменен CSS для страницы входа и регистрации при использовании PDA\n\n\n2010-10-30 12:13\n[@] Переработана админская страница компенсации планет\n\n\n2010-10-28 01:36\n[~] Немного изменен внешний вид пользовательских настроек\n\n\n2010-10-27 18:46\n[%] Регистрация: Исправлена ошибка регистрации с несозданием главной планеты\n\n\n2010-10-26 15:25\n[%] Вселенная: Исправлен баг с неправильным отображением активности на планете\n\n\n2010-10-26 12:45\n[+] Полностью переделана страница с описанием Тёмной Материи\n[@] Добавлена новая переменная url_dark_matter, указывающая на УРЛ со способами\n    внеигрового получения ТМ. Изменяется на странице настройки сервера,  секция\n    \"Ссылки и баннеры\". По умолчанию указывает на файл /dark_matter_get.php\n[@] Переменные forum_url и rules_url переименованны соответственно в  url_forum\n    и url_rules\n\n\n2010-10-26 11:22\n[~] Флоты: Кнопка \"Обратно\" теперь не перекидывает на дополнительную  страницу,\n    а возвращает на страницу отправки флота\n[~] Переработана JS-библиотека таймеров. Теперь обратный отчет  (например  -  в\n    очереди построек) идет правильно даже  при  возврате  на  страницу  кнопкой\n    \"Back\" браузера\n\n\n2010-10-26 10:19\n[~] Переработана JS-билиотека таймеров. Теперь простые счетчики правильно  идут\n    даже при возврате на страницу кнопкой \"Back\" браузера\n[~] Увеличена точность простых счетчиков. Это должно уменьшить рассинхронизацию\n    между сервером и клиентом. Например - ресурсы будут идти точнее\n[~] Разница времени между сервером и клиентом теперь определяются AJAX-запросом\n[%] Вселенная:  Исправлена  ошибка   отсылки   переработчиков:   переработчики\n    отправлялись на планету, а не на луну\n\n\n2010-10-26 00:42\n[~] Сообщения: Перекрашены сообщения.  Цвета  заголовков  теперь  соответствуют\n    цветам сообщений и, в свою очередь, синхронизированны с раскраской флотов -\n    там, где это имеет смысл\n\n\n2010-10-25 23:24\n[~] Строительство/Здания:  В  превьюшке  масштабируется  размер  шрифта,  если\n    каких-то ресурсов с учетом прилетающих флотов остается больше 100 млрд\n[%] Строительство/Здания: Теперь если здание разрушается в графе уровня пишется\n    \"-1\", а не \"+1\", как раньше\n[~] Строительство/Здания:  В  старом  интерфейсе  на  время  строительства  или\n    уничтожения здания убирается иконка \"Уничтожить здание\"\n\n\n2010-10-25 22:01\n[%] Исправлена ошибка,  дававшая  в  определенных  условиях  атаковать  слабого\n    игрока\n\n\n2010-10-25 15:11\n[~] Нельзя становиться на удержание к слабому игроку\n\n\n2010-10-25 01:59\n[%] Инфо/Защита: На странице информации  о  защитных  сооружения  броня  теперь\n    показывается правильно, а не завышенная в 10 раз\n\n\n2010-10-25 00:42\n[~] Теперь в статистике учитываются ресурсы, вложенные в строящиеся  корабли  и\n    оборону\n\n\n2010-10-24 22:49\n[%] Инфо/Юниты: На странице  информации  о  юнитах  броня  теперь  показывается\n    правильно, а не завышенная в 10 раз\n\n\n2010-10-24 22:07\n[~] Теперь игроки в нубзоне  могут  атаковать  друг  друга.  При  этом  так  же\n    применяется ограничение по разнице очков\n[~] Вселенная: Учитывая предыдущий пункт статус игрока может иметь одновременно\n    маркер слабого игрока (игрок находится в нубзоне) и маркер сильного  игрока\n    (игрок сильнее текущего). Такое отображение не является ошибкой\n[%] Вселенная: Исправлена ошибка неотображения статуса сильного игрока\n[%] Исправлена  ошибка,  не  дававшая  атаковать  ракетами   планету   игрока,\n    находившуюся на грани дальности действия ракет\n[@] Несколько  коротких  функций  перенесены  из   индивидуальных   файлов   в\n    functions.php\n\n\n2010-10-24 19:57\n[~] Ракетная шахта  теперь  требует  для  сторительства  технологию  импульсных\n    двигателей 1го уровня\n[~] Вселенная: Ракеты теперь отправляются через JS без обновления страницы\n[~] Вселенная: Переработана легенда\n[@] Полностью переписан код проверки возможности отправки флота с  определенной\n    миссией\n\n\n2010-10-23 14:49\n[%] Вселенная: Опять исправлена  ошибка,  приводившая  к  дублированию  статуса\n    планеты с активной на неактивную\n\n\n2010-10-23 04:51\n[%] Экономика/Строительство: Исправлена ошибка отображения очень больших  чисел\n    (десятки миллиардов) на превьюшке\n\n\n2010-10-23 03:44\n[@] Админка: Добавлена страница уничтожения планеты с  компенсацией.  Стоимость\n    всех построек, защиты, флота и ресурсы с уничтожаемой  планеты  зачисляются\n    на планету, указываемую администратором. Планеты должны принадлежать одному\n    игроку\n[@] Админка: Немного доработана страница параметров сервера\n\n\n2010-10-22 14:27\n[+] Программно введен запрет на прокачку  -  пересылку  ресурсов  игрокам  выше\n    уровнем\n[~] Изменен алгоритм рассчета нубзащиты. Теперь за неё отвечают два параметра.\n    Первый - game_noob_points (по умолчанию 5000) - указывает, какое количество\n    очков должен набрать игрок, что бы выйти из зоны абсолютной  нуб-защиты.  В\n    этой зоне игрока не может атаковать никто. Второй  -  game_noob_factor  (по\n    умолчанию 5) - указывает во сколько раз должны отличатся очки  игрока,  что\n    бы для противника он был нубом. Т.е. игрок с 10000 очков  будет  нубом  для\n    игроков от 50000 очков.\n[~] Из кода убрано понятие \"сильный игрок\" - нуб может атаковать  кого  угодно.\n    Обозначение во Вселенной сильных игроков осталось, но ограничение на  атаку\n    снято\n[%] Исправлена ошибка, позволяющая атаковать ракетами нубов\n[%] Исправлена ошибка, позволяющая атаковать ракетами отпусников\n\n\n2010-10-22 12:48\n[%] Статистика: Не считались ресурсы в летящих флотах. Исправлено\n\n\n2010-10-22 09:41\n[%] Вселенная: Исправлена недоработка, вызывающее предупреждение PHP на  пустой\n    системе\n\n\n2010-10-22 09:34\n[%] Обзор планеты: Исправлена ошибка, позволяющая оптравлять переработчиков  на\n    орбиту даже если уже нет свободных слотов для флотов\n\n\n2010-10-22 05:49\n[+] Вселенная: в  попапе  на  поле  обломков  показывается  сколько  еще  нужно\n    переработчиков, что бы  переработать  обломки  (с  учетом  уже  летящих)  и\n    сколько всего их нужно, что бы переработать поле\n[~] Вселенная: теперь через попап на обломках отсылается не  полное  количество\n    переработчиков, необходимых для  переработки  поля,  а  разница  между  уже\n    летящими  переработчиками  и  общим  количеством  (естественно,  с   учетом\n    количества переработчиков на планете)\n\n\n2010-10-22 03:24\n[~] Изменена стоимость солнечных спутников, что бы соответствовать бэкграунду\n\n\n2010-10-22 03:10 А не посчитать ли нам, многоуважаемые Кроты?\n[+] Добавлена статистика по ресурсам\n[~] Исправлена ошибка, не  позволявшая  переключать  параметры  статистики  при\n    просмотре со страницы логина или регистрации\n\n\n2010-10-21 17:24 Компенсируй ЭТО!\n[+] Добавлена админская страничка,  позволяющая  уничтожить  планету  игрока  и\n    компенсировать ему стоимость построек, обороны и флота\n\n\n2010-10-21 17:05\n[~] Изменен алгоритм генерации координат для домашней планеты новых игроков\n\n\n2010-10-21 14:19 Работай языком\n[~] Все локализации из admin/*.mo перенесены в файл admin.mo\n[~] Перетасованы локализации между файлами admin.mo, system.mo и overview.mo\n\n\n2010-10-20 22:59\n[%] Вселенная: Исправлен баг с дублированием активности чужих планет на планету\n    пользователя\n\n\n2010-10-20 22:19\n[~] Страницы статистики, списка банов, списка контаков сервера, списка настроек\n    доступны для просмотра находящимся в отпуске или забаненных\n[%] Исправлена неправильная работа страниц  статистики,  списка  банов,  списка\n    контаков сервера, списка настроек для залогиненных пользователей\n\n\n2010-10-20 21:55\n[!] Полностью переписан код страниц логина и регистрации\n[!] Полностью переписана система авторизации\n\n\n2010-10-20 14:59\n[%] Вселенная: Исправлена ошибка с показом попапа планеты вместо попапа луны\n\n\n2010-10-19 19:22\n[%] Регистрация: Исправлена ошибка c невозможностью зарегестрироваться\n[%] Регистрация&Логин: Исправлена ошибка с невозможностью  посмотреть  страницы\n    \"Статистика\", \"О сервере\", \"Администрация\" и \"Список забаненных\"\n[~] Теперь если у пользователя неправильные куки, они чистятся  и  пользователь\n    может зайти в игру еще раз\n\n\n2010-10-19 18:34\n[%] Обзор Империи: Исправлена ошибка с пропажей количества прибывающих кораблей\n\n\n2010-10-19 16:18\n[%] Император: Исправлена ошибка неотображения ссылки на подробные новости\n\n\n2010-10-19 04:56\n[@] Сообщения: Из файла messages.php HTML-код, относящийся к показу  сообщений,\n    вынесен в файл messages.tpl. Остаток кода сильно переработан\n\n\n2010-10-19 00:56\n[%] Экономика/Верфь: Исправлена ошибка, позволяющая при наличии выше в  очереди\n    медленно строящегося юнита использовать его время  постройки  для  создания\n    более быстрого юнита путем добавления последнего в очередь.  Например,  эта\n    ошибка использовалась для быстрой постройки множества ракет\n[@] Полностью переписана процедура HandleElementBuildingQueue и переименована в\n    eco_bld_handle_que. Соответственно переименован и файл\n\n\n2010-10-19 00:11\n[~] Экономика: Изменены характеристики Рудника: цена  увеличена  на  треть  для\n    соответствие возросшей добычи (см.\n    2010-09-28 21:10);  потребление  энергии\n    немного снижено\n[~] Экономика: Уровень всех существующих Рудников выше 5-го уровня уменьшен  на\n    единицу для соответствия увеличенной стоимости строения\n[%] Исправлена дурацкая  ошибка  в  автоапдейтере.  Теперь  корректно  работает\n    система защиты от параллельного запуска нескольких копий апдейтера\n[@] Версия БД увеличина до 20\n\n\n2010-10-18 20:21\n[+] Вселенная: Теперь индикатор входящего  своего  флота  и  попап  с  перечнем\n    кораблей отображается и на луне\n[%] Вселенная: Исправлена ошибка отображения планетарного флота на луне\n\n\n2010-10-18 09:12\n[~] Обзор планеты: Теперь на иконке своего флота на картинке планеты появляется\n    попап с составом флота, прибывающего на планету\n[~] Империя: Теперь на иконке своего флота на картинке планеты появляется попап\n    с составом флота, прибывающего на планету\n\n\n2010-10-18 05:21\n[~] Вселенная: Теперь иконка и состав флотов показывается и на чужих планетах\n\n\n2010-10-18 04:37\n[~] Вселенная: на  иконках  своих  планет  отображается  индикатор  прилетающих\n    флотов.  В  попапе  приводятся  подробные  сведения  о   сумарном   составе\n    прибывающих кораблей и ресурсов у них на борту\n\n\n2010-10-18 01:28\n[~] Вселенная:  на  иконке  обломков  отображается  количество  переработчиков\n    пользователя, летящих на эти обломки\n\n\n2010-10-17 19:55\n[@] Убран каталог /scripts. Для поддержки старых ссылок на  баннер  используйте\n    средства веб-сервера (см. install.txt)\n\n\n2010-10-17 18:36\n[~] init.inc переименован в init.php\n[%] Исправил работу старой ссылки на баннер /scripts/createbanner.php\n\n\n2010-10-17 02:44\n[%] Кажется, исправлена ошибка с очень редким неначислением ресурсов\n\n\n2010-10-17 01:48\n[@] Каталоги /fonts, /css, /images и /templates перемещены из корня  в  каталог\n    /design\n\n\n2010-10-16 18:35\n[%] Исправлен неправильный формат времени - 12 часовый вместо 24\n[~] Заменены везде даты и время константами\n\n\n2010-10-16 15:05\n[%] Исправлен баг, позволявший отправлять флоты со шпионами в экспедиции\n\n\n2010-10-16 13:09\n[%] Исправлен баг с расположением окна чата в ИЕ\n\n\n2010-10-16 06:42\n[@] Автоапдейтер использует БД для хранения информации о последнем апдейте. Ему\n    теперь не нужен доступ на запись к файловой системе\n[%] Исправлен баг в процедуре вычисления IP-адреса пользователя\n\n\n2010-10-16 05:37\n[~] Теперь при входе  на  страницу  строительства  зданий  со  страницы  обзора\n    Империи правильно отображается количество ресурсов на планете\n\n\n2010-10-15 21:03\n[@] Левое меню: Теперь не нужно писать в коде рекламного баннера дополнительный\n    код для вставки в таблицу - баннер центрируется в темплейте\n[@] Админка: Переработана страница настроек сервера\n[@] Вызов  автоапдейтера  БД  опять  перенесен  из  common.php  в  init.inc  и\n    исполняется раньше - предыдущая схема давала сбой, если апдейтились таблицы\n    пользователей\n[@] Изменены процедуры в автоапдейтере - проверка условий выполнения  отдельных\n    операций внесены в сами функции операций\n[@] Изменена процедура определения IP-адреса пользователя. Теперь движок так же\n    пытается  определить  цепочку  прокси-серверов.  Данные  о  прокси   теперь\n    вносятся в таблицу пользователей и таблицу счетчика посещений\n[@] Функция sys_logHit переименована в sys_log_hit\n[@] Добавлена возможность отключить встроенный  счетчик  посещений.  Переменная\n    game_counter в таблице `config`\n[@] Убраны лишние подкаталоги sgo, install, oparators из /templates/OpenGame\n[@] Переменная   game_date_withTime   заменена   двумя:   int_format_date   и\n    int_format_time. В игре им соответствуют  константы  FMT_DATE  и  FMT_TIME.\n    Константа DATE_TIME переименована в FMT_DATE_TIME и является  конкатенацией\n    через пробел двух вышеприведенных констант\n\n\n2010-10-15 04:02\n[~] Симулятор: Симулятор опять при пустых  технологиях  атакующего  подставляет\n    значения технологий текущего пользователя\n\n\n2010-10-15 03:47\n[~] Вселенная: На обломках показывается также общее количество ресурсов\n\n\n2010-10-15 03:02\n[~] Флот/Страница0: Добавлено цветовое кодирование  в  список  флотов.\n[~] Флот/Страница0: Теперь можно кликать на координатах планет в списке  флотов\n    - откроется Вселенная в указанной галактике и системе\n\n\n2010-10-15 01:56\n[~] Флот/Страница0: Клик на названии корабля перебросит на подробное описание\n\n\n2010-10-15 01:12\n[@] Новости: Добавлена дополнительная строка,  в  которую  можно  вбить  URL  с\n    подробностями новости. URL добавится в конце новости в виде гиперссылки  со\n    слова \"Подробнее...\"\n\n\n2010-10-15 00:06\n[~] Обзор Планеты: В графике полета флотов изменено  цветовое  кодирование  для\n    своих флотов\n\n\n2010-10-14 19:32\n[~] Изменена картинка для линейного крейсера на более подходящую по гамме\n[@] Страницы логина и регистрации теперь подгружают свой собственный CSS, а  не\n    используют global.css для хранения стилей\n[@] Почищены лишние стили в CSS-файлов встроенных скинов\n\n\n2010-10-14 16:32\n[~] Строительство/Здания: Количество ресурсов в табличке  ресурсов  в  описании\n    юнита теперь выравниваются по правой стороне\n\n\n2010-10-14 02:20\n[~] Добавлены сведения о копирайте на все страницы (через overal_footer.tpl)\n[@]    Перераспределены    функции    в    файлах    /includes/unlocalized.php,\n    /includes/functions.php, /includes/functions/sys_template.php между файлами\n    /includes/functions.php и /includes/template.php\n\n\n2010-10-14 02:20\n[@] В  CheckInputStrings  deprecated   функция   eregi_replace   заменена   на\n    preg_replace. Соответствующим  образом  изменено  значение  $ListCensure  в\n    /includes/constants.php\n[@] Функции  CheckInputStrings,  PrintPlanetCoords,  sys_logHit  перенесены  из\n    индивидуальных файлов в /includes/functions.php\n\n\n2010-10-14 01:57\n[~] Строительство/Здания: Изменен цвет рамки превьюшки юнита\n[@] Строительство/Здания: Для рамки  выделения  превьюшки  теперь  используются\n    стили из  CSS.  Добавлены  цвета  для  встроенных  стилей.  См.,  например,\n    /skins/EpicBlue/formatte.css, стиль \".unit_preview\"\n\n\n2010-10-14 00:40\n[@] Содержимое файла /db/mysql.php перенесено в /includes/db.php - все равно  в\n    текущем виде SQL-код непортабелен и сильно привязан к  MySQL.  Каталог  /db\n    вместе с содержимым уничтожен\n\n\n2010-10-14 00:30\n[~] Флоты: Немного изменен попап с описанием флотов. Добавлено общее количество\n    ресурсов, которое везет флот\n[~] Небольшое редактирование описания зданий и их названий\n\n\n2010-10-13 20:52 Работа над ошибками\n[@] Добавлена дополнительную информацию  в  запись  об  ошибке:  лог  запросов,\n    $_GET, $_POST\n[@] Улучшено отображение ошибок на странице ошибок\n[@] Теперь игрокам показывается ID ошибки в БД, а не её порядковый  номер.  Это\n    должно ускорить поиск нужной записи об ошибке\n[@] Deprecated    процедура    mysql_escape_string    заменена    везде    на\n    mysql_real_escape_string\n\n\n2010-10-13 20:08\n[@] Исправленв ошибка отображения ошибки на странице ошибок администратора\n\n\n2010-10-13 10:21\n[~] Немного изменен порядок зданий на странице  постройки.  Теперь  все  здания\n    сгруппированы по типам: первая  линейка  -  добывающие,  вторая  линейка  -\n    производящие, третья линейка - склады. На луне и/или при не всех  доступных\n    для постройки зданиях порядок пока может меняться\n\n\n2010-10-13 09:39\n[@] Добавлены  новые   переменные   game_default_language,   game_default_skin,\n    game_default_template. Подробнее см. includes/classes/cache.php . Их  можно\n    изменить на странице настройки сервера\n[@] Отформатирован update.php в PCG1\n\n\n2010-10-13 07:59\n[@] Небольшие изменения для подготовки ко включению профилирования SQL-запросов\n\n\n2010-10-13 04:50\n[~] Строительство/Здания: Переверстан блок информации о здании.  Новая  верстка\n    более статична. Кроме того, устранена проблема из старой  верстки:  слишком\n    широкая таблица требуемых ресурсов (например,  когда  количество  какого-то\n    ресурса превышала сотню миллионов) сбивала описание\n[@] Строительство/Здания: Переписаны функции вывода блока информации  о  здании.\n    Теперь генерируется не вся информация о  здании,  а  заполняются  отдельные\n    поля. Это дает возможность легко изменять верстку этого блока\n\n\n2010-10-13 02:19\n[~] Симулятор: добавлены кнопки \"0\" и \"макс\" для автозаполнения поля  значением\n    флота с текущей планеты/технологии текущего игрока. Так же добавлены кнопки\n    массового заполнения всех полей соответствующего типа\n[~] Симулятор:  Теперь  нормально  работает  табуляция:  кнопки  \"0\"  и  \"макс\"\n    исключены из очереди табуляции, а поля со  значениями  проходятся  в  более\n    логичном  порядке  -  сначала  все  поля  атакующего,  затем  -  все   поля\n    оброняющегося\n\n\n2010-10-13 00:44\n[@] Сммулятор:  почти  полностью  переписан.  Нетронутым  остался  только  код\n    генерации отчета\n[@] Симулятор: simulator.tpl теперь использует возможности PTE\n[@] Симулятор: Новая система генерации REPLAY-линка\n\n\n2010-10-11 16:25\n[%] Дополнительные  изменения,  что  бы  не  допустить  одновременного  старта\n    нескольких обсчетов статистик\n[%] Дополнительные  изменения,  что  бы  не  допустить  одновременного  старта\n    нескольких апгрейдов базы данных\n\n\n2010-10-11 16:12\n[~] Немного переписал chat.js и включил автообновление чата\n\n\n2010-10-11 14:57\n[+] Империя: Добавлено количество строящихся юнитов - выделены зеленым цветом\n\n\n2010-10-11 13:36\n[+] Империя: Добавлено количество прилетающих кораблей - выделены желтым цветом\n[%] Изменен  шедуллер,  что  бы  минимизировать  вероятность  двойного  обсчета\n    статистики\n\n\n2010-10-10 10:10\n[+] В  шпионские  отчеты  добавлена  ссылка  на  симуляцию  боя  с  указанными\n    параметрами. После тестирования фича будет ВРЕМЕННО  добавлена  на  игровые\n    сервера. После введения системы Наемников она  будет  доступна  только  при\n    определенном активном Наемнике\n[+] В левое меню в раздел \"Информация\" добавилась ссылка  \"Мировые  константы\",\n    ведущая на страницу с перечнем настроек Вселенной\n[~] Максимальное количество колоний у игрока по умолчанию увеличено до 9\n[~] Доработан новый интерфейс строительства зданий\n[~] Добавлено время строительства к превьюшке на новой  странице  строительства\n    зданий\n[~] Баланс ресурсов на  превьюшке  теперь  показывается  с  учетом  ресурсах  в\n    прибывающих собственных флотах\n[@] На админскую страницу  настройки  сервера  добавлена  возможность  изменять\n    следующие параметры: режим игры (СуперНова или оГейм); количество галактик\n    во Вселенной, систем в галактике, планет в системе; максимальное количество\n    КОЛОНИЙ (см. ниже); курсы обмена металла, кристалла, дейтерия и  ТМ; ссылка\n    на страницу с правилами.\n[@] Переменная player_max_planets изменена на player_max_colonies и теперь  это\n    максимальное количество колоний у игрока (НЕ СЧИТАЯ главную планету)\n[@] Переменные game_speed и fleet_speed теперь кратны единице (а не  2500,  как\n    раньше). Можно вводить дробную скорость. Например  скорость  флота  0.5  (с\n    точкой!) означает уменьшение скорости флота  в  два  раза.  Соответствующим\n    образом изменены все формулы\n[@] Две новые процедуры get_game_speed() и get_fleet_speed() - для  корректного\n    получения скорости игры и флота соответственно\n[@] В скрипте sn_timer теперь используются объекты, а не  массивы.  Подробности\n    см. в файле js/sn_timers.js . Соответствующим образом  везде  изменен  код,\n    использующий таймеры\n[@] Версия БД увеличена до 17. Подробности см. в файле includes/update.php\n[@] Исправлена ошибка, приводящая к исчезновению пунктов меню \"Обслуживание\"  и\n   \"Резервное копирование\"\n\n\n2010-10-08 17:06\n[~] Текущая дата-время и количество игроков  онлайн  перекочевали  со  страницы\n    обзора планеты в верхнюю панель\n[~] Новости с \"Обзора планеты\" перенесены на страницу \"Император\"\n[~] Немного переработан \"Обзор планеты\"\n[~] Теперь если есть непрочитанная новость, в левом меню возле пункта \"Новости\"\n    появляется статус \"Обновленно\"\n\n\n2010-10-08 12:34\n[~] Немного изменен алгоритм расчета  восстановления  защитных  сооружений  при\n    атаке. Теперь  при  количестве  сооружений  одного  типа  меньше  10,  шанс\n    восстановления кидается для КАЖДОГО сооружения  этого  типа  отдельно.  Для\n    особых тугодумов - теперь МОЖНО уничтожить защитные купола  и  ПЗ.  Это  НЕ\n    является изменением, а  лишь  исправление  ошибки,  когда  невозможно  было\n    уничтожить атаками всю защиту до конца\n[~] Немного изменен новый экран постройки зданий\n\n\n2010-10-04 01:43\n[!] Добавлена новая страница \"Император\" в левое  меню.  На  нее  вынесена  вся\n    статистика игрока со страницы \"Обзор планеты\"\n\n\n2010-10-03 23:27\n[~] На  странице  \"Обзор  Империи\"  количество  ресурсов  теперь  показывается\n    актуальное. Раньше нужно было зайти на планету для того, что бы  информация\n    о ней обновилась\n\n\n2010-10-03 22:08\n[+] Чёрный Рынок: добавились слайдеры на страницы продажи и покупки кораблей\n[~] \"Биржа ресурсов\" из левого меню переехала в \"Чёрный Рынок\"\n[~] Переработана структура левого меню\n\n\n2010-10-01 23:16\n[+] В настройки пользователя временно  добавлена  возможность  включить  старый\n    интерфейс строительства зданий - галочка  \"Старый  интерфейс  строительства\n    зданий\" в разделе \"Совместимость - старые интерфейсов\", меню \"Настроки\"\n\n\n2010-10-01 20:26\n[!] Изменен интерфейс строительства. Из-за хакерской атаки пришлось  накатывать\n    полусырую спешно доделанную заготовку. Интерфейс будет дорабатываться\n\n\n2010-09-29 12:30\n[~] Изменил картинку на экранах Логин и Регистрация. Теперь она  подходит  даже\n    для широкоформатных мониторов.\n[~] Добавил картинку на режим отпуска\n\n\n2010-09-28 21:10\n[~] Изменены  характеристики  Шахты  Металлов.  Для  соответствия   внутренним\n    обменным рейтингам (4-2-1) её скорость  производства  увеличена  на  треть.\n    Соответствующим образом увеличено её энергопотребление\n\n\n2010-09-28 07:54\n[~] На картинки планет тултип на иконках исследований теперь показывает  время,\n    оставшееся до конца исследования на момент обновления страницы\n\n\n2010-09-28 01:08\n[%] Исправлен баг в МИС, когда лаборатория с более низким уровнем на планете  с\n    меньшим ИД блокировала присоединение лаборатории с более высоким уровнем\n[%] Исправлен баг в  МИС,  дававший  возможность  участвовать  в  исследованиях\n    лабораториям слишком низкоуровневым для исследований  данной  технологии  в\n    одиночку\n[%] Исправлен баг в МИС, когда игнорировался уровень МИТ и  присоединялись  все\n    лаборатории на всех планетах. Хотя бы один игрок об этом сказал за год!\n[+] МИС  теперь  работает  корректно.  Присоединяется  количество  лабораторий,\n    эквивалентное уровню МИТ+1. Лаборатории присоединяются в  порядке  убывания\n    уровня и только те, которые могут в одиночку исследовать данную технологию.\n\n\n2010-09-26 04:00\n[~] В  отчете  Симулятора  в  самом  низу  теперь  два  линка,  которыми  можно\n    поделится. Один сразу же запустить симуляцию с аналогичными параметрами,  а\n    второй позволяет сначала отредактировать параметры.\n\n\n2010-09-25 23:46\n[~] Переделан внешний вид страницы логина\n[~] Переделан внешний вид страницы регистрации.  Так  же  добавлена  ссылка  на\n    правила\n\n\n2010-09-25 02:53\n[!] Начато заполнение будущей  энциклопедии  СуперНовы.  Добавлены:  таймлайн,\n    история мира, описание джамп-технологии, описание кристаллов\n[~] Изменения в коде  для  дальнейшего  облегчения  модификаций.  Подготовка  к\n    разделению движка на СН и ОГ\n\n\n2010-09-23 14:01\n[!] \"СуперНова\" стала публичным проектом!  Загрузить  исходный  код  можно  из\n    репозитория\n    git://github.com/supernova-ws/SuperNova.git\n    Disclaimer aka Отмазка: Внимание! Проект находится в стадии альфа-версии! В\n    настоящее  время  он  не  предназначен  для  production-использования!  Код\n    предоставляется \"as-is\". Вы используете его на свой страх и риск! Автор  не\n    несет ответственности за материальный, моральный, кармический,  душевный  и\n    любой другой ущерб,  причиненный  вам  от  использования,  неиспользования,\n    самим фактом существования этого кода или любым другим способом.\n    Код распространяется под лицензией GNU GENERAL PUBLIC  LICENSE  Version  2,\n    June 1991. Сама лицензия находится в файле docs/license.txt дистрибутива.\n[+] Добавлен новый корабль \"Супертранспорт\".  Его  описание,  характеристики  и\n    требования для постройки можно посмотреть в игре\n[~] Размер иконки \"Уничтожить\" на странице строительства зданий уменьшен с  25%\n    до 15% от оригинального изображения\n[~] Множество мелких багфиксов, оптимизаций и исправлений\n\n\n2010-09-14 09:17\n[%] Исправлено исчезновение некоторых надписей на странице \"Обзор Империи\"\n\n\n2010-09-13 20:01\n[~] Добавлена иконка предупреждения  о  летящем  вражеском  флоте  на  картинку\n    планеты на странице \"Обзор Империи\"\n[~] Добавлена название строющегося здания на странице \"Обзор Империи\"\n[~] На  картинке  планеты  на  страницах  \"Обзор  Империи\"  и  \"Обзор  Планеты\"\n    добавлена иконка, сигнализирующая о летящем собственном флоте. Щелчок мышью\n    на иконке переведет на страницу \"Флот\" соответствующей планеты\n[~] Если есть летящие свои флоты на  текущую  планету,  на  странице  постройки\n    зданий под описанием здания появляется строка \"С учетом летящих флотов:\". В\n    ней содержатся те же расчеты, что  и  в  строке  \"Останется  ресурсов\",  но\n    (СЮРПРИЗ!!!) с учетом ресурсов на летящих собственных флотах\n\n\n2010-09-13 12:08\n[~] На  страницах  \"Обзор  планеты\"  и  \"Обзор  империи\"  подложил  под  иконки\n    строительства фон с 75% прозрачности - это  улучшает  видимость  иконок  на\n    картинках очень светлых планет\n\n\n2010-09-13 11:55\n[~] На странице выбора кораблей (ссылка \"Флоты\") в  \"Текущие  параметры  флота\"\n    добавлено общее количество ресурсов на планете\n\n\n2010-09-13 08:28\n[~] По многочисленным просьбам игроков добавлено время прибытия  и  возвращения\n    на страницу выбора цели полета при отправке флота\n\n\n2010-09-12 13:06\n[~] По  многочисленным  просьбам  игроков  добавлены  кнопки  \"-\"  и   \"+\"   к\n    метаэлементу ввода числовых значений\n\n\n2010-09-12 12:07\n[%] Исправлена ошибка, когда счетчик сообщений увеличивался лишний раз на 1 при\n    достижении переработчиками поля обломков.\n\n\n2010-09-10 23:25\n[~] Теперь  на  страницах  \"Обзор  планеты\"  и  \"Империя\"  над  всеми  иконками\n    строительства всплывает тултип с текущим объектом строительства\n[@] Теперь на страницах \"Обзор планеты\" и \"Империя\"  можно  кликать  на  иконки\n    строительства. Клик перебросит на соотвутствующую страницу строительства на\n    указанной планете\n[~] На странице \"Империя\" под изображением планеты  добавился  таймер  текущего\n    строящегося здания (если таковое есть)\n\n\n2010-09-10 22:48\n[%] Исправлены  ошибки  отображения  скорости  флота  на  при  отправке  флота\n    (страница выбора кораблей и страница выбора цели)\n\n\n2010-09-10 21:31\n[+] Флоты: При выборе количества кораблей во флоте и выборе загрузки ресурсов в\n    трюме дополнительные элменты \"0\", \"-\", \"+\" и \"макс\" заменены слайдерами\n[+] Флоты:  На   странице   выбора   количества   кораблей   теперь   доступна\n    предварительная    информация    об    отправляемом    флоте:     скорость,\n    грузоподъемность, расход, время полета в один конец,  дальность.  Последние\n    три цифры актуальны при переходе на страницу флота через меню \"Вселенная\" -\n    будут показаны точные данные для выбранных кораблей. В случае  перехода  на\n    страницу через пункт меню \"Флот\" эти три цифры будут показаны для  перелета\n    на ту же самую планету\n[~] Флоты: Существенно почищен код яваскриптов. Скрипты флотов  оптимизированы,\n    а сам файл значительно \"похудел\"\n[~] Флоты: Так же некоторые оптимизации в темплейтах и PHP-коде отправки флотов\n[~] На странице \"Империя\" на изображениях планеты  теперь  показываются  иконки\n    строительств и заполненности планеты. Иконка атаки пока НЕ показывается\n[~] Некоторое количество небольших чисток кода\n\n\n2010-09-07 22:19\n[!] Полностью  переделан  модуль   отправки   флотов.   Уменьшено   количество\n    передаваемых данных между страницами  и  активно  используется  JavaScript.\n    Добавлены дополнительные кнопки к выбору  количества  кораблей  и  загрузке\n    трюма. Переделан алгоритм определения доступных миссий\n[!] Изменен алгоритм  генерации  событий  флота  на  странице  \"Обзор\".  Теперь\n    корректно отображаются флоты, прибывающие/отлетающие в одно и то же время -\n    нельзя замаскировать флот в САБе.\n[!] Начата  миграция  с  wz_tooltip  (overlib)  на  jQuery_ui.   Это   улучшит\n    кросс-браузерную совместимость -  в  частности,  всплывающие  окошки  будут\n    корректно позиционироваться во всех браузерах. В настоящий момент на  новую\n    библиотеку переведена страница \"Обзор\" и страница \"Флот\"\n[~] Теперь флот, в котором есть хоть один шпион,  можно  на  вражескую  планету\n    только с заданием \"Шпионаж\"\n[%] Исправил отображение  количества  игроков  онлайн  при  большом  количестве\n    игороков на сервере\n[%] Исправлен баг с невозможностью отправки  переработчиков  на  поле  обломков\n    через меню \"Флот\"\n[%] Исправлен баг, дающий возможность отправлять шпионов в атаку или в САБ\n\n\n2010-09-07 04:11\n[%] Пофиксил дурацкий баг в sn_timer.js, который приводил к \"порче\" правильного\n    запомненного времени в случае,  когда  на  странице  было  несколько  часов\n    реального времени (например - на странице \"Обзор планет\" на самой  странице\n    и в верхнем меню).\n\n\n2010-09-07 03:45\n[%] Исправлен глюк с неотображением в некоторых случаях  загрузки  хранилищ  на\n    странице \"Ресурсы\"\n\n\n2010-09-07 02:14\n[~] Обзор планеты: Переделан внешний вид индикатора застройки\n[~] Обзор  планеты:  Иконка  атаки  на  изображениях  луны  и  планеты  теперь\n    click-through. Это означает, что при  выборе  планеты/луны  можно  спокойно\n    кликать на иконку атаки, а не выцеливать место для клика, как раньше\n[~] Обзор   планеты:   Строка   с   названием   планеты    и    ссылкой    на\n    переименование/уничтожение планеты переехала  под  График  движения  флотов\n    (сразу над расписанием строительств)\n[~] Обзор планеты: Объединил вместе строки \"Время\" и \"Игроки on-line\", а так же\n    вынес её поверх уведомлений о новых сообщениях и получении ТМ\n\n[%] Обзор планеты: Исправил баг с индикатором застройки  на  изображении  луны.\n    Раньше показывался индикатор планеты, а не луны\n\n\n2010-09-07 00:04\n[~] По многочисленным просьбам игроков, таки добавил колонку \"Откуда\" в  График\n    движения флотов на странице \"Обзор планеты\"\n\n\n2010-09-06 23:05\n[%] Пофиксил отображение процента загруженности хранилищ на странице \"Ресурсы\".\n    Если процент загрузки был 0% это отображалось как \"%\".\n\n\n2010-09-06 12:25\n[+] Переделан список флотов в полете. Теперь они представляются в виде  таблицы\n    и имеют два раздела: флоты, летящие на текущую планету и флоты, летящие  на\n    другие планеты, а так же ракетные атаки.\n    Кратенький FAQ\n    Q: Почему когда я отправил флот с  фун-ей  Транспорт,  то  возврат  пишется\n       вверху?\n    A: График флотов разделен  на  события  для  текущей  планеты  и  для  всех\n       остальных. Т.е. что бы когда твою планету атакуют, ты бы  не  путался  в\n       десятках событий от двадцати флотов, а переключился на планету  и  видел\n       сразу - когда твои флоты прибывают, когда вражеские итд.\n\n    Q: Странно отображаются мои ракетные атаки...\n    A: Ракетная атака в графике движения указывает планету назначения. При этом\n       если выбрать планету, с которой её отправили, ракетная атака  перекочует\n       в верхний список.\n\n[~] Переделал верхнюю панель -  избавился  от  встроенного  кода  в  темплейте,\n    перевел счетчик ресурсов на sn_timer.js\n[~] Перевел календарь на  странице  Обзор  планеты  на  sn_timer.js.  Полностью\n    избавился от файла timer.js\n\n[%] Пофиксил логику вычисления производства (ресурсы в час). Оно, правда, кроме\n    отображения ни на что не влияло, зато теперь  правильно  тикают  ресурсы  в\n    навбаре и правильно показывает производство на странице \"Империя\"\n\n\n2010-09-03 23:21\n[~] Добавлен большой восклицательный знак на изображение  планеты,  на  которую\n    идет вражеская атака\n\n\n2010-09-03 16:13\n[~] Добавлен индикатор застройки на изображениях планеты/луны.  Легенда,  такая\n    же, как и в Обзоре планет: красный - планета застроена полностью; желтый  -\n    на 80%; зеленый - меньше 80%\n[~] Изображение луны теперь накладывается в правый верхний угол соответствующей\n    планеты\n\n\n2010-09-03 08:22\n[~] Миссия флота на странице Обзора выделяется белым цветом\n\n\n2010-09-02 01:50\n[~] В попапах с описанием флота добавлен разделитель тысяч\n[~] Миссия флота на странице Обзора выделяется италиком\n\n[%] Поправлен баг с раскраской атакующего  флота  на  странице  Обзора  планет.\n    Теперь он выделяется как полгается по скину\n\n\n2010-08-30 15:18\n[%] Исправлен баг, не позволяющий перейти на ссылку во вселенной при нажатии на\n    координаты планеты в Графике полета флотов\n[%] Исправлен  баг  с  отображением  события  \"Возвращение  флота\"  для  миссий\n    \"Колонизировать\" и \"Передислокация\". Теперь оно  отображается  только  если\n    игрок сам вернул флот с задания\n\n\n2010-08-13 23:33\n[~] Добавлен большой восклицательный знак на изображение луны, на которую  идет\n    вражеская атака (включая миссию \"Уничтожить\")\n[-] Империя:  Убрал  со  страницы  \"технологии\"  -  их  можно   посмотреть   в\n    соответствующем меню и нет смысла добавлять кучу строк в таблицу Империи  с\n    дублирующейся информацией\n[~] Империя: Используется цветовое кодирование для полей.  Застроенные  планеты\n    отмечаются красным в соответствующей ячейке строки \"Поля\"\n[~] Империя:  Используется  цветовое  кодирование  для  энергии.   Планеты   с\n    отрицательным балансом энергии отмечаются красным в соответствующей  ячейке\n    строки \"Энергия\"\n[~] Империя:  Используется  цветовое  кодирование  для  ресурсов.  Планеты   с\n    заполненными складами отмечаются красным в соответствующей ячейке ресурса\n[~] Империя: Теперь список полей на странице учитывает Терраформеры - раньше не\n    учитывал\n\n[%] Должен быть исправлен глюк с многократным повторением обоих меню\n[%] Исправлен один из глюков с зависанием флотов в САБе\n\n\n2010-08-07 15:27\n[%] Исправил баг с временем возврата из экспедиции\n\n\n2010-08-06 09:56\n[%] Исправил баг с временем возврата с удержания\n\n\n2010-08-06 08:46\n[~] Теперь новые новости отмечаются тегом  \"НОВАЯ\".  Новость  считается  новой,\n    если ей меньше трех дней\n\n[%] Исправил баг с многократным обсчетом САБа\n[%] Исправил баг, не позволявший снести здание, если ресурсов на снос  хватало,\n    но не хватало на постройку следующего уровня\n\n\n2010-08-04 17:03\n[+] Включено  кэширование  темплейтов.  Там,  где  используется  новая  система\n    темплейтов, должна увеличиться скорость генерации страниц\n\n[~] Теперь на странице флота при наведении на количество единиц в списке флотов\n    в полете показывается в красивом виде не только летящий флот, но и  ресурсы\n    на борту\n[~] Теперь на странице обзора на изображении планет добавлены  иконки  гаечного\n    ключа, реторты и космического корабля соответственно для строящихся зданий,\n    исследований и кораблей/обороны\n\n\n2010-08-01 16:19\n[+] Включена  система  реферралов.  Теперь  за  каждые  10  ТМ,   заработанные\n    приведенным  вами  игроком,  вы  получите  1  ТМ.  Подробнее  -  по  ссылке\n    \"!!!ЗАРАБОТАЙ ТМ!!!\" в левом меню на каждом из серверов\n\n[~] Оптимизированный менеджер флотов. Количество запросов при обновлении флотов\n    уменьшено на 20%. Это уменьшило загрузку сервера\n[~] Пачка мелких исправлений и оптимизаций\n\n\n2010-07-30 07:41\n[%] Исправил ошибку в именовании  новых  планет  -  бралось  имя  не  владельца\n    планеты, а того, в чей ход запускалась обработка флота\n\n[~] Добавил часы локального времени в топ-бар под дропдауном переключения планет\n\n\n2010-07-30 00:13\n[~] Теперь можно посылать один колонизатор с  ресурсами  без  флота  -  ресурсы\n    будут корректно выгружены на новосозданную колонию\n[~] Тепрь новое  название  планеты  не  такое  безличное  -  оно  включает  имя\n    пользователя и номер  колонии  по  счету.  Это  облегчает  навигацию  между\n    колониями\n\n\n2010-07-29 20:38\n[+] Все таймеры  на  стороне  клиента  теперь  корректируют  свои  показания  в\n    соответствии с сервером. Это означает, что: а) Дата и время показывается  в\n    пересчете для локальной машны и б) Таймеры синхронизированы  с  сервером  с\n    точностью  до  нескольких  секунд  (ошибка  возможно  при  большем  времени\n    отправки-получения запроса плюс время реакции сервера)\n\n[~] Переработана страница флотов: переставлены колонки таблицы в более логичном\n    порядке, добавлен таймер для времени прибытия  в  пункт  назначения  (а  не\n    только   таймер   возвращения    в    пункт    отправления),    в    пункты\n    отправления/назначения добавлены указатели на тип цели (планета, луна, поле\n    обломков) и еще кое-какие мелочи\n\n\n2010-07-25 17:36\n[~] Пофиксен баг с отправкой многострочных  сообщений  членам  Альянса.  Теперь\n    сообщения приходят в корректном виде\n[~] Пофиксен глюк интерфейса, когда при сохранении настроек пользователя вместо\n    отчета об удачном сохранении выдавало пустую плашку\n[~] Пофиксен баг в JS  на  второй  странице  флота.  Суть  бага  -  если  через\n    интерфейс \"Вселенной\" выбрать задание \"Транспорт\" или \"Передислокация\",  на\n    второй странице флота не будет виден трюм, до тех пор, пока не перещелкнешь\n    по миссиям\n[~] Пофиксен баг с вычислением скорости у малых транспортников при переходе  на\n    импульсный двигатель и у бомберов при переходе на гипердвигатель\n\n[@] Системное: Процедура отсылки сообщений теперь может работать с  несколькими\n    адресатами. Это уменьшает количество запросов к БД  при  массовой  рассылке\n    ровно в N-раз, где N - количество членов рассылки. В частности, это  сильно\n    ускоряет рассылку всем членам Альянса.\n\n\n2010-07-17 21:10\n[%] Установлен багфикс, устраняющий проблему с образованием  поля  осколков  на\n    недавно созданных планетах.\n\n\n2010-07-16 02:32\n[%] Установлен патч, устраняющий проблему с необразованием поля обломков.\n\n\n2010-07-14 02:43\n[~] Изменен алгоритм отправки флотов. Теперь для  некоторых  миссий  ресурсы  в\n    трюмы флота в принципе  незагружаемы.  Список  таких  миссий:  атака,  САБ,\n    Удержание, Шпионаж, Уничтожение, Переработка, Исследование. Для всех их нет\n    никакого смысла загружать ресурсы в трюмы.\n[%] Добавлена цветовая кодировка флотов на странице \"Обзор\" для скина EpicBlue.\n[%] Исправлена ошибка отправки сообщений новому игроку с одной планетой.\n[%] Исправлены некорректные ссылки на страницу офицеров при получении новых ТМ.\n[%] Исправлена ошибка Чёрного Рынка с продавцом Б/У кораблей.\n\n\n2010-07-12 13:20\n[!] Обзор планеты: Для таймеров используется написанная с нуля JS-библиотека.\n\n[-] Обзор планеты: Очередь построек флота убрана  -  теперь  показывает  только\n    текущую постройку на верфи\n[~] Обзор планеты: ОБновление очереди построек  флота  сделана  без  обновления\n    страницы\n[~] Обзор планеты: Список планет вынесен справа от основного блока  информации.\n    Все планеты, луны кликабельны. Для планет  со  строительством  показывается\n    текущий уровень постройки и оставшееся время. На  планеты  распространяются\n    настройки сортировки на странице настроек игрока.\n[~] Обзор планеты: Множество мелких исправлений и ускорений на странице\n\n\n2010-07-03 14:50\n[%] Куча открытых окон неиспользуемого чата сильно грузит сервер. Уже несколько\n    раз куча окон доводила сервера до состояния 500 Imternal  Error,  когда  не\n    грузились другие страницы. Поэтому введен таймаут неактивности 15 минут, по\n    истечении которого чат отключается.\n\n\n2010-06-29 20:00\n[!] Полностью с нуля написан \"Чёрный  Рынок\".  Код  вылизан,  интерфейс  сделан\n    максимально удобным (по моим  понятиям).  В  Торговце  ресурсами  добавлена\n    возможность обмена ТМ на ресурсы (обратный обмен  -  невозможен).  Торговец\n    флотом полностью переработан и позволяет теперь в одну транзакцию продавать\n    сразу несколько типов кораблей.\n\n[+] Серьезно переработана \"Вселенная\" (бывш. \"Галактика\"). Несмотря на то,  что\n    вид  \"Вселенной\"  практически  не  изменился,  была  проделана  грандиозная\n    работа: переработано  с  десяток  файлов.  Темплейты  выкушены  из  кода  и\n    перенесены в отдельный  файл.  Поп-апы  (инфо  о  планете,  луну,  альянсе,\n    обломках, пользователе) вынесены в отдельные JS-процедуры, что в  отдельных\n    случаях позволило уменьшить финальный размер страницы  в  два-три  раза,  а\n    количество запросов к БД снизить от двух до десяти (!)  раз  -  особенно  в\n    системе,  густо  заселенной  одним  игроком.  Специально  под   переработку\n    \"Вселенной\" в движок была добавлена система темплейтов из phpBB3.  В  целом\n    новая \"Вселенная\" существенно меньше грузит сервер  и  должна  работать  на\n    клиенте гораздо быстрее.\n\n\n2010-05-25 15:52\n[+] Запущена Партнерская программа\n\n\n2010-04-26 01:24\n[~] Теперь Инженер улучшает проивзодство энергии на всех генерирующих мощностях\n    (включая спутники и солнечные электростанции)\n[~] Во времени постройки  теперь  учитывается  дейетрий. Соответственно,  время\n    постройки всех юнитов, использующих дейтерий, возросло\n\n\n2010-03-03 16:58\n[~] Луна. Максимальный шанс луны уменьшен до 30%. Стоимость лун - 1% на  каждый\n    1кк ресурсов до  капа  в  30%.  Таким  образом,  максимальный  размер  луны\n    составит 8999 км.\n[%] Фикс, устраняющий проблемы с начислением ресурсов и дюпы ракет/шпионов\n\n2009-10-12 23:20\n[~] Изменена формула расчета размера луны\n    $SizeMin = 1000 + ( $Chance * 100 );\n    $SizeMax = 2999 + ( $Chance * 200 );\n    Т.е. от 1000 км до 9999 км.\n\n2009-10-12 22:32\n[~] Теперь для постройки Звезды Смерти требуется  1  уровень  Разрушителя.  Без\n    Разрушителя Звезда Смерти не строится. При Разрушителе строится одна Звезда\n    Смерти.\n\n2009-07-05 14:28\n[%] Найдена ошибка отображения в игре - вместо уровня ресерча щитов отображался\n    уровень  ресерча  брони  и  наоборот.  Поэтому  не  удивляйтесь,  если  они\n    поменялись местами.\n\n\n2010-05-03 09:38 Initial revsion\n[!] Движок СуперНовы базируется на XNova RageRepack v.226\n\n[!] Боевка: альтернативная версия боевого движка - рабочие САБы; новая  система\n    расчета боя; унифицированный код между обсчетом боя,  уничтожением  луны  и\n    симулятором; частичный возврат ресурсов  от  обороны;  оптимизированый  код\n    итд.\n[!] Симулятор боя: почти полностью переписан\n[!] Ракетная атака: полностью переписан скрипт\n[!] Ролевая система: Полностью переписана система начисления опыта и ТМ\n[!] Новости: Полноценный модуль новостей\n[!] Google AdSense (слева внизу под меню, плашка 125х125)\n[!] Безопасность: закрыты все обнаруженные дыры в  безопасности  (SQL-injection\n    через параметры страницы) на страницах, доступных пользователям.\n[!] Альянсы: Полностью переписан модуль управления Альянсами.\n[!] Друзья: Полностью переписан модуль \"Друзей\"\n[!] Админка/Статистика:  Полностью  переписан  модуль  обновления  статистики.\n[!] Темплейты:  Добавлен  движок  рендеринга  темплейтов  от  phpBB3.   Движок\n    доработан до обратной совместимости с темплейтами XNova\n[!] Чёрный Рынок: Полностью переписан Чёрный Рынок\n[!] Баннеры: Переработана система генерации баннеров/юзербаров. Унифицированы\n    процедуры их генерации, слиты в один файл  процедур  и  один  файл-враппер,\n    вынесены PHP-файлы из каталога scripts в соответствующие места\n[!] Вселенная (бывш. \"Галактика\") - почти полностью переписана\n[!] Интерфейс/Обзор: Переработана страница \"Обзор планеты\"\n[!] Движок: Поддержка xCache\n\n[+] Админка/Баннерилка: Полностью переписан код\n[+] Админка/ТМ: с нуля написана страница добавления/удаления ТМ\n[+] Админка/Ошибки: переработан список ошибок\n[+] Почта: Оптимизированы запросы к БД. Добавлены индексы к  таблице  messages.\n    Добавлена возможность просмотра отправленных сообщений (только тех, которые\n    не удалил адресат)\n\n[~] Движок: Корректное отображение количества пользователей\n[~] Движок: Убрано множество неиспользуемых файлов\n[~] Движок: Написана  с  нуля  библиотека  таймеров  js/sn_timer.js.  Формат  и\n    примеры использования - см. в файле библиотеки\n[~] Другие изменения в движке\n\n[~] Интерфейс/Альянсы:  Теперь  по  клику  на  координатах  участника  альянса\n    работает переход в его систему\n[~] Интерфейс/Верфи:  На  странице  видны  боевые  данные  кораблей  и  обороны\n    (броня/щиты/оружие)\n[~] Интерфейс/Меню: добавлен пункт меню \"Закладки\"\n[~] Интерфейс/Ресурсы:  оптимизирована  страница;   убрана   ненужная   строка\n    заполненности  хранилищ  (их  всегда  видно  в  верхней  строке   статуса);\n    добавлена возможность изменить производство сразу для всех заводов\n\n[%] Движок: Исправлен  глюк,  не  отображавший  несколько  летящих  на  планету\n    флотов, если их время прибытия было одинаковое. В частности, это  позволяло\n    скрывать все флоты САБа, кроме последнего\n[%] Множество мелких багфиксов и исправлений\n"
  },
  {
    "path": "docs/class_tree.txt",
    "content": "[*] Репозиторий и Хранилище\n        - Хранилище оперирует записями в БД - то ли Record, то ли вообще набор пропертей. Подумать\n            - Storage так же осуществляет кэширование между сессиями!\n        - Репозиторий оперирует EntityDB\n            - В перспективе ЭНТИТИ ДОЛЖНЫ ПРОИЗВОДИТЬ ВСЕ ОПЕРАЦИИ ЧТЕНИЯ/ЗАПИСИ/ПОИСКА ЧЕРЕЗ РЕПОЗИТОРИЙ!\n            - Функционал:\n                - Кэширование энтити в рамках ОДНОГО ЗАПУСКА (! подумать);\n                - Автоматическая поддержка транзакций/версий;\n                - Автоматическая поддержка блокировок;\n                - Автоматическое сохранение новых энтити в БД при добавлении их в репозиторий;\n\n\nIContainer - базовый интерфейс, перечисляющий магические методы и\n|   - isEmpty() - Does container can be declared as \"Empty\" - i.e. does not contain any data and can be destroyed\n|   - clear() - Make container \"Empty\"\n|\n+-- AccessMagic - Хранит виртуальные свойства объекта во внутреннем контейнере\n    - __constructor() - конструктор и свойство для доступа к сервисам $services\n    - protected $values - хранят значения виртуальных свойств\n    - реализация методов интерфейса\n    - public asArray() - дамп свойств в виде массива\n    |\n    +-- AccessLogged - Сохраняет изменения между коммитами в форме, пригодной для раскладки в SQL-запросы SET/+-CHANGE\n        - protected $_startValues - хранит стартовые значения переменных между загрузкой и коммитом\n        - protected $_changes - прямая запись переменных через __set()\n        - protected $_deltas - Дельта изменений от оригинального значения. Только для числовых значений - int и float\n        - protected $_currentOperation - статус текущей операции: перезапись или инкремент/декремент на значение\n        - __set() - поддерживает не только прямую запись, но и инкремент/декремент\n        - inc()/dec() - включает режим инкримента/декремента для следующей операции\n        - isChanged() - работает сейчас некорректно. Должен так же менятся $_changes при первой записи\n        - getChanges() - получить список перезаписей\n        - getDeltas() - получить список дельт по значениям\n        - accept() - подтверждает изменения\n        - reject() - откатывает изменения\n        |\n        +-- AccessLoggedV2 - Почти как AccessLogged, только с первой записи виртуального свойства сразу обрабатывает его как изменение, без записи в startValues\n        |\n        +--\n"
  },
  {
    "path": "docs/config.php.sample",
    "content": "<?php\nif(!defined(\"INSIDE\")){ die(\"attemp hacking\"); }\n\n$dbsettings = array(\n  'sn_driver'  => 'db_mysql_v5',    // db_mysql_v5 - MySQLi. Можно написать свой драйвер\n  \"server\"     => \"localhost\",      // MySQL сервер.\n  \"user\"       => \"supernova_user\", // Логин доступа MySQL сервера.\n  \"pass\"       => \"MYSQL_PASSWORD\", // Пароль доступа MySQL сервера.\n  \"name\"       => \"supernova\",      // Название MySQL базы.\n  \"prefix\"     => \"sn_\",            // Префикс для таблиц в базе и для xCache. ИСПОЛЬЗУЙТЕ РАЗНЫЕ ПРЕФИКСЫ, ЕСЛИ НА ОДНОМ СЕРВЕРЕ РАБОТАЕТ НЕСКОЛЬКО КОПИЙ SN!!!\n  \"secretword\" => \"SUPERNOVA\",      // Ключевое слово для создания Cookies. (Не забудьте изменить на своё!)\n);\n\n$game['prefix'] = $dbsettings['prefix'];\n"
  },
  {
    "path": "docs/credits.txt",
    "content": "js-cookie v2.1.3\n## Authors\n* [Klaus Hartl](https://github.com/carhartl)\n* [Fagner Brack](https://github.com/FagnerMartinsBrack)\n* And awesome [contributors](https://github.com/js-cookie/js-cookie/graphs/contributors)\n\n\njQuery UI Touch Punch 0.2.3\n * Copyright 2011–2014, Dave Furfero\n * Dual licensed under the MIT or GPL Version 2 licenses.\nDownloaded from http://touchpunch.furf.com/\n\n\nspeedometer icon\nhttp://www.flaticon.com/free-icon/speedometer_52166#term=speed&page=1&position=11\n<div>Icons made by <a href=\"http://www.flaticon.com/authors/robin-kylander\" title=\"Robin Kylander\">Robin Kylander</a> from <a href=\"http://www.flaticon.com\" title=\"Flaticon\">www.flaticon.com</a>             is licensed by <a href=\"http://creativecommons.org/licenses/by/3.0/\" title=\"Creative Commons BY 3.0\">CC BY 3.0</a></div>\n\nJerrycan icon\nhttp://www.flaticon.com/free-icon/oil_85805#term=fuel&page=1&position=28\n<div>Icons made by <a href=\"http://www.freepik.com\" title=\"Freepik\">Freepik</a> from <a href=\"http://www.flaticon.com\" title=\"Flaticon\">www.flaticon.com</a>             is licensed by <a href=\"http://creativecommons.org/licenses/by/3.0/\" title=\"Creative Commons BY 3.0\">CC BY 3.0</a></div>\n\nPost it note+pin icon\nhttp://www.flaticon.com/free-icon/post-it_14489#term=pin&page=1&position=37\n<div>Icons made by <a href=\"http://www.freepik.com\" title=\"Freepik\">Freepik</a> from <a href=\"http://www.flaticon.com\" title=\"Flaticon\">www.flaticon.com</a>             is licensed by <a href=\"http://creativecommons.org/licenses/by/3.0/\" title=\"Creative Commons BY 3.0\">CC BY 3.0</a></div>\n\nPin icon\nhttp://www.flaticon.com/free-icon/push-pin_69667#term=pin&page=1&position=57\n<div>Icons made by <a href=\"http://www.freepik.com\" title=\"Freepik\">Freepik</a> from <a href=\"http://www.flaticon.com\" title=\"Flaticon\">www.flaticon.com</a>             is licensed by <a href=\"http://creativecommons.org/licenses/by/3.0/\" title=\"Creative Commons BY 3.0\">CC BY 3.0</a></div>\n\nAdvisor image\nDune - The Battle for Arrakis - The Mentat Ammon by Grindguy\nhttp://grindguy.deviantart.com/art/Dune-The-Battle-for-Arrakis-The-Mentat-Ammon-446290900\n\nCrystal icon\nUses icons from “Kingdom Icons” by Mihaiciuc Bogdan[http://bogo-d.deviantart.com.]\nhttp://bogo-d.deviantart.com/art/Kingdom-Icons-212892514\n\n\nEnergy icon\n<div>Icons made by <a href=\"http://www.flaticon.com/authors/kirill-kazachek\" title=\"Kirill Kazachek\">Kirill Kazachek</a> from <a href=\"http://www.flaticon.com\" title=\"Flaticon\">www.flaticon.com</a> is licensed by <a href=\"http://creativecommons.org/licenses/by/3.0/\" title=\"Creative Commons BY 3.0\" target=\"_blank\">CC 3.0 BY</a></div>"
  },
  {
    "path": "docs/extra/readme.txt",
    "content": "Extra files\n\nphpBB3.zip - some phpBB3 stuff\nsources - sources for used libraries\nsql - sample SQL scripts\n"
  },
  {
    "path": "docs/extra/sql/account_by_user_name.sql",
    "content": "SELECT\n  gu.id,\n  gu.username,\n  gu.email,\n  gac.*,\n  ga.*\nFROM `sn_users` AS gu\n  LEFT JOIN sn_account_translate AS gac ON gac.user_id = gu.id\n  LEFT JOIN sn_account AS ga ON ga.account_id = gac.provider_account_id\nWHERE username = 'admin';"
  },
  {
    "path": "docs/html/.htaccess",
    "content": "<Files ~ \"\\.html$\">\n  order deny,allow\n  allow from all\n</Files>\n"
  },
  {
    "path": "docs/html/changelog.html",
    "content": "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">\n<html xmlns=\"http://www.w3.org/1999/xhtml\" lang=\"ru\" xml:lang=\"ru\" dir=\"LTR\">\n<head>\n<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\" />\n<style type=\"text/css\"><!--\nbody {font-family: monospace}\nli, ol, ul, pre {margin: 0}\ndiv {margin-bottom: 10px}\n\n.important {color: green; font-weight: bold;}\n.added {color: green;}\n.removed {font-style: italic; color: red}\n.changed {color: blue}\n.fixed {color: red}\n.admin {font-style: italic;}\n.module {color: purple; font-weight: bold;}\n.todo {}\n.date {margin-bottom: 0; color: grey}\n--></style>\n</head>\n<body>\n<h1>\nОбозначения\n~~\n[#] Модуль, не входящий в публичную версию/Private module\n[!] Нововведение или важное изменение/New features or important change\n[+] Добавлено/New functions\n[~] Изменено/Changed functions\n[%] Исправление-багфикс/Bugfixes\n[@] Эта информация будет интересна только админам и/или разработчикам/This information is only for administrators and/or developers\n\nProject \"SuperNova.WS\" Release 46 \"Совершеннолетие\"\n~~\n[!] Server Instances. Now several servers can work on one copy of SN engine\n    * Instance-specific files located in `/servers/{domainName}[{_port}]/` folders\n        * `_port` part is optional and used only for servers running on ports other then 80 or 443\n        * {domainName} is SN's domain name\n        * I.e. for `sn.domain.com` folder would be `/servers/sn.domain.com/`\n        * I.e. for `sn.other_domain.com:8080` folder would be `/servers/sn.other_domain.com_8080/`\n    * If SN detects `config.php` file in instance-specific folder it would use it instead of `/config.php`\n    * If SN detectes subfolder `avatars` in instance folder writable by web-server - it would be used for player and Alliance avatars\n        * I.e. if for domain `sn.domain.com` there is folder `/servers/sn.domain.com/avatars` and it writable by web server - it would be used as avatar storage\n[!] Brand new Fleet Dispatcher to prevent locks with main thread\n    * Rewrote dispatcher code in `classes/Fleet/FleetDispatcher.php`\n        * Added separate class `FleetDispatcherEvent` to handle all fleet event-related stuff\n        * Now for each fleet all related users/planets/fleets are locked\n        * Now missile attacks handled each in own transaction - not in large one. Should reduce probability of deadlocks\n    * Now `sys_o_get_updated()` accepts only user/planet IDs as parameters to reduce probability of index/gap DB lock\n    * Adjusted code to work with new internal API & FleetDispatcher\n        * includes/includes/flt_mission_relocate.php\n        * includes/includes/flt_mission_colonize.php\n        * includes/includes/flt_mission_transport.php\n    * Renamed internal fields and replaced internal representation from array to fields\n        * classes/Fleet/MissionData.php\n    * Moved transaction and locks into main missile loop\n        * includes/functions/flt_mission_missile.php\n    * Refactored mission code to class\n    * Now fleet dispatcher internal messages also contains count of processed IPRs\n    * Mission Explore constants moved to Fleet/Constants\n    * FFH now skips FLT_FLEET_ARRIVE events for MT_HOLD/MT_EXPEDITION\n    * Deadlock fixed: sn_fleet_page3() - 'fleet.php'@228: \"SELECT * FROM `game_fleets` WHERE `fleet_end_galaxy` = G AND `fleet_end_system` = S AND `fleet_end_planet` = P AND `fleet_end_type` = T AND `fleet_owner` = FO AND `fleet_mission` IN (1,2,9) AND `fleet_mess` = 0 FOR UPDATE\"\n    * Deadlock fixed: flotenajax.php@97: \"SELECT * FROM {{users}} WHERE `id` = X LIMIT 1 FOR UPDATE\"\n[!] Support for overriding skin images via `override.css`\n[!] Language\n    Added translation to Spanish (computer-generated)\n    Added translation to German (computer-generated)\n[!] JS cacher - merges all JS files into one and caches results\n[!] CSS cacher\n    Internal subsystem to compact several CSS files into one file and cache it\n    Updated skins to accomodate CSS cacher\n    CSS cacher now caches module's CSS files regardless of supplied extension (`.min.css`, `.css` or no extension)\n[!] Tools - Spritify\n    New tool to create sprite PNG and CSS for it from set of images\n    * Adjusted behavior in case if first frame in A-GIF smaller then largest one\n    * Now Spritify honors offset in first frame of A-GIF\n    * Spritify supports `RESTORE_TO_BACKGROUND_COLOR` disposition method\n    * Now Spritify generates pure CSS animations for extracted A-GIF frames, honoring delay between frames\n    * Animated GIF frame expansion - disposal methods `UNSPECIFIED` and `DO_NOT_DISPOSE`\n    * Basic support for animated GIFs - extract frames into sprite line\n[!] Menu\n    Switched to using sprites as menu icons\n[!] PHP 7-compatibility: SN now compatible with PHP 7.4\n    * Fixed no que items on building pages\n    * Fixed que for mass-operations\n    * Fixed other PHP 7 compatibility issues\n    * Added support for `fastcgi_finish_request()` where it supported\n    * More PHP7/MariaDB shenanigans\n\n[+] Admin/userlist\n    Added ability to reverse-sort by all sortable fields\n    Now userlist page opens by default sorted descending last login time\n[+] Admin/Ban\n    * Basic ban-by-ip facility - table `ban_ip`. Supports IPv4 ranges\n[+] Navbar\n    * Navbar now use sprites\n    * This should speed up page loading due to combining multiple standard navbar buttons into one sprite\n\n[~] Chat\n    Enchanced URL parsing/replacement in chat messages (for appropriate ACCESS_LEVELS)\n[~] Logs\n    * In log text \"\\n\" replaced with \"<br />\" on output\n[~] Admin\n    * Now in log viewer user options stored in `user.options` parsed for better readability\n[~] Admin\n    * Adjusted log detail output\n[~] Fleets\n    * `fleet_dispatch_max_time` renamed to `fleet_update_dispatch_time` for uniformity\n[~] Fleets\n    * Now length of each fleet dispatch job can be configured via `fleet_dispatch_max_time` config (float). Default: 3 seconds\n    * `GAME_FLEET_HANDLER_MAX_TIME` is obsolete\n[~] Юниты\n    Поддержка Фестиваля \"ДР-2023\"\n[~] FFH - Added total event amount to FFH timeout message\n[~] Now SN recognize `/avatars` folder in code root as folder for non-instanced servers and prefers it to use before `/image/avatar` folder\n    * It's recommended to move all avatars to this folder because in future `/image/avatar` folder would be deprecated and unsupported\n[~] DB\n    Драйвер db_mysql_v4 удалён\n    Для db_mysql_v5 `TRANSACTION ISOLATION LEVEL` установлен в `REPEATABLE READ`\n[~] Template engine fixes\n    Fixed `error: template->_tpl_load_file()` for module template files loaded on admin page\n    Fixed `error: template->_tpl_load_file()` when module template files tried to load files from core template\n        I.e. alliance manager page tries to load `eco_que` template from core template\n\n[%] HTML\n    Fixed vertical scroll bar on main content cell in latest Chrome versions\n[%] Language\n    * Fixed not loading module l10n files before user init\n[%] HTML\n    Fixed strange jQueryUI behavior on texteareas in new Chrome\n[%] News\n    Now announce editing/copying works again in Chrome\n[%] News\n    Now non-admin users doesn't see announces\n\n[@] Debug\n    Config `debug` option now can be customized\n    * if it set to `1` then `error_reporting` would be set to `E_ALL ^ E_NOTICE ^ E_DEPRECATED ^ E_WARNING`\n    * if it set to different value - then `error_reporting` would be set for this value\n        * Example: `debug` value for `E_ALL ^ E_NOTICE ^ E_DEPRECATED ^ E_WARNING` is `32767 ^ 8 ^ 8192 ^ 2` = 24565\n[@] Code - templates\n    * More robust way to add JS files for compiler\n[@] Replaced JQuery with large version for easier development\n[@] Language\n    * Changed some language variables\n        * language/en/system.mo.php\n        * language/en/tech.mo.php\n        * language/ru/system.mo.php\n        * language/ru/tech.mo.php\n[@] Code\n    `AwardConstants` now tried to be included in the `init.php` - to allow loading of modules that uses their constants\n[@] Code\n    * Functions moved from `SN` to `db_mysql`\n        * `db_transaction_start()`\n        * `db_transaction_commit()`\n        * `db_transaction_rollback()`\n        * `db_transaction_check()`\n    * `SN::DB_TRANSACTION_XXX` constants moved `db_mysql::TRANSACTION_LEVEL_XXX`\n    * `SN::TRANSACTION_XXX` constants moved `db_mysql::TRANSACTION_XXX`\n    * Variables moved from `SN` to `db_mysql`: `$db_in_transaction`, `$transaction_id`, `$transactionDepth`\n[@] Code\n    * `includes/db_deprecated.php` - removed\n    * `classes/DBAL/db_mysql_v5.php` - removed\n    * Addded `pre()` and `pred()` debug functions\n[@] Code\n    * `$sys_user_logged_in` => `SN::$sys_user_logged_in`\n    * Debug - now prepends sql log filename with current instance DB name\n    * `sys_o_get_updated` - simplified error reporting on empty user\n    * Removed `db_queries_user.php` -> `db_get_user_by_where`\n    * `db_queries_user.php` -> `db_user_by_id` now accepts only userId as first param\n    * Streamlined and formatted code a bit\n[@] DB\n    * fighting deadlocks\n        * Table `que` - index I_que_planet_id expanded to (`que_planet_id`, `que_player_id`)\n        * Table `planets` - index `id` dropped\n        * Table `users` - index `I_user_id_name` dropped\n[@] DB\n    * Transaction level for MySQL raised to 'SERIALIZABLE' to reduce deadlocks\n    * Adjusted how SQL queries reported\n[@] Fleet\n    * Added old fields to Fleet\\Fleet to use in low-level functions\n    * Rewrote fleet lock conditions from `?:` to `if`\n    * Replaces some strings with constants\n    * Some code reformatting\n[@] Code\n    * Added transaction depth counter into `SN` class\n[@] Code\n    * Now `DEBUG_SQL_XXX` constants ruled by appropriately named records in `config` table:\n        * `DEBUG_SQL_FILE_LOG` replaces `DEBUG_SQL_ONLINE` - implies `DEBUG_SQL_ERROR` and `DEBUG_SQL_COMMENT_LONG`\n        * `DEBUG_SQL_ERROR` - implies `DEBUG_SQL_COMMENT`\n        * `DEBUG_SQL_COMMENT_LONG` - implies `DEBUG_SQL_COMMENT`\n        * `DEBUG_SQL_COMMENT`\n[@] Code\n    * User options separator `|` move to constant `USER_OPTIONS_SPLIT`\n    * `EVENT_FLT_XXX` constants now are strings for better debug\n[@] DB\n    * Dropped unneeded indexes to reduce deadlocks\n        * counter.counter_id\n        * que.que_id\n        * captain.captain_id\n[@] SQL\n    * Added comments to transaction-related statements - to easier found them later in debug/dump\n    * Added separate table `festival_config` for festival-specific config\n    * Migrated festival config to separate table\n[@] Modules\n    * Added support for `javascript_filenames` in `sn_mvc`:\n        * Modules' JS now added to new subarray\n        * Now modules' JS can be cached too\n        * This fixed errors with modules' JS loaded too early and not working\n[@] Tools - `spritify` update\n[@] Code\n    Streamlined `classConfig` - listed all known config keys with types\n[@] HTML\n    Rearranged JS code/file include in `_40_js.tpl.html`\n[@] Code\n    Basic support for different environments - @see: .env.ini.example\n\nProject \"SuperNova.WS\" Release 45 \"11 years anniversary\"\n~~\n[!] Установка/обновление\n    Улучшен процесс установки/обновления\n    Расширен раздел, посвященный проблемам с установкой или обновлением в файлах /docs/install.md и /docs/install-en.md\n[!] Чат\n    Полностью новый чат\n    Встроенная система команд с поддержкой алиасов команд\n    Встроенная система помощи по командам чата - команда /help\n    Добавлен список игроков в чате с дополнительными иконками статуса и командами управления для админов\n    Возможность игрокам управлять своим состоянием видимости в чате - команда /invisible. Администрация сервера (authlevel > 0) всегда видит невидимок\n    Возможность отправлять приватные сообщения другим игрокам - команда /whisper. Приватные сообщения выделяются специальным образом, видны во всех каналах и сохраняются в истории чата. В приватных сообщенях нельзя употреблять форматирование цветом\n    Администраторы имеют возможность запретить игроку писать в чат на определенный срок или вернуть такую возможность - соответственно, команды /mute и /unmute. Запрет распространяется на все каналы и на возможность писать личные сообщения. Соответствующая иконка в списке игроков лишает его права голоса на 1 час\n    Администраторы имеют возможность блокировать и разблокировать игроков прямо из чата - соответственно, команды /mute и /unmute. Иконка в списке игроков банит его на 1 неделю\n    Системные и приватные сообщения выделяются жирным шрифтом\n    Скорость обновления в AJAX части чата регулируется переменной 'chat_refresh_rate'\n    Игроки из онлайн-списка исчезают сразу после выхода из чата - таймаут попадания в список установлен как удвоенный 'chat_refresh_rate'\n    В чате доступен расширенный функционал BBCode\n[!] Личные сообщения\n    Переработан вид сообщений\n    Добавлена функция игнора игроков в ЛС:\n        - В личных сообщениях добавлена возможность добавить игрока в игнор-лист\n        - Сообщения от игроков в игнор-листе не видны в списке ЛС\n        - Убрать игрока из игнор-листа можно на соответствующей вкладке в \"Настройках\"\n    Добавлена иконка игнора в список сообщений\n[!] Флоты\n    Диспетчер флотов теперь использует системы Task+Worker и является неблокирующим\n    Таким образом, теперь нет визуальных задержек на обсчёт флотов (кому-то из игроков раньше не везло каждые 4 секунды)\n    Так же флоты обсчитываются не кусками по 3 секунды, а в пределах, заданным параметром `fleet_update_max_run_time` (30 секунд по умолчанию)\n[!] Админка/Редактирование планет\n    Переписана и включена админка редактирования планет. Можно редактировать:\n        - Строения\n        - Флот\n        - Оборону\n        - Ресурсы\n    Можно как добавлять юниты, так и удалять их (вводя значения с минусом)\n    На экране просмотра игрока добавлена вкладка со списком планет, клик на которых открыает экран редактирования планеты\n[!] Модули\n    Изменена система версионирования модулей\n    Теперь в качестве версии модуля используется версия билда, в котором были закоммичены изменения\n[!] Код\n    Новая система рабочих процессов Worker\n    Новая система задач и блокировок (Task/Lock). Пока используется только для обсчёта летящих флотов\n    Добавлена поддержка формата изображений WebP\n[!] Документация\n    Инструкция по установке/обновлению переделана в формат MarkDown:\n        - /README -> /README.MD\n        - /docs/install.txt -> /docs/install.md\n        - /docs/install-en.txt -> /docs/install-en.md\n\n\n[+] Альянсы/Поиск\n    В результаты поиска Альянсов и в \"Рекомендуемые Альянсы\" (см. ниже) добавлены две колонки:\n        - Колонка \"Разница в очках\" указывает на разницу между количеством очков у игрока и средним количеством очков на одного игрока в Альянсе. Если она отрицательная - средний игрок в Альянсе имеет больше очков, чем текущий игрок\n        - Колонка \"Рейт\" указывает на соотношение между количеством очков у игрока и средним количеством очков на одного игрока в Альянсе. Если она меньше единицы - средний игрок в Альянсе слабее, чем текущий игрок\n    Для игрока без Альянса на странице поиска Альянса добавлен список \"Рекомендуемые Альянсы\" (далее - РА):\n        - В РА попадают Альянсы, чьё среднее количество очков на игрока в Альянсе не более чем в 5 раз отличается от количества очков текущего игрока\n        - РА сортируется по убыванию модуля разницы очков между количеством очков игрока и средним количеством очков Альянса\n        - список РА показывается и при простом заходе на страницу Альянса без поиска других Альянсов\n    Нужно отметить, что для начинающих игроков список РА будет, скорее всего, пуст - почти нет активных Альянсов, в которые входят только начинающие игроки\n    Чем дальше играет и развивается игрок, тем больше Альянсов будет в этом списке. Однако, по мере приближения к Топу сервера, этот список будет очевидным образом сужаться\n[+] Интерфейс/Обзор планеты\n    В экспериментальном порядке дизайн страницы \"Обзор Планеты\" сделан респонзивным.\n    Т.е. на широких экранах элементы страницы - инфа о планете, список планет и список флотов в полете - будут выстроены в три колонки на всю ширину монитора\n    На мобильных устройствах устройствах с узкими дисплеями элементы страницы будут располагаться друг под другом\n\n[~] Список планет\n    Унифицирован показ статуса планеты (Столица, Луна):\n        - Теперь \"Обзоре планеты\" и \"Империи\" статус планеты показывается соответствующим значком;\n        - На страницах, указанных выше, а так же в меню выбора планеты для Луны Столицы показывается оба значка\n    В \"Обзоре\" теперь название и координаты планеты/луны показываются в самом низу блока планеты\n[~] Планета/Переименование\n    Теперь при переименовании планеты на странице \"Обзор\" и \"Управление планетой\" имя планеты меняется сразу, а не после повторного обновления страницы\n\n[%] Исследования\n    Исправлена ошибка, позволяющая запустить исследование на одной планете в то время, когда на другой планете строится/исследуется Лаборатория или Нанолаборатория при отключенной настройки сервера \"BuildLab\"\n    Исследования блокируются даже если Лаборатория или Нанолаборатория сейчас не строятся, а просто находятся в очереди построек\n\n[@] Код\n    CSS:\n        - Добавлена возможность отключать наложение скинов на элементы управления (input, button) у всех дочерних элементов сразу\n    Навбар:\n        - Количество ресурсов в ресбаре теперь всегда выравнивается по правой стороне ячейки\n    Меню:\n        - Раскраска пунктов меню теперь снимается через CSS, а не через JS\n        - Вид пунктов меню кнопки/ссылки теперь переключается через CSS, а не через JS\n        - Кнопка показа/скрытия меню теперь привязывается к самому меню, а не к абсолютным координатам\n    sn_timer.js:\n        - Добавлена поддержка human-readable времени в sn_timer.js\n        - Исправлена ошибка, когда длина бара в таймере отсчёта могла быть > 100%\n    Темплейты:\n        - Теперь SnTemplate может выводить страницу целиком. Это даёт возможность объединить хидер и футер в один файл\n        - SnTemplate::display() теперь закрывает все существующие буффера вывода\n        - Теперь если у темплейта нет родителя или он не существует, то в качестве fallback темплейта используется темплейт по умолчанию\n        - Добавлена поддержка наследования темплейтов (см. _template.ini):\n            - Поддерживается только один уровень наследования\n            - Поддерживается наследование и дозагрузка _template.css\n        - Добавлена поддержка переключения темплейтов (альфа-версия без вывод в интерфейс игроков)\n        - Убраны глобальные константы, относящиеся к темплейтам: TEMPLATE_NAME, TEMPLATE_DIR, TEMPLATE_PATH\n        - Добавлен метод для добавления JS файлов в темплейт из PHP\n        - Создан новый класс SnTemplate, куда перенесена часть функций в виде статических методов\n    JS таймер отчета (например - показ ресурсов псевдо-онлайн) теперь может работать по имени класса, а не только по ИД - т.е. управлять сразу несколькими элементами\n    Бенчмарк теперь старается вставить данные о производительности в <body>, а не после\n\n\nProject \"SuperNova.WS\" Release 44 \"10 years anniversary\"\n~~\n[#] ad_promo_codes 1a2 - Промо-коды\n    (!) Промо-коды позволяют начислять указанное количество юнитов игроку, который ввёл соответствующий код:\n            - Использование промо-кода через настройки игрока. Пункт меню \"Настройки\"\n            - Максимальное количество использований (0 - без лимита)\n            - Отслеживание использований промо-кода игроками (лог)\n            - Поддерживаемый список юнитов для добавления:\n                - Планетарные ресурсы - металл, кристалл, дейтерий\n                - Тёмная Материя\n                - Корабли\n                - Артефакты\n        Админка:\n            - Список промо-кодов с количеством использований\n            - CRUD для промо-кода\n\n[#] payment_interkassa_form 0a1 - Платёжная система \"Interkassa\"\n    (!) Реализован протокол \"Interkassa Form SCI\"\n        Поддержка режима тестирования\n        Конфигурация отдельным файлом config.php в каталоге модуля. Если конфигурация недоступна - модуль отключается\n        Поддержка выбора способа платежа InterKassa\n        Генерик-плательщик InterKassa\n        Поддержка отображения приблизительной конечной цены\n\n[#] payment_unitpay_form 0a1 - Платёжная система \"UnitPay\"\n    (!) Реализован протокол \"UnitPay Form\"\n        Поддержка режима тестирования\n        Конфигурация отдельным файлом config.php в каталоге модуля. Если конфигурация недоступна - модуль отключается\n        Поддержка выбора способа платежа UnitPay\n        Генерик-плательщик UnitPay\n        Выбора метода платежа в UnitPay через интерфейс платежа СН\n        Теперь при выборе платёжной системы в СН в UnitPay не высвечивается приглашение к выбору другого метода\n\n[#] core_festival 10a5\n    (+) Админка\n            Изменена сортировка активити\n            Добавлена шкала Активити и отметки начала/конца активити\n    (+) Хайспот/Gather\n        Добавлена ловушка для тупых читеров\n        Добавлены флаги автобана\n        Добавлен пересчёт даты из шаблона в текущий год если вычисленная дата больше даты окончания хайспота\n            - Нужно в основном для НГ, что бы после НГ не отключались ништяки\n    (~) Хайспот/День Рождения СН\n        Немного переделана админка\n    (~) Объекты в космосе/Юниты\n        Sputnik теперь невозможно построить игроком\n\n[#] admin_stat 0a4\n    (~) Админка/Средний онлайн\n        График среднего онлайна внесен в таблицу с числами\n        Убраны неиспользуемые элементы\n        Удалён неиспользуемый код\n\n[#] player_award 1a1\n    (~) Улучшены медали за 1-2-3 места для 10-летия СН\n        Добавлены медали для 10-летия СН\n        Добавлены медали и их описания для СНГ-2019\n\n[!] Админка/Активность игрока\n    Добавлен просмотр активности игрока\n        - Работает только при включённом счётчике посещений\n        - Ссылка - на админской странице просмотра игрока\n        - Просмотреть можно активность с 1 января 2018 года\n        - Интервал просмотра активности - 1 час\n        - Пустой квадратик - активности в этот час не было\n        - Заполненный - была активность. При наведении курсора - указывается час и активность в минутах\n            - Из-за особенностей работы счётчика активность может быть более 1 часа\n    Красным баром добавлен процент активности в данном часе\n\n[+] Флоты/САБ\n    Максимальное количество флотов в САБе ограничено 5\n    Теперь САБ ограничивается по сумме очков игроков:\n        - Ограничение распространяется так же и в большую сторону, т.е. при атаке \"слабыми\" игроками \"сильных\"\n        - В остальном - правила и коэфициенты те же, что и при расчёте сильных/слабых игроков (ака \"нуб-защита\")\n[+] Счётчик\n    Оптимизирована работа счётчика посещений:\n        - Добавлена новая таблица `security_query_strings` для записи параметров запроса\n            - `counter` теперь ссылается на записи в ней, а не на полный URL страницы\n        - Изменена таблица `security_player_entry` - исключён ИД пользователя\n            - `counter` теперь ссылается на записи в ней и не содержит поля с ИД устройства, браузера, IP/прокси\n\n[~] Альянсы\n    Добавлена ссылка на страницу статистики Альянсов - список всех Альянсов и их статистика\n[~] Контакты\n    Добавлена ссылка на ЛС членов администрации сервера\n    Немного переверстана страница\n[~] Юниты\n    Добавлена поддержка юнитов, которые игрок не может построить, но может получить в ходе Фестиваля или других активностей - требование UNIT_CAN_NOT_BE_BUILD\n[~] Платежи\n    Добавлена поддержка отображения приблизительной конечной цены при покупке ММ (не для всех платёжных систем)\n    Немного перевёрстана страница платежей\n[~] Безопасность\n    Небольшой апдейт системы безопасности\n    ...а так же всякие мелкие оптимизации.\n\n\nProject \"SuperNova.WS\" Release 43 \"Double Release\"\n~~\n[!] PHP\n    Для работы СН требуется PHP не ниже 5.5\n\n[#] player_login_token 0a8 - Струя удачи\n    (!) Новый модуль\n    (!) Награда за логин\n        Когда игрок логинится в игру первый раз за сутки по серверному времени - он получает в награду 1 логин-токен (далее - просто \"токен\"):\n            - При получении токена игроку на экран выдаётся извещение в виде попапа\n            - Попап появляется каждый раз до тех пор, пока игрок не закроет его нажатием кнопки \"Ок\"\n            - Теперь при входе в игру несколько дней подряд количество токенов, получаемых игроком в день, медленно увеличивается - это и называется \"струя удачи\"\n            - Если игрок не входит в игру хотя бы один - \"струя\" иссякает и количество токенов в день опять падает до 1\n            - Максимальное количество токенов, которые можно получить в день - 6\n        Токены можно использовать для участия в Токен-лотерее (далее - просто \"лотерея\"):\n            - Участие в 1 раунде лотереи стоит 1 токен\n            - Игрок может участвовать в лотерее неограниченное количество раз в день, пока у него хватает токенов\n            - В лотерее игрок может выиграть приз или не выиграть ничего\n            - В настоящее время список призов включает: ресурсы, ТМ, корабли, Артефакты\n            - Шанс на выигрыш и количество юнитов в призе пропорциональны стоимости юнита\n            - Общая стоимость выигрыша масштабируется от скорости добычи ресурсов на сервере и стоимости ТМ в металле\n        Тонкая настройка таблицы выигрышей производится в исходном коде модуля (см.)\n        Добавлена дополнительная фильтрация по процентной вероятности\n        Добавлено масштабирование приза в зависимости от уровня игрока\n        Добавлены разделители тысяч в количестве выигранных юнитов\n        Добавлена иконка к пункту меню\n        Сильно переработан попап с оповещением о получении токена. Теперь в нём выводится практически вся доступная информация по токену\n        Расширена и переработана подсказка игрока на странице\n        Добавлена отладка для просмотра распределения вероятностей\n\n[#] admin_balance 0a6 - Модуль \"Админ - Баланс\"\n    (!) Новый модуль\n    (!) Экран \"Юниты\" с поддержкой кораблей\n        Добавлена страница баланса UBE\n\n[#] core_festival 8b11\n    (!) Хайспот/Объекты в космосе\n        Новый хайспот\n        Теперь Премиум-аккаунт распространяется и на ядерные техи ОвК\n        Нанофабрики и Нанолаборатории для юнитов ОвК теперь действуют как Фабрики и Лаборатории соответственно для обычных юнитов\n        Добавлено описание ивента\n        В Новапедию добален раздел \"ивенты\"\n        Ядерная технология теперь считается фундаментальной и получает бонус от Марса\n        Добавлен вотчдог на попытку получить блокировку флотов\n        Админка\n            - Добавлены Звания к именам ботов и игроков\n            - Теперь заголовки и данные таблиц ЧЛ и ОвК различаются\n    (!) Хайспот/8 Марта\n        Добавлена возможность Кавалеру открыть свой ник\n        Добавлена возможность добавить текстовое поздравление к подарку\n        В списках подарков у Дам и Кавалеров каждый подарок теперь выводится отдельно\n        В списке полученных подарков у Дам:\n            - Выводится поздравление, если оно есть;\n            - Выводится игровой ник подарившего, если он решил его открыть;\n        В списке отправленных подарков у Кавалеров:\n            - Выводится значок (А) после имени Дамы, если подарок был сделан анонимно, т.е. без раскрытия ника;\n            - Выводится поздравление, если оно есть;\n        Теперь когда Кавалер дарит подарок, Даме отправляется личное сообщение с уведомлением. Сообщение содержит:\n            - Подаренную и начисленную (с учётом подарка от АД-ии) суммы ММ;\n            - Имя Кавалера, если он решил его раскрыть;\n            - Текст поздравления, если он есть\n        Изменена иконка в навбаре\n        Полностью переработан код админки и награждения\n        Теперь так же в админке начисляется ММ и выдаются Памятные Знаки Кавалеров\n    (!) Хайспот/Gathering\n        Рефакторинг:\n            - Теперь ништяк выставляется после полной прогрузки страницы\n        Админка:\n            - Добавлена базовая админка со статистикой ивента\n            - В админку добавлен список игроков и количества/стоимости найденных ими ништяков\n            - Добавлена возможность банить игроков в хайспоте\n            - Изменена сортировка получателей ништяков в админке. Теперь они сначала сортируются по полученной ТМ, а затем - по количеству кликов\n        Интерфейс:\n            - Кнопки хайспота теперь добавляются в основную часть навбара, а не в префиксную\n            - Теперь количество ТМ и ММ+ТМ в навбаре обновляется сразу после успешной подборки ништяка\n        Хеллоуин:\n            - Добавлены некоторые изображения в большем разрешении\n            - Изменены картинки подтверждения/провала при клике на ништяки\n            - Перебалансировано количество ништяков для Хэллоуина\n    (+) Хайспот/Чёрная Луна\n        Админка\n            - Подключен рандомайзер к экрану администрирования\n            - Реализован закат лун в автоматизированном режиме\n        Изменён алгоритм генерации брэкетов:\n            - Теперь верхняя граница первого брэкета определяется исходя из количества поинтов у топ-игрока\n            - Таким образом, топ-игрок теперь принципиально имеет шанс поучаствовать в ивенте, если не сильно оторвался от других игроков\n            - Все новички добавляются в самый последний из рассчитанных брэкетов\n            - Игроки с нулевым количеством поинтов участие в расчёте не принимают - вероятнее всего это заброшенные аккаунты\n    (+) Общий\n        Поддержка скидок на Премиум-аккаунты и Массовые Операции\n        Добавлена возможность Хайспотам добавлять переменные в общий пул\n    (+) Приз за ММ\n        Админка: добавлено начисление медалей участникам ивента и призёрам\n    (+) Админка\n        Улучшена общая админка для всех хайспотов\n    (~) Смайлики: В пути к смайликам можно использовать плейсхолдер {festival} - он будет оттранслирован в относительный путь, который требуется подсистемой смайликов\n\n[#] chat_advanced 6a4\n    Добавлена трансляция в сообщении полного URL с ссылкой на новость в BBCode\n    Добавлена трансляция в сообщении сокращённого URL news://XXX в BBCode\n    Теперь при обновлении чата так же обновляется количество игроков онлайн и общее количество игроков\n    Доработан вид сообщений в чате:\n        - Выровнены время сообщения, ник (и иконки) и само сообщение\n        - Уменьшен размер передаваемых данных для приходящих сообщений\n        - Улучшен генерируемый HTML-код\n        - Оптимизированы CSS-стили\n    Улучшено отображение чата для экранов разных размеров\n    Добавлено автоматическое скрытие правой панели (списка онлайн игроков) на мелких экранах с возможностью показать список онлайн\n\n[#] player_award 0d7\n    (+) Приз за ММ\n        Добавлены медали для СНГ-2018\n    (+) 8 марта\n        Универсальные медали и памятные знаки для ивента 8 марта\n        Разделены награды за количество ММ и за персон для Кавалеров и Дам\n        Добавлены все нужные изображения медалей\n\n    (~) Картинки\n        Переделаны миниатюры для медалей на 7-8 лет СН - улучшена чёткость\n        Пережаты картинки для уменьшения размера\n\n[#] menu_customize 0d8\n    (+) Сортировка пунктов\n        Теперь если в меню появляется новый пункт, которого нет в сортировке, он добавляется не в самый низ сортировки, а сразу вслед за пунктом, за которым он располагается в оригинальном меню\n\n[#] interface_batch_operations 1a4\n    (~) Результаты\n        Добавлены разделители тысяч в отчёт о работе МО\n\n\n[!] Воинские звания\n    В игру добавлены уровни игроков и военные звания для каждого уровня\n    Уровни присваиваются в зависимости от общего количества очков игрока\n    Если в настройках сервера отключён показ статистики для команды сервера, то все члены команды имеют 20й уровень и соответствующее звание вне зависимости от реального количества очков\n    Звания показываются перед ником игрока везде, где это имеет смысл\n    Так же картинка звания показывается на странице профиля игрока (ака \"Император\")\n    Всего в игре 21 звание - от Курсанта (уровень 0) до Генералиссимуса (уровень 20)\n    Посмотреть полный список званий и соответствующих им уровней можно кликнув на картинку звания на странице Император\n    Добавлен дополнительный уменьшенный комплект иконок специально для ников - для большей отзывчивости интерфейса\n    На иконки воинских званий в никах добавлены всплывающие подсказки с названиями званийи и уровнем\n[!] Столица\n    Теперь на Столице скорость строительства зданий, флота и обороны повышена в два раза\n    Теперь на Столице скорость добычи ресурсов повышена в два раза\n    Добавлена индикация Столицы в имени планеты иконкой ♕ &#9813;\n        - В списке планет в навбаре\n        - На странице \"Обзор планеты\"\n        - На странице \"Империя\"\n        - На странице \"Вселенная\"\n[!] ЛС\n    Добавлено листание сообщений по страницам. Каждая страница вмещает по 10 сообщений\n    На странице просмотра сообщений в дроп-дауне выделены цветом удаление всех сообщений категории и всех личных сообщений\n    Убрано удаление неотмеченных сообщений. Теперь просто отметить мультигалочкой (галочкой в заголовке) все сообщения на странице и удалить их\n    При просмотре сообщений оставлена лишь кнопка \"Удалить отмеченные сообщения\" ввиду бессмысленности остальных опций\n    Если нет отмеченных сообщений кнопка \"Удалить...\" неактивна\n    При просмотре сообщений добавлена кнопка \"Показать все\" - для показа всех сообщений в категории\n    Админитраторам включены BBCode и поддержка ссылок в отправляемых ими сообщениях\n    Добавлены разделители тысяч в количестве юнитов в шпионском отчёте\n[!] Админка\n    - Альянсы:\n        - Добавлена страница для просмотра списка Альянсов\n        - Добавлена страница для просмотра отдельного Альянса:\n            - Дамп записи Альянса;\n                - В админке теперь отображается форматирование текстов Альянса\n            - Список игроков с рангами, онлайном, отпуском, баном;\n            - Передача Альянса другому игроку;\n            - Цветовое кодирование Главы:\n                - Зеленый - активен на протяжении 30 дней;\n                - Красный - неактивен.\n    - Добыча игроков:\n        Страница просмотра общей добычи игроков\n        Добавлена колонка с суммарной производительностью в металле\n        На экран добавлены сортировки\n    - Модули\n        Новый экран со списком модулей, доступных в игре и их статусе\n        Есть сортировка по имени модуля и по его активности\n        Сортировка модулей по активности сделана дефолтной\n    - Перепаковка счётчиков посещений (СП)\n        См. страницу \"Утилиты\". Работает на машинах с 2Gb+ под PHP\n        Теперь не пакуются хиты свежее трёх недель\n        Теперь так же считаются общие хиты за визит\n        Теперь записи удаляются и обновляются пакетом\n        Теперь корректно обрабатываются визиты, находящиеся на стыке пакетов\n        Теперь выводится результат перепаковки\n        Теперь считается количество обработанных записей\n        Теперь неизменённые записи не записываются в БД\n        Добавлен счётчик обработанных и пропущенных записей\n        Добавлено новое условие прерывания обработки - если разница между ID обработанных записей меньше размера пакета\n        Добавлено дополнительное время на операцию удаления записей\n    - Платежи\n        Добавлена статистика на страницу платежей\n        В платежи добавлена листалка\n    - Список игроков\n        Добавлен емейл игрока в список игроков (для auth_level 3)\n        Добавлен статус отпуска игрока в список игроков (для auth_level 3) и сортировка по этому полю\n        К никам игроков добавлены иконки званий, отпуска, ДР и наград\n        Уменьшен размер страницы\n    - Просмотр игрока\n        Добавлена информация об аккаунте игрока\n        Добавлена возможность сменить пароль на аккаунте\n        Рефакторинг страницы просмотра игрока\n    - Настройки\n        Добавлена возможность настраивать опции смены имени игроком\n    - Обслуживание\n        Очистка СП от записей несуществующих пользователей\n    - Меню\n        Название уровня члена АД-ии (Модератор, Оператор итд) теперь является ссылкой на возврат в игру\n        Если в админском меню не стоит AUTH_LEVEL - прописывается высший (AUTH_LEVEL_ADMINISTRATOR = 3)\n    Обслуживание теперь не удаляет админские сообщения\n[!] Настройки\n    Рефакторинг кода и переработка дизайна страницы настроек\n    Теперь если смена имени игрока (ника) запрещена настройками сервера - то так и пишется в Настройках Игрока\n[!] Скины\n    Новые изображения Наёмников и Губренаторов в скине EpicBlue\n    Новые изображения Наёмников и Губренаторов в скине supernova-ivash\n    Добавлены большие изображения для Губернаторов\n    Добавлены полноформатные изображения Наёмников\n[!] Модули\n    Переписана и отрефакторена подсистема управления модулями\n    Добавлены новые методы в базовый класс модулей sn_module\n    Класс sn_module:\n        - Убрана зависимость от полей манифеста модуля:\n            - 'load_order' => getLoadOrder(), self::M_LOAD_ORDER\n    Класс Modules\\Manager:\n        - В этот класс вынесен функционал управления модулями из класса sn_module\n        - Включает в себя так же функционал убраных глобальных переменных $sn_module и $sn_module_list\n    Убрана поддержка модулей из одного файла и модулей без структуры sn_modules\n    sn_sys_load_php_files() - убран блок для $modules = true;\n    Добавлена поддержка MVC-опций в модулях (подмассив 'mvc' манифеста)\n\n[+] Флоты\n    Теперь статус \"Новый игрок\" рассчитывается с учётом скорости добычи ресурсов\n    Интерфейс: теперь при нехватке места в трюме под топливо на странице выбора миссии выводится соответствующее сообщение\n    Миссии:\n        - Шпионаж\n            Миссия переписана с нуля\n            Теперь в таблице `messages` сохраняются данные, а не HTML-код:\n                - Значительно уменьшен объем данных, сохраняемых в БД;\n                - Увеличено количество информации в БД;\n                - Рендеринг сообщения осуществляется непосредственно при просмотре, а не при сканировании;\n                - Уменьшено время на обсчёт миссии (см.пред.пункт);\n            Исправлена ошибка, при которой флоты с шансом обнаружения выше 100% не уничтожались\n            Теперь на электронную почту не отправляются полные шпионские отчёты, а лишь уведомление о сообщении типа \"шпионаж\" (без различия входящих и исходящих)\n            Добавлена поддержка анти-шпионажа\n        - Экспедиции: теперь в Экспедиции не могут быть найдены ивентовые или уникальные мировые корабли\n[+] Вселенная/Луны\n    Теперь при создании лун учитывается текущая скорость добычи ресурсов:\n        - Например, для сервера со скоростью добычи x10 стоимость создания луны так же удесятеряется\n        - Исправлена древняя ошибка, из-за которой луна после боя могла получится больше, чем на неё потрачено ресурсов\n    Унифицированы алгоритмы создания лун после боя, через Артефакты, через Админку итд\n[+] Империя\n    В тайтл страницы (заголовок окна браузера) добавлено название страницы \"Обзор Империи\"\n    Добавлены разделители тысяч ко всем количествам юнитов: имеющихся на планете, стоящих в очереди, летящих с флотами\n    К количеству ресурсов на планете и производству добавлена индикация ресурсов на летящих на планету флотах\n    Добавлен новый уровень цветового кодирования процентов производства. Теперь схема выглядит так:\n        - Голубой - 100% производства\n        - Зеленый - 90-80%%;\n        - Жёлтый - 70-50%%;\n        - Оранжевый - 40-10%%;\n        - Красный - 0%;\n    Теперь фон дроп-дауна так же подсвечивается в зависимости от процента производства по вышеописанной схеме\n    Добавлено цветовое кодирование для процентов в дропдауне - см. выше. Цвет букв более яркий для лучшего чтения на фоне текущего производства\n[+] Новости\n    Добавлена листалка на страницу Новостей. Теперь можно посмотреть Новости за всю историю игры!\n    Добавлена возможность ссылаться на отдельную новость:\n        - Ссылка на новость \"прячется\" под датой новости;\n        - При открытии - открывается только одна указанная Новость;\n        - Под заголовком страницы добавляется ссылка \"Все новости\" для возврата к полному списку новостей;\n    Добавлен заголовок страницы \"Новости\"\n    Форма добавления новости спрятана под кнопку \"Добавить новость\" - что бы не мозолила АД-ии глаза\n    Цвет ссылки \"Подробнее...\" изменен на ярко-голубой для лучшей читаемости\n[+] Симулятор\n    Добавлен чекбокс \"Симуляция\", отключающий фактор случайности и гарантирующий повторяемость результатов Симулятора\n    Теперь при создании Луны указывается количество обломков, которое образовалось бы без создания Луны\n[+] Вселенная\n    Перевёрстана панель выбора Галактики/Системы:\n        - Теперь она будет масштабироваться в зависимости от размера экрана\n        - Ну и на вид стала поприятнее\n    Теперь имена Игроков/Альянсов в случае длинных названий не \"ломаются\" по статусам, а остаются на одной строке\n[+] Награды/Бессмертный\n    Теперь не меняется дата/время установки статуса после повторных начислений ММ\n    Теперь так же при установке статуса начисляется памятный знак\n    Памятный знак будет начислен всем Бессмертным при апдейте\n    Лимит для получения статуса \"Бессмертный\" увеличен до 200.000 ММ\n    Количество ММ для награды Бессмертного теперь берется из конфигурации\n[+] BBCode\n    Добавлена поддержка BBCode [news=XXX] для вставки прямой ссылки на Новость\n    Добавлена поддержка сокращённого URL news://XXX для вставки прямой ссылки на Новость\n[+] Чат\n    Добавлена трансляция в сообщении полного URL с ссылкой на новость в BBCode\n    Добавлена трансляция в сообщении сокращённого URL news://XXX в BBCode\n    Оптимизированы CSS-стили\n    Вынесен JS-код из темплейта\n    Доработан вид сообщений в чате:\n        - Выровнены время сообщения, ник (и иконки) и само сообщение\n        - Уменьшен размер передаваемых данных для приходящих сообщений\n        - Улучшен генерируемый HTML-код\n[+] Апдейтер\n    Добавлен механизм патчей: проверка существования патча и его регистрация\n    Отформатирован код\n    Удалены апдейты по релиз 39 включительно\n    Проверка верcии движка\n        - /ajax_version_check.php преобразован в класс \\Tools\\VersionCheckerDeprecated\n        - В init.php переделан вызов версион-чекера\n[+] Респонзивный дизайн\n    Улучшено отображение чата для экранов разных размеров\n    Добавлено автоматическое скрытие правой панели (списка онлайн игроков) на мелких экранах с возможностью показать список онлайн\n\n[~] Интерфейс\n    Наёмники+Планы\n        - Добавлен список требований при покупке перманентных Наёмников\n        - Отключены элементы покупки при неудовлетворённых требованиях по юнитам (в случае перманентных Наёмников)\n        - Убрано дублирование сообщения об ошибках\n    Окно сообщений: теперь при отключении сервера не показывается навбар - не производится лишних расчётов/обращений к БД\n    Квесты: добавлены разделители тысяч к целям и наградам квестов\n    Навбар: добавлена поддержка изменения ТМ/ММ во время работы страницы\n    Список планет: в списке планет на иконках построек/верфи/обороны добавлено количество активных слотов в очереди\n    Статистика\n        - Изменен порядок вывода иконок для имени игрока\n        - Убрана отдельная колонка для ссылки на страницу игрока. Теперь клик на нике является такой ссылкой\n        - Добавлено выделение названия Альянса как ссылки\n        - Изменен порядок вывода иконок для имени игрока\n    Вид ников во всей игре унифицирован с новым видом на странице \"Статистика\"\n    Постройки\n        - Теперь пункты в списке требований к постройке являются ссылками на статью Новапедии о соответствующем юните/сущности игры\n        - Теперь при сносе здания учитываются наличие Губернатора и статус Столицы - снос при Губернаторе и/или на Столице происходит быстрее\n        - Теперь время строительства и сноса округляется вверх, а не математически. Это значит, что в среднем в половине случаев время постройки/сноса увеличится на 1 секунду\n    Исследования\n        - Изменено название страницы на 'Исследование технологий', что бы не было путаницы между пунктами меню \"Исследования\" и \"Технологии\"\n        - Добавлена ссылка на закладку списка Технологий в Новапедии\n    Луны: добавлена индикация Луны в имени планеты иконкой ☽ U+263D &#9789;\n        - В списке планет в навбаре\n        - На странице \"Обзор планеты\"\n        - На странице \"Империя\"\n    Меню: рамка меню изменена на тонкую\n    Вёрстка\n        - Сделан более универсальный патч, который снимет потенциальные проблемы с исчезающим скроллом в Сркщьу 65 на любых экранах\n        - Соответственно - исправлена ошибка с пропаданием скролла в режиме Сканирования Вселенной на маленьких экранах в Сркщьу\n[~] Артефакты\n    Артефакты класса \"Крюк\" теперь не меняют количество обломков на орбите - т.е. Луна появляется без траты обломков\n[~] О сервере\n    В футере и на странице информации о сервере к версии движка теперь добавляется номер патча БД\n[~] Бенчмарк\n    Добавлена дата и время начала операции, для которой проводится бенчмарк\n[~] Изображения\n    Пережаты изображения без потерь качества\n[~] Звуки\n    Немного пережаты звуки\n[~] Статистика/Обновление\n    Теперь при сбое всех стандартных способов уведомления о занятости сервера выдаётся стандартная простая заглушка\n\n[-] Поиск\n    В поиске отключён поиск ботов\n[-] Обучение\n    Обучение по умолчанию отключено\n\n...и множество других мелких правок. Посмотреть изменения в коде и мелкие правки можно в файле docs/changelog_dev.txt\n\n\n\nProject \"SuperNova.WS\" Release 42 \"8th anniversary\"\n~~\n[#] core_festival 5a5.1 - Модуль \"Фестивали\"\n    (!) Хайспот/Ивент \"Чёрная Луна\"\n        Во вселенной появляются специальные Чёрные Луны, с которых при нахождении флота в Удержании капает ТМ игрокам\n        Админка:\n            - Команда на вычисление лун\n            - Вывод вычисленных лун\n            - Подключено и оттестировано создание лун\n            - Добавлена индикация активных лун\n            - Список флотов на каждой луне - включая владельца, доход, размер флота\n            - Список доходов каждого игрока, держащего флот в удержании\n            - Добавлена индикация поля обломков вокруг каждой из лун\n            - Добавлена сортировка пользователей по количеству ТМ в тик по убыванию\n            - Изменено цветовое кодирование процента получения ТМ:\n                - Красный - больше 100%;\n                - Синий - ровно 100%;\n                - Зеленый - >= 50%;\n                - Жёлтый - >= 10%;\n                - Оранжевый - >= 10%.\n            - Изменено цветовое кодирование имени игрока в таблице ЧЛ:\n                - Красный - игрок стоит ниже своего брэкета! Такого быть не должно - ошибка!\n                - Без цвета/Белый - игрок стоит в своём брэкете;\n                - Зеленый - на один брэкет выше;\n                - Желтый - на два брэкета выше;\n                - Оранжевый - на три или более брэкета выше.\n        Теперь тики по необходимости обновляют онлайн игроков-ботов - что бы те не превратились в и-шки\n\n    (!) Highspot \"8 Марта\":\n        При включённом Ивенте в навбаре в конце основного блока кнопок появляется новая кнопка, ведущая на страницу Ивента\n        На странице Ивента игрок с указанным в профиле мужским полом может подарить метаматерию игроку-женщине\n        Подарок будет снят со счёта игрока-мужчины и зачислен на счёт игрока-женщине\n        При зачислении игроку-женщине будет начислено на 25% больше ММ\n        Минимальное количество снимаемой метаматерии - 20.000 ММ\n        Игрок-мужчина может увидеть кому он делал подарки, сколько было списано со счёта и сколько было начислено\n        Добавлена информация об общей сумме сделанных подарков на мужской странице\n        Добавлена информация на женской странице о сумме и количестве полученной ММ\n        На мужской странице добавлена индикация позиции в конкурсе Кавалеров\n        На женской странице добавлена индикация позиции в конкурсе \"Королева Весны\"\n        Добавлена админская страница для автоматизированного награждения победителей конкурса\n\n    (!) Хайспот/Приз за ММ\n        Из хайспота SuperBorn выделен код, отвечающий за индикацию Памятного Знака за полученную ММ\n        На странице \"Император\" добавлен раздел \"Приз за ММ\":\n            - Индикация количества полученной ММ и названию соответствующей Медали;\n            - Индикация места в общей таблице призёров;\n            - Переверстан вывод данных в виде таблице;\n            - Добавлен HTML-якорь \"mm_race\";\n            - При нулевом количестве ММ у игрока в рамках Конкурса - на странице Императора показывается заглушка\n        Добавлена админка для просмотра общей таблицы призёров\n\n    (~) Админка:\n        Фестивали сортируются от новых к старым\n        Исправлена ошибка замены пункта меню \"Тёмная Материя\" на пункт меню \"Фестивали\" - теперь новый пункт добавляется сверху\n    (~) Прочее:\n        Хайспот/Паззл: Иконки хайспота в навбаре перенесены из отдельного блока в общий блок\n        День Космонавтики: Добавлены смайлы к Дню Космонавтики\n        Фестивали: Фестиваль считается активным только если имеет хоть один активный хайспот\n\n\n[#] admin_stat 0a0.2 - Модуль \"Массовые операции\"\n    Из админки перенеыены отдельно стоящие отчёты \"Онлайн игроков\", \"Регистрации игроков\" и \"Игроки-боты\"\n    Добавлены файлы локализации\n\n[#] player_award 0c9 - Модуль \"Награды игроков\"\n    (+) ДР СН\n        Добавлены памятный знаки для ивента \"День Рождения СН\" 2016 и 2017 годов\n    (+) Кавалер-2017\n        Памятный знак за подарок Даме в ходе ивента \"8 марта 2017 года\"\n        Медаль \"Лучший Кавалер-2017\"\n        Добавлена медаль \"Королева Весны-2017\"\n    (~) Картинки\n        Еще немного уменьшены размеры картинок\n    (@) Код\n        Добавлены методы для взаимодействия с другими модулями\n        Почищен код\n\n[!] Обучение\n    Добавлен блок \"Обучение\" в навбар:\n        - Заголовок с кнопкой \"Закрыть\" (действует только на текущей странице)\n        - Основной текстовой блок с картинкой Советника\n        - Блок с кнопками\n        - Футер\n    В \"Настройки\" добавлены опции:\n        - Новые опции располагаются на вкладке \"Интерфейс\", подвкладка \"Обучение\"\n        - Опция отключения Обучения\n        - Опция показа Обучения во всплывающем окне (popup)\n    Работающие кнопки \"Вперед\", \"Назад\", \"Закончить\"\n    Подгрузка новых текстов через AJAX\n    Теперь обучение можно открыть в окне\n        - По умолчанию окно открывается в правом нижнем углу страницы\n        - Окно прилеплено и не меняет местоположение при скролле\n        - Окно можно перемещать по странице, таская его за заголовок\n        - Статус обучения (на странице в навбаре или в окне), а так же положение окна на экране сохраняется в куках отдельно на каждом устройстве\n    Теперь можно задать ID первого текста из таблицы `text` в обучаловке - опция 'tutorial_first_item' в таблице `config`\n    Теперь при сбросе обучения так же сбрасывается статус \"обучение завершено\"\n    Текст и заголовок Обучения теперь могут использовать bbCode\n    В настояшее время \"Обучение\" включает только инструкцию по пользованию \"Обучением\"\n[!] Навбар\n    Новый ресурсбар (количество ресурсов на планете/в Альянсе) - теперь встроенный в навбар\n    Выводится количество ресурсов на планете, а для электроэнергии - баланс (т.е. производство минус потребление)\n    При наведении курсора мыши на ячейку с ресурсом (металл, кристалл, дейтерий) появляется попап, в котором указывается:\n        - Полное название ресурса;\n        - Количество ресурсов на складе;\n        - Размер склада;\n        - Заполнение склада в процентах\n    При открытом попапе клик на ячейку закроет попап\n    Так же попап открывается при клике/тапе на ячейке - для мобильных устройств\n    Планетбар переверстан с использованием flex и поддерживает адаптивный дизайн\n    Верстка: металл+кристалл и дейтерий+энергия/тм объединены в блоки - так красивее работает адаптивный дизайн\n    CSS и JS из темплейта вынесены в соответствующие файлы\n    В новый планетбар добавлена поддержка вертикального расположения в навбаре\n    Добавлены разделители тысяч в попапе нового планетбара\n    Включен стандартный попап для энергии в новом планетбаре\n    Вместо букв-маркеров ресурсов (М, К, Д итд) используются иконки\n    Новый планетбар используется по умолчанию\n    В \"Настройки\" добавлена опция включения старого планетбара\n    В настройках добавлена опция показа ёмкости складов в ресурсбаре. Опция действует как в новом, так и в старом ресурсбарах\n    Полностью переверстан новый ресурсбар\n    Улучшена поддержка IE в старом/новом ресурсбарах\n    В старом ресурсбаре теперь так же работают всплывающие окна с подсказками\n    Теперь в ресурсбаре Альянсов не показывается ненужная строка ёмкости хранилищ\n    В ресурсбаре улучшена индикация заполнения складов:\n        - Если количество ресурсов больше объема склада (> 100%) - размер склада будет выведен красным\n        - Если ресурсов <= 100%, но > 90% - оранжевым\n        - <= 90%, но > 75% - желтым\n        - <= 75%, но > 50% - синим\n        - <= 50% - зеленым\n        - И, наконец, если склада нет и количество ресурсов на планете равно нулю - размер склада будет выведен белым\n    Ресурсбар:\n        - Цветовое кодирование к количеству производимой энергии в ресурсбаре\n        - Цветовое кодирование к попапам в ресурсбаре\n        - Отдельный попап для энергии, выводящий потребление\n[!] Вселенная\n    Добавлен режим сканирования Вселенной:\n        - Вход в режим осуществляется нажатием кнопки \"Включить режима сканирования\"\n        - Выход - нажатием кнопки \"Выйти из режима сканирования\"\n        - Вид Вселенной переключается на минималистический\n        - Убираются лишние вертикальные отступы в ячейках\n        - В режиме сканирования отключаются: меню, навбар, подсказки\n        - Так же в режиме сканирования отключаются рамки вокруг таблиц, если они были включены в Настройках\n    В режиме сканирования теперь работают попапы на лунах/планетах/итд\n[!] Квесты\n    Полностью переделан интерфейс квестов\n    Новый статус квеста - \"Начат\":\n        - Квест отмечается как \"Начат\", если был построен хоть один юнит из условий квеста;\n        - Отметка выставляется в момент завершения постройки первого юнита\n        - Уже построенные юниты не учитываются - нужно построить хотя бы один юнит, что бы сменить статус квеста\n        - Количество начатых квестов отображаетя в навбаре на кнопке квестов желтым цветом\n    На страницу просмотра квестов добавлен фильтр квестов по статусу:\n        - Состояние фильтра запоминается между визитами на страницу квестов\n    В списке квестов к фильтру добавлен вариант \"Все, кроме выполненных\"\n[!] bbCode\n    Переписана работа с bbCode\n    Класс BBCodeParser переделан в динамический\n    Работа со списком смайликов и bbCode вынесены в новый класс Design\n    Из класса classSupernova убран теперь неиспользуемый массив $design\n    Базовые смайлики и bbCode вынесены в vars.php\n    Новый bbCode - [urlw=URL]text[/urlw] - разворачивается в активную ссылку, которая переодит по URL в текущем окне\n    Новый префикс \"faq://link.html\" - разворачивается в активную ссылку на \"link.html\" в ЧаВо (FAQ) - если ЧаВо сконфигурировано в настройках сервера\n    Активная ссылка на боевой отчёт теперь открывается в текущем окне\n    Для bbCode [c] появилась полная версия [color]\n    Теперь URL боевого отчёта с текущего сервера автоматически преобразуется в активную ссылку на боевой отчёт\n    Теперь ссылки bbCode, открывающиеся в новом окне, подчёркиваются двойной линией\n[!] Админка\n    Небольшой тест функиональности phpBB Template Engine в \"Утилитах\"\n    Добавлена страница с балансом материи\n    Рефакторинг страницы просмотра информации об игроке\n    Переверстаны страницы:\n        - Администрирование чата\n        - Утилиты\n    Пункт меню \"Тёмная материя\" стал заголовком\n    Теперь при неудачной попытке начислить ТМ/ММ назад в форму возвращается причина начисления\n    Добавлена опция настройки логотипа сервера в меню - опция 'menu_server_logo' в таблице `config`:\n        - Пустое значение - использовать логотип по умолчанию;\n        - В остальных случаях строка трактуется, как относительный путь к картинке (от корня игры), например - 'design/images/supernova.png'\n        - Опция 'menu_server_logo_disabled' позволяет отключить логотип в меню\n    Добавлена опция отключения названия сервера в меню - опция 'menu_server_name_disabled' в таблице `config`:\n    Добавлена опция отключения даты запуска в меню - опция 'menu_launch_date_disabled' в таблице `config`:\n    Изменение ММ:\n        - Полностью переписано начисление ММ\n        - Теперь можно изменять ММ так же по ID или имени игрока\n        - Теперь перед изменением ММ можно посмотреть, что будет изменено - и лишь потом подтвердить изменения\n    Админка/Изменение ТМ: Переписано изменение ТМ\n[!] Лицензия\n    Добавлен файл docs/credits.txt для списка используемых материалов и соответствующих копирайтов\n[!] Вёрстка\n    Перевёрстан базовый темплейт:\n        - Переделан на DIV-ах\n        - Меню, навбар и дополнения (новости, заметки итд) подключаются теперь в _global_header\n[!] Темплейты\n    Класс PTLTag:\n        - Замена элементов темплейта их значением\n        - Формат: {<prefix>{<text>|[elementID]|...}>\n            - <prefix> используется для идентификации. Должен быть отрезан перед передачей в PTLTag\n            - <text> - текст\n            - [elementID] - название элемента в квадратных скобках. Поддерживаются следующие элементы:\n                - Корневые значения {VAR} -> [VAR]\n                - Переменные темплейта из $DEFINE - {$VAR} -> [$VAR]\n                - Блоковые переменные на текущем уровне - {block.VAR] -> [block.VAR]\n            - Количество и комбинации текста и названий элементов могут быть любыми:\n                - На примере тэга {I_xxx}: {I_unit_[unit.ID]_red_[UNIT_SIZE]}\n            - Сейчас поддерживается в тэгах {R_xxx} и {I_xxx} - см. примеры ниже\n    Новый тег косвенной адресации {R_[XXX]}:\n        - Позволяет в рантайме выводить значение из элемента, чьё имя генерируется во время исполнения темплейта\n        - Пример:\n            - Пусть у нас в темплейте есть тэг {R_[RENDER]}\n            - Пусть во время исполнения корневой элемент 'VAR' равен 'VALUE';\n            - Пусть во время исполнения корневой элемент 'RENDER' равен '{VAR}'\n            - Тогда во время компиляции сгенерируется исполнимый код, который во время исполнения темплейта проделает следующее:\n                - Возьмет значение переменной 'RENDER', т.е. '{VAR}'\n                - Динамически скомпилирует код для вывода переменной '{VAR}'\n                - Исполнит его и выведет значение элемента 'VAR', т.е. в нашем конкретном случае - выведет 'VALUE'\n        - Тэг надо использовать с осторожностью, избегай вывода переменных, полученных напрямую от пользователя\n        - Содержимое элемента может быть любым тэгом из поддерживаемых темплейтом: {$VAR}, {block.VAR}, {D_xxx}, {I_xxx} итд\n    Тэг изображения {I_xxx} теперь работает через класс PTLTag\n        - Пример:\n            - Пусть в skin.ini есть записи\n                s_black_moon = \"planeten/small/s_black_moon.jpg\"\n                black_moon = \"planeten/black_moon.jpg\"\n            - Пусть в темплейте есть директива <!-- DEFINE $BLACK = 'black_moon' -->\n            - Тогда тэг {I_[$BLACK]} выведет '<HTTP путь к текущему скину>planeten/black_moon.jpg'\n            - А вот тэг {I_s_[$BLACK]} выведет '<HTTP путь к текущему скину>planeten/small/s_black_moon.jpg'\n        - В квадратных скобках можно использовать любые имена элментов, поддерживаемых PTLTag\n\n[+] Альянсы\n    Включена отключенная ранее опция рассылки сообщений членам Альянса - кнопка \"Послать сообщения всему Альянсу\" в блоке \"Управлеение Альянсом\"\n    Переверстаны некоторые страницы\n    Добавлены более понятные сообщения об ошибках при попытках сменить имя или тэг Альянса на уже существующие в игре\n[+] Интерфейс\n    Теперь так же работает перетаскивание элементов тапом на тач-скринах\n    В частности - теперь можно поменять положение окна Советника на мобильных устройствах\n[+] Обзор планеты\n    В список ресурсов на планете добавились всплывающие окна с подсказками\n\n[~] Новости\n    На обзоре страницы теперь можно ограничивать показываемые новости так же по времени публикации:\n        - Настройка game_news_overview_show в таблице config устанавливает давность новости;\n        - Задаётся в секундах. По умолчанию - новости давнее 2 недель (1209600 секунд) не показываются;\n        - При установке в 0 показывает все новости;\n    Переделана кнопка \"Закрыть\" в блоке новостей - вынесены стили в CSS, а обработчик нажатия - в JS\n[~] Флоты/Менеджер флотов\n    Теперь время работы менеджера флотов вычисляется непосредственно с начала его работы, а маскимальное время работы уменьшено до 3 секунд (регулируется константой GAME_FLEET_HANDLER_MAX_TIME)\n[~] Флоты/САБ\n    Переверстана страница подбора игроков в САБ\n[~] Вселенная/Переименование\n    Переверстана страница переименования Галактик/систем\n[~] UBE/Боевой отчёт\n    Добавлен BBCode для вставки в чат ссылки на бой\n\n\n\nProject \"SuperNova.WS\" Release 41 \"Festival batch fleet navbar\"\n~~\n[#] core_festival 3a3.4 - Модуль \"Фестивали\"\n    Фестиваль - это тематическое объединение нескольких хайспотов, проходящих в одно и то же время\n    Хайспот - обощающее название для акций и ивентов. Реализованы следующие хайспоты в составе базового модуля:\n        - Изменение скоростей добычи, постройки, полёта флотов;\n        - Временное добавление сезонных смайликов в чат:\n            - Новогодний набор;\n            - Набор на Хэллоуин;\n        - Изменение стоимости покупки ММ (для реализации скидок/бонусов на ММ);\n        - Изменение уровней юнитов;\n        - Мораторий на агрессивные миссии флотов;\n    Хайспот \"Головоломка\" (Puzzle) - сбор предмета из компонент, которые находятся в Экспедициях:\n         - Добавлен Activity на просмотр паззла - актуально, когда паззл уже собран, но на итог надо посмотреть\n    Хайспот \"Сбор ништяков\" (Gather) - появляющиеся на экране ништяки, на которые должны кликать игроки:\n        - Виды:\n            - Новый Год - поиск Ёлочки и её наряжание;\n            - День Рождения СН - поиск исходных кодов СН и компиляция движка;\n        - При генерации ништяка выбирается картинка из списка доступных и запоминается. Далее она остается персистентной между рефрешами страницы и записывается в лог сбора ништяков;\n        - Для ненайденных ништяков показывается плейсхолдер без названия и описания\n        - При наведении курсора ништяки подсвечиваются;\n        - Поддержка минимального количества ништяков - для того, что бы в конце ивента каждый ништяк не был высокоуровневым;\n        - Добавлены комбинации ништяков - составные ништяки, которые для своего появления требуют найти несколько других типов ништяков;\n            - Аттрибут P_REQUIRE для ограничения юнитов/составных юнитов\n            - Вывод требований на странице со списком ништяков - название, картинка, сколько нужно, сколько открыто\n            - Название и картинка требований неоткрытых юнитов не выводится\n        - Поддержка min_time и max_time для ништяков - минимальная/максимальная дата/время, когда ништяк может появлятся на экране;\n        - Теперь в min_time и max_time можно использовать шаблоны функции date() - осторожно при межгодовых акциях!\n        - Хайспот автоматически определяет наличие больших картинок - директива HIGHSPOT_GATHER_ONLY_ICONS неактуальна\n    Все стили вынесены в отдельный CSS-файл в модуле\n    Дополнительная строка в навбаре специально для кнопок ивентов;\n    Добавлено действие Christmas Tree Burn\n\n[#] interface_batch_operations 1a1 - Модуль \"Массовые операции\"\n    Добавляет новый пункт меню \"Массовые операции\"\n    Массовый развоз ресурсов/передислокация кораблей с одной планеты на несколько\n    Массовый своз ресурсов/передислокация кораблей с одной планеты на одну\n    Массовая постройка кораблей на нескольких планетах одновременно\n    Доступ к \"Массовым операциям\" покупается за ТМ:\n        - Базовая стоимость - 50.000 ТМ за 30 дней;\n        - Возможность покупки фишки на 7, 14, 60 и 90 дней (два последних - со скидкой);\n        - Возможность досрочного продления фишки;\n    Полная локализация модуля\n    Транзакции теперь делаются per-planet, а не глобальные по всем планетам. Это должно существенно улучшить отзывчивость для остальных игроков\n\n[#] info_best_battles 1d2 - Модуль \"Лучшие бои\"\n    Добавляет в меню новый пункт \"Лучшие бои\", который открывает соответствующую страницу\n    Для каждого боя выводится:\n        - Порядковый номер в таблице;\n        - Дата и время боя;\n        - Общее количество обломков в пересчете на металл;\n        - Ссылка на просмотр соответствующего боевого отчёта;\n    На странице выводится 50 лучших боёв\n    Бои сравниваются по общему количеству обломков в пересчете на металл\n    В таблицу попадают только бои, которые произошли не ранее 2-х суток от текущей даты, т.е.:\n        - 2015-11-28 в 00:11:30 будут доступны бои, произошедшие до 2015-11-26 00:00:00 (не включая полуночь);\n        - Бои, происшедшие 2015-11-26 появятся в списке лучших боёв 2015-11-29 ровно в 00:00:00 (если, конечно, образовалось больше обломков, чем у худшего из лучших боёв);\n        - Это сделано специально, что бы дать всем заинтересованным сторонам собрать лом с орбиты;\n    Лучшие бои не удаляются из базы данных во время процедуры технического обслуживания\n    Добавлено уведомление о сроке появления боя в списке для тех, кому лень пролистать до конца страницы\n    Добавлена английская локализация\n\n\n[!] Флоты/Подбор флота\n    Переверстана страница подбора кораблей во флот:\n        - Уменьшена ширина списка кораблей для удобства мобильных пользователей;\n        - Миниатюра корабля:\n            - Вместо названия корабля и скорости полёта поставлена миниатюра корабля;\n            - Клик или тап на миниатюре переадресует на страницу о подробной информации корабля;\n            - Миниатюра корабля приведена к общему стандарту - название корабля в верхней строке и количество кораблей на орбите в нижней строке (вместо отдельной колонки);\n            - Так же на миниатюре корабля выводится: скорость полёта, расход топлива и ёмкость трюмов;\n            - Если корабль не может покинуть орбиту планеты/луны (Солнечный Спутнки, ТОП итд), то вместо характеристик выводится надпись \"Спутник\";\n        - В верхней части списка кораблей продублированы все кнопки. Теперь не обязательно листать до низа страницы, что бы быстро поднять все корабли или свезти ресурсы;\n        - Настройки сортировки перенесены в самое начало страницы - по аналогии с другими страницами;\n    Теперь при отсутствии свободных слотов для нового флота:\n        - Скрываются кнопки массового выбора кораблей, перехода на следующую страницу и своза ресурсов (поскольку слотов под своз всё равно нету);\n        - Скрывается мультиэлемент выбора кораблей;\n        - Миниатюры кораблей выводятся в большем размере - что бы легче читались характеристики.\n    Названия характерстик на миниатюрах кораблей заменены иконками\n    Название корабля теперь выделяется голубым цветом\n    Добавлены настройки (пункт меню \"Настройки\", вкладка \"Интерфейс\", подвкладка \"Флоты\"), на которых можно:\n        - Включить \"старый режим\" - без картинок и с выводом количества кораблей в отдельном столбце;\n        - Отключить показ каждой характеристики корабля отдельно: т.е. скорости, ёмкости трюмов, потреблении;\n    Выделены цветом названия кораблей (желтый) и количество на орбите (зеленый\n[!] Админка/Настройки\n    Полностью переверстана таблица настроек сервера - со вкладками и на div-ах!\n[!] Дизайн\n    Общий responsive бэкграуд Блица для всех скинов\n    Responsive бэкграунд при входе в игру в обоих режимах (СН/Блиц)\n    Опять сделан прозрачным основной фон навбара\n    Скин supernova-ivash: добавлены стили для TD/TH\n    Реформат:\n        - Страница партнерской программы;\n        - Страница управления Альянсом;\n[!] Навбар\n    Ресурсный навбар:\n        - Теперь ресурсный навбар может быть сделан вертикальным\n        - \"Настройки\", раздел \"Интерфейс\", вкладка \"Панель навигации\", опция \"Вертикальная панель ресурсов\"\n        - При этом ресурсный навбар \"прижимается\" сбоку от основного навбара - полезно игрокам с широкими мониторами\n    Добавлены поясняющие надписи к кнопкам\n    Немного переделан навбар - больше флексбоксов богу флексбоксов!\n    Поддержка добавочных кнопок вверху навбара\n\n\n[+] Заметки/Закладки\n    Изменения на основной странице Закладок:\n        - Заголовок и текст закладки выделяется цветом важности. Отдельный словесный маркер важности убран за ненадобностью;\n        - Название объекта в космосе, на который указывает закладка, отображается сокращённо - (П) для Планеты, (Л) для Луны и (О) для Поля обломков\n        - Статус \"прилепленной\" закладки показывается иконкой, а не надписью \"Прилеплена\";\n        - Дата в списке закладок перенесена в заголовок;\n        - Увеличена максимальная ширина таблицы с закладками - для обладателей широкоформатных мониторов;\n        - На маленьких экранах если заголовок не влазит по ширине - он будет разнесен на нужное количество строк;\n        - Диапазоны выбора какие закладки удалять в верхней и нижней части синхронизированы. Т.е. выбор диапазона в верхнем элементе приводит к изменению диапазона в нижнем элементе - и наоборот\n    Редактирование/создание закладки:\n        - При выборе важности закладки меняется цвет заголовка, текста и самого выбранного элемента;\n        - Если в закладке пустой текст и заголовок, но указана хотя бы одна координата (галактика, система или планета) - закладка будет сохранена;\n    Исправлена ошибка, делающая удаление Закладок невозможным при выборе некоторых диапазонов\n\n[-] Апгрейд\n    Апгрейд с очень старый версий двиэка (СН версии 36 и ниже) больше не поддерживается\n\n[~] Друзья\n    Теперь можно отправлять пустой запрос на дружбу (хотя кому и зачем это может понадобиться - непонятно)\n[~] МПР\n    Теперь можно атаковать ракетами свои же планеты. Таким образом можно избавится от излишних ракет или перехватчиков в шахтах, а так же уничтожать свои защитные сооружения\n[~] Флоты\n    Время возвращения флота из САБа теперь равно времени полёта на САБ, а не чистому времени полёта флота\n\n[@] Код\n    Папка с классами перемещена в корень движка - автолоадер изменен соответствующим образом\n    Переименованы файлы с классами для полной поддержки PSR-4 автолоадера\n    Изолирован код, обращающийся к таблице `fleets`\n    Теперь модули могут добавлять свои CSS-файлы и конструкции в заголовок\n    Унифицирован вызов MVC-хуков. Теперь их потенциально можно роутить в базовом варианте\n    Вьюшки с IN_ADMIN в модулях теперь корректно выдают страницу с обрамлением админки\n[@] Код/JS\n    Обновлена библиотеека Ion Sound до версии 3.0.7\n    В основном коде заменены deprecated функции jQuery:\n        - bind(), live() и delegate() на on();\n        - unbind() - на off();\n        - В этот раз - честно-честно!\n    В класс Math добавлены функции-аналоги PHP-шных intval() и floatval() и функции округлений\n        - Эти функции всегда возвращают только числовые значения, а NaN преобразуют в 0\n        - В некоторых местах parseFloat() и parseInt() заменены на новые функции;\n    sn_format_number():\n        - Функция оптимизирована и разбита на две части\n        - Первая - считает вид числа;\n        - Вторая - выдаёт соответствующий cssClass для расцветки;\n        - При сохранении обратной совместимости (deprecated по факту) теперь можно менять сразу класс элемента, без вставки <span /> в DOMик;\n        - Добавлена соотвествующая функция-враппер elementPrettyNumber().\n    Постройка:\n        - Все значения от PHP теперь пропускаются через новые функции;\n        - Оптимизирована работа разных кусков JS и улучшен код;\n        - Убрано обращение к document;\n    ...а так же всякие мелкие оптимизации.\n\n\n\nProject \"SuperNova.WS\" Release 40 \"RD auth planet universe que sn_timer\"\n~~\n[!] Не прошло и года, как я представляю вам новый релиз СуперНовы\n    В самом деле - не прошло и года. Чуть более 7 месяцев со времени 39-го релиза\n    Как и прошлый релиз, этот делается для фиксации \"статуса-кво\" - как реперная точка в разработке и для того, что бы системные администраторы серверов на этом движке могли бы использовать новейшие разработки в области СН-строения\n    И, кстати, игра претерпела небольшой \"SEO-ребрендинг\" - анализ запросов с Гугля и Яндекса показал, что игру в основном находят как \"СуперНову\", а не как \"СверхНовую\". Поэтому было решено официально сменить русскоязычное название движка на \"Проект 'СуперНова'\". Вива, Гугль!\n[!] Редизайн игры\n    Не секрет, что я уделяю больше времени содержательной части игры, а не её внешнему виду. В конце-концов - я всё-таки программист, а не дизайнер\n    Однако неуклонный рост количества пользователей мобильных устройств в игре и одновременно - увеличение доли широкоформатных мониторов у игроков буквально вынудили уделить время и дизайну\n    В настоящий момент один и тот же темплейт (расположение элементов игры на странице) используется как для игроков с мобильных устройств, так и для пользоваетелей ПК\n    К сожалению, создание отдельного темплейта исключительно для малоформатных устройств требует огромное количество времени, а так же сильно затрудняет дальнейшую разработку из-за необходимости поддержки и синхронизации изменений в двух темплейтах\n    Поэтому было принято решение дорабатывать текущий темплейт, добавляя в него различные фишки для поддержки как малых, так и больших разрешений - не забывая при этом про среднеформатные мониторы. В число таких фишек входят:\n        - Полная переработка CSS-стилей;\n        - \"Отзывчивый дизайн\" (Responsive Design, далее - RD);\n        - Встроенная система масштабирования интерфейса;\n    Подробнее об этих и других изменениях во внешнем виде игры можно прочесть ниже\n    С учётом сложности поставленной задачи (без существенных изменений адаптировать существующую вёрстку ко всей гамме разрешений мониторов с учетом особенностей зоопарка компьютерных и мобильных браузеров) результат получился очень хорошим\n    Так же была проделана определенная работа по улучшению внешнего вида интерфейса:\n        - Совершенно новые виды для некоторых страниц - например, \"Обзор Вселенной\" и \"Технологии\" - список этим не ограничивается;\n        - Полностью переверстаны с нуля некоторые другие страницы. При небольших изменениях во внешнем виде заметно улучшилось юзабилити. К таким страницам относятся, например, \"Новости\";\n        - Полностью переписана \"Очередь построек\";\n        - Добавление элементов декора в интерфейс - рамки, тени под кнопками, эффекты итд итп. Значительную часть декора можно отключить в настройках интерфейса;\n        - Унификация раскраски элементов;\n        - ...и многое, многое другое!\n[!] Изменения в игровой механике\n    Хотя акцент в релизе сделан на внешний вид, некоторые игровые механики так же были переделаны. Порой - переписаны полностью\n    К последним относится полностью новая система генерации планет - см.ниже подробное описание изменений\n    Так же изменения притерпели и некоторые другие механики. Опять же - прочитать об этом можно ниже\n[!] Изменения в движке\n    Последнее по списку, но не по значимости. \"Внутренности\" движка так же были серьёзно доработаны - как PHP-часть на стороне стороне, так и JS-часть на стороне клиента. Можно отметить следующие изменения:\n        - Полностью переделана система авторизации (два раза за отчётный период);\n        - Полностью переписана работа JS-таймеров;\n        - Существенно переделана работа с БД - добавлена потенциальная возможность подключения к отличным от mysql БД;\n        - ...и многое, многое другое!\n    Краткий список изменений см. ниже\n\n[#] info_best_battles 1b0\n    (!) Модуль \"Лучшие бои\"\n        Добавляет в меню новый пункт \"Лучшие бои\", который открывает соответствующую страницу\n        Для каждого боя выводится:\n            - Порядковый номер в таблице;\n            - Дата и время боя;\n            - Общее количество обломков в пересчете на металл;\n            - Ссылка на просмотр соответствующего боевого отчёта;\n        На странице выводится 50 лучших боёв\n        Бои сравниваются по общему количеству обломков в пересчете на металл\n        В таблицу попадают только бои, которые произошли не ранее 2-х суток от текущей даты, т.е.:\n            - 2015-11-28 в 00:11:30 будут доступны бои, произошедшие до 2015-11-26 00:00:00 (не включая полуночь);\n            - Бои, происшедшие 2015-11-26 появятся в списке лучших боёв 2015-11-29 ровно в 00:00:00 (если, конечно, образовалось больше обломков, чем у худшего из лучших боёв);\n            - Это сделано специально, что бы дать всем заинтересованным сторонам собрать лом с орбиты;\n        Лучшие бои не удаляются из базы данных во время процедуры технического обслуживания\n\n[#] skins/immi\n    [!] В игру добавлен скин immi\n\n[#] menu_customize 0d5\n    (!) Настройки/Меню\n        Все настройки меню вынесены в отдельный таб \"Настройка меню\"\n        Настройки элементов меню сгруппированы по категориям\n    (+) Поддержка редизайна меню\n        Опция \"Показать пункты меню в виде кнопок\" переименована в \"БОЛЬШИЕ КНОПКИ В МЕНЮ\"\n        Добавлена опция \"Белые надписи на кнопка меню (отключить цвета)\"\n    (%) Пустые пункты меню\n        Теперь пустые пункты меню не показываются в меню при включенном модуле настройки меню\n    (%) Кнопка Скрыть/Показать меню\n        Исправлены проблемы с позиционированием кнопки\n\n[#] player_premium 3c3\n    (+) Ивенты\n        Поддержка бонусных уровней Премиума по ивентам\n\n[!] Миссии/Колонизация\n    !!!ВНИМАНИЕ!!! Полностью переписана с нуля генерация планет! Теперь она очень сильно отличается от оГеймовской!\n    Размер планеты:\n        - Теоретические размеры планет составляют: минимальный - 30 секторов, максимальный - 330 (бывают только при наличии в системе планет-странников);\n        - На стандартных слотах (с 1-го по 15-й) кардинально увеличен средний размер планеты - как за счёт пересчёта минимального и максимального количества секторов, так и за счёт исправления архитектурной ошибки xnova;\n        - Диаметр планеты теперь вычисляется по более вразумительной формуле;\n    Типы ядер:\n        - Полностью переработаны типы ядер\n        - Пересчитана добыча всех типов ядер с учётом экономической эффективности и собранной статистики по типам планет у игроков (Нерф? Кто сказал - \"НЕРФ\"?!. Наоборот!)\n        - Переработаны и переименованы старые типы ядер:\n            - Во всех старых типах ядер увеличена добыча;\n            - \"Лёд\" переименован в \"Метановый лёд\" и улучшена добыча;\n            - \"Камень\", \"Силикат\" и \"Руда\" - улучшена добыча;\n            - \"Металл\" переименован в \"Оливин\". Его добыча находится на уровне старого типа \"Тяжелый металл\";\n            - Ядра типа \"Тяжелый металл\" мигрированы в тип \"Оливин\" (с улучшением добычи);\n        Добавлены 4 новых типа ядра:\n            - \"Водяной лёд\" - приблизительный аналог \"Камня\" или \"Руды\" для водорода;\n            - Три новых типа ядра - \"Водородный лёд\", \"Кристалл\" и \"Металл\" (новая версия) - с охренительной добычей соответствующих типов ресурсов;\n        Десять типов ядер четырех классов:\n            - \"Базовый\" класс - тип ядра \"Стандарт\";\n            - \"Продвинутый\" - \"Водяной лёд\", \"Камень\", \"Руда\";\n            - \"Редкий\" - \"Метановый лёд\", \"Силикат\", \"Оливин\";\n            - \"Раритетный\" - \"Водородный лёд\", \"Кристалл\", \"Металл\";\n        Смена типа ядра:\n            - Стоимость смены типа ядра теперь не зависит от текущего - можно недорого поэкспериментировать как минимум с продвинутым классом ядер, а то и с редкими;\n            - Стоимость смены типа ядра теперь зависит от типа планеты;\n            - В среднем - смена ядра планеты стала ГОРАЗДО выгоднее (в пересчете затраченной ТМ на полученный прирост производительности);\n        ПРИМЕРНО половина планет во Вселенной имеет улучшенный тип ядра (или водяной лёд, или камень, или руда), еще треть имеют тип ядра \"Стандарт\". Остаток приходится на долю остальных типов ядер\n    Температура:\n        - Изменены диапазоны, в пределах которых меняется максимальная температура планеты;\n        - Разница между максимальной и минимальной температурами теперь не фиксирована (40 градусов в xnova), а определяется случайным образом в определенном диапазоне;\n        - Диапазон разницы температур зависит от места планеты в системе;\n        - !!!ВНИМАНИЕ!!! У всех старых планет (колонизированных до установки патча) минимальная температура установлена равной максимальной температуре для того, что бы изменения патча не повлияли на добычу;\n    Добавлена поддержка \"планет-странников\":\n        - Планета-странник - ранее блуждавшая по галактике экзопланета, захваченная тяготением звезды;\n        - Планеты-странники находятся за 15-м слотом в системе (если в настройках игры включен размер системы более 15 планет);\n        - Хотя планета-странник может быть достаточно тёплой, ожидать околозвёздных температур в сотни градусов Цельсия от неё не стоит. А вот получить промороженную насквозь планету - вполне реально;\n        - Планеты-странники имеют огромный разброс по всем параметрам - соответственно и типов таких планет несчётнок множество;\n        - Получить граничные значения размера планеты можно только на планете-страннике;\n        - По умолчанию размер системы увеличен до 16 планет (ПРОВЕРЬТЕ ВАШИ ЗАМЕТКИ/ЗАКЛАДКИ НА ЭКСПЕДИЦИЮ!);\n    Переработано распределение планет по системе:\n        - Естественно, средняя температура планеты падает на расстоянии от звезды (т.е. по мере увеличения номера слота);\n        - Теперь бесполезно искать планеты с ледяным ядром возле звезды или планеты с металлическим ядром на внешних границах системы;\n        - Кристаллические ядра встречаются практически по всей системе;\n        - Крупные планеты группируются в середине системы. Соответственно - на внешней и внутренней границах системы планеты более мелкие;\n        - Как и с размером планет - температурные диапазоны уменьшаются от центра системы к её границам;\n    Цветовое кодирование типов ядер - чем \"тревожнее\" цвет, тем реже этот тип ядра можно найти при колонизации:\n        - Белый - стандартное ядро\n        - Желтый - ядро \"Продвинутого\" класса\n        - Оранжевый - \"Редкий\" класс\n        - Красный - самый редкий, \"Раритетный\" класс;\n    Колонизация:\n        - Тип ядра, находимого в колонизации, ограничивается уровнем Астрокартографии:\n            - От 0 до 5 - могут быть найдены только планеты с типами ядер \"Стандарт\", \"Водяной лёд\", \"Камень\", \"Руда\"\n            - От 6 до 10 - так же могут быть найдены планеты с типами ядер \"Метановый лёд\", \"Силикат\", \"Оливин\n            - 11 и больше - так же могут быть найдены планеты с типами ядер \"Водородный лёд\", \"Кристалл\", \"Металл\"\n        - Это сделано для облегчения игры для новичков и уравнивания стартовых условий\n        - Например, если первая же найденная колония имеет тип ядра \"Водородный лёд\" - новый игрок получает сильное пенальти на развитие планеты (планеты \"Раритетного\" класса очень сложно застраивать сам-один и для нормальной застройки требуется транспорт других типов ресурсов с остальных планет)\n\n[!] Тёмная Материя\n    Теперь не нужно вручную конвертировать ММ в ТМ - конвертация осуществляется автоматически при нехватке ТМ. Это упростит обращение с ММ для новых игроков\n    Переработан интерфейс покупки Метаматерии с учётом добавления в игру автоконвертации\n    Убран ненужный блок с курсами и скидками: цена теперь всегда выводится в выбранной валюте, а скидки за оптовые покупки прописаны прямо на кнопках пакетов\n\n[!] Навигационная панель (Навбар)\n    Добавлена кнопка \"Планета\":\n        - Клик на кнопке откроет страницу \"Планета\" для текущей планеты;\n        - В правом нижнем углу кнопки показывается количество колоний у игрока и максимально возможное количество колоний;\n        - Если на планете есть очередь зданий она выводится на кнопке планеты аналогично Исследованиям и Ангару\n    Добавлена кнопка \"Верфь\":\n        - Клик на кнопке откроет страницу \"Верфь\";\n        - При наличии юнитов в очереди Верфи на кнопке будет отображено:\n            - Количество оставшихся в очереди слотов\n            - Общее время до окончания постройки всех юнитов в очереди\n            - Название юнита в текущем слоте очереди (т.е. корабль, который сейчас строится)\n            - Количество оставшихся юнитов в текущем слоте\n            - Время, оставшееся до окончания постройки текущего юнита в текущем слоте\n    Отключение кнопок:\n        - Добавлена возможность отключения следующих кнопок:\n            - \"Исследования\"\n            - \"Планета\"\n            - \"Верфь\"\n            - \"Флоты в полёте\"\n            - \"Экспедиции\"\n            - \"Квесты\"\n            - \"Метаматерия\" (при наличии ММ её количество показывается так же на кнопке \"Тёмная Материя\")\n        - Отключение кнопок происходит в пункте меню \"Настройки\", вкладка \"Интерфейс\", раздел \"Навигационная панель\"\n    Кнопка \"Сообщения\":\n        - Счетчики сообщений теперь располагаются не в линию, а по углам иконки сообщений:\n            - Левый верхний - личные сообщения;\n            - Правый верхний - сообщения от Альянса;\n            - Левый нижний - сообщения от Администрации;\n            - Правый нижний - общее количество сообщений.\n        - Таким образом на мобильных устройствах будет гораздо проще выбрать нужный тип сообщений\n        - Если в почтовом ящике есть сообщения - общее количество соощений в навбаре мягко мерцает\n    По умолчанию кнопка \"Исследования\" теперь имеет стандартную ширину. Вернуть старый вид можно включив в \"Настойках\" опцию \"Широкая кнопка исследований (старый вид)\"\n    Имена планет в дропдауне переключения приведены к стандартному виду [координаты] (тип) Имя\n    \"Локальное время\" переименовано во \"Время у игрока\", \"Серверное время\" - во \"Время на сервере\"\n    Добавлены прогресс-бары к плашке исследований\n    Убрана горизонтальная зеленая линия над общим количеством ТМ в случае, если на аккаунте нет ММ и/или ММ не подключена в игре\n    Навбар переверстан под flex-box - теперь корректно переносятся кнопки на маленьких экранах\n    Все инлайн-стили вынесены в _template.css\n[!] Меню\n    Редизайн пунктов меню:\n        - Теперь в стандартном режиме пункты меню выглядят, как кнопки. Однако при этом являются ссылками - т.е. позволяют открывать страницы в новом окне\n        - Теперь при наведении курсора на пункт меню он подсвечивается\n    Настройка меню:\n        - Опции меню вынесены в отдельный таб \"Настройка меню\"\n        - Настройка \"Показывать пункты меню в виде кнопок\" заменён на \"БОЛЬШИЕ КНОПКИ В МЕНЮ\" и теперь просто увеличивает размер пунктов меню - удобно для мобильных устройств\n        - Новая опция в \"Настройкх\": \"Белые надписи на пунктах меню (отключить раскраску меню)\" убирает раскраску пунктов меню, оставляя выделенными только категории\n        - Новая опция в \"Настройкх\": \"Низкие пункты меню\", которая возвращает меню старый вид меню\n    Немного переупорядочены пункты меню так, что бы уменьшить количество категорий и не было неактивных заголовков в категориях\n    Поддержка иконок меню, расположенных в любом месте движка\n    Ускорена работа меню в приложениях\n[!] Новости\n    Редизайн блока новостей\n    Если новость содержит больше 1 параграфа (строки, разделенные двумя CRLF подряд), то остаток новости скрывается под кнопкой \"Показать текст новости\"\n    Кнопка закрытия списка новостей теперь машстабируется вместе с интерфейсом\n    Опросы:\n        - Добавлена индикация общего количества проголосовавших\n        - Результаты опроса:\n            - Бар, показывающий процент проголосовавших, выводится под самим ответом\n            - К каждому варианту ответа добавилась индикация процента проголосовавших\n    Кнопки действия над новостью (для Администрации) перенесены из отдельных колонок под саму новость\n\n[!] Страница \"Планета\"\n    Для каждой очереди добавлена дата и время её завершения\n    Добавлено подтверждение на покупку сектора\n    Добавлены прогресс-бары к общей информации об очередях\n    Добавлены координаты и название планеты в виде заголовка страницы\n    Убрано уведомление \"У вас Х новых сообщений\"\n    Часть функционала \"Управления планетой\" вынесена на основной экран \"Планеты\":\n      - Переименование\n      - Смена типа ядра\n    Ссылка \"Управление\" переделана в кнопку\n    В расчёте миниатюр планет и иконок на них транзакции теперь делаются per-planet, а не глобально по всем планетам. Это должно существенно улучшить отзывчивость для остальных игроков\n[!] Страница \"Обзор Вселенной\"\n    Общие изменения:\n        - Изменилась цветовая кодировка обломков: добавились новые градации и изменились границы переключения цветов на более интуитивные:\n            - Без фона/рамочки - менее 1.000 единиц обломков;\n            - Зеленый фон/рамочка - не менее 1.000 и не более 1.000.000 единиц;\n            - Желтый - не менее 1.000.000 и не более 1.000.000.000 единиц;\n            - Оранжевый - не менее 1.000.000.000 и не более 1.000.000.000.000 единиц;\n            - Красный - не менее 1.000.000.000.000 единиц;\n        - Добавлена кнопка ракетной атаки в планетарные попапы\n        - При выборе \"Ракетной атаки\" страница скроллируется на форму запуска ракет. Ей добавлена толстая красная рамка, что бы визуально выделить среди остальных элментов;\n        - В \"Настройках\" на вкладке \"Интерфейс\" в разделе \"Вселенная\" добавлена возможность отключить кнопку \"Послать колонизатор для основания колонии на позиции номер Х\"\n        - После анализа востребованности фишек, убраны быстрые действия \"Просмотреть место игрока в статитстике\" и \"Добавить игрока в друзья\". Действия по-прежнему доступны в попапе игрока, однако быстрые кнопки использовались настолько мало, что было принято решение убрать эти опции;\n        - При активации AJAX-действия (отправка ракеты, отправка шпионов итд) в центре экрана пишется результат совершения действия\n    Новый вид страницы:\n        - Полный редизайн страницы\n        - Изменений настолько много, что нет смысла их тут описывать все - проще посмотреть самому\n        - Те, кому не нравится новый вид - могут включить старый вид в \"Настройках\", вкладка \"Интерфейс\", галочка 'Использовать старый вид \"Обзора Вселенной\"'\n        - Выделение поля обломков цветной рамкой в зависимости от количества ресурсов\n    Изменения в старом виде Вселенной:\n        - Быстрые действия на планету/аккаунт сделаны кнопками для облегчения судьбы наших мобильных друзей;\n        - Переработаны попапы игрока и Альянса аналогично кнопкам миссий\n        - Значительно уменьшен размер генерируемой страницы Вселенной:\n            - С иконок ракетной атаки убран код onclick и перенесен в jQuery-обработчики\n            - Кнопки миссий в планетарном попапе переделаны на button_pseudo, a onclick-код вынесен в jQuery-обработчики\n[!] Очередь построек\n    Переделана Очередь построек\n    В очередь постройки добавились прогресс-бары:\n        - Прогресс-бары можно отключить - пункт меню \"Настройки\", вкладка \"Интерфейс\", опция \"Отключить прогресс-бары\"\n        - Прогресс-бар постройки текущего юнита в текущем слоте очереди:\n            - Цвет - зеленый\n            - Выводится под таймером обратного отсчёта постройки текущего юнита\n            - Учитывается ПОЛНОЕ время постройки юнита - т.е. при перезагрузке страницы время продолжит идти\n            - Показывает процент завершения постройки текущего юнита - т.е. увеличивается со временем\n            - Виден во всех очередях постройки на первом слоте в очереди и в навбаре в плашке верфи\n        - Прогресс-бар постройки всех юнитов в текущем слоте:\n            - Цвет - синий\n            - Выводится под количеством юнитов в текущем слоте (для зданий, очевидно, выводится всегда - здания строятся поштучно)\n            - Показывает оставшееся количество юнитов к постройке в текущем слоте - т.е. уменьшается со временем\n            - Виден во всех очередях постройки на всех слотах\n        - Общий прогресс-бар очереди:\n            - Цвет - болотный;\n            - Выводится под общим таймером обратного отсчёта всей очереди\n            - Учитывается ОСТАВШЕЕСЯ время всех слотов в очереди - т.е. при перезагрузке страницы отсчёт стартует с начала\n            - Показывает поставшееся время до завершения всей очереди - т.е. уменьшается со временем\n            - Виден на странице постройки и в навбаре в плашке верфи\n    Теперь время до постройки одного юнита в первом слоте пишется на самом юните\n    \"Общее время\" теперь индицируется сверху\n    Добавлены эффекты анимации к списку юнитов в очереди:\n        - При начале строительства нового юнита таймер на миниатюре мигает один раз;\n        - При окончании строительства всех юнитов в слоте - слот исчезает;\n        - Отключить анимацию эффектов в игре можно в пункте меню \"Настройки\", вкладка \"Интерфейс\", галочка \"Отключить эффекты анимации в игре\"\n    Список юнитов в очереди переверстан на дивах с использованием flex-box\n    Оптимизирована работа таймера очередей, так что новые прогресс-бары не должны сказаться на быстродействии\n    Теперь не рендерится очередь постройки, если контейнер для неё невидим - например, в навбаре\n    На общей панели постройки количество юнитов/уровень в текущем слоте отображается на новой строке\n    Оптимизирована выдача HTML-кода - меньше использование свойства style\n    Добавлена поддержка нового кода sn_timer\n[!] Страница построек/исследований\n    Автоконвертация ресурсов:\n        - Постройка зданий/исследование технологий:\n            - На странице постройки зданий и исследования технологий добавлена возможность постройки с автоматической конвертацией ресурсов (далее просто - \"автоконвертация\")\n            - Автоконвертация доступна если для постройки/исследовния не хватает какого-то конкретного типа ресурсов, однако есть излишек других ресурсов, которые можно сконвертировать на ЧР в недостающий ресурс\n            - Конвертируются только планетарные ресурсы - металл, кристалл и дейтерий. ТМ автоматически не конвертируется\n            - Сразу после конвертации недостающих ресурсов постройка/исследование ставится в очередь\n            - Для предотвращения случайных срабатываний при нажатии на кнопку автоконвертации выскакивает дополнительное окно с подтверждением\n            - Автоконвертация ресурсов - платная. Стоимость операции составляет утроенную цену одной конвертации на ЧР или 3.000 ТМ, если ручной обмен на ЧР бесплатен\n        - Верфь/Оборона\n            - На странице постройки кораблей и обороны добавлена возможность постройки с автоконвертацией\n            - В строке максимального количества через слэш показывается максимально доступное количество юнитов при автоконвертации\n            - Для включения режима автоконвертации необходимо отметить галочку \"Автоконвертация\". При этом все элементы страницы работают так же, как и раньше, однако максимальное количество юнитов к постройке становится равно максимально доступному количеству юнитов с учётом автоконвертации\n            - Автоконвертация щадяща: даже если при включенной галочке автоконвертации поставить на постройку не больше максимального количества юнитов, доступных без автоконвертации - ТМ снята не будет и конвертация ресурсов производится не будет\n            - Автоконвертация переводит ресурсы исходя из заказанного количества юнитов - даже если в очередь может стать меньше. Это сделано специально для того, что бы можно было однократно воспользоваться автоконвертацией, а затем просто пополнять очередь новыми юнитами - с уже сконвертированных в правильном отношении ресурсов\n        - Отключить кнопку/чекбокс автоконвертации можно в \"Настройках\", вкладка \"Интерфейс\", чекбокс \"Скрыть кнопку автоконвертации\"\n    Сортировка списка юнитов:\n        - На страницы постройки (исследования, здания, верфь, оборона) добавлена возможность сортировки\n        - Для каждой из вышеперечисленных страниц настройки сортировки сохраняются отдельно\n        - Добавлена поддержка стандартной сортировки в обратном порядке\n    Количество превьюшек теперь динамически меняется в зависимости от разрешения экрана и настроек масштабирования\n    На больших экранах (там, где вмещается не менее 6 превьюшек в ряд) таблица с дополнительной информацией располагается справа от описания юнитов\n    На больших экранах блоки покупки/уничтожения теперь выстраиваются в один ряд с информацией о стоимости постройки\n    В очереди ссылки \"Очистить очередь\", ссылка Артефакта (\"Наностроитель\"/\"Эвристический чип\") и \"Отменить последнее\" сделаны кнопками. Это выделит данные элементы, а так же уменьшит количество ложных нажатий\n    Добавлено диалоговое окно с подтверждением при полной очистке очереди\n    Добавлено диалоговое окно с подтверждением при использовании Артефакта\n    При постройке зданий/исследовании технологий теперь показывается уровень, который будет строится/исследоваться\n    При постройке зданий/исследовании технологий кнопка постройки/исследования теперь отключается, если операция невозможна\n    Постройка зданий:\n        - Добавлено подтверждение на покупку сектора\n        - Теперь если при постройке зданий нет свободных секторов - превьюшки затеняются и выводится соответствующее сообщение\n    Добавлено разрежение ячеек в подтаблицы цены и допинформации о юните\n    Добавлено выделение четных рядов другим фоном в подтаблице допинформации\n[!] Страница \"Флот на орбите\"\n    На шаге \"Выбрать корабли\" при отсутствии кораблей на планете на экране подбора флота доступна кнопка \"Свезти ресурсы\"\n    На шаге \"Выбор точки назначения\" при выборе планеты/заметки/боевого союза точка назначения флота меняется соответственно\n    Шаг \"Выбор задания\":\n        - Выбор миссий на странице выбора миссий сделан иконками-пиктограммами. Это улучшит юзабилити для игроков на мобильных устройствах\n        - Для миссии \"Колонизация\" введено цветовое кодирование количества колоний:\n            - Красный - количество колоний больше максимально возможного количества (например, вследствие исчерпания срока Премиум-аккаунта или окончания специальных акций)\n            - Оранжевый - количество колоний равно максимальному количеству. Нельзя колонизировать ни одной новой планеты;\n            - Желтый - количество колоний на 1 меньше максимального количества. Текущая миссия заблокирует возможность дальнейшего расширения;\n            - Зеленый - количество колоний на 2 и более единиц меньше максимального количества. Можно спокойно запускать текущую миссию.\n        - Корявый указатель направления миссии \"=>\" заменен на красивую стрелочку с подписью типа миссии\n    Теперь при отправке флота на шагах \"Выбор точки назначения\", \"Выбор задания\" и \"Флот отправлен\" выводятся:\n      - Состав флота в виде картинок с названиями и количеством;\n      - Точка отправления флота;\n      - (если доступно) Точка назначения флота;\n      - (если доступно) Время и срок прибытия в точку отправления и назначения.\n    САБ:\n        - При создании/присоединении к САБу теперь видна дополнительная информация о флоте, к которому присоединяется САБ: состав флота, откуда и куда направляется флот, а так же дата и оставшееся время до прибытия/возвращения\n        - Теперь при нажатии кнопки \"Боевой союз\" автоматически создается САБ и в него добавляется текущий игрок. При этом ему не отсылается лишнее (в данном случае) сообщение\n        - Название САБА теперь имеет вид \"САБ <ID>\", где <ID> - назначаемый игрой идентификатор\n[!] Страница \"Технологии\"\n    Редизайн страницы:\n        - Вкладки на каждый вид технологий;\n        - Блочная, а не табличная вёрстка;\n        - Гибкая вёрстка в зависимости от размера экрана;\n        - Добавлены картинки юнитов;\n        - Списки \"Требуется\" и \"Предоставляет\" теперь являются ссылками - можно сразу перейти на просмотр описания требуемых/предоставляемых технологий;\n    Новый дизайн страницы должен подойти любому игроку - начиная от мобильных пользователей и заканчивая владельцами широкоформатных экранов: он динамичен, масштабируем, информация организована удобнее и сама страница занимает меньше экранного пространства\n    Табличный дизайн можно включить в \"Настройках\" вкладка \"Интерфейс\" опция \"Страница Технологий в виде таблицы (старый вид)\"\n[!] Страница \"Чёрный рынок\"\n    Улучшен интерфейс заглавной страницы\n    Обмен ресурсов:\n        - Переверстана страница Обмена Ресурсов. Теперь ею должно быть удобнее пользоваться мобильным пользователям, а так же меньше вероятность совершить неправильную операцию\n        - Добавлено подтверждение при обмене ТМ на ресурсы\n[!] Страница \"Настройки\"\n    Переписана с нуля работа с настройками игрока\n    Большая часть полей с настройками вынесены в отдельную таблицу player_options - уменьшен размер записи в таблице `users`\n    Настройки теперь кэшируются write-through - уменьшено количество обращений к БД\n    Закрыта возможность сменить имя игрока с использованием зарещенных символов\n    Настройки, которые имеют только один чекбокс приведены к общему виду: сначала идёт чекбокс, затем - описание. Это улучшает читаемость и удобство таблицы, а в некоторых случаях - уменьшает вертикальный размер страницы \"Настроек\"\n    Вкладка \"Профиль\":\n        - Отделено имя аккаунта от игрового имени\n        - Добавлены кнопки \"Показать пароль\" ко всем парольным полям\n    На вкладке \"Интерфейс\" добавлена опция \"Отключить рамки у таблиц\", которая убирает рамки-изображения у элементов дизайна\n    Вкладка \"Интерфейс\" теперь содержит подвкладки \"Общие\", \"Настройки меню\" (бывашя вкладка), \"Панель навигации\" и \"Вселенная\"\n\n[!] Responsive Design (RD)\n    RD - это не какая-то конкретная фишка, а целый комплекс подходов направленных на поддержку различных разрешений и ориентаций экранов, разных настроек масштабирования в браузере и СН, а так же автомасштабирования страниц в некоторых мобильных браузерах на некоторых платформах:\n        - Использование относительных единиц размера при вёрстке;\n        - Использование специальных приёмов при вёрстке;\n        - Изменение размеров и/или положения элементов страницы в зависимости от настроек экрана (использование @media запросов в CSS);\n        - ...и многое, многое другое.\n    Поэтому конкретные меры, предпринятые для поддержки подходов RD не собраны в одном месте, а отнесены к соответствующим описаниям изменений\n    Все новые страницы уже разрабатываются с учётом критериев RD. Здесь приведу только старые страницы и элементы, переделанных под RD:\n        - Страница \"Настройки\";\n        - Блок \"Подсказка\";\n        - Страница \"Обзор Вселенной\" (старая версия);\n        - Страница строительства переделана для поддержки масштабирования\n        - Чат\n        - Список планет\n[!] Дизайн\n    Внешний вид:\n        - Добавлены элементы декора - рамки\n        - Унифицирован внешний вид и раскраска сходных по функционалу элементов страницы\n        - Добавлено масштабирование фона в скины, где их не было и улучшено там, где были\n        - Отмеченные чекбоксы теперь подсвечиваются зеленым - в старой Опере и браузерах на основе WebKit\n    Вёрстка:\n        Переход на вёрстку с использованием относительных единиц размера (em, %) вместо фиксированных (px);\n        Проделана огромная работа по оптимизации и унификации CSS-стилей:\n            - Основная инофрмация о вёрстке теперь располагается в файле '_template.css' темплейта, а CSS-файлы скинов содержат в основном только раскраску элементов и настройки декора (рамки)\n            - Вынесено множество стилей из инлайна (описание вида элемента в HTML через аттрибут style) в CSS-файлы\n    Всё это, а так же конфигурабельность скинов через 'skin.ini' (см.), позволило веруть в данном релизе скин 'immi'\n    Изменены директивы viewport для лучшей поддержки мобильных устройств\n    Добавлен хак для отключения хромо-андроидовского FontBusting\n    Пережаты все картинки с сохранением качества изображения. В среднем размер картинок уменьшился примерно на 20%\n    Удалена почти все картинки-дубликаты - как просто дублирующиеся, так и появившиеся в результате добавления класса skin (см. тэги \"Код\" в changelog_dev.txt)\n    Скин EpicBlue:\n        - Добавлено изображение для ресурса \"Метаматерия\". Соответственно - оно появилось и во всех остальных скинах;\n        - Переделаны картинки ВебМани на странице платежей при выборе метода платежа\n        - Картинки юнитов:\n            - Заменены изображения Терраформера и Большого Планетарного Шита\n            - Изображения для Нанолаборатории, МИС, Астрокартографии, Гордыни, ТОПа, ПЗ, Малого Планетарного Щита, МПР и Перехватчика приведены к стилю остальных картинок\n            - За проделанную работу - особая благодарность игроку 4apaeff@Alpha\n    Скин supernova-ivash:\n        - Плашки в навбаре заменены на более подходящие по тону - спасибо Ivash@Alpha\n[!] Масштабирование интерфейса\n    В помощь Responsive Design в движок добавлена собственная система масштабирования интерфейса - кнопки в навбаре (панель вверху):\n        - Кнопка \"Шрифт -\" уменьшает размер шрифта;\n        - Кнопка \"Норма\" возвращает шрифт к размеру \"по умолчанию\" - высота 11 пикселей без учёта масштабирования браузера;\n        - Кнопка \"Шрифт +\" увеличивает размер шрифта;\n    Вместе с размерами шрифта так же масштабируются и другие элементы страницы: картинки, кнопки, чекбоксы, таблицы итд\n    При нажатии на кнопки размеры элементов меняются в реальном времени, давая возможность увидеть будущий вид страницы\n    Из-за неполной поддержки спецификаций CSS и DOM-модели в некоторых устаревших браузерах и/или на мобильных устройствах могут возникать глюки отображение. Обновление страницы решит эту проблему\n    Настройки масштабирования запоминаются в куках браузера и в настройках пользователя. При загрузке страницы они берутся сначала из куков и только если куки не установлены - из настроек. Таким образом можно иметь разные настройки масштабирования на разных устройствах\n\n[!] Админка\n    Обзор:\n        - Пункт меню \"Обзор\" в админке теперь использует тот же код, что и список пользователей - за исключением того, что \"Обзор\" показывает только игроков онлайн и показывает Альянсы\n        - Таким образом теперь в пункте меню \"Обзор\" можно использовать весь функционал страницы \"Список пользователей\": сортировку, просмотр информации о пользователе, имперсонейт итд\n        - Первыми в списке всегда выводятся игроки и только потом - Альянсы\n    Обслуживание:\n        - Добавлено удаление юнитов без планет\n        - Добавлено удаление пустых юнитов\n        - Добавлено удаление стандартных записей логов (обсчёт статистики, маинтенанс, апдейт) более чем недельной давности\n        - Добавлена чистка очередей на покинутых и удалённых планетах\n    Платежи:\n        - Добавлена ссылка на отправку письма игроку из списка платежа\n    Записи логов:\n        - Добавлена ссылка на удаление из логов записей об обновлении движка и статистики (неактуальную после успешно проведенных обновлений);\n        - Ссылка на чистку логов теперь так же удаляет записи об обслуживании БД;\n    В настройки сервера вынесены:\n        - Стартовый размер хранилищ на планете\n        - Стартовое количество ресурсов на планете\n    Добавлены разделители разрядов в сообщение о начислении игроку ТМ и ММ через админку\n\n[+] Страница \"Флоты в полёте\"\n    Добавлена возможность массового отзыва флотов\n[+] Список планет (Страницы \"Планета\" и \"Империя\")\n    Под полосу застройки и очередь на планете добавлена подложка с фоном\n    На каждой планете теперь даже при отсутствии очередей выводятся полупрозрачные иконки, при клике на которые можно сразу перейти к строительству зданий, кораблей и обороны\n    Иконка своза ресурсов на луну появляется только при выборе луны\n[+] BBCode\n    \"Макросы\":\n        - \"Макрос\" - специальная последовательность символов, которая разворачивается в контекстно-зависимые данные. См. ниже\n        - Макрос \"sn://\":\n            - Макрос доступен тем, кому доступны обычные ссылки\n            - Макрос развертывается в полный URL к корню игры, т.е. если игра стоит по адресу \"http://your_domain.tld/your_path/\", то строка \"sn://overview.php\" будет развернута в \"http://your_domain.tld/your_path/overview.php\"\n        - Макрос \"faq://\" - разворачивается в УРЛ ЧаВо, если он прописан в 'url_faq' конфигурации\n    Макросы \"sn://\" и \"faq://\" так же можно использовать в составе тэга BBCode 'url', например: \"[url=sn://index.php?page=options]Настройки игрока[/url]\"\n[+] Акции и ивенты\n    Теперь на странице информации \"О сервере\" выводится стандартное значение рейтов и значение рейтов в рамках акции\n    Теперь во время акций с увеличением скорости добычи ресурсов:\n        - Размеры хранилищ не увеличиваются - это вызывало много вопросов у новичков;\n        - Добыча И потребление энергии НЕ ИЗМЕНЯЮТСЯ - т.е. не надо строить дополнительные энергомощности или хранить избыток;\n        - Добыча в экспедиции не изменяется - как и было задумано;\n\n[~] Исследования\n    Гравитехнология для исследования теперь требует минимум 6-го уровня Гипертехнологии\n    ЗС теперь не требует напрямую Гипертехнологии для постройки\n    СН теперь не требует напрямую Гипертехнологии для постройки\n[~] Страница \"Боевой отчёт\"\n    Добавлен заголовок на страницу\n    Ссылки на планеты приведены к стандартному виду - [координаты] (тип) Имя\n    Улучшена вёрстка\n[~] Страница \"Закладки\"\n    Теперь в пределах одной категории важности заметки дополнительно сортируются в порядке убывания по координатам и типу планеты\n    Продублирована группа элементов для удаления заметок в самом верху страницы\n[~] Реклама\n    Реклама не показывается игрокам, которые играют меньше недели\n    Реклама не показывается игрокам, которые взяли Премиум-аккаунт\n[~] Партнерская программа\n    Теперь когда аффилейт (игрок, приглашенный реферралом) покупает МетаМатерию, то реферрал (игрок, пригласивший аффилейта) получает 20% от купленной ММ в виде Тёмной Материи (в отличии от 10% при получении аффилейтом ТМ)\n[~] Новапедия\n    Склады:\n        - \"Хранилища\" переименованы в \"склады\";\n        - Уточнено и расширено описание складов;\n[~] Чат\n    Теперь при маленьком размере экрана строка ввода сообщений переносится на отдельную строку - что бы было больше места для текста\n    Теперь при маленьком размере экрана текст сообщения переносится на отдельную строчку, а при совсем крохотном - так же на отдельную строчку переносится ник\n    При переносе ника/сообщения на отдельную строчку для лучшей читаемости блоки разделяются линией и перед сообщением делается отступ\n\n[-] Страница \"Симулятор\"\n    Убрана отладочная галочка \"Симуляция\" - симулятор всегда работает в режиме симуляции\n\n[@] Код\n    Огромное количество изменений в коде, выносить которые сюда не имеет смысла\n\n...и множество других мелких правок. Посмотреть изменения в коде и мелкие правки можно в файле docs/changelog_dev.txt\n\n\n\nProject \"SuperNova.WS\" Release 39 \"2014 annual joint operation report\"\n~~\n[!] ВНИМАНИЕ! Требуется версия PHP >= 5.3.2\n[!] Рад представить вам очередной релиз СуперНовы. Перед вами - результат более чем года работы. Фактически, в этом мегапатче объединены целых ПЯТЬ релизов:\n      1. \"Зима-2014\" - системный релиз, где было проведена гигантская работа по упорядочиванию внутренних механизмов работы:\n        - Все юниты были отвязаны в БД от записей планет и пользователей;\n        - Вся работа с юнитами была переписана с нуля;\n        - С нуля была написана подсистема очередей;\n        - Разделены очереди кораблей и обороны;\n        - Полностью переписаны квесты;\n        - Добавлено глубокое сквозное кэширование, что позволило в 2-4 раза увеличить скорость работы движка;\n        - ...и многое, многое другое!\n      2. \"Весна-Лето 2014\" - багфиксы и небольшие оптимизации релиза З-2014;\n      3. \"Лето-Осень 2014\" - \"мобильнутый релиз\". Было сделано множество улучшений и усовершенствования для большего удобства игры с мобильных устройств (насколько это возможно без полной переделки темплейта и всех скинов);\n      4. \"Зима 2014-2015\" - \"бета релиз\". В начале зимы была выпущена бета 39-го релиза, но потом как-то всё закрутилось - новый год, фишки к НГ, ивенты к НГ, просто фишки, просто ивенты - и объем кода, написанного после беты неожиданно сам начал тянуть на отдельный релиз\n      5. \"Март-апрель 2015\" - \"бета релиз\". В середине февраля наконец-то было решено завязать с добавлением фишек и просто пофиксить баги... Очнулся я только в середине апреля. В игре появились звук, опросы, переработан код таймера, была добавлена пачка улучшений, пофикшены баги и переписана авторизация...\n    В общем, тут я решил все-таки выпустить 39й релиз as-is и сделать стабильную ветку\n    Общий объем проделанной работы можно оценить по количеству коммитов в GitHub. Ну или хотя бы по чейнджлогу релиза, приведенному ниже\n\n[#] payment_xsolla 1a0\n    (!) Новый модуль\n    (!) Стартовый релиз для СН 35a17.0+\n        Полностью реализован протокол \"Shopping Cart 3.0 Xsolla\" - команды 'check', 'pay' и 'cancel'\n        Поддержка режима тестирования\n        Конфигурация отдельным файлом config.php в каталоге модуля. Если конфигурация недоступна - модуль отключается\n        Поддержка выбора способа платежа xSolla\n        Генерик-плательщик xSolla\n        Хочу отметить часть методов платежа, добавленных к уже существующим:\n          - мобильные платежи: через SMS, со счета мобильного, сервия ZONG от PayPal, со счета Киевстар\n          - платежные системы: PayPal, EasyPay\n          - банковские переводы: Приват24, Сбербанк Онлайн, Банк24 Национальный кредит\n          - терминалы: EasyPay, Ibox, Терминалы Украины, Терминалы России\n          - кредитные карты American Express, JCB, UnionPay\n        Список далеко не полон - на сайте xSolla можно выбрать десятки других способов оплаты\n\n[#] menu_customize 0d0\n    (!) Новый модуль\n    (!) Базовая настройка меню\n        Новый раздел \"Настройки меню\" в \"Настройках\"\n        Настраивается вид кнопки \"Спрятать/Показать меню\". Она может быть:\n          - \"прилепленная\", т.е. всегда находится в левом верхнем углу экрана - даже когда страница скроллируется\n          - \"обычная\" - т.е. находится в левом верхнем углу СТРАНИЦЫ и скроллируется как нормальный элемент\n          - \"скрытая\" - т.е. кнопка не показывается. В этом случае недоступна кнопка \"Прикрепить/Открепить меню\". Так же при выборе этого варианта сбрасывается флаг открепления меню\n        Настраивается поведение кнопки \"Спрятать/Показать меню\":\n          - можно настроить скрытие меню при наведении курсора на кнопку \"Спрятать меню\"\n          - можно настроить показ меню при наведении курсора на кнопку \"Показать меню\"\n        Настраивается вид и поведение откреплённого меню:\n          - можно настроить скрытие откреплённого меню при выводе курсора за его пределы\n          - можно настроить показ откреплённого меню поверх всех элементов\n        Добавлена возможность выводить пункты меню в виде кнопок для большего удобства мобильных пользователей\n        Эта возможность доступна при базовой настройке меню: чекбокс \"Показывать пункты меню в виде кнопок\" в \"Настройках\", вкладка \"Профиль\", раздел \"Настройка меню\"\n    (!) Расширенная настройка меню\n        Для игроков с премиум-аккаунтом доступна пользовательская сортировка пунктов меню и скрытие отдельных пунктов (с определенными ограничениями в зависимости от уровня премиума, см. ниже)\n        Режим расширенной настройки меню включается при нажатии кнопки \"Настроить пункты меню\". При этом все остальные настройки становятся недоступными\n        У элементов, которые можно менять местами, слева появляется значок в виде двойной стрелки вверх-вниз. Такой пункт можно схватить курсором мышки и перетащить на новое место\n        У элементов, которые можно скрыть, справа появляется значок глаза. Клик на нём меняет видимость пункта меню. Белый глаз - пункт меню будет виден. Черный перечеркнутый глаз - пункт меню будет скрыт\n        После окончания настройки нужно нажать кнопку \"Сохранить настройки пунктов\". Настройки пунктов будут сохранены, а страница перегружена. При этом все остальные изменения в настройках сохранены НЕ БУДУТ\n        Так же нажатием кнопки \"Сбросить настройки пунктов меню\" можно вернуть меню первоначальный вид\n        Если скрыты какие-то пункты, то в самом низу меню появляется дополнительный пункт \"Показать скрытые\", который покажет скрытые пункты меню\n        Возможности по настройке меню зависят от уровня премиум-аккаунта игрока:\n          - нет премиум-аккаунта: доступны только базовые настройки\n          - Премиум 1-го уровня: игрок может менять местами пункты меню, кроме системных (название и логотип сервера, пункты \"Как играть\", \"Настройки\", \"Выход\" и логотип движка). Игрок не может скрывать пункты меню\n          - Премиум 2-го уровня: игрок может менять местами все пункты меню. Игрок может скрывать пункты меню, кроме системных\n          - Премиум 3-го уровня: игрок может менять местами и скрывать любые пункты меню\n        Кнопка \"Открепить меню\" сделана неперемещаемой\n        Администрация сервера имеет возможность менять/прятать все пункты меню даже без премиум-аккаунта\n        Все настройки из общего темплейта перенесены в соответствующий файл темплейта модуля\n\n[#] chat_advanced 5b6\n    (!) Функционал списка сообщений и онлайн-листа переписан на CSS и jQuery\n    (!) Администрирование\n        Полностью переписаны функции администрирования\n        На кнопках бана и мьюта добавлен попап с выбором сороков бана\n        На кнопка аньмюта при наведении курсора появляется соответствующая подсказка - что позволяет её отличить от кнопки мьюта\n        На замьюченом игроке в попапе видна причина мьюта\n        При операциях бана, мьюта и анмьюта чётко указывается, к какому пользователю будет применена команда. Это позволит исключить случайные промахи при обновлении онлайн-листа\n        Во всплывающее меню для бана/мьюта добавлено поле для ввода причины бана/мьюта. По умолчанию при мьюте поле пустое, а при бане заполнено стандартной причиной \"Заблокирован из чата\"\n        Иконки бана и мьюта теперь не показываются на аккаунты, которые выше игрока по иерархии\n        Добавлено больше сроков для команд мюьта и бана в меню администрирования\n        Во всплывающем меню для мьюта добавлен чекбокс \"Забанить без РО\" с соответствующим функционалом. По умолчанию галочка включена\n        И поле причины, и чекбокс бана без РО при открытии меню выставляются в значение по умолчанию. Это сделано специально для уменьшения вероятности ошибиться\n        Код меню администрирования теперь не рендерится для обычных игроков\n        Исправлена неработа некоторых диапазонов продолжительности мьюта/бана - в частности, \"y\" и \"w\"\n        Теперь можно забанить игрока из чата без РО. Для этого сразу после срока бана надо добавить восклицательный знак. Например, так:\n          /ban id 10 7d! Бан без РО\n    (!) Интерфейс\n        Изменено позиционирование попапов и подсказок с тем, что бы они не перекрывали вызывающий их элемент\n        Так же немного изменены сами попапы для лучшей читаемости\n        Смайлик, открывающий попап со смайликами, теперь сам стал кнопкой\n        Цвет фона в поле ввода сообщения изменен на черный\n        Выпадающий список с выбором цвета заменен на кнопку, при нажатии на которую выскакивает попап с вариантами выбора цвета\n        Выбранный цвет текста сразу же отражается в поле ввода сообщения, давая возможность увидеть, как будет выглядеть сообщение в игре\n        Панель элементов переверстана на чистом CSS без участия JS для расчёта размера элемента ввода - размер строки для сообщения теперь меняется динамически при ресайзе окна браузера\n        Добавлен код для устранения проблем с потерей фокуса строки ввода под некоторыми браузерами (в частности - IE 11, возможно поможет и на некоторых мобильных устройствах)\n        Раздвинуты элементы панели ввода: кнопка выбора цвета, кнопка смайла итд\n        Ссылка на историю чата вынесена в заголовок\n    (!) Список сообщений\n        Вывод сообщений переписан-таки на DIV-ах. В результате опять уменьшился объем передаваемых данных\n        Очень сильно оптимизирован вывод сообщений по размеру\n        Теперь вторая и последующие строки многострочных сообщений выравнены по первой строке, а не переносятся на следующую\n        Корректно выравнены иконки в нике относительно надписей\n        Благодаря новому парсеру, корректно выводятся URL-ы, стоящие сразу за символами \")\", \"]\" и \"}\"\n    (!) Список онлайна\n        Очень сильно оптимизирован вывод списка по размеру - даже без минимайзера на каждой строке выигрышь составляет более 0,5 кб!\n        При наведении на статус мьюта в списке онлайна сразу появляется подсказка с именем пользователя и сроком мьюта\n        ID пользователя перенесен из тултипа в онлайн-лист - так что его теперь легко увидеть\n    (!) Смайлики\n        Уменьшен размер выдачи: попап переделан на jQuery и вынесены стили в CSS, а так же убран лишний внутренний элемент\n        Улучшено позиционирование попапа, а сам попап стал выше и шире для удобства мобильных пользователей\n        Иконки заменены кнопками - для удобства мобильных пользователей\n        Клик в попапе вне иконки закрывает попап - для удобства мобильных пользователей\n        Смайлики теперь центрированы в своих кнопках и по вертикали\n        Уменьшен размер кнопок смайликов и попапа со смайликами\n        Исправлена ошибка неправильных кодов для смайликов \":)\" и \":(\"\n    (!) В чате теперь доступен расширенный функционал BBCode (см. ниже)\n    (+) Чат корректно работает с никами, содержащими символы \"'\", \" \", \"\\\", \"/\", \"&\"\n    (+) Звук\n        Добавлено звуковое уведомление при получении сообщения в чате\n    (%) Пермачат\n        Исправлена ошибка с неработающим ресайзингом элементов пермачата\n    (@) Добавлена поддержка компактизированных ников - соответственно в БД уменьшен размер таблицы с сообщениями\n    (@) JS и CSS\n        Файлы JS и CSS модуля маркируются датой и временем последнего обновления - для форсирования обновления браузерами при изменении этих файлов\n\n[#] player_premium 3c2\n    (!) Модуль переписан\n        Добавлена возможность продления Премиума и Апгрейда на более высокий уровень\n        В интерфейс страницы добавлена развернутая таблица с ценами на все комбинации уровня премиума и срока действия\n        При имеющемся премиуме дополнительно в таблице выводится информация о базовой стоимости премиума\n        Добавлена защита от двойного срабатывания при обновлении страницы\n        Базовая стоимость премиум-аккаунта уменьшена с 25.000 ТМ до 20.000 ТМ\n        Добавлен +6 премиум\n        Поддержка пунктов-кнопок в меню\n        Премиум-аккаунт добавляет 1 слот в очередь Исследований за каждый уровень Премиума\n    (+) Ивенты\n        Поддержка скидок на Премиум по ивентам\n\n[#] adm_user_stats\n    Блокировка по расчету недельных данных уменьшена до 1 недели - хотя данные и не совсем адекватные, однако лучше видеть не совсем адекватные данные, чем никаких\n    Добавлен рассчет % для активных (активность < 1 дня) и спящих (активность < 1 недели) пользователей\n\n[#] menu_applications_button 1с0\n    Иконки-ссылки для загрузки приложений под Android, Windows 8.1, Windows Phone 8\n    Иконки располагаются в каталоге модуля\n\n[#] unit_captain 3b0\n    (~) Работа с Капитаном при возвращении флота вынесена из основного кода в модуль\n    (~) Стоимость Капитана снижена до 20.000 ТМ\n\n[#] misc_radio v2c2\n    [!] Новый  HTML5/SWF плеер, совместимый с подавляющим большинством устройств\n        Теперь определение мобильныого устройства и переключение плеера на HTML5 версию работает корректно\n        Поправлены CSS-стили под поддержку бОльшего количества браузеров\n        Обновлен плейлист\n    (@) JS и CSS\n        Файлы JS и CSS модуля маркируются датой и временем последнего обновления - для форсирования обновления браузерами при изменении этих файлов\n\n[#] payment_robokassa 3c3\n    (!) Модуль переписан под новую систему платежей\n    (+) Добавлен generic-метод RoboKassa\n\n[#] payment_webmoney 3c0\n    (!) Модуль переписан под новую систему платежей\n\n[#] player_race 2d3\n    (@) Поддержка новой версии движка\n\n[#] player_award v0b0\n    (@) Поддержка новой версии движка\n\n[!] Очередь - полная переделка\n    С нуля написана универсальная подсистема очередей\n    Теперь очереди полностью независимые от записей пользователя и планеты\n    При обновлении очереди построек на планетах будут сконвертированы в новый формат\n    Очереди верфи и обороны будут обнулены, а стоимость юнитов в очереди - возвращена на планету\n    Полностью разделены очереди кораблей и обороны\n    На обзор планеты добавлена индикация очереди обороны\n    На картинки планет в обзоре планеты и обзоре Империи добавлена иконка активной очереди обороны (щит)\n    При ошибке постановки в очередь выдается соответствующее сообщение с подробным объяснением причины ошибки\n    При постановке в очередь юнитов больше, чем максимальный размер стэка очереди (2000 по умолчанию) в очередь ставятся подряд несколько стэков - до тех пор, пока не будет поставлено в очередь нужное количество юнитов или пока не закончатся свободные слоты в очереди\n    Унифицированный интерфейс особенно хорошо смотрится с вертикальной очередью построек (для экранов с разрешением свыше 1000 пикселов по горизонтали)\n    Вертикальная очередь построек включается в \"Настройках\" соответствующим чекбоксом\n[!] Покупка юнитов - унификация страниц \"Постройки\", \"Исследования\", \"Верфь\", \"Оборона\"\n    Вид страниц \"Постройки\", \"Исследования\", \"Верфь\", \"Оборона\" теперь унифицирован\n    Переверстана страница. Теперь она более дружелюбна к мобильным пользователям:\n      - Уменьшено количество юнитов в ряду до 4-х\n      - Переверстана панель детальной информации о юните\n      - Благодаря пп. 1 и 2 удалось на 20% уменьшить ширину страницы - теперь она практически не отличается по ширине от навбара и гораздо удобней для просмотра на узких экранах\n      - Кнопка постройки юнита сделана через jQueryUI - стала толще и красивее. Кнопка \"Удалить\" осталась такой же мелкой - для исключения случайных промахах на мелких экранах\n      - Размер страницы уменьшен на 25%-35%. Например, на тестовом прогоне размер уменьшился с 82676 до 64582 байт. И это со включенным минификатором!\n      - Ссылка на покупку юнита изменяется в контексте страницы: здания и боевые юниты \"строятся\", а технологии - \"исследуются\"\n    Описание юнитов:\n      - Теперь в описании юнита показывается не кумулятивная разница бонусов с текущим уровнем, а инкрементальная. Так гораздо лучше виден эффект перехода от уровня к уровню юнита, что позволяет лучше планировать своё развитие. Пример:\n        1. Пусть есть Астрокартография 2-го уровня\n        2. Раньше четвертая строка таблицы бонусов (Ур 4) показывала разницу в +2 колонии. Это была кумулятивная разницу с текущим 2-м уровнем. Т.е. +1 колония за 3 уровень (значение в строке Ур 3) и +1 колония за четвертый уровень - итого +2 колонии\n        3. Теперь четвертая строка будет показывать разницу в +1 колонию. Т.е. разницу между 3-м и 4-м уровнем Астрокартографии, которая и есть +1 колония. Значение в третьей строке (Ур 3) останется по-прежнему +1\n        4. На самом деле - изменение выглядит в интерфейсе горзадо проще и интуитивнее, чем его объяснение\n      - Если количество бонуса с прошлого уровня не изменилось - общее число бонуса подсвечивается желтым, а не зеленым. Таким образом, быстрый взгляд на таблицу дает полное представление об изменениях бонуса по уровням: зеленый цвет - положительные изменения, красный цвет - отрицательные, желтый цвет - нет изменений. Пример:\n        1. Возьмем опять же Астрокартографию 2-го уровня\n        2. Раньше в третьей строке таблицы бонусов (Ур 3) количество экспедиций (1) подсвечивало зеленым\n        3. Теперь в той же строке количество экспедиций (1) подсвечивается желтым - поскольку новых экспедиций на третьем уровне Астрокартографии не появится\n        4. Опять же - проще посмотреть в интерфейсе, чем объяснять\n      - На странице покупки юнитов показываются требования для покупки, а так же их выполнение\n      - Если к юниту нет требований - требования не показываются вообще\n      - Убрана надпись NaN/NaN в конце требования к юниту, когда требованием является определенный Родной Мир\n      - При входе на страницу первый элемент для отображения описания выбирается по порядку отображения, а не по ID. Например, на Верфи теперь отображается Легкий Истребитель, а не Супертранспорт, как раньше\n    Здания:\n      - Теперь здание можно удалить даже если требования к постройке не удовлетворены. Т.е. теперь здания можно удалять в любом порядке и не нужны соответствующие Планы\n    Верфь и оборона:\n      - Добавлена информация о боевых характеристиках (для всех юнитов) и скоростных характеристиках (для кораблей)\n      - В списке юнитов вместо остатка при постройке 1 юнита показывается цена постройки 1 юнита: красным - если не хватает ресурса, желтым - если хватает ресурса на 1 юнит, зеленым - если после постройки юнита еще остаются ресурсы\n      - В описании в таблице стоимости юнита цена и остаток ресурсов теперь меняются динамически с учетом количества выбранных для постройки юнитов\n      - В описании юнитов под таблицей стоимости теперь показывается максимальное количество юнитов, которое можно построить с имеющимися ресурсами\n      - Теперь при вводе корректного количества юнитов и нажатии кнопки \"Enter\" юниты ставятся в очередь\n      - Кнопка \"Построить\" блокируется, если не выбрано количество юнитов\n    Исследования:\n      - Премиум-аккаунт добавляет 1 слот в очередь Исследований за каждый уровень Премиума\n      - На странице технологий отображается \"Время исследования\", а не \"Время строительства\"\n      - На странице \"Исследования\" при выборе Астрокартографии в подробном описании добавлена таблица, показывающая увеличение количества экспедиций и колоний при апгрейде технологии\n[!] Обзор Вселенной - Переработка Обзора Вселенной\n    Страница сильно переработана\n    Полностью переделана работа с попапами:\n      - Изменен принцип позиционирования попапов - теперь они по минимуму закрывают информацию от пользователя, а так же стараются не вылазить за границы окна\n      - Устранены ошибки с позиционированием попапов у большинства мобильных пользователей - в отдельных браузерах ошибка может сохранится из-за некорректной реализации в браузере масштабирования\n      - Теперь попапы открываются не только при наведении мышки, а и по клику. Повторный клик на той же ячейке закрывает попап для удобства мобильных пользователей. Впрочем, \"мышисты\" тоже могут этим пользоваться\n      - Расширены области срабатывания попапов для удобства мобильных пользователей\n      - Передеалн попап игрока: в нём сдублированы все возможности, которые дают иконки. Так что теперь иконки можно отключать для экономии места на экране без потери функциональности\n    Восстановлена работа настройки \"Время показа подсказок\" в разделе \"Вселенная\" на вкладке \"Интерфейс\"\n      - Настройка даёт возможность задать задержку между наведением курсора мыши на элемент в Обзоре Вселенной и появлением попапа\n      - По умолчанию задержка выставлена в 500 миллисекунд (0,5 секунды)\n      - Значение \"0\" означает \"использовать задержку по умолчанию\". Для фактического отключения задержки можно использовать небольшие значения, например, \"1\"\n      - Задержка действует только при наведении курсора - при клике на элементе попап появляется сразу\n    Везде, где это имело смысл, ссылки изменены на кнопки для удобства мобильных пользователей\n    Заменены иконки шпионажа и ракетной атаки - теоретически их теперь не должны блокировать рекламорезки на мобильных устройствах\n    На пустой позиции в системе теперь высвечивается большая кнопка \"Колонизировать...\"\n    Кнопка \"Перейти\" в выборе Галактики/Системы вынесена вправо от элементов листания Вселенной\n    Исправлена ошибка появления отрицательных цифр в попапе обломков, если на планете не хватает дейтерия для отправки переработчиков\n    JS-код переписан с использованием jQuery и большая его часть вынесена в отдельный файл\n    Переверстана страница с активным использованием CSS - размер итоговой страницы уменьшен на несколько десятков % - в зависимости от населенности системы и активности флотов в ней\n    Теперь на уничтоженной планете/луне не всплывает попап\n[!] Империя\n    Дважды переверстана страница \"Империя\" с активным использованием CSS и jQuery\n    При идентичном виде и идентичной функциональности, на тестовом примере (15 планет при средней застройке) выигрышь в размере составил порядка 60-70%% при уже включенном минификаторе!\n    При большем количестве объектов или более плотной застройке, выигрышь может быть еще больше\n    Добавлено количество текущих/максимальных экспедиций\n[!] Планета\n    Размер страницы уменьшен на 5%-20%\n    Добавлено отображение бонусных уровней Губернатора\n    Ссылка \"Переработать\" сделана кнопкой. Она всегда показывается, если на планете есть переработчики. При этом, если обломков нет на орбите - кнопка неактивна\n[!] Планета/Управление\n    Переверстана страница. Теперь разные типы элементов разделены на группы и упорядочены так, что бы исключить случайное нажатие на мобильных устройствах\n    Тип ядра:\n     - Увеличена высота кнопки\n     - Количество ТМ, нужной для операции, теперь форматируется с группировкой тысяч и кодируется цветом\n    Телепорт:\n     - Увеличена высота кнопки\n     - Количество ТМ, нужной для операции, теперь форматируется с группировкой тысяч и кодируется цветом\n    Губернаторы: вынесены в отдельный блок\n    Перенос столицы:\n      - Сделано подтверждение на перенос столицы;\n      - Увеличена высота кнопки и кнопка теперь отключается, если перенос невозможен\n      - Количество ТМ, нужной для операции, теперь форматируется с группировкой тысяч и кодируется цветом\n    Уточнена надпись - какой пароль нужно ввести для сноса колонии\n[!] Меню - Крадущаяся мышка, затаившееся меню\n    Новые авторские иконки: уменьшенного размера, оптимизированные, оригинальные\n    Появилась возможность спрятать меню разово или однократно - до обновления страницы или перехода на другую страницу\n      - Возможность спрятать меню однократно бывает полезна, например, в окне чата, что бы увеличить его площадь или в окне \"Империя\", что бы больше информации влезло на экран\n      - Для того, что бы однократно спрятать меню нужно навестись мышкой или тапнуть по кнопке \"Спрятать меню\" в левом верхнем углу экрана. При этом кнопка изменится на \"Показать меню\"\n      - Что бы вернуть меню достаточно навестись мышкой или тапнуть по кнопке \"Показать меню\"\n    Так же можно спрятать меню на постоянной основе тем самым, увеличивать полезную площадь страницы\n    Для того, что бы постоянно спрятать меню, нужно нажать кнопку \"Открепить меню\" в самом верху меню. В открепленном режиме меню имеются следующие особенности:\n      - Статус открепленного меню запоминается в куках устройства, т.е. для каждого устройства открепление меню настраивается отдельно\n      - При каждом следующем открытии страницы меню будет сразу в спрятанном состоянии (см. выше)\n      - Кнопка \"Открепить меню\" меняется на кнопку \"Закрепить меню\"\n      - Что бы вернуть стандартное поведение меню достаточно нажать на кнопку \"Закрепить меню\"\n      - Что бы воспользоваться, нужно навестись мышкой или тапнуть на кнопке \"Показать меню\". При этом при выходе курсора мышки за пределы меню оно автоматически скрывается\n    Особенно полезна возможность скрытия меню будет для устройств с маленькими экранами - телефонов и мелкоразмерных планшетов\n    Однако она так же может оказаться полезной и для пользователей обычных компьютеров, благо из-за функционала кнопки \"Показать меню\" привычки в работе с меню практически не нужно менять\n    В стандартных скинах убраны \"скачки\" меню в процессе рендеринга страницы\n    В раздел \"Правила игры\" добавлена ссылка на подробную документацию к игре. Файл по ссылке открывается в новом окне\n    Ссылка на движок открывается в новом окне и на странице с описанием движка\n    Рекламная ссылка перенесена в самый низ меню\n    Поддержка модуля menu_customize\n    В темплейте включён рекламный блок и логотип СН\n[!] Заметки/Закладки - Объединение Заметок и Закладок\n    Теперь в \"Заметках\" можно добавлять координаты во Вселенной и тип объекта (Планета, Луна, Поле обломков):\n      - Ввиду полной бессмысленности дублирования функционала, \"Закладки\" убраны из игры\n      - Все существуещие в игре \"Закладки\" перенесены в \"Заметки\" вместе с комментариями\n      - Удалена таблица Закладок, код и темплейт\n    Теперь Заметку можно сделать прилепленной:\n      - Такие Заметки будут отображаться на всех страницах игры под навбаром сразу после Новостей в отдельной таблице\n      - Клик на заголовке переведет на страницу редактирования Заметок\n    Таблица на второй странице отправки флота теперь берет данные из Заметок:\n      - Выбираются только записи, у которых все три координаты планеты отличны от нуля\n      - В качестве текста используется заголовок Заметки. Работает сортировка по приоритету\n    В Заметке теперь может быть пустым либо заголовок, либо текст - но не оба одновременно\n    На странице редактирования теперь только важность Заметки выделяется цветом, а не вся Заметка, как раньше\n    Клик на координатах в обычных и прилепленных Заметках откроет страницу \"Вселенная\" в указанной галактике и системе\n    При редактировании заметки иконки подтверждения и отмены изменений разнесены в вертикальной плоскости\n    Можно делать закладки на слот Экспедиции\n[!] Артефакты\n    Новый тип Артефакта: Крюк. Он телепортирует астероид из ближайшего метеоритного пояса и запускает его на орбиту планеты, создавая таким образом луну. Доступны три вида Крюков: Малый, Средний и Большой. Малый создает луну минимального диаметра (1100 км), Большой - максимального диаметра (8999 км), а Средний - луну случайного диаметра (от 1100 до 8999 км)\n    Изменена логика работы Наностроителя и Эврестического Чипа. Теперь они уменьшают время соотвественно текущего исследования в Империи и постройки/разрушения текущего строения на планете/луне в два раза (если до окончания процесса осталось больше часа) или моментально заканчивают процесс (если до окончания осталось не более 1 часа)\n    В цене Артефакта добавлен разделитель тысяч\n    Добавлены временные картинки для Крюков\n    На Эвристический чип и Наностроитель добавлена защита от случайного срабатывания. Теперь они не срабатывают, если осталось меньше 1 минуты для постройки\n    Исправлена редкая ошибка, когда АКК мог пропасть при неудачной попытке развернуть его на полной планете\n[!] Опросы\n    Добавлена возможность проводить опросы/голосования\n    Опросы прикрепляются к новостям - один опрос на одну новость\n    Можно копировать новости с опросами\n    Можно редактировать новости с опросами, но при этом потеряются текущие результаты опроса\n    Поддерживается произвольное количество ответов, но не менее двух\n    Можно устанавливать срок действия опроса, используя синтаксис PHP-функции strtotime() или просто задавая дату окончания опроса (по серверному времени). По умолчанию опрос действует 1 сутки\n    Игрок может выбрать 1 вариант ответа из списка\n    Добавлена индикация срока опроса\n[!] UBEv4.1\n    Изменена процедура обсчета боя. Теперь щиты считаются индивидуально для каждого корабля\n    Это значит, что теперь практически невозможно провести бой без потерь с обеих сторон\n[!] Флоты\n    Проверка качества отправляемого флота на предмет наличия орбитальных структур (СС, ТОП, \"Лень\")\n    Поддержка внешней активации транзакции при отправке флота - для пакетной обработки массовой отправки со страницы своза ресурсов\n    Поддержка моратория на агрессивные миссии\n    Менеджер летящих флотов:\n      - Переписан менеджер летящих флотов для избежания зависаний\n      - Новый МЛФ должен гарантировать полное отсутствие дедлоков\n      - Интервал обсчёта флотов теперь задается в таблице `config` переменной 'fleet_update_interval'\n      - Так же теперь обновление флотов не производится во время отключений сервера\n      - Все мисиии переписаны для поддержки нового МЛФ\n      - Из основного кода удалена поддержка модуля Капитанов\n      - Добавлен таймер-сторожок против зависания флотов\n    Шпионаж:\n      - Включён \"Имперский шпионаж\":\n      - Уровень Имперского шпионажа (УИШ) - это сумма уровней Шпиона и Шпионской технологии с учётом всех доступных бонусов, но без учёта количества спутников-шпионов\n      - Если УИШ шпионящего больше или равен УИШ шпиониемого, то в отчете будут видны так же Имперские Технологии\n    Экспедиция:\n      - Поддержка дополнительных событий в Экспедиции\n    Колонизация:\n      - Исправлена ошибка, позволяющая колонизировать на 1 планету больше возможного количества\n[!] Статистика\n    Полностью переделан расчёт статистики\n    Скорость расчёта статистики заметно увеличена (при одновременном увеличении количества объектов для обсчёта!). Чем больше игроков и чем активнее игра - тем больше выигрышь в скорости обсчета. Ускорение обсчета на типичном сервере составляет от 10 раз и выше\n    Теперь полностью учитываются все юниты всех типов, включая корабли в полёте\n    Теперь в статистике по ресурсам полностью учитываются все ресурсы: на планетах, вложенные в очереди строительства/верфи/обороны/исследования, находящиеся на флотах в полёте. Так же учитывается наличная ТМ (ММ не учитывается)\n    Теперь при расчете статистики Альянса так же учитывается юниты Альянса и ресурсы в банке\n    Теперь расчитывается и отображается изменение места Альянса во всех типах статистики\n    Исправлена очепятка, из-за которой вообще не учитывался дейтерий\n    Исправлена очепятка, из-за которой в очках по ресурсам не учитывались ресурсы, находящиеся в очередях\n    Все вышеуказанные изменения приведут к однократной перетусовке в статистике и росту абсолютного значения всех видов статистики\n    Теперь в базе хранится статистика за 2 недели\n    Настраивается количество дней в таблице `config` переменная 'stats_history_days' (по умолчанию - 14 дней)\n    Теперь длинные ники игроков и названия Альянсов не переносятся на вторую строку\n    Теперь для новых аккаунтов при первом обсчете статистики в изменении места показывается \"*\", а не \"-(новое место)\"\n    Настройки страницы статистики (типа статистики, Игроки/Альянсы итд) теперь передаются в строке браузера - теперь можно легко обмениваться ссылками на конкретную страницу статистики\n[!] Настройки\n    Теперь при смене пароля игроку не надо логиниться заново - при смене пароля так же изменяется кука\n    Замер времени\n      - Замеры времени делаются индивидуально для каждого устройства\n      - Теперь время не будет сбиваться при переходе с устройства на устройство вне зависимости от разницы настроек во времени/часовых поясах\n      - Таймеры в JavaScript теперь не зависят от того, был ли произведен замер или нет - нужные данные вычисляются на клиентской стороне по данным с сервера\n      - Фактически, это означает, что все таймеры теперь всегда будут корректно работать - не взирая на правильность/неправильность часовых поясов, \"сбитых\" часов итд\n      - Замер времени сохраняется для рендеринга времени на стороне сервера (дата/время сообщений, чат, новости итд)\n      - На вкладке \"Профиль\" добавлена возможность вручную выставить разницу между серверным и клиентским временем. Для этого нужно выставить галочку \"Задать вручную разницу во времени\", ввести разницу во времени в секундах и сохранить изменения\n    Переделана страница настроек под табы\n    Добавлена возможность отключать колонок \"Статистика игрока\" и \"Информация об игроке\" в обзоре Вселенной\n    Добавлена возможность ввести основной емейл - если он еще не введен\n    Добавлены подсказки, объясняющие различие между основным и вторичным емейлом\n[!] BBCode - Расширение функционала BBCode\n    Расширен функционал BBCode - добавлена поддержка уровня автора сообщений при парсинге\n      Для пользователей с разным authlevel досутпны разные BBCode. В частности, Администраторам (authlevel 3) теперь доступны следующие возможности:\n        1. Автоматическое преобразование URL-ов в ссылки\n        2. BBCode [url=HREF]text[/url]\n        3. BBCode [c] может использоваться с любыми цветами в формате [c=#XXXXXX]text[/c], где #XXXXXX - HTML-код цвета\n      В новостях теперь доступен весь функционал BBCode\n      В чате теперь доступен расширенный функционал BBCode\n    Добавлены новые смайлики:\n      :accordion: :ban: :censored: :contract: :facepalm: :help: :hmm: :maniac: :panic: :poke: :pray: :whistle:\n    Заменены лучшими версиями (в основном - без подкладки белого \"креста\") смайлики:\n      :clap: :coffee: :nea: :popcorn: :rose: :quote: :shout: :sorry: :spiteful: :ups:\n    Убраны смайлики из-за их больших размеров, которые сильно портят форматирование\n      :tratata: :maniac:\n    Теперь парсер понимает URL-ы, стоящие сразу за символами \")\", \"]\" и \"}\"\n    Устаревшие HTML-тэги <u> и <s> заменены на <span style>\n    Исправлена ошибка добавления в конец URL закрывающего BBCode при парсинге чистых URL\n[!] Звуки\n    Добавлена библиотека для поддержки звуков ion.sound © 2014 Денис Инешин лицензия http://ionden.com/a/plugins/licence.html\n    По умолчанию звуки отключены. Для включения нужно на странице \"Настройки\" (вкладка \"Интерфейс\") поставить галочку \"Включить звуки в игре\" и сохранить изменения\n    Работа звуков в устаревших и/или мобильных версиях браузеров НЕ ГАРАНТИРУЕТСЯ!\n[!] Платежи\n    Полностью переделан интерфейс системы платежей\n    Изменены скидки за оптовую покупку. Теперь небольшие сумм покупок дают большую скидку, чем раньше\n    Курсы валют и бонусы оптовой покупки сведены в одну ячейку\n    Игрок теперь может выбрать валюту по-умолчанию\n    Валюта игрока будет использована во всех расчетах вместо базовой валюты сервера, т.е. при расчете стоимости ММ, стоимости пакетов итд\n    На каждом пакете теперь указывается его стоимость в валюте игрока\n    Выбор фиксированного пакета при покупке ММ теперь сразу переводит на следующую страницу\n    Добавлено 17 новых методов платежей и картинок к ним\n    Добавлены картинки для большинства поддерживаемых методов платежей\n    Если методов оплаты более 6 - остальные сворачиваются и прячутся с возможностью в дальнейшем развернуть\n    Переупорядочены доступные методы платежей - от наиболее используемых к наименее используемым\n    Исправлено вычисление рассчётной стоимости ММ в рублях\n    Добавлена поддержка WMB - белорусских рублей на WebMoney\n    Основной валютой сервера по умолчанию установлен доллар США (USD). Скорректированы курсы всех валют\n    Платежи теперь соблюдают порядок, назначенный им в module\n[!] Император\n    Переработана страница \"Император\"\n    Теперь на странице показывается статистика изменения основных показателей игрока за прошедшие 2 недели (меньше - если не накоплена нужная статистика)\n    Добавлен пробел между \"У вас\" и количеством сообщений\n[!] Режим отпуска\n    Переделана процедура ухода в отпуск\n    Теперь на странице отпуска не отображаются: меню, новости, навбар... По прежнему блокируется вход на любые страницы игры. Отдыхать - так отдыхать!\n[!] Квесты\n    Полностью переписан механизм квестов\n    Исправлен баг неполного начисления награды, когда одновременно выполняются более одного квеста\n[!] Авторизация\n    Полностью переписана с нуля система авторизации в игре\n    Все операции системы авторизации проводятся в init.php - дальше по коду передаются только результаты авторизации. Это дает возможность позже добавить плагины для авторизации во внешних сайтах\n    Страницы логина, регистрации и восстановления пароля сведены в одну и сделаны более дружественными для мобильных пользователей:\n      - При восстановлении пароля отсылается цифровой код, что облегчает ввод с экранной клавиатуры\n      - Код восстановления высылыается не чаще раза в час и действует 1 сутки\n    Теперь при сбросе пароля происходит автологин - не надо самому логиниться с новым паролем\n    Переделан выбор языков\n    Добавлены страницы редиректа старых адресов reg.php и lostpassword.php\n    Данные об user_agent и user_proxy вынесены в отдельную таблицу со справочником\n    Исправлена ошибка входа в игру, если текущей планетой является удаленная по Обслуживанию планета\n    Исправлена проблема пропадающих элементов на странице логина\n[!] Интерфейс\n    Мультиэлемент ввода чисел - полная переработка:\n      - Теперь можно не кликать несколько раз подряд для увеличения (+)/уменьшения (-) значения в ячейке, а достаточно зажать кнопку мышки - количество будет изменятся автоматически\n      - Изменение данных при зажатой мышке проводится с ускорением - чем дольше держать кнопку мышки, тем быстрее будет изменятся значение в строке ввода\n      - Добавлены кнопки \"0\" и \"М\" - соответственно устанавливающие значение поля в 0 и в максимальное значение\n      - Теперь при входе в ячейку если значение в ней отлична от нуля, то значение выбирается (как в операционных системах)\n      - Максимальное использование jQuery\n      - Элемент переделан под jQueryUI.button(). Выглядит получше и более дружественнен к мобильным пользователям (читай - больше по размеру)\n      - Убраны устаревшие функции bind(), live() и delegate()\n      - Уменьшено количество обработчиков\n      - Ускорена работа мультиэлемента\n      - Использование спецтега <ainput> и вставка мультиэлемента на его место методами jQuery\n      - Исправлена ошибка \"NaN\" при вводе первого нечислового символа\n      - Переверстан под таблицы из-за странного поведения float div в некоторых сценариях. Пока ребята из Вилларибо верстают сайт дивами....\n      - Установлена фиксированная ширина кнопок. Это сделало вид элементов аккуратнее. Ну, и заодно - исправило некрасивость на странице отправки флотов\n      - Новый мультиэлемент работает на страницах: постройки флота и обороны; подбора кораблей во флот; черный рынок - покупка и продажа кораблей, обмен ресурсов\n    Скины:\n      - При разрешении экрана менее 1224 пикселов фон либо не грузится вообще (страница входа/регистрации) или грузится облегченная версия фона если разрешение экрана выше 768 пикселов (скины EpicBlue и supernova-ivash)\n      - Это сделано для более быстрой работы на мобильных устройствах и маломощных компьютерах. Фишка работает только в перечисленных скинах и/или на указанных страницах\n      - Так же, если размер изображентия планеты невелик, то грузится файл с меньшим разрешением. В неподдерживаемых скинах картинки планет могу отсутствовать вовсе\n    Все строки ввода данных, чекбоксы и кнопки теперь используют jQueryUI в тех браузерах, в которых он работает\n    Переработано множество страниц для совместимости с новым видом интерфейса, сделано огромное количество мелких правок - так что даже не буду пытаться их все перечислить\n    Теперь текст кнопок и вводимые данные в элементах ввода имеют жирный шрифт\n    Везде, где возможно, поля ввода для логина и пароля ограничены 32 символами\n    Добавлена индикация аккаунтов, находящихся в отпуске. Такие аккаунты отмечаются специальной иконкой в нике и надписью \"В отпуске\" на странице \"Император\"\n    Для исключения блокировки корпоративными фаерволлами и прочим, \"sex\" заменен на \"gender\"\n    Добавлена возможность выбрать пол в \"Настройках\"\n    Удалены неиспользуемые картинки в каталоге OpenGame/img\n    Все темплейты теперь используют общую иконку для отправки писем из /design/images\n    Ссылки, открывающие дополнительные окна, теперь подчеркиваются двойной линией\n[!] Админка\n    Отключена \"Панель админа\"\n    Список игроков:\n      - Добавлена колонка \"Активен\", показывающее прошедшее время с момента прошлой активности игрока\n      - При наличии модуля платежей появляется колонка с общим количеством купленной игроком ММ\n      - Убрана колонка \"Е-Мейл\" - эту и другую иноформацию об игроке теперь можно посмотреть на отдельной странице\n      - В колонке \"Рефералы\" подколонки \"Игроки\" и \"ТМ\" выравнены по правому краю\n      - Добавлены разделители тысяч в количество ТМ, заработанной рефералами\n      - Колонки переупорядочены для большего удобства\n      - Уменьшен размер страницы примерно на 40-60%%\n      - Добавлена подсветка всей строки при наведении курсора для облегчения операций с аккаунтами\n    Информация об игроке:\n      - Добавлена базовая страница с информацией об игроке. Она доступна только Администраторам и выше. Перейти на неё можно кликнув по ИД или нику игрока на странице \"Список игроков\"\n      - В настоящий момент страница является чуть облагороженным дампом соответствующей записи в таблице users без возможности редактирования\n      - Отформатированы все даты и числа (там, где это имеет смысл)\n    Обслуживание:\n      - Добавлена упаковка логов транзакций Тёмной Материи. Записи, сделанные ранее 1 числа три месяца назад, пакуются в одну запись от 1 числа указанного месяца с суммой всех транзакций за период упаковки\n      - Добавлена агрегирование статистики онлайна игроков. Записи, сделанные ранее 1 числа три месяца назад, агрегируются в записи с интервалом по 10 минут со средним арифметическим онлайна за указанный интервал\n      - Добавлена чистка общих логов. Из таблицы `logs` при обслуживании удаляются записи, сделанные ранее 1 числа три месяца назад\n      - Добавлена чистка game_watchlist и stats_hide_player_list от несуществующих пользователей\n      - Теперь удаляются все сообщения (кроме личных и альянсовских) старше 4 недель у всех игроков\n      - Теперь удаляются все сообщения (кроме личных и альянсовских) любой давности у игроков, неактивных более 4 недель\n    Переделана работа режима обслуживания. Теперь различаются источники, переводящие сайт в режим обслуживания, для каждого из которых выводится своё сообщение:\n      - Блокировка из админки. Игроки в причине блокировки видят то, что введено в настройках сайта;\n      - Блокирование из статистики - своё сообщение;\n      - Блокирование из обновления - своё сообщение;\n      - Блокирования при первой инсталляции до окончания настройки\n      - Текущий режим отображается для игроков соответствующим сообщением, автовыбором соответствующего пункта в \"Настройках\" в админке и красным сообщением вверху странице в админке же\n      - Администратор может насильно отменить режимы блокировки, устанавливаемые статистикой и обновлением - однако делать это крайне не рекомендуется\n    Обновление статы:\n      Опять переделана процедура расчёта статистики\n      Теперь указывается не интервал расчёта статистики, а \"расписание\", т.е. конкретное время запуска в привязке к текущему времени. Ниже будет подробнее объяснено на примерах\n      Формат расписания изменился и теперь выглядит так:\n        <время запуска>[,<время запуска>...]\n        <время запуска>: [ГГГГ:[ММ:[ДД:[ЧЧ:[ММ:[СС]]]]]]\n      Пустые параметры приравниваются к нулю. Лидирующий ноль укзаывать не обязательно. Т.е. записи: \"0000:00:00:00:30:00\", \"0:30:0\" и \"30:\" - равноценны. Примеры:\n        - \"00:00:27:00\" означает \"запуск в 27 минут каждого часа\", т.е. в 00:27:00, 01:27:00, 02:27:00 итд;\n        - \"04::\" означает \"запуск в 4 утра каждого дня\"\n        - \"01::,17:15\" означает \"запуск в 1 утра каждого дня и в 17 минут 15 секунд каждого часа\", т.е. каждый день в 00:17:15, 01:00:00 (это сработало дополнительное расписание), 01:17:15, затем в 02:17:15, 03:17:15, 04:17:15 итд;\n        - \"1:4:30:00\" означает \"Запуск 1 числа каждого месяца в 04:30 утра\", т.е. 1 января в 04:30:00, 1 февраля в 04:30:00, 1 марта в 04:30:00 итд;\n        - \"2015:1:1:00:00:00\" означает \"Однократное срабатывание 1 января 2015 года ровно в полночь\"\n    Начисление ТМ:\n      - Убрано начисление по планете\n      - Теперь при начислении ТМ через админку в комментариях пишется причина начисления и кто произвел начисление\n      - Исправлена ошибка начисления ТМ игрокам, чье имя начинается со знака \"-\"\n    Начисление ММ:\n      - Теперь при начислении ММ через админку в комментариях пишется причина начисления и кто произвел начисление\n    Локализация:\n      - Восстановлена работа интерфейса локализации в админке\n    Записи логов:\n      - Определение дедлоков и добавочная информация для их диагностики\n      - Для получения добавчной инофрмации о дедлоках пользователь MySQL, под которым запускается игра, должен иметь право MySQL PROCESS\n[!] Инсталляция\n    Добавлен специальный режим отключения сервера \"Инсталляция и конфигурация\". В этом режиме игра стартует после инсталляции (логин по умолчанию - admin, пароль - admin). Так же его можно включить в админке или выставив в таблице `config` в записи 'game_disable' значение 4\n    Отличие данного режима отключения от остальных в том, что нём доступны страницы login.php и logout.php\n    Основное назначение режима - настройка сервера после инсталляции движка и аварийное восстановление после сбоев в работе сервера\n\n[+] Отправка флота\n    Добавлена кнопка 1/X, где X - количество неотправленных экспедиций. Появляется только если X > 1 и X < максимального количества экспедиций\n    Кнопка 1/(доступных экспедиций) теперь появляется только если не дублирует кнопки 1/(максимальное количество экспедиций) и \"Все корабли\"\n    Теперь при отправке флота виден уровень Капитана на планете и его скиллы\n    На второй странице отправки флота список текущих планет и список Заметок с координатами переделаны в кнопки для удобства мобильных пользователей\n    При подборе кораблей во флот теперь показывается актуальная скорость каждого корабля\n    Добавлены разделители тысяч к количеству кораблей во второй колонке\n    На странице подбора кораблей во флот добавлена опция сортировки списка кораблей\n    Возможна следующие виды сортировки: \"Стандартная\", \"По названию\", \"По скорости\", \"По количеству\" и \"По ID\" - по возрастанию характеристики\n    Так же возможна инверсная сортировка - по убыванию характеристики\n    Выбор вида и хода сортировки осуществляется дропдауном и галочкой в нижней части таблицы\n    Сделанный выбор сохраняется в настройках пользователя и затем используется при следующих открытиях той же страницы\n    Изменения вида или хода сортировки перегружает страницу\n[+] Своз ресурсов\n    Теперь в расчете строки ИТОГО учитывается ёмкость трюмов флота, т.е. показывается реальное количество ресурсов, которые будут свезены по текущим данным\n    Код расчёта переписан на jQuery\n    Исправлено PHP-предупреждение, если у игрока только одна планета\n[+] Сообщения\n    Отправка сообщений - \"Переписка\":\n      - Теперь при отправке сообщения в ответ на личное сообщения, доступна история переписки. Она показывается под формой создания сообщения\n      - В истории показываются в порядке написания сообщения от собеседника игроку и неудаленные сообщения от игрока собеседнику, но не более 20 сообщений\n      - Так же добавлена автоматическая замена нескольких подряд префиксов ответов (\"RE:\") на один\n    \"Написать сообщение\" теперь стало отдельной кнопкой\n[+] Рекорды\n    Для флотов и обороны теперь показывается суммарное количество юнитов на всех планетах и лунах. Флоты в полёте по-прежнему не учитываются\n[+] Чат\n    Добавлен звук при получении нового сообщения в чате\n    Теперь если по какой-либо причине отключается модуль chat_advanced, то в чате не видны личные и информационные сообщения\n    В списке онлайна Администраторы сервера теперь всегда идут первыми\n    Скруглены углы в попапах и в админском элементе ввода сообщений\n    Уменьшены размеры кнопок со смайликами, а так же уменьшен размер соответствующего попапа\n    Исправленна ошибка появления кнопок chat_advanced при открытии открепленного главного меню\n\n[~] Партнерская программа\n    Добавлена прямая ссылка\n    Теперь в простых ссылках указывается не УРЛ, а имя сервера\n    Изменены УРЛы на действительные (с reg.php на login.php)\n    Исправлена регистрация по партнерским ссылкам - теперь правильно регистрируются привлеченные игроки\n    Исправлена ошибка, при которой баннер не показывался без регистрации\n[~] Новости\n    Теперь статус новости (например, \"СВЕЖАЯ\") пишется перед датой, а не перед текстом новости\n    Теперь по умолчанию галочка \"Разослать новость всем игрокам\" отключена\n[~] О сервере\n    Добавлена индикация режима взаимодействия игроков с 1 IP (мультиаккаунтов)\n[~] Поиск\n    Отключён поиск планет. Я вообще с трудом понимаю, зачем он был изначально сделан в игре...\n[~] Симулятор боя\n    Переверстан интерфейс симулятора\n[~] Чёрный рынок\n    При полностью пустом списке б/у кораблей в продаже он пополняется случайным образом\n\n[%] Альянсы\n    Альянс теперь можно передать любому участнику - а не только заместителю главы\n    Исправлена ошибка с возможностью сделать Альянс пустым именем/тэгом/титулом главы\n    Исправлено незаполнение имени user_as_ally тэгом при создании Альянса\n    Исправлена ошибка отправки многострочных сообщений\n    Исправлена невозможность включить прием заявок после его отключения\n    Исправлена ошибка попадения в список кандидатов на передачу Альянса игроков не из Альянса\n    Исправлен прием игрока в Альянс, чей тег или название содержат спецсимволы\n\n[@] Документация\n    В пример конфигурационного файла добавлено уведомление о необходимости использования разных префиксов для нескольких копий СН на одном сервере\n[@] jQuery\n    Добавлены виджеты Droppable и Sortable\n[@] Темплейты\n    {-path_prefix-} заменен на {D_SN_ROOT_VIRTUAL}\n    Добавлена глобальная переменная SN_GOOGLE - в темплейт и JS\n    Добавлено подключение CSS, специфичного для темплейта - _template.css - из корня темплейта\n    Основная часть страницы теперь центрируется не через <center>\n[@] Код\n    Рендерер ников:\n      - Полностью переписан рендерер ников\n      - Добавлена поддержка компактизированных скин-независимых ников\n      - Расширена возможность отключать части ника\n      - Иконки теперь имеют фиксированные положения, не зависимые от порядка загрузки модулей\n      - Добавлена поддержка нового рендерера ников в стандартный чат\n    Статистика:\n      - Добавлено принудительное увеличение памяти в процедуру расчета статистики\n      - Размер памяти, резирвируемый под процесс PHP при обсчете статистики, можно менять переменной 'stats_php_memory' в таблице `config`. Синтаксис - такой же, как и в php.ini. Значение по умолчанию - 1024M\n      - Минимальный интервал обсчета задается переменной 'stats_minimal_interval' в таблице `config`. Значение по умолчанию - 600 секунд\n      - Теперь во время расчёта статистики движок переходит в режим обслуживания\n    Лог ТМ:\n      - Лог ТМ при найме/покупке Чертежа теперь пишется на языке текущего пользователя. Так же в него пишется стоимость и срок найма\n      - В лог ТМ теперь пишется запись об увольнении Наёмника с детальной информацией\n      - Более подробный лог траты ТМ при покупке секторов на планете\n    Метаматерия:\n      - Добавлено и заполнено поле dark_matter_total в таблице `users`. Поле так же изменяется при начислении ТМ внутренними механизмами движка\n    Локализация:\n      - Добавлена поддержка вариантов языка - типа, en-US и en-UK\n      - Добавлена подсистема сбора информации об употреблении строк локализации в коде. Включается переменной \"server_locale_log_usage\" в таблице `config`\n    Две новые директивы отладки в init.php:\n      - DEBUG_SQL_COMMENT - включает комментирование SQL-запросов\n      - DEBUG_SQL_ONLINE - включает лог SQL-запросов в таблицу `logs`. Так же подразумевает DEBUG_SQL_COMMENT\n    Теперь можно отключить защиту от взаимодействия аккаунтов с одним IP выставив в таблице `config` параметр `game_multiaccount_enabled` в 1\n    Убраны BOM-префиксы в исходниках - таким образом, восстановлена работа создания баннера на странице \"Заработай ТМ\" и, собственно, сама генерация баннера\n    Исправлено предупреждение в /includes/classes/supernova.php line 125\n    Исправлено предупреждение Warning: Invalid argument supplied for foreach() in includes/db.php on line 365\n    Исправлено предупреждение в uni_coordinates_valid()\n    Поставлены заглушки для будущей поддержки GeoIP\n[@] Модули\n    Теперь модули могут добавлять пункты в админское меню\n[@] Apache\n    Добавлены файлы .htaccess\n[@] JS и CSS\n    Все файлы JS и CSS в основном коде маркируются датой и временем последнего обновления - для форсирования обновления браузерами при изменении этих файлов\n    Информация из global.css, относящяяся к темплейту OpenGame перенесена в CSS темплейта\n[@] Локализация\n    Добавлена переменная активного языка в classLocale\n    Добавлено уведомление о критической ошибке при попытке вызвать функции локализации с $lang не в виде класса\n[@] БД\n    При старте уровень транзакций сессии устанавливается в REPEATABLE_READ для меньшей зависимости от настроек сервера\n    Добавлена возможность установливать уровень отдельной транзакции в sn_start_transaction()\n    В doquery() запрос теперь обрабатывается функцией trim()\n\n\n\nProject \"SuperNova.WS\" Release 38 \"Admin astro expo news bugfix\"\n~~\n[#] player_award 0a2\n    (!) Новый модуль\n        Раздел \"Награды и достижения\" на странице Императора, видимый всем\n        Поддержка орденов, медалей, памятных знаков, вымпелов, бэйджей\n        Отдельный тип опции рендера ника\n    (!) Орден Спонсора четырех степеней - в комплекте\n        Иконка Ордена Спонсора в нике везде, где допускаются иконки\n    (!) Бессмертный\n        Памятный знак \"Бессмертный\"\n        Знак начисляется автоматически при покупке хотя бы одной единицы ММ\n        Статус \"Бессмертного\" означает сохранение аккаунта при автоматической чистке БД (Админка/Обслуживание)\n[#] misc_radio 0a0\n    (!) Новый модуль\n        Новый пункт меню 'Радио \"Космос\"', открываюший в новом окне плеер радио\n[#] chat_advanced 2c1\n    (!) Перманентный чат (миничат)\n        Реализован миничат через iframe. Чат делается перманентным при нажатии ссылки \"Прикрепить\" под списком онлайна пользователей. Можно прикреплять как общий чат, так и чат Альянса. Одновременно может быть прикреплен только один чат - общий или Альянса\n        Прикрепленный чат можно открепить - ссылка \"Открепить\" под списком онлайна в прикрепленном чате\n        Можно изменять соотношение фреймов, отведенных под основной экран и миничат. Для этого нужно потянуть за разделитель между фреймами\n    (+) История чата\n        Добавлены кнопки листания на страницу вперед/назад, на первую/последнюю страницы истории\n    (+) Смайлики\n        Добавлен смайлик :sarcasm:\n        Теперь смайлики размещены в отдельном попапе и не занимают лишнее место. Вызвать попап можно кликнув на смайлик слева от строки ввода сообщения\n    (~) Whisper\n        Команда \"/w\" теперь корректно работает с никами, в которых есть пробелы. Для этого ник нужно заключить в двойные кавычки. Подсказка по команде изменена соответствующим образом\n        Изменен формат вывода шепота: \"(от кого) -> (кому)> (сообщение)\"\n        Теперь клик на имени собеседника в списке сообщений так же добавит в строку текущего сообщения команду \"/w <имя адресата> \". Так будет легче переписываться с игроками, находящимися вне чата\n    (~) Интерфейс\n        Все неявные элементы, клик по которым совершает какое-либо действие на странице (например, ник в списке онлайна) выделены соответствующим образом\n        Теперь ники подчеркиваются цветом ника и не подчеркиваютя иконки (если таковые есть в оформлении)\n        Изменен алгоритм смены фокуса, что бы окно миничата не мешало работе в основном окне\n        Убрана дублирующаяся надпись \"Игроки онлайн\", а количество игроков перенесена в заголовок списка онлайна\n    (~) Клавиатура\n        По \"Ctrl+Enter\" теперь так же отсылаются сообщения\n    (~) Таймаут\n        Теперь при отключении чата по таймауту можно обновить окно чата/миничата соответсвующей ссылкой, которая появляется вместо строки ввода сообщения\n        Теперь таймаут так же убирает список игроков онлайн\n[#] player_race 2d0\n    (+) Марс\n        Марсиане так же получают +1 уровень к Астрокартографии\n    (+) Родные миры\n        Теперь родной мир можно выбрать сразу на странице \"Родные миры\"\n[#] unit_res_metamatter 0a0\n    (!) Новый модуль\n        Модуль активирует новый ресурс в игре - Метаматерию\n        Метаматерия - новый тип ресурсов, который можно приобрести только за реальные деньги. Таким образом отделяются ресурсы, которые можно приобрести внутри игры и ресурсы, которые можно только купить. Это нужно в первую очередь для добавления услуг и сервисов, которые требуют от движка платежей в реальных деньгах - например, СМС-информирование об атаках\n        Так же это позволит добавить в игру больше возможностей для взаимодействия игроков, не опасаясь сильного дисбаланса от такого взаимодействия и/или смещения экономики игры в сторону pay-2-win\n        Добавлена возможность начислить игроку ММ из админки\n        Модуль поставляется в пакете с любым платежным модулем\n        Добавлена иконка Метаматерии в навбар\n        Добавлено поле для общего количества полученной метаматерии - в частности для получения статуса \"Бессмертный\" (см. ниже). Статус \"Бессмертный\" работает так же при отсуствии модуля player_award\n        В админку добавлена страница просмотра платежей с фильтрами\n[#] Модули платежей\n    Все модули платежей полностью переписаны\n    Максимальная унификация модулей - все общие части вынесены в модуль-родитель\n    Все сообщения внутри модуля генерируются во внутренних кодах СН\n    Добавлена подсистема конвертации внутренних кодов в коды платежный систем (там, где это имеет смысл)\n    Все модули переделаны под работу с Метаматерией, а не ТМ\n\n\n[!] Технологии/Астрокартография\n    Экспедиционная технология и Колонизационная технология заменены одной технологией Астрокартографии\n    Стоимость имеющихся уровней устаревших технологий возвращена на главную планету игрока, а сами технологии удалены\n    Устаревшие технологии, находящиеся в исследовательской очереди игроков, удалены, а их стоимость возвращена на планету, где была запущена технология\n    По умолчанию убрано ограничение на максимальное количество колоний\n    Уровень развития Астрокартографии влияет на:\n      1. Максимальное количество колоний\n      2. Максимальное количество экспедиций\n      3. Максимальное время отправки флота в экспедицию\n    Теперь видно текущее и максимальное количество колоний:\n      1. В Обзоре Империи (первая колонка, вторая строка)\n      2. На странице выбора кораблей во флот при переходе на неё из Обзора Вселенной с миссией \"Колонизация\"\n      3. На странице выбора миссии \"Колонизация\"\n[!] Полностью переписаны Экспедиции\n    Экспедиции теперь планово-прибыльные, т.е. полеты в экспу одним и тем же флотом в среднем будут приносить прибыль, а не убытки, как раньше\n    Количественные результаты Экспедиций (нахождение флота, ресурсов, ТМ) теперь привязаны к стоимости отправляемого флота. Т.е. чем дороже флот в пересчете на ресурсы - тем больше будет найдено в Экспедиции\n    Экспедиции стали средне- и высокоуровневым контентом - существуют минимальные размеры флотов, которые вообще имеет смысл посылать в Экспедиции. Меньшие флоты попросту не будут ничего привозить (см.ниже). Хотя, например, фармить ресурсы транспортами можно прямо со старта игры - учитывая плановую прибыльность обновленных Экспедиций это вполне имеет смысл, особенно \"шахтерам\"\n    Теперь максимальная длительность Экспедиции зависит от уровня Астротехнологии - 1 час за каждый уровень технологии\n    Теперь время нахождения флота в Экспедиции влияет на шанс найти что-либо в процессе миссии - как на положительный шанс, так и на отрицательный\n    Изменены шансы происходящих событий (отношения расчитаны для обновленной Экспедиции в 1 час):\n      1. Шанс того, что в Экспедиции не произойдет ничего, увеличен примерно в 2,5 раза\n      2. Шанс потери флота уменьшен почти в 20 (!) раз\n      3. Шанс нахождения ресурсов увеличен на треть\n      4. Шанс нахождения флота уменьшен на треть\n      5. Шанс нахождения ТМ увеличен на треть\n    Изменено количество находимых в Экспедиции ништяков и оно теперь привязано к общей стоимости флота:\n      0. Количество ништяков может быть \"Нормальным\", \"Большим\" и \"Очень большим\". Соответственно меняются возможное количество находимых ништяков (идея упёрта с Огейма)\n      1. Количество ништяков отбалансированно для достижения планово-прибыльного характера Экспедиции и для компенсации изменения шанса происходящих событий\n      2. Количество находимых ТМ теперь вариабельно. Максимальное количество находимых ТМ - 10.000. Коэфцициент пересчета стоимости флота в ТМ зависит от курса ТМ (который, в свою очередь, зависит от скорости добычи ресурсов). Чем он выше - тем больше кораблей надо на шанс получения 1 ТМ. Для ориентировки - на х1 нужно запустить 10 эсминцев для получения 1 ТМ\n      3. Качество найденного флота теперь очень сильно зависит от качества исследовательского флота: все находимые корабли дешевле, чем самый дорогой корабль в Экспедиции. Максимальная стоимость флота зависит от скорости добычи на сервере\n      4. Качество найденных ресурсов теперь вариабельно. В среднем находится 50% металла, 37,5% кристалла и 12,5% дейтерия. Однако в частном случае доли ресурсов могут варьироваться в очень широких пределах. Максимальное количество находимых ресурсов прямо пропорционально скорости добычи ресов\n    Теперь за полёты в Экспедиции начисляется экспедиционный опыт. За набор экспедиционного опыта начисляются уровни. При получении нового уровня начисляется 1.000 ТМ\n    Количество опыта для получения уровня - геометрическая прогрессия с первым членом 10 и показателем 1,05. Ниже дается для ориентировки небольшая таблица: в первой колонке - экспедиционный уровень, во второй - количество опыта для перехода на следующий уровень, в третьей - общее количество экспедиционного опыта для перехода на следующий уровень. Собственно, таблица:\n        1      10        10\n        2      10        20\n        3      11        31\n        4      11        42\n        5      12        54\n        6      12        66\n        7      13        79\n        8      14        93\n        9      14       107\n       10      15       122\n       15      19       209\n       20      25       321\n       25      32       465\n       30      41       650\n       35      52       887\n       40      67     1.189\n       45      85     1.575\n       50     109     2.070\n       75     369     7.530\n      100   1.252    26.052\n      150  14.361   301.323\n      200 164.691 3.458.217\n    Посмотреть текущее текущий уровень, общее количество экспедиционного опыта и необходимое количество для перехода на следующий уровень можно на странице \"Император\"\n    Для облегчения регулярных Экспедиций с одной и той же планеты добавлена новая кнопка на экран подбора флота. При максимальном количестве экспедиций (Х) более одной доступна новая кнопка на странице подбора флота - \"1/X\"\n[!] Админка\n    Полностью переписана страница \"Список сообщений\"\n    Полностью переписана страница \"Флоты в полёте\". Процедуры унифицированы с пользовательской частью\n    Полностью переписана страница \"Обзор\"\n    Полностью переписана страница \"Добавить луну\"\n    Переработана страница \"Записи система логов\"\n    Переработана страница \"Начисление ТМ\"\n    Переработана страница \"Начисление ММ\"\n    Меню в админке переделано на динамическое - по типу меню игроков\n    Теперь сразу после обслуживания происходит обновление статистики - для устранения разрывов в местах игроков, которые могут появится из-за удаления старых аккаунтов\n    Убраны операции очистки таблиц, дублирующие работу констраинтов\n    Теперь для запуска обновления из админки используется гораздо более безопасная проверка по AUTHLEVEL пользователя, а не по HTTP_REFERRER вызывающей страницы\n[!] Страница игрока\n    Добавлена возможность просмотреть страницу игрока (ака \"Император\")\n    Для этого нужно кликнуть на иконку \"Император\", которая доступна:\n      1. На странице статистики\n      2. В результатах поиска\n      3. В Обзоре Вселенной\n\n\n[+] Новости\n    Добавлена информация о публикаторе новости\n    Изменено отображение новости\n    Теперь свежие новости показываются на всех страницах залогиненного пользователя\n    Теперь для того, что бы скрыть свежие новости не обязательно открывать страницу новостей - достаточно кликнуть на кнопку \"Закрыть\" в правом верхнем углу списка новостей\n    Добавлена вторичная сортировка новостей по ID\n[+] Меню\n    Переформатировано меню - убраны дублирующиеся пункты, ЧаВо перенесено вверх\n    Пункты меню \"ЧаВо\", \"Форум\" и \"Правила игры\" открываются в новых окнах\n[+] Обзор Империи\n    Оптимизирован HTML-код страницы. В среднем в минифицированном состоянии выигрыш составил порядка 6 кб на 1 планету/луну. Чем больше объектов в Империи и чем больше типов юнитов - тем больше выигрышь\n    Строка таблицы с координатами перемещена под строку с названием планет для унификации вывода\n    Строка с количеством секторов убрана - она дублирует информацию на иконке планеты\n\n\n[~] Отпуск\n    Минимальный срок отпуска составляет 1 неделю\n    Введен таймаут на следующий отпуск - 1 недели с момента выхода из предыдущего отпуска\n[~] Удержание\n    Длительность удержания теперь находится в промежутке от 1 до 12 часов\n[~] Статистика\n    Теперь на странице статистики показывается так же время следующего обновления\n    Время предыдущего и следующего обновления учитывает разницу между локальным и серверным временем\n[~] Навбар\n    Если страница с навбаром открыта во фрейме (например, при прикреплении чата) в под навбаром появляется ссылка \"Обновить страницу\", при нажатии которой страница по фрейме будет обновлена\n[~] Локальное время\n    Теперь замер разницы между клиентским и серверным временем производится автоматически каждый час\n\n\n[%] Альянсы\n    Исправлена ошибка \"налазания\" длинного внешнего текста на логотип Альянса\n[%] Флоты\n    Исправлена ошибка при отправке Капитана с миссией \"Транспорт\"\n    Усилена защита от отправки флотов в нетранспортную миссию с ресурсами\n[%] Сообщения\n    Исправлена ошибка в сообщениях, если указан неправильный класс сообщений\n[%] Поиск\n    Исправлена ссылка на страницу статистики для ранка 1000+\n    Убрана ссылка на страницу статистики для неучаствующих в подсчете аккаунтов (например - Адмиинистрации сервера)\n[%] Боевой отчет\n    Устранено появление строки \"Дата и время\" для симулированных отчетов при ненулевой разнице клиентского и серверного времени\n[%] Настройки\n    Исправлена индикация режима защиты планет Администрации\n    Исправлено отображение статуса удаления аккаунта\n\n\n[@] admin/Sypex Dumper\n    Sypex Dumper обновлен до версии 2.0.11\n[@] Темплейты\n    Добавлена поддержка нескольких темплейтов\n    Меню и навбар могут быть отключены параметрами в темплейте\n    Добавлена возможность подгрузки серверных CSS для скинов\n[@] Рендер ников\n    Добавлена опция, позволяющая присвоить нику дополнительные CSS-классы\n[@] БД\n    Изменена таблица `payment`\n[@] Расписание\n    Изменен формат расписания. Теперь он определяет интервал запуска задачи и имеет вид:\n      Г-М-Д Ч:И:С\n    где Г, М, Д, Ч, И, С - соответственно длина интервала в годах, месяцах, днях, часах, минутах и секундах\n    Значения левее первой значащей цифры можно не указывать. Например, \"0-0-1 0:0:0\" можно записать как \"1 0:0:0\" и это будет означать \"запустить задачу раз в сутки\"\n    Нулевые значения можно опустить. Например, предыдущий интервал можно записать так же в виде \"1 ::\". Обращаю внимание на пробел между \"1\" и \":\"! Пробел - значащий разделитель и его опускать в данном случае нельзя, потому что интервал \"1::\" будет истолкован как \"запустить задачу раз в час\"!\n[@] Вселенная\n    Добавлены картинки-плейсхолдеры для аватара/лого Альянса/миниатюры планеты\n[@] Прочее\n    $time_now теперь определяется из $microtime\n    Новая процедура определения локальных путей для поддержки PHP 5.3+\n    Методы локализации инкапсулированы в класс и при работе с объектами вызовы процедур редиректят в методы объекта\n    Убрана ошибка уровня PHP_STRICT в классах кэширования\n    message() теперь работает через PTE-объект\n    Чёрный рынок переписан на использование result вместо message()\n\n\n\nProject \"SuperNova.WS\" Release 37 \"Year of Work\"\n~~\n[#] payment_webmoney 0a4 - модуль приема платежей на кошельки WebMoney\n    Требуется СН не ниже 37a9.20\n    Поддержка нескольких кошельков с разными валютами\n    Поддержка SUCCESS_URL\n[#] payment_robokassa 0a1 - модуль платежей через агрегатора RoboKassa\n    Требуется СН не ниже 37a9.30\n[#] Расширенный чат - chat_advanced v1d0\n    Требуется СН не ниже 37a4.0\n    Добавлена поддержка локального времени в чат и историю чата\n    Теперь можно использовать команды  при  выбранном  цвете  сообщения.  Ранее\n    такие команды не воспринимались системой чата\n    Произведена замена цветов для лучшей читаемости сообщений: red  ->  maroon,\n    blue -> cyan\n    Цвет green оставлен для пользвателей, а подтверждающие системные  сообщения\n    используют цвет lime - как и в остальном интерфейсе сервера\n    Системные и приватные сообщения теперь выделяются жирным шрифтом\n    Клик на имени игрока в списке онлайна теперь всегда добавляет команду  \"/w\"\n    в начало сообщения - а не в конец, как ранее\n    Скорость обновления  в  AJAX  части  чата  теперь  регулируется  переменной\n    'chat_refresh_rate'\n    Теперь игроки из онлайн-списка  исчезают  сразу  после  выхода  из  чата  -\n    таймаут попадания в список установлен как удвоенный 'chat_refresh_rate',  а\n    не как 'chat_timeout' ранее и вычисляется по дополнительному полю, а не  по\n    `chat_player_activity` как ранее\n[#] player_premium 1d3\n    Добавлена индикация уровня премиума в меню\n    Добавлена  индикация  остатка  времени  Премиума  в  пункт  меню   в   виде\n    прогресс-бара с цветовым кодированием:\n      Зеленый - осталось не менее 50% времени пермиума\n      Желтый - осталось меньше 50%, но не менее 25%\n      Оранжевый - осталось меньше 25%, но не менее 10%\n      Красный - осталось меньше 10%\n      Цвет фона - нет Премиума\n[#] Локализация/Узбекский\n    Добавлены переводы на узбекский для модулей: chat_advanced, player_premium,\n    player_race, player_race_units, unit_captain и unit_hope\n\n\n[!] Экономика/Плотность планеты\n    Добавлен новый параметр  планеты  -  плотность.  Он  определяет  химический\n    состав геосферы планеты и влияет на добычу ресурсов на ней\n    Плотность планеты лежит в диапазоне от 850 до 9250 кг/м3.  Плотность  новых\n    планет распределена случайным образом по нормальному распределению\n    Существует 7 классов плотности - с уникальным набором  коэфициентов  добычи\n    для каждого класса:\n      Ледяные планеты (<2000 кг/м3) - встречаются  очень  редко:  очень  низкая\n      добыча металла, очень низкая  добыча  кристаллов,  очень  высокая  добыча\n      дейтерия\n      Силикатные планеты (2000=3250 кг/м3) - встречаются  редко:  очень  низкая\n      добыча металла, очень высокая добыча  кристаллов  и  еще  хорошая  добыча\n      дейтерия\n      Каменные планеты (3250-4500 кг/м3) - встречаются  часто:  хорошая  добыча\n      металлов, высокая добыча кристаллов и низкая добыча дейтерия\n      Стандарнтые планеты (4500-5750 кг/м3) - встречаются очень часто:  хорошая\n      добыча металлов, хорошая добыча кристаллов и хорошая добыча дейтерия\n      Железнорудные  планеты  (5750-7000  кг/м3)  -  встречаются  часто:  очень\n      хорошая  добыча  металлов,  низкая  добыча  кристаллов  и  низкая  добыча\n      дейтерия\n      Металлические планеты (7000-8250 кг/м3)  -  встречаются  редко:  отличная\n      добыча металлов, низкая добыча кристаллов и низкая добыча дейтерия\n      Тяжелометаллические планеты (>8250  кг/м3)  -  встречаются  очень  редко:\n      великолепная добыча металлов, очень  низкая  добыча  кристаллов  и  очень\n      низкая добыча дейтерия\n    Стартовая планета имеет плотность 5500 кг/м3 и принадлежит  к  4-му  классу\n    плотности. Все луны имеют плотность 2500 кг/м3 и принадлежат ко 2-му классу\n    плотности\n    Тип ядра планеты можно изменить  за  ТМ.  Возможность  доступна  на  экране\n    управления  планетой  (Обзор  планеты  ->  Управление).   Стоимость   смены\n    высчитывается динамически и зависит от того,  насколько  сильно  отличается\n    текущий тип ядра от желаемого\n    Добавлено отображение типа ядра планеты на страницу \"Обзор планеты\"\n    На страницу \"Обзор Империи\"  добавлено  отображение  типа  ядра  планеты  с\n    цветовым кодированием:\n      Зеленый - тип ядра встречается очень часто\n      Желтый - тип ядра встречается часто\n      Оранжевый - тип ядра встречается редко\n      Красный - тип ядра встречается очень редко\n    В Новапедию добавлена статья про плотность и типы ядер планет\n[!] Артефакты\n    Добавлены два новых Артефакта: \"Эвристический чип\" и \"Наностроитель\"\n    Артефакты уменьшают на 1 час соответственно время текущего  исследования  и\n    время постройки/уничтожения текущего здания на текущей планете\n    Если  оставшееся  время  исследования/постройки/уничтожения  меньше  одного\n    часа, то Артефакт обнуляет время. Разница не переходит на следующий слот  в\n    очереди\n    Стоимость эвристического чипа составляет 20.000 ТМ\n    Стоимость наностроителя составляет 5.000 ТМ\n    В очередь построек добавлена возможность использовать Наностроитель  -  при\n    наличии Артефакта на складе\n    В очередь построек добавлена возможность использовать Эвристического чипа -\n    при наличии Артефакта на складе\n[!] Экономика\n    Изменен  алгоритм  расчетов  бонусов  добычи  ресурсов.  Список   изменений\n    приводится ниже:\n      1. Бонусы на добычу ресурсов улучшают так же базовую добычу на планете\n      2. Бонусы на добычу ресурсов так же увеличивают потребление сопутствующих\n      ресурсов - дейтерия (для Термоядерной Электростанции) и энергии (для всех\n      остальных шахт)\n      3. Бонусы на  добычу  ресурсов  улучшают  так  же  выработку  энергии  на\n      спутниках\n      4. Естественное производство  дает  100%  ресурсов  даже  при  недостатке\n      энергии\n    Изменен  алгоритм  работы  Термоядерной  электростанции.  Теперь   ТЭС   не\n    использует ресурсы со склада,  а  оперирует  только  балансом  производства\n    дейтерия. Т.е. ТЭС работает только при положительном  балансе  производства\n    дейтерия И генерации энергии одновременно. Это сделано  для  того,  что  бы\n    оставленная \"без присмотра\" ТЭС с отрицательным  балансом  по  дейтерию  не\n    выжрала весь ресурс со склада\n    Как следствие - ТЭС не отключается при положительном  балансе  производства\n    дейтерия и энергии, даже если количество дейтерия на планете равно  0.  Это\n    упростит своз ресурсов с планет, на которых энергия генерируется только  на\n    ТЭС\n    Теперь при эффективности добычи ресурсов менее 100%  вместе  с  актуальными\n    значениями добычи в ячейку добавляется рассчетное значение добычи в круглых\n    скобках. Это упростит балансировку производсва при недостатке ресурсов\n    Убрана задержка в обновлении информации о производстве ресурсов\n[!] Локальное (клиентское) и серверное время\n    Изменена процедура замера разницы между  локальным  и  серверным  временем.\n    Теперь она производится не каждый раз при обращении к серверу, а один раз и\n    сохраняется в БД. При заметном изменении разницы  можно  заново  произвести\n    эту  операцию,  установив  галочку  \"Замерить   разницу   между   локальным\n    (клиентским) и серверным временем\"  на  странице  настроек  пользователя  и\n    сохранив настройки. Замер будет произведен  при  следующем  открытии  любой\n    страницы игры\n    Теперь вместо локального или серверного времени одновременно показывается и\n    локальное, и серверное время в следующих местах:\n      1. В навбаре - часы реального времени\n      2. При отправке флота на экране выбора точки назначения - в графе времени\n      прибытия и возвращения флота\n      3. При отправке флота на экране подтверждения отправки - в графе  времени\n      прибытия и возвращения флота\n    Теперь  вместо  серверного  времени  показывается  локальное  в   следующих\n    местах:\n      1. В событиях навбара (флоты и экспедиции)\n      2. В новостях\n      3. На экране флотов в полете\n      4. На экране обзора планеты в списке летящих флотов\n      5. В чате и истории чата\n      6. В боевых отчетах\n      7. В сообщениях\n    Переформатирован навбар для добавления локального и серверного времени\n    Повышена устойчивость механизма к ошибкам на стороне клиента:  неправильный\n    часовой пояс, неправильные настройки DST  в  операционной  системе,  сильно\n    отстающие/спешащие часы итд\n[!] ТМ/Платежи\n    Понижена в 2,5 раза цена ТМ. Теперь за 1 гривну можно купить 2500 ТМ\n    Размер лота (шага покупки) установлен в 2500 ТМ\n    Изменена система бонусов за оптовые покупки ТМ:\n      от 50.000 ТМ - бонус 2% к количеству ТМ\n      от 100.000 ТМ - бонус 4% к количеству ТМ\n      от 200.000 ТМ - бонус 7% к количеству ТМ\n      от 250.000 ТМ - бонус 11% к количеству ТМ\n      от 375.000 ТМ - бонус 15% к количеству ТМ\n      от 500.000 ТМ - бонус 22% к количеству ТМ\n      от 750.000 ТМ - бонус 33% к количеству ТМ\n      от 1.000.000 ТМ - бонус 44% к количеству ТМ\n      от 1.250.000 ТМ - бонус 55% к количеству ТМ\n    Список доступных цен и список  скидок  строится  теперь  по  данным  модуля\n    sn_payment\n    Добавлена поддержка модулей с более чем одним количеством шагов при покупке\n    Добавлена поддержка мультивалютности\n    Добавлена поддержка SUCCESS_URL в платежных системах\n    Добавлена индикация внутренних курсов системы\n    Теперь большую часть информационных элементов на странице можно свернуть\n[!] Настройки пользователя/Смена имени пользователя\n    Добавлена  возможность  изменения  имени  пользователя  за  ТМ.   Стоимость\n    изменения - 100.000 ТМ\n    Игра сохраняет историю изменения имени пользователя. Только бывший владелец\n    может при желании вернуть себе старое имя - опять же за ТМ\n    Поиск по имени так же производится по старым именам. В случае, если  старое\n    имя  пользователя  соответствует  критериям  поиска,  в  результаты   будет\n    добавлена  еще  одна  строка,  в  которой   будет   указано   текущее   имя\n    пользователя, а после него в скобках  и  выделенное  цветом  -  старое  имя\n    пользователя. Никто не спрячется от своей истории!\n    Максимальная длина имени пользователя уменьшена до 32 символов\n    Переменная настроек сервера 'game_user_changename' отвечает за  возможность\n    смены имени пользователя самим пользователем:\n      0 - смена имени запрещена\n      1 - смена имени разрешена и свободна\n      2 - смена имени разрешена, но стоит ТМ. Стоимость смены имени  указана  в\n      переменной 'game_user_changename_cost' (100.000 ТМ по умолчанию)\n    По умолчанию включена смена пользователем своего имени за ТМ\n[!] Исследования\n    Изменен алгоритм рассчета эффективного уровня  лаборатории  и  необходимого\n    времени исследования при настройке сервера \"Строить  лабораторию  во  время\n    исследования: Нет\"\n    Теперь при  идущем  исследовании  блокируется  постройка/уничтожение  нано-\n    и/или лабораторий на все планетах\n    Теперь  блокируется  попытка  начать  исследование  на  планете,  где  идет\n    постройка/уничтожение нано- и/или лабораторий\n    Однако возможно начать исследование на другой планете. В таком исследовании\n    не будут участвовать все планеты где происходить  модификация  нано-  и/или\n    лабораторий. При этом по окончании постройки/уничтожения время исследования\n    не пересчитывается\n[!] Обновление\n    Теперь на время обновления сервер отключается\n    Теперь обновления можно запустить только из админ-консоли\n    В сообщение об обновлении сервера добавлена ссылка для Команды Сервера\n    Множество ускорений в процедуре обновления\n[!] Локализация/Узбекский\n    Добавлен перевод на узбекский от Акмалжона Мусаева\n[!] Очередь\n    Обновленная система очереди\n\n\n\n[+] Навбар\n    Теперь в событиях навбара (флоты и экпедиции) показывается тип  объекта,  к\n    которому относится событие (планета или луна)\n[+] Обзор Империи\n    Добавлена возможность управления  производством  шахт  со  страницы  Обзора\n    Империи\n    Дроп-дауны  в   колонке   \"ИТОГО\"   выставляют   соответствующие   проценты\n    производства для зданий соответствующего типа сразу на всех планетах\n    Кнопки  \"Сохранить\"  продублированы  в  заголовке  каждого  типа  юнитов  и\n    действуют сразу на всю страницу\n[+] Симулятор\n    Добвлена поддержка Фортификатора для защищающегося флота\n[+] Админка/Список игроков\n    Добавлены  две  колонки  со  сведениями  о  реферралах  игрока:  количестве\n    привлеченных игроков и количество заработанных ими ТМ\n[+] Чат\n    Добавлены смайлики\n    Произведена замена цветов для лучшей читаемости сообщений: red  ->  maroon,\n    blue -> cyan\n[+] Постройки\n    Теперь на луне можно строить Нанофабрику\n\n[-] Шпионаж\n    Временно отключен вывод технологий при  шпионаже  -  до  переделки  системы\n    шпионажа\n[-] Админка\n    Временно ограничен доступ к некоторым  админским  страницам  Модераторам  и\n    Операторам - до переделки системы доступа\n\n[~] Обзор Империи\n    Теперь не показываются \"пустые\" строчки для юнитов, которых нет в Империи\n    Для производства ресурсов и складов используется структура 'caps' планеты\n    Правильно считается общее количество полей на всех объектах Империи\n[~] Артефакты\n    Теперь  после  операций  по   покупке/применению   Артефакта   страница   с\n    соответствующим списком открывается на последнем Артефакте\n[~] Сообщения\n    Теперь если есть URL перехода после сообщения есть возможность  перейти  на\n    соответствующую  страницу  по  ссылке  \"Продолжить\"  под   сообщением,   не\n    дожидаясь таймаута\n[~] Рекорды\n    В несколько раз ускорена страница Рекордов\n[~] Скины\n    Заменена картинка \"Черетеж ТОП\"\n\n[%] Админка/Список пользователей\n    Убрана отладка\n[%] Навбар\n    Исправлено смещение надписи в индикаторе исследования влево\n[%] Настройки пользователя\n    Названия групп настроек отцентрированы\n[%] Админка/Обслуживание\n    Исправлена ошибка удаления покинутых планет\n[%] Сообщение\n    Исправлена ошибка отправки сообщения об окончании строительства  на  верфи.\n    Теперь сообщение отправляется один раз, а не каждый  раз,  когда  на  верфи\n    строится юнит\n[%] Меню\n    Исправлена смена названия пункта меню \"Настройки\" на \"Опции\" при заходе  на\n    страницу Альянса\n[%] Экспедиции\n    Исправлена редкая ошибка при которой можно было отправить экспедиций больше\n    максимального количества\n[%] Вселенная\n    Исправлен показ места в статистике и показ кнопки-ссылки на статистику  для\n    скрываемых из статистики пользователей (по умолчанию к таким  пользователям\n    относится команда сервера)\n[%] Боевой отчет\n    Исправлена ошибка открытия неправильной системы во Вселенной при  клике  на\n    координаты в отчете\n[%] Локализация/Английский\n    Исправлены сообщения боевого отчета\n[%] Локализация/Русский\n    Исправлены некоторые очепятки\n[%] Чёрный Рынок\n    Исправлена невозможность продать/купить ТОП на ЧР\n[%] Флоты\n    Исправлена ошибка при приглашении в САБ самого себя\n    Исправлено ошибочное сообщение \"неисследованное пространство\"  в  заголовке\n    страницы\n    Исправлена ошибка отправки флота дальше, чем позволяет запас топлива\n\n[@] Код\n    Расчеты уровня премиума вынесены в модуль\n    Изменены некоторые SQL-запросы\n    Добавлен простенький бенчмарк\n    infos.php  теперь  использует  прямое  обращение  к  production   юнита   и\n    подмассиву modifiers\n    eco_bld_structures.php теперь использует обращение к подмассиву modifiers\n    mercenaries и plans перенесены из таблицы powerup в таблицу unit\n    Константа MAX_OVERFLOW исключена из кода\n    Обработан eco_get_planet_caps и связанные процедуры\n    Добавлена функция вычисления случайного числа, распределенного нормально\n[@] Код/БД\n    Артефакты перенесены из таблицы игроков в таблицу юнитов\n    Удалены лишние поля Технологий из таблицы игрока\n    Добавлены констраинты в некоторые таблицы\n    Удалена колонка `que` из таблицы `users`\n    premium перенесен из таблицы powerup в таблицу unit\n    Исследования и очередь исследований перенесены в соответствующие таблицы\n[@] Код/JS\n    Переписаны некоторые процедуры fleet.js на использование jQuery\n[@] MVC\n    $sn_i18n['pages'] -> $sn_mvc['i18n']\n[@] Обслуживание\n    Процедура обслуживания теперь так же удаляет боевые отчеты  UBE  старше  60\n    дней\n[@] Модули\n    Изменен алгоритм слияния массивов переменных в модулях\n\n\n\nProject \"SuperNova.WS\" Release 36 \"UBEv4 captains chat Happy New Year 2013!!!\"\n~~\n[#] Модуль \"Капитаны\"\n    Требуется СН не ниже 36a0.24\n    Капитан - это опытный командующий, который летает с флотами и за счет более\n    тонкого  управления  флотами  улучшает  эффективные   характеристики   всех\n    кораблей\n    Найм и управление Капитанами осуществляется  через  пункт  меню  \"Капитаны\"\n    (сразу под \"Наемниками\")\n    Каждый Капитан привязан к определенной планете или луне. Нельзя иметь  двух\n    Капитанов на одном небесном теле. Капитан,  летящий  с  флотом,  все  равно\n    считается привязанным к планете\n    Капитана   можно   перевозить   с   одной   планеты   на   другую   миссией\n    \"Передислокация\". При этом на время перелёта Капитан считается  привязанным\n    сразу к обоим планетам - стартовой и финишной\n    С флотом можно отправить только одного Капитана\n    При  гибели  флота  Капитан  так   же   погибает.   Под   \"гибелью   флота\"\n    подразумевается  уничтожение  всех  кораблей  флота.  Это  верно  как   для\n    атакующих флотов, так и для флотов, стоящих в удержании\n    Капитан на  планете  не  участвует  в  защите  планеты  при  атаке  -  этим\n    занимается Фортификатор. Зато при  полном  уничтожении  всего  планетарного\n    флота такой Капитан не погибнет\n    За каждый выигранный простой бой (САБы и миссия \"Уничтожить\" не  считаются)\n    Капитан  атакующего  флота  получает  1  пункт  опыта.  За   \"победы\"   над\n    неактивными игроками опыт не начисляется. Так же не начисляется опыт,  если\n    бой закончился выигрышем атакующего за 1 раунд\n    Капитаны всегда улучшают характеристики кораблей своего флота -  даже  если\n    участвуют в бою, за который они  не  получат  опыта:  атака  на  неактивных\n    игроков, удержание, САБ, уничтожение луны и т.д.\n    При наборе определенного количества опыта Капитан получает  новый  уровень.\n    Чем выше уровень - тем больше опыта нужно для получения следующего уровня\n    Повышение в уровне  дает  возможность  улучшать  умения  Капитанов.  Каждый\n    уровень умений дает 1% к базовому значению соответствующей характеристики\n    Умения Капитана включают бонусы к щитам, броне и атаке\n    Уровни Капитана вкладываются в умения один раз и навсегда - поэтому заранее\n    тщательно планируйте развитие своего Капитана\n    Уровни Капитанов указываются в списке юнитов на Обзоре Империи.  На  заднем\n    фоне ячейки с уровнем выводится прогресс-бар развития Капитана  с  цветовым\n    кодированием:\n      Пустая ячейка - Капитан не нанят, либо только что получил уровень\n      Красный прогресс-бар - до следующего уровня осталось больше 50% опыта\n      Оранжевый - не меньше 50% опыта, но меньше 80%\n      Желтый - не меньше 80% опыта\n      Зеленый - в следующем бою Капитан получит новый уровень\n    В списке флотов на странице \"Флоты в полёте\" и для своих флотов на странице\n    \"Обзор планеты\" если во флоте есть Капитан перед  количеством  кораблей  во\n    флоте высвечивается \"*\", а в попапе состава показывается его уровень\n[#] Модуль \"Продвинутый чат\"\n    Требуется СН не ниже 36a1.7\n    Встроенная система команд с поддержкой алиасов команд\n    Встроенная система помощи по командам чата - команда /help\n    Добавлен список  игроков  в  чате  с  дополнительными  иконками  статуса  и\n    командами управления для админов\n    Возможность игрокам управлять своим состоянием видимости в чате  -  команда\n    /invisible. Администрация сервера (authlevel > 0) всегда видит невидимок\n    Возможность  отправлять  приватные  сообщения  другим  игрокам  -   команда\n    /whisper. Приватные сообщения выделяются специальным образом, видны во всех\n    каналах  и  сохраняются  в  истории  чата.  В  приватных  сообщенях  нельзя\n    употрблять форматирование цветом\n    Администраторы  имеют  возможность  запретить  игроку  писать  в   чат   на\n    определенный срок или вернуть такую возможность -  соответственно,  команды\n    /mute и /unmute. Запрет распространяется на все  каналы  и  на  возможность\n    писать личные сообщения. Соответствующая иконка в списке игроков лишает его\n    права голоса на 1 час\n    Администраторы имеют возможность блокировать и разблокировать игроков прямо\n    из чата - соответственно, команды /mute и /unmute. Иконка в списке  игроков\n    банит его на 1 неделю\n    Максимальное время нахождения игрока в списке онлайн совпадает с  таймаутом\n    чата на странице сервера - т.е. в  списке  онлайн  игрок  будет  виден  еще\n    некоторое время после выхода из чата\n\n[!] UBEv4\n    Написан с нуля боевой движок и боевые отчеты\n    Особенности подготовки к бою:\n    1. Бой теперь считается не по $time_now, а по времени прилета  флота  -  на\n    случай, если бой сильно отложенный. Например, при сбоях движка  или  низкой\n    активности  сервера.  Так  будут  отработаны  корректно  все  удержания   в\n    правильное время\n    Особенности хода боя:\n    1. Броня не регенерируется между раундами\n    2. Если броня упала  ниже  75%  -  корабль  имеет  шанс  взорваться  равный\n    проценту от общего здоровья\n    3. Новый механизм боя: подлов атакующего или sneak defense. Если в САБе и в\n    удержании участвуют флоты одного и того же  игрока,  то  прилетающие  флоты\n    этого игрока будут сражаться на стороне защитника. Аккуратно смотрите, кого\n    приглашает в САБ. Хе-хе\n    Особенности подведения итогов боя:\n    1. Если в бою участвует хотя бы один флот Админов с любой стороны - лом  не\n    выпадает ни с кого!\n    2. Возвращение обломков с оборонных сооружений не производится\n    3. В миссии  \"Уничтожить\"  шанс  уничтожения  флота  от  взрыва  одного  из\n    кораблей при попытке уничтожить луну теперь так же  зависит  от  количества\n    гравидвигателей во флоте - чем их больше, тем шанс выше\n    4. В миссии \"Уничтожить\" корабли могут взорваться даже в  случае  успешного\n    уничтожения  луны.  Как  и  раньше,  подрыв  кораблей   с   гравидвигателем\n    уничтожает весь флот\n    5. Теперь в рейдовый опыт засчитываются исключительно одиночные  атаки.  Ни\n    \"Удержание\", ни \"САБ\" не засчитывается. Т.е. вообще не засчитываются -  вне\n    зависимости от результата боя\n    6. Теперь атаки на неактивных игроков (\"i-шки\") не приносят рейдовый опыт\n    7. Количество свободных полей на луне зависит от её размера и  определяется\n    по формуле Размер/1000 с округлением вверх до целого\n    8. Изменен расчет поля обломков. Теперь на орбите оказывается от 30% до 70%\n    выброшенных за  борт  ресурсов  и  от  20%  до  40%  обломков  кораблей.  В\n    детерминированном симуляторе процент обломков на орбите всегда равен 30%, а\n    обломки, выброшенные из трюма всегда составляют 50% от потерь\n    9. Шанс уничтожения луны теперь всегда лежит в пределах 1%-99%\n    Боевой отчет теперь состоит из трёх частей: \"Основная  информация  о  бое\",\n    \"Боевые потери\" и лог раундов\n    \"Основная информация о бое\" показывает:\n    1. Время проведения боя (если доступно)\n    2. Место боя (если доступно) - координаты планеты, её тип и имя\n    3. Результат боя (выигрыш атакующего, ничья, проигрыш атакующего)\n    4. Обломки на орбите\n    5. Шанс образования луны и результат такой попытки\n    6. (Для миссии  \"Уничтожить\")  Состояние  кораблей  с  гравидвигателями  по\n    итогам боя. Шанс уничтожения луны оставшимися кораблями и  результат  такой\n    попытки. Шанс взрыва кораблей и итог миссии\n    Раздел \"Боевые потери\" показывает:\n    1. (На планетах) Количество восстановленных боевых сооружений\n    2. Общие потери боевых единиц каждого из участвующих в бою игроков. Если  у\n    одного игрока участвовало в бою несколько флотов - будут показаны суммарные\n    потери по всем флотам. Это верно для всех параметров в  этом  разделе.  Для\n    планетарной обороны в потери не включаются восстановленные единицы\n    3. (В случае победы атакующих) Количество ресурсов, вывезенных  с  планеты.\n    Для  планеты  это  будет  положительное  число,  для  атакующих  флотов   -\n    отрицательное\n    4. (Для флотов) Количество ресурсов  потерянных  из-за  уменьшения  емкости\n    трюмов вследствии уничтожения части флота. Эти ресурсы рассматриваются  как\n    \"боевые потери\" - они  плюсуются  к  обломкам  на  орбите  и  к  потерям  в\n    пересчете на ресурсы\n    5. Общие потери в пересчете на ресурсы. Включает стоимость боевых единиц на\n    момент боя, вывоз с планеты и ресурсы, потерянные из-за уменьшения трюмов\n    6. Общие потери в ресурсах в пересчете на металл по курсу Черного Рынка  на\n    момент проведения боя. Писькомерка для сравнения\n    \"Лог раундов\" показывает результаты расчета каждого раунда для всех флотов\n    1.  Показывает  координаты  и  тип  планеты,  с  которой  прилетели   флоты\n    атакующих/защитников\n    2.  Расширено  количество  информации  о  боевых  подраздеениях   Добавлена\n    информация о \"Пробое\" и \"Уроне\". \"Пробой\" - атака, которая пришлась на щиты\n    и была ими поглощена (или пропущена - см. ниже). \"Урон\"  -  атака,  которая\n    пришлась на броню\n    3. Цветовое кодирование информации о подразделениях:\n       Зеленый означает, что вся атака в раунде поглощена щитами\n       Желтый - часть атаки пробила щиты (\"пробой\") и нанесла урон по броне, но\n       при этом ни одна боевая единица не уничтожена\n       Оранжевый - один или более боевых единиц уничтожено\n       Красный - все оставшиеся боевые единицы уничтожены в этом раунде\n       Число  в  скобках  в  столбце  потерь  -   количество   боевых   единиц,\n       взорвавшихся в раунде из-за фатальных повреждений\n    В боевом отчете координаты планет являются ссылками на Вселенную\n    Доработан симулятор для поддержки изменений в UBEv4:\n    1. Стандартный режим работы симулятора - полная определенность  результатов\n    в зависимости от начальной конфигурации (галочка \"Симуляция\" включена)\n    2. Добавлен второй режим работы  -  недетрминированный  симулятор  (галочка\n    \"Симуляция\" отключена). В этом режиме работы  проводится  полная  симуляция\n    боя (включая образование луны) с применением генератора случайных  чисел  -\n    т.е. так, как происходил бы обычный бой. В  этом  режиме  результаты  могут\n    сильно отличаться от симуляции к симуляции. Так же в этом режиме происходит\n    запись боевого отчета с результатом симуляции в БД\n    3. В  стандартном  режиме  если  шанс  образования  луны  больше  1  всегда\n    образуется луна со средним размером для данного шанса\n\n[+] Меню\n    Редизайн меню\n    Изменен порядок расположения пунктов\n    Высота пункта меню увеличена до 16 пикселов\n    Добавлены иконки. Размер иконки ограничен 14 пикселами в высоту\n\n[~] Чат\n    Добавился новый BBCode \"s\" - зачёркнутый текст\n    В чате Альянса в нике участника теперь не указывается Альянс\n    Переформатирован вывод списка смайлов. Список смайликов теперь генерируется\n    автоматически из всего доступного списка\n    При открытии окна чата курсор позиционируется в строку набора сообщения\n    Реформатирование HTML-кода страницы чата\n    Переделан в preMVC\n    Три файла чата интегрированы в один\n[~] Новости\n    Количество новостей ограничено 20-ю самыми свежими\n    На странице Обзора планеты добавлена подсказка как закрыть окно со  свежими\n    новостями\n[~] Флоты\n    Изменены ограничения на отправку Шпионов. Их можно посылать  в  одиночку  в\n    миссии Шпионаж, Передислокация и Транспорт. Во все остальные миссии Шпионов\n    тоже можно отсылать - но только в сопровождении других кораблей\n[~] Регистрация: Пароль теперь указывается в отдельной строке\n[~] Восстановление пароля: Новый пароль теперь указывается в отдельной строке\n[~] Админка/Бан:  Теперь  срок  бана  инкрементальный  -  каждый   новый    бан\n    увеличивает срок блокировки, а не заменяет его\n[~] Админка/Список игроков: Для забаненных игроков при наведении на ячейку бана\n    выскакивает тултип с ником забанившего, датой и причиной бана\n\n[%] Альянсы\n    Исправлена надпись при отправке письма членам Альянса\n[%] Обзор Империи\n    Исправлена пропажа индикации количества строящихся зданий\n    Исправлена ошибка смещения фона для производящих зданий\n[%] Флоты/Фаланга\n    Исправлена ошибка сканирования пустого места во Вселенной\n[%] Навбар\n    Изменена разметка навбара, что бы его не перекашивало в случае вывода блока\n    информации до него\n    Исправлена ошибка неправильного цветового кодирования  остатков  энергии  в\n    планетбаре\n    Исправлена ошибка смены планеты на preMVC-страницах\n[%] Исследования\n    Добавлен патч, исправляющий багоюз на медленных MySQL серверах\n[%] Чёрный Рынок\n    Исправлена несовместимость с Opera 12.x\n    Исправлена уязвимость в Скупщике лома\n[%] Наемники: Исправлена ссылка на дерево развития\n[%] Чат: Исправлена ошибка появленя всех сообщений пользователя  в  чате  после\n    его удаления\n[%] Отпуск: Исправлено сообщение об ошибке на загрушке отпуска\n[%] Админка/Обслуживание: Исправлена ошибка обслуживания базы\n\n[@] Модули\n    Устранена ошибка на случай, если модуль не возвращает темплейта. Такого  не\n    должно происходить в нормальной ситуации\n[@] Код\n    Переформатирован HTML-код обзора планеты\n    Поддержка опций движка в глобальном объекте $supernova\n    Поддержка опций на страницах в подмассиве 'pages'\n[@] БД\n    В таблицу флотов добавлены идентификаторы планет старта/назначения\n[@] Документация\n    Обновлен конвертер txt2html\n\n\n\nProject \"SuperNova.WS\" Release 35 \"MVC race reparse teleportation recycle\"\n~~\n[#] Модули: Расы\n    Шесть фиксированных рас: земляне, луниты, меркурианцы, венериане, марсиане,\n    республиканцы\n    Иконка расы отображается в чате,  в  статистике,  в  попапе  информации  об\n    игроке во Вселенной и на странице Императора. Удержание курсора над иконкой\n    расы вызывает тултип с её названием. Клик - открывает страницу с  описанием\n    всех рас\n    Каждая раса имеет собственные бонусы. Бонусы рас действуют сразу  же  после\n    выбора родного мира - не нужно, например, исследовать техи, что бы получить\n    к ним бонус\n    Раса выбирается после регистрации на странице Императора\n    Первый выбор расы производится бесплатно, каждая смена расы  стоит  100.000\n    ТМ\n    Описание текущей расы доступно на странице Императора. Там же  есть  ссылка\n    на описание всех рас в игре с указанием их символов\n[#] Модули: Расовые юниты - требуется наличие модуля \"Расы\"\n    Шесть уникальных юнитов - по одной каждой из рас:\n    Земная \"Лень\" - боевой солнечный спутник\n    Лунная \"Зависть\" - легкий бомбардировщик\n    Меркурианское \"Обжорство\" - емкий переработчик\n    Венерианский \"Гнев\" - истребитель-перехватчик\n    Марсианская \"Гордыня\" - усовершенствованный линейный крейсер\n    Республиканская \"Жадность\" - боевой транспорт\n\n[!] Телепортация планеты - новая возможность, доступна на  странице  управления\n    планетой\n    Телепортация может производится только на свободное место - там, где нет ни\n    планет, ни лун, ни обломков, включая уничтоженные объекты\n    Телепортация перемещает  в  новые  координаты  планету  вместе  с  флотами,\n    находящимися на орбите планеты\n    Если у планеты есть луна - она  так  же  перемещается  в  новые  координаты\n    вместе с флотами\n    Телепортация  невозможна,  если  в  окрестностях  планеты   есть   какая-то\n    активность флотов (т.е. есть флоты, имеющие в  качестве  точки  отправления\n    или назначения саму планету, луну или поле обломков)\n    После телепортации  необходимо  выждать  некоторое  время  перед  следующей\n    телепортацией -  нарушенная  метрика  пространства  вокруг  планеты  должна\n    нормализироваться\n    Стоимость телепортации и таймаут перед следующим прыжком задаются в таблице\n    `config` соответственно переменными 'planet_teleport_cost' (по умолчанию  -\n    50.000 ТМ) и 'planet_teleport_timeout' (по умолчанию - 1 сутки)\n[!] Перенос столицы  -  новая  возможность,  доступна  на  странице  управления\n    планетой\n    Теперь любая планета может быть назначена столицей\n    Стоимость переноса столицы по умолчанию составляет 25.000 ТМ. Она  задается\n    в таблице `config` переменной 'planet_capital_cost'\n[!] МПР\n    Изменение алгоритма ракетного удара\n    Алгоритм  ракетного  удара  теперь  не  привязан  к   численным   значениям\n    идентификаторов юнитов и сильно оптимизирован по скорости\n    Теперь при атаке МПР учитываются  щиты  оборонных  сооружений.  Это  должно\n    слегка  уменьшить  эффективность  ракет  и  повысить   живучесть   защитных\n    сооружений с большим количеством щитов\n    Теперь при ракетном ударе рандомизируются параметры атаки, брони и щитов  у\n    соответствующих юнитов. Границы такие же, как и для сражений  флотов  -  от\n    80% до 120%\n    В результате изменений в алгоритме существенно повысилась живучесть ПЗ  при\n    ракетном ударе\n    Добавлена поддержка усиления залпа для МПР\n[!] Переработка\n    Полностью переделана работа с полем обломков\n    Полностью переписан алгоритм запуска переработчиков\n    В  попапе  вместе  с  абсолютными  теперь  показываются   и   относительные\n    значениями в процентах\n    В попапе добавилось три строки:\n    1. Строка \"В полете\" показывает емкость трюмов переработчиков пользователя,\n    которые уже летят на данное поле\n    2. Строка \"На орбите\" показывает емкость переработчиков на  орбите  текущей\n    планеты или луны\n    3. Строка \"К переработке\" показывает сумму двух предыдущих строк\n    На  основном  экране  Вселенной  к  иконке  обломков  добавлена   индикация\n    процентного значения из строки \"В полете\". Она имеет цветовое кодирование:\n    1.  Зеленый  цвет  означает,  что  прибывающие   флоты   игрока   полностью\n    переработают поле обломков на ресурсы\n    2.  Желтый  цвет  означает,  что  к   полю   летит   некоторое   количество\n    переработчиков, которых не хватит что бы целиком переработать  обломки,  но\n    на  текущей  планете  есть  достаточно  переработчиков,  что  бы  полностью\n    обработать поле\n    3. Оранжевый означает, что к полю летит флот иргока с  переработчиками,  но\n    их не хватит на полную обработку обломков, даже  включая  те  корабли,  что\n    находятся на орбите\n    4.  Красный  цвет  значит,  что  к  полю  обломков  не  летит   ни   одного\n    переработчика игрока\n[!] Статистика\n    Теперь можно управлять появлением игроков  в  статистике  и  рекордах.  Для\n    этого на странице настроек сервера появились дополнительные настройки.  Они\n    размещаются в разделе \"Статистика и рекорды\"\n    Отключение настройки \"Прятать админов\" добавит в статистику и рекорды  всех\n    пользователей с  authlevel > 0. По умолчанию она включена\n    Настройка \"Прятать игроков\" позволяет указать  через  запятую  перечень  ID\n    игроков, которые не будут участвовать в статистике и  рекордах.  Это  может\n    быть полезно для создания NPC - ботов или  игроков,  которые  исполняют  их\n    роли\n    Так  же  в  этот  раздел  вынесена  настройка  расписания   автоматического\n    обновления статистики. ВНИМАНИЕ!!! КРАЙНЕ НЕ РЕКОМЕНДУЕТСЯ МЕНЯТЬ  ЗНАЧЕНИЕ\n    ПО УМОЛЧАНИЮ!!!\n    Добавлена опция \"Скрывать  ссылки  на  ЛС\".  При  её  включении  в  таблице\n    статистики не показывается URL на создание личного сообщения игрокам\n    Теперь переход по определенной позиции  (например  со  страницы  Вселенной)\n    скроллирует страницу сразу на эту позицию\n    Немного уменьшен размер страницы статистики\n[!] MVC\n    Базовая поддержка MVC - встроенная система моделей и видов\n    Все страницы, переделанные под MVC, перемещены в /includes/pages\n[!] Рендерер имен\n    Добавлен механизм рендеринга имени пользователя\n    Чат, статистика, Вселенная и страница Императора  теперь  используют  общий\n    механизм рендеринга имени пользователя\n[!] Чат\n    Переписан чат\n    Чат теперь инкрементальный - с сервера передается не всё содержимое чата, а\n    только новые сообщения. Чат  корректно  работает  когда  у  игрока  открыто\n    несколько окон с чатом\n    Исправлена проблема со скроллированием чата в Chrome v20+\n    Теперь при отключении чата по таймауту содержимое окна не  стирается,  а  в\n    него добавляется соответствующее сообщение. Так же прячутся элементы ввода:\n    выбор цветов, строка сообщения, кнопка \"Отправить\" и панель смайлов\n    Основное окно чата переписано под preMVC\n    Новый код чата (как JS, так и PHP) заметно компактнее, аккуратнее и быстрее\n    старого\n    Увеличена длина поля для ника в чате\n[!] Дерево технологий\n    Полностью переписано дерево технологий (бывш. techtree.php)\n    Рядом с названиями юнитов там, где это имеет смысл, отображаются их  уровни\n    в Империи/на текущей планете\n    Теперь вместо полного уровня с учетом бонусов отображаются отдельно базовые\n    уровни и отдельно бонус к ним\n    Добавлена  поддержка  дополнительных  требований  к  строительству   юнитов\n    (например - модуля расовых юнитов)\n[!] Заметки\n    С нуля написаны заметки. Что еще сказать?\n[!] Друзья\n    Страница друзей написана с нуля\n    Теперь подробно сообщается  обо  всех  ошибках  и  результатах  операций  с\n    заявками\n    В личную почту отправляются сообщения по  приходу,  принятию  и  отверганию\n    заявки, а так же при разрыве дружеских отношений\n    Цветовое кодирование статуса друга: зеленый - онлайн, желтый -  бездействие\n    от 5 до 15 минут, оранжевый - оффлайн, красный - оффлайн более суток\n[!] Модули\n    Поддержка ali_ally_player 12a0\n    Поддержка player_premium 1b0\n[!] Новапедия\n    Полностью написана с нуля страница информации о юнитах\n    Теперь  в  Новапедии  показываются  требования  для  постройки/исследования\n    юнита\n    Теперь для корабля показываются данные для всех типов  двигателей,  которые\n    возможно на него установить\n    Улучшено отображение информации о кораблях и обороне\n[!] Поиск\n    Полностью переписан поиск\n    Добавлена подсказка\n    Добавлена сортировка по Альянсу, имени игрока, имени планеты\n    Настройка сервера \"Скрывать ссылки на  ЛС\"  теперь  распространяется  и  на\n    результаты поиска\n[!] Документация\n    Вся документация сконвертирована в UTF-8\n\n[+] Меню\n    Добавлен пункт меню \"Тёмная материя\"\n    Добавлена возможность добавления иконки к пункту меню.  Иконки  берутся  из\n    подкаталога 'icons' текущего скина\n    Добавлена прямая поддержка CSS-стилей для элементов меню\n    Под логотип сервера в ALT вместо 'supernova.ws' подкладывается имя сервера\n[+] Обзор Империи\n    Значительно оптимизирован HTML-код\n    Размер HTML-кода уменьшен на величину от 30% и в отдельных случаях до  80%.\n    Среднему игроку оптимизация даст уменьшение размера загружаемого  файла  на\n    40-50%% (включает так же выигрышь от оптимизации Списка планет - см.ниже)\n    В колонку \"ИТОГО\" добавлена сумма по строящимся и  прибывающим  на  планеты\n    юнитам\n    Теперь юниты всегда групируются согласно их принадлежности.  Например,  при\n    подключении модуля расовых юнитов они добавляются в категорию \"Флот\", а  не\n    как ранее в конце таблицы\n[+] Список планет\n    Значительно оптимизирован HTML-код\n    В Списке планет убрана иконка, отвечающая за исследование вследствие полной\n    бессмысленности\n[+] Реклама\n    Добавлена возможность управлять мета-тегами 'description' и 'keywords'  без\n    редактирования темплейта! Их содержимое хранится в таблице `config` в полях\n    `adv_seo_meta_description` и `adv_seo_meta_keywords` соответственно\n[+] Модули\n    Система модулей переписана с учетом базовой поддержки MVC\n    Автоматическая загрузка языков\n[+] Планетарные врата\n    Интерфейс переделан по примеру страницы \"Флоты на орбите\"\n[+] Вселенная\n    Шаблоны попапов легенды, планет, лун, обломков, игроков и альянсов вынесены\n    из JS-скрипта в шаблон страницы\n    Убрано количество летящих флотов - эта информация есть в навбаре\n    Полностью переписана работа AJAX-части,  отвечающей  за  отправку  шпионов,\n    переработчиков и ракет\n    Количество переработчиков теперь включает все виды кораблей, которые  могут\n    перерабатывать обломки\n    Интерфейс  запуска  ракет  использует  группу  защитных  сооружений,  а  не\n    хард-кодед перечень, как было раньше\n    В попап легенды добавлены расшифровки для иконок действия\n    В попапе планеты показывается её диаметр\n    В попапе луны миссия  \"Уничтожить\"  показывается  только  если  на  текущей\n    планете игрока есть ЗС\n    Из попапа игрока убраны ссылки - все, что можно было  сделать  по  ссылкам,\n    можно теперь\n    Добавлено новая  иконка  действия  -  \"Статистика\".  Её  тултип  показывает\n    статистику игрока\n    Расширена подсказка\n[+] Смайлы\n    В чат добавлены следующие смайлы: nea, ups, quote, shout, sorry, spiteful\n\n[~] Обзор планеты\n    Переписана процедура отсылки переработчиков\n    Теперь выводится результат отсылки переработчиков\n[~] На странице \"Тёмная материя\" дополнен список возможного использования ТМ  и\n    проставлены ссылки на соответствующие страницы\n[~] HTTPS\n    Теперь СН нормально работает и по HTTPS протоколу\n[~] Император\n    Со страницы убраны баннер и юзербар\n[~] UBEv3\n    Улучшена поддержка залпового огня\n[~] Юниты/Корабли\n    Небольшой ребаланс кораблей\n    СуперНова - атака уменьшена на  порядок.  При  этом  боевая  эффективность\n    корабля изменилось незначительно, благодаря изменению коэфициента  усиления\n    залпа. Немного уменьшилась эффективность против наземной обороны  и  легких\n    кораблей и увеличилась эффективность против средних кораблей\n    Бомбардировщик: понижена эффективность против ионных орудий  и  повышена  -\n    против плазменных\n[~] Исследования\n    Добавлена индикация бонусных уровней (например, от  премиума)  на  страницу\n    исследований\n[~] Наемники\n    Добавлена индикация бонусных уровней (например, от  премиума)  на  страницу\n    наемников\n[~] Стили\n    Цвет бонусов изменен с \"yellow\" на \"gold\" - это даст  возможность  отличать\n    их от, например, прибывающих на планету юнитов\n[~] Локализация\n    К эффектам Технолога и  Фортификатора  добавлена  информация  о  добавлении\n    слотов к очередям\n[~] Флот\n    Полностью локализована страница приглашения в САБ\n[~] Регистрация\n    Изменено сообщение при регистрации игрока\n[~] Тёмная Материя\n    Немного переработан интерфейс страницы\n[~] Админка/Утилиты\n    В шифрование паролей добавлен генератор паролей\n[~] Инициализация\n    Изменен алгоритм определения корневого каталога СН. Теперь движок корректно\n    работает в каталогах-симлинках\n\n[%] Фаланга\n    Теперь нельзя сканировать удаленную планету\n[%] Локализация\n    Исправлено описание фаланги\n    Исправлена ошибка чтения информации о локализации\n    Небольшая правка опечаток в русской локализации\n[%] Альянсы\n    Исправлена ошибка отображения полей информации Альянса при наличии  лого  и\n    отсутствии внешнего текста Альянса\n[%] Вселенная\n    Исправлена опечатка в легенде\n    Исправлена ошибка с перебросом по координатам [1:1:1] при выборе планеты из\n    выпадающего меню\n[%] Обзор планеты\n    Добавлена проверка на уровень губернатора при его отображении - если  вдруг\n    при прямых манипуляциях в базе у планеты есть ИД губернатора,  но  нет  его\n    уровня. В нормальных условиях такого произойти не может\n[%] Флот\n    Исправлена ошибка отправки корабля, если его ID больше 300 или меньше 200\n    Теперь невозможно пригласить в САБ игрока, на которого летит этот САБ\n[%] UBEv3\n    Исправлена ошибка с определением кораблей с  ID  >  300  как  защиты  и  их\n    восстановлением. Ошибка проявляется только в сторонних модулях\n[%] Альянсы\n    Исправлена проблема с отсылкой сообщений всему Альянсу\n[%] Корабли\n    Исправлено нулевое потребление некоторых юнитов (в частности  -  шпионского\n    зонда)\n\n[@] Темплейты\n    Рендерер страницы теперь подхватывает заголовок страницы, если  он  есть  -\n    переменная PTL {PAGE_HEADER}\n    Содержимое переменной $template_result автоматически загружаетя в  темплейт\n    в файле index.php\n    Файл темплейта _result_message автоматически  подгружается  при  рендеринге\n    темплейта, если в структуре переменных темплейта есть массив 'result'\n[@] Модули\n    Изменена процедура инициализации  -  модули  теперь  грузятся  до  проверки\n    наличия  страниц.  Это  сделано  на  случай,  если  модуль  добавляет  свои\n    собственные страницы как, например, модуль Премиума и модуль Рас\n    Теперь можно указывать  в  качестве  страницы  загрузки  файла  локализации\n    пустое множество '' - файлы в этом массиве будут загружаться всегда\n    Поддержка  дерева  зависимости  модулей  -  теперь  можно  делать   модули,\n    зависящие от других модулей\n    Автоматическая загрузка зависимых модулей в правильном порядке\n[@] Код\n    Из файла vars.php выделены три  отдельных  файла  со  структурами,  боевыми\n    юнитами и всеми остальными\n    Так же добавлена дополнительная служебная информация для  того,  что  бы  в\n    симуляторе не пропадали защитные сооружения  при  добавлении  новых  юнитов\n    через модули\n    Убраны неиспользуемые данные \"скорострела\"\n    Из информации о боевых юнитов убраны ненужные данные о единичных усилениях\n    Численные значения для  защитных  сооружений  и  ракет  заменены  везде  на\n    константы\n    Везде  из  текста  убраны  ссылки  на  переменную  $GLOBALS  для  поддержки\n    рефакторинга в IDE\n    Библиотека \"tw-sack.js\" больше не используется - она заменена на jQuery\n    Исправлена очепятка в названии константы технологии ионного двигателя\n    Убран неиспользуемый код \"скорострела\"\n    js_safe_string() теперь корректно работает со строками,  где  есть  перевод\n    строки, включая Линуксовские и Маковские форматы файлов\n    sn_function_call теперь корректно отрабатывает несуществующие функции\n    Добавлена функция sn_get_groups()\n    eco_bld_tech.php теперь не использует $sn_data\n    Оптимизирован код Альянсов\n    Все страницы интерфейса игроков переписаны без использования $parse\n    Теперь в doquery() префикс {{table}} не используется и не обрабатывается\n    Страницы login.php, phalanx.php переписаны без использования $parse\n    JS: В объявлениях скриптов все конструкции  language=\"javascript\"  заменены\n    на type=\"text/javascript\"\n    Теперь  движок  может   работать   с   неограниченным   количеством   типов\n    кораблей-переработчиков\n[@] Меню\n    Заменены типы элементов меню на \"lang\" там, где это было возможно\n    Стандартное меню вынесено из файла template.php в includes/vars_menu.php\n    Парсер меню теперь понимает вложенные конструкции и константы для типа меню\n    'lang' - т.е. конструкции вида 'info[STRUC_MINE_METAL][description]'\n[@] MVC\n    Частичная поддержка структуры MVCv2 в init.php\n    Добавлена поддержка анонимных MVC-страниц в common.php\n    Добавлена поддержка MVC-страниц на страницы логина/регистрации\n\n\n\nProject \"SuperNova.WS\" Release 34 \"Happy Birthday Supernova! 3rd anniversary\"\n~~\n[!] Юбилей: 3 года назад 11 июня 2009 года был запущен первый сервер того,  что\n    превратилось в Проект \"СуперНова\"\n\n[#] Админка: Редактирование характеристик планеты\n    Модуль admin_planet_edit_extra v1c0\n    В  админке  можно  менять  основные   характеристики   планеты:   название,\n    изображение, размер, температуру, губернатора  и  его  уровень,  количество\n    обломков на орбите\n\n[#] Покупка ТМ: Модуль платежной системы XSolla\n    Модуль payment_xsolla_currency v1b0\n    Реализован протокол XSolla \"Виртуальная валюта\" (без отката платежей)\n    Поддержка плагина XSolla PayStation\n\n[#] Премиумный аккаунт\n    Модуль player_premium v1a2\n    Премиумный аккаунт покупается игроком за ТМ\n    Имеется 5 уровней премиума\n    Премиумный аккаунт дает  бонус,  эквивалентный  своему  уровню,  к  уровням\n    технологий и Наемников (не Губернаторов!)  Империи,  а  так  же  к  уровням\n    следующих зданий на планетах: ко всем шахтам и складам  ресурсов,  ко  всем\n    электростанциям, к Фабрике роботов, к Верфи, к Нанофабрике, к Лаборатории и\n    к Нанолаборатории\n    Ник обладателя премиумного аккаунта выделяется в чате отдельным стилем  (по\n    умолчанию - желтым цветом)\n\n[!] Файл \"/docs/release.txt\" приведен в соответствие с актуальной информацией\n[!] Покупка ТМ\n    Поддержка платежных модулей: таблица платежей,  базовые  настройки,  строки\n    локализации и константы\n    Интерфейс покупки ТМ (требуется хотя бы один модуль платежной системы)\n    Поддержка payment_xsolla_currency v1b0\n    Поддержка методов 'LINK', 'GET' и 'POST' в системах платежа\n[!] Админка: Воплощение\n    Теперь можно Воплотиться в любого игрока, посмотреть  игру  его  глазами  и\n    поуправлять игрой его ру... эээ... интерфейсом!\n    Воплощение доступно только Администраторам сервера\n    Воплотиться можно только в игрока меньшего уровня - т.е. нельзя Воплотиться\n    в такого же Администратора\n    Вложенные  Воплощения   недопустимы:   нельзя   Воплотиться,   будучи   уже\n    Воплощенным в кого-то. Сначала Развоплотитесь\n    Для Воплощения используйте соответствующую иконку в \"Списке игроков\"\n    При Воплощении изменяется только  onlinetime  пользователя.  Вся  остальная\n    информация (IP, User-agent итд) сохраняется\n    Для Развоплощения используйте соответствующий пункт меню или \"Выход\"\n    Если на аккаунте игрока есть ошибки,  или  игрок  заблокировае,  или  игрок\n    находится в отпуске, то попытке  Воплощения  будет  выведено  сообщение  об\n    ошибке/блокировке/отпуске,  которое  увидел  бы  игрок  на   вашем   месте.\n    Обновление страницы вернет вас в ваш аккаунт\n    После штатного Развоплощения (т.е. из меню, а не при ошибке и не из  игрока\n    в отпуске) Администратора возвращает на страницу списка игроков\n    ВНИМАНИЕ! Перед использованием Воплощения почистите куки в браузере!  Из-за\n    изменений в работе кукесов кэш браузера может содержать дубликаты куков\n[!] Чертежи\n    \"Чертеж\" - это  программный  пакет,  дающий  доступ  к  производсту  юнитов\n    определенного типа. Доступны следующие чертежи:\n    1. Здания: термоядерная электростанция\n    2. Корабли: супертранспорт, гипертранспорт, Звезда Смерти, \"СуперНова\"\n    3. Защитные постройки: планетарная защита\n    Чертеж  покупается  на  Империю,  после  чего  указанный  юнит  доступен  к\n    производству на всех планетах\n    Чертеж является перманентым\n    Чертежи заменяют Наемников в требованиях к постройке\n    Наемники \"Разрушитель\" и \"Ассасин\" сконвертированы соответственно в \"Чертеж\n    ЗС\" и \"Чертеж СН\". Остальные четрежи надо покупать самостоятельно\n[!] День Рождения: Добавлена подсистема подарков на ДР игрока\n    Игрок может ввести свой ДР на своей странице настроек. ДР вводится один раз\n    и  после  этого  не  может  быть  изменен.  Дата  проходит  валидизацию   в\n    соответствии с серверными настройками формата даты\n    Игрок с ДР на текущую дату будет отмечен специальной иконкой в статистике и\n    на странице Вселенной. При наведении на иконку всплывает подсказка с  датой\n    рождения\n    Амдинистратор сервера может назначить количество  ТМ  в  подарок  на  ДР  в\n    настройках (опция \"Подарок игроку на день  рождения\").  Если  это  значение\n    установлено в 0 - подарки отключены.\n    Выдача подарков происходит один раз в сутки  всем  игрокам,  день  рождения\n    которых находится не далее чем в \"Ретро-рождение\" дней от текущей даты.\n    При этом подарки выдаются только игрокам,  которые  на  момент  выдачи  уже\n    имели введенную дату рождения. Движок гарантированно начислит подарки  даже\n    если  ДР  пришелся  на  день  неактивности   сервера   (неисправность   или\n    обслуживание).\n    Такая система выбрана с одной стороны - что бы не обидеть игроков в  случае\n    проблем с сервером, а с другой стороны - что  бы  избежать  злоупотреблений\n    (например - ввести послезавтрашнюю дату ДР, на следующий день  получить  ТМ\n    за \"прошлый ДР\", а через день -  еще  и  за  \"нынешний\".  Такой  вариант  в\n    текущей системе начисления подарков не прокатит)\n[!] Навбар: Полностью переработан навбар.\n    Вся информация теперь выводится поверх кликабельных иконок  с  всплывающими\n    подсказками\n    Добавлена информация о текущих исследованиях пользователя\n[!] Админка: Список игроков\n    Полностью переписан \"Список игроков\" с использованием PTE\n    Сокращено количество строк локализации\n    Альянсы-игроки больше не выводятся в списке\n    Пишется полный срок бана\n    Для мультиаккаунтов подсвечиваются все адреса с одинаковым IP и  в  скобках\n    добавляется количество игроков с таким же адресом\n    Теперь невозможно удалить  игрока  того  же  уровня  -  для  предотвращения\n    разборок между членами команды одного уровня\n[!] Админка: Параметры MySQL сервера\n    В админку на страницу утилит  добавлен  вывод  информации  о  настройках  и\n    параметрах MySQL сервера\n[!] Админка: Шифрование пароля\n    Полностью переписана утилита шифрования пароля в MD5\n[!] Верфь: Полностью переписана работа верфи\n[!] Из данного файла удалена информация о ранних релизах (до момента разделения\n    чейнджлога на девелоперский и пользовательский, т.е. начиная с релиза 25  и\n    ранее) что бы исключить дублирование информации. Посмотреть эту  информацию\n    можно в девелоперском чейнджлоге /docs/changelog_dev.txt\n[!] Статистика\n    Полностью переписана страница вывода статистики игроков и Альянсов\n    Она теперь использует PTE\n    Полностью переписаны алгоритмы работы страницы\n    Для игроков добавлены отображение следующих  видов  статистики:  \"Проведено\n    боев\", \"Выиграно боев\", \"Проиграно боев\", \"Уровень за постройки\",  \"Уровень\n    за исследования\", \"Уровень  за  рейдерство\".  В  качестве  исходных  данных\n    используется информация из записей игроков (т.е. актуальная  информация  на\n    момент просмотра статистики), поэтому изменение для данных типов статистики\n    всегда будет равно 0\n[!] Покупка секторов на планете\n    Теперь можно за ТМ докупать дополнительные сектора на планете - один сектор\n    за раз, максимальное количество секторов не ограничено\n    Cектор можно купить в нескольких местах: в \"Обзоре планеты\", в  \"Управлении\n    планетой\" и на экране строительства зданий\n    Стоимость сектора для планеты -  геометрическая  прогрессия  с  количеством\n    секторов в качестве номера члена, БС = 1000 и Ф = 1.01\n    Ориентировочная стоимость покупи  1  сектора  на  планете  составляет:  для\n    планеты размером 100 секторов - 2678  ТМ,  150  секторов  -  4404  ТМ,  163\n    сектора - 5013 ТМ, 200 секторов - 7244 ТМ, 250 секторов  -  11913  ТМ,  300\n    секторов - 19493 ТМ, 330 секторов - 26508 ТМ\n[!] Строительство\n    Минимальное время постройки/исследования юнита уменьшено до 1 секунды\n\n[+] Корабли/Технологии\n    Бонус к скорости полета кораблей теперь вычисляется относительно требуемого\n    уровня технологии двигателя. При равной технологии пользователя бонус равен\n    нулю, при  отличной  -  разнице  уровней  между  требованиями  постройки  и\n    пользовательской умноженной на бонус  двигателя.  Если  уровень  технологии\n    пользователя  меньше,  чем  требуемый  уровень  (например,  для   кораблей,\n    купленных на Черном  Рынке),  то  корабль  получает  пенальти  к  скорости,\n    вычисляемое аналогично, но не более 95%\n    Пример.  Бомбардировщик  требует  Ионный  двигатель  6-го  уровня.  Базовая\n    скорость  полета  корабля  -  4.000.  Каждый  уровень   технологии   Ионных\n    двигателей дает 20% к скорости полета. Таким образом:\n      * При  технологии   Ионных  двигателей  8-го   уровня   скорость   полета\n        Бомбардировщика составит:\n          4.000 * (1 + (8 - 6) * (20 / 100)) = 4.000 * (1 + 2 * 0,2) = 5.600\n      * При технологии 6-го уровня - 4.000\n      * При технологии 3-го уровня\n          4.000 * (1 + (3 - 6) * (20 / 100)) = 4.000 * (1 - 3 * 0,2) = 1.600\n      * Без технологии пенальти к уровню будет равно 120%,  поэтому  вступит  в\n        силу ограничение:\n          4.000 * (1 + (0 - 6) * (20 / 100)) = 4.000 * (1 - 0,95) = 200\n    Технологии двигателей теперь  так  же  влияют  на  расход  топлива.  Каждый\n    уровень, выше требуемого, уменьшает расход  топлива  на  10%  от  бонуса  к\n    скорости за уровень, но не больше чем 50% от расхода. Каждый уровень,  ниже\n    требуемого - увеличивает расход на 20% от бонуса.\n    Например, для Бомбардировщика каждый уровень Ионного двигателя,  ниже  6-го\n    будет увеличивать расход  топлива  на  4%  до  12%  при  полном  отсутствии\n    технологии. Каждый уровень, выше 6-го будет уменьшать расход топлива на 2%,\n    вплоть до 25-го уровня, где вступит в силу ограничение.\n[+] Локализация: Добавлена система отката языков в случае, если включаемый файл\n    не найден в указанном месте движка/для указанного языка\n[+] Офицеры: Академик, Фортификатор, Инженер\n    Теперь Фортификатор добавляет +1 слот к  очереди  постройки  оборонительных\n    сооружений за каждый уровень. Вследствие этого фактор Губернатора измененен\n    с 1.00 до 1.25. Текущие Фортификаторы остались без изменений\n    Теперь Инженер добавляет +1 слот к очереди постройки кораблей и  зданий  за\n    каждый уровень. Кроме того, бонус к скорости строительства зданий  увеличен\n    до 10%. Вследствие этого БС увеличена до 500, фактор увеличен  до  1.65.  У\n    нанятых инженеров уровень понижен  в  два  раза  с  округлением  в  большую\n    сторону. В целом это означает, что Инженер стоит  дешевле  в  пересчете  на\n    эффективный уровень\n    Изменена логика работы Академика, Фортификатора и Инженера. Все они  влияют\n    на скорость постройки юнитов, однако раньше  зависимость  была  практически\n    экспоненциальная. Вдобавок слишком большой бонус от Академика в  Альянсе  в\n    сочетании с полностью  прокачанным  Наемником  у  игрока  мог  приводить  к\n    артефактам в работе исследований\n    Теперь бонус указанных офицеров - это процент увеличения скорости постройки\n    соответствующих  юнитов,  а  не  процент,  на  который  уменьшается   время\n    постройки. Т.е. это - слагаемое в знаменатели дроби. Если  говорить  совсем\n    просто: 100% бонуса от офицера уменьшают время постройки юнита  в  2  раза,\n    200% - в три раза, 300% - в четыре раза и так далее\n    В связи с данными изменениями  сняты  ограничения  на  максимальные  уровни\n    Фортификатора и Инженера. Кроме того, бонус Академика увеличен с 5% до 10%,\n    а его максимальный уровень - до 30\n[+] MVC: Базовая поддержка MVC\n[+] Постройки/Здания\n    На превьюшках зданий и в информационной панели  дополнительно  отображаются\n    бонусные уровни - включая таблицу расчета производства\n[+] Ресурсы\n    Выводятся бонусные уровни зданий\n\n[~] Альянсы: Немного оптимизирован код страницы Управления Альянсом\n[~] Флоты\n    На странице выбора миссии таблица загрузки ресурсов по умолчанию отключена\n    Добавлено дополнительное сообщение при совпадении планеты отправки и пункта\n    назначения\n    Добавлено дополнительное сообщение при попытке отправить незагруженный флот\n    с миссией \"Транспорт\"\n    Добавлено дополнительное сообщение при попытке отправить флот с ресурсами в\n    миссию, отличную от миссий \"Транспорт\", \"Передислокация\" и \"Колонизация\"\n[~] Очередь верфи\n    Теперь так же показывается количество юнитов, когда юнит один в очереди\n[~] Ракеты: небольшой ребаланс ракет\n    Емкость шахты увеличена до 12-и, а размер межпланетной ракеты  увеличен  до\n    3-х. Таким образом на один уровень шахты теперь влазит 12 перехватчиков или\n    4  ракеты  (вместо  10  перехватчиков  и  5   ракет,   как   было   ранее),\n    а количество ракет к перехватчикам на уровень шахты увеличилось до  3  к  1\n    вместо 2 к 1. Мощность ракет осталась прежней\n[~] Система: Усовершенствован способ определения корневого каталога игры\n[~] Админка/Редактирование планеты\n    При выбранной планете невозможно изменить её ID для  исключения  перезаписи\n    информации о текущей планете\n    Добавлено форматирование количества юнитов/ресурсов на планете\n    Поддержка admin_planet_edit_extra v1c0\n[~] Продажа ТМ: Доработан дизайн страницы. Добавлена информация о бонусах\n[~] Навбар: Виджет экспедиций теперь ведет на страницу отправки флотов\n[~] События флотов\n    Фаланга теперь показывает входящие и исходящие ракетные атаки\n    Индикатор атаки на планету теперь так же реагирует на ракетные атаки\n[~] Скины: Обновлен скин supernova-ivash\n[~] Меню\n    Добавлена дата запуска сервера (под логотипом). Для уже запущенных серверов\n    она  равна  дате  запуска  апдейта.  Она  хранится  в  таблице  `config`  в\n    переменной \"server_start_date\"\n[~] Воплощение: Теперь при Воплощении  в  забаненного  персонажа  администратор\n    автоматически развоплощается\n[~] Меню\n    Пункты \"Технология\" и \"Квесты\" перемещены в раздел \"Информация\"\n[~] Новости: Заголовок \"Новости\" таблицы актуальных  новостей  теперь  является\n    ссылкой - клик на него раскрывает список всех новостей\n[~] Обзор планеты\n    Переформатирован вывод статуса Ворот для луны\n    Убран таймер исследований вследствие его полной бессмысленности\n[~] Юниты/Гравитационная технология\n    Изменены   требования   и   цена:   теперь   для   исследования   требуется\n    Энергетическая  технология  12  уровня,  а  цена  исследования   составляет\n    100.000.000 металла, 100.000.000 кристаллов и 50.000.000 дейтерия\n[~] Черный Рынок/Инфотрейдер: Добавлена информация об уровне премиума\n\n[%] Своз ресурсов: Исправлена ошибка со смещением цветового кодирования емкости\n    транспортов на одну строку вниз\n[%] Локализация/EN: Исправлены очепятки\n[%] Статистика: Исправлено неотображение пола игрока при просмотре статистики с\n    экранов логина/регистрации\n[%] Меню: Исправлена ссылка с пункта \"Статистика\"\n[%] Флоты: Исправлена ошибка, дающая возможность отправить флот с  ресурсами  в\n    миссии \"Атака\" или \"Удержание\"\n[%] Очередь верфи и исследований: Исправлена ошибка с индикацией конца  очереди\n    - \"undefined\" вместо \"Очередь пуста\"\n[%] Админка: Закрыты защитой по authlevel не закрытые ранее файлы\n[%] Альянсы: Исправлена ошибка  незасчета  уровня  технологии  при  составлении\n    списка доступных к постройке юнитов на верфи (корабли и оборона)\n[%] Экономика/Строительство: Исправлен ошибка багоюз медленных  соединений  при\n    постройке зданий\n[%] Админка/Редактор локализаций\n    Добавлена поддержка констант SNC_VER_ в домене 'admin'\n[%] Исследования: Исправлена ошибка повторного начисления ресурсов  при  отмене\n    исследования\n[%] Артефакты\n    Исправлена ошибка, когда АКК мог быть развернут на  планете  с  уже  идущим\n    строительством\n[%] Флоты\n    Исправлена  мгновенная  скорость  полета  если  для   двигателей   кораблей\n    соответствующие технологии еще не исследованы (например - корабль куплен на\n    ЧР или найден в экспедиции)\n[%] Локализация/EN: Исправлены очепятки\n[%] Фаланга: Исправлена уязвимость в отправке флота\n[%] Исправлена ошибка вычисления стоимости Наемников\n[%] Исследования: Исправлена ошибка расчета времени для исследований Альянса  в\n    случае, если Альянсу известна технология МИС\n[%] Сообщения: Исправлена ошибка при попытке отправить сообщение без адресата\n[%] Исследования\n    Устранена ошибка  в  формуле  рассчета  скорости  исследования  технологии.\n    Теперь корректно рассчитывается время исследования для игроков и  Альянсов,\n    а так же корректно обрабатывается случай,  когда  у  игрока  нет  ни  одной\n    лаборатории\n    После исправления время исследования увеличится чуть более, чем в два  раза\n    для игроков с МИС,  а  для  игроков  без  МИС  -  упадет  на  один  уровень\n    лаборатории\n\n[@] Документация: readme преобразован в UTF8\n[@] Добавлена компенсация  работы  механизма  Magic  Quotes.  Подробнее  -  см.\n    /docs/install.txt, подраздел \"Magic Quotes\"\n[@] Юниты: Добавлена дополнительный аттрибут \"max\" ко всем юнитам и  его  общая\n    проверка в eco_get_build_data()\n[@] Меню\n    Меню теперь является динамическим\n[@] Модули\n    Автоматическая загрузка и регистрация модулей\n    Автоматическое перекрытие функций методами модуля из $manifest\n    Автоматическое подгрузка специфических пунктов меню\n    Автоматическая загрузка конфигурации модуля из файла\n    Теперь можно перекрывать функции методами из класса\n    Добавлена поддержка  \"цепи  перекрытий\".  Можно  протаскивать  сквозь  цепь\n    результат вычислений, модифицируя его на каждом шагу (см. пример реализации\n    перекрытия mrc_get_level)\n    Теперь в манифесте модуля можно задавать  список  констант,  которые  будут\n    автоматически назначены при его инициализации\n    Теперь  в  манифесте  модуля  можно  задавать  список  переменных,  которые\n    автоматически заменят (в случае обычных переменных) или дополнят (в  случае\n    одноуровневых массивов) соответствующие глобальные переменные.  Специальный\n    механизм гарантирует корректную работу с константами в таких  переменных  и\n    массивах - даже  тех,  которые  были  только  назначены  при  инициализации\n    модуля\n    Конструктор  теперь  поддерживает  загрузку  индексированныъ  элементов   в\n    многоуровневые массивы  типа  sn_data,  включая  использование  констант  в\n    качестве индексов. Подробнее см. в \"sn_module.php\"\n    Теперь  при  инициализации  модуля  в  цепочку  вызовов  функций  корректно\n    инсталлируется оригинальная основная функция из движка\n[@] Подсказки: Можно задавать ширину  подсказки  для  согласования  с  основной\n    страницей\n[@] Константы типов юнитов приведены к единому формату \"UNIT_xxx\"\n[@] Всем юнитам прописаны типы\n[@] События флотов\n    Переработана система событий флотов\n[@] Файлы\n    Расширение файлов локализации изменено с  \".mo\"  на  \".mo.php\"  для  лучшей\n    поддержки в различных IDE\n[@] Локализация\n    В дополнение к стандартным путям  \"language/<ISO2>/<domain>.mo.php\"  теперь\n    так же  поддерживаются  пути  вида  \"language/<domain>_<ISO2>.mo.php\".  Это\n    сделано для упрощения структуры подкаталогов в модуле\n[@] Очереди\n    Упразднена  константа  MAX_BUILDING_QUEUE_SIZE.   Теперь   размер   очереди\n    построек зданий и верфи/обороны задается переменными из таблицы `config`  -\n    соотвественно 'server_que_length_structures' и  'server_que_length_hangar'.\n    По умолчанию их значения равны 5\n[@] Классы\n    Новый метод  'assign_recursive'  класса  \"template\"  -  позволяет  в  одном\n    операторе заполнить  как  переменные  темплейта,  так  и  блоки  -  включая\n    вложенные\n[@] Скины\n    Изменена  организация  CSS-файлов.  Файл   \"formate.css\"   переименован   в\n    \"skin.css\". К нему присоединен в конце файл  \"default.css\".  Таким  образом\n    сохранена последовательность загрузки стилей и при  этом  все  стили  скина\n    находятся теперь в одном файле\n    Теперь движок подгружает файл \"/design/css/global_server.css\" .  Этот  файл\n    может использоваться для добавления специфичных глобальных стилей сервера -\n    он не входит в дистрибутив и не будет перезаписан  при  обновлении  движка.\n    Файл  грузится  после  \"global.css\"  и,  следовательно,  может  перекрывать\n    глобальные стили \"по умолчанию\". Однако он грузится после скинового CSS  и,\n    следовательно, будет  перекрыт стилями скина\n    Изменена система раскраски меню. Теперь каждому пункту  меню  присваиваются\n    присваиваются собственные аттрибуты HTML ID и CLASS.  КРАЙНЕ  рекомендуется\n    производить раскраску меню через аттрибут  ID  (см.  пример  в  formate.css\n    скина EpicBlue). Список ID элементов меню  можно  узнать  либо  в  браузере\n    (используя  функцию  \"Inspect  Element\"  или  аналогичную),  либо  в  файле\n    \"/includes/template.php\", функция tpl_render_menu(), переменная $sn_menu\n    В базовый CSS перенесено цветовое кодирование чисел  и  сообщений  (ошибка,\n    предупреждение итд). При желании они  могут  быть  перекрыти  в  CSS-файлах\n    стилей\n    Выделение Администрации и  премиумных  аккаунтов  проводится  через  стили.\n    Соответственно,  в  основной  скин  добавлены  стили  классов  \".nick_admin\",\n    \".nick_operator\", \".nick_moderator\"  и  \".nick_premium\"\n    Добавлены  классы  \".same_alliance\"  и  \".same_player\"  для   выделения   в\n    статистике соответственно Альянса игрока и самого игрока\n    supernova-ivash: Скин приведен в соответствие с текущим положением дел\n\n\n\nProject \"SuperNova.WS\" Release 33 \"Women Day v2012!\"\n~~\n[@] ВНИМАНИЕ!!! В этой версии скорректирована работа партнерской  программы,  а\n    так же повторно применен патч для масштабирования ТМ -  для  тех  серверов,\n    где он не был применен  раньше.  Если  вы  вносили  изменения  в  настройки\n    партнерской системы или начисления ТМ - сверьте текущие настройки в таблице\n    \"config\" с эталонными настройками в конце файла /docs/sql/supernova.sql!\n[@] ВНИМАНИЕ!!! PHP 5.3.1 содержит баг, который делает невозможной  полноценную\n    работу СН начиная с v33a12! Обновите  PHP,  или  сделайте  откат  до  более\n    ранней версии PHP, или используйте предыдущую версию СН.\n    Описание бага: https://bugs.php.net/bug.php?id=50394\n[@] ВНИМАНИЕ!!! Удален скин 'xnova'\n    Из дистрибутива игры удален скин 'xnova' из-за занимаемого им размера\n    Скачать скин можно с основного сайта проекта по ссылке\n    http://supernova.ws/files/skins/supernova-skin-xnova.zip\n    либо с SourceForge по ссылке\n    http://sourceforge.net/projects/supernova-ws/files/skins/\n[@] Среда разработки изменена на WAMP Server 2.2. Конфигурация:\n    MySQL 5.1.41\n    Apache 2.2\n    PHP 5.2.9-2 + xCache 1.3.2\n\n[#] Альянсы:   Альянсы  теперь  могут  рекрутировать  Наемников  и  исследовать\n    технологии!\n    1. Каждый Альянс имеет счет с ресурсами металл/кристалл/дейтерий/ТМ\n    2. Член Альянса может перевести ресурсы на счет Альянса. Сделать это  можно\n       на главной странице Альянса в разделе \"Ресурсы Альянса\".  Там  же  можно\n       увидеть состояние счета Альянса и бонусы, предоставляемые Альянсом  (см.\n       ниже). Кроме того в топбар  добавлено  отображение  ресурсов  Альянса  и\n       индикация наличия/отсутствия бонусов участникам Альянса\n    3. Ресурсы со счета Альянса могут расходоваться только  на  нужды  Альянса.\n       Вывод ресурсов со счета Альянса невозможен\n    4. Владелец   Альянса  с  его  счета   может   исследовать   технологии   и\n       рекрутировать Наемников - соответственно пункты \"Технологии  Альянса\"  и\n       \"Наемники Альянса\" на странице управления\n    5. После достижения минимально необходимого размера Альянса (10 человек  по\n       умолчанию, задается  в  таблице  `config`  записью  'ali_bonus_members')\n       каждый член Альянса получает бонус к своим Наемникам и технологиям\n    6. Значение бонуса зависит от количества игроков в Альянсе и вычисляется по\n       формуле: Бонус = round(уровень  технологии  или  Наемника  /  количество\n       игроков),  где round() - операция математического округления.\n    7. Бонусы   от  Наемников  и  Технологий  так  же  действуют  при  проверки\n       требований  к  постройкам/исследованиям.  Например:  игрок   состоит   в\n       Альянсе, дающем бонус  +2  к  Лазерной  технологии,  а  его  собственный\n       уровень технологии равен 4. Эффективный уровень технологии этого  игрока\n       равен 6. Это  означает,  что  находясь  в  Альянсе  он  имеет  доступ  к\n       исследованию Ионной  технологии  (требуется  ЛТ  5-го  уровня)  и  может\n       строить Тяжелый Лазер (требуется ЛТ 6-го уровня). Очевидно, если  бы  он\n       не находился в Альянсе, эти постройки были бы заблокированы\n    8. При исследовании технологии уровень лаборатории равен количеству игроков\n       в Альянсе на момент начала  исследования.  Активные  исследования  видны\n       членам Альянса на странице информации\n    Примеры:\n      1. Альянс из 10 человек купил Технологию 4 уровня. Бонус = round(4/10)  =\n         round(0,4) = 0\n      2. Альянс из 10 человек купил Технологию 7 уровня. Бонус = round(7/10)  =\n         round(0,7) = 1\n    Выбранная механика бонусов  Альянса  призван  обеспечить  достижение  сразу\n    нескольких целей:\n    1. Исключить злоупотребление фишкой,  когда  2-3  игрока  формируют  Альянс\n       исключительно для получения бонусов\n    2. Активизировать межальянсную активность: бонусы от ресусов Альянса  можно\n       получить только начиная с определенного количества участников. Ну и  чем\n       больше игроков в Альянсе, тем больше у него ресурсов\n    3. Усилить лояльность игроков к  Альянсу  -  при  выходе  (или  выгоне)  из\n       Альянса игрок теряет  все  бонусы  и  (самое  неприятное)  все  ресурсы,\n       пожертвованные  в Альянс\n    4. Исключить появление  мегаальянсов:  чем  больше  игроков  -  тем  больше\n       ресурсов они могут пожертвовать, но тем меньше  бонусов  получит  каждый\n       отдельный игрок\n    5. Слабые игроки в сильных  Альянсах  получают  доступ  к  end-game  юнитам\n       (если, конечно, глава Альянса  решит  потратить  ТМ  на  соответствующих\n       Наемников) и бонус в развитии\n    6. Сильные игроки смогут  поднять  эффективные  уровни  Технологий  даже  в\n       больших Альянсах. Например, если в Альянсе 15 человек, то исследовать 15\n       уровень технологии всем Альянсом будет  проще  и  дешевле,  чем  каждому\n       игроку отдельно\n    7. То же самое распространяется и на Наемников. При этом только Альянс дает\n       возможность получить эффективный уровень Наемников больше максимального\n    Дополнительно доступны еще три метода  расчета  бонусов.  Подробнее  см.  в\n    /docs/readme.txt\n\n[!] Иконка сайта: Новая иконка сайта! Мегареспект ув.Помощнику Ivash!\n[!] Запущен сервер обновлений.\n    1. Движок сервера общается с сервером обновлений по протоколу HTTP.\n    2. Если установлен CURL и подключен к PHP, то  для  проверки  версии  будет\n       использован именно он. Убедитесь, что  CURL  правильно  настроен  и  ему\n       разрешен доступ к внешним ресурсам\n    3. Если CURL не установлен,  будет  осуществлена  попытка  получить  версию\n       через file_get_contents(). Убедитесь, что в PHP разрешается обращаться к\n       внешним сайтам через соответствующую функцию\n    В  настоящее  время  сервер  обновлений  поддерживает  следующие   функции:\n    проверка версии движка и регистрация сайта.  Доступ  к  ним  осуществляется\n    из админки со страницы настроек сервера\n    1. При проверке версии передаются только анонимные данные - текущая  версия\n       БД, номер релиза и версия игры.\n    2. Результат проверки - рекомендация  сервера  обновлений  о  необходимости\n       обновления текущей версии игры\n    3. Есть два варианта проверки версии: ручная и автоматическая\n    4. Ручная проверка версии выполняется в ручном  режиме  по  нажатию  кнопки\n       \"Проверить версию\" на странице настроек.\n    5. Автоматическая проверка версии (по умолчанию -  отключена)  производится\n       самостоятельно движком по расписанию. Как и при ручной,  передается  тот\n       же объем анонимных данных\n    6. Период автоматической проверки версии  задается  в  секундах  в  таблице\n       config  переменной  server_updater_check_period.  По  умолчанию   период\n       проверки равен 24 часам (раз в сутки).\n    7. Результат и время последней проверки выводится в левом меню.\n    8. Результат и время  последней  проверки  так  же  выводится  на  странице\n       настроек.\n    9. Предусмотрено цветовое кодирование результатов  проверки  (как  в  левом\n       меню, так и в настройках): зеленый - обновление необязательно, желтый  -\n       желательно обновить движок, оранжевый - крайне рекомендуется обновление,\n       красный - ошибка проверки версии\n    Регистрация сервера нужна для  ряда  запросов  к  серверу  обновлений.  При\n    регистрации передается минимум информации,  необходимой  для  идентификации\n    сервера:\n    1. Полный URL сервера - т.е. HTTP-адрес  и  подкаталог  сервера.  Например:\n       http://myserver.com/myfolder/.    Это    необходимо    для     первичной\n       идентификации сервера. Полный путь необходим для того, что бы  различать\n       несколько копий СуперНовы, установленных на одном IP или домене.\n    2. Внутреннее название сервера. Используется для подстановки в сообщения.\n    Зачем  вообще  регистрировать  свой  сервер?  В   будущем  планируется  ряд\n    возможностей, которые буду доступны только зарегистрированным  серверам.  В\n    их число входит (отсортированы по запланированным срокам реализации):\n    1. Автоматическое получение чейнджлога\n    2. Автоматизированное обновление движка\n    3. Участие в рейтинге серверов\n    4. Багрепорты от администраторов серверов\n    5. Чат для администраторов серверов\n    6. По запросу - удаленная диагностика сервера\n    7. ...и многое, многое другое\n    Зачем регистрировать свой сервер прямо сейчас?\n    1. Запросы от администраторов  зарегестрированных  серверов  имеют  больший\n       приоритет при диагностике проблем и обработке багрепортов.\n    2. При регистрации кроме индивидуального ключа серверу выдается  уникальный\n       идентификационный номер,  который  будет  использоваться  при  первичной\n       сортировке серверов. Чем раньше  будет  зарегистрирован  сервер  -  тем,\n       например, выше он будет в общем каталоге серверов...\n[!] Наемники: Добавлена поддержка временных Наемников  (ВН).  Осталась  так  же\n    поддержка постоянных Наемников (ПН). Тип Наемников выбирается в  настройках\n    сервера\n    1. ВН (как следует из названия) не являются постоянными,  а  нанимаются  на\n    определенный срок. По истечению срока Наемник исчезает\n    2. В режиме ВН отсутствует понятие \"веток развития\" и  для  найма  доступны\n    сразу все Наемники. Соответственно не отображаются требования  к  Наемникам\n    на странице \"Технологии\"\n    3. Базовая цена покупки ПН в режиме ВН становится ценой  найма  на  базовый\n    период найма (БПН). По умолчанию он равен одному среднекаелндарному  месяцу\n    (30 дней, 2592000 секунд). Изменить БПН можно на странице настроек сервера\n    4. Предусмотрена система скидок/наценок  в  зависимости  от  срока  покупки\n    Наемника.  Настройки  содержатся  в  массиве  $mrc_hire_discount  в   файле\n    /officer.php.  Индекс  элемента  -  количество  секунд  найма,  значение  -\n    коэффициент скидки. \"1\" означает, что  на  данный  интервал  найма  нет  ни\n    наценки, ни скидки и при пересчете на количество секунд в БПН его стоимость\n    будет в точности равна стоимости БПН.  Если  число  меньше  единицы  -  это\n    означает скидку; больше единицы - наценку\n    5. Временного наемника можно увольнять до истечения срока найма.  ВНИМАНИЕ!\n    При увольнении наемников вся портаченная на найм ТМ будет утеряна!\n    6. Режим Наемников отображается на странице \"Мировые константы\"\n    При переключении режима Наемников следует учитывать следующие особенности:\n    1.  При  включении  ВН  все  постоянные  Наемники  будут  преобразованы  во\n    временные со сроком действия равному БПН. В случае  необходимости  изменить\n    БПН нужно СНАЧАЛА его изменить, а затем переключать режим работы Наемников\n    2. После включения ВН изменение базового интервала найма не влияет  на  уже\n    рекрутированных Наемников, а влияет только на цену нового найма\n    3.  При  отключении  ВН  все  активные  на  этот  момент   Наемники   будут\n    преобразованы в постоянные - вне зависимости от того,  на  какой  срок  они\n    были наняты и сколько времени осталось до срока истечения найма. Информация\n    о сроках найма при этом теряется\n    4.  При  отключении  ВН  активизируются   ограничения   по   рекрутированию\n    Наемников, однако уже нанятые Наемники останутся активными и  будут  влиять\n    на игру вне зависимости от того, может  игрок  их  купить  или  нет.  Такой\n    способ переключение выбран для исключения потери ТМ, вложенных  игроками  в\n    Наемников\n    Переработана  страница  рекрутирования  Наемников:\n    1. Добавлена поддержка временных Наемников\n    2. Стоимость найма  отображается  динамически  в  зависимости  от  текущего\n    режима Наемников, выбранного уровня и срока найма\n    3. В режиме ПН видны все наемники - даже  недоступные  (с  соответствующими\n    пояснениями)\n    4. В режиме ПН можно нанимать сразу несколько уровней Наемников\n    5. При найме постоянных наемников показывается ровно  столько  уровней,  на\n    сколько хватает ТМ\n[!] Аватары: Добавлена поддержка серверных аватаров игроков\n    Аватары могут быть  загружены  с  локального  диска  на  странице  настроек\n    пользователя.\n    Поддерживаются файлы форматов JPG, GIF и PNG размером до 200КБ. Загруженные\n    картинки будут отмасштабированы до размеров 128х128.\n    Аватар отображается на странице \"Император\" и в попапе игрока  на  странице\n    \"Вселенная\"\n[!] Альянс: Добавлена поддержка серверных логотипов  Альянсов\n    Логотипы могут быть загружены с локального  диска  на  странице  управления\n    Альянсом\n    Поддерживаются файлы форматов JPG, GIF и PNG размером до 200КБ. Загруженные\n    картинки будут отмасштабированы до размеров 128х128\n    Логотип отображается на странице информации об Альянсе и в  попапе  Альянса\n    на странице \"Вселенная\"\n[!] Вселенная: Галактики и системы могут иметь собственные названия!\n    1. По умолчанию галактики и системы не имеют собственных названий\n    2. Увидеть текущее имя галактики или системы можно на странице \"Вселенная\"\n    3.  Назвать  галактику  или  систему  можно   по   ссылке   \"Переименовать\"\n    соответственно  возле  координат  галактики   или   системы   на   странице\n    \"Вселенная\"\n    4. Именование галактики или системы имеет  соотвествующую  стоимость  -  по\n    умолчанию 10000 ТМ для галактики и 1000 ТМ  для  системы  -  т.н.  \"базовую\n    стоимость  именования\".  Изменить  базовую  стоимость  именования  можно  в\n    настройках  сервера.  Игроки  могут  видеть   текущую   базовую   стоимость\n    именования на странице \"Мировые константы\"\n    5.  При  именовании  галактики  или  системы  игрок  может  назначить  цену\n    именования. Минимальная цена именования равна базовой стоимости именования\n    6. При переименовании уже именованной галактики или системы,  игрок  должен\n    уплатить ранее назначенную  стоимость  именования  плюс  базовая  стоимость\n    именования. Таким образом,  если  первый  игрок  назначил  цену  именования\n    системы в 2500 ТМ, а базовая цена именования системы составляет 1000 ТМ, то\n    игрок, желающий переименовать ту же систему должен уплатить не меньше  3500\n    ТМ. Таким образом более высокая цена именования  галактики  или  системы  в\n    определенной степени защищает объект от переименования\n    7. Как было сказано в п.2, имя галактики и системы видны всем игрокам\n    8. Все действия по переименованию галактик и систем записываются  в  лог  -\n    код события 104\n[!] Модульность: Базовая  поддержка  модульности  -  динамически  перекрываемые\n    функции. Подробное описание по использованию динамического перекытия см.  в\n    разделе \"Модульная система\" файла /docs/readme.txt\n[!] Исследования: Очередь исследований перенесена с планет на пользователя\n    1. Исследования теперь могут производится даже на планетах  со  строящимися\n       лабораториями/нанолабораториями\n    2.  В  случае  отсутствия  МИС  для  проведения   исследования   выбирается\n       лаборатория  с  максимальным  эффективным   уровнем   (т.е.   с   учетом\n       нанолабораторий)\n    3. При отмене исследования ресурсы возвращаются на ту  планету,  с  которой\n       были взяты\n    4. Награда за квесты на исследование теперь всегда начисляется на  основную\n       планету игрока\n    5. При апдейте все идущие исследования будут перенесены в  пользовательскую\n       очередь\n[!] Чёрный Рынок: Продавец информации\n    На Чёрном Рынке  доступна  новая  услуга:  продажа  информации.\n    Письма от Продавца Информации всегда приходят в почтовый ящик - даже если у\n    игрока отключено получение шпионских отчетов. Мистика какая-то!\n    Информация об игроке: текущие уровни активных Наемников.\n[!] Темплейты: Минификатор\n    ВНИМАНИЕ! ЭТО - ЭКСПЕРИМЕНТАЛЬНАЯ ФИШКА! ИСПОЛЬЗУЙТЕ ЕЁ  НА  СВОЙ  СТРАХ  И\n    РИСК!\n    Минификатор уменьшает размер генерируемого движком HTML-кода  путем  замены\n    нескольких  идущих  подряд  пустых  символов  (перевод  строки,  табуляция,\n    пробел) одним символом пробела.\n    Минификатор умеет сжимать HTML и встроенный JS-код. Для JS-кода он  так  же\n    удаляет однострочные комментарии.\n    Минификатор работает на уровне темплейтов и если включено  кэширование,  то\n    минификатор вызывается  только  один  раз  при  компиляции  кода  и  дальше\n    кэшируется   уменьшенный   скомпилированный   темплейт,    что    исключает\n    необходимость в повторном вызове минификатора. Этим он  выгодно  отличается\n    от  минификаторов, работающих на уровне сессии через ob_hanler()\n    В  среднем  по  сайту  минификатор   дает   выигрыш   порядка   7-8%%   при\n    незначительном падении производительности.\n    По  умолчанию  минификатор  отключен.  Включить  его  можно  в  админке   в\n    настройках сервера - пункт \"Минификатор темплейтов\"\n\n[+] Пол: Добавлено отображение пола игрока на странице \"Император\", на странице\n    статистики и в попапе игрока на странице \"Вселенная\"\n[+] Настройки: Отображение логотипов Альянсов и аватаров  игроков  на  странице\n    \"Вселенная\" может быть отключено в настройках игроков\n[+] Вселенная: На попапе игрока отображается его текущее звание в Альянсе\n[+] ЧР/Торговец ресурсами: Теперь  можно  поменять  ТМ  сразу  на  все  ресурсы\n    (опция \"Все ресурсы\" в дропдауне выбора ресурсов). При этом вводимая  сумма\n    будет разделена на три части и на  каждую  из  этих  третей  будет  куплено\n    соответствующее количество ресурсов по курсу. Стоимость такой операции -  в\n    три раза больше базовой стоимость обмена\n[+] Квесты: Теперь в награду за исполнение квеста можно одновременно ставить до\n    четырех видов ресурсов\n[+] Скины: Добавлена возможность перекрыть дефолтные стили  элементов  jQueryUI\n    (файл /design/css/jquery.css) стилями, специфическими для скина. Для  этого\n    в корневой каталог скина  нужно  положить  файл  jquerу.css  с  настройками\n    стилей элемента. Сгенерировать файл под свою тему можно на сайте jQuery  по\n    ссылке: http://jqueryui.com/themeroller/\n[+] Постройки:   Вертикальная  очередь  построек.   Включается   в   настройках\n    пользователя в секции \"Настройки интерфейса\"\n[+] Исследования: Добавлены подробные  сообщения  об  ошибке  в  случае,  когда\n    технология не может быть исследована (нехватка ресурсов,  неудовлетворенные\n    требования итд)\n[+] Обзор планеты: Настраиваемое количество колонок в списке планет\n    На странице настроке пользователя можно  указать,  сколько  колонок  должно\n    быть в списке планет - пункт \"Количество колонок в списке планет\" в разделе\n    \"Настройки интерфейса\"\n    Можно выставить количество колонок в 0 и  указать  максимальное  количество\n    рядов с списке - см. соответствующий пункт там же.  В  этом  случае  движок\n    рассчитает количество колонок исходя из этого  числа.\n    Обращаю внимание - указывается именно максимальное количество  рядов!  Т.е.\n    если у игрока 6  планет,  а  количество  рядов  указано  5,  то  количество\n    необходимых колонок для того, что бы число рядов не привысило 5 будет равно\n    двум. Соответственно, список планет будет сформирован в виде  двух  колонок\n    по три ряда. Если же колоний будет 12 - список планет будет  выглядеть  как\n    таблица три колонки по четыре ряда.\n    Данная особенность связана с построением списка планет -  слева  направо  и\n    сверху вниз. Естественно, не составило  бы  никакого  труда  сделать  вывод\n    списка сверху вниз, а затем справо налево - это было бы даже легче.  Однако\n    при выбранном способе  сохраняется  пользовательская  сортировка  планет  -\n    более \"важные\" колонии всегда будут \"выше\" в списке\n[+] Документация: Добавлен файл /docs/html/changelog.html - чейнджлог в html\n[+] Навбар: Переработан навбар\n    Из навбара убрано количество текущих ресурсов на  планете/в  альянсе  -  их\n    присутствие на большей части экранов не имело практического смысла при том,\n    что отнимало драгоценное вертикальное пространство страницы.\n    Сведения о количестве ресурсов на планете вынесены в планетарный навбар. На\n    странице настроек пользователя можно сделать планетарный навбар  постоянным\n    - т.е. будет полностью восстановлен функционал старого навбара\n    Там, где сведения о количестве  ресурсов  на  планете  смысл  имеет  -  они\n    добавлены или включен планетарный навбар (см. ниже)\n\n[~] Обзор планеты: Добавлено текущее количество ресурсов  на  планете,  текущий\n    размер хранилищ, а так же - количество ресурсов на прилетающих флотах\n[~] Черный Рынок/Скупщик кораблей и Продавец б/у кораблей\n    Добавлено количество текущих ресурсов на планету\n[~] Модуль \"Альянсы-игроки\" - v6\n    Добавлено количество  текущих  ресурсов  в  таблицу  перечислений  ресурсов\n    Альянсу\n    Модуль необходимо обновить до версии v6+ для работы с SN v33a29+!\n[~] Экономика/Строительство\n    На всех экранах строительства (здания, флот, оборона)  включен  планетарный\n    навбар\n[~] Флоты\n    Добавлено количество  дейтерия на  планете  на  страницу  выбора  кораблей,\n    страницу выбора точки назначения и страницу своза ресурсов\n    Так же на странице выбора кораблей включен планетарный навбар\n[~] Альянсы\n    Добавлен заголовок на страницу Альянсов с тэгом Альянса\n    Ссылка на управление Альянсом/игроками перенесена в самую верхнюю таблицу\n[~] Постройки: Информация о  постройках  кэшируется  при  заходе  на  страницу.\n    Ширина таблицы построек устанавливается на максимальную из  возможных.  Это\n    предотвращает \"баян\" - прыжки ширины таблицы построек\n[~] Император: Теперь на  страницы  показывается  так  же  очки  за  ресурсы  и\n    исследования. Немного переформатирована таблица статистики\n[~] Статистика:   Оптимизирован   алгоритм   подсчета   статистики.   Отключено\n    обновление \"очков планеты\"\n[~] Чат: Теперь при таймауте чата скрипт поллинга полностью прекращает работу\n[~] Экономика: При расчете  времени  постройки  юнитов  учитывается  не  только\n    количество ресурсов, но и  их  качество.  Время  постройки  нормированы  по\n    дейтерию, т.е. постройки с большей долей низкоуровневых  ресурсов  строятся\n    быстрее\n[~] Админка: Добавлены label for для всех чекбоксов\n[~] Скины: EpicBlue имеет собственное оформление элементов jQueryUI\n[~] Вселенная: При создании новой планеты из имени планеты  исключен  знак  \"№\"\n    для более корректной работы функций PHP\n[~] Альянсы:  Количество  игроков  в  Альянсе  теперь   изменяется  сразу   при\n    изменении, а не при апдейте статистики/обслуживании сервера\n[~] Альянсы: Теперь список членов Альянса распознает права просматривающего без\n    захода в админскую  часть,  поэтому  из  админки  убран  пункт  \"Управление\n    участниками\"\n[~] Альянсы: Изменено отображение логотипа на странице Альянса\n[~] Список планет: Клик на иконке  летящего  союзного  флота  теперь  открывает\n    сраницу \"Флоты в полете\"\n[~] Чёрный Рынок\n    Редизайн основного экрана и экрана обмена ресурсов\n    Откуда взялась эта странная надпись? Очень странно...\n[~] Меню: Переработано меню\n[~] Чат: Тэг  Альянса  после  имени  игрока  теперь  указывается  в  квадратных\n    скобках, а адресат сообщения - в круглых. Сделано для унификации  написания\n    тэга Альянса в движке\n[~] Статистика: Теперь полностью отрабатывается переход со ссылок Вселенной  на\n    страницу статистики: правильно выбирается тип статистики  (игрок/Альянс)  и\n    страница статистики, корректно отрабатываются дропдауны (выбирается  именно\n    текущий тип/страница статистики).\n    Cписок статистики скроллируется либо до выбранного объекта - если позволяет\n    размер страницы, либо максимально вниз, если размер страницы не позволяет\n    Выбранный объект отмечается знаком \">\" в столбце рангов\n    Все числа в ячейках отцентрированы по правому краю\n[~]  Логин/Регистрация:  Данные   по   серверу   (размер,   скорость,   онлайн)\n    сгруппированы в один блок и теперь видны так же на странице регистрации\n\n[%] Партнерка: Исправлено отображения количества начисленных ТМ\n[%] Наемники:  Исправлена  ошибка  начисления  процентных  бонусов:  в  случаях\n    многократных бонусов происходило  начисление  бонуса  на  бонус,  а  не  на\n    базовое значение. Это приводило к получению завышенных бонусов  -  например,\n    на Адмирале и Навигаторе\n[%] Альянсы: Исправлена ошибка при отправке письма группам Альянса\n[%] Альянсы:   Исправлена  ошибка  с  неправильным  отображением  звания  главы\n    Альянса\n[%] Регистрация: Исправлена ошибка, когда игрок мог создать  аккаунт/планету  с\n    пробелами в начале/в конце\n[%] Админка: Исправлена ошибка создания квеста\n[%] Ракетная атака: Исправлена ошибка, из-за которой ракеты били в  десять  раз\n    слабее\n[%] UBE: Исправлена ошибка неучета бонуса  Альянсов  в  бою.  ВНИМАНИЕ!  Бонусы\n    Альянса и от Наемников по-прежнему не будут видны в логе боя!\n[%] Альянсы: Исправлена ошибка выхода из Альянса - у игрока  не  затирался  тэг\n    Альянса\n[%] Флоты: Исправлена уязвимость, позволяющая дублировать флоты\n[%] Локализация/EN: Исправлены очепятки, спасибо tedk28\n\n[@] Админка: Редизайн интерфейса просмотра логов (бывш. \"Ошибки\")\n[@] Аватары: Для корректной  работы  подсистемы  аватаров  в  PHP  должен  быть\n    корректно настроен временный каталог и движку должна быть разрешена  запись\n    в каталог /images/avatar\n    Максимальный размер аватара настраивается  в  таблице  `config`  переменные\n    avatar_max_width и avatar_max_height\n[@] Пол: В скины добавлена иконка пола в подкаталог \"images\" скина.  Файлы  для\n    мужского  и  женского  пола  называются  соответственно  \"sex_male.png\"   и\n    \"sex_female.png\". Встроенные скины обновлены автоматически\n[@] Новости: Изменения в редактировании новости\n    При редактировании новости галочка рассылки новости по умолчанию отключена\n    При  редактировании  новости  не  изменяется  глобальное  время   написания\n    последней новости -  т.е.  отредактированная  новость  не  включает  список\n    последних новостей на обзоре планеты\n[@] JS: Обновлен jQuery до версии 1.7.1. Обновлен jQuery-UI до версии 1.8.17\n[@] БД: Доработан дамп. Теперь по умолчанию новосоздаваемый игрок имеет мужской\n    пол - так же, как и дефолтный администраторский аккаунт\n[@] Локализация: Все строки локализации, относящиеся к  наемникам,  вынесены  в\n    файл mrc_mercenary.mo\n[@] Исследования: Исследования теперь завернуты в  транзакции  -  это  позволит\n    избежать  злоупотреблений  связанных  с  частым  обновлением  страницы   на\n    медленных соединениях\n[@] Файл todo.txt заменен на todo.xls\n[@] $sn_data['groups']['prod'] => $sn_data['groups']['factories']\n[@] Чёрный Рынок: Почищены языковые файы от неиспользуемых строк\n[@] Темплейты\n    Расширение файлов темплейтов изменено с \".tpl\" на \".tpl.html\" для  большего\n    удобства разработки\n    Теперь при использовании директивы <!--  INCLUDE  -->  НЕ  НУЖНО  указывать\n    расширение подключаемого файла\n[@] Файлы: Удалены неиспользуемые файлы faq.php, faq1.php, faq2.php\n[@] Файлы: Сильно переработана организация файлов PHP\n    Многие процедуры поменяли свое местоположение\n    Множество файлов теперь не грузятся  автоматически  при  старте  движка,  а\n    грузятся лишь по потребности. В частности - все файлы  миссий  подгружаются\n    только в менеджере летящих флотов,  а  сам  менеджер  грузится  только  при\n    потребности в обработке флотов.  Кроме  того,  боевой  движок  подгружается\n    только в симуляторе и при обсчете боев (Миссии \"Атака\" и \"Уничтожить\")\n    Все это позволило заметно сократить размеры кода в памяти сервера\n[@] Модули: Загрузчики модулей теперь располагаются в каталоге /modules, а не в\n    /modules/_functions\n\n\n\nProject \"SuperNova.WS\" Release 32 \"Happy New Year v2012!\"\n~~\n[!] Исправлена работа с большими числами:\n      Все числовые значение в HTTP-запросах трактуются как  числа  с  плавающей\n      запятой. Все идентификаторы передаются как строки\n      Все идентификаторы в  БД  являются  BIGINT(20).  Соответствующим  образом\n      переконфигурированы (добавлены или изменены) FOREIGHN KEYS\n      Переработаны  все  таблицы,  что  бы  исключить  переполнение  при  любом\n      разумном сценарии  использования  движка  (скажем,  вплоть  до  скоростей\n      x1000000)\n\n[+] Постройки:  На   ресурсогенерящие   постройки   добавлена   подсказка    по\n    производительности\n\n[~] Скины: Обновлен скин СН/Иваш\n[~] Постройки:  Показываются  все  возможные  строения  на  планете/луне.  Если\n    невозможно осуществить постройку - показывается причина\n    Иконки  информации  и   своза   ресурсов   поменялись   местами,   что   бы\n    соответствовать  расположению  иконки  своза  ресурсов  на  списке   планет\n    (Обзор/Империя)\n[~] Постройки: Теперь при невозможности постройки юнита не затемняется название\n    и текущий уровень юнита\n[~] Постройки: В описании юнитов разнесены ссылки на  постройку  и  уничтожение\n    юнита во избежании случайного выбора не того действия\n[~] Постройки:  Из  темплейта  постройки задний  яваскрипт  вынесен в отдельный\n    файл\n[~] Постройки: Увеличено  место  для  картинки  здания  до  150  пикселей.  Это\n    предотвратит \"скачки\"  описания  постройки  при  перемещении  выделения  на\n    иконках\n[~] Экономика: Производство и потребление электроэнергии теперь  масштабируется\n    согласно скорости добычи\n[~] Верфь: Обновлен интерфейс верфи\n[~] Верфь: Ограничено максимальное количество строящихся юнитов на верфи\n[~] Экономика:   Увеличена  базовая  добыча  шахты  кристаллов  с  20  до   32.\n    Соответственно увеличено энергопотребление с 10 до 16\n[~] Статистика:  Статистика  теперь  считается  не  в  транзакции,  что  бы  не\n    блокировать игроков. Сохраняется статистика за 10 дней\n[~] Рекорды: Переписана страница рекордов\n[~] Постройки: Ссылки на создание и  уничтожение  юнитов  раскрашены  в  цвета,\n    согласно  CSS  (по  умолчанию:  зеленый  для  создания,   красный   -   для\n    уничтожения)\n\n[%] Вселенная: Исправлена ошибка с запуском  отрицательного  количества  юнитов\n    через AJAX\n[%] Альянсы: Исправлена ошибка с неотображением тэга  у  членов  новосозданного\n    Альянса\n[%] Постройки: Исправлена ошибка  с  невозможностью  выбора  превьюшки  здания,\n    которое нельзя построить\n[%] Постройки: Исправлена ошибка с невозможностью уничтожить  постройку,  когда\n    не хватает ресурсов на создание постройки, но хватает на её уничтожение\n[%] Админка: Исправлена ошибка самопроизвольного сброса  флага  масштабирования\n    хранилищ\n[%] Друзья: Исправлена надпись с неправильной кодировкой при ответе  на  письмо\n    друга/кандидата\n[%] Исправлена уязвимость, позволяющая быстрыми многократными  постройками  или\n    отменами добиться увеличения ресурсов\n[%] Рекорды: Исправлена ошибка, когда в списке  рекордсменов  появлялись  члены\n    команды сервера\n[%] Флоты: Исправлена уязвимость, позволяющая передавать флоты другим игрокам\n\n[@] DB: Обновлен дамп БД до версии 32\n[@] PTE: Парсер темплейтов теперь понимает конструкции вида  {L_tech[D_CONST]},\n    которые будут развернуты в $lang['tech'][CONST]\n[@] eco_get_build_data() возвращает  время  постройки  в  отдельном  подмассиве\n    RES_TIME, а не в массивах действий BLD_CREATE/BLD_DESTROY\n[@] Числовые идентификаторы для строений заменены константами STRUC_xxx\n\n\n\nProject \"SuperNova.WS\" Release 31 \"Artifact governors edit localized inflation\"\n~~\n[!] Скины: Новый скин \"supernova-ivash\". Автор - Ivash\n[!] Артефакты: Добавлена подсистема Артефактов - редких объектов с  уникальными\n    свойствами.\n    Артефакты можно купить за ТМ.\n    Артефакты являются одноразовыми - после использования Артефакт исчезает.\n    Некоторые Артефакты настолько мощные, что их  количество  в  одной  Империи\n    ограничено.\n    Использование некоторых Артефактов привязано к планетам -  т.е.  их  эффект\n    будет распространятся только на  эту  планету.  Эффекты  других  Артефактов\n    распространяются на всю Империю. Особо  мощные  Артефакты  могут  оказывать\n    влияние на солнечную систему, галактику или даже Вселенную\n    Добавлен Артефакт - \"Большой Адронный Колайдер\"\n    Добавлены Артефакты - Автономный  Колонизирующий  Комплекс  трех уровней\n[!] Наемники: Произведено разделение офицеров на \"наемников\" и  \"губернаторов\".\n    Наемники покупаются через общее меню слева и их бонусы распространяются  на\n    всю Империю. Губернаторы покупаются отдельно на каждую планету на  странице\n    \"Управление  планетой\"  и  их  бонусы   распространяются   только   на   ту\n    планету/луну,  на  которой  они  куплены.  Так  же  произведены   следующие\n    изменения:\n    1. Стоимость наемников и губернаторов теперь рассчитывается по формуле:\n      БС * (Фактор ^ Уровень),\n      где БС - базовая стоимость наемника, Фактор - заранее заданная  величина,\n      ^ - операция возведения в степень, Уровень - новый уровень наемника\n    2. По умолчанию БС = 3000, Фактор = 1\n    3. Максимальный уровень карго-мастера увеличен до 20\n    4. Упразднены  наемники  Геолог,   Энергетик,   Архиктектор,   Конструктор,\n    Фортификатор, Разрушитель - их функции переданы губернаторам (см. ниже)\n    5. Вся ТМ за упраздненных наемников возвернута игрокам\n    6. Реорганизованы ветки развития наемников\n       \"Шахтерская\" теперь выглядит как \"Карго-мастер\" - \"Шпион\" - \"Академик\" -\n       \"Разрушитель\"\n       \"Рейдерская\" теперь выглядит как \"Адмирал\" - \"Координатор\" - \"Навигатор\"\n       - \"Ассасин\"\n    7. Балансировка губернаторов проведена из  расчета  на  \"среднего  игрока\",\n    имеющего 6 планет. Такие игроки не  получат  пенальти  при  оснащении  всех\n    планет  губернаторами.  Понятно,  что  общая   производительность   Империи\n    уменшится, однако это те жертвы, на которые  я  готов  пойти.  В  целом  же\n    изменение направлено на уменьшение среднего  количества  ТМ  у  игроков.  В\n    особенности - у топов и саб-топов\n    8. Новый губернатор \"Технолог\" объединяет функции Геолога и Энергетика БС =\n    800, Фактор = 1.06, стоимость 20 уровня - 29418 ТМ, не имеет ограничения по\n    уровню. С 5-го уровня позволяет строить термоядерную электростанцию\n    9. Новый губернатор \"Инженер\" объединяет функции Архитектора и Конструктора\n    БС = 400, Фактор = 1.25, стоимость 10-го уровне - 13298  ТМ,  максимального\n    15-го - 43868 ТМ\n    10. Губернатор \"Фортификатор\" так же играет для планеты роль Защитника БС =\n    2000, Фактор = 1, стоимость максимального 8-го уровня - 16000  ТМ.  С  3-го\n    уровня позволяет строить Планетарную защиту. Дает хозяину планеты бонус 10%\n    за каждый уровень к атаке, броне и щитам при обороне\n    11. Вследствие полной бессмысленности найма Технолога на лунах во избежание\n    напрасных трат ТМ игроками он убран со страницы управления луной\n    12.  Страница  покупки  губернаторов  требует  подтверждение  операции  при\n    покупке губернатора, отличного от текущего. Страница покупки имеет защту от\n    случайной покупки губернатора при обновлении страницы\n[!] Локализация: СуперНова использует  кодировку  UTF-8  при  работе  с  БД  и\n    рендере HTML-страниц. Таким образом поддерживаются любые наборы символов\n    Все файлы локализации пропущены через редактор и приведены к одному виду\n    Добавлены заголовки в файлы локализации\n[!] Редактор локализаций: В админке добавлен редактор локализаций - пункт  меню\n    \"Локализация\" в разделе \"Утилиты\"\n    1. Выбор пункта  меню  \"Локализация\"  открывает  выбор  т.н.  \"домена\"  для\n    редактирования. Домен - это совокупность строк локализации,  относящихся  к\n    отдельному   аспекту   игры.   Домен   эквивалентен   языковому   файлу   с\n    соответствующим именем\n    2.  После  выбора  домена  и  подтверждения  выбора  открывается   страница\n    редактирования строк локализации. Открытие больших  файлов  может  занимать\n    существенное время - поэтому запаситесь терпением\n    3. После редактирования строк локализации и подтверждения редактор  создаст\n    файлы \"<имя домена>.mo.new\" в каждой папке языка\n    4. Файлы .mo.new имеют приоритет перед обычными  .mo  файлами  локализации.\n    Т.е. если в одном языковом каталоге присутствуют оба типа файлов,  редактор\n    загрузит для редактирования .mo.new\n    5. Для того, что бы движок  подгрузил  новый  файл  локализации,  требуется\n    изменить его расширение с .mo.new  на  .mo.  Обычно  это  перезапишет  файл\n    текущий файл локализации - поэтому следует заранее  сделать  его  резервную\n    копию\n    6. ВНИМАНИЕ! Следует соблюдать осторожность при  замене  старых  файлов  на\n    новые! Редактор не сохраняет комментарии и игнорирует дополнительный код  в\n    файлах локализации! В результате простая перезапись файлов  может  нарушить\n    нормальную  работу  подсистемы  локализации   движка!   Если   ваши   файлы\n    локализации  содержат  дополнительный  PHP-код,  то  они  требуют   ручного\n    вмешательства после обработке в редакторе!\n    7. Редактор локализаций корректно работает с константами внутри доменов\n    8. В редакторе есть возможность добавлять и удалять строки локализации\n[!] Межгалактические Врата: Интерфейс Врат  вынесен  на  отдельную  страницу  и\n    доступен с Обзора планеты (куда вынесен таймер готовности врат)\n    Все таймеры врат заменены на sn_timer\n[!] Экономика: ТМ смасштабирована в отношении 1 к 1000. Т.е. весь приход  ТМ  и\n    все цены в ТМ увеличены в 1000 раз\n[!] Тёмная Материя: За исследования начисляются опыт,  за  опыт  -  уровни,  за\n    уровни - ТМ. Таблица необходимого опыта  для  набора  уровней  эквивалентна\n    таблице опыта  за  постройки  (см.  /docs/readme.txt).  За  каждый  уровень\n    начисляется 1000 ТМ\n[!] Флот: Новый корабль - Гипертранспорт. Предназначен для  ТОП  игроков  и/или\n    скоростных Вселенных\n\n[+] Обзор  планеты/Обзор  Империи:  Таймер  очереди  строительства  зданий  под\n    иконкой планеты теперь переключается на  следующее  здание  в  очереди  при\n    окончании строительства текущего. Ранее таймер  показывал  только  прогресс\n    постройки первого здания в очереди\n[+] Список  планет:  На   изображения  планет   добавлен   индикатор   текущего\n    губернатора и его уровень\n[+] Инфо/Флот: Показываются текущие характеристики корабля (с учетом  наемников\n    и технологий)\n[+] Новости: Добавлена лента новостей на  страницу  обзора  планеты.  Выводятся\n    только  последние  непрочитанные  новости.  Количество  выводимых  новостей\n    настраивается на сервере (по умолчанию - 3)\n[+] Вселенная: В попап Альянсов добавлен его ранг\n[+] Экономика: Новая  настройка  сервера  \"Масштабировать  склады  от  скорости\n    добычи\". Настройка доступна в общих настройках сервера  в  разделе  \"Прочие\n    параметры\". По умолчанию возможность отключена\n[+] Строительство/Здания: В ссылке на уничтожение здания указывается количество\n    требуемых ресурсов и время\n[+] Новапедия: Включена страница информации для Артефактов. Добавлены  описания\n    всех стандартных ресурсов на русском и английском языках, а атк же включена\n    страница информации для них\n\n[~] ТМ: Изменения в ТМ текущего игрока отображаются сразу по факту\n[~] Луна: Имя создаваемой луны теперь не такое длинное\n[~] Список планет: Добавлена всплывающая подсказка на иконку губернатора\n[~] Поиск: Оптимизированы запросы поиска\n    При поиске Альянса поиск подстроки происходит одновременно в имени и тэге\n[~] Статистика: Изменен  расчет  статистики.  Теперь  в  тратах  каждый  ресурс\n    считается согласно курсу обмена.  Таким  образом  игроки  с  более  ценными\n    ресурсами получат больше очков\n[~] Вселенная: При обнаружении  планеты  с  отсутствующим  пользователем  в  БД\n    планета удаляется с отсрочкой 24 часа\n[~] Партнерка: Добавлено  ограничение  по  минимальному  количеству  ТМ,  после\n    которого   начинается   начисление   бонусов   реферралу    -    переменная\n    \"rpg_bonus_minimum\" в таблице \"config\"\n[~] Шпионаж: Оптимизирована процедура генерации шпионского рапорта\n[~] Фаланга:   Исправлен  и  переработан  алгоритм  работы  фаланги.  Добавлено\n    два исключения:\n      1. Флот, летящий с луны, фалангой не виден\n      2. Флот, летящий на задание \"Удержание\" фалангой не виден\n[~] Технологии:   Пересмотрены  технологии.  Устранены  противоречия  в  ветках\n    развития  (например,  ионный   двигатель   можно   было   исследовать   без\n    технологии). Технологии переупорядочены в более логичном порядке\n\n[%] Флоты: Добавлен патч для mysql-серверов со  включенным  STRICT_TRANS_TABLES\n    при отправке флотов\n[%] Локализация:   Добавлен  патч  для  серверов  с   неправильно   настроенной\n    кодировкой в HTTP-заголовке\n[%] Шпионаж: Корректно выставляется время шпионского рапорта\n[%] Локализация/EN: Исправлены ошибки с двумя l в \"Metall\" и \"Crystall\"\n[%] Исправлен   неработающий   просмотр   бана   при   входе   заблокированного\n    пользователя\n\n[@] SYS: Устаревшие функции заменены актуальными аналогами:\n      int_buildCounter => tpl_parse_planet\n      GetTargetDistance, GetMissionDuration, GetFleetConsumption =>\n        flt_travel_data\n      GetShipConsumption, get_ship_speed => get_ship_data\n      GetFleetMaxSpeed => flt_fleet_speed\n[@] SYS: В описании структуры кораблей (vars.php) данные о двигателях  вынесены\n    в  отдельный  массив  'engine'.  Теперь  можно   указывать   неограниченное\n    количество двигателей для апгрейда корабля\n[@] Апдейтер: Доработан апдейтер, что бы предотвратить зацикливание\n[@] Админка: В \"Утилиты\" добавлена  возможность  форсировать  только  последний\n    апдейт\n[@] Новости:  На  странице  новостей,  странице  Императора  и  обзоре  планеты\n    рендерятся одной процедурой и используют один  темплейт.  Индикатор  свежих\n    новостей теперь  ориентируется  на  дату  просмотра,  а  не  на  количество\n    новостей\n[@] ТМ: Исправлена лишняя ошибка в логах, если количество изменяемой ТМ равно 0\n    (например, при бесплатном рынке или офицерах)\n[@] ТМ: Изменение ТМ в переменной $user производится в теле rpg_points_change\n[@] БД: В таблице `users` убраны неиспользуемые поля. Изменены типы  нескольких\n    полей на более логичные.\n    Добавлено поле `ally_tag`\n    В таблицы `users` и `alliance` добавлены поля с текущими значениями ранга и\n    очков статистики\n[@] Админка/Настройки:   Состояние  все  чекбоксов  (включен/выключен)   теперь\n    определются в темлейте\n\n\n\nProject \"SuperNova.WS\" Release 30d4 \"Emailing phalanx research antiSSF\"\n~~\n[!] Документация: Добавлен файл с английской версией  инструкции  по  установке\n    /docs/install-en.txt (google translated)\n[!] Локализация: Доступен выбор языка до входа в игру на страницах регистрации,\n    логина, восстановления забытого пароля.\n    Все доступные до входа в игру страницы (включая статистику, банлист, список\n    контактов, настройки вселенной итд) используют  выбранный  при  регистрации\n    язык.\n    При  регистрации  выбранный язык сохраняется в настройках пользователя\n\n[%] Отправка флотов: Исправлена ошибка на mySQL серверах со  включенной  опцией\n    STRICT_TRANS_TABLES\n[%] Сообщения:  Исправлена  ошибка  отправки  сообщений  от  системы  на  mySQL\n    серверах со STRICT_TRANS_TABLES\n[%] Локализация: Исправлена ошибка загрузки не-дефолтного языка\n\n\n\nProject \"SuperNova.WS\" Release 30 \"Emailing phalanx research antiSSF\"\n~~\n[!] Сообщения:  Добавлена  возможность  пересылки  личных  сообщений  на  емейл\n    игрока. Возможность включается администратором сервера в настройках - опция\n    \"Разрешить  пересылку  ЛС  на  email\".  После  этого  в  настройках  игрока\n    появляются дополнительные опции для всех категорий входящих сообщений\n[!] Фаланга: Переписан вывод фаланги с использованием  функций  СН.  Теперь  он\n    выглядит аналогично списку  событий  флота  на  странице  \"Обзор  планеты\".\n    Алгоритм работы - почти оффовский:\n    1. Показываются все флоты, летящие от сканируемой планеты или же к ней\n    2. Полет A --> B\n       a) скан B => можно увидеть время прибытия флота\n       b) скан A => можно увидеть время возвращения флота (но не  его  прибытия\n       на B)\n    3. Возвращение B --> A\n       a) скан B => не видно ничего\n       b) скан A => виден возвращающийся флот\n    4. Особый случай: задание \"Передислокация\" A --> B\n       a) флот виден только на B, но не на A\n       b) после отзыва флот нигде не виден\n    5. Полнота информации о флотах зависит от уровня шпионажа (см. ниже)\n[!] Админка: С нуля создан интерфейс редактирования юнитов/ресурсов на  планете\n    - пункт меню  \"Редактировать\"  в  разделе  \"Планета\".  Он  доступен  членам\n    команды начиная с Оператора (authlevel=2) и выше\n[!] Исследования: Полностью переписан интерфейс Лаборатории\n    Очередь исследований приведена к стандартному виду\n    Обработка очереди исследований теперь производится  при  каждом  обновлении\n    страницы, а не только при входе в интерфейс Лаборатории\n[!] АнтиРМФ: Если флот атакующего уничтожен за один раунд, то:\n      1. Атакующий не получает отчета о бое\n      2. Флоты, находящиеся в удержании так и остаются на орбите\n\n[+] Антибашинг: Добавлена возможность настройки системы антибашинга на страницу\n    настроек сервера\n[+] Мировые константы: Добавлена информация о текущих настройках антибашинга\n[+] Обновление: Добавлена возможность форсировать обновление в случае проблем с\n    автоматическим   обновлением.    Возможность    доступна    в    интерфейсе\n    Администратора, пункт меню \"Утилиты\"\n[+] Сообщения:  Теперь  настройки  автоматических  уведомлений   включены   по\n    умолчанию для новых игроков\n[+] Верфь:  Переверстан  интерфейс   Верфи  и  Обороны.  Вид  очереди  построек\n    обновлен. Теперь они выглядят так же, как и очередь постройки зданий\n    Добавлена возможность удалить последний добавленный элемент из очереди\n    Кнопка \"Построить\" дублируется возле каждого юнита. Функционал  сохранен  -\n    по её нажатию будут построены все выбранные юниты\n[+] Обзор планеты: Вид очереди построек верфи  и  очереди  исследований  теперь\n    аналогичен очереди построек зданий\n[+] Обзор планеты/Фаланга: На  количество  отображаемой  информации  о  летящих\n    чужих флотах влияет эффективный уровень шпионажа (технология+наемник):\n    Меньше 4 - нет информации о летящем флоте\n    Больше 4 - видно общее количество кораблей во флоте и везет ли флот ресурсы\n    Больше 6 - виден качественный состав флота - т.е. сколько групп кораблей во\n    флоте и сколько кораблей в каждой группе\n    Больше 8 - видно точное количество ресурсов в трюмах кораблей\n    Больше 10 - виден количественный состав флота\n[+] Сообщения:  Изменена  цветовая  кодировка  сообщений.\n    Категории  сообщений переупорядочены\n    Добавлена  возможность  очистить  сообщения  определенной   категории,   не\n    открывая их - на случай переполнения  почтового ящика\n    Добавлена подсказка\n[+] Админка/Список планет: \"Список планет\", \"Список лун\" и  \"Активные  планеты\"\n    используют один и тот же код и шаблон. Список планет теперь показывает  тип\n    планеты, хозяина планеты (имя и ИД), а для лун - родительскую планету и  её\n    ИД\n[+] Чат:  Боевые  отчеты   теперь  преобразуются  в  ссылки.   Из   соображений\n    безопасности работают только ссылки на текущем сервере. По клику на  ссылку\n    боевой отчет открывается в новом окне\n[+] Сообщения: Изменена процедура  генерации  писем  с  уведомлением  о  боевом\n    отчете следующим образом:\n      1. Если бой  закончился  за  один  раунд  проигрышем  атакующего,  то  он\n         получает сообщение о том, что связь с флотом прервалась и не  получает\n         никакой дополнительной информации (включая ссылку на боевой отчет)\n      2. Теперь все участники боя (включая членов  САБа  и  хозяинов  флотов  в\n         удержании) получают одинаковые письма (кроме случая, описанного в п.1)\n      3. Уведомление о бое всегда содержит потери атакующих  и  оброняющихся  и\n         сведения о поле обломков\n      4. Сведения о вывозе ресурсов с планеты добавляются в  отчет  только  при\n         выигрыше атакующих\n      5. Уведомления теперь корректно окрашиваются  для  всех  участников  боя:\n         красным, если участник проиграл, зеленым - если выиграл, оранжевым - в\n         случае ничьи\n      6. Все числа в уведомлении теперь форматируются\n\n[~] Отладка: Теперь при отключенной  глобальной  отладке  не  генерируется  лог\n    запросов\n[~] Обновление: Автоапдейтер на время работы отключает отладку вне  зависимости\n    от глобальных настроек\n[~] Обновление: Оптимизирована работа апдейтера для апгрейда с 25-28 релизов\n[~] Обновление: Апдейтер использует собственные процедуры запросов к БД\n[~] Обновление:   Ускорен   автоапдейтер   при   повторном   запуске   на   уже\n    сконвертированных таблицах\n[~] Настройки:  Включение  защиты  планет   от   атак   доступно   только   для\n    Администраторов во избежание злоупотреблений\n[~] Флоты: Переупорядочен список кораблей\n[~] Новапедия: Отредактировано короткое описание большого транспорта\n[~] Офицеры: Уровень офицеров указывается и при максимальном уровне\n[~] Админка/Бан: По умолчанию включена галочка РО и выставлен срок бана в 3 дня\n    Бан и разбан объединены в один пункт меню и на одну страницу интерфейса\n[~] Админка/Меню: Объединены разделы \"Планета\" и \"Луна\" в раздел \"Вселенная\"\n[~] Админка/Меню: Под названием сервера добавлены часы серверного времени\n[~] Боевой отчет: Если в результате боя появилась луна, то в отчет  пишется  её\n    имя, а не имя планеты, на орбите которой она была создана\n[~] Вселенная:  Теперь  при  создании  луны  с  орбиты  списывается  количество\n    обломков, из которых сформировалась луна\n\n[%] Вселенная:  Исправлена  ошибка  создания  новой  планеты  при  регистрации,\n    возникающая при определенных настройках MySQL\n[%] Настройки:  Исправлена   ошибка  сохранения   настроек,   возникающая   при\n    определенных настройках MySQL\n[%] SQL: Исправлена ошибка с индексами в таблице users\n[%] Обслуживание: Исправлена ошибка при обслуживании таблицы Альянсов\n[%] Локализация/Английский: Исправлены баги в локализации\n\n[@] Логи: Изменения Тёмной Материи  вынесены  из  глобального  лога в отдельную\n    таблицу. Это  существенно  облегчило  поиск  неисправностей  на  сервере  и\n    подозрительных действий пользователей. Старые записи перенесены в отдельную\n    таблицу\n[@] БД: Все существующие таблицы сконвертированы в UTF8\n[@] БД: Все существующие таблицы переведены на InnoDB\n[@] sn_timer: Таймер корректно работает с очередью, в которой количество юнитов\n    больше 1\n[@] Сообщения: Переработаны файлы локализации\n[@] Общие: Устаревшая функция SYS_mysqlSmartEscape заменена на соответствующие\n[@] Общие: Устаревшие массивы  $pricelist,  $resources,  $reslist,  $sn_groups,\n    $CombatCaps заменены в коде на $sn_data\n[@] Обслуживание: Корректно пересчитывается количество аккаунтов в БД\n\n\n\nProject \"SuperNova.WS\" Release 29 \"Quest messaging\"\n~~\n[!] Readme: Добавил раздел  \"Благодарности\".  Желающие  вычеркнуть  себя  могут\n    написать мне об этом в личку\n[!] Квесты: Добавлена подсистема квестов\n     1. Администратор сервера может самостоятельно добавлять  новые  квесты\n     2. В настоящий момент доступны квесты на постройку зданий  и  обороны,  на\n     исследование и на постройку кораблей (триггер срабатывает при  наличии  на\n     планете Х кораблей одного типа)\n     3. За квесты можно выставлять награду - определенного количество одного из\n     ресурсов: металл, кристалл, дейтерий или ТМ.\n     4. Доступ к конструктору квестов осуществляется из меню \"Квесты\"  страницы\n     администрирования. Создание квестов доступно только Администратору сервера\n     (auth_level = 3)\n     5.  Игрок  может  просматривать  список  доступных  квестов  и  их  статус\n     (выполнен или не выполнен)\n     6. По выполнению квеста игроку высылается письмо с уведомлением\n     7. Общее количество  и  количество  выполненных  квестов  видно  игроку  в\n     навбаре\n     8. Администратор может посмотреть выполненные квесты игрока  по  ссылке  в\n     его профиле (Поиск через админпанель)\n     9.   Игроки,   превысившие   условия   квеста,    автоматически    получат\n     вознаграждение при следующей проверке на  критерии  выполнения.  Например,\n     если целью квеста является постройка шахты 10го уровня, то  при  постройке\n     шахты  выше  9го  уровня  на  любой  планете   игрок   получит   квестовое\n     вознаграждение. То же самое верно и  по  отношению  к  уничтожению  шахты.\n     Однако, если при уничтожении шахты её уровень окажется ниже 10го, то игрок\n     вознаграждения не получит, хотя он уже и имел шахту 10го уровня\n[!] Сообщения: Полностью переписана  система  сообщений.  В  системе  произошли\n    следующие изменения:\n    1. Можно писать письма любому игроку! Форма создания нового  письма  теперь\n    корректно обрабатывает  имена  игроков,  введенных  в  строку  \"Кому\".  Эта\n    возможность доступна из списка  категорий  сообщений  по  ссылке  \"Написать\n    сообщение\" в самом низу таблицы категорий\n    2. В списке писем теперь работает чекбокс в заголовке. Клик на нем приведет\n    к выбору всех сообщений. Повторный клик - к снятию всех отметок\n    3. Добавлен дополнительный диапазон для удаления сообщений - \"Все сообщения\n    данной категории\". Внимание! В категории \"Все сообщения\" его выбор приведет\n    к полной очистке почтового ящика!\n    4. При  первом  открытии  страницы  создания  нового  сообщения  больше  не\n    выскакивают угрожающие красные надписи\n    5. Счетчик  сообщений в  навбаре  работает  без  задержек. Т.е. если  игрок\n    перешел  в  категорию  с  непрочитанными  сообщениями,  счетчик   изменится\n    соответствующим образом  сразу  же  после  перехода,  а  не  при  следующей\n    загрузке страницы\n    6. Множество мелких стилистических доработок\n    7. Оптимизированы алгоритмы работы подсистемы сообщений, а так же  почти  в\n    два раза уменьшен объем  передаваемой  информации  от  клиента  к  серверу.\n    Особенно это заметно при удалении большого количества сообщений\n[!] Сообщения:   Настройка  автоматических  уведомлений.  Теперь  в  настройках\n    пользователя можно отключить получения определенных категории сообщений.  В\n    этот список входят: Шпионские отчёты, Военные отчёты,  Отчеты  переработки,\n    Прибытие флота, Отчёты экспедиций, Сообщения очереди построек.\n[!] Настройки: Полностью переписана система настроек пользователя\n\n[+] Империя: Добавлено цветовое кодирование для производящих структур.  Уровень\n    производства, выставленный на странице \"Ресурсы\",  кодируется  цветом  фона\n    соответствующей ячейки: зеленый -  100%,  желтый  -  80-90%%,  оранжевый  -\n    70-50%%, красный - 40-10%%, цвет  фона  -  0%  или  структура  не  является\n    производящей. Пропорционально уровню производительности  меняется  и  длина\n    кодированной полоски\n[+] Интерфейс: Новый тип  сортировки  планет  -  по  общему  количество  полей.\n    Учитываются терраформеры (на планетах) и лунные базы (на лунах)\n[+] Боевой отчет: Локализован\n[+] Сообщения: Добавлена новый класс сообщений \"Сообщения Администрации\". К ним\n    относятся:\n    1. Сообщения системы квестов\n    2. Новости сервера\n    3. Сообщения Администрации\n    Уведомления этого класса НЕ МОГУТ быть отключены в настройках пользователя\n[+] Сообщения:  Восстановлена  функциональноксть  класса  сообщений  \"Сообщения\n    очереди построек\". К ним относятся:\n    1. Уведомления о  завершении  исследований.  Уведомление  высылается  после\n       входа на страницу исследований\n    2. Уведомления об окончании работы верфи на планете. Уведомление высылается\n       по окончании очереди строительства Верфи\n    3. Уведомление об окончалии строительных работ на  планете  (постройка  или\n       разрушение здания). Уведомление высылается пакетно в  полуавтоматическом\n       режиме.  Это  означает,  что  сообщение  генерируется  каждый  раз   при\n       обращении к планете (сканирование шпионажом  или  игроком,  переключение\n       активной планеты игроком итд). При  этом  в  сообщение  указываются  все\n       изменения, произошедшие на момент обращения\n    Уведомления этого класса могут быть отключены в настройках пользователя\n\n[~] Навбар: По клику на  счетчике  флотов  и  экспедиций  открывается  страница\n    флотов в полете\n[~] Список планет: Изменена цветовая  кодировка  полосы  застройки:  зеленый  -\n    менее 50% застройки, желтый - не меньше 50% и меньше 80%,  оранжевый  -  не\n    меньше 80% и меньше 100%, красный - 100% застройки\n[~] Список планет: На полосы застройки добавилось  застроенное  и  максимальное\n    количество полей на планете\n[~] Список планет: Сортировка учитывается в списке планет на  страницах  \"Обзор\n    планеты\" и \"Империя\"\n[~] Флоты: Переоформлена страница своза ресурсов. Теперь так же считается сумма\n    выбранных для своза ресурсов по каждой планете\n[~] Империя: Переформатирована страница Империи\n[~] Боевой отчет: Добавлено количество убитых корблей в предыдущем раунде.\n[~] Флоты/Сообщения: Приглашение в САБ теперь  относится  к  категории  \"Боевые\n    отчеты\", а не к категории \"Сообщения от игроков\"\n[~] Обзор планеты: Теперь если планет больше  5,  то  они  показываются  в  две\n    колонки\n[~] Мировые  константы:  Добавлен  вывод  информации  о  разешении  прокачки  и\n    разрешении удержания на слабом соаловце\n[~] Шпионаж: Изменены сообщения при уничтожении разведфлота\n\n[~] Навбар: Клик на индикаторе сообщений Администрации, Альянса или от  другого\n    игрока сразу открывает просмотр соответствующих сообщений\n[~] Вселенная:  Во  всплывающем  меню  на  поле  обломков  добавлена  индикация\n    количества летящих переработчиков игрока\n\n[%] Альянсы: Исправлена ошибка невозможности выхода из Альянса\n[%] Антибашинг: Исправлена ошибка в подсистеме  техобслуживания  из-за  которой\n    удалялись нужные записи из таблицы башинга и расчет волн сбрасывался\n[%] Вселенная: Исправлена ошибка  с  неправильным  наложением  иконки  летящего\n    флота, когда флоту отдана команда \"Обратно\"\n[%] Обзор планеты: Исправлена ошибка с добавлением лишнего события, когда флоту\n    отдана команда \"Обратно\"\n[%] Вселенная: Исправлена возможность  появления  фантомных  лун.  Существующие\n    фантомные луны удалены\n[%] JS: Исправлена ошибка в js_timer, приводящая к некорректной работе  таймера\n    после таймера с типом \"date&time with delta\"\n[%] Флоты:   Исправлена  ошибка  обработки  миссии  \"Транспорт\"  если   планета\n    назначения не существует\n[%] Флоты: Исправлена  ошибка,  позволяющая  атаковать  в  САБе  более  слабого\n    игрока\n[%] Вселенная: Исправлены ошибка  индикации  на  поле  обломков  чужих  летящих\n    переработчиков  и  ошибка   невозможности   послать   переработчики   через\n    всплывающее меню, если уже на то же поле летят чужие переработчики\n[%] Бой: В отчете правильно указывается количество захваченных ресурсов\n[%] Бой: Исправлены предупреждения, выдающиеся в случае,  когда  какой-либо  из\n    флотов пустой\n[%] Настройки: Исправлена  ошибка,  позволяющая  уйти  в  отпуск  когда  что-то\n    строится или исследуется на неосновной планете\n\n[@] SQL: Обновлен дамп БД до версии 29\n[@] Update: Изменен апдейтер так, что бы не  выдавать  ошибки  при  апгрейде  с\n    версии <26. Так же введена конфигурация времени блокировки базы апдейтером\n[@] Update: Добавлена процедура валидизации  таблицы  игроков  по  имени  и  ИД\n    Альянса. Так же добавлены constraint на эти поля\n[@] GIT: GIT теперь так же игнорирует SQL-файлы в каталоге бэкапа\n[@] Локализация: Немного оптимизирована локализация\n[@] Настройки: Изменена подсистема дополнительных настроек пользователя\n[@] Системное: sys_get_param_int_val теперь так же  обрабатывает  непрописанные\n    чекбоксы - те, которые возвращают \"on\"  и  \"off\".  Для  таких  значение  он\n    возвращаеет соответственно \"1\" и \"{$default}\"\n[@] Флоты: Добавлена возможность разрешения прокачки транспортировкой  ресурсов\n    от более слабого игрока  более  сильному.  Опция  \"Разрешить  прокачку\"  на\n    странице настроек сервера\n[@] Настройки:  Добавлена   опция  сервера,  разрешающая  удержание  на  слабом\n    со-аловце. Опция \"Разрешить  удержание  на  слабом  соаловце\"  на  странице\n    настроек сервера\n[@] Апдейтер: Исправлена ошибка добавления внешних ключей к таблице users\n\n\n\nProject \"SuperNova.WS\" Release 28 \"Chatting Diplomacy Alliance Bashing\"\n~~\n[!] Чат: Полностью  переписан  внутренний  чат.\n      1.  Полностью  переписана  JS-часть.  В  частности  -  AJAX-вызовы   теперь\n      осуществляются  через jQuery\n      2. Добавлена  заплатка  для  корректной  работы  чата  в  глюкофоксе\n      3. Добавлена защита на стороне клиента от слишком частых обновлений\n      4. Полностью переработана PHP-часть чата\n      5. Корректно показывает заголовок в истории  чата  -  \"общий  чат\"  и  \"чат\n      альянса\" соответственно\n      6. История чата теперь грузиться в виде нормальной страницы СН, а не в виде\n      \"обмылка\", как раньше\n      7. Содержимое языкового файла chat.mo отфильтровано и влито в system.mo\n      8. Множество других добавлений и усовершенствований\n[!] Альянсы: Добавленв подсистема дипломатии\n    Информация о текущих дипломатических  отношениях  Альянса  отображается  на\n    странице информации об Альянсе и доступна для просмотра любому игроку\n    Глава Альянса может начинать переговоры и принимать предложения  от  других\n    Альянсов, выбрав пункт \"Переговоры\" в заголовке таблицы дипломатии. Там  же\n    можно сделать предложение об изменении отношений  другому  Альянсу.  Нельзя\n    сделать предложение  текущих  отношений  (т.е.  если  Альянсы  находятся  в\n    отношениях \"Война\" нельзя  опять  предложить  отношение  \"Война\")  В  общем\n    случае что бы изменилось отношения между Альянсами, другая  сторона  должна\n    потвердить предложение об изменении по ссылке \"Переговоры\", доступной главе\n    Альянса (исключения из данного правила изложены ниже).\n    Отношения между Альянсами бывают следующие:\n      1. Нейтралитет. Отношение  по  умолчанию.  Нет  никаких  ограничений  или\n      бонусов\n      2. Война. Отключается  система  защиты  башинга  между  членами  Альянса,\n      находящимися  в  состоянии  войны.   Автоматически   принимается   второй\n      стороной. Подробнее см.ниже в описании защиты от башинга\n      3. Мир. Рекомендуется выставлять этот статус  после  заключения  пакта  о\n      ненападении.  С  точки  зрения  движка  оно  ничем   не   отличается   от\n      \"Нейтралитета\" и нужно для информирования остального игрового  сообщества\n      о неких устных договоренностях - буде в  таком  информировании  возникнет\n      нужда. Альянсы вольны следовать или не следовать данной  рекомендации,  а\n      так же решать  -  хотят  они  оповестить  Вселенную  об  изменении  своих\n      отношений или нет\n[!] Антибашинг: Добавлена система защиты от башинга. Защита не  дает  отправить\n    больше флотов и волн, чем указано в правилах.\n    Настройки по умолчанию - в течении 24 часов 3 волны по 3 атаки не более  30\n    минут между атаками в одной волне.\n    Настроить систему защиты от башинга можно через таблицу `config`  -  группа\n    параметров fleet_bashing_*.\n    Установка параметра fleet_bashing_attack в  0  означает  полное  отключение\n    системы защиты.\n      1. Атакой считаются миссии: \"Атака\",  \"Совместная  атака\"  и  \"Уничтожить\n      луну\". Миссия \"Ракетная атака\" атакой не считается\n      2. Учитываются флоты в полете. Т.е. если игрок уже запустил две  волны  и\n      еще одна находится в полете - он больше не сможет запускать флоты.\n      3. Атаки засчитываются по факту - т.е.  если  полностью  отменить  волну,\n      находящуюся в полете, игрок сразу же  сможет  послать  на  планету  новые\n      флоты, не дожидаясь возвращения волны\n      4. Атаки учитываются  вне  зависимости  от  результата  (выигрыш,  ничья,\n      проигрыш)\n      5.  При  САБе  атака  засчитывается  ВСЕМ  нападающим  -  дабы   избежать\n      \"карусели\", т.е. когда несколько игроков по  очереди  запускают  САБы,  а\n      остальные к ним присоединяются\n      6. Все флоты одного игрока в одном САБе считаются как один флот\n      7. Если Альянсы находятся в  отношении  \"Война\",  защита  от  башинга  не\n      работает\n      8. Объявление войны не требует согласия. Это означает, что когда Альянс А\n      предложил  Альянсу  Б  отношение  \"Война\",  это  предложение  принимается\n      автоматически и отношения устанавливаются сразу для обоих Альянсов\n      9. Выход из состояния войны требует согласия обоих сторон\n      10. Выход из состояния войны обратной  силы  не  имеет!  Т.е.  если  было\n      объявлено перемирие  когда  планеты  одного  из  Альянсов  находятся  под\n      атакой, то флоты все равно долетят и совершат нападение  -  какое  бы  ни\n      было новое отношение между Альянсами  (если,  конечно,  атакующий  их  не\n      отзовет)\n      11.  А  вы  знаете  почему   такой   относительно   простой   вещи   (40+\n      человекочасов) нет на Оффе? А что бы денежки снимать за разбан!\n[!] Экономика: Изменена выработка энергии.\n      Во-первых  -  модификатор  скорости игры теперь не действует на энергию.\n      Во-вторых  -  температура   планеты   влияет   на   выработку   солнечных\n      электростанций\n      В-третьих - Производство энергии на  термоядерной  электростанции  теперь\n      считается по формуле оффа:\n        (Э) * 30 * (1,05 + (Е) * 0,01) ^ (Э)\n      где Э - уровень электростанции, Т - уровень энергетической технологии\n      В-четвертых - энергетическая технология  больше  не  дает  дополнительный\n      бонус к производительности электроэнергии\n      Выработка энергии изменена исходя из следующих правил:\n      1. Электростанция может поддерживать одну шахту и один синтезатор рудника\n      того же уровня (взято с оффа)\n      2. Формула выработки термоядерной электростанции взята с оффа\n      3.  Средняя  температура  на  планете  -  20  градусов  (это  планеты   с\n      минимальной температурой 0 градусов и максимальной 40 градусов). На такой\n      планете эффективность солнечной электростанции будет 100%\n\n[+] Интерфейс:  В  списках  летящих  флотов  к  количеству  кораблей  во  флоте\n    добавляется в конце знак \"+\" если флот везет ресурсы\n[+] Обзор Империи: Добавлена температура планеты. В колонке ИТОГО - минимальная\n    и максимальная среди всех температур\n[+] Новости: Добавлена возможность массовой рассылки новости всем игрокам\n[+] Экономика: Изменена схема работы МИС. Теперь МИС работает следующим образом:\n      1. По каждой планете вычисляется эффективный уровень исследования (ЭУИ) =\n      уровень лаборатории / (0,5 ^ уровень нанитки)\n      2. Планеты сортируются по эффективному уровню\n      3. Отсекаются планеты с уровенм лаборатории, недостаточным для проведения\n      данного исследования\n      4. Выбирается верхние (уровень МИС + 1) планет в списке и суммируется ЭУИ\n      этих планет\n      5. Получившееся число подставляется в формулу вычисления времени исследования\n      Следствия:\n      1. Нанолаборатория теперь увеличивают эффективность лаборатории только на\n      той планете, на которой они расположены\n      2. Время исследования теперь одинаково на  всех  планетах.  На  некоторых\n      планетах чуть больше, на некоторых - чуть меньше, но в среднем  -  лучше,\n      чем было раньше\n      3. Имеет смысл держать только (уровень МИС + 1) планет  с  лабораториями.\n      Остальные просто не будут подключаться.\n      4. Примечание к следствию 3 - собственно, так было и раньше -  все  равно\n      исследование могло проводиться только на одной планете\n[+] Навбар: Переработана ячейка сообщений\n[+] Навбар:  Добавлено  количество  флотов  и  экспедиций  в  полете  и  всего.\n    Количество флотов и экспедиций в  полете  интерактивно:  оно  автоматически\n    изменяется в соответствие с происходящими событиями - прибытие, возвращение\n    и окончание миссии флота (как они должны были произойти на момент  загрузки\n    страницы).  При  наведении  курсора  на  соответствующую  ячейку  всплывает\n    подсказка с описанием ближайшего события\n[+] Список планет: Справа от иконки планеты добавлены три колонки, показывающие\n    процент производительности шахт и  синтезаторов:  серый  -  шахта  металла,\n    голубой - синтезатор кристаллов, фиолетовый -  синтезатор дейтерия.  Высота\n    колонки пропорциональна проценту производства, а  фон  кодирует  диапазоны:\n    желтый - 80-90%, оранжевый - 50-70%%, красный - меньее 50%. На высоту и фон\n    колонки влияет ИСКЛЮЧИТЕЛЬНО процент производства, выставленный на странице\n    \"Ресурсы\"\n[+] Империя: Цифра производства ресурсов теперь  кодируется  цветом  аналогично\n    фону колонки прозиводства ресурсов (см. выше)\n[+] Свезти ресурсы: Добавлен JS-счетчик  общего  количества  свозимых  ресурсов\n    с учетом чекбоксов\n[+] Свезти ресурсы: Добавлено отображение количества необходимых  ресурсов  при\n    переходе по кнопке \"Свезти ресурсы\" со страницы постройки\n[+] Император: Добавлена дата регистрации: \"Император [Имярек] с [дата]\"\n\n[~] Экономика: Энергия считается более аккуратно\n[~] Экономика:  Изменена   формула  расчета  МИС.  Теперь  нанолаборатории   на\n    планетах, включенных в МИС так же работают\n[~] Вселенная: Правильно считается минимальная и максимальная температура луны\n[~] Вселенная: Стартовая планета теперь имеет температуру 0/40\n[~] Чат: Ники модераторов (auth_level=1) и  операторов  (auth_level=2)  в  чате\n    теперь тоже выделяются.  По  умолчании  соответственно  зеленым  и  красным\n    цветом\n[~] Новости: Чекбокс \"Разослать новость игрокам\" включен по умолчанию\n[~] НоваПедия: \"Ракетный двигатель\" переименован в \"Химический\", а \"Импульсный\"\n    - в  \"Ионный\".  Для  них  полностью  изменено  описание.  Так  же  изменено\n    соответствующе описание кораблей.\n[~] HTML: Исправлен хидер, что бы быть W3C-compliant\n[~] Флоты: Страница 0 -  перемещена  кнопка  \"Дальше\"  на  одну  строку  вверх.\n    Добавена кнопка \"Свезти ресурсы\"\n[~] Список  забаненных:  Полностью  переписан.  Список  теперь  сортируется  по\n    возрастанию даты бана - последние забаненные появляются  в  начале  списка.\n    Добавлено отображение разбанов\n[~] Чат: Добавлена трансляция в смайлы сочетания \":)\" - :smile\n[~] Произведено   разделение  между  \"Релизом\"   и   \"Версией\"   в   интерфейсе\n    пользователя. \"Релиз\" - это крупное обновление движка, выкладываемое в виде\n    одного файла в общий доступ. Версия - небольшое  обновление,  недостаточное\n    для  смены  номера  релиза.  Подробнее  об  этом  можно  прочесть  в  файле\n    /docs/html/developer.html\n\n[~] Флоты: Удалена ссылка на редактирование закладок со  страницы  1  -  теперь\n    закладки можно редактировать через левое меню\n\n[%] Рапорты: На странице просмотра  рапортов  максимальное  количество  в  поле\n    ввода кода выставлено в 32\n[%] Флоты:  Теперь   при  возврате  последнего  флота  в  САБе  САБ   корректно\n    уничтожается\n[%] Ракеты: Исправлена ошибка в процедуре ракетной атаки - технологии  щитов  и\n    брони были перепутаны местами\n[%] Экономика:  Исправлен   глюк  с  невычитанием  дейтерия  при  отрицательном\n    балансе\n[%] JS: Исправлен глюк в скрипте таймера из-за которого  не  отсчитывало  назад\n    ресурсы при переполненных складах\n[%] Империя: Исправлена ошибка отображения маскимального  количества  полей  на\n    луне\n[%] НоваПедия:   Исправлена  очепятка  из-за  которой  в  списке  кораблей   на\n    химических двигателях не показывался переработчик\n[%] Фаерфокс: Исправлен ВНЕЗАПНЫЙ отказ глюкобага отправлять сообщения  в  чат.\n    Тормозилла  -  так  поддерживать!  Ибо   то,   что   висит   можно   только\n    поддерживать.\n[%] Альянсы: Исправлены ошибки редактирования информации Альянсов\n[%] Статистика: Исправлено неправильно отображение даты  последнего  обновления\n    статистики при просмотре статистики Альянсов\n[%] Чат: Исправлена ошибка парсинга смайлов \":(\" и \";)\" (код последнего заменен\n    на \":wink:\" из-за непоняток с парсингом)\n[%] Админка: Исправлены сообщения \"Page not found\" в формах\n\n[@] БД: Изменена структура таблицы банов banned\n[@] Система:  Добавлена  процедура  ежедневного  обслуживания:  чистка  таблицы\n    башинга, чистка таблицы САБ\n[@] Админка: Проставлены права доступа к отдельным страницам в  зависимости  от\n    уровня.\n    1. Модератор (authlevel=1) имеет доступ к  следующим  страницам:  overview,\n    activeplanet, banned, changelog, planetlist, statbuilder, tools, md5enc. Он\n    может: видеть список  игроков  онлайн  и  их  активность,  видет  список  и\n    активность планет,  вручную  обновлять  статистику,  банить  и  разбанивать\n    пользователей\n    2. Оператор (authlevel=2) дополнительно имеет доступ к следующим страницам:\n    add_building,  add_def,  add_money,   add_moon,   add_research,   add_ship,\n    del_building,  del_def,  del_money,   del_research,   del_ship,   moonlist,\n    showfliyingfleets. Дополнительно к функциям модератора он может:  добавлять\n    и убирать на планетах здания, корабли, защиту, ресурсы; добавлять и убирать\n    технологии игрока; видеть все луны и добавлять луны к  планетам;  видеть  и\n    редактировать флоты в полете\n    3.  Администратор   (authlevel=3)   имеет   доступ   ко   всем   страницам,\n    включая     delete_user,     admin_darkmatter,     errors,     maintenance,\n    maintenance_ajax,   messagelist,    messall,    admin_chat,    paneladmina,\n    planetcompensate, settings, userlist. Дополнительно к функциям оператора он\n    может: добавлять и убирать ТМ у игроков; видеть  полный  список  игроков  с\n    IP-адресами;  удалять  игроков;  запускать   процедуру   обслуживания   БД;\n    просматривать и удалять личные сообщения; просматривать и удалять сообщения\n    чата; просматривать и  удалять  сообщения  системы  логов;  изменять  права\n    пользователей; изменять настройки игры; возмещать игроку  стоимость  затрат\n    на планету\n[@] Админка: Введена дополнительная защита от взлома. Теперь член команды  игры\n    не может назначить кому-либо уровень доступа, равный  или  больший  своего.\n    Таким образом через админку невозможно  назначить  второго  Администратора.\n    Однако это можно проделать напрямую в БД\n[@] Админка: Исправлено ложное  срабатывание  системы  определения  взлома  при\n    обновлении пользователем страницы  \"Флоты  в  полете\"  сразу  после  отдачи\n    команды \"Обратно\" последнему из текущих флотов.  Так  же  в  предупреждение\n    теперь логгится состав флот, который пытался вернуть пользователь\n[@] Разработка: добавлен каталог '.local' для облегчения  разработки.  Файлы  в\n    этом каталоге игнорируются GIT-ом, но при этом корректно подключают внешние\n    файлы для обработки и выполнения\n[@] Разработка:  добавлена  процедура  sn_db_perform().  Отныне  для   вставки\n    одиночных записей следует использовать только её. См. файл db.php\n\n\n\nProject \"SuperNova.WS\" Release 27 - We speak English! (2011-03-15 00:10)\n~~\n[!] Движок:  Теперь  СН  может  размещаться  на  веб-сервере  на  любом  уровне\n    вложенности каталогов\n[!] Локализация: Добавлена английская локализация (с)  madmax1991.\n[!] Локализация: Серьезно переработаны файлы локализации: добавлена полноценная\n    информация о локализации (файл language.mo в каталоге локализации); удалено\n    множество неиспользуемых файлов;  несколько  маленьких  файлов  локализации\n    \"влиты\" в system.mo. Формат файла language.mo на текущий момент объявляется\n    финальным. Просьба всем локализаторам придерживаться этого формата\n[!] Включена возможность выбора языка для пользователей\n[!] Закладки: Полностью переписана система закладок. Теперь закладки хранятся в\n    отдельной таблице и не захламляют данные пользователя. Полностью переделано\n    редактирование закладок\n[!] Боевой отчет: Добавлена страница для просмотра боевого отчета по его  коду.\n    В меню в раздел \"Информация\" добавлена соответствующая ссылка\n[!] Интерфейс: Разнесены по разным страницам  отправка  флота  и  информация  о\n    флотах в полете. Теперь флот отправляется через пункт меню \"Флот на орбите\"\n    раздела \"Планета\", а информация о летящих флотах доступна через пункт  меню\n    \"Флоты в полете\" раздела \"Империя\"\n[!] \"Крейсер\" переименован в \"Эсминец\", а \"Линкор\" - в \"Крейсер\"\n\n[+] Админка: Язык игры по  умолчанию  теперь  выбирается  из  списка  доступных\n    языков\n[+] ЧаВо: В настройках сервера  добавлена  возможность  задать  URL  для  ЧаВо.\n    Добавлен соответствующий пункт в левое меню\n[+] Добавлен файл /README на английском языке.\n\n[~] Экономика/Ресурсы: По многочисленным просьбам добавлена колонка \"В час\"\n[~] Админка: Процедура  обслуживания  БД  теперь  удаляет  только  сообщения  с\n    неизвестным адресатом и сообщения, старше 30 дней\n[~] Регистрация: Пароль теперь так же указывается на странице  пост-регистрации\n    - на случай, если письмо с паролем не дойдет до адресата\n[~] Флоты: Немного изменил страницу 1 отправки флотов - теперь закладки, базы и\n    боевые союзы выводятся бок-о-бок в три колонки\n[~] Логин: Переработаны  меню  страниц  логина,  регистрации  и  восстановления\n    забытого пароля. Теперь оно одинаково  для  всех  страниц  и  кроме  старых\n    пунктов дополнительно включает блок ссылок логин/регистрация/восстановление\n    пароля, ссылку на FAQ, ссылку на новости сервера  (к  ним  теперь  разрешен\n    доступ незалогиненных/забаненных пользователей).\n\n[~] Интерфейс: Теперь если в настройках сервера отсутсвует какой  либо  из  URL\n    (адрес форума, ссылка на правила, ссылка на FAQ), то соответствующие пункты\n    меню и ссылки скрываются или не подсвечиваются. В дампе БД по умолчанию все\n    URL идут пустыми\n[~] Интерфейс: Переработана страница ТМ. Теперь если в конфигурации отсутствует\n    URL с подробностями покупки ТМ - информация о возможности покупки просто не\n    выводится\n\n[%] Экономика/Ресурсы: Добавлена проверка на корректный процент производства на\n    странице \"Ресурсы\"\n[%] Исследования: Теперь невозможно исследовать технологии во  время  постройки\n    лаборатории или нанолаборатории\n[%] Вселенная: Исправлена ошибка  с  неправильной  ссылкой  на  экспедицию  при\n    количестве планет в системе не равном 15\n[%] Флот: Закрыта уязвимость к передаче неправильных ИД  кораблей  на  странице\n    флота\n[%] Флот: Добавлена проверка на корректное время Экспедиций и Удержания\n[%] Флот: Теперь корректно выводится сообщени об ошибке при  попытке  отправить\n    флот на несуществующую планету\n[%] Флот: исправлена ошибка  создания  САБа  в  случае,  когда  летит  максимум\n    флотов\n[%] Флот: исправлена ошибка неудаления пустого САБа после атаки\n[%] Флот: Исправлена ошибка дублирования списка САБов\n[%] Альянсы: Исправлена ошибка вывода заявки  на  странице  управления  заявок.\n    Теперь  если  в  заявке  есть   переводы   строк,   то   заявка   корректно\n    форматируется\n[%] Админка: Исправлена баннерилка\n[%] Вселенная: Корректно выводится сообщение при попытке нападения на игрока  в\n    отпуске\n[%] Безопасность:   Исправлена  ошибка  невозможности  доступа   незалогиненных\n    пользователей к статистике, контактам итд\n[%] Реклама: Исправлена ошибка несохранения  параметров  рекламного  блока  при\n    перезапуске сервера\n\n[@] БД: Версия БД увеличена до 27. Обновлен дамп\n[@] Обновлены инструкции в файле /docs/install.txt\n[@] SQL: По умолчанию в дампе счетчик посещений - отключен, а игра - включена\n[@] Теперь можно отключить защиту слабых игроков, сбросив game_noob_factor в 0\n[@] Исправлено несколько участков кода, выдающих предупреждения в логи\n[@] Убрана запись сообщения в логи о постройке ПЗ/отмене очереди верфи\n[@] Чат: Изменилось выделение сообщений команды сервера. Теперь выделяется  ник\n    и  в  сообщениях  можно  использовать  все  стандартные  цвета.  Переменная\n    конфигурации     chat_admin_msgFormat      заменена      на      переменную\n    chat_admin_highlight. В ней можно использовать  HTML  коды.  Место  вставки\n    ника обозначается как '$1' - см. пример в БД\n[@] Изменена система слежения за  игроками.  Теперь  не  логгируются  неопасные\n    запросы (SELECT, START TRANSACTION,  COMMIT,  ROLLBACK).  При  логгировании\n    запроса так же записывается стандартный набор переменных\n[@] В процедуру апдейта добавлена очистка старого списка САБов\n[@] Альянсы: Страницы \"Настройка прав  доступа\",  \"Список  участиков  Альянса\",\n    \"Поиск Альянса\", \"Создание Альянса\", \"Управление заявками\"  переделаны  под\n    PTE\n\n\n\nProject \"SuperNova.WS\" Release 26 - Speed It Up! (2011-02-07 13:02)\n~~\n[!] changelog.txt разделен на пользовательский и девелоперский. Все изменения в\n    процессе  разработки  новой  версии  вносятся  в   changelog_dev.txt.   При\n    подготовке релиза в changelog.txt переносятся только  финальные  изменения\n[!] Новый менеджер летящих флотов - теперь рядно-блокирующий,  тразакционный  и\n    кэширующий! Обновленный код на порядок уменьшает нагрузку на сервер за счет\n    отказа от табличной блокировки и встроенной системе  кэширования  запросов.\n    Система  \"событий\"  гарантирует  корректный  порядок  обработки  флотов  (с\n    точностью до секунды - предела текущей организации таблиц).  Целостность  и\n    валидность результатов обеспечена добавлением транзакций. Всё это позволило\n    уменьшить дискретизацию обработки флотов до 4х секунд на  серверах  с  300+\n    онлайна.\n\n[+] Свезти ресурсы: Теперь можно отдельно выбирать типы ресурсов, которые нужно\n    свезти\n[+] Свезти ресурсы: Добавлены колонки \"ВСЕГО\" - общее  количество  ресурсов  на\n    планете и \"Трюмы\" - общая грузоподъемность транспортного флота  с  цветовым\n    кодированием. Галочки в колонке \"ВСЕГО\" теперь не влияют на набор вывозимых\n    ресурсов, а используются только для облегчения выбора\n[+] Полностью переписана работа алгоритма миссии \"Шпионаж\". Теперь он полностью\n    соответствует оффовскому (расчет разницы  уровней,  влияние  зондов,  шансы\n    обнаружения шпионажа флотом итд). От старого кода осталась только генерация\n    рапорта\n[+] Скрипт обновления статистики завернут в транзакции.  Это  дало  50  кратное\n    увеличение скорости исполнения\n\n[~] Вселенная: Теперь отображаются  все  планеты  в  зависимости  от  настройки\n    game_max_planet, а не 16 штук\n[~] Восстановление пароля: Теперь  в  восстановлении  пароля  участвует  адрес,\n    указанный при регистрации\n[~] Автологин стал более параноидальным. Это позволило  избавиться  от  большей\n    части ошибок\n[~] Настройки: При попытке уйти в отпуск теперь  выдается  отдельное  сообщение\n    при летящих флотах и отдельное сообщение при постройке на планетах\n[~] Чёрный Рынок: Торговец ресурсами - Добавлена защита от повторного обмена\n[~] Сообщения: В навбаре теперь дополнительно отображаются количество сообщений\n    от других игроков  и  количество  сообщений  от  членов  альянса.  Цветовое\n    кодирование зависит от скина и такое же, как на странице сообщений\n[~] Навбар: Стартовое  время  и  количество  ресурсов  теперь  прописывается  в\n    темплейте. На медленных соединениях до конца загрузки  страницы  в  навбаре\n    будут не заглушки, а значения, акутуальные на момент запроса\n[~] Император: Все числа теперь показываются с разделителем тысяч\n[~] Настройки  пользователя:   Ко  всем  чекбоксам  добавлены  label   for   на\n    соответствующие  надписи.  Теперь  можно  кликать  на   надпись,   что   бы\n    переключить чекбокс\n[~] Здания: По окончании очереди построек страница автоматически обновляется\n[~] Верфь: Теперь при отмене очереди открывается та же страница верфи (флот или\n    оборона)\n[~] Обзор планеты: При выборе луны в списке колоний её иконка увеличивается  на\n    50%.  Изображение  планеты,  которой  принадлежит  луна,  так  же  остается\n    увеличенным\n[~] Обзор планеты/Обзор Империи: На превьюшку колонии добавлена иконка тележки.\n    Щелчок на неё открывает интерфейс своза ресурсов на данную планету/луну\n[~] Обзор планеты/Обзор Империи: В списке планет полоса  застроенности  планеты\n    перенесена под картинку\n[~] Обзор планеты: Иконка вражеской атаки на превьюшке луны составляет 70%  для\n    лучшей различимости при невыбранной луне\n\n[%] Настройки: Исправлена ошибка с невозможностью ухода в отпуск\n[%] Флоты:  Исправлены   ошибка  \"Не  хватает  топлива\"  при  отправки   миссий\n    \"Колонизация\" и \"Экспедиция\"\n[%] Чёрный Рынок/Торговец ресурсами: закрыт  эксплойт,  позволяющий  обменивать\n    ресурсы на ТМ\n[%] Чёрный   Рынок:     Теперь   невозможно   продать/купить   не-корабль    на\n    соответствующей странице Чёрного Рынка\n[%] Боевка: Корабли теперь не будут увозить отрицательные ресурсы с планеты\n[%] Экономика: Производительность теперь не может быть отрицательной\n[%] Экономика: Исправлена ошибка, когда при отрицательной добыче ресурсы  могли\n    уйти в минус\n[%] Таймер: JS-таймер теперь не будет считать ресурсы меньше 0\n[%] Закрыта дыра, позволяющая поставить  в  очередь  больше  зданий,  чем  есть\n    свободного места на планете\n[%] Устранена ошибка зацикливания перенаправлений при удалении колонии\n[%] Устранена ошибка неначисления опыта/уровня  при  атаках,  если  в  процессе\n    генерации рапорта произошел сбой\n[%] Вселенная: Исправлена ошибка неотображения названий планет с символов \"'\"\n[%] Своз ресурсов: перед свозом ресурсов не пересчитывались данные  о  ресурсах\n    на планетах, поэтому свозилось количество ресурсов меньшее, чем могло\n[%] Сообщения:   Теперь   при   появлении   нового   внутриигрового   сообщения\n    (возвращение флота, шпионаж, отчеты переработчиков итд), счетчик  сообщений\n    реагирует сразу, а не после обновления страницы\n\n[@] Версия БД увеличена до 26. Обновленный дамп\n[@] Теперь админ тоже не может  ходить  по  клиентской  части  при  отключенном\n    сервере во избежание порчи БД при бэкапе или апгрейде\n[@] Таблица  `errors`  влита  в   `logs`.   Новая   таблице   переформирована:\n    добавлены новые  и  переупорядочены  старые  поля  для  удобства  просмотра\n    человеком; добавлено поле с  дампом  переменных  для  дальнейшего  разбора.\n    Старые таблицы сохранены соответственно как `errors_backup` и `logs_backup`\n[@] Изменены коды  операции  со  статистикой.  Раньше  код  102  пересекался  с\n    операцией \"изменение Тёмной Материи\"\n[@] Добавлена   обработка  ситуации,  когда  после  установки  движок   сначала\n    запустили на пустой базе, а только затем залили в неё дамп\n[@] Вселенная: Добавлена обработка исключительной ситуации, когда у планеты нет\n    хозяина: в цикл просмотра системы, в выгрузку  кэша  в  темплейт,  в  самом\n    темплейте\n[@] Счетчик посещений теперь можно отключить из настроек сервера\n[@] Чёрный Рынок/Торговец ресурсами: Переработан  внутренний  алгоритм  работы.\n    Модуль теперь использует коды событий 9xx\n[@] Унифицированы алгоритмы и  файлы  постройки  флота  и  защиты.  Это  должно\n    полностью снять  проблемы  с  отрицательными  ресурсами  после  верфи  и  с\n    постройкой лишних единиц флота/защиты на верфях\n[@] Добавлена защита от выполнения файлов .INC вне основного кода\n\n\n[!] Информацию о предыдущих изменениях можно  посмотреть  в  полном  чейнджлоге\n    /docs/changelog_dev.txt</h1>\n</body>\n</html>\n"
  },
  {
    "path": "docs/html/coding-guidelines.html",
    "content": "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">\n<html xmlns=\"http://www.w3.org/1999/xhtml\" dir=\"ltr\" lang=\"en\" xml:lang=\"en\">\n<head>\n\n<meta http-equiv=\"content-type\" content=\"text/html; charset=utf-8\" />\n<meta http-equiv=\"content-style-type\" content=\"text/css\" />\n<meta http-equiv=\"content-language\" content=\"en\" />\n<meta http-equiv=\"imagetoolbar\" content=\"no\" />\n<meta name=\"resource-type\" content=\"document\" />\n<meta name=\"distribution\" content=\"global\" />\n<meta name=\"copyright\" content=\"2007 phpBB Group\" />\n<meta name=\"keywords\" content=\"\" />\n<meta name=\"description\" content=\"Olympus coding guidelines document\" />\n<title>phpBB3 &bull; Coding Guidelines</title>\n\n<link href=\"stylesheet.css\" rel=\"stylesheet\" type=\"text/css\" media=\"screen, projection\" />\n\n</head>\n\n<body id=\"phpbb\" class=\"section-docs\">\n\n<div id=\"wrap\">\n\t<a id=\"top\" name=\"top\" accesskey=\"t\"></a>\n\t<div id=\"page-header\">\n\t\t<div class=\"headerbar\">\n\t\t\t<div class=\"inner\"><span class=\"corners-top\"><span></span></span>\n\n\t\t\t<div id=\"doc-description\">\n\t\t\t\t<a href=\"../index.php\" id=\"logo\"><img src=\"site_logo.gif\" alt=\"\" /></a>\n\t\t\t\t<h1>Coding Guidelines</h1>\n\t\t\t\t<p>Olympus coding guidelines document</p>\n\t\t\t\t<p style=\"display: none;\"><a href=\"#start_here\">Skip</a></p>\n\t\t\t</div>\n\n\t\t\t<span class=\"corners-bottom\"><span></span></span></div>\n\t\t</div>\n\t</div>\n\n\t<a name=\"start_here\"></a>\n\n\t<div id=\"page-body\">\n\n<!-- BEGIN DOCUMENT -->\n\n<p>These are the phpBB Coding Guidelines for Olympus, all attempts should be made to follow them as closely as possible.</p>\n\n<h1>Coding Guidelines</h1>\n\n\t<div class=\"paragraph menu\">\n\t\t<div class=\"inner\"><span class=\"corners-top\"><span></span></span>\n\n\t\t<div class=\"content\">\n\n<ol>\n\t<li><a href=\"#defaults\">Defaults</a>\n\t<ol style=\"list-style-type: lower-roman;\">\n\t\t<li><a href=\"#editorsettings\">Editor Settings</a></li>\n\t\t<li><a href=\"#fileheader\">File Header</a></li>\n\t\t<li><a href=\"#locations\">File Locations</a></li>\n\t\t<li><a href=\"#constants\">Special Constants</a></li>\n\t</ol>\n\t</li>\n\t<li><a href=\"#code\">Code Layout/Guidelines</a>\n\t<ol style=\"list-style-type: lower-roman;\">\n\t\t<li><a href=\"#namingvars\">Variable/Function/Class Naming</a></li>\n\t\t<li><a href=\"#codelayout\">Code Layout</a></li>\n\t\t<li><a href=\"#sql\">SQL/SQL Layout</a></li>\n\t\t<li><a href=\"#optimizing\">Optimizations</a></li>\n\t\t<li><a href=\"#general\">General Guidelines</a></li>\n\t\t<li><a href=\"#phprestrictions\">Restrictions on the Use of PHP</a></li>\n\t</ol>\n\t</li>\n\t<li><a href=\"#styling\">Styling</a>\n\t<ol style=\"list-style-type: lower-roman;\">\n\t\t<li><a href=\"#cfgfiles\">Style Config Files</a></li>\n\t\t<li><a href=\"#genstyling\">General Styling Rules</a></li>\n\t</ol></li>\n\t<li><a href=\"#templating\">Templating</a>\n\t<ol style=\"list-style-type: lower-roman;\">\n\t\t<li><a href=\"#templates\">General Templating</a></li>\n\t\t<li><a href=\"#inheritance\">Template Inheritance</a></li>\n\t</ol></li>\n\t<li><a href=\"#charsets\">Character Sets and Encodings</a></li>\n\t<li><a href=\"#translation\">Translation (<abbr title=\"Internationalisation\">i18n</abbr>/<abbr title=\"Localisation\">L10n</abbr>) Guidelines</a>\n\t<ol style=\"list-style-type: lower-roman;\">\n\t\t<li><a href=\"#standardisation\">Standardisation</a></li>\n\t\t<li><a href=\"#otherconsiderations\">Other considerations</a></li>\n\t\t<li><a href=\"#writingstyle\">Writing Style</a></li>\n\t</ol>\n\t</li>\n\t<li><a href=\"#vcs\">VCS Guidelines</a>\n\t<ol style=\"list-style-type: lower-roman;\">\n\t\t<li><a href=\"#repostruct\">Repository structure</a></li>\n\t\t<li><a href=\"#commitmessage\">Commit Messages and Repository Rules</a></li>\n\t</ol>\n\t</li>\n\t<li><a href=\"#changes\">Guidelines Changelog</a></li>\n\t<li><a href=\"#disclaimer\">Copyright and disclaimer</a></li>\n</ol>\n\n\t\t</div>\n\n\t\t<span class=\"corners-bottom\"><span></span></span></div>\n\t</div>\n\n\t<hr />\n\n<a name=\"defaults\"></a><h2>1. Defaults</h2>\n\n\t<div class=\"paragraph\">\n\t\t<div class=\"inner\"><span class=\"corners-top\"><span></span></span>\n\n\t\t<div class=\"content\">\n\n<a name=\"editorsettings\"></a><h3>1.i. Editor Settings</h3>\n\n\t<h4>Tabs vs Spaces:</h4>\n\t<p>In order to make this as simple as possible, we will be using tabs, not spaces. We enforce 4 (four) spaces for one tab - therefore you need to set your tab width within your editor to 4 spaces. Make sure that when you <strong>save</strong> the file, it's saving tabs and not spaces. This way, we can each have the code be displayed the way we like it, without breaking the layout of the actual files.</p>\n\t<p>Tabs in front of lines are no problem, but having them within the text can be a problem if you do not set it to the amount of spaces every one of us uses. Here is a short example of how it should look like:</p>\n\n\t<div class=\"codebox\"><pre>\n{TAB}$mode{TAB}{TAB}= request_var('mode', '');\n{TAB}$search_id{TAB}= request_var('search_id', '');\n\t</pre></div>\n\n\t<p>If entered with tabs (replace the {TAB}) both equal signs need to be on the same column.</p>\n\n\t<h3>Linefeeds:</h3>\n\t<p>Ensure that your editor is saving files in the UNIX (LF) line ending format. This means that lines are terminated with a newline, not with Windows Line endings (CR/LF combo) as they are on Win32 or Classic Mac (CR) Line endings. Any decent editor should be able to do this, but it might not always be the default setting. Know your editor. If you want advice for an editor for your Operating System, just ask one of the developers. Some of them do their editing on Win32.</p>\n\n\t<a name=\"fileheader\"></a><h3>1.ii. File Layout</h3>\n\n\t<h4>Standard header for new files:</h4>\n\t<p>This template of the header must be included at the start of all phpBB files: </p>\n\n\t<div class=\"codebox\"><pre>\n/**\n*\n* @package {PACKAGENAME}\n* @version &#36;Id: &#36;\n* @copyright (c) 2007 phpBB Group\n* @license http://opensource.org/licenses/gpl-license.php GNU Public License\n*\n*/\n\t</pre></div>\n\n\t<p>Please see the <a href=\"#locations\">File Locations section</a> for the correct package name.</p>\n\n\t<h4>PHP closing tags</h4>\n\n\t<p>A file containg only PHP code should not end with the optional PHP closing tag <strong>?&gt;</strong> to avoid issues with whitespace following it.</p>\n\n\t<h4>Newline at end of file</h4>\n\n\t<p>All files should end in a newline so the last line does not appear as modified in diffs, when a line is appended to the file.</p>\n\n\t<h4>Files containing inline code:</h4>\n\n\t<p>For those files you have to put an empty comment directly after the header to prevent the documentor assigning the header to the first code element found.</p>\n\n\t<div class=\"codebox\"><pre>\n/**\n* {HEADER}\n*/\n\n/**\n*/\n{CODE}\n\t</pre></div>\n\n\t<h4>Files containing only functions:</h4>\n\n\t<p>Do not forget to comment the functions (especially the first function following the header). Each function should have at least a comment of what this function does. For more complex functions it is recommended to document the parameters too.</p>\n\n\t<h4>Files containing only classes:</h4>\n\n\t<p>Do not forget to comment the class. Classes need a separate @package definition, it is the same as the header package name. Apart from this special case the above statement for files containing only functions needs to be applied to classes and it's methods too.</p>\n\n\t<h4>Code following the header but only functions/classes file:</h4>\n\n\t<p>If this case is true, the best method to avoid documentation confusions is adding an ignore command, for example:</p>\n\n\t<div class=\"codebox\"><pre>\n/**\n* {HEADER}\n*/\n\n/**\n* @ignore\n*/\nSmall code snipped, mostly one or two defines or an if statement\n\n/**\n* {DOCUMENTATION}\n*/\nclass ...\n\t</pre></div>\n\n\t<a name=\"locations\"></a><h3>1.iii. File Locations</h3>\n\n\t<p>Functions used by more than one page should be placed in functions.php, functions specific to one page should be placed on that page (at the bottom) or within the relevant sections functions file. Some files in <code>/includes</code> are holding functions responsible for special sections, for example uploading files, displaying &quot;things&quot;, user related functions and so forth.</p>\n\n\t<p>The following packages are defined, and related new features/functions should be placed within the mentioned files/locations, as well as specifying the correct package name. The package names are bold within this list:</p>\n\n\t<ul>\n\t\t<li><strong>phpBB3</strong><br />Core files and all files not assigned to a separate package</li>\n\t\t<li><strong>acm</strong><br /><code>/includes/acm</code>, <code>/includes/cache.php</code><br />Cache System</li>\n\t\t<li><strong>acp</strong><br /><code>/adm</code>, <code>/includes/acp</code>, <code>/includes/functions_admin.php</code><br />Administration Control Panel</li>\n\t\t<li><strong>dbal</strong><br /><code>/includes/db</code><br />Database Abstraction Layer.<br />Base class is <code>dbal</code>\n\t\t\t<ul>\n\t\t\t\t<li><code>/includes/db/dbal.php</code><br />Base DBAL class, defining the overall framework</li>\n\t\t\t\t<li><code>/includes/db/firebird.php</code><br />Firebird/Interbase Database Abstraction Layer</li>\n\t\t\t\t<li><code>/includes/db/msssql.php</code><br />MSSQL Database Abstraction Layer</li>\n\t\t\t\t<li><code>/includes/db/mssql_odbc.php</code><br />MSSQL ODBC Database Abstraction Layer for MSSQL</li>\n\t\t\t\t<li><code>/includes/db/mysql.php</code><br />MySQL Database Abstraction Layer for MySQL 3.x/4.0.x/4.1.x/5.x</li>\n\t\t\t\t<li><code>/includes/db/mysqli.php</code><br />MySQLi Database Abstraction Layer</li>\n\t\t\t\t<li><code>/includes/db/oracle.php</code><br />Oracle Database Abstraction Layer</li>\n\t\t\t\t<li><code>/includes/db/postgres.php</code><br />PostgreSQL Database Abstraction Layer</li>\n\t\t\t\t<li><code>/includes/db/sqlite.php</code><br />Sqlite Database Abstraction Layer</li>\n\t\t\t</ul>\n\t\t</li>\n\t\t<li><strong>diff</strong><br /><code>/includes/diff</code><br />Diff Engine</li>\n\t\t<li><strong>docs</strong><br /><code>/docs</code><br />phpBB Documentation</li>\n\t\t<li><strong>images</strong><br /><code>/images</code><br />All global images not connected to styles</li>\n\t\t<li><strong>install</strong><br /><code>/install</code><br />Installation System</li>\n\t\t<li><strong>language</strong><br /><code>/language</code><br />All language files</li>\n\t\t<li><strong>login</strong><br /><code>/includes/auth</code><br />Login Authentication Plugins</li>\n\t\t<li><strong>VC</strong><br /><code>/includes/captcha</code><br />CAPTCHA</li>\n\t\t<li><strong>mcp</strong><br /><code>mcp.php</code>, <code>/includes/mcp</code>, <code>report.php</code><br />Moderator Control Panel</li>\n\t\t<li><strong>ucp</strong><br /><code>ucp.php</code>, <code>/includes/ucp</code><br />User Control Panel</li>\n\t\t<li><strong>utf</strong><br /><code>/includes/utf</code><br />UTF8-related functions/classes</li>\n\t\t<li><strong>search</strong><br /><code>/includes/search</code>, <code>search.php</code><br />Search System</li>\n\t\t<li><strong>styles</strong><br /><code>/styles</code>, <code>style.php</code><br />phpBB Styles/Templates/Themes/Imagesets</li>\n\t</ul>\n\n\t<a name=\"constants\"></a><h3>1.iv. Special Constants</h3>\n\n\t<p>There are some special constants application developers are able to utilize to bend some of phpBB's internal functionality to suit their needs.</p>\n\n\t<div class=\"codebox\"><pre>\nPHPBB_MSG_HANDLER          (overwrite message handler)\nPHPBB_DB_NEW_LINK          (overwrite new_link parameter for sql_connect)\nPHPBB_ROOT_PATH            (overwrite $phpbb_root_path)\nPHPBB_ADMIN_PATH           (overwrite $phpbb_admin_path)\nPHPBB_USE_BOARD_URL_PATH   (use generate_board_url() for image paths instead of $phpbb_root_path)\nPHPBB_DISABLE_ACP_EDITOR   (disable ACP style editor for templates)\nPHPBB_DISABLE_CONFIG_CHECK (disable ACP config.php writeable check)\n\nPHPBB_ACM_MEMCACHE_PORT     (overwrite memcached port, default is 11211)\nPHPBB_ACM_MEMCACHE_COMPRESS (overwrite memcached compress setting, default is disabled)\nPHPBB_ACM_MEMCACHE_HOST     (overwrite memcached host name, default is localhost)\n\nPHPBB_QA                   (Set board to QA-Mode, which means the updater also checks for RC-releases)\n</pre></div>\n\n<h4>PHPBB_USE_BOARD_URL_PATH</h4>\n\n<p>If the <code>PHPBB_USE_BOARD_URL_PATH</code> constant is set to true, phpBB uses generate_board_url() (this will return the boards url with the script path included) on all instances where web-accessible images are loaded. The exact locations are:</p>\n\n<ul>\n\t<li>/includes/session.php - user::img()</li>\n\t<li>/includes/functions_content.php - smiley_text()</li>\n</ul>\n\n<p>Path locations for the following template variables are affected by this too:</p>\n\n<ul>\n\t<li>{T_THEME_PATH} - styles/xxx/theme</li>\n\t<li>{T_TEMPLATE_PATH} - styles/xxx/template</li>\n\t<li>{T_SUPER_TEMPLATE_PATH} - styles/xxx/template</li>\n\t<li>{T_IMAGESET_PATH} - styles/xxx/imageset</li>\n\t<li>{T_IMAGESET_LANG_PATH} - styles/xxx/imageset/yy</li>\n\t<li>{T_IMAGES_PATH} - images/</li>\n\t<li>{T_SMILIES_PATH} - $config['smilies_path']/</li>\n\t<li>{T_AVATAR_PATH} - $config['avatar_path']/</li>\n\t<li>{T_AVATAR_GALLERY_PATH} - $config['avatar_gallery_path']/</li>\n\t<li>{T_ICONS_PATH} - $config['icons_path']/</li>\n\t<li>{T_RANKS_PATH} - $config['ranks_path']/</li>\n\t<li>{T_UPLOAD_PATH} - $config['upload_path']/</li>\n\t<li>{T_STYLESHEET_LINK} - styles/xxx/theme/stylesheet.css (or link to style.php if css is parsed dynamically)</li>\n\t<li>New template variable {BOARD_URL} for the board url + script path.</li>\n</ul>\n\n\t\t</div>\n\n\t\t<div class=\"back2top\"><a href=\"#wrap\" class=\"top\">Back to Top</a></div>\n\n\t\t<span class=\"corners-bottom\"><span></span></span></div>\n\t</div>\n\n\t<hr />\n\n<a name=\"code\"></a><h2>2. Code Layout/Guidelines</h2>\n\n\t<div class=\"paragraph\">\n\t\t<div class=\"inner\"><span class=\"corners-top\"><span></span></span>\n\n\t\t<div class=\"content\">\n\n\t<p>Please note that these Guidelines applies to all php, html, javascript and css files.</p>\n\n\t<a name=\"namingvars\"></a><h3>2.i. Variable/Function/Class Naming</h3>\n\n\t<p>We will not be using any form of hungarian notation in our naming conventions. Many of us believe that hungarian naming is one of the primary code obfuscation techniques currently in use.</p>\n\n\t<h4>Variable Names:</h4>\n\t<p>Variable names should be in all lowercase, with words separated by an underscore, example:</p>\n\n\t<div class=\"indent\">\n\t\t<p><code>$current_user</code> is right, but <code>$currentuser</code> and <code> $currentUser</code> are not.</p>\n\t</div>\n\n\t<p>Names should be descriptive, but concise. We don't want huge sentences as our variable names, but typing an extra couple of characters is always better than wondering what exactly a certain variable is for. </p>\n\n\t<h4>Loop Indices:</h4>\n\t<p>The <em>only</em> situation where a one-character variable name is allowed is when it's the index for some looping construct. In this case, the index of the outer loop should always be $i. If there's a loop inside that loop, its index should be $j, followed by $k, and so on. If the loop is being indexed by some already-existing variable with a meaningful name, this guideline does not apply, example:</p>\n\n\t<div class=\"codebox\"><pre>\nfor ($i = 0; $i &lt; $outer_size; $i++)\n{\n   for ($j = 0; $j &lt; $inner_size; $j++)\n   {\n      foo($i, $j);\n   }\n}\n\t</pre></div>\n\n\t<h4>Function Names:</h4>\n\t<p>Functions should also be named descriptively. We're not programming in C here, we don't want to write functions called things like \"stristr()\". Again, all lower-case names with words separated by a single underscore character. Function names should preferably have a verb in them somewhere. Good function names are <code>print_login_status()</code>, <code>get_user_data()</code>, etc. </p>\n\n\t<h4>Function Arguments:</h4>\n\t<p>Arguments are subject to the same guidelines as variable names. We don't want a bunch of functions like: <code>do_stuff($a, $b, $c)</code>. In most cases, we'd like to be able to tell how to use a function by just looking at its declaration. </p>\n\n\t<h4>Class Names:</h4>\n\n\t<p>Apart from following the rules for function names, all classes should meet the following conditions:</p>\n\t<ul>\n\t\t<li>Every class must be defined in a separate file.</li>\n\t\t<li>The classes have to be located in a subdirectory of <code>includes/</code>.</li>\n\t\t<li>Classnames to be prefixed with <code>phpbb_</code> to avoid name clashes, the filename should not contain the prefix.</li>\n\t\t<li>Class names have to reflect the location of the file they are defined in. The longest list of prefixes, separated by underscores, which is a valid path must be the directory in which the file is located. So the directory names must not contain any underscores, but the filename may. If the filename would be empty the last directory name is used for the filename as well.</li>\n\t\t<li>Directories should typically be a singular noun (e.g. <code>dir</code> in the example below, not <code>dirs</code>.</li>\n\t</ul>\n\n\t<p>So given the following example directory structure you would result in the below listed lookups</p>\n\t<div class=\"codebox\"><pre>\nincludes/\n  class_name.php\n  dir/\n    class_name.php\n    dir.php\n      subdir/\n        class_name.php\n\t</pre></div>\n\n\t<div class=\"codebox\"><pre>\nphpbb_class_name            - includes/class_name.php\nphpbb_dir_class_name        - includes/dir/class_name.php\nphpbb_dir                   - includes/dir/dir.php\nphpbb_dir_subdir_class_name - includes/dir/subdir/class_name.php\n\t</pre></div>\n\n\n\t<h4>Summary:</h4>\n\t<p>The basic philosophy here is to not hurt code clarity for the sake of laziness. This has to be balanced by a little bit of common sense, though; <code>print_login_status_for_a_given_user()</code> goes too far, for example -- that function would be better named <code>print_user_login_status()</code>, or just <code>print_login_status()</code>.</p>\n\n\t<h4>Special Namings: </h4>\n\t<p>For all emoticons use the term <code>smiley</code> in singular and <code>smilies</code> in plural.</p>\n\n\t<a name=\"codelayout\"></a><h3>2.ii. Code Layout</h3>\n\n\t<h4>Always include the braces:</h4>\n\t<p>This is another case of being too lazy to type 2 extra characters causing problems with code clarity. Even if the body of some construct is only one line long, do <em>not</em> drop the braces. Just don't, examples:</p>\n\n\t<p class=\"bad\">// These are all wrong. </p>\n\n\t<div class=\"codebox\"><pre>\nif (condition) do_stuff();\n\nif (condition)\n\tdo_stuff();\n\nwhile (condition)\n\tdo_stuff();\n\nfor ($i = 0; $i &lt; size; $i++)\n\tdo_stuff($i);\n\t</pre></div>\n\n\t<p class=\"good\">// These are all right. </p>\n\t<div class=\"codebox\"><pre>\nif (condition)\n{\n\tdo_stuff();\n}\n\nwhile (condition)\n{\n\tdo_stuff();\n}\n\nfor ($i = 0; $i &lt; size; $i++)\n{\n\tdo_stuff();\n}\n\t</pre></div>\n\n\t<h4>Where to put the braces:</h4>\n\t<p>This one is a bit of a holy war, but we're going to use a style that can be summed up in one sentence: Braces always go on their own line. The closing brace should also always be at the same column as the corresponding opening brace, examples:</p>\n\n\t<div class=\"codebox\"><pre>\nif (condition)\n{\n\twhile (condition2)\n\t{\n\t\t...\n\t}\n}\nelse\n{\n\t...\n}\n\nfor ($i = 0; $i &lt; $size; $i++)\n{\n\t...\n}\n\nwhile (condition)\n{\n\t...\n}\n\nfunction do_stuff()\n{\n\t...\n}\n\t</pre></div>\n\n\t<h4>Use spaces between tokens:</h4>\n\t<p>This is another simple, easy step that helps keep code readable without much effort. Whenever you write an assignment, expression, etc.. Always leave <em>one</em> space between the tokens. Basically, write code as if it was English. Put spaces between variable names and operators. Don't put spaces just after an opening bracket or before a closing bracket. Don't put spaces just before a comma or a semicolon. This is best shown with a few examples, examples:</p>\n\n\t<p>// Each pair shows the wrong way followed by the right way. </p>\n\n\t<div class=\"codebox\"><pre>\n$i=0;\n$i = 0;\n\nif($i&lt;7) ...\nif ($i &lt; 7) ...\n\nif ( ($i &lt; 7)&amp;&amp;($j &gt; 8) ) ...\nif ($i &lt; 7 &amp;&amp; $j &gt; 8) ...\n\ndo_stuff( $i, 'foo', $b );\ndo_stuff($i, 'foo', $b);\n\nfor($i=0; $i&lt;$size; $i++) ...\nfor ($i = 0; $i &lt; $size; $i++) ...\n\n$i=($j &lt; $size)?0:1;\n$i = ($j &lt; $size) ? 0 : 1;\n\t</pre></div>\n\n\t<h4>Operator precedence:</h4>\n\t<p>Do you know the exact precedence of all the operators in PHP? Neither do I. Don't guess. Always make it obvious by using brackets to force the precedence of an equation so you know what it does. Remember to not over-use this, as it may harden the readability. Basically, do not enclose single expressions. Examples:</p>\n\n\t<p class=\"bad\">// what's the result? who knows. </p>\n\t<div class=\"codebox\"><pre>\n$bool = ($i &lt; 7 &amp;&amp; $j &gt; 8 || $k == 4);\n\t</pre></div>\n\n\t<p class=\"bad\">// now you can be certain what I'm doing here.</p>\n\t<div class=\"codebox\"><pre>\n$bool = (($i &lt; 7) &amp;&amp; (($j &lt; 8) || ($k == 4)));\n\t</pre></div>\n\n\t<p class=\"good\">// But this one is even better, because it is easier on the eye but the intention is preserved</p>\n\t<div class=\"codebox\"><pre>\n$bool = ($i &lt; 7 &amp;&amp; ($j &lt; 8 || $k == 4));\n\t</pre></div>\n\n\t<h4>Quoting strings:</h4>\n\t<p>There are two different ways to quote strings in PHP - either with single quotes or with double quotes. The main difference is that the parser does variable interpolation in double-quoted strings, but not in single quoted strings. Because of this, you should <em>always</em> use single quotes <em>unless</em> you specifically need variable interpolation to be done on that string. This way, we can save the parser the trouble of parsing a bunch of strings where no interpolation needs to be done.</p>\n\t<p>Also, if you are using a string variable as part of a function call, you do not need to enclose that variable in quotes. Again, this will just make unnecessary work for the parser. Note, however, that nearly all of the escape sequences that exist for double-quoted strings will not work with single-quoted strings. Be careful, and feel free to break this guideline if it's making your code easier to read, examples:</p>\n\n\t<p class=\"bad\">// wrong </p>\n\t<div class=\"codebox\"><pre>\n$str = \"This is a really long string with no variables for the parser to find.\";\n\ndo_stuff(\"$str\");\n\t</pre></div>\n\n\t<p class=\"good\">// right</p>\n\t<div class=\"codebox\"><pre>\n$str = 'This is a really long string with no variables for the parser to find.';\n\ndo_stuff($str);\n\t</pre></div>\n\n\t<p class=\"bad\">// Sometimes single quotes are just not right</p>\n\t<div class=\"codebox\"><pre>\n$post_url = $phpbb_root_path . 'posting.' . $phpEx . '?mode=' . $mode . '&amp;amp;start=' . $start;\n\t</pre></div>\n\n\t<p class=\"good\">// Double quotes are sometimes needed to not overcroud the line with concentinations</p>\n\t<div class=\"codebox\"><pre>\n$post_url = \"{$phpbb_root_path}posting.$phpEx?mode=$mode&amp;amp;start=$start\";\n\t</pre></div>\n\n\t<p>In SQL Statements mixing single and double quotes is partly allowed (following the guidelines listed here about SQL Formatting), else it should be tryed to only use one method - mostly single quotes.</p>\n\n\t<h4>Commas after every array element:</h4>\n\t<p>If an array is defined with each element on its own line, you still have to modify the previous line to add a comma when appending a new element. PHP allows for trailing (useless) commas in array definitions. These should always be used so each element including the comma can be appended with a single line</p>\n\n\t<p class=\"bad\">// wrong</p>\n\t<div class=\"codebox\"><pre>\n$foo = array(\n\t'bar' => 42,\n\t'boo' => 23\n);\n\t</pre></div>\n\n\t<p class=\"good\">// right </p>\n\t<div class=\"codebox\"><pre>\n$foo = array(\n\t'bar' => 42,\n\t'boo' => 23,\n);\n\t</pre></div>\n\n\n\t<h4>Associative array keys:</h4>\n\t<p>In PHP, it's legal to use a literal string as a key to an associative array without quoting that string. We don't want to do this -- the string should always be quoted to avoid confusion. Note that this is only when we're using a literal, not when we're using a variable, examples:</p>\n\n\t<p class=\"bad\">// wrong</p>\n\t<div class=\"codebox\"><pre>\n$foo = $assoc_array[blah];\n\t</pre></div>\n\n\t<p class=\"good\">// right </p>\n\t<div class=\"codebox\"><pre>\n$foo = $assoc_array['blah'];\n\t</pre></div>\n\n\t<p class=\"bad\">// wrong</p>\n\t<div class=\"codebox\"><pre>\n$foo = $assoc_array[\"$var\"];\n\t</pre></div>\n\n\t<p class=\"good\">// right </p>\n\t<div class=\"codebox\"><pre>\n$foo = $assoc_array[$var];\n\t</pre></div>\n\n\t<h4>Comments:</h4>\n\t<p>Each complex function should be preceded by a comment that tells a programmer everything they need to know to use that function. The meaning of every parameter, the expected input, and the output are required as a minimal comment. The function's behaviour in error conditions (and what those error conditions are) should also be present - but mostly included within the comment about the output.<br /><br />Especially important to document are any assumptions the code makes, or preconditions for its proper operation. Any one of the developers should be able to look at any part of the application and figure out what's going on in a reasonable amount of time.<br /><br />Avoid using <code>/* */</code> comment blocks for one-line comments, <code>//</code> should be used for one/two-liners.</p>\n\n\t<h4>Magic numbers:</h4>\n\t<p>Don't use them. Use named constants for any literal value other than obvious special cases. Basically, it's ok to check if an array has 0 elements by using the literal 0. It's not ok to assign some special meaning to a number and then use it everywhere as a literal. This hurts readability AND maintainability. The constants <code>true</code> and <code>false</code> should be used in place of the literals 1 and 0 -- even though they have the same values (but not type!), it's more obvious what the actual logic is when you use the named constants. Typecast variables where it is needed, do not rely on the correct variable type (PHP is currently very loose on typecasting which can lead to security problems if a developer does not have a very close eye to it).</p>\n\n\t<h4>Shortcut operators:</h4>\n\t<p>The only shortcut operators that cause readability problems are the shortcut increment <code>$i++</code> and decrement <code>$j--</code> operators. These operators should not be used as part of an expression. They can, however, be used on their own line. Using them in expressions is just not worth the headaches when debugging, examples:</p>\n\n\t<p class=\"bad\">// wrong </p>\n\t<div class=\"codebox\"><pre>\n$array[++$i] = $j;\n$array[$i++] = $k;\n\t</pre></div>\n\n\t<p class=\"good\">// right </p>\n\t<div class=\"codebox\"><pre>\n$i++;\n$array[$i] = $j;\n\n$array[$i] = $k;\n$i++;\n\t</pre></div>\n\n\t<h4>Inline conditionals:</h4>\n\t<p>Inline conditionals should only be used to do very simple things. Preferably, they will only be used to do assignments, and not for function calls or anything complex at all. They can be harmful to readability if used incorrectly, so don't fall in love with saving typing by using them, examples:</p>\n\n\t<p class=\"bad\">// Bad place to use them</p>\n\t<div class=\"codebox\"><pre>\n($i &lt; $size &amp;&amp; $j &gt; $size) ? do_stuff($foo) : do_stuff($bar);\n\t</pre></div>\n\n\t<p class=\"good\">// OK place to use them </p>\n\t<div class=\"codebox\"><pre>\n$min = ($i &lt; $j) ? $i : $j;\n\t</pre></div>\n\n\t<h4>Don't use uninitialized variables.</h4>\n\t<p>For phpBB3, we intend to use a higher level of run-time error reporting. This will mean that the use of an uninitialized variable will be reported as a warning. These warnings can be avoided by using the built-in isset() function to check whether a variable has been set - but preferably the variable is always existing. For checking if an array has a key set this can come in handy though, examples:</p>\n\n\t<p class=\"bad\">// Wrong </p>\n\t<div class=\"codebox\"><pre>\nif ($forum) ...\n\t</pre></div>\n\n\t<p class=\"good\">// Right </p>\n\t<div class=\"codebox\"><pre>\nif (isset($forum)) ...\n\t</pre></div>\n\n\t<p class=\"good\">// Also possible</p>\n\t<div class=\"codebox\"><pre>\nif (isset($forum) &amp;&amp; $forum == 5)\n\t</pre></div>\n\n\t<p>The <code>empty()</code> function is useful if you want to check if a variable is not set or being empty (an empty string, 0 as an integer or string, NULL, false, an empty array or a variable declared, but without a value in a class). Therefore empty should be used in favor of <code>isset($array) &amp;&amp; sizeof($array) &gt; 0</code> - this can be written in a shorter way as <code>!empty($array)</code>.</p>\n\n\t<h4>Switch statements:</h4>\n\t<p>Switch/case code blocks can get a bit long sometimes. To have some level of notice and being in-line with the opening/closing brace requirement (where they are on the same line for better readability), this also applies to switch/case code blocks and the breaks. An example:</p>\n\n\t<p class=\"bad\">// Wrong </p>\n\t<div class=\"codebox\"><pre>\nswitch ($mode)\n{\n\tcase 'mode1':\n\t\t// I am doing something here\n\t\tbreak;\n\tcase 'mode2':\n\t\t// I am doing something completely different here\n\t\tbreak;\n}\n\t</pre></div>\n\n\t<p class=\"good\">// Good </p>\n\t<div class=\"codebox\"><pre>\nswitch ($mode)\n{\n\tcase 'mode1':\n\t\t// I am doing something here\n\tbreak;\n\n\tcase 'mode2':\n\t\t// I am doing something completely different here\n\tbreak;\n\n\tdefault:\n\t\t// Always assume that a case was not caught\n\tbreak;\n}\n\t</pre></div>\n\n\t<p class=\"good\">// Also good, if you have more code between the case and the break </p>\n\t<div class=\"codebox\"><pre>\nswitch ($mode)\n{\n\tcase 'mode1':\n\n\t\t// I am doing something here\n\n\tbreak;\n\n\tcase 'mode2':\n\n\t\t// I am doing something completely different here\n\n\tbreak;\n\n\tdefault:\n\n\t\t// Always assume that a case was not caught\n\n\tbreak;\n}\n\t</pre></div>\n\n\t<p>Even if the break for the default case is not needed, it is sometimes better to include it just for readability and completeness.</p>\n\n\t<p>If no break is intended, please add a comment instead. An example:</p>\n\n\t<p class=\"good\">// Example with no break </p>\n\t<div class=\"codebox\"><pre>\nswitch ($mode)\n{\n\tcase 'mode1':\n\n\t\t// I am doing something here\n\n\t// no break here\n\n\tcase 'mode2':\n\n\t\t// I am doing something completely different here\n\n\tbreak;\n\n\tdefault:\n\n\t\t// Always assume that a case was not caught\n\n\tbreak;\n}\n\t</pre></div>\n\n\t<h4>Class Members</h4>\n\t<p>Use the explicit visibility qualifiers <code>public</code>, <code>private</code> and <code>protected</code> for all properties instead of <code>var</code>.\n\n\t<p>Place the <code>static</code> qualifier before the visibility qualifiers.</p>\n\n\t<p class=\"bad\">//Wrong </p>\n\t<div class=\"codebox\"><pre>\nvar $x;\nprivate static function f()\n\t</pre></div>\n\n\t<p class=\"good\">// Right </p>\n\t<div class=\"codebox\"><pre>\npublic $x;\nstatic private function f()\n\t</pre></div>\n\n\t<h4>Constants</h4>\n\t<p>Prefer class constants over global constants created with <code>define()</code>.</p>\n\n\t<a name=\"sql\"></a><h3>2.iii. SQL/SQL Layout</h3>\n\n\t<h4>Common SQL Guidelines: </h4>\n\t<p>All SQL should be cross-DB compatible, if DB specific SQL is used alternatives must be provided which work on all supported DB's (MySQL3/4/5, MSSQL (7.0 and 2000), PostgreSQL (7.0+), Firebird, SQLite, Oracle8, ODBC (generalised if possible)).</p>\n\t<p>All SQL commands should utilise the DataBase Abstraction Layer (DBAL)</p>\n\n\t<h4>SQL code layout:</h4>\n\t<p>SQL Statements are often unreadable without some formatting, since they tend to be big at times. Though the formatting of sql statements adds a lot to the readability of code. SQL statements should be formatted in the following way, basically writing keywords: </p>\n\n\t<div class=\"codebox\"><pre>\n$sql = 'SELECT *\n&lt;-one tab-&gt;FROM ' . SOME_TABLE . '\n&lt;-one tab-&gt;WHERE a = 1\n&lt;-two tabs-&gt;AND (b = 2\n&lt;-three tabs-&gt;OR b = 3)\n&lt;-one tab-&gt;ORDER BY b';\n\t</pre></div>\n\n\t<p>Here the example with the tabs applied:</p>\n\n\t<div class=\"codebox\"><pre>\n$sql = 'SELECT *\n\tFROM ' . SOME_TABLE . '\n\tWHERE a = 1\n\t\tAND (b = 2\n\t\t\tOR b = 3)\n\tORDER BY b';\n\t</pre></div>\n\n\t<h4>SQL Quotes: </h4>\n\t<p>Double quotes where applicable (The variables in these examples are typecasted to integers before) ... examples: </p>\n\n\t<p class=\"bad\">// These are wrong.</p>\n\t<div class=\"codebox\"><pre>\n\"UPDATE \" . SOME_TABLE . \" SET something = something_else WHERE a = $b\";\n\n'UPDATE ' . SOME_TABLE . ' SET something = ' . $user_id . ' WHERE a = ' . $something;\n\t</pre></div>\n\n\t<p class=\"good\">// These are right. </p>\n\n\t<div class=\"codebox\"><pre>\n'UPDATE ' . SOME_TABLE . \" SET something = something_else WHERE a = $b\";\n\n'UPDATE ' . SOME_TABLE . \" SET something = $user_id WHERE a = $something\";\n\t</pre></div>\n\n\t<p>In other words use single quotes where no variable substitution is required or where the variable involved shouldn't appear within double quotes. Otherwise use double quotes.</p>\n\n\t<h4>Avoid DB specific SQL: </h4>\n\t<p>The &quot;not equals operator&quot;, as defined by the SQL:2003 standard, is &quot;&lt;&gt;&quot;</p>\n\n\t<p class=\"bad\">// This is wrong.</p>\n\t<div class=\"codebox\"><pre>\n$sql = 'SELECT *\n\tFROM ' . SOME_TABLE . '\n\tWHERE a != 2';\n\t</pre></div>\n\n\t<p class=\"good\">// This is right. </p>\n\t<div class=\"codebox\"><pre>\n$sql = 'SELECT *\n\tFROM ' . SOME_TABLE . '\n\tWHERE a &lt;&gt; 2';\n\t</pre></div>\n\n\t<h4>Common DBAL methods: </h4>\n\n\t<h4>sql_escape():</h4>\n\n\t<p>Always use <code>$db-&gt;sql_escape()</code> if you need to check for a string within an SQL statement (even if you are sure the variable cannot contain single quotes - never trust your input), for example:</p>\n\n\t<div class=\"codebox\"><pre>\n$sql = 'SELECT *\n\tFROM ' . SOME_TABLE . \"\n\tWHERE username = '\" . $db-&gt;sql_escape($username) . \"'\";\n\t</pre></div>\n\n\t<h4>sql_query_limit():</h4>\n\n\t<p>We do not add limit statements to the sql query, but instead use <code>$db-&gt;sql_query_limit()</code>. You basically pass the query, the total number of lines to retrieve and the offset.</p>\n\n\t<p><strong>Note: </strong> Since Oracle handles limits differently and because of how we implemented this handling you need to take special care if you use <code>sql_query_limit</code> with an sql query retrieving data from more than one table.</p>\n\n\t<p>Make sure when using something like \"SELECT x.*, y.jars\" that there is not a column named jars in x; make sure that there is no overlap between an implicit column and the explicit columns.</p>\n\n\t<h4>sql_build_array():</h4>\n\n\t<p>If you need to UPDATE or INSERT data, make use of the <code>$db-&gt;sql_build_array()</code> function. This function already escapes strings and checks other types, so there is no need to do this here. The data to be inserted should go into an array - <code>$sql_ary</code> - or directly within the statement if one or two variables needs to be inserted/updated. An example of an insert statement would be:</p>\n\n\t<div class=\"codebox\"><pre>\n$sql_ary = array(\n\t'somedata'\t\t=&gt; $my_string,\n\t'otherdata'\t\t=&gt; $an_int,\n\t'moredata'\t\t=&gt; $another_int\n);\n\n$db-&gt;sql_query('INSERT INTO ' . SOME_TABLE . ' ' . $db-&gt;sql_build_array('INSERT', $sql_ary));\n\t</pre></div>\n\n\t<p>To complete the example, this is how an update statement would look like:</p>\n\n\t<div class=\"codebox\"><pre>\n$sql_ary = array(\n\t'somedata'\t\t=&gt; $my_string,\n\t'otherdata'\t\t=&gt; $an_int,\n\t'moredata'\t\t=&gt; $another_int\n);\n\n$sql = 'UPDATE ' . SOME_TABLE . '\n\tSET ' . $db-&gt;sql_build_array('UPDATE', $sql_ary) . '\n\tWHERE user_id = ' . (int) $user_id;\n$db-&gt;sql_query($sql);\n\t</pre></div>\n\n\t<p>The <code>$db-&gt;sql_build_array()</code> function supports the following modes: <code>INSERT</code> (example above), <code>INSERT_SELECT</code> (building query for <code>INSERT INTO table (...) SELECT value, column ...</code> statements), <code>UPDATE</code> (example above) and <code>SELECT</code> (for building WHERE statement [AND logic]).</p>\n\n\t<h4>sql_multi_insert():</h4>\n\n\t<p>If you want to insert multiple statements at once, please use the separate <code>sql_multi_insert()</code> method. An example:</p>\n\n\t<div class=\"codebox\"><pre>\n$sql_ary = array();\n\n$sql_ary[] = array(\n\t'somedata'\t\t=&gt; $my_string_1,\n\t'otherdata'\t\t=&gt; $an_int_1,\n\t'moredata'\t\t=&gt; $another_int_1,\n);\n\n$sql_ary[] = array(\n\t'somedata'\t\t=&gt; $my_string_2,\n\t'otherdata'\t\t=&gt; $an_int_2,\n\t'moredata'\t\t=&gt; $another_int_2,\n);\n\n$db->sql_multi_insert(SOME_TABLE, $sql_ary);\n\t</pre></div>\n\n\t<h4>sql_in_set():</h4>\n\n\t<p>The <code>$db-&gt;sql_in_set()</code> function should be used for building <code>IN ()</code> and <code>NOT IN ()</code> constructs. Since (specifically) MySQL tend to be faster if for one value to be compared the <code>=</code> and <code>&lt;&gt;</code> operator is used, we let the DBAL decide what to do. A typical example of doing a positive match against a number of values would be:</p>\n\n\t<div class=\"codebox\"><pre>\n$sql = 'SELECT *\n\tFROM ' . FORUMS_TABLE . '\n\tWHERE ' . $db-&gt;sql_in_set('forum_id', $forum_ids);\n$db-&gt;sql_query($sql);\n\t</pre></div>\n\n\t<p>Based on the number of values in $forum_ids, the query can look differently.</p>\n\n\t<p class=\"good\">// SQL Statement if $forum_ids = array(1, 2, 3);</p>\n\n\t<div class=\"codebox\"><pre>\nSELECT FROM phpbb_forums WHERE forum_id IN (1, 2, 3)\n\t</pre></div>\n\n\t<p class=\"good\">// SQL Statement if $forum_ids = array(1) or $forum_ids = 1</p>\n\n\t<div class=\"codebox\"><pre>\nSELECT FROM phpbb_forums WHERE forum_id = 1\n\t</pre></div>\n\n\t<p>Of course the same is possible for doing a negative match against a number of values:</p>\n\n\t<div class=\"codebox\"><pre>\n$sql = 'SELECT *\n\tFROM ' . FORUMS_TABLE . '\n\tWHERE ' . $db-&gt;sql_in_set('forum_id', $forum_ids, <strong>true</strong>);\n$db-&gt;sql_query($sql);\n\t</pre></div>\n\n\t<p>Based on the number of values in $forum_ids, the query can look differently here too.</p>\n\n\t<p class=\"good\">// SQL Statement if $forum_ids = array(1, 2, 3);</p>\n\n\t<div class=\"codebox\"><pre>\nSELECT FROM phpbb_forums WHERE forum_id <strong>NOT</strong> IN (1, 2, 3)\n\t</pre></div>\n\n\t<p class=\"good\">// SQL Statement if $forum_ids = array(1) or $forum_ids = 1</p>\n\n\t<div class=\"codebox\"><pre>\nSELECT FROM phpbb_forums WHERE forum_id <strong>&lt;&gt;</strong> 1\n\t</pre></div>\n\n\t<p>If the given array is empty, an error will be produced.</p>\n\n\t<h4>sql_build_query():</h4>\n\n\t<p>The <code>$db-&gt;sql_build_query()</code> function is responsible for building sql statements for select and select distinct queries if you need to JOIN on more than one table or retrieving data from more than one table while doing a JOIN. This needs to be used to make sure the resulting statement is working on all supported db's. Instead of explaining every possible combination, i will give a short example:</p>\n\n\t<div class=\"codebox\"><pre>\n$sql_array = array(\n\t'SELECT'\t=&gt; 'f.*, ft.mark_time',\n\n\t'FROM'\t\t=&gt; array(\n\t\tFORUMS_WATCH_TABLE\t=&gt; 'fw',\n\t\tFORUMS_TABLE\t\t=&gt; 'f'\n\t),\n\n\t'LEFT_JOIN'\t=&gt; array(\n\t\tarray(\n\t\t\t'FROM'\t=&gt; array(FORUMS_TRACK_TABLE =&gt; 'ft'),\n\t\t\t'ON'\t=&gt; 'ft.user_id = ' . $user-&gt;data['user_id'] . ' AND ft.forum_id = f.forum_id'\n\t\t)\n\t),\n\n\t'WHERE'\t\t=&gt; 'fw.user_id = ' . $user-&gt;data['user_id'] . '\n\t\tAND f.forum_id = fw.forum_id',\n\n\t'ORDER_BY'\t=&gt; 'left_id'\n);\n\n$sql = $db-&gt;sql_build_query('SELECT', $sql_array);\n\t</pre></div>\n\n\t<p>The possible first parameter for sql_build_query() is SELECT or SELECT_DISTINCT. As you can see, the logic is pretty self-explaining. For the LEFT_JOIN key, just add another array if you want to join on to tables for example. The added benefit of using this construct is that you are able to easily build the query statement based on conditions - for example the above LEFT_JOIN is only necessary if server side topic tracking is enabled; a slight adjustement would be:</p>\n\n\t<div class=\"codebox\"><pre>\n$sql_array = array(\n\t'SELECT'\t=&gt; 'f.*',\n\n\t'FROM'\t\t=&gt; array(\n\t\tFORUMS_WATCH_TABLE\t=&gt; 'fw',\n\t\tFORUMS_TABLE\t\t=&gt; 'f'\n\t),\n\n\t'WHERE'\t\t=&gt; 'fw.user_id = ' . $user-&gt;data['user_id'] . '\n\t\tAND f.forum_id = fw.forum_id',\n\n\t'ORDER_BY'\t=&gt; 'left_id'\n);\n\nif ($config['load_db_lastread'])\n{\n\t$sql_array['LEFT_JOIN'] = array(\n\t\tarray(\n\t\t\t'FROM'\t=&gt; array(FORUMS_TRACK_TABLE =&gt; 'ft'),\n\t\t\t'ON'\t=&gt; 'ft.user_id = ' . $user-&gt;data['user_id'] . ' AND ft.forum_id = f.forum_id'\n\t\t)\n\t);\n\n\t$sql_array['SELECT'] .= ', ft.mark_time ';\n}\nelse\n{\n\t// Here we read the cookie data\n}\n\n$sql = $db-&gt;sql_build_query('SELECT', $sql_array);\n\t</pre></div>\n\n\t<a name=\"optimizing\"></a><h3>2.iv. Optimizations</h3>\n\n\t<h4>Operations in loop definition: </h4>\n\t<p>Always try to optimize your loops if operations are going on at the comparing part, since this part is executed every time the loop is parsed through. For assignments a descriptive name should be chosen. Example:</p>\n\n\t<p class=\"bad\">// On every iteration the sizeof function is called</p>\n\t<div class=\"codebox\"><pre>\nfor ($i = 0; $i &lt; sizeof($post_data); $i++)\n{\n\tdo_something();\n}\n\t</pre></div>\n\n\t<p class=\"good\">// You are able to assign the (not changing) result within the loop itself</p>\n\t<div class=\"codebox\"><pre>\nfor ($i = 0, $size = sizeof($post_data); $i &lt; $size; $i++)\n{\n\tdo_something();\n}\n\t</pre></div>\n\n\t<h4>Use of in_array(): </h4>\n\t<p>Try to avoid using in_array() on huge arrays, and try to not place them into loops if the array to check consist of more than 20 entries. in_array() can be very time consuming and uses a lot of cpu processing time. For little checks it is not noticable, but if checked against a huge array within a loop those checks alone can be a bunch of seconds. If you need this functionality, try using isset() on the arrays keys instead, actually shifting the values into keys and vice versa. A call to <code>isset($array[$var])</code> is a lot faster than <code>in_array($var, array_keys($array))</code> for example.</p>\n\n\n\t<a name=\"general\"></a><h3>2.v. General Guidelines</h3>\n\n\t<h4>General things:</h4>\n\t<p>Never trust user input (this also applies to server variables as well as cookies).</p>\n\t<p>Try to sanitize values returned from a function.</p>\n\t<p>Try to sanitize given function variables within your function.</p>\n\t<p>The auth class should be used for all authorisation checking.</p>\n\t<p>No attempt should be made to remove any copyright information (either contained within the source or displayed interactively when the source is run/compiled), neither should the copyright information be altered in any way (it may be added to).</p>\n\n\t<h4>Variables: </h4>\n\t<p>Make use of the <code>request_var()</code> function for anything except for submit or single checking params. </p>\n\t<p>The request_var function determines the type to set from the second parameter (which determines the default value too). If you need to get a scalar variable type, you need to tell this the request_var function explicitly. Examples:</p>\n\n\t<p class=\"bad\">// Old method, do not use it</p>\n\t<div class=\"codebox\"><pre>\n$start = (isset($HTTP_GET_VARS['start'])) ? intval($HTTP_GET_VARS['start']) : intval($HTTP_POST_VARS['start']);\n$submit = (isset($HTTP_POST_VARS['submit'])) ? true : false;\n\t</pre></div>\n\n\t<p class=\"good\">// Use request var and define a default variable (use the correct type)</p>\n\t<div class=\"codebox\"><pre>\n$start = request_var('start', 0);\n$submit = (isset($_POST['submit'])) ? true : false;\n\t</pre></div>\n\n\t<p class=\"bad\">// $start is an int, the following use of request_var therefore is not allowed</p>\n\t<div class=\"codebox\"><pre>\n$start = request_var('start', '0');\n\t</pre></div>\n\n\t<p class=\"good\">// Getting an array, keys are integers, value defaults to 0</p>\n\t<div class=\"codebox\"><pre>\n$mark_array = request_var('mark', array(0));\n\t</pre></div>\n\n\t<p class=\"good\">// Getting an array, keys are strings, value defaults to 0</p>\n\t<div class=\"codebox\"><pre>\n$action_ary = request_var('action', array('' =&gt; 0));\n\t</pre></div>\n\n\t<h4>Login checks/redirection: </h4>\n\t<p>To show a forum login box use <code>login_forum_box($forum_data)</code>, else use the <code>login_box()</code> function.</p>\n\n\t<p>The <code>login_box()</code> function can have a redirect as the first parameter. As a thumb of rule, specify an empty string if you want to redirect to the users current location, else do not add the <code>$SID</code> to the redirect string (for example within the ucp/login we redirect to the board index because else the user would be redirected to the login screen).</p>\n\n\t<h4>Sensitive Operations: </h4>\n\t<p>For sensitive operations always let the user confirm the action. For the confirmation screens, make use of the <code>confirm_box()</code> function.</p>\n\n\t<h4>Altering Operations: </h4>\n\t<p>For operations altering the state of the database, for instance posting, always verify the form token, unless you are already using <code>confirm_box()</code>. To do so, make use of the <code>add_form_key()</code> and <code>check_form_key()</code> functions. </p>\n\t<div class=\"codebox\"><pre>\n\tadd_form_key('my_form');\n\n\tif ($submit)\n\t{\n\t\tif (!check_form_key('my_form'))\n\t\t{\n\t\t\ttrigger_error('FORM_INVALID');\n\t\t}\n\t}\n\t</pre></div>\n\n\t<p>The string passed to <code>add_form_key()</code> needs to match the string passed to <code>check_form_key()</code>. Another requirement for this to work correctly is that all forms include the <code>{S_FORM_TOKEN}</code> template variable.</p>\n\n\n\t<h4>Sessions: </h4>\n\t<p>Sessions should be initiated on each page, as near the top as possible using the following code:</p>\n\n\t<div class=\"codebox\"><pre>\n$user-&gt;session_begin();\n$auth-&gt;acl($user-&gt;data);\n$user-&gt;setup();\n\t</pre></div>\n\n\t<p>The <code>$user-&gt;setup()</code> call can be used to pass on additional language definition and a custom style (used in viewforum).</p>\n\n\t<h4>Errors and messages: </h4>\n\t<p>All messages/errors should be outputed by calling <code>trigger_error()</code> using the appropriate message type and language string. Example:</p>\n\n\t<div class=\"codebox\"><pre>\ntrigger_error('NO_FORUM');\n\t</pre></div>\n\n\t<div class=\"codebox\"><pre>\ntrigger_error($user-&gt;lang['NO_FORUM']);\n\t</pre></div>\n\n\t<div class=\"codebox\"><pre>\ntrigger_error('NO_MODE', E_USER_ERROR);\n\t</pre></div>\n\n\t<h4>Url formatting</h4>\n\n\t<p>All urls pointing to internal files need to be prepended by the <code>$phpbb_root_path</code> variable. Within the administration control panel all urls pointing to internal files need to be prepended by the <code>$phpbb_admin_path</code> variable. This makes sure the path is always correct and users being able to just rename the admin folder and the acp still working as intended (though some links will fail and the code need to be slightly adjusted).</p>\n\n\t<p>The <code>append_sid()</code> function from 2.0.x is available too, though does not handle url alterations automatically. Please have a look at the code documentation if you want to get more details on how to use append_sid(). A sample call to append_sid() can look like this:</p>\n\n\t<div class=\"codebox\"><pre>\nappend_sid(&quot;{$phpbb_root_path}memberlist.$phpEx&quot;, 'mode=group&amp;amp;g=' . $row['group_id'])\n\t</pre></div>\n\n\t<h4>General function usage: </h4>\n\n\t<p>Some of these functions are only chosen over others because of personal preference and having no other benefit than to be consistant over the code.</p>\n\n\t<ul>\n\t\t<li>\n\t\t\t<p>Use <code>sizeof</code> instead of <code>count</code></p>\n\t\t</li>\n\t\t<li>\n\t\t\t<p>Use <code>strpos</code> instead of <code>strstr</code></p>\n\t\t</li>\n\t\t<li>\n\t\t\t<p>Use <code>else if</code> instead of <code>elseif</code></p>\n\t\t</li>\n\t\t<li>\n\t\t\t<p>Use <code>false</code> (lowercase) instead of <code>FALSE</code></p>\n\t\t</li>\n\t\t<li>\n\t\t\t<p>Use <code>true</code> (lowercase) instead of <code>TRUE</code></p>\n\t\t</li>\n\t</ul>\n\n\t<h4>Exiting</h4>\n\n\t<p>Your page should either call <code>page_footer()</code> in the end to trigger output through the template engine and terminate the script, or alternatively at least call the <code>exit_handler()</code>. That call is necessary because it provides a method for external applications embedding phpBB to be called at the end of the script.</p>\n\n\t<a name=\"phprestrictions\"></a><h3>2.vi. Restrictions on the Use of PHP</h3>\n\n\t<h4>Dynamic code execution:</h4>\n\n\t<p>Never execute dynamic PHP code (generated or in a constant string) using any of the following PHP functions:</p>\n\n\t<ul>\n\t\t<li><strong>eval</strong></li>\n\t\t<li><strong>create_function</strong></li>\n\t\t<li><strong>preg_replace</strong> with the <strong>e</strong> modifier in the pattern</li>\n\t</ul>\n\n\t<p>If absolutely necessary a file should be created, and a mechanism for creating this file prior to running phpBB should be provided as a setup process.</p>\n\n\t<p>The <strong>e</strong> modifier in <strong>preg_replace</strong> can be replaced by <strong>preg_replace_callback</strong> and objects to encapsulate state that is needed in the callback code.</p>\n\n\t\t</div>\n\n\t\t<div class=\"back2top\"><a href=\"#wrap\" class=\"top\">Back to Top</a></div>\n\n\t\t<span class=\"corners-bottom\"><span></span></span></div>\n\t</div>\n\n\t<hr />\n\n<a name=\"styling\"></a><h2>3. Styling</h2>\n\t<div class=\"paragraph\">\n\t\t<div class=\"inner\"><span class=\"corners-top\"><span></span></span>\n\n\t\t<div class=\"content\">\n\t<a name=\"cfgfiles\"></a><h3>3.i. Style Config Files</h3>\n\t<p>Style cfg files are simple name-value lists with the information necessary for installing a style. Similar cfg files exist for templates, themes and imagesets. These follow the same principle and will not be introduced individually. Styles can use installed components by using the required_theme/required_template/required_imageset entries. The important part of the style configuration file is assigning an unique name.</p>\n\t<div class=\"codebox\"><pre>\n        # General Information about this style\n        name = prosilver_duplicate\n        copyright = &copy; phpBB Group, 2007\n        version = 3.0.3\n        required_template = prosilver\n        required_theme = prosilver\n        required_imageset = prosilver\n\t</pre></div>\n\t<a name=\"genstyling\"></a><h3>3.2. General Styling Rules</h3>\n<p>Templates should be produced in a consistent manner. Where appropriate they should be based off an existing copy, e.g. index, viewforum or viewtopic (the combination of which implement a range of conditional and variable forms). Please also note that the intendation and coding guidelines also apply to templates where possible.</p>\n\n<p>The outer table class <code>forumline</code> has gone and is replaced with <code>tablebg</code>.</p>\n<p>When writing <code>&lt;table&gt;</code> the order <code>&lt;table class=\"\" cellspacing=\"\" cellpadding=\"\" border=\"\" align=\"\"&gt;</code> creates consistency and allows everyone to easily see which table produces which \"look\". The same applies to most other tags for which additional parameters can be set, consistency is the major aim here.</p>\n<p>Each block level element should be indented by one tab, same for tabular elements, e.g. <code>&lt;tr&gt;</code> <code>&lt;td&gt;</code> etc., whereby the intendiation of <code>&lt;table&gt;</code> and the following/ending <code>&lt;tr&gt;</code> should be on the same line. This applies not to div elements of course.</p>\n<p>Don't use <code>&lt;span&gt;</code> more than is essential ... the CSS is such that text sizes are dependent on the parent class. So writing <code>&lt;span class=\"gensmall\"&gt;&lt;span class=\"gensmall\"&gt;TEST&lt;/span&gt;&lt;/span&gt;</code> will result in very very small text. Similarly don't use span at all if another element can contain the class definition, e.g.</p>\n\n<div class=\"codebox\"><pre>\n&lt;td&gt;&lt;span class=&quot;gensmall&quot;&gt;TEST&lt;/span&gt;&lt;/td&gt;\n</pre></div>\n\n<p>can just as well become:</p>\n<div class=\"codebox\"><pre>\n&lt;td class=&quot;gensmall&quot;&gt;TEST&lt;/td&gt;\n</pre></div>\n\n<p>Try to match text class types with existing useage, e.g. don't use the nav class where viewtopic uses gensmall for example.</p>\n\n<p>Row colours/classes are now defined by the template, use an <code>IF S_ROW_COUNT</code> switch, see viewtopic or viewforum for an example.</p>\n\n<p>Remember block level ordering is important ... while not all pages validate as XHTML 1.0 Strict compliant it is something we're trying to work too.</p>\n\n<p>Use a standard cellpadding of 2 and cellspacing of 0 on outer tables. Inner tables can vary from 0 to 3 or even 4 depending on the need.</p>\n\n<p><strong>Use div container/css for styling and table for data representation.</strong></p>\n\n<p>The separate catXXXX and thXXX classes are gone. When defining a header cell just use <code>&lt;th&gt;</code> rather than <code>&lt;th class=\"thHead\"&gt;</code> etc. Similarly for cat, don't use <code>&lt;td class=\"catLeft\"&gt;</code> use <code>&lt;td class=\"cat\"&gt;</code> etc.</p>\n\n<p>Try to retain consistency of basic layout and class useage, i.e. _EXPLAIN text should generally be placed below the title it explains, e.g. <code>{L_POST_USERNAME}&lt;br /&gt;&lt;span class=\"gensmall\"&gt;{L_POST_USERNAME_EXPLAIN}&lt;/span&gt;</code> is the typical way of handling this ... there may be exceptions and this isn't a hard and fast rule.</p>\n\n<p>Try to keep template conditional and other statements tabbed in line with the block to which they refer.</p>\n\n<p class=\"good\">this is correct</p>\n<div class=\"codebox\"><pre>\n<span class=\"comment\">&lt;!-- BEGIN test --&gt;</span>\n\t&lt;tr&gt;\n\t\t&lt;td&gt;&#123;test.TEXT&#125;&lt;/td&gt;\n\t&lt;/tr&gt;\n<span class=\"comment\">&lt;!-- END test --&gt;</span>\n</pre></div>\n\n<p class=\"good\">this is also correct:</p>\n<div class=\"codebox\"><pre>\n<span class=\"comment\">&lt;!-- BEGIN test --&gt;</span>\n&lt;tr&gt;\n\t&lt;td&gt;&#123;test.TEXT&#125;&lt;/td&gt;\n&lt;/tr&gt;\n<span class=\"comment\">&lt;!-- END test --&gt;</span>\n</pre></div>\n\n<p>it gives immediate feedback on exactly what is looping - decide which way to use based on the readability.</p>\n\n\t\t</div>\n\n\t\t<div class=\"back2top\"><a href=\"#wrap\" class=\"top\">Back to Top</a></div>\n\n\t\t<span class=\"corners-bottom\"><span></span></span></div>\n\t</div>\n\n\t<hr />\n\n<a name=\"templating\"></a><h2>4. Templating</h2>\n\t<div class=\"paragraph\">\n\t\t<div class=\"inner\"><span class=\"corners-top\"><span></span></span>\n\n\t\t<div class=\"content\">\n\t<a name=\"templates\"></a><h3>4.i. General Templating</h3>\n\n<h4>File naming</h4>\n<p>Firstly templates now take the suffix &quot;.html&quot; rather than &quot;.tpl&quot;. This was done simply to make the lifes of some people easier wrt syntax highlighting, etc.</p>\n\n<h4>Variables</h4>\n<p>All template variables should be named appropriately (using underscores for spaces), language entries should be prefixed with L_, system data with S_, urls with U_, javascript urls with UA_, language to be put in javascript statements with LA_, all other variables should be presented 'as is'.</p>\n\n<p>L_* template variables are automatically tried to be mapped to the corresponding language entry if the code does not set (and therefore overwrite) this variable specifically. For example <code>{L_USERNAME}</code> maps to <code>$user-&gt;lang['USERNAME']</code>. The LA_* template variables are handled within the same way, but properly escaped to be put in javascript code. This should reduce the need to assign loads of new lang vars in Modifications.\n</p>\n\n<h4>Blocks/Loops</h4>\n<p>The basic block level loop remains and takes the form:</p>\n<div class=\"codebox\"><pre>\n<span class=\"comment\">&lt;!-- BEGIN loopname --&gt;</span>\n\tmarkup, {loopname&#46;X_YYYYY}, etc&#46;\n<span class=\"comment\">&lt;!-- END loopname --&gt;</span>\n</pre></div>\n\n<p>A bit later loops will be explained further. To not irritate you we will explain conditionals as well as other statements first.</p>\n\n<h4>Including files</h4>\n<p>Something that existed in 2.0.x which no longer exists in 3.0.x is the ability to assign a template to a variable. This was used (for example) to output the jumpbox. Instead (perhaps better, perhaps not but certainly more flexible) we now have INCLUDE. This takes the simple form:</p>\n\n<div class=\"codebox\"><pre>\n<span class=\"comment\">&lt;!-- INCLUDE filename --&gt;</span>\n</pre></div>\n\n<p>You will note in the 3.0 templates the major sources start with <code>&lt;!-- INCLUDE overall_header.html --&gt;</code> or <code>&lt;!-- INCLUDE simple_header.html --&gt;</code>, etc. In 2.0.x control of &quot;which&quot; header to use was defined entirely within the code. In 3.0.x the template designer can output what they like. Note that you can introduce new templates (i.e. other than those in the default set) using this system and include them as you wish ... perhaps useful for a common &quot;menu&quot; bar or some such. No need to modify loads of files as with 2.0.x.</p>\n\n<p>Added in <strong>3.0.6</strong> is the ability to include a file using a template variable to specify the file, this functionality only works for root variables (i.e. not block variables).</p>\n<div class=\"codebox\"><pre>\n<span class=\"comment\">&lt;!-- INCLUDE {FILE_VAR} --&gt;</span>\n</pre></div>\n\n<p>Template defined variables can also be utilised.</p>\n\n<div class=\"codebox\"><pre>\n<span class=\"comment\">&lt;!-- DEFINE $SOME_VAR = 'my_file.html' --&gt;</span>\n<span class=\"comment\">&lt;!-- INCLUDE {$SOME_VAR} --&gt;</span>\n</pre></div>\n\n<h4>PHP</h4>\n<p>A contentious decision has seen the ability to include PHP within the template introduced. This is achieved by enclosing the PHP within relevant tags:</p>\n\n<div class=\"codebox\"><pre>\n<span class=\"comment\">&lt;!-- PHP --&gt;</span>\n\techo &quot;hello!&quot;;\n<span class=\"comment\">&lt;!-- ENDPHP --&gt;</span>\n</pre></div>\n\n<p>You may also include PHP from an external file using:</p>\n\n<div class=\"codebox\"><pre>\n<span class=\"comment\">&lt;!-- INCLUDEPHP somefile&#46;php --&gt;</span>\n</pre></div>\n\n<p>it will be included and executed inline.<br /><br />A note, it is very much encouraged that template designers do not include PHP. The ability to include raw PHP was introduced primarily to allow end users to include banner code, etc. without modifying multiple files (as with 2.0.x). It was not intended for general use ... hence <!-- w --><a href=\"http://www.phpbb.com\">www.phpbb.com</a><!-- w --> will <strong>not</strong> make available template sets which include PHP. And by default templates will have PHP disabled (the admin will need to specifically activate PHP for a template).</p>\n\n<h4>Conditionals/Control structures</h4>\n<p>The most significant addition to 3.0.x are conditions or control structures, &quot;if something then do this else do that&quot;. The system deployed is very similar to Smarty. This may confuse some people at first but it offers great potential and great flexibility with a little imagination. In their most simple form these constructs take the form:</p>\n\n<div class=\"codebox\"><pre>\n<span class=\"comment\">&lt;!-- IF expr --&gt;</span>\n\tmarkup\n<span class=\"comment\">&lt;!-- ENDIF --&gt;</span>\n</pre></div>\n\n<p>expr can take many forms, for example:</p>\n\n<div class=\"codebox\"><pre>\n<span class=\"comment\">&lt;!-- IF loop&#46;S_ROW_COUNT is even --&gt;</span>\n\tmarkup\n<span class=\"comment\">&lt;!-- ENDIF --&gt;</span>\n</pre></div>\n\n<p>This will output the markup if the S_ROW_COUNT variable in the current iteration of loop is an even value (i.e. the expr is TRUE). You can use various comparison methods (standard as well as equivalent textual versions noted in square brackets) including (<code>not, or, and, eq, neq, is</code> should be used if possible for better readability):</p>\n\n<div class=\"codebox\"><pre>\n== [eq]\n!= [neq, ne]\n&lt;&gt; (same as !=)\n!== (not equivalent in value and type)\n=== (equivalent in value and type)\n&gt; [gt]\n&lt; [lt]\n&gt;= [gte]\n&lt;= [lte]\n&amp;&amp; [and]\n|| [or]\n% [mod]\n! [not]\n+\n-\n*\n/\n,\n&lt;&lt; (bitwise shift left)\n&gt;&gt; (bitwise shift right)\n| (bitwise or)\n^ (bitwise xor)\n&amp; (bitwise and)\n~ (bitwise not)\nis (can be used to join comparison operations)\n</pre></div>\n\n<p>Basic parenthesis can also be used to enforce good old BODMAS rules. Additionally some basic comparison types are defined:</p>\n\n<div class=\"codebox\"><pre>\neven\nodd\ndiv\n</pre></div>\n\n<p>Beyond the simple use of IF you can also do a sequence of comparisons using the following:</p>\n\n<div class=\"codebox\"><pre>\n<span class=\"comment\">&lt;!-- IF expr1 --&gt;</span>\n\tmarkup\n<span class=\"comment\">&lt;!-- ELSEIF expr2 --&gt;</span>\n\tmarkup\n\t&#46;\n\t&#46;\n\t&#46;\n<span class=\"comment\">&lt;!-- ELSEIF exprN --&gt;</span>\n\tmarkup\n<span class=\"comment\">&lt;!-- ELSE --&gt;</span>\n\tmarkup\n<span class=\"comment\">&lt;!-- ENDIF --&gt;</span>\n</pre></div>\n\n<p>Each statement will be tested in turn and the relevant output generated when a match (if a match) is found. It is not necessary to always use ELSEIF, ELSE can be used alone to match &quot;everything else&quot;.<br /><br />So what can you do with all this? Well take for example the colouration of rows in viewforum. In 2.0.x row colours were predefined within the source as either row color1, row color2 or row class1, row class2. In 3.0.x this is moved to the template, it may look a little daunting at first but remember control flows from top to bottom and it's not too difficult:</p>\n\n<div class=\"codebox\"><pre>\n&lt;table&gt;\n\t<span class=\"comment\">&lt;!-- IF loop&#46;S_ROW_COUNT is even --&gt;</span>\n\t\t&lt;tr class=&quot;row1&quot;&gt;\n\t<span class=\"comment\">&lt;!-- ELSE --&gt;</span>\n\t\t&lt;tr class=&quot;row2&quot;&gt;\n\t<span class=\"comment\">&lt;!-- ENDIF --&gt;</span>\n\t&lt;td&gt;HELLO!&lt;/td&gt;\n&lt;/tr&gt;\n&lt;/table&gt;\n</pre></div>\n\n<p>This will cause the row cell to be output using class row1 when the row count is even, and class row2 otherwise. The S_ROW_COUNT parameter gets assigned to loops by default. Another example would be the following: </p>\n\n<div class=\"codebox\"><pre>\n&lt;table&gt;\n\t<span class=\"comment\">&lt;!-- IF loop&#46;S_ROW_COUNT &gt; 10 --&gt;</span>\n\t\t&lt;tr bgcolor=&quot;#FF0000&quot;&gt;\n\t<span class=\"comment\">&lt;!-- ELSEIF loop&#46;S_ROW_COUNT &gt; 5 --&gt;</span>\n\t\t&lt;tr bgcolor=&quot;#00FF00&quot;&gt;\n\t<span class=\"comment\">&lt;!-- ELSEIF loop&#46;S_ROW_COUNT &gt; 2 --&gt;</span>\n\t\t&lt;tr bgcolor=&quot;#0000FF&quot;&gt;\n\t<span class=\"comment\">&lt;!-- ELSE --&gt;</span>\n\t\t&lt;tr bgcolor=&quot;#FF00FF&quot;&gt;\n\t<span class=\"comment\">&lt;!-- ENDIF --&gt;</span>\n\t&lt;td&gt;hello!&lt;/td&gt;\n&lt;/tr&gt;\n&lt;/table&gt;\n</pre></div>\n\n<p>This will output the row cell in purple for the first two rows, blue for rows 2 to 5, green for rows 5 to 10 and red for remainder. So, you could produce a &quot;nice&quot; gradient effect, for example.<br /><br />What else can you do? Well, you could use IF to do common checks on for example the login state of a user:</p>\n\n<div class=\"codebox\"><pre>\n<span class=\"comment\">&lt;!-- IF S_USER_LOGGED_IN --&gt;</span>\n\tmarkup\n<span class=\"comment\">&lt;!-- ENDIF --&gt;</span>\n</pre></div>\n\n<p>This replaces the existing (fudged) method in 2.0.x using a zero length array and BEGIN/END.</p>\n\n<h4>Extended syntax for Blocks/Loops</h4>\n\n<p>Back to our loops - they had been extended with the following additions. Firstly you can set the start and end points of the loop. For example:</p>\n\n<div class=\"codebox\"><pre>\n<span class=\"comment\">&lt;!-- BEGIN loopname(2) --&gt;</span>\n\tmarkup\n<span class=\"comment\">&lt;!-- END loopname --&gt;</span>\n</pre></div>\n\n<p>Will start the loop on the third entry (note that indexes start at zero). Extensions of this are:\n<br /><br />\n<code>loopname(2)</code>: Will start the loop on the 3rd entry<br />\n<code>loopname(-2)</code>: Will start the loop two entries from the end<br />\n<code>loopname(3,4)</code>: Will start the loop on the fourth entry and end it on the fifth<br />\n<code>loopname(3,-4)</code>: Will start the loop on the fourth entry and end it four from last<br />\n</p>\n\n<p>A further extension to begin is BEGINELSE:</p>\n\n<div class=\"codebox\"><pre>\n<span class=\"comment\">&lt;!-- BEGIN loop --&gt;</span>\n\tmarkup\n<span class=\"comment\">&lt;!-- BEGINELSE --&gt;</span>\n\tmarkup\n<span class=\"comment\">&lt;!-- END loop --&gt;</span>\n</pre></div>\n\n<p>This will cause the markup between <code>BEGINELSE</code> and <code>END</code> to be output if the loop contains no values. This is useful for forums with no topics (for example) ... in some ways it replaces &quot;bits of&quot; the existing &quot;switch_&quot; type control (the rest being replaced by conditionals).</p>\n\n<p>Another way of checking if a loop contains values is by prefixing the loops name with a dot:</p>\n\n<div class=\"codebox\"><pre>\n<span class=\"comment\">&lt;!-- IF .loop --&gt;</span>\n\t<span class=\"comment\">&lt;!-- BEGIN loop --&gt;</span>\n\t\tmarkup\n\t<span class=\"comment\">&lt;!-- END loop --&gt;</span>\n<span class=\"comment\">&lt;!-- ELSE --&gt;</span>\n\tmarkup\n<span class=\"comment\">&lt;!-- ENDIF --&gt;</span>\n</pre></div>\n\n<p>You are even able to check the number of items within a loop by comparing it with values within the IF condition:</p>\n\n<div class=\"codebox\"><pre>\n<span class=\"comment\">&lt;!-- IF .loop &gt; 2 --&gt;</span>\n\t<span class=\"comment\">&lt;!-- BEGIN loop --&gt;</span>\n\t\tmarkup\n\t<span class=\"comment\">&lt;!-- END loop --&gt;</span>\n<span class=\"comment\">&lt;!-- ELSE --&gt;</span>\n\tmarkup\n<span class=\"comment\">&lt;!-- ENDIF --&gt;</span>\n</pre></div>\n\n<p>Nesting loops cause the conditionals needing prefixed with all loops from the outer one to the inner most. An illustration of this:</p>\n\n<div class=\"codebox\"><pre>\n<span class=\"comment\">&lt;!-- BEGIN firstloop --&gt;</span>\n\t{firstloop.MY_VARIABLE_FROM_FIRSTLOOP}\n\n\t<span class=\"comment\">&lt;!-- BEGIN secondloop --&gt;</span>\n\t\t{firstloop.secondloop.MY_VARIABLE_FROM_SECONDLOOP}\n\t<span class=\"comment\">&lt;!-- END secondloop --&gt;</span>\n<span class=\"comment\">&lt;!-- END firstloop --&gt;</span>\n</pre></div>\n\n<p>Sometimes it is necessary to break out of nested loops to be able to call another loop within the current iteration. This sounds a little bit confusing and it is not used very often. The following (rather complex) example shows this quite good - it also shows how you test for the first and last row in a loop (i will explain the example in detail further down):</p>\n\n<div class=\"codebox\"><pre>\n<span class=\"comment\">&lt;!-- BEGIN l_block1 --&gt;</span>\n\t<span class=\"comment\">&lt;!-- IF l_block1.S_SELECTED --&gt;</span>\n\t\t&lt;strong&gt;{l_block1.L_TITLE}&lt;/strong&gt;\n\t\t<span class=\"comment\">&lt;!-- IF S_PRIVMSGS --&gt;</span>\n\n\t\t\t<span class=\"comment\">&lt;!-- the ! at the beginning of the loop name forces the loop to be not a nested one of l_block1 --&gt;</span>\n\t\t\t<span class=\"comment\">&lt;!-- BEGIN !folder --&gt;</span>\n\t\t\t\t<span class=\"comment\">&lt;!-- IF folder.S_FIRST_ROW --&gt;</span>\n\t\t\t\t\t&lt;ul class=&quot;nav&quot;&gt;\n\t\t\t\t<span class=\"comment\">&lt;!-- ENDIF --&gt;</span>\n\n\t\t\t\t&lt;li&gt;&lt;a href=&quot;{folder.U_FOLDER}&quot;&gt;{folder.FOLDER_NAME}&lt;/a&gt;&lt;/li&gt;\n\n\t\t\t\t<span class=\"comment\">&lt;!-- IF folder.S_LAST_ROW --&gt;</span>\n\t\t\t\t\t&lt;/ul&gt;\n\t\t\t\t<span class=\"comment\">&lt;!-- ENDIF --&gt;</span>\n\t\t\t<span class=\"comment\">&lt;!-- END !folder --&gt;</span>\n\n\t\t<span class=\"comment\">&lt;!-- ENDIF --&gt;</span>\n\n\t\t&lt;ul class=&quot;nav&quot;&gt;\n\t\t<span class=\"comment\">&lt;!-- BEGIN l_block2 --&gt;</span>\n\t\t\t&lt;li&gt;\n\t\t\t\t<span class=\"comment\">&lt;!-- IF l_block1.l_block2.S_SELECTED --&gt;</span>\n\t\t\t\t\t&lt;strong&gt;{l_block1.l_block2.L_TITLE}&lt;/strong&gt;\n\t\t\t\t<span class=\"comment\">&lt;!-- ELSE --&gt;</span>\n\t\t\t\t\t&lt;a href=&quot;{l_block1.l_block2.U_TITLE}&quot;&gt;{l_block1.l_block2.L_TITLE}&lt;/a&gt;\n\t\t\t\t<span class=\"comment\">&lt;!-- ENDIF --&gt;</span>\n\t\t\t&lt;/li&gt;\n\t\t<span class=\"comment\">&lt;!-- END l_block2 --&gt;</span>\n\t\t&lt;/ul&gt;\n\t<span class=\"comment\">&lt;!-- ELSE --&gt;</span>\n\t\t&lt;a class=&quot;nav&quot; href=&quot;{l_block1.U_TITLE}&quot;&gt;{l_block1.L_TITLE}&lt;/a&gt;\n\t<span class=\"comment\">&lt;!-- ENDIF --&gt;</span>\n<span class=\"comment\">&lt;!-- END l_block1 --&gt;</span>\n</pre></div>\n\n<p>Let us first concentrate on this part of the example:</p>\n\n<div class=\"codebox\"><pre>\n<span class=\"comment\">&lt;!-- BEGIN l_block1 --&gt;</span>\n\t<span class=\"comment\">&lt;!-- IF l_block1.S_SELECTED --&gt;</span>\n\t\tmarkup\n\t<span class=\"comment\">&lt;!-- ELSE --&gt;</span>\n\t\t&lt;a class=&quot;nav&quot; href=&quot;{l_block1.U_TITLE}&quot;&gt;{l_block1.L_TITLE}&lt;/a&gt;\n\t<span class=\"comment\">&lt;!-- ENDIF --&gt;</span>\n<span class=\"comment\">&lt;!-- END l_block1 --&gt;</span>\n</pre></div>\n\n<p>Here we open the loop l_block1 and doing some things if the value S_SELECTED within the current loop iteration is true, else we write the blocks link and title. Here, you see <code>{l_block1.L_TITLE}</code> referenced - you remember that L_* variables get automatically assigned the corresponding language entry? This is true, but not within loops. The L_TITLE variable within the loop l_block1 is assigned within the code itself.</p>\n\n<p>Let's have a closer look to the markup:</p>\n\n<div class=\"codebox\"><pre>\n<span class=\"comment\">&lt;!-- BEGIN l_block1 --&gt;</span>\n.\n.\n\t<span class=\"comment\">&lt;!-- IF S_PRIVMSGS --&gt;</span>\n\n\t\t<span class=\"comment\">&lt;!-- BEGIN !folder --&gt;</span>\n\t\t\t<span class=\"comment\">&lt;!-- IF folder.S_FIRST_ROW --&gt;</span>\n\t\t\t\t&lt;ul class=&quot;nav&quot;&gt;\n\t\t\t<span class=\"comment\">&lt;!-- ENDIF --&gt;</span>\n\n\t\t\t&lt;li&gt;&lt;a href=&quot;{folder.U_FOLDER}&quot;&gt;{folder.FOLDER_NAME}&lt;/a&gt;&lt;/li&gt;\n\n\t\t\t<span class=\"comment\">&lt;!-- IF folder.S_LAST_ROW --&gt;</span>\n\t\t\t\t&lt;/ul&gt;\n\t\t\t<span class=\"comment\">&lt;!-- ENDIF --&gt;</span>\n\t\t<span class=\"comment\">&lt;!-- END !folder --&gt;</span>\n\n\t<span class=\"comment\">&lt;!-- ENDIF --&gt;</span>\n.\n.\n<span class=\"comment\">&lt;!-- END l_block1 --&gt;</span>\n</pre></div>\n\n<p>The <code>&lt;!-- IF S_PRIVMSGS --&gt;</code> statement clearly checks a global variable and not one within the loop, since the loop is not given here. So, if S_PRIVMSGS is true we execute the shown markup. Now, you see the <code>&lt;!-- BEGIN !folder --&gt;</code> statement. The exclamation mark is responsible for instructing the template engine to iterate through the main loop folder. So, we are now within the loop folder - with <code>&lt;!-- BEGIN folder --&gt;</code> we would have been within the loop <code>l_block1.folder</code> automatically as is the case with l_block2:</p>\n\n<div class=\"codebox\"><pre>\n<span class=\"comment\">&lt;!-- BEGIN l_block1 --&gt;</span>\n.\n.\n\t&lt;ul class=&quot;nav&quot;&gt;\n\t<span class=\"comment\">&lt;!-- BEGIN l_block2 --&gt;</span>\n\t\t&lt;li&gt;\n\t\t\t<span class=\"comment\">&lt;!-- IF l_block1.l_block2.S_SELECTED --&gt;</span>\n\t\t\t\t&lt;strong&gt;{l_block1.l_block2.L_TITLE}&lt;/strong&gt;\n\t\t\t<span class=\"comment\">&lt;!-- ELSE --&gt;</span>\n\t\t\t\t&lt;a href=&quot;{l_block1.l_block2.U_TITLE}&quot;&gt;{l_block1.l_block2.L_TITLE}&lt;/a&gt;\n\t\t\t<span class=\"comment\">&lt;!-- ENDIF --&gt;</span>\n\t\t&lt;/li&gt;\n\t<span class=\"comment\">&lt;!-- END l_block2 --&gt;</span>\n\t&lt;/ul&gt;\n.\n.\n<span class=\"comment\">&lt;!-- END l_block1 --&gt;</span>\n</pre></div>\n\n<p>You see the difference? The loop l_block2 is a member of the loop l_block1 but the loop folder is a main loop.</p>\n\n<p>Now back to our folder loop:</p>\n\n<div class=\"codebox\"><pre>\n<span class=\"comment\">&lt;!-- IF folder.S_FIRST_ROW --&gt;</span>\n\t&lt;ul class=&quot;nav&quot;&gt;\n<span class=\"comment\">&lt;!-- ENDIF --&gt;</span>\n\n&lt;li&gt;&lt;a href=&quot;{folder.U_FOLDER}&quot;&gt;{folder.FOLDER_NAME}&lt;/a&gt;&lt;/li&gt;\n\n<span class=\"comment\">&lt;!-- IF folder.S_LAST_ROW --&gt;</span>\n\t&lt;/ul&gt;\n<span class=\"comment\">&lt;!-- ENDIF --&gt;</span>\n</pre></div>\n\n<p>You may have wondered what the comparison to S_FIRST_ROW and S_LAST_ROW is about. If you haven't guessed already - it is checking for the first iteration of the loop with <code>S_FIRST_ROW</code> and the last iteration with <code>S_LAST_ROW</code>. This can come in handy quite often if you want to open or close design elements, like the above list. Let us imagine a folder loop build with three iterations, it would go this way:</p>\n\n<div class=\"codebox\"><pre>\n&lt;ul class=&quot;nav&quot;&gt; <span class=\"comment\">&lt;!-- written on first iteration --&gt;</span>\n\t&lt;li&gt;first element&lt;/li&gt; <span class=\"comment\">&lt;!-- written on first iteration --&gt;</span>\n\t&lt;li&gt;second element&lt;/li&gt; <span class=\"comment\">&lt;!-- written on second iteration --&gt;</span>\n\t&lt;li&gt;third element&lt;/li&gt; <span class=\"comment\">&lt;!-- written on third iteration --&gt;</span>\n&lt;/ul&gt; <span class=\"comment\">&lt;!-- written on third iteration --&gt;</span>\n</pre></div>\n\n<p>As you can see, all three elements are written down as well as the markup for the first iteration and the last one. Sometimes you want to omit writing the general markup - for example:</p>\n\n<div class=\"codebox\"><pre>\n<span class=\"comment\">&lt;!-- IF folder.S_FIRST_ROW --&gt;</span>\n\t&lt;ul class=&quot;nav&quot;&gt;\n<span class=\"comment\">&lt;!-- ELSEIF folder.S_LAST_ROW --&gt;</span>\n\t&lt;/ul&gt;\n<span class=\"comment\">&lt;!-- ELSE --&gt;</span>\n\t&lt;li&gt;&lt;a href=&quot;{folder.U_FOLDER}&quot;&gt;{folder.FOLDER_NAME}&lt;/a&gt;&lt;/li&gt;\n<span class=\"comment\">&lt;!-- ENDIF --&gt;</span>\n</pre></div>\n\n<p>would result in the following markup:</p>\n\n<div class=\"codebox\"><pre>\n&lt;ul class=&quot;nav&quot;&gt; <span class=\"comment\">&lt;!-- written on first iteration --&gt;</span>\n\t&lt;li&gt;second element&lt;/li&gt; <span class=\"comment\">&lt;!-- written on second iteration --&gt;</span>\n&lt;/ul&gt; <span class=\"comment\">&lt;!-- written on third iteration --&gt;</span>\n</pre></div>\n\n<p>Just always remember that processing is taking place from up to down.</p>\n\n\t<h4>Forms</h4>\n\t\t<p>If a form is used for a non-trivial operation (i.e. more than a jumpbox), then it should include the <code>{S_FORM_TOKEN}</code> template variable.</p>\n\t\t<div class=\"codebox\"><pre>\n&lt;form method=&quot;post&quot; id=&quot;mcp&quot; action=&quot;{U_POST_ACTION}&quot;&gt;\n\n\t&lt;fieldset class=\"submit-buttons\"&gt;\n\t\t&lt;input type=&quot;reset&quot; value=&quot;{L_RESET}&quot; name=&quot;reset&quot; class=&quot;button2&quot; /&gt;&nbsp;\n\t\t&lt;input type=&quot;submit&quot; name=&quot;action[add_warning]&quot; value=&quot;{L_SUBMIT}&quot; class=&quot;button1&quot; /&gt;\n\t\t{S_FORM_TOKEN}\n\t&lt;/fieldset&gt;\n&lt;/form&gt;\n\t\t</pre></div><br />\n\n\t<a name=\"inheritance\"></a><h3>4.ii. Template Inheritance</h3>\n\t\t<p>When basing a new template on an existing one, it is not necessary to provide all template files. By declaring the template  to be &quot;<strong>inheriting</strong>&quot; in the template configuration file.</p>\n\n\t\t<p>The limitation on this is that the base style has to be installed and complete, meaning that it is not itself inheriting.</p>\n\n\t\t<p>The effect of doing so is that the template engine will use the files in the new template where they exist, but fall back to files in the base template otherwise. Declaring a style to be inheriting also causes it to use some of the configuration settings of the base style, notably database storage.</p>\n\n\t\t<p>We strongly encourage the use of inheritance for styles based on the bundled styles, as it will ease the update procedure.</p>\n\n\t\t<div class=\"codebox\"><pre>\n        # General Information about this template\n        name = inherits\n        copyright = &copy; phpBB Group, 2007\n        version = 3.0.3\n\n        # Defining a different template bitfield\n        template_bitfield = lNg=\n\n        # Are we inheriting?\n        inherit_from = prosilver\n\t\t</pre></div>\n\n\t\t</div>\n\n\t\t<div class=\"back2top\"><a href=\"#wrap\" class=\"top\">Back to Top</a></div>\n\n\t\t<span class=\"corners-bottom\"><span></span></span></div>\n\t</div>\n\n\t<hr />\n\n\n\n<a name=\"charsets\"></a><h2>5. Character Sets and Encodings</h2>\n\n\t<div class=\"paragraph\">\n\t\t<div class=\"inner\"><span class=\"corners-top\"><span></span></span>\n\n\t\t<div class=\"content\">\n\n\n\n<h4>What are Unicode, UCS and UTF-8?</h4>\n<p>The <a href=\"http://en.wikipedia.org/wiki/Universal_Character_Set\">Universal Character Set (UCS)</a> described in ISO/IEC 10646 consists of a large amount of characters. Each of them has a unique name and a code point which is an integer number. <a href=\"http://en.wikipedia.org/wiki/Unicode\">Unicode</a> - which is an industry standard - complements the Universal Character Set with further information about the characters' properties and alternative character encodings. More information on Unicode can be found on the <a href=\"http://www.unicode.org/\">Unicode Consortium's website</a>. One of the Unicode encodings is the <a href=\"http://en.wikipedia.org/wiki/UTF-8\">8-bit Unicode Transformation Format (UTF-8)</a>. It encodes characters with up to four bytes aiming for maximum compatibility with the <a href=\"http://en.wikipedia.org/wiki/ASCII\">American Standard Code for Information Interchange</a> which is a 7-bit encoding of a relatively small subset of the UCS.</p>\n\n<h4>phpBB's use of Unicode</h4>\n<p>Unfortunately PHP does not faciliate the use of Unicode prior to version 6. Most functions simply treat strings as sequences of bytes assuming that each character takes up exactly one byte. This behaviour still allows for storing UTF-8 encoded text in PHP strings but many operations on strings have unexpected results. To circumvent this problem we have created some alternative functions to PHP's native string operations which use code points instead of bytes. These functions can be found in <code>/includes/utf/utf_tools.php</code>. They are also covered in the <a href=\"http://area51.phpbb.com/docs/code/\">phpBB3 Sourcecode Documentation</a>. A lot of native PHP functions still work with UTF-8 as long as you stick to certain restrictions. For example <code>explode</code> still works as long as the first and the last character of the delimiter string are ASCII characters.</p>\n\n<p>phpBB only uses the ASCII and the UTF-8 character encodings. Still all Strings are UTF-8 encoded because ASCII is a subset of UTF-8. The only exceptions to this rule are code sections which deal with external systems which use other encodings and character sets. Such external data should be converted to UTF-8 using the <code>utf8_recode()</code> function supplied with phpBB. It supports a variety of other character sets and encodings, a full list can be found below.</p>\n\n<p>With <code>request_var()</code> you can either allow all UCS characters in user input or restrict user input to ASCII characters. This feature is controlled by the function's third parameter called <code>$multibyte</code>. You should allow multibyte characters in posts, PMs, topic titles, forum names, etc. but it's not necessary for internal uses like a <code>$mode</code> variable which should only hold a predefined list of ASCII strings anyway.</p>\n\n<div class=\"codebox\"><pre>\n// an input string containing a multibyte character\n$_REQUEST['multibyte_string'] = 'K&#228;se';\n\n// print request variable as a UTF-8 string allowing multibyte characters\necho request_var('multibyte_string', '', true);\n// print request variable as ASCII string\necho request_var('multibyte_string', '');\n</pre></div>\n\n<p>This code snippet will generate the following output:</p>\n\n<div class=\"codebox\"><pre>\nK&#228;se\nK??se\n</pre></div>\n\n<h4>Unicode Normalization</h4>\n\n<p>If you retrieve user input with multibyte characters you should additionally normalize the string using <code>utf8_normalize_nfc()</code> before you work with it. This is necessary to make sure that equal characters can only occur in one particular binary representation. For example the character &#197; can be represented either as <code>U+00C5</code> (LATIN CAPITAL LETTER A WITH RING ABOVE) or as <code>U+212B</code> (ANGSTROM SIGN). phpBB uses Normalization Form Canonical Composition (NFC) for all text. So the correct version of the above example would look like this:</p>\n\n<div class=\"codebox\"><pre>\n$_REQUEST['multibyte_string'] = 'K&#228;se';\n\n// normalize multibyte strings\necho utf8_normalize_nfc(request_var('multibyte_string', '', true));\n// ASCII strings do not need to be normalized\necho request_var('multibyte_string', '');\n</pre></div>\n\n<h4>Case Folding</h4>\n\n<p>Case insensitive comparison of strings is no longer possible with <code>strtolower</code> or <code>strtoupper</code> as some characters have multiple lower case or multiple upper case forms depending on their position in a word. The <code>utf8_strtolower</code> and the <code>utf8_strtoupper</code> functions suffer from the same problem so they can only be used to display upper/lower case versions of a string but they cannot be used for case insensitive comparisons either. So instead you should use case folding which gives you a case insensitive version of the string which can be used for case insensitive comparisons. An NFC normalized string can be case folded using <code>utf8_case_fold_nfc()</code>.</p>\n\n<p class=\"bad\">// Bad - The strings might be the same even if strtolower differs</p>\n\n<div class=\"codebox\"><pre>\nif (strtolower($string1) == strtolower($string2))\n{\n\techo '$string1 and $string2 are equal or differ in case';\n}\n</pre></div>\n\n<p class=\"good\">// Good - Case folding is really case insensitive</p>\n\n<div class=\"codebox\"><pre>\nif (utf8_case_fold_nfc($string1) == utf8_case_fold_nfc($string2))\n{\n\techo '$string1 and $string2 are equal or differ in case';\n}\n</pre></div>\n\n<h4>Confusables Detection</h4>\n\n<p>phpBB offers a special method <code>utf8_clean_string</code> which can be used to make sure string identifiers are unique. This method uses Normalization Form Compatibility Composition (NFKC) instead of NFC and replaces similarly looking characters with a particular representative of the equivalence class. This method is currently used for usernames and group names to avoid confusion with similarly looking names.</p>\n\n\t\t</div>\n\n\t\t<div class=\"back2top\"><a href=\"#wrap\" class=\"top\">Back to Top</a></div>\n\n\t\t<span class=\"corners-bottom\"><span></span></span></div>\n\t</div>\n\n\t<hr />\n\n<a name=\"translation\"></a><h2>6. Translation (<abbr title=\"Internationalisation\">i18n</abbr>/<abbr title=\"Localisation\">L10n</abbr>) Guidelines</h2>\n\n\t<div class=\"paragraph\">\n\t\t<div class=\"inner\"><span class=\"corners-top\"><span></span></span>\n\n\t\t<div class=\"content\">\n\n\t<a name=\"standardisation\"></a><h3>6.i. Standardisation</h3>\n\n\t<h4>Reason:</h4>\n\n\t<p>phpBB is one of the most translated open-source projects, with the current stable version being available in over 60 localisations. Whilst the ad hoc approach to the naming of language packs has worked, for phpBB3 and beyond we hope to make this process saner which will allow for better interoperation with current and future web browsers.</p>\n\n\t<h4>Encoding:</h4>\n\n\t<p>With phpBB3, the output encoding for the forum in now UTF-8, a Universal Character Encoding by the Unicode Consortium that is by design a superset to US-ASCII and ISO-8859-1. By using one character set which simultaenously supports all scripts which previously would have required different encodings (eg: ISO-8859-1 to ISO-8859-15 (Latin, Greek, Cyrillic, Thai, Hebrew, Arabic); GB2312 (Simplified Chinese); Big5 (Traditional Chinese), EUC-JP (Japanese), EUC-KR (Korean), VISCII (Vietnamese); et cetera), this removes the need to convert between encodings and improves the accessibility of multilingual forums.</p>\n\n\t<p>The impact is that the language files for phpBB must now also be encoded as UTF-8, with a caveat that the files must <strong>not contain</strong> a <acronym title=\"Byte-Order-Mark\">BOM</acronym> for compatibility reasons with non-Unicode aware versions of PHP. For those with forums using the Latin character set (ie: most European languages), this change is transparent since UTF-8 is superset to US-ASCII and ISO-8859-1.</p>\n\n\t<h4>Language Tag:</h4>\n\n\t<p>The <abbr title=\"Internet Engineering Task Force\">IETF</abbr> recently published <a href=\"http://tools.ietf.org/html/rfc4646\">RFC 4646</a> for tags used to identify languages, which in combination with <a href=\"http://tools.ietf.org/html/rfc4647\">RFC 4647</a> obseletes the older <a href=\"http://tools.ietf.org/html/rfc3066\">RFC 3006</a> and older-still <a href=\"http://tools.ietf.org/html/rfc1766\">RFC 1766</a>. <a href=\"http://tools.ietf.org/html/rfc4646\">RFC 4646</a> uses <a href=\"http://www.loc.gov/standards/iso639-2/php/English_list.php\">ISO 639-1/ISO 639-2</a>, <a href=\"http://www.iso.ch/iso/en/prods-services/iso3166ma/02iso-3166-code-lists/list-en1.html\">ISO 3166-1 alpha-2</a>, <a href=\"http://www.unicode.org/iso15924/iso15924-codes.html\">ISO 15924</a> and <a href=\"http://unstats.un.org/unsd/methods/m49/m49.htm\">UN M.49</a> to define a language tag. Each complete tag is composed of subtags which are not case sensitive and can also be empty.</p>\n\n\t<p>Ordering of the subtags in the case that they are all non-empty is: <code>language</code>-<code>script</code>-<code>region</code>-<code>variant</code>-<code>extension</code>-<code>privateuse</code>. Should any subtag be empty, its corresponding hyphen would also be ommited. Thus, the language tag for English will be <code>en</code> <strong>and not</strong> <code>en-----</code>.</p>\n\n\t<p>Most language tags consist of a two- or three-letter language subtag (from <a href=\"http://www.loc.gov/standards/iso639-2/php/English_list.php\">ISO 639-1/ISO 639-2</a>). Sometimes, this is followed by a two-letter or three-digit region subtag (from <a href=\"http://www.iso.ch/iso/en/prods-services/iso3166ma/02iso-3166-code-lists/list-en1.html\">ISO 3166-1 alpha-2</a> or <a href=\"http://unstats.un.org/unsd/methods/m49/m49.htm\">UN M.49</a>). Some examples are:</p>\n\n\t<table summary=\"Examples of various possible language tags as described by RFC 4646 and RFC 4647\">\n\t<caption>Language tag examples</caption>\n\t<thead>\n\t<tr>\n\t\t<th scope=\"col\">Language tag</th>\n\t\t<th scope=\"col\">Description</th>\n\t\t<th scope=\"col\">Component subtags</th>\n\t</tr>\n\t</thead>\n\t<tbody>\n\t<tr>\n\t\t<td><code>en</code></td>\n\t\t<td>English</td>\n\t\t<td><code>language</code></td>\n\t</tr>\n\t<tr>\n\t\t<td><code>mas</code></td>\n\t\t<td>Masai</td>\n\t\t<td><code>language</code></td>\n\t</tr>\n\t<tr>\n\t\t<td><code>fr-CA</code></td>\n\t\t<td>French as used in Canada</td>\n\t\t<td><code>language</code>+<code>region</code></td>\n\t</tr>\n\t<tr>\n\t\t<td><code>en-833</code></td>\n\t\t<td>English as used in the Isle of Man</td>\n\t\t<td><code>language</code>+<code>region</code></td>\n\t</tr>\n\t<tr>\n\t\t<td><code>zh-Hans</code></td>\n\t\t<td>Chinese written with Simplified script</td>\n\t\t<td><code>language</code>+<code>script</code></td>\n\t</tr>\n\t<tr>\n\t\t<td><code>zh-Hant-HK</code></td>\n\t\t<td>Chinese written with Traditional script as used in Hong Kong</td>\n\t\t<td><code>language</code>+<code>script</code>+<code>region</code></td>\n\t</tr>\n\t<tr>\n\t\t<td><code>de-AT-1996</code></td>\n\t\t<td>German as used in Austria with 1996 orthography</td>\n\t\t<td><code>language</code>+<code>region</code>+<code>variant</code></td>\n\t</tr>\n\t</tbody>\n\t</table>\n\n\t<p>The ultimate aim of a language tag is to convey the needed <strong>useful distingushing information</strong>, whilst keeping it as <strong>short as possible</strong>. So for example, use <code>en</code>, <code>fr</code> and <code>ja</code> as opposed to <code>en-GB</code>, <code>fr-FR</code> and <code>ja-JP</code>, since we know English, French and Japanese are the native language of Great Britain, France and Japan respectively.</p>\n\n\t<p>Next is the <a href=\"http://www.unicode.org/iso15924/iso15924-codes.html\">ISO 15924</a> language script code and when one should or shouldn't use it. For example, whilst <code>en-Latn</code> is syntaxically correct for describing English written with Latin script, real world English writing is <strong>more-or-less exclusively in the Latin script</strong>. For such languages like English that are written in a single script, the <a href=\"http://www.iana.org/assignments/language-subtag-registry\"><abbr title=\"Internet Assigned Numbers Authority\">IANA</abbr> Language Subtag Registry</a> has a \"Suppress-Script\" field meaning the script code <strong>should be ommitted</strong> unless a specific language tag requires a specific script code. Some languages are <strong>written in more than one script</strong> and in such cases, the script code <strong>is encouraged</strong> since an end-user may be able to read their language in one script, but not the other. Some examples are:</p>\n\n\t<table summary=\"Examples of using a language subtag in combination with a script subtag\">\n\t<caption>Language subtag + script subtag examples</caption>\n\t<thead>\n\t<tr>\n\t\t<th scope=\"col\">Language tag</th>\n\t\t<th scope=\"col\">Description</th>\n\t\t<th scope=\"col\">Component subtags</th>\n\t</tr>\n\t</thead>\n\t<tbody>\n\t<tr>\n\t\t<td><code>en-Brai</code></td>\n\t\t<td>English written in Braille script</td>\n\t\t<td><code>language</code>+<code>script</code></td>\n\t</tr>\n\t<tr>\n\t\t<td><code>en-Dsrt</code></td>\n\t\t<td>English written in Deseret (Mormon) script</td>\n\t\t<td><code>language</code>+<code>script</code></td>\n\t</tr>\n\t<tr>\n\t\t<td><code>sr-Latn</code></td>\n\t\t<td>Serbian written in Latin script</td>\n\t\t<td><code>language</code>+<code>script</code></td>\n\t</tr>\n\t<tr>\n\t\t<td><code>sr-Cyrl</code></td>\n\t\t<td>Serbian written in Cyrillic script</td>\n\t\t<td><code>language</code>+<code>script</code></td>\n\t</tr>\n\t<tr>\n\t\t<td><code>mn-Mong</code></td>\n\t\t<td>Mongolian written in Mongolian script</td>\n\t\t<td><code>language</code>+<code>script</code></td>\n\t</tr>\n\t<tr>\n\t\t<td><code>mn-Cyrl</code></td>\n\t\t<td>Mongolian written in Cyrillic script</td>\n\t\t<td><code>language</code>+<code>script</code></td>\n\t</tr>\n\t<tr>\n\t\t<td><code>mn-Phag</code></td>\n\t\t<td>Mongolian written in Phags-pa script</td>\n\t\t<td><code>language</code>+<code>script</code></td>\n\t</tr>\n\t<tr>\n\t\t<td><code>az-Cyrl-AZ</code></td>\n\t\t<td>Azerbaijani written in Cyrillic script as used in Azerbaijan</td>\n\t\t<td><code>language</code>+<code>script</code>+<code>region</code></td>\n\t</tr>\n\t<tr>\n\t\t<td><code>az-Latn-AZ</code></td>\n\t\t<td>Azerbaijani written in Latin script as used in Azerbaijan</td>\n\t\t<td><code>language</code>+<code>script</code>+<code>region</code></td>\n\t</tr>\n\t<tr>\n\t\t<td><code>az-Arab-IR</code></td>\n\t\t<td>Azerbaijani written in Arabic script as used in Iran</td>\n\t\t<td><code>language</code>+<code>script</code>+<code>region</code></td>\n\t</tr>\n\t</tbody>\n\t</table>\n\n\t<p>Usage of the three-digit <a href=\"http://unstats.un.org/unsd/methods/m49/m49.htm\">UN M.49</a> code over the two-letter <a href=\"http://www.iso.ch/iso/en/prods-services/iso3166ma/02iso-3166-code-lists/list-en1.html\">ISO 3166-1 alpha-2</a> code should hapen if a macro-geographical entity is required and/or the <a href=\"http://www.iso.ch/iso/en/prods-services/iso3166ma/02iso-3166-code-lists/list-en1.html\">ISO 3166-1 alpha-2</a> is ambiguous.</p>\n\n\t<p>Examples of English using marco-geographical regions:</p>\n\n\t<table summary=\"Examples for English of ISO 3166-1 alpha-2 vs. UN M.49 code\">\n\t<caption>Coding for English using macro-geographical regions</caption>\n\t<thead>\n\t<tr>\n\t\t<th scope=\"col\">ISO 639-1/ISO 639-2 + ISO 3166-1 alpha-2</th>\n\t\t<th scope=\"col\" colspan=\"2\">ISO 639-1/ISO 639-2 + UN M.49 (Example macro regions)</th>\n\t</tr>\n\t</thead>\n\t<tbody>\n\t<tr>\n\t\t<td><dl><dt><code>en-AU</code></dt><dd>English as used in <strong>Australia</strong></dd></dl></td>\n\t\t<td rowspan=\"2\"><dl><dt><code>en-053</code></dt><dd>English as used in <strong>Australia &amp; New Zealand</strong></dd></dl></td>\n\t\t<td rowspan=\"3\"><dl><dt><code>en-009</code></dt><dd>English as used in <strong>Oceania</strong></dd></dl></td>\n\t</tr>\n\t<tr>\n\t\t<td><dl><dt><code>en-NZ</code></dt><dd>English as used in <strong>New Zealand</strong></dd></dl></td>\n\t</tr>\n\t<tr>\n\t\t<td><dl><dt><code>en-FJ</code></dt><dd>English as used in <strong>Fiji</strong></dd></dl></td>\n\t\t<td><dl><dt><code>en-054 </code></dt><dd>English as used in <strong>Melanesia</strong></dd></dl></td>\n\t</tr>\n\t</tbody>\n\t</table>\n\n\t<p>Examples of Spanish using marco-geographical regions:</p>\n\n\t<table summary=\"Examples for Spanish of ISO 3166-1 alpha-2 vs. UN M.49 code\">\n\t<caption>Coding for Spanish macro-geographical regions</caption>\n\t<thead>\n\t<tr>\n\t\t<th scope=\"col\">ISO 639-1/ISO 639-2 + ISO 3166-1 alpha-2</th>\n\t\t<th scope=\"col\" colspan=\"2\">ISO 639-1/ISO 639-2 + UN M.49 (Example macro regions)</th>\n\t</tr>\n\t</thead>\n\t<tbody>\n\t<tr>\n\t\t<td><dl><dt><code>es-PR</code></dt><dd>Spanish as used in <strong>Puerto Rico</strong></dd></dl></td>\n\t\t<td rowspan=\"3\"><dl><dt><code>es-419</code></dt><dd>Spanish as used in <strong>Latin America &amp; the Caribbean</strong></dd></dl></td>\n\t\t<td rowspan=\"4\"><dl><dt><code>es-019</code></dt><dd>Spanish as used in <strong>the Americas</strong></dd></dl></td>\n\t</tr>\n\t<tr>\n\t\t<td><dl><dt><code>es-HN</code></dt><dd>Spanish as used in <strong>Honduras</strong></dd></dl></td>\n\t</tr>\n\t<tr>\n\t\t<td><dl><dt><code>es-AR</code></dt><dd>Spanish as used in <strong>Argentina</strong></dd></dl></td>\n\t</tr>\n\t<tr>\n\t\t<td><dl><dt><code>es-US</code></dt><dd>Spanish as used in <strong>United States of America</strong></dd></dl></td>\n\t\t<td><dl><dt><code>es-021</code></dt><dd>Spanish as used in <strong>North America</strong></dd></dl></td>\n\t</tr>\n\t</tbody>\n\t</table>\n\n\t<p>Example of where the <a href=\"http://www.iso.ch/iso/en/prods-services/iso3166ma/02iso-3166-code-lists/list-en1.html\">ISO 3166-1 alpha-2</a> is ambiguous and why <a href=\"http://unstats.un.org/unsd/methods/m49/m49.htm\">UN M.49</a> might be preferred:</p>\n\n\t<table summary=\"Example where the ISO 3166-1 alpha-2 is ambiguous\">\n\t<caption>Coding for ambiguous ISO 3166-1 alpha-2 regions</caption>\n\t<thead>\n\t<tr>\n\t\t<th scope=\"col\" colspan=\"2\"><code>CS</code> assignment pre-1994</th>\n\t\t<th scope=\"col\" colspan=\"2\"><code>CS</code> assignment post-1994</th>\n\t</tr>\n\t</thead>\n\t<tbody>\n\t<tr>\n\t\t<td colspan=\"2\">\n\t\t<dl>\n\t\t<dt><code>CS</code></dt><dd><strong>Czechoslovakia</strong> (ISO 3166-1)</dd>\n\t\t<dt><code>200</code></dt><dd><strong>Czechoslovakia</strong> (UN M.49)</dd>\n\t\t</dl>\n\t\t</td>\n\t\t<td colspan=\"2\">\n\t\t<dl>\n\t\t<dt><code>CS</code></dt><dd><strong>Serbian &amp; Montenegro</strong> (ISO 3166-1)</dd>\n\t\t<dt><code>891</code></dt><dd><strong>Serbian &amp; Montenegro</strong> (UN M.49)</dd>\n\t\t</dl>\n\t\t</td>\n\t</tr>\n\t<tr>\n\t\t<td>\n\t\t<dl>\n\t\t<dt><code>CZ</code></dt><dd><strong>Czech Republic</strong> (ISO 3166-1)</dd>\n\t\t<dt><code>203</code></dt><dd><strong>Czech Republic</strong> (UN M.49)</dd>\n\t\t</dl>\n\t\t</td>\n\t\t<td>\n\t\t<dl>\n\t\t<dt><code>SK</code></dt><dd><strong>Slovakia</strong> (ISO 3166-1)</dd>\n\t\t<dt><code>703</code></dt><dd><strong>Slovakia</strong> (UN M.49)</dd>\n\t\t</dl>\n\t\t</td>\n\t\t<td>\n\t\t<dl>\n\t\t<dt><code>RS</code></dt><dd><strong>Serbia</strong> (ISO 3166-1)</dd>\n\t\t<dt><code>688</code></dt><dd><strong>Serbia</strong> (UN M.49)</dd>\n\t\t</dl>\n\t\t</td>\n\t\t<td>\n\t\t<dl>\n\t\t<dt><code>ME</code></dt><dd><strong>Montenegro</strong> (ISO 3166-1)</dd>\n\t\t<dt><code>499</code></dt><dd><strong>Montenegro</strong> (UN M.49)</dd>\n\t\t</dl>\n\t\t</td>\n\t</tr>\n\t</tbody>\n\t</table>\n\n\t<h4>Macro-languages &amp; Topolects:</h4>\n\n\t<p><a href=\"http://tools.ietf.org/html/rfc4646\">RFC 4646</a> anticipates features which shall be available in (currently draft) <a href=\"http://www.sil.org/iso639-3/\">ISO 639-3</a> which aims to provide as complete enumeration of languages as possible, including living, extinct, ancient and constructed languages, whether majour, minor or unwritten. A new feature of <a href=\"http://www.sil.org/iso639-3/\">ISO 639-3</a> compared to the previous two revisions is the concept of <a href=\"http://www.sil.org/iso639-3/macrolanguages.asp\">macrolanguages</a> where Arabic and Chinese are two such examples. In such cases, their respective codes of <code>ar</code> and <code>zh</code> is very vague as to which dialect/topolect is used or perhaps some terse classical variant which may be difficult for all but very educated users. For such macrolanguages, it is recommended that the sub-language tag is used as a suffix to the macrolanguage tag, eg:</p>\n\n\t<table summary=\"Examples of macrolanguages used with sub-language subtags\">\n\t<caption>Macrolanguage subtag + sub-language subtag examples</caption>\n\t<thead>\n\t<tr>\n\t\t<th scope=\"col\">Language tag</th>\n\t\t<th scope=\"col\">Description</th>\n\t\t<th scope=\"col\">Component subtags</th>\n\t</tr>\n\t</thead>\n\t<tbody>\n\t<tr>\n\t\t<td><code>zh-cmn</code></td>\n\t\t<td>Mandarin (Putonghau/Guoyu) Chinese</td>\n\t\t<td><code>macrolanguage</code>+<code>sublanguage</code></td>\n\t</tr>\n\t<tr>\n\t\t<td><code>zh-yue</code></td>\n\t\t<td>Yue (Cantonese) Chinese</td>\n\t\t<td><code>macrolanguage</code>+<code>sublanguage</code></td>\n\t</tr>\n\t<tr>\n\t\t<td><code>zh-cmn-Hans</code></td>\n\t\t<td>Mandarin (Putonghau/Guoyu) Chinese written in Simplified script</td>\n\t\t<td><code>macrolanguage</code>+<code>sublanguage</code>+<code>script</code></td>\n\t</tr>\n\t<tr>\n\t\t<td><code>zh-cmn-Hant</code></td>\n\t\t<td>Mandarin (Putonghau/Guoyu) Chinese written in Traditional script</td>\n\t\t<td><code>macrolanguage</code>+<code>sublanguage</code>+<code>script</code></td>\n\t</tr>\n\t<tr>\n\t\t<td><code>zh-nan-Latn-TW</code></td>\n\t\t<td>Minnan (Hoklo) Chinese written in Latin script (POJ Romanisation) as used in Taiwan</td>\n\t\t<td><code>macrolanguage</code>+<code>sublanguage</code>+<code>script</code>+<code>region</code></td>\n\t</tr>\n\t</tbody>\n\t</table>\n\n\t<a name=\"otherconsiderations\"></a><h3>6.ii. Other considerations</h3>\n\n\t<h4>Normalisation of language tags for phpBB:</h4>\n\n\t<p>For phpBB, the language tags are <strong>not</strong> used in their raw form and instead converted to all lower-case and have the hyphen <code>-</code> replaced with an underscore <code>_</code> where appropriate, with some examples below:</p>\n\n\t<table summary=\"Normalisation of language tags for usage in phpBB\">\n\t<caption>Language tag normalisation examples</caption>\n\t<thead>\n\t<tr>\n\t\t<th scope=\"col\">Raw language tag</th>\n\t\t<th scope=\"col\">Description</th>\n\t\t<th scope=\"col\">Value of <code>USER_LANG</code><br />in <code>./common.php</code></th>\n\t\t<th scope=\"col\">Language pack directory<br />name in <code>/language/</code></th>\n\t</tr>\n\t</thead>\n\t<tbody>\n\t<tr>\n\t\t<td><code>en</code></td>\n\t\t<td>British English</td>\n\t\t<td><code>en</code></td>\n\t\t<td><code>en</code></td>\n\t</tr>\n\t<tr>\n\t\t<td><code>de-AT</code></td>\n\t\t<td>German as used in Austria</td>\n\t\t<td><code>de-at</code></td>\n\t\t<td><code>de_at</code></td>\n\t</tr>\n\t<tr>\n\t\t<td><code>es-419</code></td>\n\t\t<td>Spanish as used in Latin America &amp; Caribbean</td>\n\t\t<td><code>en-419</code></td>\n\t\t<td><code>en_419</code></td>\n\t</tr>\n\t<tr>\n\t\t<td><code>zh-yue-Hant-HK</code></td>\n\t\t<td>Cantonese written in Traditional script as used in Hong Kong</td>\n\t\t<td><code>zh-yue-hant-hk</code></td>\n\t\t<td><code>zh_yue_hant_hk</code></td>\n\t</tr>\n\t</tbody>\n\t</table>\n\n\t<h4>How to use <code>iso.txt</code>:</h4>\n\n\t<p>The <code>iso.txt</code> file is a small UTF-8 encoded plain-text file which consists of three lines:</p>\n\n\t<ol>\n\t\t<li><code>Language's English name</code></li>\n\t\t<li><code>Language's local name</code></li>\n\t\t<li><code>Authors information</code></li>\n\t</ol>\n\n\t<p><code>iso.txt</code> is automatically generated by the language pack submission system on phpBB.com. You don't have to create this file yourself if you plan on releasing your language pack on phpBB.com, but do keep in mind that phpBB itself does require this file to be present.</p>\n\n\t<p>Because language tags themselves are meant to be machine read, they can be rather obtuse to humans and why descriptive strings as provided by <code>iso.txt</code> are needed. Whilst <code>en-US</code> could be fairly easily deduced to be \"English as used in the United States\", <code>de-CH</code> is more difficult less one happens to know that <code>de</code> is from \"<span lang=\"de\">Deutsch</span>\", German for \"German\" and <code>CH</code> is the abbreviation of the official Latin name for Switzerland, \"<span lang=\"la\">Confoederatio Helvetica</span>\".</p>\n\n\t<p>For the English language description, the language name is always first and any additional attributes required to describe the subtags within the language code are then listed in order separated with commas and enclosed within parentheses, eg:</p>\n\n\t<table summary=\"English language description examples of iso.txt for usage in phpBB\">\n\t<caption>English language description examples for iso.txt</caption>\n\t<thead>\n\t<tr>\n\t\t<th scope=\"col\">Raw language tag</th>\n\t\t<th scope=\"col\">English description within <code>iso.txt</code></th>\n\t</tr>\n\t</thead>\n\t<tbody>\n\t<tr>\n\t\t<td><code>en</code></td>\n\t\t<td>British English</td>\n\t</tr>\n\t<tr>\n\t\t<td><code>en-US</code></td>\n\t\t<td>English (United States)</td>\n\t</tr>\n\t<tr>\n\t\t<td><code>en-053</code></td>\n\t\t<td>English (Australia &amp; New Zealand)</td>\n\t</tr>\n\t<tr>\n\t\t<td><code>de</code></td>\n\t\t<td>German</td>\n\t</tr>\n\t<tr>\n\t\t<td><code>de-CH-1996</code></td>\n\t\t<td>German (Switzerland, 1996 orthography)</td>\n\t</tr>\n\t<tr>\n\t\t<td><code>gws-1996</code></td>\n\t\t<td>Swiss German (1996 orthography)</td>\n\t</tr>\n\t<tr>\n\t\t<td><code>zh-cmn-Hans-CN</code></td>\n\t\t<td>Mandarin Chinese (Simplified, Mainland China)</td>\n\t</tr>\n\t<tr>\n\t\t<td><code>zh-yue-Hant-HK</code></td>\n\t\t<td>Cantonese Chinese (Traditional, Hong Kong)</td>\n\t</tr>\n\t</tbody>\n\t</table>\n\n\t<p>For the localised language description, just translate the English version though use whatever appropriate punctuation typical for your own locale, assuming the language uses punctuation at all.</p>\n\n\t<h4>Unicode bi-directional considerations:</h4>\n\n\t<p>Because phpBB is now UTF-8, all translators must take into account that certain strings may be shown when the directionality of the document is either opposite to normal or is ambiguous.</p>\n\n\t<p>The various Unicode control characters for bi-directional text and their HTML enquivalents where appropriate are as follows:</p>\n\n\t<table summary=\"Table of the various Unicode bidirectional control characters\">\n\t<caption>Unicode bidirectional control characters &amp; HTML elements/entities</caption>\n\t<thead>\n\t<tr>\n\t\t<th scope=\"col\">Unicode character<br />abbreviation</th>\n\t\t<th scope=\"col\">Unicode<br />code-point</th>\n\t\t<th scope=\"col\">Unicode character<br />name</th>\n\t\t<th scope=\"col\">Equivalent HTML<br />markup/entity</th>\n\t\t<th scope=\"col\">Raw character<br />(enclosed between '')</th>\n\t</tr>\n\t</thead>\n\t<tbody>\n\t<tr>\n\t\t<td><code>LRM</code></td>\n\t\t<td><code>U+200E</code></td>\n\t\t<td>Left-to-Right Mark</td>\n\t\t<td><code>&amp;lrm;</code></td>\n\t\t<td>'&#x200E;'</td>\n\t</tr>\n\t<tr>\n\t\t<td><code>RLM</code></td>\n\t\t<td><code>U+200F</code></td>\n\t\t<td>Right-to-Left Mark</td>\n\t\t<td><code>&amp;rlm;</code></td>\n\t\t<td>'&#x200F;'</td>\n\t</tr>\n\t<tr>\n\t\t<td><code>LRE</code></td>\n\t\t<td><code>U+202A</code></td>\n\t\t<td>Left-to-Right Embedding</td>\n\t\t<td><code>dir=&quot;ltr&quot;</code></td>\n\t\t<td>'&#x202A;'</td>\n\t</tr>\n\t<tr>\n\t\t<td><code>RLE</code></td>\n\t\t<td><code>U+202B</code></td>\n\t\t<td>Right-to-Left Embedding</td>\n\t\t<td><code>dir=&quot;rtl&quot;</code></td>\n\t\t<td>'&#x202B;'</td>\n\t</tr>\n\t<tr>\n\t\t<td><code>PDF</code></td>\n\t\t<td><code>U+202C</code></td>\n\t\t<td>Pop Directional Formatting</td>\n\t\t<td><code>&lt;/bdo&gt;</code></td>\n\t\t<td>'&#x202C;'</td>\n\t</tr>\n\t<tr>\n\t\t<td><code>LRO</code></td>\n\t\t<td><code>U+202D</code></td>\n\t\t<td>Left-to-Right Override</td>\n\t\t<td><code>&lt;bdo dir=&quot;ltr&quot;&gt;</code></td>\n\t\t<td>'&#x202D;'</td>\n\t</tr>\n\t<tr>\n\t\t<td><code>RLO</code></td>\n\t\t<td><code>U+202E</code></td>\n\t\t<td>Right-to-Left Override</td>\n\t\t<td><code>&lt;bdo dir=&quot;rtl&quot;&gt;</code></td>\n\t\t<td>'&#x202E;'</td>\n\t</tr>\n\t</tbody>\n\t</table>\n\n\t<p>For <code>iso.txt</code>, the directionality of the text can be explicitly set using special Unicode characters via any of the three methods provided by left-to-right/right-to-left markers/embeds/overrides, as without them, the ordering of characters will be incorrect, eg:</p>\n\n\t<table summary=\"Effect of using Unicode bidirectional control characters within iso.txt\">\n\t<caption>Unicode bidirectional control characters iso.txt</caption>\n\t<thead>\n\t<tr>\n\t\t<th scope=\"col\">Directionality</th>\n\t\t<th scope=\"col\">Raw character view</th>\n\t\t<th scope=\"col\">Display of localised<br />description in <code>iso.txt</code></th>\n\t\t<th scope=\"col\">Ordering</th>\n\t</tr>\n\t</thead>\n\t<tbody>\n\t<tr>\n\t\t<td><code>dir=&quot;ltr&quot;</code></td>\n\t\t<td>English (Australia &amp; New Zealand)</td>\n\t\t<td dir=\"ltr\">English (Australia &amp; New Zealand)</td>\n\t\t<td class=\"good\">Correct</td>\n\t</tr>\n\t<tr>\n\t\t<td><code>dir=&quot;rtl&quot;</code></td>\n\t\t<td>English (Australia &amp; New Zealand)</td>\n\t\t<td dir=\"rtl\">English (Australia &amp; New Zealand)</td>\n\t\t<td class=\"bad\">Incorrect</td>\n\t</tr>\n\t<tr>\n\t\t<td><code>dir=&quot;rtl&quot;</code> with <code>LRM</code></td>\n\t\t<td>English (Australia &amp; New Zealand)<code>U+200E</code></td>\n\t\t<td dir=\"rtl\">English (Australia &amp; New Zealand)&#x200E;</td>\n\t\t<td class=\"good\">Correct</td>\n\t</tr>\n\t<tr>\n\t\t<td><code>dir=&quot;rtl&quot;</code> with <code>LRE</code> &amp; <code>PDF</code></td>\n\t\t<td><code>U+202A</code>English (Australia &amp; New Zealand)<code>U+202C</code></td>\n\t\t<td dir=\"rtl\">&#x202A;English (Australia &amp; New Zealand)&#x202C;</td>\n\t\t<td class=\"good\">Correct</td>\n\t</tr>\n\t<tr>\n\t\t<td><code>dir=&quot;rtl&quot;</code> with <code>LRO</code> &amp; <code>PDF</code></td>\n\t\t<td><code>U+202D</code>English (Australia &amp; New Zealand)<code>U+202C</code></td>\n\t\t<td dir=\"rtl\">&#x202D;English (Australia &amp; New Zealand)&#x202C;</td>\n\t\t<td class=\"good\">Correct</td>\n\t</tr>\n\t</tbody>\n\t</table>\n\n\t<p>In choosing which of the three methods to use, in the majority of cases, the <code>LRM</code> or <code>RLM</code> to put a &quot;strong&quot; character to fully enclose an ambiguous punctuation character and thus make it inherit the correct directionality is sufficient.</p>\n\t<p>Within some cases, there may be mixed scripts of a left-to-right and right-to-left direction, so using <code>LRE</code> &amp; <code>RLE</code> with <code>PDF</code> may be more appropriate. Lastly, in very rare instances where directionality must be forced, then use <code>LRO</code> &amp; <code>RLO</code> with <code>PDF</code>.</p>\n\t<p>For further information on authoring techniques of bi-directional text, please see the W3C tutorial on <a href=\"http://www.w3.org/International/tutorials/bidi-xhtml/\">authoring techniques for XHTML pages with bi-directional text</a>.</p>\n\n\t<h4>Working with placeholders:</h4>\n\n\t<p>As phpBB is translated into languages with different ordering rules to that of English, it is possible to show specific values in any order deemed appropriate. Take for example the extremely simple &quot;Page <em>X</em> of <em>Y</em>&quot;, whilst in English this could just be coded as:</p>\n\n\t<div class=\"codebox\"><pre>\n\t...\n'PAGE_OF'\t=&gt;\t'Page %s of %s',\n\t\t/* Just grabbing the replacements as they\n\t\tcome and hope they are in the right order */\n\t...\n\t</pre></div>\n\n\t<p>&hellip; a clearer way to show explicit replacement ordering is to do:</p>\n\n\t<div class=\"codebox\"><pre>\n\t...\n'PAGE_OF'\t=&gt;\t'Page %1$s of %2$s',\n\t\t/* Explicit ordering of the replacements,\n\t\teven if they are the same order as English */\n\t...\n\t</pre></div>\n\n\t<p>Why bother at all? Because some languages, the string transliterated back to English might read something like:</p>\n\n\t<div class=\"codebox\"><pre>\n\t...\n'PAGE_OF'\t=&gt;\t'Total of %2$s pages, currently on page %1$s',\n\t\t/* Explicit ordering of the replacements,\n\t\treversed compared to English as the total comes first */\n\t...\n\t</pre></div>\n\n\t<a name=\"writingstyle\"></a><h3>6.iii. Writing Style</h3>\n\n\t<h4>Miscellaneous tips &amp; hints:</h4>\n\n\t<p>As the language files are PHP files, where the various strings for phpBB are stored within an array which in turn are used for display within an HTML page, rules of syntax for both must be considered. Potentially problematic characters are: <code>'</code> (straight quote/apostrophe), <code>&quot;</code> (straight double quote), <code>&lt;</code> (less-than sign), <code>&gt;</code> (greater-than sign) and <code>&amp;</code> (ampersand).</p>\n\n\t<p class=\"bad\">// Bad - The un-escapsed straight-quote/apostrophe will throw a PHP parse error</p>\n\n\t<div class=\"codebox\"><pre>\n\t...\n'CONV_ERROR_NO_AVATAR_PATH'\n\t=&gt;\t'Note to developer: you must specify $convertor['avatar_path'] to use %s.',\n\t...\n\t</pre></div>\n\n\t<p class=\"good\">// Good - Literal straight quotes should be escaped with a backslash, ie: \\</p>\n\n\t<div class=\"codebox\"><pre>\n\t...\n'CONV_ERROR_NO_AVATAR_PATH'\n\t=&gt;\t'Note to developer: you must specify $convertor[\\'avatar_path\\'] to use %s.',\n\t...\n\t</pre></div>\n\n\t<p>However, because phpBB3 now uses UTF-8 as its sole encoding, we can actually use this to our advantage and not have to remember to escape a straight quote when we don't have to:</p>\n\n\t<p class=\"bad\">// Bad - The un-escapsed straight-quote/apostrophe will throw a PHP parse error</p>\n\n\t<div class=\"codebox\"><pre>\n\t...\n'USE_PERMISSIONS'\t=&gt;\t'Test out user's permissions',\n\t...\n\t</pre></div>\n\n\t<p class=\"good\">// Okay - However, non-programmers wouldn't type \"user\\'s\" automatically</p>\n\n\t<div class=\"codebox\"><pre>\n\t...\n'USE_PERMISSIONS'\t=&gt;\t'Test out user\\'s permissions',\n\t...\n\t</pre></div>\n\n\t<p class=\"good\">// Best - Use the Unicode Right-Single-Quotation-Mark character</p>\n\n\t<div class=\"codebox\"><pre>\n\t...\n'USE_PERMISSIONS'\t=&gt;\t'Test out user&rsquo;s permissions',\n\t...\n\t</pre></div>\n\n\t<p>The <code>&quot;</code> (straight double quote), <code>&lt;</code> (less-than sign) and <code>&gt;</code> (greater-than sign) characters can all be used as displayed glyphs or as part of HTML markup, for example:</p>\n\n\t<p class=\"bad\">// Bad - Invalid HTML, as segments not part of elements are not entitised</p>\n\n\t<div class=\"codebox\"><pre>\n\t...\n'FOO_BAR'\t=&gt;\t'PHP version &lt; 4.3.3.&lt;br /&gt;\n\tVisit &quot;Downloads&quot; at &lt;a href=&quot;http://www.php.net/&quot;&gt;www.php.net&lt;/a&gt;.',\n\t...\n\t</pre></div>\n\n\t<p class=\"good\">// Okay - No more invalid HTML, but &quot;&amp;quot;&quot; is rather clumsy</p>\n\n\t<div class=\"codebox\"><pre>\n\t...\n'FOO_BAR'\t=&gt;\t'PHP version &amp;lt; 4.3.3.&lt;br /&gt;\n\tVisit &amp;quot;Downloads&amp;quot; at &lt;a href=&quot;http://www.php.net/&quot;&gt;www.php.net&lt;/a&gt;.',\n\t...\n\t</pre></div>\n\n\t<p class=\"good\">// Best - No more invalid HTML, and usage of correct typographical quotation marks</p>\n\n\t<div class=\"codebox\"><pre>\n\t...\n'FOO_BAR'\t=&gt;\t'PHP version &amp;lt; 4.3.3.&lt;br /&gt;\n\tVisit &ldquo;Downloads&rdquo; at &lt;a href=&quot;http://www.php.net/&quot;&gt;www.php.net&lt;/a&gt;.',\n\t...\n\t</pre></div>\n\n\t<p>Lastly, the <code>&amp;</code> (ampersand) must always be entitised regardless of where it is used:</p>\n\n\t<p class=\"bad\">// Bad - Invalid HTML, none of the ampersands are entitised</p>\n\n\t<div class=\"codebox\"><pre>\n\t...\n'FOO_BAR'\t=&gt;\t'&lt;a href=&quot;http://somedomain.tld/?foo=1&amp;bar=2&quot;&gt;Foo &amp; Bar&lt;/a&gt;.',\n\t...\n\t</pre></div>\n\n\t<p class=\"good\">// Good - Valid HTML, amperands are correctly entitised in all cases</p>\n\n\t<div class=\"codebox\"><pre>\n\t...\n'FOO_BAR'\t=&gt;\t'&lt;a href=&quot;http://somedomain.tld/?foo=1&amp;amp;bar=2&quot;&gt;Foo &amp;amp; Bar&lt;/a&gt;.',\n\t...\n\t</pre></div>\n\n\t<p>As for how these charcters are entered depends very much on choice of Operating System, current language locale/keyboard configuration and native abilities of the text editor used to edit phpBB language files. Please see <a href=\"http://en.wikipedia.org/wiki/Unicode#Input_methods\">http://en.wikipedia.org/wiki/Unicode#Input_methods</a> for more information.</p>\n\n\t<h4>Spelling, punctuation, grammar, et cetera:</h4>\n\n\t<p>The default language pack bundled with phpBB is <strong>British English</strong> using <a href=\"http://www.cambridge.org/\">Cambridge University Press</a> spelling and is assigned the language code <code>en</code>. The style and tone of writing tends towards formal and translations <strong>should</strong> emulate this style, at least for the variant using the most compact language code. Less formal translations or those with colloquialisms <strong>must</strong> be denoted as such via either an <code>extension</code> or <code>privateuse</code> tag within its language code.</p>\n\n\t\t</div>\n\n\t\t<div class=\"back2top\"><a href=\"#wrap\" class=\"top\">Back to Top</a></div>\n\n\t\t<span class=\"corners-bottom\"><span></span></span></div>\n\t</div>\n\n\t<hr />\n\n<a name=\"vcs\"></a><h2>7. VCS Guidelines</h2>\n\n\t<div class=\"paragraph\">\n\t\t<div class=\"inner\"><span class=\"corners-top\"><span></span></span>\n\n\t\t<div class=\"content\">\n\n\t<p>The version control system for phpBB3 is git. The repository is available at <a href=\"http://github.com/phpbb/phpbb3\" title=\"repository\">http://github.com/phpbb/phpbb3</a>.</p>\n\n\t<a name=\"repostruct\"></a><h3>7.i. Repository Structure</h3>\n\n\t<ul>\n\t\t<li><strong>develop</strong><br />The latest unstable development version with new features etc.</li>\n\t\t<li><strong>develop-*</strong><br />Development branches of stable phpBB releases. Branched off of <code>develop</code> at the time of feature freeze.\n\t\t\t<ul>\n\t\t\t\t<li><strong>phpBB3.0</strong><code>develop-olympus</code><br />Development branch of the stable 3.0 line. Bug fixes are applied here.</li>\n\t\t\t\t<li><strong>phpBB3.1</strong><code>develop-ascraeus</code><br />Development branch of the stable 3.1 line. Bug fixes are applied here.</li>\n\t\t\t</ul>\n\t\t</li>\n\t\t<li><strong>master</strong><br />A branch containing all stable phpBB3 release points</li>\n\t\t<li><strong>tags</strong><br />Released versions. Stable ones get merged into the master branch.\n\t\t\t<ul>\n\t\t\t\t<li><code>release-3.Y-BX</code><br />Beta release X of the 3.Y line.</li>\n\t\t\t\t<li><code>release-3.Y-RCX</code><br />Release candidate X of the 3.Y line.</li>\n\t\t\t\t<li><code>release-3.Y.Z-RCX</code><br />Release candidate X of the stable 3.Y.Z release.</li>\n\t\t\t\t<li><code>release-3.0.X</code><br />Stable <strong>3.0.X</strong> release.</li>\n\t\t\t\t<li><code>release-2.0.X</code><br />Old stable 2.0.X release.</li>\n\t\t\t</ul>\n\t\t</li>\n\t</ul>\n\n\t<a name=\"commitmessage\"></a><h3>7.ii. Commit Messages and Reposiory Rules</h3>\n\n\t<p>Information on repository rules, such as commit messages can be found at <a href=\"http://wiki.phpbb.com/display/DEV/Git\" title=\"phpBB Git Information\">http://wiki.phpbb.com/display/DEV/Git</p>.\n\n\t\t</div>\n\n\t\t<div class=\"back2top\"><a href=\"#wrap\" class=\"top\">Back to Top</a></div>\n\n\t\t<span class=\"corners-bottom\"><span></span></span></div>\n\t</div>\n\n\t<hr />\n\n<a name=\"disclaimer\"></a><h2>9. Copyright and disclaimer</h2>\n\n\t<div class=\"paragraph\">\n\t\t<div class=\"inner\"><span class=\"corners-top\"><span></span></span>\n\n\t\t<div class=\"content\">\n\n\t<p>This application is opensource software released under the <a href=\"http://opensource.org/licenses/gpl-license.php\">GPL</a>. Please see source code and the docs directory for more details. This package and its contents are Copyright (c) 2000, 2002, 2005, 2007 <a href=\"http://www.phpbb.com/\">phpBB Group</a>, All Rights Reserved.</p>\n\n\t\t</div>\n\n\t\t<div class=\"back2top\"><a href=\"#wrap\" class=\"top\">Back to Top</a></div>\n\n\t\t<span class=\"corners-bottom\"><span></span></span></div>\n\t</div>\n\n<!-- END DOCUMENT -->\n\n\t<div id=\"page-footer\">\n\t\t<div class=\"version\"> $Id$ </div>\n\t</div>\n</div></div>\n\n<div>\n\t<a id=\"bottom\" name=\"bottom\" accesskey=\"z\"></a>\n</div>\n\n</body>\n</html>\n"
  },
  {
    "path": "docs/html/developer.html",
    "content": "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">\n<html xmlns=\"http://www.w3.org/1999/xhtml\" dir=\"ltr\" lang=\"ru\" xml:lang=\"ru\">\n<head>\n\n<meta http-equiv=\"content-type\" content=\"text/html; charset=utf-8\" />\n<meta http-equiv=\"content-style-type\" content=\"text/css\" />\n<meta http-equiv=\"content-language\" content=\"ru\" />\n<meta http-equiv=\"imagetoolbar\" content=\"no\" />\n<meta name=\"resource-type\" content=\"document\" />\n<meta name=\"distribution\" content=\"global\" />\n<meta name=\"copyright\" content=\"2009 Gorlum for http://supernova.ws\" />\n<meta name=\"keywords\" content=\"browser game, браузерка, ogame, огейм, supernova, сверхновая, супернова, xnova, ragerepack, clone, multiplayer, ога, клон\" />\n<meta name=\"description\" content=\"Документация для разработчиков и администраторов Проекта &quot;СуперНова&quot;. Documentation for SuperNoava developers and administrators.\" />\n<meta name=\"author\" content=\"Gorlum, gorlum@supernova.ws\" />\n<meta name=\"language\" content=\"russian\" />\n\n<title>Для разработчиков - Документация - СуперНова</title>\n\n<link href=\"stylesheet.css\" rel=\"stylesheet\" type=\"text/css\" media=\"screen, projection\" />\n\n</head>\n\n<body id=\"phpbb\" class=\"section-docs\">\n\n<div id=\"wrap\">\n  <a id=\"top\" name=\"top\" accesskey=\"t\"></a>\n  <div id=\"page-header\">\n    <div class=\"headerbar\">\n      <div class=\"inner\"><span class=\"corners-top\"><span></span></span>\n\n      <div id=\"doc-description\">\n        <a href=\"../index.php\" id=\"logo\"><img src=\"supernova.png\" alt=\"\" /></a>\n        <h1>Проект &quot;СуперНова&quot;</h1>\n        <p>Документация для разработчиков</p>\n        <p style=\"display: none;\"><a href=\"#start_here\">Skip</a></p>\n      </div>\n\n      <span class=\"corners-bottom\"><span></span></span></div>\n    </div>\n  </div>\n\n  <a name=\"start_here\"></a>\n\n  <div id=\"page-body\">\n\n<!-- BEGIN DOCUMENT -->\n\n<p>Сведения для разработчиков, желающих модифицировать код Проекта &quot;СуперНова&quot;</p>\n\n<h1>Документация для разработчиков</h1>\n<div class=\"paragraph menu\"><div class=\"inner\"><span class=\"corners-top\"><span></span></span><div class=\"content\">\n\n<ul>\n  <li><a href=\"#defaults\">Общие сведения</a>\n    <ul>\n      <li><a href=\"#code-about\">Немного о коде и его структуре</a></li>\n      <li><a href=\"#pcg\">Стандарты кодирования (Coding Guidelines)</a></li>\n      <li><a href=\"#prefixes\">Модули, подсистемы и префиксы</a></li>\n      <li><a href=\"#versions\">Версионирование</a></li>\n    </ul>\n  </li>\n\n  <li><a href=\"#git\">Использование GIT</a>\n  <ul>\n    <li><a href=\"#git-commits\">Коммиты в GIT</a></li>\n  </ul>\n  </li>\n\n  <li><a href=\"#pte\">Шаблоны</a>\n<!--\n  <ol style=\"list-style-type: lower-roman;\">\n    <li><a href=\"#namingvars\">Variable/Function/Class Naming</a></li>\n  </ol>\n-->\n  </li>\n\n  <li><a href=\"#logs\">Логи</a>\n  <ul>\n    <li><a href=\"#logs_codes\">Коды логов (таблица logs)</a></li>\n  </ul>\n  </li>\n\n<!--\n  <li><a href=\"#styling\">Styling</a>\n  <ol style=\"list-style-type: lower-roman;\">\n    <li><a href=\"#cfgfiles\">Style Config Files</a></li>\n    <li><a href=\"#genstyling\">General Styling Rules</a></li>\n  </ol></li>\n  <li><a href=\"#templating\">Templating</a>\n  <ol style=\"list-style-type: lower-roman;\">\n    <li><a href=\"#templates\">General Templating</a></li>\n    <li><a href=\"#inheritance\">Template Inheritance</a></li>\n  </ol></li>\n  <li><a href=\"#charsets\">Character Sets and Encodings</a></li>\n  <li><a href=\"#translation\">Translation (<abbr title=\"Internationalisation\">i18n</abbr>/<abbr title=\"Localisation\">L10n</abbr>) Guidelines</a>\n  <ol style=\"list-style-type: lower-roman;\">\n    <li><a href=\"#standardisation\">Standardisation</a></li>\n    <li><a href=\"#otherconsiderations\">Other considerations</a></li>\n    <li><a href=\"#writingstyle\">Writing Style</a></li>\n  </ol>\n  </li>\n  <li><a href=\"#vcs\">VCS Guidelines</a>\n  <ol style=\"list-style-type: lower-roman;\">\n    <li><a href=\"#repostruct\">Repository structure</a></li>\n    <li><a href=\"#commitmessage\">Commit Messages and Repository Rules</a></li>\n  </ol>\n  </li>\n  <li><a href=\"#changes\">Guidelines Changelog</a></li>\n  <li><a href=\"#disclaimer\">Copyright and disclaimer</a></li>\n-->\n</ul>\n\n</div><span class=\"corners-bottom\"><span></span></span></div></div><hr />\n\n<a name=\"defaults\"></a><h2>Общие сведения</h2>\n<div class=\"paragraph\"><div class=\"inner\"><span class=\"corners-top\"><span></span></span><div class=\"content\">\n\n<a name=\"code-about\"></a><h3>Немного о видах и структуре кода</h3>\n\n<p>\nКод в СуперНове делится на несколько частей. Каждая из них имеет свое происхождение, свою идеологию и свою структуру\n</p>\n\n<ol>\n  <li>\n    Оригинальный код XNova 0.8b RageRepack v226. Сборная солянка из оригинального кода XNova 0.8b и кучи модов с просторов\n    интеренета, вбитых в оригинальный движок при помощи лома и какой-то матери. Качество варьируется от \"хорошого\" (очень, очень\n    мало такого кода!) до \"ужасного\" и попросту \"нерабочего\". Например, в коде RR ПОЧТИ ВСЕ параметры, попадающие через запросы\n    GET и POST передавались в БД напрямую без какой либо проверки и обработки! В общем - одна сплошная дыра для SQL-injection и\n    радость малолетней хакервоты (школоты, начитавшейся журнала \"Хакер\").\n  </li>\n  <li>\n    Этап \"совместимости\" (от запуска сервера в июне 2009 и примерно до апреля-мая 2010го). Тогда я максимально старался сохранить\n    обратную совместимость с исходным кодом для более легкого встраивания модов из инета. Но за почти год работы сервера не нашлось\n    ни единого мода, который мне бы захотелось встроить в движок, а геммороя из-за неоптимальной архитектуры и грубой состыковки старых\n    модов было предостаточно. И примерно в это время я решил плюнуть на совместимость с родным движком и начать переписывать код целиком.\n    Качество кода этого этапа так же варьируется от \"хорошого\" (мало) до \"ужасного\" (к счастью - тоже мало). Нерабочий код был только в тестовых версиях и\n    фиксился до выкатывания на паблик. Алгоритмы максимально наследуют оригиналу, поэтому если были какие-то логические ошибки в расчетах -\n    они успешно повторялись в новом коде.\n  </li>\n  <li>\n    Этап \"пре-паблик\" (май 2010 - октябрь 2010). Я плюнул на совместимость и принялся переписывать целые подсистемы. Зачастую происходило\n    так - сообщени о баге или заявка на фишку, я нахожу причину/код нуждающийся в модификации, хватаюсь за голову при виде кода и начинаю переписывать.\n    Так были переписаны Альянсы, Друзья, система отправки флотов и многое-многое другое, включая кучу подсистем. Подробнее можно посмотреть в docs/changelog_dev.txt.\n    Код - от \"плохого\" в начале этапа до \"очень хорошего\" в конце. В это же время я начал следовать phpBB Coding Standarts и подключил\n    библиотеку темплейтов от phpBB3. Алгоритмы этого этапа примерно 50 на 50 - половина следует за оригиналом, половина переписывается с нуля.\n  </li>\n  <li>\n    Этап \"паблик\" (октябрь 2010 - декабрь 2010). Несколько инсталляций и отзывов заставили переработать подсистемы статистики и апдейтера,\n    а так же проработать документацию для сторонних пользователей. Так же отзывы показали, что от унаследованного кода нужно избавлятся полностью.\n    Полурабочие системы регистрации и входа в игру, нерабочая система восстановления паролей, нерабочая очередь построек (отключенная на моих\n    серверах, но включаемая в настройках) и множество других косяков - то, на что я и игроки моих вселенных уже не обращали внимание. Так же\n    обнаружилось несколько неприятных багов, о которых игроки моих серверов не сообщали ни разу. Обнаружилась нестойкость движка к подмене\n    данных на стороне клиента - изначально в RR так же полностью отсутствовала проверка корректности получаемых данных, а сам я очень жестко банил на\n    сервере хакеров и проблема остро не вставала... до начала эксплуатации в других условиях. Заодно стало понятно, что код предыдущих этапов надо\n    переписывать по той же причине. Начал перетрушивать файловую организацию движка. Чем больше файлов - тем медленнее работает сервер. На моем\n    сервере это незаметно, но на слабеньких компьютерах, на shared хостингах и просто на плохо настроенных машинах полезли проблемы. Код от \"средненького\"\n    до \"очень хорошего\". Почти всё, переписанное или написанное на этом этапе использует мои собственные алгоритмы (там, где это имеет смысл). Остальные\n    алгоритмы либо элементарные, либо модифицированы для увеличения скорости работы.\n  </li>\n  <li>\n    Этап \"оптимизации\" (январь 2011 - январь 2012). Нагрузочное тестирование на одном из серверов продемонстрировало полную несостоятельность\n    самой критичной части движка - менеджера летящих флотов - и подтвердило мнение о необходимости полностью избавлятся от унаследованного кода, меняя\n    алгоритмы работы.\n  </li>\n  <li>\n    Этап \"MVC\" (начиная с февраля 2012 года). MVC ака Model-Controller-View - концепция разделения кода по функционалу. Подробнее - http://ru.wikipedia.org/wiki/Model-View-Controller .\n    MVC кажется избыточной, но для сложных проектов она очень сильно облегчает создание модульных приложений.<br />\n    В районе нового 2012 года я проводил различные эксперименты по использованию сторонних MVC-фреймворков.\n    Увы, результаты оказались крайне плачевными. Проблемы в изначальной архитектуре xnova даже с учетом моих исправлений (а иногда, увы, благодаря им) не давало шансов на использование\n    существующего кода. Вывод однозначен - для использования сторонних фреймворков надо переписывать полностью ВСЁ. Это потребует несколько горлумомесяцев\n    и в текущих условиях невозможно. Поэтому я принял следующее решение - постепенно довести архитектуру СуперНовы до MVC-подобной и лишь затем начинать миграцию.\n    Текущий этап посвящен именно этому. Он разбит на несколько подэтапов:\n    <ol>\n      <li>\n        Избавление от кода, написанного на 1, 2 и частично 3 этапах. Это так называемый \"xnova-код\" или попросту \"говнокод\". Собственно по косвенным данным GIT и моим \n        прикидкам, собственно кода xnova осталось где-то процентов 10-15, если считать темплейты. Если не считать - не более 5%. Однако количества говнокода существенно\n        больше. Я сам активно плодил его на 1-3 этапах. Плохое знание PHP и короткое знакомство с его возможностями, слабое знание исходников xnova,\n        слабое понимание перспектив движка, следование изначальной архитектуре - вот причины выхода говнокода из моих рук.</li>\n      <li>\n        Избавление от кода, написанного с середины 3-го и по 5й этапы включительно. Это так называемый \"PTL-код\". Большой шаг вперед по сравнению с говнокодом! Я набрался опыта в PHP,\n        ознакомился с его возможностями и самое главное - выработал стратегию развития СуперНовы. Стратегия - понятно, еще не архитектура, но уже значительный шаг вперед.\n        Напрямую используется phpBB Template Libriary. В этой парадигме рендерер движка оперирует объектами-темплейтами, а не строками, как xnova-движок. Почти весь текущий код\n        основной части движка является PTL-кодом. По сути своей это т.н. spaghetti-код в легкой разновидности: смесь реакций на действия, изменений данных, рендеринга темплейта - и все \n        это густо приправлено MySQL-кодом без малейшего намека на абстракцию. Конечно, бывает еще хуже - как, например, в говнокоде, где представление (HTML-код) не было\n        отделено от данных. Сейчас движок находится именно на этом подэтапе. Начиная с версии 35a12.0 я вплотную занялся рефакторингом кода и переписыванием явных фрагментов\n        говнокода. Например, в настоящий момент (сентябрь 2012) я избавляюсь от использований переменной $parse и связанной с ней кода. Заодно это так же влечет полное переписывание\n        отдельных страниц и целых подсистем. Так, в частнсти, были переписаны страницы Заметок и Технологий. А так же многие другие.\n      </li>\n      <li>\n        Рефакторинг кода под концепцию MVC. Часть работы проделана уже сейчас. Например, модуль player_race уже является по факту MVC. Особенности разработки движка (восходящий метод)\n        привели к существованию нескольких подтипов MVC-кода:\n        <ul>\n          <li>\n            preMVC - это страницы, которые используют механизм MVCv1 (см. ниже), но не являются классами. Например, страница чата (/includes/pages/chat.php, да и все остальные\n            в этом каталоге)\n          </li>\n          <li>\n            MVCv1 - это код, который использует текущие возможности MVC-модели. Например, модуль player_race (см. массив 'mvc' в манифесте). Большая часть этих возможностей\n            заточена на создание модулей и пока слабо приспособлена для обработки отдельных страниц. Поэтому сейчас я разрабатываю следующую более универсальню версию\n            MVC-подобной архитектуры MVCv2.\n          </li>\n          <li>\n            MVCv2 существует пока только в виде архитектуры в моей голове. Предполагается, что это будет совсем MVC и вообще 42. Посмотрим.\n          </li>\n        </ul>\n      </li>\n    </ol>\n  </li>\n</ol><br />\n\n<p>\nВ движке СН (как и в любом программном продукте) есть баги. Я стараюсь тщательно тестировать все обновления, однако наличие говнокода и его взаимодействие с нормальным кодом\nвносит свои коррективы.\n</p>\n\n<p>\nНе стоит путать \"баги\" с \"особенностями\". Я с самого начала открещивался от оффа. Копировать чужое мне неинтересно. Кроме того, отдельные аспекты оффа\nвызывали недоумения, вопросы и лично мне портили удовольстви от игры. Я завязал играть на оффе, когда выяснил, что вышедшего из нуб-зоны игрока может атаковать топ-1.\nЕстественно, у меня защита новых и слабых игроков реализована иначе - по примеру XNova.\n</p>\n\n<p>\nЯ меняю те части игры, которые не нравятся лично мне, на те, которые мне нравятся. Т.е. я делаю игру, в которую лично мне интересно играть. Все пожелания и\nпредложения рассматриваются именно в таком ключе - будет ли мне интересно играть в игру с такими изменениями? Например, мне (за небольшим исключением) нравится\nтекущая система расчета боя. Система оГейма мне кажется крайне нелогичной, неинтуитивной и неинтересной. Поэтому я буду доделывать свою систему, а не ориентироваться\nна офф.\n</p>\n\n<p>\nВообще, статус проекта, как такового - \"Альфа\". Это значит, что отдельные аспекты игры в ходе разработки могут быть полностью изменены. Планы изменений есть в\nисходниках - желающие могут с ними ознакомиться.\n</p>\n\n<a name=\"pcg\"></a><h3>Стандарты кодирования (Coding Guidelines)</h3>\n<p>Начиная с августа 2010 года, я  стараюсь  следовать <a href=\"coding-guidelines.html\">phpBB3  Coding  Guidelines</a> (далее - PCG) с одним  исключением:  табуляция  расширяются  в  два  пробела  и заменяются по возможности на пробелы. Выделяются два уровня совместимости:  PCG или PCG0 - код отформатирован в соответствии с PCG; PCG1 - включает PCG0, а так же  означает,  что  переменные  названы  в  соответствии  с  PCG  и   правильно расставлены одинарные и двойные кавычки</p>\n\n<a name=\"prefixes\"></a><h3>Модули, подсистемы и префиксы</h3>\n<p>Предопределенные функции и пакеты</p>\n<table width=\"700\">\n  <tr><th>ИД</th><th>Подсистема</th><th>Что относится к подсистеме</th></tr>\n  <tr><td>adm</td><td>admin</td><td>Администрирование сервера</td></tr>\n  <tr><td>ali</td><td>alliances</td><td>Альянсы: создание и управление</td></tr>\n  <tr><td>bud</td><td>buddy</td><td>Система Друзей</td></tr>\n  <tr><td>cht</td><td>chat</td><td>Чат</td></tr>\n  <tr><td>coe</td><td>combat</td><td>Боевой движок: обсчет боя между флотами,  обсчет ракетных атак, симулятор боя</td></tr>\n  <tr><td>eco</td><td>economic</td><td>Экономика (расчет начисления ресурсов)</td></tr>\n  <tr><td>eco_bld</td><td>building</td><td>Строительство и покупка юнитов (включая офицеров, исследования и работу верфей)</td></tr>\n  <tr><td>flt</td><td>fleet</td><td>Флоты: отправка флотов, обработка флотов в полете, обработка небоевых миссий</td></tr>\n  <tr><td>int</td><td>interface</td><td>Интерфейсные функции</td></tr>\n  <tr><td>log</td><td>logs</td><td>Система встроенных логов</td></tr>\n  <tr><td>msg</td><td>message</td><td>Система сообщений</td></tr>\n  <tr><td>qst</td><td>quest</td><td>Подсистема квестов</td></tr>\n  <tr><td>reg</td><td>registration</td><td>Работа с незарегестрированными пользователями: логин, регистрация, восстановление пароля</td></tr>\n  <tr><td>rpg</td><td>rpg</td><td>Ролевая система (опыт, уровни, начисление ТМ, Чёрный Рынок итд)</td></tr>\n  <tr><td>sys</td><td>system</td><td>Системные функции</td></tr>\n  <tr><td>uni</td><td>universe</td><td>Вселенная (планеты, луны, поля обломков)</td></tr>\n  <tr><th>sn</th><th>supernova</th><th>Функции, не относящиеся к какому-либо из вышеперечисленных блоков</th></tr>\n</table>\n\n<p>\nИдентификаторы широко используются в названиях файлов, функций, переменных и так далее для облегчения навигации в коде\n</p>\n\n<a name=\"versions\"></a><h3>Версионирование</h3>\n<p>\n  СуперНова состоит из двух взаимосвязанных компонентов: базы данных MySQL и PHP-движка.\n  Версионирование каждого из компонентов осуществляется отдельно.\n</p>\n\n<p>\n  В БД СуперНовы в таблце <code>config</code> в записи с ключом <code>`config_name` = 'db_version'</code> хранится её текущая версия.\n  Версия БД - число, которое увеличивается каждый раз при изменении структуры БД.\n</p>\n\n<p>\n  Версия движка СуперНовы хранится в файле <code>/includes/constants.php</code> в константе <code>SN_VERSION</code>.\n  Номер релиза хранится в том же файле в константе <code>SN_RELEASE</code>.\n  Движок версионируются строкой <code>&lt;release&gt;&lt;stage&gt;[&lt;edition&gt;][r&lt;revision&gt;]</code>, где\n</p>\n<ul>\n  <li><code>&lt;release&gt;</code> - релиз СуперНовы. Увеличивается при одноразовом значительном изменении в\n    коде движка (новая фишка, переписывание целой подсистемы итд), при накоплении большого числа мелких правок или\n    при изменении версии БД.</li>\n  <li>\n    <code>&lt;stage&gt;</code> - этап текущей версии. Строчная буква английского алфавита, увеличивающаяся при переходе на\n    следующий этап работы над движком. Может принимать следующие значения:\n    <ul>\n      <li><code>a</code> - \"alpha\" - предварительная \"скелетная\" версия. На публике появляется в качестве \"proof of conception\"\n        и используется для оценки востребованности изменения, предварительной демонстрации возможностей, поиска грубых ошибок в\n        логике работы итд. Обычно не уходит дальше тестового сервера. Крайне нежелательно устанавливать куда-либо кроме\n        сервера, предназначенного специально для тестирования</li>\n      <li><code>b</code> - \"beta\" - версия для открытого тестирования. Рекомендуется для установки тем, кто хочет пораньше\n        ознакомиться с новыми фишками и желает помочь в отлове багов. Качество таких версий может колебаться от \"чуть лучше альфы\" до\n        \"почти релиз\". Желательно ставить на отдельную тестовую вселенную, которой не страшны вайпы</li>\n      <li><code>c</code> - \"candidate\" - пре-релиз. Версия, в которой устранены все известные ошибки и вылизан интерфейс. Установка\n        такой версии на production сервер относительно безопасна и очень желательна для поиска особо хитрых багов, проявляющихся только\n        под большими нагрузками или в особых условиях</li>\n      <li><code>d</code> - \"deployment\" - стабильная версия для развертывания на production серверах, релиз</li>\n      <li><code>e</code> - \"extra\" - пост-релиз с небольшими изменениями. Это может быть обновленный дамп БД, изменения в документации,\n        поправки в локализации, тюнинг интерфейса, а так же косметические багфиксы. Устанавливать этот релиз необязательно</li>\n      <li><code>f</code> - \"fixes\" - пост-релиз с фиксами внезапно обнаруженных в релизе критических багов или проблем с внутриигровым балансом.\n        Объема добавленного кода недостаточно для выпуска следующего полноценного релиза, однако апгрейд до этой версии крайне рекомендуется\n      </li>\n    </ul>\n  </li>\n  <li><code>&lt;edition&gt;</code> - редакция очередного этапа текущей версии. Необязательный параметр. Изменяется при необходимости\n    отметить модификацию кода на текущем этапе</li>\n  <li><code>r&lt;revision&gt;</code> - ревизия движка. Необязательный параметр. Изменяется автоматически системой контроля версии.\n    Добавляется, когда необходимо различать редакции СуперНовы в процессе разработки</li>\n</ul>\n\n<p>\n  В движке вместе с номером его версиии хранится так же требуемая версия БД (константа <code>DB_VERSION</code> в\n  <code>/includes/constants.php</code>). Если движок запустится на версии БД выше, чем та, с которой может работать, он аварийно\n  прекратит свою работу с сообщением об ошибке. В нормальных условиях такого произойти не должно.\n  Такое может произойти, например, при восстановлении бэкапа более новой версии БД на старый движок.  Данный механизм призван блокировать\n  возможные повреждения структуры БД в случае ошибок администратора сервера, сбоев в скрипте резервирования/восстановления, хакерской\n  атаки итд.\n</p>\n\n<p>\n  В движок встроен автоапдейтер структуры БД. Поэтому если более новая версия движка будет запущена на старой версии БД, он\n  автоматически производет необходимые изменения в структуре базы (включая все необходимые перерасчеты). Поэтому в случае необходимости\n  можно восстанавливать старые бэкапы БД на новую версию движка.\n</p>\n\n</div><div class=\"back2top\"><a href=\"#wrap\" class=\"top\">Back to Top</a></div><span class=\"corners-bottom\"><span></span></span></div></div><hr />\n\n<a name=\"git\"></a><h2>Использование GIT</h2>\n<div class=\"paragraph\"><div class=\"inner\"><span class=\"corners-top\"><span></span></span><div class=\"content\">\n\n<a name=\"git-commits\"></a><h3>Коммиты в GIT</h3>\n\n<p>\n  Начиная с версии 26 описание коммитов в GIT-репозиториях стандартизировано. Они имеют следующий формат:<br />\n  <code>&lt;type&gt;-&lt;subsystem_affected&gt;[-version]: &lt;comment&gt;</code>\n</p>\n<p>\n  Начиная с ревизии 34a8.1 описание коммитов в GIT-репозиториях изменилось. Теперь они имеют следующий формат:<br />\n  <code>&lt;subsystem_affected&gt;-&lt;type&gt;-version: &lt;comment&gt;</code><br />\n  Обращаю внимание, что параметр <code>version</code> стал обязательным и контролируется git-хуком\n</p>\n<p>\n  Расшифровка переменных:\n</p>\n<ul>\n  <li><code>type</code> - тип коммита. Может принимать следующие значения:\n    <ul>\n      <li><code>release</code> - релиз движка. В комментарии указывается тэг, по которому можно получить данный релиз</li>\n      <li><code>crit</code> - устранена критическая ошибка в коде или уязвимость. Необходимо срочно установить это обновление</li>\n      <li><code>fix</code> - устранена ошибка в коде. Крайне желательно установить это обновление</li>\n      <li><code>feat</code> - обновление добавляет новую небольшую фичу в движок (большая фича меняет номер версии). Можно поставить это\n        обновление, можно дождаться очередного релиза</li>\n      <li><code>misc</code> - мелкое изменение, не влияющее на игру. Например - изменение форматирования для соответствия PCG1, переименование\n        переменных, небольшие изменения в алгоритмах и т.д.</li>\n      <li><code>doc</code> - изменение в документации</li>\n      <li><code>work</code> - промежуточный рабочий коммит. Обычно делается что бы зафиксировать работу над текущими изменениями в проекте, переключиться в другую ветку\n        итд. Серия таких коммитов закрывается одним из вышеперечисленных типов.\n      </li>\n      <li><code>git</code>, а так же все коммиты без префиксов - технические коммиты, связанные с использованием GIT: внесение изменений в конфигурацию GIT,\n        изменение тэгов, разделение/слияние веток итд</li>\n    </ul>\n  </li>\n  <li>\n    <code>subsystem_affected</code> - список <a href=\"#prefixes\">кодов подсистем</a>, которые затрагивает этот коммит. Если их несколько, то ранее они разделялись знаком \"+\".\n    Теперь упоминается только та подсистема, изменения в которой наиболее значительны\n  </li>\n  <li><code>version</code> - <a href=\"#versions\">версия</a> движка</li>\n  <li><code>comment</code> - краткое описание коммита</li>\n</ul>\n\n</div><div class=\"back2top\"><a href=\"#wrap\" class=\"top\">Back to Top</a></div><span class=\"corners-bottom\"><span></span></span></div></div><hr />\n\n<a name=\"pte\"></a><h2>Шаблоны</h2>\n<div class=\"paragraph\"><div class=\"inner\"><span class=\"corners-top\"><span></span></span><div class=\"content\">\n\n<p>\n  Движок использует phpBB3 Tepmlate Engine (далее - PTE). PTE доработан до полной обратной  совместимости  со  старым  template-движком  XNova,  а  так  же   для\n  совместимости с моими новвоведениями. Из основных отличий можно отметить следующие:\n</p>\n\n<ul>\n  <li>Доработанный  PTE  понимает название переменных из шаблона, написанные маленькими буквами</li>\n  <li>Переменные вида L_xxx (&quot;L&quot; от &quot;language&quot;) разворачиваются в переменную $lang['xxx']</li>\n  <li>Переменные вида С_xxx (&quot;C&quot; от &quot;config&quot;) разворачиваются в переменную конфигурации xxx ($config-&gt;xxx)</li>\n  <li>Переменные вида D_xxx (&quot;D&quot; от &quot;define&quot;) разворачиваются в константу xxx</li>\n  <li>Поддерживаются индексированные значения переменных вида xxx[yyy]</li>\n  <li>Символьные переменные не могут использоваться в условиях - их необходимо передавать в виде стандартных переменных PTL</li>\n</ul>\n<p>Подробнее о формате темплейтов можно  узнать в <a href=\"coding-guidelines.html\">PCG</a> и из документации phpBB3.</p>\n<p>Шаблоны  хранятся в каталоге <code>/design/templates/&lt;имя шаблона&gt;</code>. В\nнастоящее время имеют расширение .TPL.HTML, а не .HTML, как в оригинальном PTE</p>\n\n</div><div class=\"back2top\"><a href=\"#wrap\" class=\"top\">Back to Top</a></div><span class=\"corners-bottom\"><span></span></span></div></div><hr />\n\n<a name=\"logs\"></a><h2>Логи</h2>\n<div class=\"paragraph\"><div class=\"inner\"><span class=\"corners-top\"><span></span></span><div class=\"content\">\n\n<p>СуперНова имеет встроенную систему логов для облегчения работы администрации сервера</p>\n\n<a name=\"logs_codes\"></a><h3>Коды логов (таблица logs)</h3>\n<table width=\"700px\">\n<tr><th>0</th><th>Код по умолчанию</th></tr>\n\n<tr><th>100</th><th>Информационные коды</th></tr>\n<tr><td>102</td><td>Изменение количества Тёмной Материи</td></tr>\n<tr><td>103</td><td>Изменение структуры БД</td></tr>\n<tr><td>104</td><td>Переименование объекта Вселенной</td></tr>\n<tr><td>105</td><td>Пользователь отменил премиум аккаунт</td></tr>\n<tr><td>190</td><td>Запуск обновления статистики игроков</td></tr>\n<tr><td>191</td><td>Сообщения системы обновления статистики</td></tr>\n<tr><td>192</td><td>Обновление статистики игроков выполнено успешно</td></tr>\n\n<tr><th>2xx</th><th>Операция завершена успешно</th></tr>\n<tr><td>200</td><td>OK</td></tr>\n<!--\n<tr><td>201</td><td>Created</td></tr>\n<tr><td>202</td><td>Accepted</td></tr>\n<tr><td>203</td><td>Non-Authoritative Information</td></tr>\n<tr><td>204</td><td>No Content</td></tr>\n<tr><td>205</td><td>Reset Content</td></tr>\n<tr><td>206</td><td>Partial Content</td></tr>\n-->\n<tr><th>3xx</th><th>Предупреждения системы логов</th></tr>\n<tr><th>300</th><th>Общий код по умолчанию</th></tr>\n<tr><td>301</td><td>Возможный багоюз. Когда-то данное действие пользователя приводило к ошибке, дающей ему неоправданное преимущество в игре (удвоение  флота, бесплатная или моментальная постройка итд), т.е. являлось хаком. Сейчас эта ошибка устранена, но стоит присмотрется к пользователю - возможно он багоюзер или хакер.</td></tr>\n<tr><td>302</td><td>Попытка взлома. Пользователь передал серверу данные, не совпадаю реальными (например - другой ID пользователя вместо своего, другой ID альянса, вместо своего итд). Обычно это означает попытку взлома. Очень редко это может означать о наличии ошибки в коде игры</td></tr>\n<tr><td>303</td><td>Пользователь не найден в системе. Это может означать, что пользователь на странице логина или регестрируется. Или это может означать ошибку</td></tr>\n<tr><td>304</td><td>Колонизация с несколькими кораблями</td></tr>\n<tr><td>305</td><td>Попытка взлома. Пользователь передал отрицательное количество ресурсов на странице Чёрный Рынок->Торговец ресурсами. В нормальных условиях это сделать невозможно. До фикса это приводило к увеличению ресурса на указанную величину.</td></tr>\n<tr><td>306</td><td>Попытка взлома. Пользователь передал в списке кораблей на странице Чёрный Рынок не-корабль. В нормальных условиях это сделать невозможно.</td></tr>\n<tr><td>307</td><td>Попытка взлома. Пользователь передал отрицательное количество кораблей на странице Чёрный Рынок. В нормальных условиях это сделать невозможно.</td></tr>\n<tr><td>399</td><td>Запись системы слежения за игроками</td></tr>\n\n<tr><th>4xx</th><th>Ошибки при запросе клиента</th></tr>\n<tr><td>401</td><td>Unauthorized. Пользователь попытался получить доступ к части сайта, не доступной для его уровня доступа</td></tr>\n<tr><td>402</td><td>Ошибка изменения количества Тёмной Материи</td></tr>\n<tr><td>403</td><td>Forbidden. Попытка взлома - пользователь попытался выполнять отдельный файл вне нормального хода операции. Например - попытался выполнить файл, предназначенный только для include</td></tr>\n<!--<br />\n  400 Bad Request<br />\n  403 Forbidden<br />\n  404 Not Found<br />\n  405 Method Not Allowed<br />\n  406 Not Acceptable<br />\n  407 Proxy Authentication Required<br />\n  408 Request Timeout<br />\n  409 Conflict<br />\n  410 Gone<br />\n  411 Length Required<br />\n  412 Precondition Failed<br />\n  413 Request Entity Too Large<br />\n  414 Request-URI Too Long<br />\n  415 Unsupported Media Type<br />\n  416 Requested Range Not Satisfiable<br />\n  417 Expectation Failed<br />\n-->\n<tr><th>500</th><th>Ошибки сервера - сбой в БД или ошибки в коде движка</th></tr>\n<tr><td>501</td><td>У игрока отрицательное количество ресурсов</td></tr>\n<tr><td>502</td><td>Ошибка записи пользователя: у пользователя в качестве родного мира назначена несуществующая планета</td></tr>\n<tr><td>503</td><td>У планеты нет хозяина</td></tr>\n<tr><td>504</td><td>Таймаут менеджера флотов</td></tr>\n<!--<br />\n  501 Not Implemented<br />\n  502 Bad Gateway<br />\n  503 Service Unavailable<br />\n  504 Gateway Timeout<br />\n  505 HTTP Version Not Supported<br />\n-->\n</table>\n\n</div><div class=\"back2top\"><a href=\"#wrap\" class=\"top\">Back to Top</a></div><span class=\"corners-bottom\"><span></span></span></div></div><hr />\n\n<!-- END DOCUMENT -->\n\n  <div id=\"page-footer\">\n    <div class=\"version\">Project &quot;SuperNova.WS&quot; Release 34. Last updated: 2012-09-04 03:48</div>\n  </div>\n</div></div>\n\n<div>\n  <a id=\"bottom\" name=\"bottom\" accesskey=\"z\"></a>\n</div>\n\n</body>\n</html>\n"
  },
  {
    "path": "docs/html/readme.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n    <head>\n        <meta charset=\"UTF-8\">\n        <title>Document</title>\n        <script src=\"http://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js\"></script>\n        <style type=\"text/css\">\n            body{\n            background: #000;\n            color: #b8b6b4;\n            }\n            .page{\n            width: 960px;\n            margin: 0 auto;\n            }\n            .list{\n            list-style: none;\n            }\n            a{\n            color: #b8b6b4;\n            }\n            table{\n            width: 100%;\n            }\n        </style>\n    </head>\n    <body>\n        <div class=\"page\">\n            <ul class=\"contents\">\n                <li><a href=\"#chapter_1\">Проект \"СуперНова\"</a></li>\n                <li><a href=\"#chapter_2\">Актуальность</a></li>\n                <li><a href=\"#chapter_3\">Disclaimer aka Отмазка</a></li>\n                <li><a href=\"#chapter_4\">Предуведомления о необходимости наличия квалификации</a></li>\n                <li><a href=\"#chapter_5\">Помощь проекту</a></li>\n                <li><a href=\"#chapter_6\">Установка</a></li>\n                <li><a href=\"#chapter_7\">Благодарности</a></li>\n                <li><a href=\"#chapter_8\">Проект \"СуперНова\" в интернете</a></li>\n                <li><a href=\"#chapter_9\">Документация</a></li>\n                <li><a href=\"#chapter_10\">Терминология</a></li>\n                <li><a href=\"#chapter_11\">Тёмная Материя</a></li>\n                <li><a href=\"#chapter_12\">Лог движения ТМ</a></li>\n                <li><a href=\"#chapter_13\">Покупка ТМ за внеигровые ресурсы</a></li>\n                <li><a href=\"#chapter_14\">Страница платежа \"Тёмная Материя\"</a></li>\n                <li><a href=\"#chapter_15\">Модуль платежной системы XSolla: payment_xsolla_currency</a></li>\n                <li><a href=\"#chapter_16\">Модуль платежной системы WebMoney: payment_webmoney</a></li>\n                <li><a href=\"#chapter_17\">Модуль платежной системы RoboKass: payment_robokassa</a></li>\n                <li><a href=\"#chapter_18\">Партнерская программа</a></li>\n                <li><a href=\"#chapter_19\">Развитие: опыт, уровни и ТМ</a></li>\n                <li><a href=\"#chapter_20\">Строительство и исследование</a></li>\n                <li><a href=\"#chapter_21\">Рейдерство</a></li>\n                <li><a href=\"#chapter_22\">Модуль \"Премиумный аккаунт\": player_premium</a></li>\n                <li><a href=\"#chapter_23\">Период действия премиума</a></li>\n                <li><a href=\"#chapter_24\">Уровень премиума</a></li>\n                <li><a href=\"#chapter_25\">Преимущества премиума</a></li>\n                <li><a href=\"#chapter_26\">Покупка секторов на планете</a></li>\n                <li><a href=\"#chapter_27\">Телепортация планеты</a></li>\n                <li><a href=\"#chapter_28\">Перенос столицы</a></li>\n                <li><a href=\"#chapter_29\">Плотность планеты</a></li>\n                <li><a href=\"#chapter_30\">Офицеры (наемники и губернаторы) и Чертежи</a></li>\n                <li><a href=\"#chapter_31\">Бонус к скорости постройки/исследования</a></li>\n                <li><a href=\"#chapter_32\">Наемники</a></li>\n                <li><a href=\"#chapter_33\">Губернаторы</a></li>\n                <li><a href=\"#chapter_34\">Чертежи</a></li>\n                <li><a href=\"#chapter_35\">Артефакты</a></li>\n                <li><a href=\"#chapter_36\">Модуль \"Капитаны\": unit_captain</a></li>\n                <li><a href=\"#chapter_37\">Смена имени пользователя</a></li>\n                <li><a href=\"#chapter_38\">Чёрный Рынок</a></li>\n                <li><a href=\"#chapter_39\">Продавец информации</a></li>\n                <li><a href=\"#chapter_40\">[#] Модуль player_race: Расы</a></li>\n                <li><a href=\"#chapter_41\">[#] Модуль player_race_units: Расовые юниты</a></li>\n                <li><a href=\"#chapter_42\">Флот: корабли, миссии и боевая система</a></li>\n                <li><a href=\"#chapter_43\">Боевая система</a></li>\n                <li><a href=\"#chapter_44\">UBEv4</a></li>\n                <li><a href=\"#chapter_45\">Шпионаж</a></li>\n                <li><a href=\"#chapter_46\">Удержание</a></li>\n                <li><a href=\"#chapter_47\">Прочее</a></li>\n                <li><a href=\"#chapter_48\">Корабли</a></li>\n                <li><a href=\"#chapter_49\">Экономика</a></li>\n                <li><a href=\"#chapter_50\">Исследования</a></li>\n                <li><a href=\"#chapter_51\">Система рейтингования и статистика</a></li>\n                <li><a href=\"#chapter_52\">Статистика</a></li>\n                <li><a href=\"#chapter_53\">Защита \"начинающих\" и \"слабых\" игроков</a></li>\n                <li><a href=\"#chapter_54\">Прокачка</a></li>\n                <li><a href=\"#chapter_55\">Квесты</a></li>\n                <li><a href=\"#chapter_56\">Модуль новостей</a></li>\n                <li><a href=\"#chapter_57\">Игроки</a></li>\n                <li><a href=\"#chapter_58\">Альянсы</a></li>\n                <li><a href=\"#chapter_59\">[#] Технологии и Наемники Альянсов</a></li>\n                <li><a href=\"#chapter_60\">Башинг</a></li>\n                <li><a href=\"#chapter_61\">АнтиРМФ</a></li>\n                <li><a href=\"#chapter_62\">НоваПедия</a></li>\n                <li><a href=\"#chapter_63\">Заметки</a></li>\n                <li><a href=\"#chapter_64\">Друзья</a></li>\n                <li><a href=\"#chapter_65\">Закладки</a></li>\n                <li><a href=\"#chapter_66\">Сообщения</a></li>\n                <li><a href=\"#chapter_67\">Чат</a></li>\n                <li><a href=\"#chapter_68\">Модуль \"Продвинутый чат\": chat_advanced</a></li>\n                <li><a href=\"#chapter_69\">День Рождения</a></li>\n                <li><a href=\"#chapter_70\">Интерфейс</a></li>\n                <li><a href=\"#chapter_71\">Логин и регистрация</a></li>\n                <li><a href=\"#chapter_72\">Навбар и планетбар</a></li>\n                <li><a href=\"#chapter_73\">Вселенная</a></li>\n                <li><a href=\"#chapter_74\">Переработка поля обломков</a></li>\n                <li><a href=\"#chapter_75\">Обзор планеты</a></li>\n                <li><a href=\"#chapter_76\">Фаланга</a></li>\n                <li><a href=\"#chapter_77\">Шпионаж в Обзоре планеты и Фаланге</a></li>\n                <li><a href=\"#chapter_78\">Межгалактические Врата</a></li>\n                <li><a href=\"#chapter_79\">Строительство зданий</a></li>\n                <li><a href=\"#chapter_80\">Верфь</a></li>\n                <li><a href=\"#chapter_81\">Флоты</a></li>\n                <li><a href=\"#chapter_82\">Меню</a></li>\n                <li><a href=\"#chapter_83\">Поиск</a></li>\n                <li><a href=\"#chapter_84\">Империя</a></li>\n                <li><a href=\"#chapter_85\">Император</a></li>\n                <li><a href=\"#chapter_86\">Мировые константы</a></li>\n                <li><a href=\"#chapter_87\">Список забаненных</a></li>\n                <li><a href=\"#chapter_88\">Прочее</a></li>\n                <li><a href=\"#chapter_89\">Администрирование</a></li>\n                <li><a href=\"#chapter_90\">[#] Модуль: Редактирование характеристик планеты</a></li>\n                <li><a href=\"#chapter_91\">Права доступа к администраторской части</a></li>\n                <li><a href=\"#chapter_92\">Воплощение</a></li>\n                <li><a href=\"#chapter_93\">Список игроков</a></li>\n                <li><a href=\"#chapter_94\">Google AdSense</a></li>\n                <li><a href=\"#chapter_95\">Локализация</a></li>\n                <li><a href=\"#chapter_96\">Уничтожение планет с компенсацией</a></li>\n                <li><a href=\"#chapter_97\">Редактирование планет и лун</a></li>\n                <li><a href=\"#chapter_98\">Прочее</a></li>\n                <li><a href=\"#chapter_99\">Персонализация</a></li>\n                <li><a href=\"#chapter_100\">Модули</a></li>\n                <li><a href=\"#chapter_101\">Серверные значения по умолчанию</a></li>\n                <li><a href=\"#chapter_102\">Скины</a></li>\n                <li><a href=\"#chapter_103\">Внутренние изменения</a></li>\n                <li><a href=\"#chapter_104\">Большие числа</a></li>\n                <li><a href=\"#chapter_105\">Статистика</a></li>\n                <li><a href=\"#chapter_106\">Баннерилка</a></li>\n                <li><a href=\"#chapter_107\">Минификатор темплейтов</a></li>\n                <li><a href=\"#chapter_108\">Локальное (клиентское) и серверное время</a></li>\n                <li><a href=\"#chapter_109\">Система темплейтов</a></li>\n                <li><a href=\"#chapter_110\">Система отпусков</a></li>\n                <li><a href=\"#chapter_111\">Прочее</a></li>\n                <li><a href=\"#chapter_112\">База данных</a></li>\n                <li><a href=\"#chapter_113\">Модульная система</a></li>\n                <li><a href=\"#chapter_114\">Динамическое перекрытие функций</a></li>\n                <li><a href=\"#chapter_115\">Сервер обновлений</a></li>\n                <li><a href=\"#chapter_116\">Технические детали</a></li>\n                <li><a href=\"#chapter_117\">Проверка версии</a></li>\n                <li><a href=\"#chapter_118\">Регистрация сервера</a></li>\n                <li><a href=\"#chapter_119\">Copyright</a></li>\n                <li><a href=\"#chapter_120\">Update</a></li>\n            </ul>\n            <ul class=\"list\">\n                <li class=\"chapter\" id=\"chapter_1\">\n                    <h2 class=\"heading\">Проект \"СуперНова\"</h2>\n                    <div class=\"desc\">\n                        <p>\"СуперНова\"  (далее  -  СН)  -  многопользовательская  браузерная  космическая\n                            стратегия, клон oGame. СН базируется на  XNova  RageRepack  v.226  (далее  RR).\n                            Данный файл содержит исключительно отличия СН от RR\n                        </p>\n                    </div>\n                </li>\n                <li class=\"chapter\" id=\"chapter_2\">\n                    <h2 class=\"heading\">Актуальность</h2>\n                    <div class=\"desc\">\n                        <p>Файл изменен 2013-10-13 22:27\n                            Информация актуальна для Project \"SuperNova.WS\" Release 37 37c0\n                        </p>\n                    </div>\n                </li>\n                <li class=\"chapter\" id=\"chapter_3\">\n                    <h2 class=\"heading\">Disclaimer aka Отмазка</h2>\n                    <div class=\"desc\">\n                        <p>ВНИМАНИЕ! Проект находится в стадии  альфа-версии!  В  настоящее  время  он  не\n                            предназначен для  production-использования!  Код  предоставляется  \"as-is\".  Вы\n                            используете его на свой  страх  и  риск!  Автор  не  несет  ответственности  за\n                            материальный,  моральный,  кармический,  душевный   и   любой   другой   ущерб,\n                            причиненный вам от использования, неиспользования, самим  фактом  существования\n                            этого кода или любым другим способом.\n                        </p>\n                        <p>ВНИМАНИЕ! Хотя СН является клоном оГейм, она НЕ ЯВЛЯЕТСЯ ОФФЛАЙК!  Т.е.  многие\n                            аспекты игры отличаются как  от  официального  оГейма,  так  и  от  RR.  Движок\n                            изменен, что бы соответствовать моему пониманию об интересной игре.  Принимайте\n                            это во внимание, когда решаете - устанавливать этот движок себе или нет.\n                        </p>\n                        <p>ВНИМАНИЕ! Статус проекта \"СуперНова\" - альфа-версия. Практически это означает,\n                            что очередной апдейт может полностью изменить отдельный аспект игры.\n                            Примерный план развития движка содержится в файле /docs/changelog.xls\n                            Код распространяется под лицензией GNU GENERAL PUBLIC LICENSE Version  2,  June\n                            1991. Сама лицензия находится в файле /docs/license.txt дистрибутива.\n                        </p>\n                        <p>Доолнение к лицензии: движок является полностью бесплатным  до  тех  пор,  пока\n                            сохраняются мои копирайты. Так же вы не имеете права  продавать  сам  движок  и\n                            любые продукты на его основе (однако имеете право взимать  плату  за  доступ  к\n                            игре и/или продавать внутриигровые ресурсы).\n                        </p>\n                    </div>\n                </li>\n                <li class=\"chapter\" id=\"chapter_4\">\n                    <h2 class=\"heading\">Предуведомления о необходимости наличия квалификации</h2>\n                    <div class=\"desc\">\n                        <p> Данные инструкции предполагают умение самостоятельно настроить или использовать\n                            сторонний  веб-хостинг,  знакомство  с  MySQL  и  PHP,  доступ  к  инструментам\n                            управленя MySQL и  хостингом.  Если  вы  не  обладаете  опытом  самостоятельной\n                            настройки сайтов - этот дистрибутив вам не подойдет\n                        </p>\n                        <p>ВНИМАНИЕ! Я не могу протестировать все возможные комбинации и  версии  MySQL  &amp;\n                            PHP &amp; XCache &amp; веб-серверов! Это означает,  что  при  некоторых  комбинациях  и\n                            настройках среды движок может  не  работать.  Именно  для  этого  нужны  навыки\n                            настройки и конфигурации веб-серверов.\n                        </p>\n                    </div>\n                </li>\n                <li class=\"chapter\" id=\"chapter_5\">\n                    <h2 class=\"heading\">Помощь проекту</h2>\n                    <div class=\"desc\">\n                        <p>Вы можете помочь проекту, отправив WebMoney на кошельки:\n                            WMZ (WM-USD) Z409323360409\n                            WMR (WM-RUB) R961266352219\n                            WMU (WM-UAH) U726314912308\n                        </p>\n                        <p> Если вы пользуетесь ВебМанями для покупки различных электронных товаров, то  вы\n                            можете одновременно\n                        </p>\n                        <p> а) купить привычный электронный товар по хорошей цене\n                            б) помочь проекту.\n                            <a href=\"http://gorlum.plati.ru\">http://gorlum.plati.ru</a> - пополнения сотовых, Skype, WoW - игра и  ТК  и  прочие\n                            электрические товары за WM. Каждая покупка, сделанная с этого  линка,  принесет\n                            мне небольшие комиссионные.\n                        </p>\n                        <p>Помните! Ничто так не укрепляет веру в нужность работы, как пожертвование!</p>\n                    </div>\n                </li>\n                <li class=\"chapter\" id=\"chapter_6\">\n                    <h2 class=\"heading\">Установка</h2>\n                    <div class=\"desc\">\n                        <p>ВНИМАНИЕ!!! PHP 5.3.1 содержит  баг,  который  делает  невозможной  полноценную\n                            работу СН начиная с v33a12! Обновите PHP, или сделайте откат  до  более  ранней\n                            версии PHP, или используйте предыдущую версию СН.\n                            Описание бага: <a href=\"https://bugs.php.net/bug.php?id=50394\">https://bugs.php.net/bug.php?id=50394</a>\n                            Подробные инструкции по установке содержатся в файлах:\n                            /docs/install.md\n                            /docs/install-en.md\n                        </p>\n                    </div>\n                </li>\n                <li class=\"chapter\" id=\"chapter_7\">\n                    <h2 class=\"heading\">Благодарности</h2>\n                    <div class=\"desc\">\n                        <div class=\"sub\">\n                            <ul>\n                                <li>\n                                    <h3 class=\"title\">Активные Помощники (в алфавитном порядке)</h3>\n                                    <ul>\n                                        <li>Ivash</li>\n                                        <li>Mahomed</li>\n                                        <li>PIR</li>\n                                    </ul>\n                                    <h4>Спонсоры</h4>\n                                    <ul>\n                                        <li>Мухит Гусманов</li>\n                                        <li>andre777_86</li>\n                                        <li>Tourist</li>\n                                        <li>jilior</li>\n                                        <li>websasha</li>\n                                        <li>igorrnc</li>\n                                        <li>nt</li>\n                                    </ul>\n                                </li>\n                            </ul>\n                        </div>\n                    </div>\n                </li>\n                <li class=\"chapter\" id=\"chapter_8\">\n                    <h2 class=\"heading\">Проект \"СуперНова\" в интернете</h2>\n                    <div class=\"desc\">\n                        Основной сайт проекта\n                        http://supernova.ws\n                        <div class=\"sub\">\n                            <ul>\n                                <li>\n                                    <h4>GitHub</h4>\n                                    GitHub используется как основное хранилище кода в интернете. На нем вы  найдете\n                                    самую последнюю версию СуперНовы\n                                    \"СуперНова\" на GitHub:\n                                    http://github.com/supernova-ws/SuperNova\n                                    GIT-репозиторий:\n                                    git://github.com/supernova-ws/SuperNova.git\n                                    Он же по HTTP:\n                                    https://github.com/supernova-ws/SuperNova#\n                                    Архивы инсталляции всех релизов через HTTPS:\n                                    https://github.com/supernova-ws/SuperNova/zipball/master\n                                    (кнопка \"download .zip\" скачает самый свежий релиз)\n                                    В репозитории есть две ветки\n                                    Ветка \"master\" содержит последний стабильный релиз движка плюс самые актуальные\n                                    багфиксы.  Перед  выкладыванием  в  эту  ветку  движок  неделями   и   месяцами\n                                    тестируется на нескольких серверах\n                                    А  вот  ветка  \"trunk\"  -  для  самых  смелых,  храбрых  и  попросту  говоря  -\n                                    безрассудных. Это - копия моей рабочей  ветки.  Она  содержит  то,  над  чем  я\n                                    работаю в данный момент. В общем - я даже не гарантирую,  что  чекаут  из  этой\n                                    ветки  вообще запустится. Используйте её на свой страх и риск. Я предупредил.\n                                    Загрузка исходников из ветки trunk одним архивом через HTTPS:\n                                    https://github.com/supernova-ws/SuperNova/zipball/trunk\n                                </li>\n                                <li>\n                                    <h4>SourceForge</h4>\n                                    Так же есть репозиторий на SourceForge:\n                                    git://supernova-ws.git.sourceforge.net/gitroot/supernova-ws/supernova-ws\n                                    Архив инсталляции для загрузки - для тех, кто не осилил GIT\n                                    http://sourceforge.net/projects/supernova-ws/files/\n                                </li>\n                                <li>\n                                    <h4>Поддержка</h4>\n                                    Форум поддержки проекта:\n                                    http://forum.supernova.ws/viewforum.php?f=73\n                                </li>\n                                <li>\n                                    <h4>\"Живые\" Вселенные</h4>\n                                    СуперНова (х1):\n                                    http://supernova.supernova.ws\n                                    оГейм (x2):\n                                    http://alpha.supernova.ws\n                                    Бета (x50):\n                                    http://beta.supernova.ws\n                                </li>\n                            </ul>\n                        </div>\n                    </div>\n                </li>\n                <li class=\"chapter\" id=\"chapter_9\">\n                    <h2 class=\"heading\">Документация</h2>\n                    <div class=\"desc\">\n                        Вся документация и сопроводительные файлы находятся в каталоге \"docs\"  в  корне\n                        движка\n                        <p>Лицензия (на английском):\n                            /docs/license.txt\n                        </p>\n                        <p>Документация на проект (этот файл, на русском):\n                            /docs/readme.txt\n                        </p>\n                        <p>История изменения для пользователей (на русском, начиная с 25-го релиза):\n                            /docs/changelog.txt\n                            /docs/changelog.html\n                        </p>\n                        <p>Полная история изменений для администраторов и разработчиков (на русском):\n                            /docs/changelog_dev.txt\n                        </p>\n                        <p>Инструкции по установке (на русском):\n                            /docs/install.md\n                        </p>\n                        <p>Инструкции по установке (английский, google-translated):\n                            /docs/install-en.md\n                        </p>\n                        <p>Документация для администраторов и разработчиков (на русском):\n                            /docs/html/developer.html\n                        </p>\n                        <p>Планы развития (на русском):\n                            /docs/changelog.xls\n                        </p>\n                        <p>Coding Gudelines для разработчиков (на английском):\n                            /docs/html/coding-guidelines.html\n                        </p>\n                        <p>Беллитризированная внутриигровая предыстория (на русском):\n                            /docs/SuperNova - All you didn't want to know but forced to.docx\n                        </p>\n                    </div>\n                </li>\n                <li class=\"chapter\" id=\"chapter_10\">\n                    <h2 class=\"heading\">Терминология</h2>\n                    <div class=\"desc\">\n                        <ul>\n                            <li> \"Сервер\" - веб-сервер с установленным PHP, MySQL и xCache</li>\n                            <li>\"СН\" или \"движок\" - исполнимый код Проекта \"СуперНова\"</li>\n                            <li>\"Вселенная\"  -  экземпляр  движка,  установленный  на  сервер  и  имеющий  свою\n                                собственную отдельну БД. Один сервер может поддерживать несколько Вселенных\n                            </li>\n                            <li>\"Топ\" - игрок, входящий в первые 5% игроков по статистике</li>\n                        </ul>\n                    </div>\n                </li>\n                <li class=\"chapter\" id=\"chapter_11\">\n                    <h2 class=\"heading\">Тёмная Материя</h2>\n                    <div class=\"desc\">\n                        <p>Тёмная Материя (ТМ)  -  особый  внутриигровой  ресурс,  почти  не  связанный  с\n                            остальной  экономикой.  ТМ  позволяет  получить  доступ  к   уникальным   и/или\n                            ограниченным  юнитам,  возможностям  или  технологиям,   которые   сложно   или\n                            невозможно получить другим образом\n                        </p>\n                        <p>ТМ служит нескольким целям:  ускорителем  в  развитии  начинающим  игрокам  (на\n                            ранних этапах игры ТМ достается легче, а её использование дает больший  эффект,\n                            чем на поздних), поощрением постоянным игрокам (чем дольше  игрок  играет,  тем\n                            больше ТМ у него будет), дополнительным фактором случайности (на поздних этапах\n                            развития  ТМ  можно  получить  только  рискуя),  способом  рекламы  (встроенная\n                            партнерская  программа  поощраяет  игроков  как   можно   шире   распространять\n                            информацию о сервере  и  вознаграждет  их  за  эффектиную  рекламу)  и  методом\n                            монетизации игры (владельцам серверов рекомендуется продавать именно ТМ,  а  не\n                            другие ресурсы -  за  счет  этого  достигается  относительное  равенство  между\n                            донаторами и обычными игроками, минимизируется вмешательство владельца в игру и\n                            упрощается менджмент платежей)\n                        </p>\n                        <p>ТМ смасштабирована в отношении 1 к 1.000. Т.е. весь приход ТМ и все цены  в  ТМ\n                            увеличены в 1.000 раз по отношению к RR\n                        </p>\n                        <p>В настоящий момент  существует  пять  внутриигровых  источников  получения  ТМ:\n                            постройка зданий, исследования, рейды на врагов, экспедиции и квесты. По логике\n                            игры ТМ считается редким и ценным  ресурсом,  каждая  единица  которого  должна\n                            добываться огромным трудом и вкладываться с умом и  перспективой.  Кроме  того,\n                            множество вопросов вызывает ограничение в 150 уровней по экономике. Изменения в\n                            ролевой системе призваны устранить оба этих недостатка\n                        </p>\n                    </div>\n                </li>\n                <li class=\"chapter\" id=\"chapter_12\">\n                    <h2 class=\"heading\">Лог движения ТМ</h2>\n                    <div class=\"desc\">\n                        <p>Все изменения в количестве ТМ заносятся в специальную таблицу \"log_dark_matter\"\n                            для  предотвращения   возможных   злоупотрблений   как   со   стороны   игроков\n                            (использование багов или дырок в движке),  так  и  со  стороны  членов  команды\n                            сервера. По этой причине таблица не доступна из админской части игры\n                        </p>\n                    </div>\n                </li>\n                <li class=\"chapter\" id=\"chapter_13\">\n                    <h2 class=\"heading\">Покупка ТМ за внеигровые ресурсы</h2>\n                    <div class=\"desc\">\n                        <p>Движок СН содержит подсистему для  покупки  ТМ  игроками  за  реальные  деньги.\n                            Однако для её функционирования нужен хотя бы  один  модуль  платежной  системы,\n                            которые не входят в дистрибутив.  За  получением  этих  модулей  обращайтесь  к\n                            автору движка\n                        </p>\n                        <p> В настоящее  время  поддержка  платежных  модулей:  таблицу  платежей;  базовые\n                            настройки подсистемы; строки локализации  и  константы;  интерфейс  покупки  ТМ\n                            (требуется хотя бы один модуль платежной системы);  поддержку  методов  'LINK',\n                            'GET' и 'POST' в системах платежа;  система  бонусов  при  одноразовой  покупки\n                            большого количества ТМ\n                        </p>\n                    </div>\n                </li>\n                <li class=\"chapter\" id=\"chapter_14\">\n                    <h2 class=\"heading\">Страница платежа \"Тёмная Материя\"</h2>\n                    <div class=\"desc\">\n                        <p>Понижена в 2,5 раза цена ТМ. Теперь за 1 гривну можно купить 2500 ТМ\n                            Размер лота (шага покупки) установлен в 2500 ТМ\n                            Изменена система бонусов за оптовые покупки ТМ:\n                        </p>\n                        от 50.000 ТМ - бонус 2% к количеству ТМ\n                        от 100.000 ТМ - бонус 4% к количеству ТМ\n                        от 200.000 ТМ - бонус 7% к количеству ТМ\n                        от 250.000 ТМ - бонус 11% к количеству ТМ\n                        от 375.000 ТМ - бонус 15% к количеству ТМ\n                        от 500.000 ТМ - бонус 22% к количеству ТМ\n                        от 750.000 ТМ - бонус 33% к количеству ТМ\n                        от 1.000.000 ТМ - бонус 44% к количеству ТМ\n                        от 1.250.000 ТМ - бонус 55% к количеству ТМ\n                        <p>Список  доступных  цен  и  список  скидок  строится  теперь  по  данным  модуля\n                            sn_payment\n                        </p>\n                        <p>Добавлена поддержка модулей с более чем одним количеством шагов при покупке\n                            Добавлена поддержка мультивалютности\n                            Добавлена поддержка SUCCESS_URL в платежных системах\n                            Добавлена индикация внутренних курсов системы\n                            Теперь большую часть информационных элементов на странице можно свернуть\n                        </p>\n                    </div>\n                </li>\n                <li class=\"chapter\" id=\"chapter_15\">\n                    <h2 class=\"heading\">Модуль платежной системы XSolla: payment_xsolla_currency</h2>\n                    <div class=\"desc\">\n                        <p>Реализован протокол XSolla \"Виртуальная валюта\" (без отката платежей)\n                            Поддержка плагина XSolla PayStation\n                        </p>\n                    </div>\n                </li>\n                <li class=\"chapter\" id=\"chapter_16\">\n                    <h2 class=\"heading\">Модуль платежной системы WebMoney: payment_webmoney</h2>\n                    <div class=\"desc\">\n                        <p>Реализован приема платежей на кошельки WebMoney\n                            Поддержка мультивалютности\n                            Поддержка SUCCESS_URL\n                        </p>\n                    </div>\n                </li>\n                <li class=\"chapter\" id=\"chapter_17\">\n                    <h2 class=\"heading\">Модуль платежной системы RoboKass: payment_robokassa</h2>\n                    <div class=\"desc\">\n                        <p>Реализован прием платежей через агрегатора RoboKassa</p>\n                    </div>\n                </li>\n                <li class=\"chapter\" id=\"chapter_18\">\n                    <h2 class=\"heading\">Партнерская программа</h2>\n                    <div class=\"desc\">\n                        <p>Написан  с  нуля  модуль  Партнерской  программы.   Каждому   игроку   выдается\n                            персонализированная ссылка, персонализированный  баннер  и  персонализированный\n                            юзербар, а так же коды для  размещения  всего  этого  добра  на  форумах  и/или\n                            домашних страничках. Если на сервер по ссылке  приходит  новый  пользователь  и\n                            регестрируется  на  сайте,  приведший  его  игрок  получает  10%  от  всех  ТМ,\n                            заработанных   новым   пользователем   (включая   дополнительные   начисления).\n                            Количество бонуса партнеру определяется переменной \"rpg_bonus_divisor\", которая\n                            по умолчанию равна 10 (т.е. 10% - это 1/10 от ТМ реферрала)\n                        </p>\n                        <p>Партнерка имеет ограничение  по  минимальному  количеству  ТМ,  после  которого\n                            начинается начисление бонусов  реферралу  -  переменная  \"rpg_bonus_minimum\"\n                            Настройки партнерской программы осуществляются в таблице \"config\"\n                        </p>\n                    </div>\n                </li>\n                <li class=\"chapter\" id=\"chapter_19\">\n                    <h2 class=\"heading\">Развитие: опыт, уровни и ТМ</h2>\n                    <div class=\"desc\">\n                        <p>За  совершение  определенных  действий  в  игре  начисляется  опыт.  Накопление\n                            определенного количество опыта отмечается увеличением уровня и  вознаграждением\n                            в  виде  ТМ.  Опыт,  необходимый  для  получения   нового   уровня   растет   в\n                            геометрической прогрессии - т.е. чем выше уровень,  тем  больше  нужно  набрать\n                            опыта для перехода на следующий уровень\n                        </p>\n                        <p>Опыт можно получить за  постройку  и  разрушение  зданий  (строительный  опыт),\n                            исследование новых технологий (исследовательский опыт)  и  сражение  с  другими\n                            игроками (рейдерский опыт). Все три вида опыта независимы друг от  друга,  дают\n                            разные типы уровней и имеют разные шкалы развития\n                        </p>\n                    </div>\n                </li>\n                <li class=\"chapter\" id=\"chapter_20\">\n                    <h2 class=\"heading\">Строительство и исследование</h2>\n                    <div class=\"desc\">\n                        <p>Строительный и исследовательский опыт  начисляется  из  расчета  один  балл  на\n                            каждую вложенную полную тысячу ресурсов в пересчете на металл по внутриигровому\n                            курсу  в  соответственно  постройку  или  разрушение   зданий   и   исседование\n                            технологий. В отличии от xNova СН не имеет ограничений по уровням строительства\n                            и исследования\n                        </p>\n                        <p>Шкала развития при строительстве и исследованиях имеет следующий вид:</p>\n                        1    50      50      50\n                        2   100      52     102\n                        3   150      53     155\n                        4   200      55     209\n                        5   250      56     265\n                        6   300      58     323\n                        7   350      60     383\n                        8   400      61     445\n                        9   450      63     508\n                        10   500      65     573\n                        20 1.000      88   1.344\n                        30 1.500     118   2.379\n                        40 2.000     158   3.770\n                        50 2.500     213   5.640\n                        60 3.000     286   8.153\n                        70 3.500     384  11.530\n                        80 4.000     517  16.068\n                        90 4.500     694  22.167\n                        100 5.000     933  30.364\n                        110 5.500   1.254  41.380\n                        120 6.000   1.685  56.185\n                        130 6.500   2.264  76.081\n                        140 7.000   3.043 102.820\n                        150 7.500   4.090 138.754\n                        160  НЕТ    5.497 187.048\n                        170  НЕТ    7.387 251.950\n                        180  НЕТ    9.927 339.172\n                        190  НЕТ   13.342 456.392\n                        200  НЕТ   17.930 613.926\n                        ИТД\n                        <p>Первая колонка - уровень\n                            Вторая колонка - старое общее  количество  опыта,  необходимое  для  достижения\n                            уровня\n                        </p>\n                        <p> Третья колонка - новое количество опыта, необходимое для перехода на  следующий\n                            уровень (геометрическая прогрессия с первым членом 50 и коэффициентом 1,03)\n                            Четвертая колонка - новое общее количество опыта,  необходимое  для  достижения\n                            уровня\n                        </p>\n                    </div>\n                </li>\n                <li class=\"chapter\" id=\"chapter_21\">\n                    <h2 class=\"heading\">Рейдерство</h2>\n                    <div class=\"desc\">\n                        <p> За каждую выигранное сражение начисляется один балл рейдерского опыта\n                            Шкала развития рейдера имеет следующий вид:\n                        </p>\n                        1    10    10      10\n                        2    20    10      20\n                        3    30    11      31\n                        4    40    11      42\n                        5    50    11      53\n                        6    60    12      65\n                        7    70    12      77\n                        8    80    12      89\n                        9    90    13     102\n                        10   100    13     115\n                        20   200    18     269\n                        30   300    24     476\n                        40   400    32     754\n                        50   500    43   1.128\n                        60   600    57   1.631\n                        70   700    77   2.306\n                        80   800   103   3.214\n                        90   900   139   4.433\n                        100 1.000   187   6.073\n                        110 1.100   251   8.276\n                        120 1.200   337  11.237\n                        130 1.300   453  15.216\n                        140 1.400   609  20.564\n                        150 1.500   818  27.751\n                        160 1.600 1.099  37.410\n                        170 1.700 1.477  50.390\n                        180 1.800 1.985  67.834\n                        190 1.900 2.668  91.278\n                        200 2.000 3.586 122.785\n                        ИТД\n                        <p>Первая колонка - уровень\n                            Вторая колонка - старое общее  количество  опыта,  необходимое  для  достижения\n                            уровня\n                        </p>\n                        <p> Третья колонка - новое количество опыта, необходимое для перехода на  следующий\n                            уровень (геометрическая прогрессия с первым членом 10 и коэффициентом 1,03)\n                            Четвертая колонка - новое общее количество опыта,  необходимое  для  достижения\n                            уровня\n                        </p>\n                    </div>\n                </li>\n                <li class=\"chapter\" id=\"chapter_22\">\n                    <h2 class=\"heading\">Модуль \"Премиумный аккаунт\": player_premium</h2>\n                    <div class=\"desc\">\n                        <p>Премиумный аккаунт - особый вид аккаунта, дающий  бонус  игрокам,  которые  его\n                            приобрели\n                        </p>\n                        <p>Премиумный аккаунт покупается игроком за ТМ, поэтому приобрести его может любой\n                            игрок, вне зависимости от того, вкладывал ли он в игру реальные деньги или нет\n                            Добавлена индикация уровня премиума в меню\n                        </p>\n                        <p>Добавлена индикация остатка времени Премиума в пункт меню в виде  прогресс-бара\n                            с цветовым кодированием:\n                        </p>\n                        <p>Зеленый - осталось не менее 50% времени пермиума</p>\n                        <p>Желтый - осталось меньше 50%, но не менее 25%</p>\n                        <p> Оранжевый - осталось меньше 25%, но не менее 10%</p>\n                        <p>Красный - осталось меньше 10%</p>\n                        <p>Цвет фона - нет Премиума</p>\n                    </div>\n                </li>\n                <li class=\"chapter\" id=\"chapter_23\">\n                    <h2 class=\"heading\">Период действия премиума</h2>\n                    <div class=\"desc\">\n                        <p>Премиумный  аккаунт  покупается  на  определенный  срок.  По   умолчанию   срок\n                            приобретения премиума - 30 дней. Именно в расчете на этот  срок  дается  расчет\n                            цены ниже. Однако премиум может быть куплен на  больший  или  меньший  срок.  В\n                            частности, полный список сроков покупки составляет:  1  неделя,  2  недели,  30\n                            дней, 60 дней и 90 дней. При приобретении премиума на  срок,  больший  30  дней\n                            предусмотрены скидки, меньше 30 дней - пенальти на  цену  покупки  относительно\n                            цены премиума в пересчете  на  30  дней.  Список  коэфициентов  изменения  цены\n                            выглядит следующим образом: 1,5 для 1й недели, 1,2 - для 2х недель, 0,9 для  60\n                            дней и 0,8 для 90 дней.\n                        </p>\n                    </div>\n                </li>\n                <li class=\"chapter\" id=\"chapter_24\">\n                    <h2 class=\"heading\">Уровень премиума</h2>\n                    <div class=\"desc\">\n                        Премиумные аккаунты бывают различных уровней. По умолчанию доступно  5  уровней\n                        премиума. БС составляет 25.000 ТМ, а фактор - 3. Это дает нам следующую линейку\n                        цен: 1й уровень - 25.000 ТМ, 2й уровень - 100.000 ТМ (25.000  с  0го  на  1й  +\n                        75.000 с 1го на 2й), 3й - 325.000 ТМ, 4й - 1.000.000 ТМ, 5й - 3.025.000 ТМ\n                        Пример: премиум 3-го уровня на 1 неделю будет стоить\n                        325.000 * (7 / 30) * 1,5 = 113.750 ТМ\n                        где\n                        325.000 - базовая цена ТМ премиума 3 уровня на 30 дней\n                        (7 / 30) - коэфициент пересчета базовый цены на период покупки (7 дней)\n                    </div>\n                </li>\n                <li class=\"chapter\" id=\"chapter_25\">\n                    <h2 class=\"heading\">Преимущества премиума</h2>\n                    <div class=\"desc\">\n                        <p>Ник обладателя премиумного аккаунта выделяется  в  чате  отдельным  стилем  (по\n                            умолчанию - желтым цветом. Настраивается в CSS - стиль \".nick_premium\")\n                        </p>\n                        <p> Премиумный  аккаунт  дает  бонус,  эквивалентный  своему  уровню,   к   уровням\n                            технологий и Наемников (не Губернаторов!) Империи, а так же к уровням  следущих\n                            зданий на планетах: ко всем шахтам и складам ресурсов, ко всем электростанциям,\n                            к Фабрике роботов, к Верфи, к Нанофабрике, к Лаборатории и к Нанолаборатории\n                            Премиум чрезвычайно эффективен - и чем выше  уровень  премиума,  тем  более  он\n                            эффективен. Именно поэтому на высокие  уровни  выставлены  такие  большие  цены\n                            (фактически, их можно назвать \"заградительными\" для премиума выше третьего).\n                            Например, максимальный (5й уровень) премиума дает:\n                        </p>\n                        <ul>\n                            <li>1. +5 уровней к шахтам и складам -  это  почти  удвоение  добычи  и  увеличение\n                                складов почти в 8 раз\n                            </li>\n                            <li> 2. +5 уровней к технологиям и Наемникам - это по факту  +100%  ко  всем  боевым\n                                характеристикам\n                            </li>\n                            <li>3. +5 уровней к технологиям и Наемникам - это от 50 до 150%% прирост к скорости\n                                полета - в зависимости  от  типа  двигателя,  да  еще  +25%  к  скорости  от\n                                Навигатора\n                            </li>\n                            <li>4. +5  к  производственным   зданиям  -  это  ускорение  всех  строительств   и\n                                исследований  более  чем  в  32  раза  (на  планетах   с   нанофабриками   и\n                                нанолабораториями соответственно)\n                            </li>\n                            <li>5. И еще множество мелких плюшек в виде увеличения количества слотов под  флоты\n                                и экспедиции, увеличение количества колоний  (но  не  больше,  чем  максимум\n                                сервера), открытие новых юнитов и т.д.\n                            </li>\n                        </ul>\n                    </div>\n                </li>\n                <li class=\"chapter\" id=\"chapter_26\">\n                    <h2 class=\"heading\">Покупка секторов на планете</h2>\n                    <div class=\"desc\">\n                        <p> Теперь можно за ТМ докупать дополнительные сектора на планете - один сектор  за\n                            раз, максимальное количество секторов не ограничено\n                            Cектор можно купить в нескольких местах:  в  \"Обзоре  планеты\",  в  \"Управлении\n                            планетой\" и на экране строительства зданий\n                        </p>\n                        Стоимость  сектора  для  планеты  -  геометрическая  прогрессия  с  количеством\n                        секторов в качестве номера члена, БС = 1000 и Ф = 1.01\n                        Стоимость покупи 1 сектора на планете  составляет:  для  планеты  размером  100\n                        секторов - 2.678 ТМ, 150 секторов - 4.404 ТМ,  163  сектора  -  5.013  ТМ,  200\n                        секторов - 7.244 ТМ, 250 секторов - 11.913 ТМ, 300 секторов -  19.493  ТМ,  330\n                        секторов - 26.508 ТМ\n                    </div>\n                </li>\n                <li class=\"chapter\" id=\"chapter_27\">\n                    <h2 class=\"heading\">Телепортация планеты</h2>\n                    <div class=\"desc\">\n                        <p>Теперь  можно  телепортировать  планеты  в  пределах  Вселенной.   Телепортация\n                            доступна на странице управления планетой\n                        </p>\n                        <p>Телепортация может производится только на свободное место -  там,  где  нет  ни\n                            планет, ни лун, ни обломков (включая уничтоженные объекты)\n                            Телепортация  перемещает  в  новые  координаты   планету   вместе   с   флотом,\n                            находящимся на орбите планеты\n                        </p>\n                        <p> Если у планеты есть луна - она так же перемещается в новые координаты вместе  с\n                            флотом\n                        </p>\n                        <p>Телепортация невозможна, если в окрестностях планеты есть  какая-то  активность\n                            флотов (т.е. есть флоты, имеющие в качестве точки  отправления  или  назначения\n                            саму планету, луну или поле обломков)\n                        </p>\n                        <p>После  телепортации  необходимо  выждать  некоторое   время   перед   следующей\n                            телепортацией  -  нарушенная  метрика  пространства   вокруг   планеты   должна\n                            нормализироваться\n                        </p>\n                        <p>Стоимость телепортации и таймаут перед следующим  прыжком  задаются  в  таблице\n                            'config' соответственно  переменными  'planet_teleport_cost'  (по  умолчанию  -\n                            50.000 ТМ) и 'planet_teleport_timeout' (по умолчанию - 1 сутки)\n                        </p>\n                    </div>\n                </li>\n                <li class=\"chapter\" id=\"chapter_28\">\n                    <h2 class=\"heading\">Перенос столицы</h2>\n                    <div class=\"desc\">\n                        <p>Теперь можно перенести столицу (основной мир) на любую планету. Перенос столицы\n                            осуществляется со страницы управления планетой\n                        </p>\n                        <p>Стоимость переноса столицы по умолчанию составляет 25.000 ТМ.  Она  задается  в\n                            таблице 'config' переменной 'planet_capital_cost'\n                        </p>\n                    </div>\n                </li>\n                <li class=\"chapter\" id=\"chapter_29\">\n                    <h2 class=\"heading\">Плотность планеты</h2>\n                    <div class=\"desc\">\n                        <p>Добавлен новый параметр планеты - плотность. Он  определяет  химический  состав\n                            геосферы планеты и влияет на добычу ресурсов на ней\n                        </p>\n                        <p>Плотность планеты лежит в диапазоне от  850  до  9250  кг/м3.  Плотность  новых\n                            планет распределена случайным образом по нормальному распределению\n                            Существует 7 классов плотности - с уникальным набором коэфициентов  добычи  для\n                            каждого класса:\n                        </p>\n                        Ледяные планеты (&lt;2000 кг/м3) - встречаются очень редко: очень низкая  добыча\n                        металла, очень низкая добыча кристаллов, очень высокая добыча дейтерия\n                        Силикатные планеты (2000=3250 кг/м3) - встречаются редко: очень низкая добыча\n                        металла, очень высокая добыча кристаллов и еще хорошая добыча дейтерия\n                        Каменные планеты  (3250-4500  кг/м3)  -  встречаются  часто:  хорошая  добыча\n                        металлов, высокая добыча кристаллов и низкая добыча дейтерия\n                        Стандарнтые планеты (4500-5750 кг/м3)  -  встречаются  очень  часто:  хорошая\n                        добыча металлов, хорошая добыча кристаллов и хорошая добыча дейтерия\n                        Железнорудные планеты (5750-7000 кг/м3) - встречаются  часто:  очень  хорошая\n                        добыча металлов, низкая добыча кристаллов и низкая добыча дейтерия\n                        Металлические планеты (5750-7000 кг/м3) - встречаются редко: отличная  добыча\n                        металлов, низкая добыча кристаллов и низкая добыча дейтерия\n                        Тяжелометаллические  планеты  (&gt;7000  кг/м3)  -  встречаются   очень   редко:\n                        великолепная добыча металлов, очень низкая добыча кристаллов и  очень  низкая\n                        добыча дейтерия\n                        Стартовая планета имеет плотность  5500  кг/м3  и  принадлежит  к  4-му  классу\n                        плотности. Все луны имеют плотность 2500 кг/м3 и  принадлежат  ко  2-му  классу\n                        плотности\n                        Тип  ядра  планеты  можно  изменить  за  ТМ.  Возможность  доступна  на  экране\n                        управления  планетой   (Обзор   планеты   -&gt;   Управление).   Стоимость   смены\n                        высчитывается динамически  и  зависит  от  того,  насколько  сильно  отличается\n                        текущий тип ядра от желаемого\n                        Добавлено отображение типа ядра планеты на страницу \"Обзор планеты\"\n                        На страницу \"Обзор Империи\" добавлено отображение типа ядра планеты с  цветовым\n                        кодированием:\n                        Зеленый - тип ядра встречается очень часто\n                        Желтый - тип ядра встречается часто\n                        Оранжевый - тип ядра встречается редко\n                        Красный - тип ядра встречается очень редко\n                        В Новапедию добавлена статья про плотность и типы ядер планет\n                    </div>\n                </li>\n                <li class=\"chapter\" id=\"chapter_30\">\n                    <h2 class=\"heading\">Офицеры (наемники и губернаторы) и Чертежи</h2>\n                    <div class=\"desc\">\n                        Полностью переработана система офицеров.  Произведено  разделение  офицеров  на\n                        \"наемников\" и \"губернаторов\". Стоимость нового уровня офицера рассчитывается по\n                        формуле:\n                        БС * (Фактор ^ Уровень), где\n                        БС - базовая стоимость Наемника в ТМ. Наемники за ресурсы не поддеживаются\n                        Фактор - заранее заданная величина\n                        ^ - операция возведения в степень\n                        Уровень - новый уровень Наемника\n                        БС  и  Фактор  прописываются  индивидуально  для  каждого  Наемника   в   файле\n                        \"/includes/vars.php\". По умолчанию БС = 3.000, Фактор = 1\n                    </div>\n                </li>\n                <li class=\"chapter\" id=\"chapter_31\">\n                    <h2 class=\"heading\">Бонус к скорости постройки/исследования</h2>\n                    <div class=\"desc\">\n                        <p>Изменена логика работы Академика, Фортификатора и Инженера. Все они  влияют  на\n                            скорость  постройки  юнитов,  однако  раньше   зависимость   была   практически\n                            экспоненциальная. Вдобавок слишком большой  бонус  от  Академика  в  Альянсе  в\n                            сочетании с полностью прокачанным Наемником у игрока мог приводить к артефактам\n                            в работе исследований\n                        </p>\n                        <p>Теперь бонус указанных офицеров - это  процент  увеличения  скорости  постройки\n                            соответствующих юнитов, а не процент, на который уменьшается  время  постройки.\n                            Т.е. это - слагаемое в знаменатели дроби\n                        </p>\n                        <p>Если говорить совсем просто: 100% бонуса от офицера уменьшают  время  постройки\n                            юнита в 2 раза, 200% - в три раза, 300% - в четыре раза и так далее\n                        </p>\n                    </div>\n                </li>\n                <li class=\"chapter\" id=\"chapter_32\">\n                    <h2 class=\"heading\">Наемники</h2>\n                    <div class=\"desc\">\n                        <p>Наемники покупаются через общее меню слева и их бонусы распространяются на  всю\n                            Империю\n                        </p>\n                        <p>СН поддерживает два режима работы с Наемниками - Постоянных  Наемников  (ПН)  и\n                            Временных Наемников (ВН). Режим Наемников переключается в настройках сервера  и\n                            может быть изменен на лету (см. ниже пункт \"Переключение режима Наемников\")\n                        </p>\n                        <div class=\"sub\">\n                            <h3 class=\"title\">Постоянные Наемники</h3>\n                            <ul>\n                                <li>\n                                    В режиме ПН  Наемники  разбиты  на  две  независимые  ветки  -  \"шахтерскую\"  и\n                                    \"рейдерскую\". \"Ветка\" означает, что для рекрутирования  Наемника,  находящегося\n                                    правее  по  списку,  небоходимо  сначала  нанять  один  или  несколько  уровней\n                                    Наемника, находящегося левее\n                                    \"Шахтерская\" ветка имеет вид: \"Карго-мастер\" =&gt; \"Шпион\" =&gt; \"Академик\"\n                                    \"Рейдерская\" ветка имеет вид: \"Адмирал\" =&gt; \"Координатор\" =&gt; \"Навигатор\"\n                                    <ul>\n                                        <li>\n                                            <h4>Временные Наемники</h4>\n                                            ВН  (как  следует  из  названия)  не  являются  постоянными,  а  нанимаются  на\n                                            определенный срок. По истечению срока Наемник исчезает\n                                            В режиме ВН отсутствует понятие \"веток развития\" и для найма доступны сразу все\n                                            Наемники. Соответственно не отображаются требования  к  Наемникам  на  странице\n                                            \"Технологии\"\n                                            Базовая цена покупки ПН в режиме ВН становится ценой найма  на  базовый  период\n                                            найма (БПН). По умолчанию он равен одному  среднекаелндарному  месяцу(30  дней,\n                                            2.592.000 секунд). Изменить БПН можно на странице настроек сервера\n                                            Предусмотрена система скидок/наценок в зависимости от срока  покупки  Наемника.\n                                            Настройки содержатся в массиве $mrc_hire_discount в файле /officer.php.  Индекс\n                                            элемента  -  количество  секунд  найма,  значение  -  коэффициент  скидки.  \"1\"\n                                            означает, что на данный  интервал  найма  нет  ни  наценки,  ни  скидки  и  при\n                                            пересчете на количество секунд в БПН  его  стоимость  будет  в  точности  равна\n                                        </li>\n                                        <li>\n                                            <h4>стоимости БПН. Если число меньше единицы - это означает скидку; больше  единицы</h4>\n                                            наценку\n                                            Временного наемника можно увольнять до истечения  срока  найма.  ВНИМАНИЕ!  При\n                                            увольнении наемников вся портаченная на найм ТМ будет утеряна!\n                                            Режим Наемников отображается на странице \"Мировые константы\"\n                                        </li>\n                                        <li>\n                                            <h4>Переключение режима Наемников</h4>\n                                            <p> При включении ВН все постоянные Наемники будут преобразованы  во  временные  со\n                                                сроком действия равному БПН. В случае необходимости изменить БПН нужно  СНАЧАЛА\n                                                его изменить, а затем переключать режим работы Наемников\n                                                После включения  ВН  изменение  базового  интервала  найма  не  влияет  на  уже\n                                                рекрутированных Наемников, а влияет только на цену нового найма\n                                                При отключении ВН все активные на этот момент Наемники  будут  преобразованы  в\n                                                постоянные - вне зависимости от того, на какой срок они были наняты  и  сколько\n                                                времени осталось до срока истечения найма. Информация о сроках найма  при  этом\n                                                теряется\n                                            </p>\n                                            <p>При отключении  ВН  активизируются  ограничения  по  рекрутированию  Наемников,\n                                                однако уже нанятые Наемники останутся активными и  будут  влиять  на  игру  вне\n                                                зависимости от того, может игрок их купить или нет. Такой  способ  переключение\n                                                выбран для исключения потери ТМ, вложенных игроками в Наемников\n                                            </p>\n                                        </li>\n                                        <h4>Страница рекрутирования</h4>\n                                        Переработана страница рекрутирования Наемников:\n                                        1. Добавлена поддержка временных Наемников\n                                        2. Стоимость найма отображается динамически в зависимости  от  текущего  режима\n                                        Наемников, выбранного уровня и срока найма\n                                        3. В режиме ПН видны  все  наемники  -  даже  недоступные  (с  соответствующими\n                                        пояснениями)\n                                        4. В режиме ПН можно нанимать сразу несколько уровней Наемников\n                                        5. При найме  постоянных  наемников  показывается  ровно  столько  уровней,  на\n                                        сколько хватает ТМ\n                                    </ul>\n                                </li>\n                            </ul>\n                        </div>\n                    </div>\n                </li>\n                <li class=\"chapter\" id=\"chapter_33\">\n                    <h2 class=\"heading\">Губернаторы</h2>\n                    <div class=\"desc\">\n                        <p>Губернаторы покупаются отдельно  на  каждую  планету  на  странице  \"Управление\n                            планетой\" и их бонусы распространяются только на ту  планету/луну,  на  которой\n                            они куплены. Текущий губернатор и его уровен отображается  на  странице  \"Обзор\n                            планеты\", а так же в виде индикатора на изображениях планет в \"Обзоре  планеты\"\n                            и \"Обзоре Империи\"\n                        </p>\n                        <p> Губернаторов  можно  сменять.  При  покупке  нового  губернатора  его  развитие\n                            начинается с первого  уровня,  а  вся  ТМ,  вложенные  в  предыдущего  офицера,\n                            теряется. Страница покупки  губернаторов  требует  подтверждение  операции  при\n                            покупке губернатора, отличного от текущего.\n                            Страница покупки имеет защту от случайной покупки  губернатора  при  обновлении\n                            страницы\n                        </p>\n                        <p>Балансировка губернаторов проведена из расчета на \"среднего игрока\", имеющего 6\n                            планет при скорости х2. Такие игроки не получат  пенальти  при  оснащении  всех\n                            планет губернаторами. Понятно, что общая производительность Империи  уменшится,\n                            однако это те жертвы, на которые я готов пойти. В целом же изменение направлено\n                            на уменьшение среднего количества ТМ у игроков.  В  особенности  -  у  топов  и\n                            саб-топов\n                            \"Технолог\" объединяет функции Геолога и Энергетика: каждый уровень дает  +5%  к\n                            добыче металла, кристалла, дейтерия, а так  же  к  выработке  электроэнергии на\n                            солнечной  и  термоядерной  электростанциях.  Технолог   5го  уровня  позволяет\n                            строить термоядерные электростанции.\n                            Вследствие  полной  бессмысленности  найма  Технолога  на  лунах  во  избежание\n                            напрасных трат ТМ игроками он убран со страницы управления луной\n                            БС 800, Фактор 1,06, нет ограничения по уровням\n                            Каждый уровень \"Инженера\":\n                        </p>\n                        1. Увеличивает скорость постройки зданий и кораблей на 10%\n                        2. Добавляет по одному слоту к очередям постройки кораблей и зданий\n                        БС 500, Фактор 1,65, не имеет ограничения по уровню\n                        Каждый уровень \"Фортификатора\":\n                        1. Увеличивает   скорость   постройки   защитных сооружений и ракет на 10%\n                        2. На 10% увеличивает все боевые характеристики кораблей на орбите и  оборонных\n                        сооружений на поверхности планеты\n                        3. Добавляет 1 слот к очереди постройки обронительных сооружений\n                        БС 2.000, Фактор 1,25, не имеет ограничения по уровню\n                    </div>\n                </li>\n                <li class=\"chapter\" id=\"chapter_34\">\n                    <h2 class=\"heading\">Чертежи</h2>\n                    <div class=\"desc\">\n                        <p>\"Чертеж\"  -  это  программный  пакет,  дающий  доступ  к   производсту   юнитов\n                            определенного  типа.  В  терминах  xNova  Чертеж  можно  считать  одноуровневым\n                            перманентным Наемником. Имеющиеся раннее  Наемники  \"Разрушитель\"  и  \"Ассасин\"\n                            сконвертированы соответственно в \"Чертеж ЗС\" и \"Чертеж СН\".  Остальные  четрежи\n                            надо покупать самостоятельно\n                            Доступны следующие чертежи:\n                        </p>\n                        <ul>\n                            <li>1. Здания: термоядерная электростанция</li>\n                            <li>2. Корабли: супертранспорт, гипертранспорт, Звезда Смерти, \"СуперНова\"</li>\n                            <li>3. Защитные постройки: планетарная защита</li>\n                        </ul>\n                        <p> Чертеж является перманентым\n                            Чертежи заменяют Наемников в требованиях к постройке\n                            Чертеж покупается на Империю, после чего указанный юнит доступен к производству\n                            на всех планетах\n                        </p>\n                        <p>Если в движке установлен  модуль  \"Альянсы-Игроки\",  то  Чертежи  доступны  для\n                            покупки Альянсами. Стоимость такого Чертежа равна базовой стоимости  умноженной\n                            на минимальное количество членов Альянса  -  но  такой  Чертеж  дает  доступ  к\n                            постройке указанных юнитов сразу всем членам\n                        </p>\n                    </div>\n                </li>\n                <li class=\"chapter\" id=\"chapter_35\">\n                    <h2 class=\"heading\">Артефакты</h2>\n                    <div class=\"desc\">\n                        <p>\"Артефакты\" - редкие объекты с уникальными  свойствами,  приобретаемые  за  ТМ.\n                            Артефакты  являются  одноразовыми  -  после  использования  Артефакт  исчезает.\n                            Некоторые Артефакты  настолько  мощные,  что  их  количество  в  одной  Империи\n                            ограничено\n                        </p>\n                        <p>Использование некоторых Артефактов привязано к планетам - т.е. их эффект  будет\n                            распространятся   только   на   эту   планету.   Эффекты   других    Артефактов\n                            распространяются на всю Империю. Особо мощные Артефакты могут оказывать влияние\n                            на солнечную систему, галактику или даже Вселенную\n                        </p>\n                        <div class=\"sub\">\n                            <h3 class=\"title\">Большой Адронный Колайдер (БАК)</h3>\n                            <ul>\n                                <li>\n                                    БАК генерирует поток гравитонного излучения, направленный в  место  наибольшего\n                                    скопления обломков на  орбите,  притягивая  к  ним  другие  обломки.  Иногда  в\n                                    результате может образоваться луна. Шанс создания луны - 1%  за  каждый  полный\n                                    миллион обломков, но не более 30%'\n                                    ВНИМАНИЕ! Использование БАК не гарантирует появления луны!\n                                    <ul>\n                                        <li>\n                                            <h4>Автономный Колонизационный Комплекс (АКК)</h4>\n                                            <p>АКК является набором готовых  конструкций  и  программ,  позволяющий  мгновенно\n                                                развернуть на новооткрытой планете колонию. Если на планете  уже  есть  здания,\n                                                входящие в  АКК,  они  будут  либо  усовершенствованы  (если  их  уровень  ниже\n                                                возможностей АКК), либо останутся нетронутыми\n                                                АКК может быть полностью развернут  на  планете  даже  при  нехватке  свободных\n                                                секторов, поэтому особо ценен при колонизации малых планет\n                                                АКК не может быть развернут на луне\n                                            </p>\n                                            <p>Существуют три вида АКК, различающихся по стоимости и возможностям:\n                                                Малый  АКК  -  колония  включает  в  себя  Рудник,  Синтезатор  кристаллов  и\n                                                Синтезатор дейтерия  10го  уровня,  Солнечную  электростанцию  14го  уровня и\n                                                Фабрику роботов 4го уровня\n                                            </p>\n                                            <p>Средний АКК -  колония  включает  в  себя  Рудник,  Синтезатор  кристаллов  и\n                                                Синтезатор дейтерия  15го  уровня,  Солнечную  электростанцию  20го  уровня и\n                                                Фабрику роботов 8го уровня\n                                            </p>\n                                            <p>Большой АКК -  колония  включает  в  себя  Рудник,  Синтезатор  кристаллов  и\n                                                Синтезатор  дейтерия  20го  уровня,  Солнечную  электростанцию  25го  уровня,\n                                                Фабрику роботов 10го уровня и Нанофабрику 1го уровня\n                                            </p>\n                                        </li>\n                                        <li>\n                                            <h4>\"Эвристический чип\" и \"Наностроитель\"</h4>\n                                            <p>Артефакты уменьшают на 1 час соответственно время текущего исследования и время\n                                                постройки/уничтожения текущего здания на текущей планете\n                                            </p>\n                                            <p>Если оставшееся время исследования/постройки/уничтожения меньше одного часа, то\n                                                Артефакт обнуляет время. Разница не переходит на следующий слот в очереди\n                                                Стоимость эвристического чипа составляет 20.000 ТМ\n                                                Стоимость наностроителя составляет 5.000 ТМ\n                                            </p>\n                                            <p>В очередь построек  добавлена  возможность  использовать  Наностроитель  -  при\n                                                наличии Артефакта на складе\n                                            </p>\n                                            <p>В очередь построек добавлена возможность использовать Эвристического чипа - при\n                                                наличии Артефакта на складе\n                                            </p>\n                                        </li>\n                                    </ul>\n                                </li>\n                            </ul>\n                        </div>\n                    </div>\n                </li>\n                <li class=\"chapter\" id=\"chapter_36\">\n                    <h2 class=\"heading\">Модуль \"Капитаны\": unit_captain</h2>\n                    <div class=\"desc\">\n                        <p> Капитан - это опытный командующий, который летает с флотами  и  за  счет  более\n                            тонкого управления флотами улучшает эффективные характеристики всех кораблей во\n                            флоте\n                        </p>\n                        <p>С флотом можно отправить только одного Капитана\n                            Каждый Капитан привязан к определенной планете  или  луне.  Нельзя  иметь  двух\n                            Капитанов на  одном  небесном  теле.  Капитан,  летящий  с  флотом,  все  равно\n                            считается привязанным к планете\n                            Капитана можно перевозить с одной планеты на другую  миссией  \"Передислокация\".\n                        </p>\n                        <div class=\"sub\">\n                            <h3 class=\"title\">При этом на время перелёта Капитан считается привязанным сразу к обоим планетам стартовой и финишной</h3>\n                            <ul>\n                                <li>\n                                    <ul>\n                                        <li>\n                                            <h4>Найм и гибель Капитана</h4>\n                                            Капитан нанимается за ТМ\n                                            Найм и управление Капитанами осуществляется через пункт меню \"Капитаны\"  (сразу\n                                            под \"Наемниками\")\n                                            При гибели флота Капитан так же погибает. Под \"гибелью  флота\"  подразумевается\n                                            уничтожение всех кораблей флота. Это верно как для атакующих флотов, так и  для\n                                            флотов, стоящих в удержании\n                                            Капитан на планете не участвует в защите планеты при атаке  -  этим  занимается\n                                            Фортификатор. Зато  при  полном  уничтожении  всего  планетарного  флота  такой\n                                            Капитан не погибнет\n                                        </li>\n                                        <li>\n                                            <h4>Опыт, уровни и навыки Капитана</h4>\n                                            За каждый выигранный простой бой (САБы  и  миссия  \"Уничтожить\"  не     считаются)\n                                            Капитан атакующего флота получает 1 пункт опыта. За  \"победы\"  над      неактивными\n                                            игроками опыт не начисляется. Так же не начисляется опыт, если  бой     закончился\n                                            выигрышем атакующего за 1 раунд\n                                            Капитаны всегда улучшают характеристики  кораблей  своего  флота  -     даже  если\n                                            участвуют в бою, за который они не получат опыта: атака на  неактивных  игроков,\n                                            удержание, САБ, уничтожение луны и т.д.\n                                            При наборе определенного количества опыта Капитан получает новый    уровень\n                                            Чем выше уровень - тем больше опыта нужно для получения следующего  уровня\n                                            Повышение в уровне дает возможность улучшать умения Капитанов.\n                                            Умения Капитана включают бонусы к щитам, броне и атаке\n                                            Каждый  уровень  умений   дает   1%   к   базовому   значению       соответствующей\n                                            характеристики кораблей во флоте, которым управляет Капитан\n                                            Уровни Капитана вкладываются в умения один раз и  навсегда  -   поэтому  заранее\n                                            тщательно планируйте развитие своего Капитана\n                                        </li>\n                                        <li>\n                                            <h4>Капитаны в Обзоре Империи</h4>\n                                            Уровни Капитанов указываются в списке юнитов на Обзоре Империи. На  заднем  фоне\n                                            ячейки  с  уровнем  выводится  прогресс-бар  развития   Капитана    с   цветовым\n                                            кодированием:\n                                            Пустая ячейка - Капитан не нанят, либо только что получил уровень\n                                            Красный прогресс-бар - до следующего уровня осталось больше 50% опыта\n                                            Оранжевый - не меньше 50% опыта, но меньше 80%\n                                            Желтый - не меньше 80% опыта\n                                            Зеленый - в следующем бою Капитан получит новый уровень\n                                        </li>\n                                        <li>\n                                            <h4>Капитаны в летящих флотах</h4>\n                                            В списке флотов на странице \"Флоты в полёте\" и для  своих  флотов   на  странице\n                                            \"Обзор планеты\" если во флоте есть Капитан перед количеством кораблей   во  флоте\n                                            высвечивается \"*\", а в попапе состава показывается его уровень\n                                        </li>\n                                    </ul>\n                                </li>\n                            </ul>\n                        </div>\n                    </div>\n                </li>\n                <li class=\"chapter\" id=\"chapter_37\">\n                    <h2 class=\"heading\">Смена имени пользователя</h2>\n                    <div class=\"desc\">\n                        <p>Добавлена возможность изменения имени пользователя за ТМ. Стоимость изменения -\n                            100.000 ТМ\n                        </p>\n                        <p>Игра сохраняет историю изменения имени  пользователя.  Только  бывший  владелец\n                            может при желании вернуть себе старое имя - опять же за ТМ\n                            Поиск по имени так же производится по старым именам. В случае, если старое  имя\n                            пользователя соответствует критериям поиска, в результаты будет  добавлена  еще\n                            одна строка, в которой будет указано текущее имя пользователя, а после  него  в\n                            скобках и выделенное цветом - старое имя пользователя. Никто  не  спрячется  от\n                            своей истории!\n                        </p>\n                        <p>Максимальная длина имени пользователя уменьшена до 32 символов\n                            Переменная настроек  сервера  'game_user_changename'  отвечает  за  возможность\n                            смены имени пользователя самим пользователем:\n                        </p>\n                        0 - смена имени запрещена\n                        1 - смена имени разрешена и свободна\n                        2 - смена имени разрешена, но стоит  ТМ.  Стоимость  смены  имени  указана  в\n                        переменной 'game_user_changename_cost' (100.000 ТМ по умолчанию)\n                        По умолчанию включена смена пользователем своего имени за ТМ\n                    </div>\n                </li>\n                <li class=\"chapter\" id=\"chapter_38\">\n                    <h2 class=\"heading\">Чёрный Рынок</h2>\n                    <div class=\"desc\">\n                        <p>Полностью переписан Чёрный Рынок. Теперь можно менять ТМ на  ресурсы  (Торговец\n                            Ресурсами), продавать несколько типов  кораблей  за  одну  транзакцию  (Скупщик\n                            флота). Так же добавлен \"Продавец кораблей\", где можно  выкупить  ранее  кем-то\n                            проданные корабли\n                        </p>\n                        <div class=\"sub\">\n                            <h3 class=\"title\">Торговец ресурсами</h3>\n                            <ul>\n                                <li>\n                                    Теперь можно поменять ТМ сразу на все ресурсы (опция \"Все ресурсы\" в  дропдауне\n                                    выбора ресурсов). При этом вводимая сумма будет разделена на  три  части  и  на\n                                    каждую из этих третей будет  куплено  соответствующее  количество  ресурсов  по\n                                    курсу.\n                                    Стоимость такой операции -  в три раза больше базовой стоимость обмена.\n                                </li>\n                            </ul>\n                        </div>\n                    </div>\n                </li>\n                <li class=\"chapter\" id=\"chapter_39\">\n                    <h2 class=\"heading\">Продавец информации</h2>\n                    <div class=\"desc\">\n                        <p>На Чёрном Рынке доступна новая услуга: продажа информации.\n                            Письма от Продавца Информации всегда приходят в почтовый ящик  -  даже  если  у\n                            игрока отключено получение шпионских отчетов. Мистика какая-то!\n                        </p>\n                        <div class=\"sub\">\n                            <h3 class=\"title\">Информация об игроке</h3>\n                            <ul>\n                                <li>\n                                    Текущие уровни активных Наемников и (если есть) уровень премиума\n                                </li>\n                            </ul>\n                        </div>\n                    </div>\n                </li>\n                <li class=\"chapter\" id=\"chapter_40\">\n                    <h2 class=\"heading\">[#] Модуль player_race: Расы</h2>\n                    <div class=\"desc\">\n                        <p> ДАННЫЙ МОДУЛЬ НЕ ВХОДИТ В СОСТАВ ПУБЛИЧНОЙ ВЕРСИИ!\n                            Модуль обеспечивает функциональность \"рас\" - дополнительные бонусы  игроку  при\n                            выборе одной из предустановленных  \"Родных  миров\"  -  так  в  терминологии  СН\n                            называются расы\n                        </p>\n                        <p>Раса выбирается после регистрации на странице  Императора.  Первый  выбор  расы\n                            производится бесплатно, каждая смена расы стоит 100.000 ТМ\n                            В модуле имеется шесть предустановленных рас  с  возможностью  изменения  любой\n                            расы либо напрямую в модуле, либо отдельным модулем\n                        </p>\n                        <p>Список  предустановленных  рас:  земляне,   луниты,   меркурианцы,   венериане,\n                            марсиане, республиканцы (Астероидные Республики)\n                        </p>\n                        <p>Иконка расы отображается в чате, в статистике, в попапе информации об игроке во\n                            Вселенной и на странице Императора. Удержание курсора над иконкой расы вызывает\n                            тултип с её названием. Клик - открывает страницу с описанием всех рас\n                            Каждая раса имеет собственные бонусы.  Бонусы  рас  действуют  сразу  же  после\n                            выбора родного мира - не нужно, например, исследовать техи, что бы  получить  к\n                            ним бонус\n                        </p>\n                        <p>Описание текущей расы доступно на странице Императора. Там же  есть  ссылка  на\n                            описание всех рас в игре с указанием их символов\n                        </p>\n                    </div>\n                </li>\n                <li class=\"chapter\" id=\"chapter_41\">\n                    <h2 class=\"heading\">[#] Модуль player_race_units: Расовые юниты</h2>\n                    <div class=\"desc\">\n                        <p>ДАННЫЙ МОДУЛЬ НЕ ВХОДИТ В СОСТАВ ПУБЛИЧНОЙ ВЕРСИИ!\n                            ДЛЯ ФУНКЦИОНИРОВАНИЯ ДАННЫЙ МОДУЛЬ ТРЕБУЕТ НАЛИЧИЕ МОДУЛЯ PLAYER_RACE!\n                        </p>\n                        Модуль добавляет по одному уникальному юниту каждой расе\n                        Пакет так же содержит модуль-пример добавления новой расы и расового юнита\n                        Шесть уникальных юнитов - по одной каждой из рас:\n                        Земная \"Лень\" - боевой солнечный спутник\n                        Лунная \"Зависть\" - легкий бомбардировщик\n                        Меркурианское \"Обжорство\" - емкий переработчик\n                        Венерианский \"Гнев\" - истребитель-перехватчик\n                        Марсианская \"Гордыня\" - усовершенствованный линейный крейсер\n                        Республиканская \"Жадность\" - боевой транспорт\n                    </div>\n                </li>\n                <li class=\"chapter\" id=\"chapter_42\">\n                    <h2 class=\"heading\">Флот: корабли, миссии и боевая система</h2>\n                    <div class=\"desc\">\n                    </div>\n                </li>\n                <li class=\"chapter\" id=\"chapter_43\">\n                    <h2 class=\"heading\">Боевая система</h2>\n                    <div class=\"desc\">\n                        <p>СН  использует  собственную  боевую  систему  UBE.  Все  расчеты,  касающиеся\n                            обработки  боя,  унифицированы.  Т.е.  обычный  бой,  симулятор,  удержание   и\n                            уничтожение луны - все они используют один и тот  же  алгоритм  расчета.  В  RR\n                            использовались три разных движка для разных расчетов\n                        </p>\n                        <div class=\"sub\">\n                            <h3 class=\"title\">Особенности UBE</h3>\n                            <ul>\n                                <li>\n                                    <ul>\n                                        <li>1. Своя система расчета повреждений</li>\n                                        <li>2. Свой собственный вариант \"скорострела\" (НЕ ТАКОЙ, КАК НА ОФФЕ!)</li>\n                                        <li>3. Свой алгоритм расчета добычи с планет</li>\n                                        <li>4. Полностью рабочие САБы</li>\n                                        <li>5. Работает удержание на лунах при задании \"Уничтожить\"</li>\n                                    </ul>\n                                    <p>В UBE НЕТ понятия \"скорострела\". Вместо  него  используется  понятие  \"залповый\n                                        огонь\". По функционалу \"залповый огонь\" и \"скорострел\" - понятия  одного  ряда.\n                                        Но алгоритмы расчета у них РАЗНЫЕ.\n                                    </p>\n                                    <p>Боевая система работает следуюшим образом. Предполагается,  что  оба  флота  (в\n                                        случае боя один на один) летят в формации \"диск\". Что, в целом, логично -  надо\n                                        максимизировать   возможность   каждого   корабля   дать   залп.   Залп   флота\n                                        распределяется между классами кораблей противника в зависимости от брони (ака -\n                                        тоннажа, ака размера). Тоже логично  -  в  большую  цель  легче  попасть.  Щиты\n                                        принципиально отличаются от брони тем,  что  щиты  считаются  целиком  по  типу\n                                        кораблея, а броня - индивидуально для каждого корабля. Т.е.  что  бы  повредить\n                                        один корабль,  сначала  надо  пробить  щиты  по  типу  кораблей.\n                                    </p>\n                                    <p>При выстреле атакующего в противника, против которого у аттакера есть  залповый\n                                        огонь, урон модифицируется согласно внутренним таблицам. Залповый  огонь  может\n                                        как увеличивать наносимые повреждения, так и уменьшать -  значения  в  таблицах\n                                        залпового огня подобраны таким образом, что бы с одной стороны  сделать  каждый\n                                        корабль уникальным и применимым в определенных условиях, а с другой  стороны  -\n                                        сбалансировать все корабли между собой и не допустить появление суперкораблей.\n                                        Обращаю внимание - залповый огонь применяется  сразу  по  щитам.  Дальше  часть\n                                        урона поглощается  щитами,  а  если  что-то  прошло  сквозь  щиты  -  наносится\n                                        повреждение по броне. А там сравниваем прошедший  урон  с  броней  и  списываем\n                                        нужное количество корабликов.\n                                    </p>\n                                    <ul>\n                                        <li>\n                                            <h4>Случайная составляющая</h4>\n                                            Бои в СН имеют случайную составляющую. Т.е. результаты двух проведенных боев  в\n                                            одних и тех же начальных условиях могут различаться кардинально. С точки зрения\n                                            игрового  мира,  случайная  составляющая  отражает  маневры  кораблей,   работу\n                                            пилотов, наводчиков и всего остального экипажа корабля, а так  же  случайности,\n                                            присущие реальному миру.\n                                        </li>\n                                        <li>\n                                            <h4>Симулятор боя</h4>\n                                            В симуляторе убрана  случайная  составляющая.  Это  позволит  не  пересчитывать\n                                            результаты по десять раз, что бы получить примерное представление о  бое,  а  с\n                                            первого раза увидеть результат \"в нормальных условиях\" без влияния случайностей.\n                                            Симулятор поддерживает продвинутую систему ссылок. Теперь можно  давать  ссылку\n                                            как на результат симуляции, так и на форму с заполненными  входные  данные  для\n                                            дальнейших экспериментов\n                                        </li>\n                                        <li>\n                                            <h4>FAQ по боевой системе</h4>\n                                            Q: Что означает в описании корабля Х строка  \"Одним  залпом  поражает  КорабльY\n                                            ZШтук\"?\n                                            A: Она означает, что если корабль Х (при отсутсвии бонусов к  щитам/броне/урону\n                                            и отсутсвии офицеров) встретится с ZШтук кораблей разновидности КорабльY, то за\n                                            один раунд корабль Х уничтожит все корабли противника.\n                                            Для кораблей, которых нет в  списке  залпового  огня,  количество  убиваемых\n                                            кораблей за раунд равно: (БроняЗащитника + ЩитыЗащитника) / АтакаНападающего.\n                                            Q: Корабль Х уничтожил корабли противника, но ответным залпом его самого сбили!\n                                            A: А так вполне может быть. Выживаемость корабля Х никто не гарантировал\n                                            Q: Как-то странно выбираются цели при залпе...\n                                            A: Увы, что бы корректно выбирать цели, необходимо обсчитывать каждый  корабль.\n                                            Т.е. во-первых - иметь  индивидуальную  запись  каждого  корабля.  Во-вторых  -\n                                            вычислять эффективность удара в зависимости от параметров каждого корабля, т.е.\n                                            совершать просмотр  огромного  количества  информации  каждый  ход.  Если  даже\n                                            применять всякие алгоритмы оптимизации и сортировки, то  на  величинах  порядка\n                                            десятков тысяч кораблей (обычная бойня между топ-флотами) количество вычислений\n                                            будет чрезвычайно велико. И это  не  считая  сложности  разработки  более-менее\n                                            оптимального алгоритма. Поэтому ради  ускорения  расчетов  приходится  идти  на\n                                            сознательное огрубление модели.\n                                            Q: Просуммировал нанесенные повреждения, а затем посчитал, сколько должно  было\n                                            быть нанесено повреждений. Цифры не совпадают!!!\n                                            A: Поскольку применяется достаточно грубая и упрощенная модель (см.  предыдущий\n                                            ответ), часть урона может пропасть. Например, если несколько атакующих стреляют\n                                            по одним и тем же кораблям и первым залпом  корабли  защитника  уничтожены,  то\n                                            остальной урон прийдется на пустое место, т.е.  \"пропадет\".  Учет  \"пропавшего\"\n                                            урона потребовал бы перерасчета всех оставшихся категорий  кораблей  на  каждой\n                                            итерации, т.е.  существенно  увеличил  бы  нагрузку  (эксперимент  такого  рода\n                                            проводился). Кроме того, если урон приходится на последние корабли  противника,\n                                            часть его все равно пропадет.\n                                            Q: Посчитал в симуляторе - СН не смогла убить туеву хучу  кораблей!  Корабль  -\n                                            гавон!!!\n                                            A: Еще раз - применяется достаточно грубая и  упрощенная  модель  обсчета  боя,\n                                            см. пред-предыдущий ответ. В частности, каждый  раунд  количество  уничтоженных\n                                            кораблей округляется вниз до  ближайшего  целого.  Т.е.  если  в  одном  раунде\n                                            корабль атакующего нанес повреждений после щита на 0.99 кораблей защитник,  это\n                                            значение округляется до 0 и в следующем раунде количество кораблей  у  защтника\n                                            будет таким же. Грубо говоря, это означает, что в начале раунда  все  уцелевшие\n                                            корабли имеют полную броню. В  случае  симулятора,  где  отсутствует  случайная\n                                            составляющая, при определенных условиях СН не сможет убить ни  одного  корабля.\n                                            Впрочем, на  граничных  случаях  движок  офф-сервера  тоже  ведет  себя  крайне\n                                            странно. У каждого движка - свои особенности.\n                                        </li>\n                                        <li>\n                                            <h4>Ракетный удар</h4>\n                                            Полностью переписан скрипт ракетной атаки. Основные моменты:\n                                            [*] Полностью устранена ошибка  с  отрицательными  защитными  строениями  после\n                                            атаки\n                                            [*] Полностью устранена ошибка с размножением защитных строений после атаки\n                                            [*] Теперь ракетами в принципе можно уничтожить планетарную защиту (раньше  это\n                                            было невозможно)\n                                            [*] Учитывается уровень вооружений нападающего и уровень брони обороняющегося\n                                            [*] Учитываются  щиты  оборонных  сооружений.  Это  должно  слегка    уменьшить\n                                            эффективность ракет и повысить  живучесть  защитных  сооружений  с  большим\n                                            количеством щитов\n                                            [*] При ракетном  ударе  рандомизируются  параметры  атаки,  брони  и  щитов  у\n                                            соответствующих юнитов. Границы такие же, как и для сражений  флотов  -  от\n                                            80% до 120%\n                                            [*] Максимально выбирается весь ракетный урон - ранее существенная часть  урона\n                                            могла быть потеряна\n                                            [*] Полностью переписана логика случайной атаки - когда ракетам не задана цель.\n                                            Теперь она ДЕЙСТВИТЕЛЬНО случайная (в рамках предыдущего пункта)\n                                            [*] При выборе конкретного сооружения для атаки ракета  бьет  ИСКЛЮЧИТЕЛЬНО  по\n                                            выбранному сооружению\n                                            [*] Правильно и всегда срабатывают перехватчики\n                                            [*] Теперь после атаки можно собирать обломки защитных сооружений  -  защитнику\n                                            автоматически добавляется половина стоимости сооружений в металле и четверть\n                                            - в кристаллах\n                                            [*] Устранен глюк с многократным подсчетом одной атаки\n                                            [*] Расширено  и  дополнено  сообщение  о  результатах  нападаения,  приходящее\n                                            защитнику\n                                            [*] В результате изменений в алгоритме существенно повысилась живучесть ПЗ  при\n                                            ракетном ударе\n                                            [*] Добавлена поддержка усиления залпа для МПР\n                                        </li>\n                                        <li>\n                                            <h4>Луна</h4>\n                                            Теперь при создании луны с орбиты списывается количество обломков,  из  которых\n                                            сформировалась луна\n                                        </li>\n                                    </ul>\n                                </li>\n                            </ul>\n                        </div>\n                    </div>\n                </li>\n                <li class=\"chapter\" id=\"chapter_44\">\n                    <h2 class=\"heading\">UBEv4</h2>\n                    <div class=\"desc\">\n                        <p> В 36м релизе была произведена замена UBEv3 на UBEv4: с нуля был написан  боевой\n                            движок и боевые отчеты. В этом  разделе  будут  перечислены  особенности  новой\n                            боевой системы и её отличия от UBEv3\n                        </p>\n                        <div class=\"sub\">\n                            <h3 class=\"title\">Особенности подготовки к бою</h3>\n                            <ul>\n                                <li>\n                                    Бой теперь считается не по $time_now, а по времени прилета флота -  на  случай,\n                                    если бой сильно отложенный. Например, при сбоях движка  или  низкой  активности\n                                    сервера. Так будут отработаны корректно все удержания в правильное время\n                                    <ul>\n                                        <li>\n                                            <h4>Особенности хода боя</h4>\n                                            Броня не регенерируется между раундами\n                                            Если броня упала ниже 75% - корабль имеет шанс взорваться  равный  проценту  от\n                                            общего здоровья\n                                        </li>\n                                        <li>\n                                            <h4>Sneak defense</h4>\n                                            Новый механизм боя: подлов атакующего или sneak defense. Если в САБе и в\n                                            удержании участвуют флоты одного и того же  игрока,  то  прилетающие  флоты\n                                            этого игрока будут сражаться на стороне защитника. Аккуратно смотрите, кого\n                                            приглашает в САБ. Хе-хе\n                                        </li>\n                                        <li>\n                                            <h4>Особенности подведения итогов боя</h4>\n                                            Если в бою участвует хотя бы один  флот  Админов  с  любой  стороны  -  лом  не\n                                            выпадает ни с кого!\n                                            Возвращение обломков с оборонных сооружений не производится\n                                            В миссии \"Уничтожить\" шанс уничтожения флота от взрыва одного из  кораблей  при\n                                            попытке уничтожить луну теперь так же зависит от количества гравидвигателей  во\n                                            флоте - чем их больше, тем шанс выше\n                                            В  миссии  \"Уничтожить\"  корабли  могут  взорваться  даже  в  случае  успешного\n                                            уничтожения луны. Как и раньше, подрыв кораблей  с  гравидвигателем  уничтожает\n                                            весь флот\n                                            Теперь  в  рейдовый  опыт  засчитываются  исключительно  одиночные  атаки.   Ни\n                                            \"Удержание\", ни \"САБ\" не засчитывается. Т.е.  вообще  не  засчитываются  -  вне\n                                            зависимости от результата боя\n                                            Теперь атаки на неактивных игроков (\"i-шки\") не приносят рейдовый опыт\n                                            Количество свободных полей на луне зависит от  её  размера  и  определяется  по\n                                            формуле Размер/1000 с округлением вверх до целого\n                                            Изменен расчет поля обломков. Теперь  на  орбите  оказывается  от  30%  до  70%\n                                            выброшенных  за  борт  ресурсов  и  от  20%  до  40%   обломков   кораблей.   В\n                                            детерминированном симуляторе процент обломков на орбите  всегда  равен  30%,  а\n                                            обломки, выброшенные из трюма всегда составляют 50% от потерь\n                                            9. Шанс уничтожения луны теперь всегда лежит в пределах 1%-99%\n                                        </li>\n                                        <li>\n                                            <ul>\n                                                <li>\n                                                    <h4>Боевой отчёт</h4>\n                                            Боевой отчет теперь состоит  из  трёх  частей:  \"Основная  информация  о  бое\",\n                                            \"Боевые потери\" и лог раундов\n                                            \"Основная информация о бое\" показывает:\n                                          <ul>\n                                                <li>1. Время проведения боя (если доступно)</li>\n                                                <li>2. Место боя (если доступно) - координаты планеты, её тип и имя</li>\n                                                <li>3. Результат боя (выигрыш атакующего, ничья, проигрыш атакующего)</li>\n                                                <li>4. Обломки на орбите</li>\n                                                <li>5. Шанс образования луны и результат такой попытки</li>\n                                                <li>6. (Для миссии \"Уничтожить\") Состояние кораблей с гравидвигателями по  итогам</li>\n                                          </ul>\n                                           <p> боя.  Шанс  уничтожения  луны  оставшимися  кораблями  и  результат  такой\n                                                                                       попытки. Шанс взрыва кораблей и итог миссии\n                                                                                       Раздел \"Боевые потери\" показывает:</p>\n                                            <ul>\n                                                <li>1. (На планетах) Количество восстановленных боевых сооружений</li>\n                                            <li>2. Общие потери боевых единиц каждого из участвующих в бою  игроков.  Если  у\n                                                                                        одного  игрока  участвовало  в  бою  несколько  флотов  -  будут  показаны\n                                                                                        суммарные потери по всем флотам. Это верно  для  всех  параметров  в  этом\n                                                                                        разделе. Для планетарной обороны в потери  не  включаются  восстановленные\n                                                                                        единицы</li>\n                                            <li>3. (В случае победы атакующих) Количество ресурсов, вывезенных с планеты. Для\n                                                                                        планеты  это  будет  положительное   число,   для   атакующих   флотов   -\n                                                                                        отрицательное</li>\n                                            <li>4. (Для флотов)  Количество  ресурсов  потерянных  из-за  уменьшения  емкости\n                                                                                        трюмов вследствии уничтожения части флота. Эти ресурсы рассматриваются как\n                                                                                        \"боевые потери\" - они плюсуются  к  обломкам  на  орбите  и  к  потерям  в\n                                                                                        пересчете на ресурсы</li>\n                                            <li>5. Общие потери в пересчете на ресурсы. Включает стоимость боевых  единиц  на\n                                                                                        момент боя, вывоз с планеты и ресурсы, потерянные из-за уменьшения трюмов</li>\n                                           <li> 6. Общие потери в ресурсах в пересчете на металл по курсу  Черного  Рынка  на\n                                                                                       момент проведения боя. Писькомерка для сравнения\n                                                                                       \"Лог раундов\" показывает результаты расчета каждого раунда для всех флотов</li>\n                                            </ul>\n                                           <ul>\n                                              <li>  1.  Показывает  координаты  и  тип  планеты,  с   которой   прилетели   флоты\n                                                                                          атакующих/защитников</li>\n                                            <li>2.  Расширено  количество  информации  о   боевых   подраздеениях   Добавлена\n                                                                                        информация о \"Пробое\" и \"Уроне\". \"Пробой\" - атака,  которая  пришлась  на\n                                                                                        щиты и была ими поглощена (или пропущена - см.  ниже).  \"Урон\"  -  атака,\n                                                                                        которая пришлась на броню</li>\n                                           <li> 3. Цветовое кодирование информации о подразделениях:\n                                                                                       Зеленый означает, что вся атака в раунде поглощена щитами\n                                                                                       Желтый - часть атаки пробила щиты (\"пробой\") и нанесла урон по  броне,  но\n                                                                                       при этом ни одна боевая единица не уничтожена\n                                                                                       Оранжевый - один или более боевых единиц уничтожено\n                                                                                       Красный - все оставшиеся боевые единицы уничтожены в этом раунде\n                                                                                       Число в скобках в столбце потерь - количество боевых единиц,  взорвавшихся\n                                                                                       в раунде из-за фатальных повреждений\n                                                                                       В боевом отчете координаты планет являются ссылками на Вселенную</li>\n                                           </ul>\n                                                </li>\n                                            </ul>\n                                        </li>\n                                        <li>\n                                            <ul>\n                                                <li>\n                                                    <h4>Симулятор боя</h4>\n                                            <p>Доработан симулятор для поддержки изменений в UBEv4\n                                                                                        Стандартный режим работы  симулятора  -  полная  определенность  результатов  в\n                                                                                        зависимости от начальной конфигурации (галочка \"Симуляция\" включена)</p>\n                                            <p>Добавлен  второй  режим  работы   -   недетрминированный   симулятор   (галочка\n                                                                                        \"Симуляция\" отключена). В этом режиме работы проводится  полная  симуляция  боя\n                                                                                        (включая образование луны) с применением генератора случайных чисел - т.е. так,\n                                                                                        как происходил бы обычный бой. В этом режиме результаты могут сильно отличаться\n                                                                                        от симуляции к симуляции. Так же в этом режиме происходит запись боевого отчета\n                                                                                        с результатом симуляции в БД</p>\n                                            <p>В стандартном режиме если шанс образования луны больше 1 всегда образуется луна\n                                                                                        со средним размером для данного шанса</p>\n                                                </li>\n                                            </ul>\n                                        </li>\n                                    </ul>\n                                </li>\n                            </ul>\n                        </div>\n                    </div>\n                </li>\n                <li class=\"chapter\" id=\"chapter_45\">\n                    <h2 class=\"heading\">Шпионаж</h2>\n                    <div class=\"desc\">\n                        <p>Переписана  подсистема  шпионажа.  Корректно  выставляется   время   шпионского\n                            рапорта. Переписана процедура генерации шпионского рапорта\n                            Алгоритм  работы  шпионажа  -  почти  оффовский,  потому  что  он  меня  вполне\n                            устраивает:\n                        </p>\n                        <ul>\n                            <li>1. Эффективный уровень шпионажа (ЭУШ) на миссию определяется по формулам:\n                                Для атакующего:\n                                TechLevel + MercLevel + sqrt(ProbeNum) - 1\n                                Для защитника:\n                                TechLevel + MercLevel\n                                где\n                                TechLevel - уровень технологии шпионажа\n                                MercLevel - уровень Наемника \"Шпион\"\n                                ProbeNum - количество шпионских зондов, летящих в миссию\n                                sqrt() - функция извлечения квадратного корня\n                            </li>\n                            <li>2. Вычисляется разница между ЭУШ  атакующего  и  ЭУШ  защитника.  Если  разница\n                                уровней...\n                                меньше 2 - атакующий видит только ресурсы\n                                2 и выше - атакующий видит флот на орбите (только  флот  хозяина  планеты!\n                                Удержание не видно!)\n                                3 и выше - атакующий видит защитные сооружения\n                                5 и выше - атакующий видит постройки\n                                7 и выше - атакующий видит технологии\n                            </li>\n                            <li>3. Шанс (в процентах) обнаружения шпионского флота защитником  определяется  по\n                                формуле:\n                                К-во зондов*pow(2, ЭУШ защитника - ЭУШ аттакера)*К-во кораблей на орбите/4\n                                где\n                                pow(2, X) - функция степени двойки, т.е. \"2 в степени X\".\n                                Обращаю внимание, что  в  некоторых  случаях  шанс  обнаружения  может  быть\n                                больше 100% - например при большом флоте на орбите, при  большом  количестве\n                                кораблей в шпионском флоте,  при  большой  разнице  между  ЭУШ  защитника  и\n                                аттакующего\n                            </li>\n                            <li>4. После вычисления шанс обнаружения сравнивается со случайным числом от  0  до\n                                99. Если шпионский флот обнаружен - он в полном составе уничтожается (да,  в\n                                шпионаж все корабли летят со снятым оружием и броней!).\n                                Из вышеописанного алгоритма следует несколько интересных следствий:\n                                1. Количество зондов решает\n                                2. Флот на орбите решает зонды\n                                3. Защититься  от  шпионажа   невозможно,  но   можно   сделать   его   дорогим\n                                удовольствием. Настолько дорогим, что летать в шпионские миссии  на  равного\n                                по уровню шпионажа игрока будут только в endgame. Собственно,  для  этого  и\n                                изменены цифры разницы уровней\n                            </li>\n                            <li>4. Посылать количество зондов, некратное квадрату - не имеет смысла. Все  равно\n                                в расчетах округлится  вниз  до  ближайшего  квадрата,  а  риск  обнаружения\n                                шпионажа существенно вырастет\n                            </li>\n                            <li>5. И уж тем более нет смысла посылать в шпионаж любые корабли кроме зондов</li>\n                            <li>6. Развивай шпионаж с молоду!</li>\n                            <li>7. Офицер \"Шпион\" - он не зря в условно-шахтерской ветке!</li>\n                        </ul>\n                    </div>\n                </li>\n                <li class=\"chapter\" id=\"chapter_46\">\n                    <h2 class=\"heading\">Удержание</h2>\n                    <div class=\"desc\">\n                        <p> В СН ограничения защиты слабых игроков (см.) распространяется и  на  удержание.\n                            Т.е. в нормальном режиме невозможно стать в удержание к слабому игроку.  Данный\n                            подход выбран для того, что бы уменьшить шанс появления  доминирующего  Альянса\n                            (впрочем, как показывает практика ВСЕХ онлайн-игр -  это  все  равно  случается\n                            рано или поздно.  Монополизация  -  путь  к  глобальному  доминированию!).  Для\n                            неразделяющих такую точку зрения администраторов  в  настройки  сервера  введен\n                            дополнительный пункт,  разрешающий  становится  в  Удержание  к  более  слабому\n                            соаловцу\n                        </p>\n                    </div>\n                </li>\n                <li class=\"chapter\" id=\"chapter_47\">\n                    <h2 class=\"heading\">Прочее</h2>\n                    <div class=\"desc\">\n                        <p>На странице выбора миссии таблица загрузки ресурсов по умолчанию отключена\n                            Добавлено дополнительное сообщение при совпадении  планеты  отправки  и  пункта\n                            назначения\n                        </p>\n                        <p>Добавлено дополнительное сообщение при попытке отправить незагруженный  флот  с\n                            миссией \"Транспорт\"\n                        </p>\n                        <p>Добавлено дополнительное сообщение при попытке отправить  флот  с  ресурсами  в\n                            миссию, отличную от миссий \"Транспорт\", \"Передислокация\" и \"Колонизация\"\n                            Изменены ограничения на отправку Шпионов. Их можно посылать в одиночку в миссии\n                            \"Шпионаж\", \"Передислокация\" и \"Транспорт\". Во все остальные миссии Шпионов тоже\n                            можно отсылать - но только в сопровождении других кораблей\n                        </p>\n                    </div>\n                </li>\n                <li class=\"chapter\" id=\"chapter_48\">\n                    <h2 class=\"heading\">Корабли</h2>\n                    <div class=\"desc\">\n                        <div class=\"sub\">\n                            <h3 class=\"title\">Супертранспорт</h3>\n                            <ul>\n                                <li>\n                                    Более медленная и емкая версия транспортного корабля\n                                    <ul>\n                                        <li>\n                                            <h4>Гипертранспорт</h4>\n                                            Новый корабль - Гипертранспорт. Предназначен для ТОП игроков  и/или  скоростных\n                                            Вселенных\n                                        </li>\n                                    </ul>\n                                </li>\n                            </ul>\n                        </div>\n                    </div>\n                </li>\n                <li class=\"chapter\" id=\"chapter_49\">\n                    <h2 class=\"heading\">Экономика</h2>\n                    <div class=\"desc\">\n                        <p>Новая настройка сервера \"Масштабировать склады от скорости  добычи\".  Настройка\n                            доступна в общих настройках сервера в разделе \"Прочие параметры\". По  умолчанию\n                            возможность включена\n                        </p>\n                        <p>При расчете времени постройки юнитов учитывается не только количество ресурсов,\n                            но и их качество. Время постройки нормированы  по дейтерию,  т.е.  постройки  с\n                            большей долей низкоуровневых ресурсов строятся быстрее\n                            Изменен алгоритм расчетов бонусов добычи ресурсов. Список изменений  приводится\n                            ниже:\n                        </p>\n                        <ul>\n                            <li>1. Бонусы на добычу ресурсов улучшают так же базовую добычу на планете</li>\n                            <li>2. Бонусы на добычу ресурсов так  же  увеличивают  потребление  сопутствующих\n                                ресурсов - дейтерия (для Термоядерной Электростанции)  и  энергии  (для  всех\n                                остальных шахт)\n                            </li>\n                            <li>3. Бонусы на добычу ресурсов улучшают так же выработку энергии на спутниках</li>\n                            <li>4.  Недостаток  энергии  влияет  на  базовую  добычу  -   т.е.   она   теперь\n                                масштабируется в зависимости эффективности производства\n                            </li>\n                            <li>5. Естественное производство дает 100% ресурсов даже при недостатке энергии\n                                Изменен алгоритм работы Термоядерной электростанции. Теперь ТЭС  не  использует\n                                ресурсы со склада, а оперирует только балансом производства дейтерия. Т.е.  ТЭС\n                                работает только при положительном балансе  производства  дейтерия  И  генерации\n                                энергии одновременно. Это сделано для того, что бы оставленная \"без  присмотра\"\n                                ТЭС с отрицательным  балансом  по  дейтерию  не выжрала весь ресурс со склада\n                                Как следствие - ТЭС  не  отключается  при  положительном  балансе  производства\n                                дейтерия и энергии, даже если количество  дейтерия  на  планете  равно  0.  Это\n                                упростит своз ресурсов с планет, на которых энергия генерируется только на ТЭС\n                                Теперь при эффективности  добычи  ресурсов  менее  100%  вместе  с  актуальными\n                                значениями добычи в ячейку добавляется рассчетное  значение  добычи  в  круглых\n                                скобках. Это упростит балансировку производсва при недостатке ресурсов\n                                Убрана задержка в обновлении информации о производстве ресурсов\n                            </li>\n                        </ul>\n                        <div class=\"sub\">\n                            <h3 class=\"title\">Энергия</h3>\n                            <ul>\n                                <li>\n                                    <p>Изменена выработка энергии по сравнению с оффом и RR.\n                                        Во-первых - модификатор скорости игры теперь не действует на выработку энергии.\n                                        Во-вторых - на выработку энергии солнечными электростанциями влияет температура\n                                        планеты.\n                                    </p>\n                                    <p>В-третьих  -  Производство  энергии  на  термоядерной   электростанции   теперь\n                                        считается по формуле оффа:\n                                        30 * [Э] * (1,05 + [Т] * 0,01) ^ [Э]\n                                        где Э - уровень электростанции, Т - уровень энергетической технологии\n                                        В-четвертых - энергетическая технология больше не дает дополнительный  бонус  к\n                                        производительности электроэнергии. Однако  офицер  Энергетик  по-прежнему  дает\n                                        бонус\n                                    </p>\n                                    <p>Выработка энергии изменена исходя из следующих правил:\n                                        1. Электростанция может поддерживать одну шахту и один синтезатор рудника  того\n                                        же уровня (взято с оффа)\n                                        2. Формула выработки термоядерной электростанции взята с оффа\n                                        3. Средняя температура на планете - 20  градусов  (это  планеты  с  минимальной\n                                        температурой  0  градусов  и  максимальной  40  градусов).  На  такой   планете\n                                        эффективность солнечной электростанции будет 100%\n                                    </p>\n                                </li>\n                            </ul>\n                        </div>\n                    </div>\n                </li>\n                <li class=\"chapter\" id=\"chapter_50\">\n                    <h2 class=\"heading\">Исследования</h2>\n                    <div class=\"desc\">\n                        <p>Пересмотрены технологии. Устранены противоречия в  ветках  развития  (например,\n                            ионный  двигатель  можно   было   исследовать   без   технологии).   Технологии\n                            переупорядочены в более логичном порядке\n                            Добавлены подробные сообщения об ошибке в случае,  когда  технология  не  может\n                            быть исследована (нехватка ресурсов, неудовлетворенные требования итд)\n                        </p>\n                        <div class=\"sub\">\n                            <h3 class=\"title\">Лаборатория</h3>\n                            <ul>\n                                <li>\n                                    Полностью переписан интерфейс Лаборатории\n                                    Очередь исследований приведена к стандартному виду\n                                    Обработка  очереди  исследований  теперь  производится  при  каждом  обновлении\n                                    страницы, а не только при входе в интерфейс Лаборатории\n                                    <ul>\n                                        <li>\n                                            <h4>Межгалактическая Исследовательская Сеть</h4>\n                                            Изменена схема работы МИС. Теперь МИС работает следующим образом:\n                                            1. По каждой планете вычисляется эффективный уровень исследования (ЭУИ)\n                                            ЭУИ = уровень лаборатории / (0,5 ^ уровень нанитки)\n                                            2. Планеты сортируются по эффективному уровню\n                                            3. Отсекаются планеты  с  уровенм  лаборатории,  недостаточным  для  проведения\n                                            данного исследования\n                                            4. Выбирается верхние (уровень МИС + 1) планет в списке и суммируется ЭУИ  этих\n                                            планет\n                                            5. Получившееся число подставляется в формулу вычисления времени исследования\n                                            Следствия:\n                                            1. Нанолаборатория теперь увеличивают эффективность лаборатории только  на  той\n                                            планете, на которой они расположены\n                                            2. Время исследования теперь одинаково на всех планетах. На некоторых  планетах\n                                            чуть больше, на некоторых - чуть меньше, но в  среднем  -  лучше,  чем  было\n                                            раньше\n                                            3. Имеет смысл держать  только  (уровень  МИС  +  1)  планет  с  лабораториями.\n                                            Остальные просто не будут подключаться.\n                                            3.1. Примечание к следствию 3 - собственно, так  было  и  раньше  -  все  равно\n                                            исследование могло проводиться только на одной планете\n                                        </li>\n                                        <li>\n                                           <ul>\n                                                <li><h4>Очередь исследований</h4>\n                                                <p>Очередь исследований перенесена с планет на пользователя\n                                                                                                При отмене исследования ресурсы возвращаются на ту планету, с которой были взяты\n                                                                                                Награда за квесты на исследование теперь всегда начисляется на основную планету\n                                                                                                игрока</p>\n                                                <p>Изменен  алгоритм  рассчета  эффективного  уровня  лаборатории  и  необходимого\n                                                                                                времени исследования  при  настройке  сервера  \"Строить  лабораторию  во  время\n                                                                                                исследования: Нет\"\n                                                                                                Теперь при идущем исследовании блокируется  постройка/уничтожение  нано-  и/или\n                                                                                                лабораторий на все планетах</p>\n                                                <p>Теперь  блокируется  попытка  начать  исследование   на   планете,   где   идет\n                                                                                                постройка/уничтожение нано- и/или лабораторий\n                                                                                                Однако возможно начать исследование на другой планете. В таком исследовании  не\n                                                                                                будут  участвовать  все  планеты  где  происходить  модификация   нано-   и/или\n                                                                                                лабораторий. При этом по окончании постройки/уничтожения время исследования  не\n                                                                                                пересчитывается</p></li>\n                                           </ul>\n                                        </li>\n                                        <li>\n                                            <h4>Технологии двигателей</h4>\n                                            Бонус к скорости полета кораблей  теперь  вычисляется  относительно  требуемого\n                                            уровня технологии двигателя. При равной  технологии  пользователя  бонус  равен\n                                            нулю,  при  отличной  -  разнице  уровней  между   требованиями   постройки   и\n                                            пользовательской  умноженной  на  бонус  двигателя.  Если  уровень   технологии\n                                            пользователя меньше, чем требуемый уровень (например, для  кораблей,  купленных\n                                            на  Черном  Рынке),  то  корабль  получает  пенальти  к  скорости,  вычисляемое\n                                            аналогично, но не более 95%\n                                            Пример. Бомбардировщик требует Ионный двигатель 6-го уровня.  Базовая  скорость\n                                            полета корабля - 4.000. Каждый уровень технологии Ионных двигателей дает 20%  к\n                                            скорости полета. Таким образом:\n                                            * При технологии Ионных двигателей 8-го уровня скорость полета составит:\n                                            4.000 * (1 + (8 - 6) * (20 / 100)) = 4.000 * (1 + 2 * 0,2) = 5.600\n                                            * При технологии 6-го уровня - 4.000\n                                            * При технологии 3-го уровня\n                                            4.000 * (1 + (3 - 6) * (20 / 100)) = 4.000 * (1 - 3 * 0,2) = 1.600\n                                            * Без технологии пенальти к уровню будет равно 120%, поэтому вступит  в  силу\n                                            ограничение:\n                                            4.000 * (1 + (0 - 6) * (20 / 100)) = 4.000 * (1 - 0,95) = 200\n                                            Технологии двигателей теперь так же влияют на расход топлива.  Каждый  уровень,\n                                            выше требуемого, уменьшает расход топлива  на  10%  от  бонуса  к  скорости  за\n                                            уровень, но не больше чем 50% от расхода. Каждый  уровень,  ниже  требуемого  -\n                                            увеличивает расход на 20% от бонуса.\n                                            Например, для Бомбардировщика каждый уровень Ионного двигателя, ниже 6-го будет\n                                            увеличивать расход топлива на 4%  до  12%  при  полном  отсутствии  технологии.\n                                            Каждый уровень, выше 6-го будет уменьшать расход топлива на 2%, вплоть до 25-го\n                                            уровня, когда вступит в силу ограничение.\n                                        </li>\n                                    </ul>\n                                </li>\n                            </ul>\n                        </div>\n                    </div>\n                </li>\n                <li class=\"chapter\" id=\"chapter_51\">\n                    <h2 class=\"heading\">Система рейтингования и статистика</h2>\n                    <div class=\"desc\">\n                    </div>\n                </li>\n                <li class=\"chapter\" id=\"chapter_52\">\n                    <h2 class=\"heading\">Статистика</h2>\n                    <div class=\"desc\">\n                        <p>Изменен расчет статистики. Теперь в тратах  каждый  ресурс  считается  согласно\n                            курсу обмена. Таким образом игроки с более  ценными  ресурсами  получат  больше\n                            очков\n                        </p>\n                        <p>Добавлена статистика по ресурсам\n                            Для игроков добавлены отображение следующих видов статистики: \"Проведено боев\",\n                            \"Выиграно  боев\",  \"Проиграно  боев\",  \"Уровень  за  постройки\",  \"Уровень   за\n                            исследования\", \"Уровень за рейдерство\". В качестве исходных данных используется\n                            информация из записей игроков (т.е. актуальная информация на  момент  просмотра\n                            статистики), поэтому изменение для данных типов статистики всегда  будет  равно\n                            нулю\n                        </p>\n                        <p>Полностью  переписана  страница  вывода  статистики  игроков   и   Альянсов   с\n                            использованием PTE\n                            Теперь можно управлять появлением игроков в статистике и рекордах. Для этого на\n                            странице настроек сервера появились дополнительные настройки. Они размещаются в\n                            разделе \"Статистика и рекорды\"\n                        </p>\n                        <p>Отключение настройки \"Прятать админов\" добавит  в  статистику  и  рекорды  всех\n                            пользователей с  authlevel &gt; 0. По умолчанию она включена\n                            Настройка  \"Прятать  игроков\"  позволяет  указать  через  запятую  перечень  ID\n                            игроков, которые не будут участвовать в статистике и рекордах. Это  может  быть\n                            полезно для создания NPC - ботов или  игроков,  которые  исполняют  их роли\n                            Так же в этот раздел вынесена настройка расписания  автоматического  обновления\n                            статистики.  ВНИМАНИЕ!!!   КРАЙНЕ   НЕ   РЕКОМЕНДУЕТСЯ   МЕНЯТЬ   ЗНАЧЕНИЕ   ПО\n                            УМОЛЧАНИЮ!!!\n                        </p>\n                        <p> Добавлена опция \"Скрывать ссылки на ЛС\". При её включении в таблице  статистики\n                            не показывается URL на создание личного сообщения игрокам\n                            Теперь  переход  по  определенной  позиции  (например  со  страницы  Вселенной)\n                            скроллирует страницу сразу на эту позицию\n                            Немного уменьшен размер страницы статистики\n                        </p>\n                    </div>\n                </li>\n                <li class=\"chapter\" id=\"chapter_53\">\n                    <h2 class=\"heading\">Защита \"начинающих\" и \"слабых\" игроков</h2>\n                    <div class=\"desc\">\n                        <p>Полностью переписан алгоритм защиты игроков от нападения более сильного игрока.\n                            Возьмем двух игроков - Игрок1 и Игрок2. Если у Игрока1 количество  очков  общей\n                            статистики в game_noob_factor (по умолчанию - 5) раз больше, чем у Игрока2,  то\n                            Игрок1 по  отношению  к  Игроку2  считается  \"сильным\"  игроком,  а  Игрок2  по\n                            отношению к Игроку1  считается  \"слабым\"  игроком.  \"Слабые\"  игроки  полностью\n                            защищены от агрессивных действий со стороны \"сильных\" - на них нельзя нападать,\n                            нельзя шпионить, нельзя уничтожать луну итд. \"Слабый\" игрок может  нападать  на\n                            \"сильных\"\n                        </p>\n                        <p>Установка game_noob_factor в 0 отключает защиту \"слабых\" игроков\n                            Игроки,  у  которых  количество  очков  в   общей   статистике   не   превышает\n                            game_noob_points (по умолчанию  5.000)  считаются  \"начинающими\"  игроками.  На\n                            \"начинающего\" игрока может напасть только другой \"начинающий\"  (естественно,  с\n                            учетом соотношения \"слабый-сильный\", см. выше). \"Начинающий\" может нападать  на\n                            кого угодно.\n                        </p>\n                        <p>Установка game_noob_points в 0 отключает защиту \"начинающих\" игроков</p>\n                    </div>\n                </li>\n                <li class=\"chapter\" id=\"chapter_54\">\n                    <h2 class=\"heading\">Прокачка</h2>\n                    <div class=\"desc\">\n                        <p>Под \"прокачкой\" понимается пересылка ресурсов от слабого игрока к  сильному.  В\n                            СН встроена система  защиты  от  прямой  прокачки  (прямая  пересылка  ресурсов\n                            миссией \"Транспорт\"). Однако вместе с  позитивными  моментами  защита  несет  и\n                            негативные моменты - фактически исключен обмен ресурсами между игроками, потому\n                            что в обмене в  подавляющем  большинстве  случаев  один  игрок  является  более\n                            сильным,  а  второй  -  более  слабым.  С  моей   точки   зрения   практическая\n                            невозможность обмена  ресурсами  вполне  компенсируется  невозможностью  прямой\n                            прокачки (существуют, правда, способы непрямой  прокачки,  которые  я  не  буду\n                            здесь офишировать в силу  общедоступности  движка  вообще  и  данного  файла  в\n                            частности любым игрокам). Однако  для  администраторов,  не  разделяющих  такую\n                            точку зрения, добавлена возможность разрешения прокачки в  настройках  сервера.\n                            Соответствующие установки настройки видны игрокам на странице сервера  \"Мировые\n                            константы\"\n                        </p>\n                    </div>\n                </li>\n                <li class=\"chapter\" id=\"chapter_55\">\n                    <h2 class=\"heading\">Квесты</h2>\n                    <div class=\"desc\">\n                        <p>В СН реализована подсистема квестов. Администратор сервера может самостоятельно\n                            добавлять новые квесты и выбирать  награду  за  его  исполнение:  фиксированное\n                            количество одного или любой комбинации ресурсов: металл, кристалл, дейтерий или\n                            ТМ. Доступ к конструктору квестов  осуществляется  из  меню  \"Квесты\"  страницы\n                            администрирования. Создание  квестов  доступно  только  Администратору  сервера\n                            (auth_level = 3). Администратор может посмотреть выполненные квесты  игрока  по\n                            ссылке в профиле игрока (поиск через админпанель)\n                        </p>\n                        <p>Доступны квесты на постройку зданий и обороны, на исследование и  на  постройку\n                            кораблей.   Игроки,   превысившие   условия   квеста,   автоматически   получат\n                            вознаграждение при следующей проверке на критерии  выполнения.  Например,  если\n                            целью квеста является постройка шахты 10го уровня, то при постройке шахты  выше\n                            9го уровня на любой планете игрок получит квестовое вознаграждение. То же самое\n                            верно и по отношению к уничтожению шахты. Однако, если при уничтожении шахты её\n                            уровень окажется ниже 10го, то игрок вознаграждения не получит, хотя он  уже  и\n                            имел  шахту  10го  уровня.\n                        </p>\n                        <p>Триггер на условие квеста \"постройка кораблей\" является локальным и срабатывает\n                            при постройке указанного корабля. Т.е. если есть квест на постройку 10 ЛИ, а на\n                            планете есть 20  ЛИ,  то  вознаграждение  квеста  игрок  получит  только  после\n                            постройки 1 ЛИ. \"Локальность\" означает, что  если  в  указанном  примере  игрок\n                            имеет 10 ЛИ на всех колониях, но на одной колонии их количество меньше  условия\n                            квеста, то вознаграждение за квест он получит только если в момент постройки ЛИ\n                            их количество на одной планете будет равно 10.\n                        </p>\n                        <p>Игрок может просматривать список доступных квестов и их статус (выполнен или не\n                            выполнен). По выполнению квеста игроку высылается письмо с уведомлением.  Общее\n                            количество и количество выполненных квестов видно игроку в навбаре\n                        </p>\n                    </div>\n                </li>\n                <li class=\"chapter\" id=\"chapter_56\">\n                    <h2 class=\"heading\">Модуль новостей</h2>\n                    <div class=\"desc\">\n                        <p> Добавлен полноценный модуль новостей, доступный по  ссылке  \"Новости\"  в  левом\n                            меню. Доступ к управлению новостями админы получают по той же  ссылке  -  права\n                            определяются по authlevel\n                        </p>\n                        <p>К пункту меню \"Новости\" добавлен индикатор непрочитанных сообщений\n                            Добавлена дополнительная строка в админском интерфейсе, в которую  можно  вбить\n                            URL с подробностями новости. URL добавится в конце новости в  виде  гиперссылки\n                            со слова \"Подробнее...\" из текста новости\n                        </p>\n                        <p>Добавлена возможность массовой рассылки новости всем игрокам\n                            Добавлена лента новостей на страницу обзора планеты. Выводятся только последние\n                            непрочитанные новости. Количество выводимых новостей настраивается  на  сервере\n                            (по умолчанию - 3)\n                        </p>\n                        <p>Количество новостей ограничено 20-ю самыми свежими\n                            На странице Обзора планеты добавлена подсказка  как  закрыть  окно  со  свежими\n                            новостями\n                        </p>\n                        <div class=\"sub\">\n                            <h3 class=\"title\">Редактирование новости</h3>\n                            <ul>\n                                <li>\n                                    <p>При редактировании новости галочка рассылки новости по умолчанию отключена\n                                        При редактировании новости не изменяется глобальное время  написания  последней\n                                        новости - т.е. отредактированная новость не включает список последних  новостей\n                                        на обзоре планеты\n                                    </p>\n                                </li>\n                            </ul>\n                        </div>\n                    </div>\n                </li>\n                <li class=\"chapter\" id=\"chapter_57\">\n                    <h2 class=\"heading\">Игроки</h2>\n                    <div class=\"desc\">\n                        <p>Добавлено  отображение  пола  игрока  на  странице  \"Император\",  на   странице\n                            статистики и в попапе игрока на странице \"Вселенная\"\n                        </p>\n                        <div class=\"sub\">\n                            <h3 class=\"title\">Аватар</h3>\n                            <ul>\n                                <li>\n                                    <p>Добавлена поддержка серверных аватаров игроков\n                                        Аватары  могут  быть  загружены  с  локального  диска  на   странице   настроек\n                                        пользователя.\n                                    </p>\n                                    <p>Поддерживаются файлы форматов JPG, GIF и PNG  размером  до  200КБ.  Загруженные\n                                        картинки будут отмасштабированы до размеров 128х128.\n                                        Аватар отображается на странице \"Император\"  и  в  попапе  игрока  на  странице\n                                        \"Вселенная\".\n                                    </p>\n                                </li>\n                            </ul>\n                        </div>\n                    </div>\n                </li>\n                <li class=\"chapter\" id=\"chapter_58\">\n                    <h2 class=\"heading\">Альянсы</h2>\n                    <div class=\"desc\">\n                        <p>Полностью  переписан  модуль  управления   Альянсами.   Кардинально   сокращено\n                            количество прав, упрощен интерфейс, переписана система заявок и так далее.\n                        </p>\n                        <div class=\"sub\">\n                            <h3 class=\"title\">Логотип Альянса</h3>\n                            <ul>\n                                <li>\n                                    Добавлена поддержка серверных логотипов  Альянсов\n                                    Логотипы могут  быть  загружены  с  локального  диска  на  странице  управления\n                                    Альянсом\n                                    Поддерживаются файлы форматов JPG, GIF и PNG  размером  до  200КБ.  Загруженные\n                                    картинки будут отмасштабированы до размеров 128х128\n                                    Логотип отображается на странице информации об Альянсе и в  попапе  Альянса  на\n                                    странице \"Вселенная\"\n                                    <ul>\n                                        <li>\n                                            <h4>Дипломатия</h4>\n                                            <p>СН содержит подсистему дипломатии Альянсов. Дипломатия позволяет  устанавливать\n                                                                                        различные  типы  отношений  между  Альянсами.  Некоторые  отношения  влияют  на\n                                                                                        внутриигровую механику, некоторые - нет\n                                                                                        Информация  о  текущих  дипломатических  отношениях  Альянса  отображается   на\n                                                                                        странице информации об Альянсе и доступна для просмотра любому игроку\n                                                                                        Глава Альянса может начинать  переговоры  и  принимать  предложения  от  других\n                                                                                        Альянсов, выбрав пункт \"Переговоры\" в  заголовке  таблицы  дипломатии.  Там  же\n                                                                                        можно сделать  предложение  об  изменении  отношений  другому  Альянсу.  Нельзя\n                                                                                        сделать предложение текущих отношений (т.е. если Альянсы находятся в отношениях\n                                                                                        \"Война\" нельзя  опять  предложить  отношение  \"Война\").</p>\n                                            <p>В общем случае что бы изменилось  отношения  между  Альянсами,  другая  сторона\n                                                                                        должна потвердить предложение об изменении по  ссылке  \"Переговоры\",  доступной\n                                                                                        главе Альянса (исключения из данного правила изложены ниже).\n                                                                                        Отношения между Альянсами бывают следующие:</p>\n                                            <ul>\n                                               <li> 1. Нейтралитет. Отношение по умолчанию. Нет никаких ограничений или бонусов</li>\n                                            <li>2.  Война.  Отключается  система  защиты   башинга   между   членами   Альянса,\n                                                                                        находящимися в состоянии  войны.  Автоматически  принимается  второй  стороной.\n                                                                                        Подробнее см.ниже в описании защиты от башинга</li>\n                                            <li>3.  Мир.  Рекомендуется  выставлять  этот  статус  после  заключения  пакта   о\n                                                                                        ненападении. С точки зрения движка оно ничем не отличается от \"Нейтралитета\"  и\n                                                                                        нужно  для  информирования  остального  игрового  сообщества  о  неких   устных\n                                                                                        договоренностях - буде в таком информировании возникнет нужда.  Альянсы  вольны\n                                                                                        следовать или не следовать данной рекомендации, а так же  решать  -  хотят  они\n                                                                                        оповестить Вселенную об изменении своих отношений или нет</li>\n                                            </ul>\n                                        </li>\n                                    </ul>\n                                </li>\n                            </ul>\n                        </div>\n                    </div>\n                </li>\n                <li class=\"chapter\" id=\"chapter_59\">\n                    <h2 class=\"heading\">[#] Технологии и Наемники Альянсов</h2>\n                    <div class=\"desc\">\n                        <p>ДАННЫЙ МОДУЛЬ НЕ ВХОДИТ В СОСТАВ ПУБЛИЧНОЙ ВЕРСИИ!\n                            Альянсы теперь могут рекрутировать Наемников и исследовать технологии!\n                        </p>\n                        <div class=\"sub\">\n                            <h3 class=\"title\">Счет Альянса</h3>\n                            <ul>\n                                <li>\n                                    <p>Каждый Альянс имеет счет с ресурсами металл/кристалл/дейтерий/ТМ\n                                                                        Ресурсы со счета Альянса могут расходоваться только  на  нужды  Альянса.  Вывод\n                                                                        ресурсов со счета Альянса невозможен</p>\n                                    <p>Член Альянса может перевести ресурсы на счет  Альянса.  Сделать  это  можно  на\n                                                                        главной странице Альянса в разделе \"Ресурсы  Альянса\".  Там  же  можно  увидеть\n                                                                        состояние счета Альянса и бонусы, предоставляемые Альянсом (см. ниже).\n                                                                        На ряде страниц (в частности - на странице Исследований) в  верхнем  ресурсбаре\n                                                                        показывается не количество ресурсов на планете игрока,  а  количество  ресурсов\n                                                                        Альянса</p>\n                                    <p>Глава  Альянса  с  его  счета  может  исследовать  технологии  и  рекрутировать\n                                                                        Наемников - соответственно пункты \"Исследования Альянса\" и  \"Наемники  Альянса\"\n                                                                        на странице управления</p>\n                                    <ul>\n                                        <li>\n                                            <h4>Исследования Альянса</h4>\n                                            При исследовании технологии уровень  лаборатории  равен  количеству  игроков  в\n                                            Альянсе на момент  начала  исследования.\n                                        </li>\n                                        Активные исследования видны членам Альянса на странице информации\n                                        <li>\n                                            <h4>Бонус Альянса</h4>\n                                            После  достижения  минимально  необходимого  размера  Альянса  (10  человек  по\n                                            умолчанию, задается в таблице 'config' записью 'ali_bonus_members') каждый член\n                                            Альянса получает бонус к своим Наемникам и технологиям\n                                            Бонусы от Наемников и Технологий так же действуют  при  проверки  требований  к\n                                            постройкам/исследованиям. Например: игрок состоит в Альянсе, дающем бонус +2  к\n                                            Лазерной технологии, а его собственный уровень технологии равен 4.  Эффективный\n                                            уровень технологии этого игрока равен 6. Это означает, что находясь  в  Альянсе\n                                            он имеет доступ к исследованию Ионной технологии (требуется ЛТ 5-го  уровня)  и\n                                            может строить Тяжелый Лазер (требуется ЛТ 6-го уровня). Очевидно, если  бы  он\n                                            не находился в Альянсе, эти постройки были бы заблокированы\n                                        </li>\n                                        <li>\n                                            <h4>Стандартный алгоритм расчета</h4>\n                                            Данный алгоритм включен по умолчанию (переменная ali_bonus_algorithm в  таблице\n                                            config установлена в 0).\n                                            Значение бонуса зависит от  количества  игроков  в  Альянсе  и  вычисляется  по\n                                            формуле:\n                                            Бонус = round(уровень технологии или Наемника / количество игроков),\n                                            где round() - операция математического округления.\n                                            Примеры:\n                                            1. Альянс из 10 человек купил Технологию 4 уровня:\n                                            Бонус = round(4/10) = round(0,4) = 0\n                                            2. Альянс из 10 человек купил Технологию 7 уровня:\n                                            Бонус = round(7/10) = round(0,7) = 1\n                                        </li>\n                                        <li>\n                                            <ul>\n                                                <li>\n                                                    <h4>Почему все работает именно так?</h4>\n                                            Выбранная  механика  бонусов  Альянса  призван  обеспечить   достижение   сразу\n                                            нескольких целей:\n                                            <ul>\n                                                <li>1. Исключить   злоупотребление  фишкой,  когда  2-3  игрока  формируют   Альянс\n                                                                                            исключительно для получения бонусов</li>\n                                            <li>2. Активизировать межальянсную активность:  бонусы  от  ресусов  Альянса  можно\n                                                                                        получить только начиная с определенного  количества  участников.  Ну  и  чем\n                                                                                        больше игроков в Альянсе, тем больше у него ресурсов</li>\n                                            <li>3. Усилить лояльность игроков к Альянсу - при выходе (или  выгоне)  из  Альянса\n                                                                                        игрок теряет все бонусы и (самое неприятное) все ресурсы,  пожертвованные  в\n                                                                                        Альянс</li>\n                                           <li> 4. Исключить появление мегаальянсов: чем больше игроков - тем  больше  ресурсов\n                                                                                       они могут пожертвовать, но  тем  меньше  бонусов  получит  каждый  отдельный\n                                                                                       игрок</li>\n                                            <li>5. Слабые игроки в сильных Альянсах получают доступ к  end-game  юнитам  (если,\n                                                                                        конечно, глава Альянса решит потратить ТМ на  соответствующих  Наемников)  и\n                                                                                        бонус в развитии</li>\n                                            <li>6. Сильные игроки смогут поднять эффективные уровни Технологий даже  в  больших\n                                                                                        Альянсах. Например, если в Альянсе 15 человек,  то  исследовать  15  уровень\n                                                                                        технологии всем Альянсом будет проще и дешевле, чем каждому игроку отдельно</li>\n                                            <li>7. То же самое распространяется и на Наемников. При  этом  только  Альянс  дает\n                                                                                        возможность получить эффективный уровень Наемников больше максимального\n                                                                                        Выбранный метод расчета  бонусов  позволяет  создавать  оптимальную  среду  для\n                                                                                        Альянсов. Регулируя минимальное  количество  участников  для  получения  бонуса\n                                                                                        можно косвенно регулировать размер Альянса.\n                                                                                        Оптимальный размер Альянса будет равен:</li>\n                                            </ul>\n\n                                           <p> floor(\n                                                                                       &lt;минимальный размер&gt;\n                                                                                       * 1,5)\n                                                                                       где floor() - операция отбрасывания дробной части\n                                                                                       Однако, возможны варианты. В  движке  реализованы  три  метода  расчета  бонуса\n                                                                                       Альянса:  по  количеству  членов  Альянса  (описанный  выше,  используется   по\n                                                                                       умолчанию); по минимальному размеру Альянса; по очкам Альянса; по месту Альянса\n                                                                                       в статистике.</p>\n                                                </li>\n                                            </ul>\n\n                                        </li>\n                                        <li>\n                                            <ul>\n                                                <li>\n                                                    <h4>Расчет по минимальному размеру Альянса</h4>\n                                            Данный алгоритм включается установкой переменной ali_bonus_algorithm в  таблице\n                                            config в 1.\n                                            Значение бонуса вычисляется  по формуле:\n                                            Бонус = round(\n                                            &lt;уровень&gt;\n                                            *\n                                            &lt;участники&gt;\n                                            / sqr(\n                                            &lt;минимальный размер&gt;\n                                            )),\n                                            где\n                                            round() - операция математического округления,\n                                            sqr() - операция возведения в квадрат\n                                            &lt;уровень&gt;\n                                            - уровень технологии или Наемника,\n                                            &lt;участники&gt;\n                                            - количиство игроков в Альянсе,\n                                            &lt;минимальный размер&gt;\n                                            - минимальный размер Альянса для получения бонуса.\n                                            Примеры для параметров по умолчанию:\n                                            1. Альянс из 10 человек купил Технологию 4 уровня:\n                                            Бонус = round(4 * 10 / 100) = round(0,4) = 0\n                                            2. Альянс из 10 человек купил Технологию 17 уровня:\n                                            Бонус = round(10 * 10 / 100) = round(1,7) = 2\n                                            3. Альянс из 20 человек купил Технологию 17 уровня:\n                                            Бонус = floor(17 * 20 / 100) = round(3,4) = 3\n                                            При использовании этого  метода  нужно  уделять  особое  внимание  минимальному\n                                            размеру Альянса. Небольшой размер (6 и меньше)  будет  давать  ускоренный  рост\n                                            бонуса в зависимости от количества членов Альянса -  и  спровоцирует  появление\n                                            мегаальянсов. С другой стороны, большой размер (13 и выше) сделает  практически\n                                            бесполезным  исследования  и  рекрутинг  Наемников,  поскольку  прирост  бонуса\n                                            требует  либа  создание   ГИГАальянса,   либо   недостижимо   высоких   уровней\n                                            технологий/Наемников.\n                                            Этот метод рекомендуется:\n                                            1. При стандартных настойках (скорость игры - 1х-3х, мин.размер  -  10)  -  для\n                                            небольших и средних серверов.\n                                            2. Мин.размер порядка 8 - для быстрых (10х и выше) PvP серверов.\n                                            3. Мин.размер порядка 12 - для больших серверов  с  околостандартной  скоростью\n                                            игры (1x-3x).\n                                                </li>\n                                            </ul>\n                                        </li>\n                                        <li>\n                                            <ul>\n                                                <li>\n                                                    <h4>Расчет по очкам Альянса</h4>\n                                            Данный алгоритм включается установкой переменной ali_bonus_algorithm в  таблице\n                                            config в 2.\n                                            Значение бонуса вычисляется по формуле:\n                                            Бонус = round(\n                                            &lt;уровень&gt;\n                                            *\n                                            &lt;очки&gt;\n                                            /\n                                            &lt;количество игроков&gt;\n                                            /\n                                            &lt;делитель&gt;\n                                            ),\n                                            где\n                                            round() - операция математического округления,\n                                            &lt;уровень&gt;\n                                            - уровень технологии или Наемника,\n                                            &lt;очки&gt;\n                                            - количество очков Альянса,\n                                            &lt;количество игроков&gt;\n                                            - количество игроков в Альянсе,\n                                            &lt;делитель&gt;\n                                            - задается переменной ali_bonus_divisor  в  таблице  config,  по\n                                            умолчанию равен 10.000.000\n                                            Данный метод следует использовать с осторожностью и  перед  его  использованием\n                                            тщательно  исследовать  статистику  сервер  для  выбора  оптимального  значения\n                                            ali_bonus_divisor. Кроме того, данный метод поощрает \"кучкование\" топов в одном\n                                            Альянсе.\n                                                </li>\n                                            </ul>\n                                        </li>\n                                        <li>\n                                            <ul>\n                                                <li>\n                                                    <h4>Расчет по месту Альянса в статистике</h4>\n                                            Данный алгоритм включается установкой переменной ali_bonus_algorithm в  таблице\n                                            config в 3.\n                                            Значение бонуса  вычисляется  по  достаточно  сложной  формуле,  которую  можно\n                                            посмотреть в исходном коде. Здесь же я приведу словесное описание.\n                                            1. Рейтинг Альянсов разбивается на  определенное  количество  \"категорий\".  Оно\n                                            задается переменной ali_bonus_brackets в таблице config и по умолчанию равно\n                                            10. Т.е. динамически строится таблица уровней.\n                                            2. Определяется, в какую из категорий попал текущий Альянс и в  соответствии  с\n                                            этим ему присваивается \"уровень\".\n                                            3. В зависимости от уровня назначается коэфициент бонуса. Если Альянс находится\n                                            в   первой   категории   -   ему   устанавливается   коэфициент   10   (т.е.\n                                            ali_bonus_brackets), во второй - 9 и так далее  до  последней  категории,  в\n                                            которой присваивается коэфициент 1.\n                                            4. Полученный  коэфициент  делится  на  ali_bonus_brackets_divisor  из  таблицы\n                                            config   (50   по   умолчанию).    Фактически,    ali_bonus_brackets_divisor\n                                            контролирует   как   сильно   уровень   Альянса   влияет   на    бонус    от\n                                            технологии/Наемника.\n                                            5. Бонус  Альянса  получается  умножением  результата  предыдущих  операций  на\n                                            уровень технологии или Наемника.\n                                            Пример 1\n                                            Настройки по умолчанию. Альянс занмает 5 место из 80.\n                                            1. Размер категории = 80/10 = 8. Т.е. первый уровень - Альянсы с рейтингом от 1\n                                            до 9, второй уровень - от 10 до 18 итд.\n                                            2. Наш Альянс попал в первую категорию - его уровень равен 1.\n                                            3. Его коэфициент бонуса равен 10.\n                                            4. Множитель бонуса = 10/50 = 0,2.\n                                            5. Таким образом бонус будет равен 20% от уровня технологии/Наемника.\n                                            До 2го уровня - бонус будет равен нулю.\n                                            С 3го по 7й - бонус равен 1.\n                                            С 8го по 12й - бонус равен 2.\n                                            И так далее\n                                            Пример 2\n                                            5 категорий, делитель 50. Альянс занмает 50 место из 80.\n                                            1. Размер категории = 80/5 = 16. Таблица уровней: 1-16, 17-32, 33-48, 49-64,\n                                            65-80.\n                                            2. Альянс попал в 4ю категорию, уровень равен 4.\n                                            3. Коэфициент бонуса равен 2.\n                                            4. Множитель бонуса = 2/50 = 0,04\n                                            5. Таким образом бонус будет равен 4% от уровня технологии/Наемника.\n                                            До 12го уровня - бонус будет равен нулю.\n                                            С 13го по 37й - бонус равен 1.\n                                            С 38го по 62й - бонус равен 2.\n                                            И так далее\n                                            На последнем примере демонстрируется важность правильного подбора делителя  под\n                                            количество категорий.\n                                                </li>\n                                            </ul>\n                                        </li>\n                                    </ul>\n                                </li>\n                            </ul>\n                        </div>\n                    </div>\n                </li>\n                <li class=\"chapter\" id=\"chapter_60\">\n                    <h2 class=\"heading\">Башинг</h2>\n                    <div class=\"desc\">\n                        <div class=\"sub\">\n                            <h3 class=\"title\">Определения</h3>\n                            <ul>\n                                <li>\n                                   <p> Атака - отправка флота с  агрессивной  миссией  (\"Атака\",  \"Совместная  атака\",\n                                                                       \"Уничтожение\") на объект Вселенной (планету или луну) другого игрока\n                                                                       Башинг - многократная повторная атака одним игроком одного и  того  же  объекта\n                                                                       Вселенной за некоторый промежуток времени. Этот  промежуток  называется  \"окном\n                                                                       башинга\". Обычно окно башинга принимается равным 24 часам</p>\n                                   <p> Волна - несколько флотов, посланные один  за  другим  за  небольшой  промежуток\n                                                                       времени. Этот промежуток называется \"окно волны\" и обычно его принимают  равным\n                                                                       30 минутам.\n                                                                       Как правило, количество флотов  в  одной  волне  так  же  ограничивают,  а  все\n                                                                       следующие флоты относят к новой волне. Максимальное числов флотов в одной волне\n                                                                       называется \"длина волны\". Традиционно длину волны принимают равной 3 флотам.</p>\n                                    <ul>\n                                        <li>\n                                            <h4>Антибашинг</h4>\n                                            <p>СН содержит систему защиты от  башинга  \"Антибашинг\".  Защита  не  дает  игроку\n                                                                                        отправить на одну планету больше боевых флотов, чем предусмотрено правилами.\n                                                                                        Изменить параметры Антибашинга можно на странице  настроек  сервера.  Установка\n                                                                                        количества атак в одной волне в 0 означает  полное отключение системы защиты\n                                                                                        Настройки системы по умолчанию: 3 волны по 3 атаки c промежутком  не  более  30\n                                                                                        минут между атаками в одной волне за 24 часа</p>\n                                            <p>Антибашинг учитывает флоты в полете. Т.е. если игрок уже запустил две  волны  и\n                                                                                        еще одна находится в полете - он больше не сможет запускать флоты\n                                                                                        Атаки засчитываются по факту - т.е. если полностью отменить волну,  находящуюся\n                                                                                        в полете, игрок сразу же сможет послать на планету новые  флоты,  не  дожидаясь\n                                                                                        возвращения ранее отправленных флотов. Частичная отмена  волны  может  изменить\n                                                                                        распределение следующих флотов по волнам.</p>\n                                            Атаки учитываются вне зависимости от результата (выигрыш, ничья, проигрыш)\n                                           <p> При САБе атака засчитывается ВСЕМ нападающим - дабы избежать  \"карусели\",  т.е.\n                                                                                       когда  несколько  игроков  по  очереди  запускают  САБы,  а  остальные  к   ним\n                                                                                       присоединяются. При этом все флоты одного игрока в  одном  САБе  считаются  как\n                                                                                       одна атака\n                                                                                       Добавлена  возможность  настройки  системы  антибашинга  на  страницу  настроек\n                                                                                       сервера</p>\n                                            <p>Если Альянсы находятся в отношении \"Война\",  защита  от  башинга  не  работает.\n                                                                                        Однако объявление войны отключает Антибашинг не сразу, а лишь спустя  12  часов\n                                                                                        (данный параметр настраивается по усмотрению администратора сервера)\n                                                                                        Объявление войны  не  требует  согласия.  Это  означает,  что  когда  Альянс  А\n                                                                                        предложил  Альянсу   Б   отношение   \"Война\",   это   предложение   принимается\n                                                                                        автоматически и отношения устанавливаются сразу для обоих Альянсов. А вот выход\n                                                                                        из состояния войны (т.е. смена  отношения  \"Война\"  на  любое  другое)  требует\n                                                                                        согласия обоих сторон.</p>\n                                            <p>Выход из состояния войны обратной силы  не  имеет!  Т.е.  если  было  объявлено\n                                                                                        перемирие когда планеты одного из Альянсов находятся под атакой, то  флоты  все\n                                                                                        равно долетят и совершат нападение - какое бы ни  было  новое  отношение  между\n                                                                                        Альянсами (если, конечно, атакующий их не отзовет)</p>\n                                        </li>\n                                        <li>\n                                            <h4>Тривиа</h4>\n                                            А вы знаете почему такой относительно простой вещи (40+ человекочасов)  нет  на\n                                            Оффе? А что бы денежки снимать за разбан!\n                                        </li>\n                                    </ul>\n                                </li>\n                            </ul>\n                        </div>\n                    </div>\n                </li>\n                <li class=\"chapter\" id=\"chapter_61\">\n                    <h2 class=\"heading\">АнтиРМФ</h2>\n                    <div class=\"desc\">\n                        Если флот атакующего уничтожен за один раунд, то:\n                        <ul>\n                            <li>1. Атакующий не получает отчета о бое</li>\n                            <li>2. Флоты, находящиеся в удержании так и остаются на орбите\n                                Изменена процедура генерации писем с уведомлением  о  боевом  отчете  следующим\n                                образом:\n                            </li>\n                            <li>1. Если бой закончился за один раунд проигрышем атакующего,  то  он  получает\n                                сообщение о том,  что  связь  с  флотом  прервалась  и  не  получает  никакой\n                                дополнительной информации (включая ссылку на боевой отчет)\n                            </li>\n                            <li>2. Теперь все  участники  боя  (включая  членов  САБа  и  хозяинов  флотов  в\n                                удержании) получают одинаковые письма (кроме случая, описанного в п.1)\n                                3. Уведомление о бое  всегда  содержит  потери  атакующих  и  оброняющихся  и\n                                сведения о поле обломков\n                            </li>\n                            <li>4. Сведения о вывозе ресурсов  с  планеты  добавляются  в  отчет  только  при\n                                выигрыше атакующих\n                            </li>\n                            <li>5.  Уведомления  теперь  корректно  окрашиваются  для  всех  участников  боя:\n                                красным, если участник проиграл, зеленым - если выиграл, оранжевым - в случае\n                                ничьи\n                            </li>\n                            <li>6. Все числа в уведомлении теперь форматируются</li>\n                        </ul>\n                    </div>\n                </li>\n                <li class=\"chapter\" id=\"chapter_62\">\n                    <h2 class=\"heading\">НоваПедия</h2>\n                    <div class=\"desc\">\n                        <p>\"Ракетный двигатель\" переименован в \"Химический\", а \"Импульсный\" - в  \"Ионный\".\n                            Для них полностью изменено описание. Так же  изменено  соответствующе  описание\n                            кораблей\n                        </p>\n                        <p> Полностью написана с нуля страница информации о юнитах\n                            Теперь в Новапедии показываются требования для постройки/исследования юнита\n                            Теперь для корабля показываются  данные  для  всех  типов  двигателей,  которые\n                            возможно на него установить\n                        </p>\n                        <p>Улучшено отображение информации о кораблях и обороне</p>\n                        <div class=\"sub\">\n                            <h3 class=\"title\">Дерево технологий</h3>\n                            <ul>\n                                <li>\n                                    Полностью переписано дерево технологий (бывш. techtree.php)\n                                    Рядом с названиями юнитов там, где это имеет смысл, отображаются  их  уровни  в\n                                    Империи/на текущей планете\n                                    Теперь вместо полного уровня с учетом  бонусов  отображаются  отдельно  базовые\n                                    уровни и отдельно бонус к ним\n                                    <ul>\n                                        <li>\n                                            <h4>Добавлена поддержка дополнительных требований к строительству юнитов  (например</h4>\n                                            требование определенной расы для модуля расовых юнитов)\n                                        </li>\n                                    </ul>\n                                </li>\n                            </ul>\n                        </div>\n                    </div>\n                </li>\n                <li class=\"chapter\" id=\"chapter_63\">\n                    <h2 class=\"heading\">Заметки</h2>\n                    <div class=\"desc\">\n                        С нуля написаны заметки\n                    </div>\n                </li>\n                <li class=\"chapter\" id=\"chapter_64\">\n                    <h2 class=\"heading\">Друзья</h2>\n                    <div class=\"desc\">\n                        <p>Страница друзей написана с нуля\n                            Теперь подробно сообщается обо всех ошибках и результатах операций с заявками\n                            В личную почту отправляются сообщения по приходу, принятию и отверганию заявки,\n                            а так же при разрыве дружеских отношений\n                        </p>\n                        <p>Цветовое кодирование статуса друга: зеленый - онлайн, желтый - бездействие от 5\n                            до 15 минут, оранжевый - оффлайн, красный - оффлайн более суток\n                        </p>\n                    </div>\n                </li>\n                <li class=\"chapter\" id=\"chapter_65\">\n                    <h2 class=\"heading\">Закладки</h2>\n                    <div class=\"desc\">\n                        <p> Полностью переписана система закладок. Теперь  закладки  хранятся  в  отдельной\n                            таблице и не захламляют данные пользователя.  Кроме  того,  такое  расположение\n                            закладок повышает устойчивость движка к SQL-injection.\n                        </p>\n                        <p>Полностью переделано редактирование закладок. Добавлена возможность копирования\n                            закладок\n                        </p>\n                    </div>\n                </li>\n                <li class=\"chapter\" id=\"chapter_66\">\n                    <h2 class=\"heading\">Сообщения</h2>\n                    <div class=\"desc\">\n                        <p>Полностью  переписана  система  сообщений\n                            Можно  писать  письма  любому  игроку!  Форма  создания  нового  письма  теперь\n                            корректно  обрабатывает  имена  игроков,  введенных  в   строку   \"Кому\".   Эта\n                            возможность  доступна  из  списка  категорий  сообщений  по  ссылке   \"Написать\n                            сообщение\" в  самом  низу  таблицы  категорий.  При  первом  открытии  страницы\n                            создания нового сообщения больше не выскакивают угрожающие красные надписи\n                            В списке писем теперь работает чекбокс в заголовке.  Клик  на  нем  приведет  к\n                            выбору всех сообщений. Повторный клик - к снятию всех отметок\n                            Добавлен дополнительный диапазон для удаления сообщений - \"Все сообщения данной\n                            категории\". Внимание! В категории \"Все сообщения\" его выбор приведет  к  полной\n                            очистке почтового ящика!\n                        </p>\n                        <p>Счетчик сообщений в навбаре работает без задержек. Т.е. если  игрок  перешел  в\n                            категорию  с  непрочитанными  сообщениями,  счетчик  изменится  соответствующим\n                            образом сразу же после перехода, а не при следующей загрузке страницы\n                            Клик на индикаторе сообщений Администрации, Альянса или от другого игрока сразу\n                            открывает просмотр соответствующих сообщений\n                            Оптимизированы алгоритмы работы подсистемы сообщений, а так же почти в два раза\n                            уменьшен объем передаваемой информации  от  клиента  к  серверу.  Особенно  это\n                            заметно при удалении большого количества сообщений\n                            Добавлена возможность просмотра отправленных сообщений (только тех, которые  не\n                            удалил адресат)\n                        </p>\n                        <p>Теперь можно сразу из отчета  шпиона  запускать  симуляцию  боя  по  полученным\n                            разведданным\n                            Перекрашены сообщения. Цвета заголовков теперь соответствуют  цветам  сообщений\n                            и, в свою очередь, синхронизированны с раскраской флотов - там, где  это  имеет\n                            смысл\n                            Категории  сообщений переупорядочены\n                        </p>\n                        <div class=\"sub\">\n                            <h3 class=\"title\">Добавлена возможность очистить сообщения определенной категории, не открывая их</h3>\n                            <ul>\n                                <li> на случай переполнения  почтового ящика\n                                    <p>Добавлена подсказка\n                                                                        В настройках пользователя  можно  отключить  получения  определенных  категории\n                                                                        сообщений. В этот список  входят:  Шпионские  отчёты,  Военные  отчёты,  Отчеты\n                                                                        переработки, Прибытие флота, Отчёты  экспедиций,  Сообщения  очереди  построек.\n                                                                        Настройки автоматических уведомлений включены по умолчанию для новых игроков\n                                                                        Добавлена новый класс сообщений \"Сообщения  Администрации\".  Уведомления  этого\n                                                                        класса  НЕ  МОГУТ  быть  отключены  в  настройках  пользователя.  К  сообщениям\n                                                                        Администрации  относятся:  сообщения  системы  квестов,   новости   сервера   и\n                                                                        собственно сообщения Администрации (личные сообщения, отправленные от игроков с\n                                    <p>                                    auth_level &gt; 0)</p>\n                                                                        Восстановлена функциональноксть класса сообщений \"Сообщения очереди  построек\".\n                                                                        Уведомления этого класса могут быть отключены в настройках пользователя. К  ним\n                                                                        относятся:</p>\n                                    <ul>\n                                       <li> 1. Уведомления о завершении исследований. Уведомление высылается после  входа\n                                                                           на страницу исследований</li>\n                                    <li>2. Уведомления об окончании работы верфи на планете.  Уведомление  высылается\n                                                                        по окончании очереди строительства Верфи</li>\n                                    <li>3. Уведомление об окончалии строительных  работ  на  планете  (постройка  или\n                                                                        разрушение здания). Уведомление высылается  пакетно  в  полуавтоматическом\n                                                                        режиме. Это означает, что сообщение генерируется каждый раз при  обращении\n                                                                        к планете  (сканирование  шпионажом  или  игроком,  переключение  активной\n                                                                        планеты игроком итд). При этом  в  сообщение  указываются  все  изменения,\n                                                                        произошедшие на момент обращения</li>\n                                    </ul>\n                                    Добавлена возможность пересылки личных сообщений на емейл  игрока.  Возможность\n                                    включается администратором сервера в настройках - опция \"Разрешить пересылку ЛС\n                                    на email\". После этого в настройках игрока появляются дополнительные опции  для\n                                    всех категорий входящих сообщений\n                                </li>\n                            </ul>\n                        </div>\n                    </div>\n                </li>\n                <li class=\"chapter\" id=\"chapter_67\">\n                    <h2 class=\"heading\">Чат</h2>\n                    <div class=\"desc\">\n                        <p>Полностью переписан внутренний чат. JS-часть переведена  на  jQuery.  Добавлена\n                            заплатка для корректной работы чата в FireFox\n                            Имена  членов  Команды  сервера  (Администраторов,  Операторов  и  Модераторов)\n                            выделяются в чате, что бы отметить их среди сообщений обычных пользователей. По\n                            умолчанию имя Администратора выделяется пурпурным цветом, оператора -  красным,\n                            модератора - зеленым. В таблице  config  можно  настроить  стили  выделения  по\n                            своему вкусу\n                        </p>\n                        <p>Добавлена  возможность  настраивать  таймаут  чата  через  интерфейс   настроек\n                            Вселенной (раздел \"Настройки чата\"/\"Таймаут  по  неактивности\").  Значение  \"0\"\n                            означает, что таймаут отключен (не рекомендуется, потому что  при  неправильных\n                            настройках сервера большое одновременное количество запросов может  привести  к\n                            DoS)\n                        </p>\n                        <p>История чата теперь грузиться в виде  нормальной  страницы  СН,  а  не  в  виде\n                            \"обмылка\". Корректно показывает заголовок в истории чата - \"общий чат\"  и  \"чат\n                            альянса\" соответственно\n                            Боевые отчеты  теперь  преобразуются  в  ссылки.  Из  соображений  безопасности\n                            работают только ссылки на текущем сервере. По  клику  на  ссылку  боевой  отчет\n                            открывается в новом окне\n                        </p>\n                        <p>Добавлена защита на стороне клиента от слишком частых обновлений\n                            Содержимое языкового файла chat.mo отфильтровано и влито в system.mo\n                            Тэг Альянса после имени игрока  теперь  указывается  в  квадратных  скобках,  а\n                            адресат сообщения - в круглых. Сделано для унификации написания тэга Альянса  в\n                            движке\n                        </p>\n                        <p>Чат теперь инкрементальный - с сервера передается не  всё  содержимое  чата,  а\n                            только новые сообщения. Чат корректно работает когда у игрока открыто несколько\n                            окон с чатом\n                        </p>\n                        <p>Исправлена проблема со скроллированием чата в Chrome v20+\n                            Теперь при отключении чата по таймауту содержимое окна не стирается, а  в  него\n                            добавляется соответствующее сообщение. Так же прячутся  элементы  ввода:  выбор\n                            цветов, строка сообщения, кнопка \"Отправить\" и панель смайлов\n                            Новый код чата (как JS, так и PHP) заметно  компактнее,  аккуратнее  и  быстрее\n                            старого\n                        </p>\n                        <p>Увеличена длина поля для ника в чате\n                            Добавился новый BBCode \"s\" - зачёркнутый текст\n                            В чате Альянса в нике участника теперь не указывается Альянс\n                            Переформатирован вывод списка смайлов.  Список  смайликов  теперь  генерируется\n                            автоматически из всего доступного списка\n                            При открытии окна чата курсор позиционируется в строку набора сообщения\n                            Реформатирование HTML-кода страницы чата\n                            Три файла чата интегрированы в один\n                            Множество других добавлений и усовершенствований\n                        </p>\n                    </div>\n                </li>\n                <li class=\"chapter\" id=\"chapter_68\">\n                    <h2 class=\"heading\">Модуль \"Продвинутый чат\": chat_advanced</h2>\n                    <div class=\"desc\">\n                        <p>Имеется возможность установки модуля чата с расширенной функциональностью\n                            Расширенный чат имеет встроенную систему команд с поддержкой  алиасов.  Команды\n                            вводятся в строке ввода сообщения и начинаются символом \"/\"\n                            Чат оборудован встроенной системов помощи по командам чата - команда  /help.  В\n                            ней  можно  узнать  более  подробно  о  досутпных   командах   и   формате   их\n                            использования. Здесь же описание будет дано  лишь  список  доступных  команд  и\n                            описание их функций\n                        </p>\n                        <p>Добавлен список игроков в чате с дополнительными иконками статуса  и  командами\n                            управления для админов\n                        </p>\n                        <p>Команда  /invisible  дает  возможность  игрокам  управлять   своим   состоянием\n                            видимости в чате. Администрация сервера (authlevel &gt; 0) всегда видит невидимок\n                            Команда /whisper  позволяет  отправлять  приватные  сообщения  другим  игрокам.\n                            Приватные сообщения выделяются специальным образом, видны  во  всех  каналах  и\n                            сохраняются  в  истории  чата.  В  приватных  сообщенях   нельзя   использовать\n                            форматирование цветом\n                        </p>\n                        <p>Администраторы имеют возможность запретить игроку писать в чат на  определенный\n                            срок или вернуть такую возможность - соответственно, команды /mute  и  /unmute.\n                            Запрет распространяется на все каналы и на возможность писать личные сообщения.\n                            Соответствующая иконка в списке игроков лишает его права голоса на 1 час\n                            Администраторы имеют возможность блокировать и разблокировать игроков прямо  из\n                            чата - соответственно, команды /mute и /unmute. Иконка в списке  игроков  банит\n                            его на 1 неделю\n                        </p>\n                        <p>Максимальное время нахождения игрока в списке онлайн совпадает с таймаутом чата\n                            на странице сервера - т.е. в списке онлайн  игрок  будет  виден  еще  некоторое\n                            время после выхода из чата\n                            Добавлена поддержка локального времени в чат и историю чата\n                            Теперь можно использовать команды при выбранном цвете  сообщения.  Ранее  такие\n                            команды не воспринимались системой чата\n                        </p>\n                        <div class=\"sub\">\n                            <h3 class=\"title\">Произведена замена цветов для лучшей читаемости сообщений: red -&gt; maroon,  blue</h3>\n                            <ul>\n                                <li>&gt; cyan\n                                    Цвет green оставлен для  пользвателей,  а  подтверждающие  системные  сообщения\n                                    используют цвет lime - как и в остальном интерфейсе сервера\n                                    Системные и приватные сообщения теперь выделяются жирным шрифтом\n                                    Клик на имени игрока в списке онлайна теперь всегда добавляет  команду  \"/w\"  в\n                                    начало сообщения - а не в конец, как ранее\n                                    Скорость  обновления  в  AJAX  части  чата   теперь   регулируется   переменной\n                                    'chat_refresh_rate'\n                                    Теперь игроки из онлайн-списка исчезают сразу после выхода из  чата  -  таймаут\n                                    попадания в список установлен  как  удвоенный  'chat_refresh_rate',  а  не  как\n                                    'chat_timeout'  ранее  и  вычисляется  по  дополнительному  полю,   а   не   по\n                                    'chat_player_activity' как ранее\n                                </li>\n                            </ul>\n                        </div>\n                    </div>\n                </li>\n                <li class=\"chapter\" id=\"chapter_69\">\n                    <h2 class=\"heading\">День Рождения</h2>\n                    <div class=\"desc\">\n                        <p>Игрок может ввести свой ДР на своей странице настроек. ДР вводится один  раз  и\n                            после этого не может быть изменен. Дата проходит валидизацию в  соответствии  с\n                            серверными настройками формата даты\n                            Игрок с ДР на текущую дату будет отмечен специальной иконкой в статистике и  на\n                            странице Вселенной.  При  наведении  на  иконку  всплывает  подсказка  с  датой\n                            рождения\n                        </p>\n                        <p>Амдинистратор  сервера  может  назначить  количество  ТМ  в  подарок  на  ДР  в\n                            настройках (опция  \"Подарок  игроку  на  день  рождения\").  Если  это  значение\n                            установлено в 0 - подарки отключены.\n                        </p>\n                        <p>Выдача подарков происходит один раз в сутки всем игрокам, день рождения которых\n                            находится не далее чем в \"Ретро-рождение\" дней от текущей даты.\n                            При этом подарки выдаются только игрокам, которые на момент  выдачи  уже  имели\n                            введенную дату рождения. Движок гарантированно начислит подарки  даже  если  ДР\n                            пришелся на день неактивности сервера (неисправность или обслуживание).\n                        </p>\n                        <p>Такая система выбрана с одной стороны - что бы  не  обидеть  игроков  в  случае\n                            проблем с сервером, а с  другой  стороны  -  что  бы  избежать  злоупотреблений\n                            (например - ввести послезавтрашнюю дату ДР, на следующий день  получить  ТМ  за\n                            \"прошлый ДР\", а через день - еще и  за  \"нынешний\".  Такой  вариант  в  текущей\n                            системе начисления подарков не прокатит)\n                        </p>\n                    </div>\n                </li>\n                <li class=\"chapter\" id=\"chapter_70\">\n                    <h2 class=\"heading\">Интерфейс</h2>\n                    <div class=\"desc\">\n                    </div>\n                </li>\n                <li class=\"chapter\" id=\"chapter_71\">\n                    <h2 class=\"heading\">Логин и регистрация</h2>\n                    <div class=\"desc\">\n                        <p> Полностью переделаны экраны логина и регистрации\n                            Полностью переделана подсистема восстановления забытого пароля\n                            Незалогиненные пользователи имеют  возможность  предварительно  ознакомиться  с\n                            сервером: почитать новости, просмотреть настройки сервер, статистику игроков  и\n                            список банов, связаться с администраторами, ознакомится  с  правилами  и  ЧаВо,\n                            сходить на форум\n                        </p>\n                    </div>\n                </li>\n                <li class=\"chapter\" id=\"chapter_72\">\n                    <h2 class=\"heading\">Навбар и планетбар</h2>\n                    <div class=\"desc\">\n                        Полностью переработан навбар\n                        Сведения о количестве ресурсов на планете вынесены в планетбар (по умолчанию он\n                        автоматически появляется на странице \"Обзор планеты\" и на  некоторых  страницах\n                        <div class=\"sub\">\n                            <h3 class=\"title\">постройки юнитов). В настройках пользователя можно включить планетбар постоянно</h3>\n                            <ul>\n                                <li>\n                                    т.е. будет полностью восстановлен функционал старого навбара\n                                    <p>Переработана  ячейка  сообщений.  Теперь  в   ней   кроме   общего   количества\n                                        непрочитанных сообщений отдельно отображается  количество  новых  сообщений  от\n                                        администрации, от игроков и от членов Альянса\n                                    </p>\n                                    <p> Добавлено отображения количества флотов и экспедиций - находящихся в  полете  и\n                                        максимально доступных. Количество флотов и экспедиций  в  полете  интерактивно:\n                                        оно  автоматически  изменяется  в  соответствие  с  происходящими  событиями  -\n                                        прибытие, возвращение и окончание миссии флота (как они должны  были  произойти\n                                        на момент загрузки страницы). При наведении курсора на  соответствующую  ячейку\n                                        всплывает  подсказка с описанием ближайшего события\n                                    </p>\n                                    <p>Добавлена информация о текущих исследованиях пользователя\n                                        Вся информация теперь  выводится  поверх  кликабельных  иконок  с  всплывающими\n                                        подсказками\n                                    </p>\n                                </li>\n                            </ul>\n                        </div>\n                    </div>\n                </li>\n                <li class=\"chapter\" id=\"chapter_73\">\n                    <h2 class=\"heading\">Вселенная</h2>\n                    <div class=\"desc\">\n                        Серьезно переработан код \"Вселенной\" (бывш. \"Галактика\").\n                        Шаблоны вытащены из кода и перенесены в TPL-файл.\n                        Попапы (инфо о планетах, лунах итд) вынесены в отдельные  JS-процедуры,  что  в\n                        отдельных случаях позволило уменьшить финальный размер страницы в два-три раза,\n                        а количество запросов к БД снизить от двух до  десяти  (!)  раз  -  особенно  в\n                        системе, густо заселенной одним игроком.\n                        Библиотека  попапов  изменена  с  overlib  на  jQuery  -  это   дало   отличную\n                        кросс-браузерную совместимость и устранение проблем с попапами в ИЕ.\n                        В целом новая \"Вселенная\"  существенно  меньше  грузит  сервер  и  работает  на\n                        клиенте гораздо быстрее\n                        Ракеты теперь отправляются через AJAX без обновления страницы\n                        На  изображении  вражеских  планет,  на  которые  летят  флоты  игрока   теперь\n                        отображается соответствующая иконка и при наведении всплывает попап со  списком\n                        всех входящих кораблей\n                        В попап Альянсов добавлен его ранг\n                        На попапе игрока отображается его текущее звание в Альянсе\n                        Шаблоны попапов легенды, планет, лун, обломков, игроков и альянсов вынесены  из\n                        JS-скрипта в шаблон страницы\n                        Убрано количество летящих флотов - эта информация есть в навбаре\n                        Полностью  переписана  работа  AJAX-части,  отвечающей  за  отправку   шпионов,\n                        переработчиков и ракет\n                        Количество переработчиков теперь включает  все  виды  кораблей,  которые  могут\n                        перерабатывать обломки\n                        Интерфейс запуска ракет использует группу защитных сооружений, а не  хард-кодед\n                        перечень, как было раньше\n                        В попап легенды добавлены расшифровки для иконок действия\n                        В попапе планеты показывается её диаметр\n                        В попапе луны миссия \"Уничтожить\" показывается только если на  текущей  планете\n                        игрока есть ЗС\n                        Из попапа игрока убраны ссылки - все, что можно было сделать по ссылкам,  можно\n                        теперь\n                        Добавлено новая иконка действия - \"Статистика\". Её тултип показывает статистику\n                        игрока\n                        Расширена подсказка\n                        <div class=\"sub\">\n                            <h3 class=\"title\">Переработка</h3>\n                            <ul>\n                                <li>\n                                    Полностью переделана работа с полем обломков\n                                    Полностью переписан алгоритм запуска переработчиков\n                                    В попапе вместе с абсолютными теперь показываются и  относительные  значения  в\n                                    процентах\n                                    В попапе добавилось три строки:\n                                    1. Строка \"В полете\" показывает емкость трюмов  переработчиков  пользователя,\n                                    которые уже летят на данное поле\n                                    2. Строка \"На орбите\" показывает емкость  переработчиков  на  орбите  текущей\n                                    планеты или луны\n                                    3. Строка \"К переработке\" показывает сумму двух предыдущих строк\n                                    На основном экране Вселенной к иконке обломков добавлена индикация  процентного\n                                    значения из строки \"В полете\". Она имеет цветовое кодирование:\n                                    1. Зеленый цвет означает, что прибывающие флоты игрока полностью переработают\n                                    поле обломков на ресурсы\n                                    2.  Желтый  цвет  означает,   что   к   полю   летит   некоторое   количество\n                                    переработчиков, которых не хватит что бы целиком переработать обломки, но\n                                    на текущей планете  есть  достаточно  переработчиков,  что  бы  полностью\n                                    обработать поле\n                                    3. Оранжевый означает, что к полю летит флот иргока с переработчиками, но  их\n                                    не хватит на полную обработку  обломков,  даже  включая  те  корабли,  что\n                                    находятся на орбите\n                                    4. Красный цвет значит, что к полю обломков не летит ни одного  переработчика\n                                    игрока\n                                    <ul>\n                                        <li>\n                                            <h4>Именование систем и галактик</h4>\n                                    Галактики и системы  могут  иметь  собственные  названия.  Увидеть  и  изменить\n                                    текущее имя галактики или системы можно на странице \"Вселенная\"\n                                    По умолчанию  галактики  и  системы  не  имеют  собственных  названий.  Назвать\n                                    галактику или систему можно  по  ссылке  \"Переименовать\"  соответственно  возле\n                                    координат галактики или системы. Выбранное  имя  галактики/системы  видны  всем\n                                    игрокам\n                                    Именование галактики или системы имеет соотвествующую стоимость - по  умолчанию\n                                    10.000 ТМ для галактики и 1.000  ТМ  для  системы  -  т.н.  \"базовую  стоимость\n                                    именования\". Изменить базовую стоимость именования можно в настройках  сервера.\n                                    Игроки могут видеть текущую базовую стоимость именования на  странице  \"Мировые\n                                    константы\"\n                                    При именовании галактики или системы игрок  может  назначить  цену  именования.\n                                    Стартовая цена именования равна базовой стоимости именования\n                                    При переименовании уже именованной галактики или системы, игрок должен уплатить\n                                    ранее назначенную стоимость именования плюс базовая стоимость именования.\n                                    Пример\n                                    Пусть базовая цена именования системы составляет 1.000 ТМ.\n                                    Первый игрок решил переименовать систему  без  названия.  Он  мог  бы  уплатить\n                                    базовую стоимость в 1.000 ТМ, однако решил уплатить 2.500 ТМ\n                                    Теперь цена переименования системы для следующего игрока составит\n                                    &lt;Текущая цена имени&gt;\n                                    +\n                                    &lt;Базовая цена&gt;\n                                    = 2.500 + 1.000 = 3.500 ТМ\n                                    Таким  образом  более  высокая  цена  именования  галактики   или   системы   в\n                                    определенной степени защищает объект от переименования\n                                    Все действия по переименованию галактик и  систем  записываются  в  лог  -  код\n                                    события  104  -  для  отслеживания  нарушителей  Правил  сервера  о  допустимых\n                                    идентификаторах\n                                        </li>\n                                    </ul>\n                                </li>\n                            </ul>\n                        </div>\n                    </div>\n                </li>\n                <li class=\"chapter\" id=\"chapter_74\">\n                    <h2 class=\"heading\">Переработка поля обломков</h2>\n                    <div class=\"desc\">\n                        <p>Полностью переписан интерфейс переработки  обломков.  Он  учитывает  количество\n                                                переработчиков, уже  отправленных  на  поле  обломков  и  позволяет  эффективно\n                                                собирать обломки с нескольких планет\n                                                На обломках показывается общее количество ресурсов\n                                                На иконке обломков отображается количество переработчиков пользователя, летящих\n                                                на эти обломки</p>\n                    </div>\n                </li>\n                <li class=\"chapter\" id=\"chapter_75\">\n                    <h2 class=\"heading\">Обзор планеты</h2>\n                    <div class=\"desc\">\n                        Полностью переработана страница \"Обзор планеты\".\n                        <ul>\n                            <li>1. Список планет  с  картинками  перенесен  из  таблицы  направо.  На  картинки\n                                                    добавлены иконки: активного строительства, активного исследования,  активной\n                                                    верфи, индикатор прибывающего  собственного  флота,  индикатор  прибывающего\n                                                    враждебного флота, статус застройки  планеты.  Так  же  указывается  текущее\n                                                    строительство на каждой планете и таймер обратного отсчета\n                                                    Справа  от  иконки  планеты  добавлены  три  колонки,  показывающие  процент\n                                                    производительности шахт и синтезаторов: серый -  шахта  металла,  голубой  -\n                                                    синтезатор кристаллов,  фиолетовый  -  синтезатор дейтерия.  Высота  колонки\n                                                    пропорциональна проценту производства, а фон кодирует  диапазоны:  желтый  -\n                                                    80-90%, оранжевый - 50-70%%, красный - меньее 50%. На высоту и  фон  колонки\n                                                    влияет ИСКЛЮЧИТЕЛЬНО процент производства, выставленный на странице \"Ресурсы\"</li>\n                        <li>2. Полностью переработан график движения  флотов.  Теперь  отдельно  выделяются\n                                                такие  события,  как  \"Прибытие\",  \"Возвращение\"  и   \"Окончание   задания\".\n                                                Отделяются события для текущей выбранной планеты и для остальных</li>\n                        <li>3. Добавлены три таймера обратного отсчета для  строительства,  исследований  и\n                                                верфи. Таймер верфи и таймер зданий поддерживают очередь построек.</li>\n                        <li>4. Статистика игрока и баннеры перенесены на страницу \"Император\"\n                                                Полностью переписано управление планетой\n                                                Добавлен новый тип сортировки планет - по общему количество полей.  Учитываются\n                                                терраформеры (на планетах) и лунные базы (на лунах)\n                                                Изменена цветовая кодировка полосы застройки: зеленый -  менее  50%  застройки,\n                                                желтый - не меньше 50% и меньше 80%, оранжевый - не меньше 80% и  меньше  100%,\n                                                красный - 100% застройки\n                                                На полосы застройки добавилось застроенное и максимальное количество  полей  на\n                                                планете\n                                                Если планет больше  5, то список планет форматируется в две колонки\n                                                Вид очереди построек верфи и очереди  исследований  теперь  аналогичен  очереди\n                                                построек зданий\n                                                Таймер очереди строительства зданий под иконкой планеты теперь переключается на\n                                                следующее здание в очереди при окончании строительства текущего.  Ранее  таймер\n                                                показывал  только  прогресс постройки первого здания в очереди\n                                                Вертикальная очередь построек. Включается в настройках  пользователя  в  секции\n                                                \"Настройки интерфейса\"\n                                                Добавлено текущее количество ресурсов на планете, текущий  размер  хранилищ,  а\n                                                так же - количество ресурсов на прилетающих флотах</li>\n                        </ul>\n                        <div class=\"sub\">\n                            <h3 class=\"title\">Настраиваемое количество колонок в списке планет</h3>\n                            <ul>\n                                <li>\n                                    <p>На странице настройке пользователя можно указать, сколько колонок должно быть в\n                                                                        списке планет - пункт \"Количество колонок в списке планет\" в разделе \"Настройки\n                                                                        интерфейса\"</p>\n                                    <p>Можно выставить количество колонок в 0 и указать максимальное количество  рядов\n                                                                        в списке - см. соответствующий пункт там же. В этом  случае  движок  рассчитает\n                                                                        количество колонок исходя из этого числа.</p>\n                                    <p>Обращаю внимание - указывается именно максимальное количество рядов! Т.е.  если\n                                                                        у игрока 6 планет, а количество рядов  указано  5,  то  количество  необходимых\n                                                                        колонок для того,  что  бы  число  рядов  не  привысило  5  будет  равно  двум.\n                                                                        Соответственно, список планет будет сформирован в  виде  двух  колонок  по  три\n                                                                        ряда. Если же колоний будет 12 - список планет будет выглядеть как таблица  три\n                                                                        колонки по четыре ряда.</p>\n                                    <p>Данная особенность связана с построением списка планет - слева направо и сверху\n                                                                        вниз. Естественно, не составило бы никакого труда сделать вывод  списка  сверху\n                                                                        вниз, а затем справо налево - это было бы  даже  легче.  Однако  при  выбранном\n                                                                        способе сохраняется пользовательская сортировка планет - более \"важные\" колонии\n                                                                        всегда будут \"выше\" в списке</p>\n                                </li>\n                            </ul>\n                        </div>\n                    </div>\n                </li>\n                <li class=\"chapter\" id=\"chapter_76\">\n                    <h2 class=\"heading\">Фаланга</h2>\n                    <div class=\"desc\">\n                        Переписан вывод  фаланги  с  использованием  функций  СН.  Теперь  он  выглядит\n                        аналогично списку событий флота на странице \"Обзор планеты\". Алгоритм работы  -\n                        почти оффовский:\n                        1. Показываются все флоты, летящие от сканируемой планеты или же к ней\n                        2. Полет A --&gt; B\n                        a) скан B =&gt; можно увидеть время прибытия флота\n                        b) скан A =&gt; можно увидеть время возвращения флота (но не его прибытия на B)\n                        3. Возвращение B --&gt; A\n                        a) скан B =&gt; не видно ничего\n                        b) скан A =&gt; виден возвращающийся флот\n                        Полнота информации о флотах зависит от уровня шпионажа (см. ниже)\n                        Особые случаи:\n                        1. Задание \"Передислокация\" A --&gt; B\n                        a) флот виден только на B, но не на A\n                        b) после отзыва флот нигде не виден\n                        2. Флот, летящий с луны, фалангой не виден\n                        3. Флот, летящий на задание \"Удержание\" фалангой не виден\n                        4. Фаланга показывает так же ракетные атаки\n                    </div>\n                </li>\n                <li class=\"chapter\" id=\"chapter_77\">\n                    <h2 class=\"heading\">Шпионаж в Обзоре планеты и Фаланге</h2>\n                    <div class=\"desc\">\n                        На количество отображаемой информации о летящих чужих флотах влияет эффективный\n                        уровень шпионажа (технология+наемник):\n                        Меньше 4 - нет информации о летящем флоте\n                        4 и выше - видно общее количество кораблей во флоте и везет ли флот ресурсы\n                        6 и выше - виден качественный состав флота - т.е. сколько групп  кораблей  во\n                        флоте и сколько кораблей в каждой группе\n                        8 и выше - видно точное количество ресурсов в трюмах кораблей\n                        10 и выше - виден количественный состав флота\n                    </div>\n                </li>\n                <li class=\"chapter\" id=\"chapter_78\">\n                    <h2 class=\"heading\">Межгалактические Врата</h2>\n                    <div class=\"desc\">\n                        Интерфейс Врат вынесен на отдельную страницу и доступен с Обзора планеты  (куда\n                        вынесен таймер готовности врат)\n                        Интерфейс переделан по примеру страницы \"Флоты на орбите\"\n                    </div>\n                </li>\n                <li class=\"chapter\" id=\"chapter_79\">\n                    <h2 class=\"heading\">Строительство зданий</h2>\n                    <div class=\"desc\">\n                        Новый интерфейс строительства зданий  -  более  компактный  и  удобный\n                        Немного изменен  порядок  зданий  на  странице  постройки.  Теперь  все  здания\n                        сгруппированы  по  типам:  первая  линейка  -  добывающие,  вторая  линейка   -\n                        производящие, третья линейка - склады. На луне и/или при не всех доступных  для\n                        постройки зданиях порядок пока может меняться\n                        Дополнительно показываются остатки ресурсов с  учетом  прибывающих  на  планету\n                        флотов с грузом\n                        Написанная с нуля очередь построек\n                    </div>\n                </li>\n                <li class=\"chapter\" id=\"chapter_80\">\n                    <h2 class=\"heading\">Верфь</h2>\n                    <div class=\"desc\">\n                        <p>Переверстан интерфейс Верфи и Обороны. Вид очереди  построек  обновлен.  Теперь\n                                                они выглядят так же, как и очередь постройки зданий\n                                                Добавлена возможность удалить последний добавленный элемент из очереди\n                                                Кнопка \"Построить\" дублируется возле каждого юнита. Функционал сохранен - по её\n                                                нажатию будут построены все выбранные юниты</p>\n                    </div>\n                </li>\n                <li class=\"chapter\" id=\"chapter_81\">\n                    <h2 class=\"heading\">Флоты</h2>\n                    <div class=\"desc\">\n                        <p>Полностью переработана страница флотов. Добавлено цветовое кодирование в списке\n                                                флотов</p>\n                        <p>Добавлены  слайдбары  для  выбора  количества  кораблей.  На  первой   странице\n                                                указываются предварительные параметры флота (грузоподъемность,  скорость,  если\n                                                указана точка назначения - время полета  и расход дейтерия)\n                                                Команда на возвращение флота теперь не ведет на промежуточную страницу, а сразу\n                                                обрабатывается подсистемой флотов</p>\n                        <p>В списках летящих флотов к количеству кораблей во  флоте  добавляется  в  конце\n                                                знак \"+\" если флот везет ресурсы</p>\n                        <div class=\"sub\">\n                            <h3 class=\"title\">Своз ресурсов</h3>\n                            <ul>\n                                <li>\n                                    Добавлена новая возможность - свезти ресурсы с  остальных  планет  на  текущую.\n                                    Игрок выбирает колонии и луны, с которых нужно свезти  ресурсы  на  текущую,  а\n                                    движок автоматически рассчитает  нужное  количество  транспортов,  загрузит  их\n                                    ресурсами и отправит флоты.\n                                    В перевозке ресурсов участвуют только транспортные  корабли:  Малый  транспорт,\n                                    Большой транспорт и Супертранспорт.  Корабли  загружаются  в  порядке  убывания\n                                    емкости трюма\n                                    Есть возможность выбирать все ресурсы одного типа на всех планетах; все ресурсы\n                                    на одной планете; все ресурсы на всех планетах\n                                    Имеется JS-счетчик, показывающий общее количество свозимых ресурсов как по типу\n                                    ресурсов, так и по отдельной планете\n                                    Открыть страницу своза ресурсов можно из  списка  кораблей  на  орбите,  списка\n                                    летящих флотов, кликнув на желтой иконке  тележки  на  изображении  планеты  на\n                                    страницах \"Обзор планеты\" и  \"Империя\",  а  так  же  на  изображении  юнита  на\n                                    странице строительства. В последнем случае так же будет отображатся  количество\n                                    ресурсов, недостающих для создания данного юнита\n                                </li>\n                            </ul>\n                        </div>\n                    </div>\n                </li>\n                <li class=\"chapter\" id=\"chapter_82\">\n                    <h2 class=\"heading\">Меню</h2>\n                    <div class=\"desc\">\n                       <ul>\n                            <li>Редизайн меню</li>\n                            <li>Изменен порядок расположения пунктов</li>\n                            <li>Высота пункта меню увеличена до 16 пикселов</li>\n                            <li>Добавлены иконки. Размер иконки ограничен 14 пикселами в высоту</li>\n                       </ul>\n                    </div>\n                </li>\n                <li class=\"chapter\" id=\"chapter_83\">\n                    <h2 class=\"heading\">Поиск</h2>\n                    <div class=\"desc\">\n                        <ul>\n                            <li>Полностью переписан поиск</li>\n                        <li>Добавлена сортировка по Альянсу, имени игрока, имени планеты</li>\n                       <li> Настройка сервера \"Скрывать ссылки на  ЛС\"  распространяется  и  на  результаты\n                                               поиска</li>\n                        </ul>\n                    </div>\n                </li>\n                <li class=\"chapter\" id=\"chapter_84\">\n                    <h2 class=\"heading\">Империя</h2>\n                    <div class=\"desc\">\n                        На изображения планет добавлены иконки, как на странице  \"Обзор  планеты\"  (см.\n                        выше).\n                        Убрано многократно дублирующееся перечисление технологий, идентичных  для  всех\n                        планет.\n                        Добавлена дополнительная колонка, в которой суммируются данные по всей Империи.\n                        В списке построек отображаются строящиеся/уничтожаемые здания\n                        В списке кораблей отображаются строящиеся и прилетающие корабли\n                        В списке защитных сооружени отображаются строящаяся защита\n                        Цифра производства ресурсов теперь кодируется цветом  аналогично  фону  колонок\n                        прозиводства ресурсов (см. выше)\n                        Добавлено цветовое кодирование для производящих структур. Уровень производства,\n                        выставленный на странице  \"Ресурсы\",  кодируется  цветом  фона  соответствующей\n                        ячейки: зеленый - 100%, желтый  -  80-90%%,  оранжевый  -  70-50%%,  красный  -\n                        40-10%%, цвет фона - 0% или структура не является производящей. Пропорционально\n                        уровню производительности меняется и длина кодированной полоски\n                        Значительно оптимизирован HTML-код\n                        Размер HTML-кода уменьшен на величину от 30% и  в  отдельных  случаях  до  80%.\n                        Среднему игроку оптимизация  даст  уменьшение  размера  загружаемого  файла  на\n                        40-50%% (включает так же выигрышь от оптимизации Списка планет - см.)\n                        В колонку \"ИТОГО\" добавлена  сумма  по  строящимся  и  прибывающим  на  планеты\n                        юнитам\n                        Теперь юниты всегда  групируются  согласно  их  принадлежности.  Например,  при\n                        подключении модуля расовых юнитов они добавляются в категорию \"Флот\"\n                    </div>\n                </li>\n                <li class=\"chapter\" id=\"chapter_85\">\n                    <h2 class=\"heading\">Император</h2>\n                    <div class=\"desc\">\n                        <p>Новая страница. Содержит небольшой блок новостей, блок статистики игрока и блок\n                                                баннеров/юзербаров</p>\n                    </div>\n                </li>\n                <li class=\"chapter\" id=\"chapter_86\">\n                    <h2 class=\"heading\">Мировые константы</h2>\n                    <div class=\"desc\">\n                        <p>Новая страница. Показывает текущие настройки Вселенной пользователям\n                                                Добавлен вывод информации о разешении прокачки и разрешении удержания на слабом\n                                                соаловце\n                                                Добавлена информация о текущих настройках антибашинга</p>\n                    </div>\n                </li>\n                <li class=\"chapter\" id=\"chapter_87\">\n                    <h2 class=\"heading\">Список забаненных</h2>\n                    <div class=\"desc\">\n                        <p>Полностью переписан список забаненных. Список теперь сортируется по возрастанию\n                                                даты бана - последние забаненные появляются  в  начале  списка.\n                                                Добавлено отображение разбанов.</p>\n                    </div>\n                </li>\n                <li class=\"chapter\" id=\"chapter_88\">\n                    <h2 class=\"heading\">Прочее</h2>\n                    <div class=\"desc\">\n                       <p> Страницы статистики, списка банов, списка контаков Вселенной,  списка  настроек\n                                               доступны для просмотра незалогиненным, находящимся в отпуске и забаненных</p>\n                    </div>\n                </li>\n                <li class=\"chapter\" id=\"chapter_89\">\n                    <h2 class=\"heading\">Администрирование</h2>\n                    <div class=\"desc\">\n                    </div>\n                </li>\n                <li class=\"chapter\" id=\"chapter_90\">\n                    <h2 class=\"heading\">[#] Модуль: Редактирование характеристик планеты</h2>\n                    <div class=\"desc\">\n                        <p>Модуль admin_planet_edit_extra не входит в основной  состав  дистрибутива.  Для\n                                                получения модуля обращайтесь к автору движка\n                                                В админке  добавляется  возможность  менять  основные  характеристики  планеты:\n                                                название,  изображение,  размер,  температуру,  губернатора  и   его   уровень,\n                                                количество обломков на орбите</p>\n                    </div>\n                </li>\n                <li class=\"chapter\" id=\"chapter_91\">\n                    <h2 class=\"heading\">Права доступа к администраторской части</h2>\n                    <div class=\"desc\">\n                        Права доступа к отдельным страницам админки устанавливаются  в  зависимости  от\n                        уровня игрока  (столбец  authlevel  в  таблице  users).  Выделяют  три  уровня\n                        доступа:\n                        1. Модератор  (authlevel=1)  имеет  доступ  к  следующим  страницам:  overview,\n                        adm_planet_list, banned, changelog, statbuilder,  tools,  md5enc\n                        Он может: видеть  список  игроков  онлайн  и  их  активность,  видет  список  и\n                        активность лун  и  планет,  вручную  обновлять  статистику  игроков,  банить  и\n                        разбанивать пользователей\n                        2. Оператор (authlevel=2) дополнительно имеет  доступ  к  следующим  страницам:\n                        planet_edit, add_research, del_research, showfliyingfleets\n                        Дополнительно к функциям модератора он может: редактировать юниты и ресурсы  на\n                        лунах и планетах; добавлять и  убирать  технологии  игрока;  добавлять  луны  к\n                        планетам; видеть и редактировать флоты в полете\n                        3.  Администратор  (authlevel=3)  имеет  доступ  ко  всем  страницам,   включая\n                        delete_user,   admin_darkmatter,   errors,    maintenance,    maintenance_ajax,\n                        messagelist, messall,  admin_chat,  paneladmina,  planet_compensate,  settings,\n                        userlist\n                        Дополнительно к функциям оператора он может: добавлять и убирать ТМ у  игроков;\n                        видеть  полный  список  игроков  с  IP-адресами;  удалять  игроков;   запускать\n                        процедуру  обслуживания  БД;  просматривать   и   удалять   личные   сообщения;\n                        просматривать и удалять  сообщения  чата;  просматривать  и  удалять  сообщения\n                        системы логов; изменять права пользователей; изменять настройки игры; возмещать\n                        игроку стоимость затрат на планету\n                    </div>\n                </li>\n                <li class=\"chapter\" id=\"chapter_92\">\n                    <h2 class=\"heading\">Воплощение</h2>\n                    <div class=\"desc\">\n                        <p>Теперь можно Воплотиться  в  любого  игрока,  посмотреть  игру  его  глазами  и\n                                                поуправлять игрой его ру... эээ...  интерфейсом!  Эта  возможность  чрезвычайна\n                                                полезна для диагностики  редких  неисправностей  и  для  отслеживания  действий\n                                                игроков, подзреваемых в читерстве или нарушении Правил серве\n                                                Воплощение доступно только Администраторам сервера\n                                                Воплотиться можно только в игрока меньшего уровня - т.е. нельзя  Воплотиться  в\n                                                такого же Администратора</p>\n                        <p>Вложенные Воплощения недопустимы: нельзя Воплотиться, будучи уже Воплощенным  в\n                                                кого-то. Сначала Развоплотитесь\n                                                Для Воплощения используйте соответствующую иконку в \"Списке игроков\"\n                                                При Воплощении изменяется только onlinetime а. Вся  остальная  информация  (IP,\n                                                User-agent итд) сохраняется</p>\n                        Д<p>ля Развоплощения используйте соответствующий пункт меню или \"Выход\"\n                                                Если на  аккаунте  игрока  есть  ошибки,  или  игрок  заблокировае,  или  игрок\n                                                находится  в  отпуске,  то  попытке  Воплощения  будет  выведено  сообщение  об\n                                                ошибке/блокировке/отпуске, которое увидел бы игрок на вашем  месте.  Обновление\n                                                страницы вернет вас в ваш аккаунт</p>\n                        <p>После штатного Развоплощения (т.е. из меню, а не при ошибке и не  из  игрока  в\n                                                отпуске) Администратора возвращает на страницу списка игроков\n                                                ВНИМАНИЕ! Перед использованием Воплощения  почистите  куки  в  браузере!  Из-за\n                                                изменений в работе кукесов кэш браузера может содержать дубликаты куков</p>\n                    </div>\n                </li>\n                <li class=\"chapter\" id=\"chapter_93\">\n                    <h2 class=\"heading\">Список игроков</h2>\n                    <div class=\"desc\">\n                       <ul>\n                            <li>Полностью переписан \"Список игроков\" с использованием liTE</li>\n                        <li>Сокращено количество строк локализации</li>\n                        <li>Пишется полный срок бана</li>\n                       <li> Для мультиаккаунтов подсвечиваются все адреса  с  одинаковым  Ili  и  в  скобках\n                                               добавляется количество игроков с таким же адресом</li>\n                        <li>Теперь невозможно удалить игрока того же уровня - для  предотвращения  разборок\n                                                между членами команды одного уровня</li>\n                       </ul>\n                    </div>\n                </li>\n                <li class=\"chapter\" id=\"chapter_94\">\n                    <h2 class=\"heading\">Google AdSense</h2>\n                    <div class=\"desc\">\n                        Внизу левого меню добавлен блок для  вставки  рекламы  от  Гугля.  В  принципе,\n                        ничего не мешает вставить туда рекламу от любого  другого  рекламного  сайта  -\n                        соответствующий HTML-код задается в настройках Вселенной\n                    </div>\n                </li>\n                <li class=\"chapter\" id=\"chapter_95\">\n                    <h2 class=\"heading\">Локализация</h2>\n                    <div class=\"desc\">\n                        <p>СуперНова использует кодировку UTF-8 при работе с БД и  рендере  HTML-страниц.\n                                                Таким  образом  поддерживаются  любые  наборы  символов.  Некоторые  непрвильно\n                                                настроенные сервера могут выставлять некорректную кодировку в HTTP-заголовке  -\n                                                для них в движке добавлен патч, устраняющий проблему</p>\n                        <p>СН разрабатывается  на  русском  языке.  В  27  релизе  добавлена  англоязычная\n                                                локализация и включена возможность смены языков пользователем\n                                                Добавлена полноценная информация о локализации  (файл  language.mo  в  каталоге\n                                                локализации). Существенно переработаны файлы локализаций: убраны неиспользуемые\n                                                файлы, а мелкие файлы, содержащие меньше десятка строк слиты с соответствующими\n                                                файлами. Все файлы локализации пропущены через редактор и  приведены  к  одному\n                                                виду. Добавлены заголовки в файлы локализации</p>\n                        <div class=\"sub\">\n                            <h3 class=\"title\">Система отката языка</h3>\n                            <ul>\n                                <li>\n                                    Добавлена система отката языков в случае, если требуемый  файл  локализации  не\n                                    найден. Для встроенных файлов предусмотрена двухуровневая система проверок.\n                                    Первый уровень - проверка по языку. Она происходит в следующем порядке:\n                                    <ul>\n                                        <li>1. Язык из адреса страницы - параметр lang</li>\n                                        <li>2. Язык пользователя</li>\n                                        <li>3. Язык сервера по умолчанию</li>\n                                        <li>4. Русский</li>\n                                        <li>5. Английский</li>\n                                    </ul>\n                                    Для  каждого  языка  происходит проверка на наличие локализации в двух местах:\n                                    1. Ищется файл \"language/\n                                    &lt;код_языка&gt;\n                                    /\n                                    &lt;имя_домена&gt;\n                                    .mo.php\"\n                                    2. Ищется файл \"language/\n                                    &lt;имя_домена&gt;\n                                    _\n                                    &lt;код_языка&gt;\n                                    .mo.php\"\n                                    Для модулей добавляется еще один уровень проверки - поиск файлов из предыдущего\n                                    пункта происходит сначала  относительно  каталога  \"/modules/\n                                    &lt;имя_модуля&gt;\n                                    /\",  и\n                                    только если там файл не найден - происходит поиск относительно корня движка\n                                    Если не найден ни один из указанных файлов, то происходит откат на более низкий\n                                    уровень - ищется файлы языка, стоящие ниже по списку\n                                    <h4>Редактор локализаций</h4>\n                                    В админке добавлен редактор локализаций - пункт меню  \"Локализация\"  в  разделе\n                                    \"Утилиты\"\n                                    Выбор  пункта  меню   \"Локализация\"   открывает   выбор   т.н.   \"домена\"   для\n                                    редактирования. Домен -  это  совокупность  строк  локализации,  относящихся  к\n                                    отдельному аспекту игры. Домен эквивалентен языковому файлу  с  соответствующим\n                                    именем\n                                    После выбора домена и подтверждения выбора открывается страница  редактирования\n                                    строк локализации. Открытие больших файлов может занимать существенное время  -\n                                    поэтому запаситесь терпением\n                                    В редакторе есть возможность добавлять и удалять строки  локализации.  Редактор\n                                    локализаций корректно работает с константами внутри доменов\n                                    После редактирования строк локализации и подтверждения редактор  создаст  файлы\n                                    \"\n                                    &lt;имя домена&gt;\n                                    .mo.new\" в каждой папке языка\n                                    Файлы \".mo.new\" имеют для редактора  приоритет  перед  обычными  \".mo\"  файлами\n                                    локализации. Т.е. если в одном языковом каталоге присутствуют оба типа  файлов,\n                                    редактор загрузит для редактирования \".mo.new\"\n                                    Для того, что бы движок подгрузил новый файл  локализации,  требуется  изменить\n                                    его расширение с .mo.new на .mo.  Обычно  это  перезапишет  файл  текущий  файл\n                                    локализации - поэтому следует заранее сделать его резервную копию\n                                    ВНИМАНИЕ! Следует соблюдать осторожность при замене  старых  файлов  на  новые!\n                                    Редактор не сохраняет комментарии и  игнорирует  дополнительный  код  в  файлах\n                                    локализации! В результате простая перезапись файлов может  нарушить  нормальную\n                                    работу подсистемы локализации движка!  Если  ваши  файлы  локализации  содержат\n                                    дополнительный PHP-код, то они требуют ручного вмешательства после обработке  в\n                                    редакторе! Ни один из встроенных файлов локализации SN не содержит лишнего кода\n                                    и может быть безопасно обработано встроенным редактором\n                                </li>\n                            </ul>\n                        </div>\n                    </div>\n                </li>\n                <li class=\"chapter\" id=\"chapter_96\">\n                    <h2 class=\"heading\">Уничтожение планет с компенсацией</h2>\n                    <div class=\"desc\">\n                        Добавлена возможность  уничтожения  планет  игроков  с  компенсацией  стоимости\n                        зданий, строений и ресурсов\n                    </div>\n                </li>\n                <li class=\"chapter\" id=\"chapter_97\">\n                    <h2 class=\"heading\">Редактирование планет и лун</h2>\n                    <div class=\"desc\">\n                        <p>С нуля создан интерфейс редактирования юнитов/ресурсов на планете - пункт  меню\n                                                \"Редактировать\" в разделе \"Планета\".  Он  доступен  членам  команды  начиная  с\n                                                Оператора (authlevel=2) и выше</p>\n                    </div>\n                </li>\n                <li class=\"chapter\" id=\"chapter_98\">\n                    <h2 class=\"heading\">Прочее</h2>\n                    <div class=\"desc\">\n                       <p> В админку на страницу утилит добавлен вывод информации о настройках и\n                                               параметрах MySQL сервера\n                                               Полностью переписана утилита шифрования пароля в MD5</p>\n                    </div>\n                </li>\n                <li class=\"chapter\" id=\"chapter_99\">\n                    <h2 class=\"heading\">Персонализация</h2>\n                    <div class=\"desc\">\n                        <p>Движок СР имеет множество опций, используя которые можно настроить внешний  вид\n                                                сервера и его функционал,  не  прибегая  к  изменению  исходного  кода.  КРАЙНЕ\n                                                рекомендуется  использовать  встроенные  возможности  СН  по  настройке  -  для\n                                                облегчения дальнейших обновлений движка!</p>\n                    </div>\n                </li>\n                <li class=\"chapter\" id=\"chapter_100\">\n                    <h2 class=\"heading\">Модули</h2>\n                    <div class=\"desc\">\n                        <p>Модуль - это некий часть функционала движка, вынесенного в  отдельный  каталог.\n                                                СН обладает развитой поддержкой системы модулей: модули могут иметь собственные\n                                                файлы локализации, собственные темплейты, изменять главное меню  игры  и,  само\n                                                собой, содержать собственный исполнимый PHP-код</p>\n                       <p> К сожалению, в настоящее время система модулей все еще развивается и  интерфейс\n                                               модулей меняется от версии к версии. Актуальные примеры модулей можно найти  на\n                                               форуме движка. Так же некоторые детали реализации модулей можно  найти  ниже  в\n                                               разделе \"Модульная система\"</p>\n                    </div>\n                </li>\n                <li class=\"chapter\" id=\"chapter_101\">\n                    <h2 class=\"heading\">Серверные значения по умолчанию</h2>\n                    <div class=\"desc\">\n                        <p>Теперь администраторы сторонних серверов могут задавать скин, темплейт  и  язык\n                                                по умолчанию. Это сделано для поддержки авторских разработок при обновлении  из\n                                                репозитория</p>\n                        <p>Администраторы так же могут конфигурировать основные ссылки - ссылки  на  ЧаВо,\n                                                на правила, на форум и на страницу  покупки  ТМ.  В  случае  отсутствии  ссылок\n                                                соответствующие пункты и ссылки скрываются</p>\n                    </div>\n                </li>\n                <li class=\"chapter\" id=\"chapter_102\">\n                    <h2 class=\"heading\">Скины</h2>\n                    <div class=\"desc\">\n                        Добавлена  возможность  перекрыть  дефолтные  стили  элементов  jQueryUI  (файл\n                        /design/css/jquery.css) стилями, специфическими для скина. Для этого в корневой\n                        каталог скина нужно положить файл jquerу.css  с  настройками  стилей  элемента.\n                        Сгенерировать  файл  под  свою  тему  можно  на   сайте   jQuery   по   ссылке:\n                        http://jqueryui.com/themeroller/\n                        В скины добавлена иконка пола в подкаталог \"images\" скина. Файлы для мужского и\n                        женского пола  называются  соответственно  \"sex_male.png\"  и  \"sex_female.png\".\n                        Встроенные скины обновлены автоматически\n                    </div>\n                </li>\n                <li class=\"chapter\" id=\"chapter_103\">\n                    <h2 class=\"heading\">Внутренние изменения</h2>\n                    <div class=\"desc\">\n                    </div>\n                </li>\n                <li class=\"chapter\" id=\"chapter_104\">\n                    <h2 class=\"heading\">Большие числа</h2>\n                    <div class=\"desc\">\n                       <p> Исправлена работа с большими числами. Все  числовые  значение  в  HTTP-запросах\n                                               трактуются как числа с плавающей запятой.  Все  идентификаторы  передаются  как\n                                               строки. Все идентификаторы в БД являются  BIGINT(20).  Соответствующим  образом\n                                               переконфигурированы (добавлены или изменены) FOREIGHN  KEYS.  Переработаны  все\n                                               таблицы,  что  бы  исключить   переполнение   при   любом   разумном   сценарии\n                                               использования движка (скажем, вплоть до скоростей x1000000)</p>\n                    </div>\n                </li>\n                <li class=\"chapter\" id=\"chapter_105\">\n                    <h2 class=\"heading\">Статистика</h2>\n                    <div class=\"desc\">\n                        <p>Полностью переписан модуль обновления статистики.  Максимум  вычислений  теперь\n                                                проводятся на стороне MySQL сервера без необходимости перекачивать данные в PHP\n                                                и обратно. Обновление статистики завернуто в транзакции. Все эти меры привели к\n                                                тому, что скорость обновления статистики на сервере с 500  аккаунтами  (P4  3,0\n                                                GHz, 2 Gb RAM) составляет меньше секунды!</p>\n                        <p>Добавлена возможность автоматического  обновления  статистики  (настройка  пока\n                                                только  через  БД).  Автоматическое  обновление  статистики   производится   по\n                                                расписанию через AJAX</p>\n                    </div>\n                </li>\n                <li class=\"chapter\" id=\"chapter_106\">\n                    <h2 class=\"heading\">Баннерилка</h2>\n                    <div class=\"desc\">\n                        Полностью переписан код баннерилки. Добавлена возможность включить РО при бане.\n                        При разбане режим РО остается, но  разрешается  из  него  выйти.  По  умолчанию\n                        включена галочка РО и выставлен срок бана в 3 дня. Бан и  разбан  объединены  в\n                        один пункт меню и на одну страницу интерфейса\n                    </div>\n                </li>\n                <li class=\"chapter\" id=\"chapter_107\">\n                    <h2 class=\"heading\">Минификатор темплейтов</h2>\n                    <div class=\"desc\">\n                        <p>Минификатор уменьшает  размер  генерируемого  движком  HTML-кода  путем  замены\n                                                нескольких идущих подряд пустых символов (перевод  строки,  табуляция,  пробел)\n                                                одним символом пробела.</p>\n                        <p>Минификатор умеет сжимать HTML и встроенный  JS-код.  Для  JS-кода  он  так  же\n                                                удаляет однострочные комментарии.</p>\n                        <p>Минификатор работает на уровне  темплейтов  и  если  включено  кэширование,  то\n                                                минификатор вызывается только один раз при компиляции кода и дальше  кэшируется\n                                                уменьшенный скомпилированный темплейт, что исключает необходимость в  повторном\n                                                вызове минификатора. Этим он выгодно отличается от минификаторов, работающих на\n                                                уровне сессии через ob_hanler()</p>\n                        <p>В среднем по сайту минификатор дает выигрыш порядка  7-8%%  при  незначительном\n                                                падении производительности.</p>\n                        <p>По умолчанию минификатор отключен. Включить его можно в  админке  в  настройках\n                                                сервера - пункт \"Минификатор темплейтов\"</p>\n                        <div class=\"sub\">\n                            <h3 class=\"title\">Очереди</h3>\n                            <ul>\n                                <li>\n                                    <p>Полностью переписана очередь построек\n                                                                        Исправлена ошибка, позволяющая при наличии выше в  очереди  на  верфи  медленно\n                                                                        строящегося юнита использовать его время постройки для создания более  быстрого\n                                                                        юнита  путем  добавления   последнего   в   очередь.   Например,   эта   ошибка\n                                                                        использовалась для быстрой постройки множества ракет</p>\n                                    <p>Унифицированы алгоритмы и файлы постройки флота и защиты. Это должно  полностью\n                                                                        снять проблемы с отрицательными ресурсами после верфи  и  с  постройкой  лишних\n                                                                        единиц флота/защиты на верфях</p>\n                                    <ul>\n                                        <li>\n                                            <h4>Менеджер флотов в полете</h4>\n                                    Полностью  переписан  менеджер  летящих  флотов  -  теперь   рядно-блокирующий,\n                                    тразакционный и кэширующий! Обновленный код на порядок  уменьшает  нагрузку  на\n                                    сервер за счет встроенной системе кэширования запросов b перехода  c  табличной\n                                    блокировки на рядную.\n                                    Система \"событий\" гарантирует корректный порядок обработки флотов (с  точностью\n                                    до секунды - предела текущей  организации  таблиц).  Целостность  и  валидность\n                                    результатов обеспечена добавлением  транзакций.  Всё  это  позволило  уменьшить\n                                    дискретизацию обработки  флотов  до  4х  секунд  на  серверах  с  300+  онлайна\n                                    (предыдущий менеджер при онлайне порядка 200 человек и дискретизацией 12 секунд\n                                    примерно через два часа работы \"выбивал\" MySQL сервер). При этом среднее  время\n                                    ожидания блокировки не превышает 250 мс.\n                                        </li>\n                                    </ul>\n                                </li>\n                            </ul>\n                        </div>\n                    </div>\n                </li>\n                <li class=\"chapter\" id=\"chapter_108\">\n                    <h2 class=\"heading\">Локальное (клиентское) и серверное время</h2>\n                    <div class=\"desc\">\n                        <p>Изменена процедура замера разницы между локальным и серверным временем.  Теперь\n                                                она производится  не  каждый  раз  при  обращении  к  серверу,  а  один  раз  и\n                                                сохраняется в БД </p>\n                       <p> При заметном изменении разницы можно заново произвести эту операцию,  установив\n                                               галочку \"Замерить разницу между локальным (клиентским) и серверным временем\" на\n                                               странице настроек пользователя и сохранив настройки. Замер будет произведен при\n                                               следующем открытии любой страницы игры</p>\n                        <p>Теперь вместо локального или серверного  времени  одновременно  показывается  и\n                                                локальное, и серверное время в следующих местах:</p>\n                        <ul>\n                            <li>1. В навбаре - часы реального времени</li>\n                            <li>2. При отправке флота на экране выбора точки назначения - в графе времени\n                                прибытия и возвращения флота\n                            </li>\n                            <li>3. При отправке флота на экране подтверждения отправки - в графе  времени\n                                прибытия и возвращения флота\n                            </li>\n                        </ul>\n                        Теперь вместо серверного времени показывается локальное в следующих местах:\n                        <ul>\n                            <li>1. В событиях навбара (флоты и экспедиции)</li>\n                            <li>2. В новостях</li>\n                            <li>3. На экране флотов в полете</li>\n                            <li>4. На экране обзора планеты в списке летящих флотов</li>\n                            <li>5. В чате и истории чата</li>\n                            <li>6. В боевых отчетах</li>\n                            <li>7. В сообщениях</li>\n                        </ul>\n                        Переформатирован навбар для добавления локального и серверного времени\n                        Повышена устойчивость механизма к  ошибкам  на  стороне  клиента:  неправильный\n                        часовой  пояс,  неправильные  настройки  DST  в  операционной  системе,  сильно\n                        отстающие/спешащие часы итд\n                    </div>\n                </li>\n                <li class=\"chapter\" id=\"chapter_109\">\n                    <h2 class=\"heading\">Система темплейтов</h2>\n                    <div class=\"desc\">\n                        <p>Добавлен движок рендеринга темплейтов от phpBB3. Движок доработан  до  обратной\n                                                совместимости с темплейтами XNova.</p>\n                       <p> В темплейтах можно указывать префикс \"L_\" что бы взять  переменную  из  массива\n                                               $lang и не  нужно  целиком  передавать  в  массив  $lang  в  процедуру  разбора\n                                               темплейтов. Так же можно  использовать  локализованные  значения  в  одномерных\n                                               массивах. Например, $lang['tech'][401] в темплейте превратится в {L_tech[401]}.\n                                               Поддерживаются так же строковые индексы массивов.</p>\n                        <p>В темплейтах можно указывать префикс \"C_\",  что  бы  использовать  значение  из\n                                                конфигурации Вселенной. Не работает в условных операторах.</p>\n                        <p>В темплейтах  можно  указывать  префикс  \"D_\",  что  бы  использовать  значения\n                                                PHP-констант</p>\n                    </div>\n                </li>\n                <li class=\"chapter\" id=\"chapter_110\">\n                    <h2 class=\"heading\">Система отпусков</h2>\n                    <div class=\"desc\">\n                        Переписана с нуля система отпусков.  Теперь  работает  опция  \"Отключить  режим\n                        отпуска\". На пользовательскую страницу настроек добавлен  таймер,  показывающий\n                        минимальный срок отпуска, если игрок уйдет в отпуск сейчас\n                        <div class=\"sub\">\n                            <h3 class=\"title\">JS библиотека таймеров</h3>\n                            <ul>\n                                <li>\n                                    Написана  с  нуля  универсальная  система  внутриигровых  таймеров  на  стороне\n                                    клиента. Корректно обрабатываются нажатие кнопки Back в браузере (через  AJAX).\n                                    Корреткно обрабатывается  рассинхронизация  часов  между  клиентом  и  сервером\n                                    (включая часовые пояса)\n                                    [@] sn_timer: Таймер корректно работает с очередью, в которой количество юнитов\n                                    больше 1\n                                    <ul>\n                                        <li>\n                                            <h4>/.local</h4>\n                                            Добавлен каталог '/.local' для облегчения разработки.  Файлы  в  этом  каталоге\n                                            игнорируются GIT-ом,  но  при  этом  корректно  подключают  внешние  файлы  для\n                                            обработки и выполнения\n                                        </li>\n                                        <li>\n                                            <h4>Баннер и юзербар</h4>\n                                            Полностью  переписана  система  генерации   баннеров/юзербаров.   Унифицированы\n                                            процедуры их генерации,  слиты  в  один  файл  процедур  и  один  файл-враппер,\n                                            вынесены PHP-файлы из каталога scripts в соответствующие места.\n                                        </li>\n                                        <li>\n                                            <h4>Автоматическое обновление</h4>\n                                            Написан с нуля модуль автоматического обновления структуры БД\n                                            Апдейтер использует собственные процедуры запросов к БД\n                                            Добавлена возможность форсировать обновление в случае проблем с  автоматическим\n                                            обновлением. Можно форсировать как все  обновления,  так  и  только  последнее.\n                                            Возможность доступна в интерфейсе Администратора, пункт меню \"Утилиты\"\n                                            Автоапдейтер на время работы отключает отладку вне  зависимости  от  глобальных\n                                            настроек\n                                        </li>\n                                        <li>\n                                            <h4>Обслуживание</h4>\n                                            Добавлена автоматическая процедура  ежедневного  обслуживания:  чистка  таблицы\n                                            башинга, чистка таблицы САБ\n                                        </li>\n                                        <li>\n                                            <h4>Логи ошибок и событий</h4>\n                                            Полностью переработана система внутренних логов сообытий. События сохраняются с\n                                            двумя  компонентами  -  пригодной  для  чтения  человеком  напрямую  из  БД   и\n                                            machine-readable, предназначенной для разбора встроенным  парсером.  Во  второй\n                                            компоненте  сохраняется  backtrace,  переменные   окружения   ($_GET,   $_POST,\n                                            $_SERVER,  $_COOKIE),  логи  запросов,  а  так  же  любые  другие  по   желанию\n                                            разработчика. Встроенный парсер позволяет просматривать  подробности  о  каждой\n                                            ошибке на отдельной странице.\n                                            Предусмотрено два уровня событий - \"Информационное сообщение\" и  \"Ошибка\".  Так\n                                            же система событий имеет развитую систему кодов ошибок\n                                            Игрокам показывается ID ошибки в БД, а не  её  порядковый  номер.  Это  заметно\n                                            ускоряет поиск нужной записи об ошибке\n                                            Изменения Тёмной Материи вынесены из глобального лога в отдельную таблицу.  Это\n                                            существенно облегчило поиск неисправностей на сервере и подозрительных действий\n                                            пользователей. Старые записи перенесены в отдельную таблицу\n                                        </li>\n                                        <li>\n                                            <h4>xCache</h4>\n                                            Движок  теперь   поддерживает   xCache.   Использование   xCache   настоятельно\n                                            рекомендуется - это заметно разгрузит сервер и уменьшит количество обращений  к\n                                            БД. Хотя движок может работать и без кэшера, в дальнейшем СН все  больше  будет\n                                            оптимизироваться для использование opcode-cacher\n                                        </li>\n                                        <li>\n                                            <h4>Sypex Dumper</h4>\n                                            В дистрибутив добавлена копия Sypex Dumper v2.0.8 (freeware)  для  создания\n                                            резервных копий (и, естественно, их восстановления)\n                                        </li>\n                                        <li>\n                                            <h4>Прочее</h4>\n                                            Закрыты все обнаруженные дыры в  безопасности  (SQL-injection  через  параметры\n                                            страницы) на страницах, доступных пользователям.  Так  же  полностью  исключена\n                                            возможность начисления ТМ посредством взлома извне и изменение  прав  отдельных\n                                            игроков\n                                            Офицеры отвязаны от конкретных цифр и привязаны к константам-идентификаторам\n                                            Удалено множество лишних файлов\n                                            Таблицы 'lunas' и 'galaxy' больше  не  используются.  Вся  их  функциональность\n                                            перенесена в таблицу 'planets'\n                                            Шаблоны даты и времени везде заменены константами.\n                                            Все PHP-deprecated функции заменены более современными аналогами\n                                            Устаревшая функция SYS_mysqlSmartEscape заменена на соответствующие\n                                            Устаревшие массивы $pricelist, $resources,  $reslist,  $sn_groups,  $CombatCaps\n                                            заменены в коде на $sn_data\n                                            Введена дополнительная защита от взлома. Теперь  член  команды  игры  не  может\n                                            назначить кому-либо уровень доступа, равный или больший своего.  Таким  образом\n                                            через админку невозможно назначить второго  Администратора.  Однако  это  можно\n                                            проделать напрямую в БД\n                                        </li>\n                                    </ul>\n                                </li>\n                            </ul>\n                        </div>\n                    </div>\n                </li>\n                <li class=\"chapter\" id=\"chapter_111\">\n                    <h2 class=\"heading\">Прочее</h2>\n                    <div class=\"desc\">\n                        [@] ВНИМАНИЕ!!! Удален скин 'xnova'\n                        Из дистрибутива игры удален скин 'xnova' из-за занимаемого им размера\n                        Скачать скин можно с основного сайта проекта по ссылке\n                        http://supernova.ws/files/skins/supernova-skin-xnova.zip\n                        либо с SourceForge по ссылке\n                        http://sourceforge.net/projects/supernova-ws/files/skins/\n                        [@] Среда разработки изменена на WAMP Server 2.2. Конфигурация:\n                        MySQL 5.1.41\n                        Apache 2.2\n                        PHP 5.2.9-2 + xCache 1.3.2\n                        [@] JS: Обновлен jQuery до версии 1.7.1. Обновлен jQuery-UI до версии 1.8.17\n                        [@] $sn_data['groups']['prod'] =&gt; $sn_data['groups']['factories']\n                        [@] SYS: Устаревшие функции заменены актуальными аналогами:\n                        int_buildCounter =&gt; tpl_parse_planet\n                        GetTargetDistance, GetMissionDuration, GetFleetConsumption =&gt;\n                        flt_travel_data\n                        GetShipConsumption, get_ship_speed =&gt; get_ship_data\n                        GetFleetMaxSpeed =&gt; flt_fleet_speed\n                        [@] SYS: В описании структуры кораблей (vars.php) данные о двигателях  вынесены\n                        в  отдельный  массив  'engine'.  Теперь  можно   указывать   неограниченное\n                        количество двигателей для апгрейда корабля\n                        [@] Новости:  На  странице  новостей,  странице  Императора  и  обзоре  планеты\n                        рендерятся одной процедурой и используют один  темплейт.  Индикатор  свежих\n                        новостей теперь  ориентируется  на  дату  просмотра,  а  не  на  количество\n                        новостей\n                        [@] ТМ: Изменение ТМ в переменной $user производится в теле rpg_points_change\n                        [@] Админка/Настройки:  Состояние  все  чекбоксов  (включен/выключен)   теперь\n                        определются в темлейте\n                        [@] Все таймеры врат заменены на sn_timer\n                        [@] Вселенная: При обнаружении  планеты  с  отсутствующим  пользователем  в  БД\n                        планета удаляется с отсрочкой 24 часа\n                        [@] Темплейты\n                        Расширение файлов темплейтов изменено с \".tpl\" на \".tpl.html\" для  большего\n                        удобства разработки\n                        Теперь при использовании директивы <!--  INCLUDE  -->  НЕ  НУЖНО  указывать\n                        расширение подключаемого файла\n                        Рендерер страницы теперь подхватывает заголовок страницы, если  он  есть  -\n                        переменная PTL {PAGE_HEADER}\n                        Содержимое переменной $template_result автоматически загружаетя в  темплейт\n                        в файле index.php\n                        Файл темплейта _result_message автоматически  подгружается  при  рендеринге\n                        темплейта, если в структуре переменных темплейта есть массив 'result'\n                        [@] Файлы: Удалены неиспользуемые файлы faq.php, faq1.php, faq2.php\n                        [@] Файлы: Сильно переработана организация файлов PHP\n                        Многие процедуры поменяли свое местоположение\n                        Множество файлов теперь не грузятся  автоматически  при  старте  движка,  а\n                        грузятся лишь по потребности. В частности - все файлы  миссий  подгружаются\n                        только в менеджере летящих флотов,  а  сам  менеджер  грузится  только  при\n                        потребности в обработке флотов.  Кроме  того,  боевой  движок  подгружается\n                        только в симуляторе и при обсчете боев (Миссии \"Атака\" и \"Уничтожить\")\n                        Все это позволило заметно сократить размеры кода в памяти сервера\n                        [@] Модули: Загрузчики модулей теперь располагаются в каталоге /modules, а не в\n                        /modules/_functions\n                        [@] Документация: README преобразован в UTF8\n                        [@] Добавлена компенсация  работы  механизма  Magic  Quotes.  Подробнее  -  см.\n                        \"/docs/install.txt\", подраздел \"Magic Quotes\"\n                        [@] Юниты\n                        Добавлена дополнительный аттрибут \"max\" ко всем юнитам и его общая проверка\n                        в eco_get_build_data()\n                        [@] Меню\n                        Меню теперь является динамическим\n                        Добавлена возможность добавления иконки к пункту меню.  Иконки  берутся  из\n                        подкаталога 'icons' текущего скина\n                        Добавлена прямая поддержка CSS-стилей для элементов меню\n                        Под логотип сервера в ALT вместо 'supernova.ws' подкладывается имя сервера\n                        [@] Модули\n                        Автоматическая загрузка и регистрация модулей\n                        Автоматическое перекрытие функций методами модуля из $manifest\n                        Автоматическое подгрузка специфических пунктов меню\n                        Автоматическая загрузка конфигурации модуля из файла\n                        Теперь можно перекрывать функции методами из класса\n                        Добавлена поддержка  \"цепи  перекрытий\".  Можно  протаскивать  сквозь  цепь\n                        результат вычислений, модифицируя его на каждом шагу (см. пример реализации\n                        перекрытия mrc_get_level)\n                        Теперь в манифесте модуля можно задавать  список  констант,  которые  будут\n                        автоматически назначены при его инициализации\n                        Теперь  в  манифесте  модуля  можно  задавать  список  переменных,  которые\n                        автоматически заменят (в случае обычных переменных) или дополнят (в  случае\n                        одноуровневых массивов) соответствующие глобальные переменные.  Специальный\n                        механизм гарантирует корректную работу с константами в таких  переменных  и\n                        массивах - даже  тех,  которые  были  только  назначены  при  инициализации\n                        модуля\n                        Теперь  при  инициализации  модуля  в  цепочку  вызовов  функций  корректно\n                        инсталлируется оригинальная основная функция из движкам\n                        Система модулей переписана с учетом базовой поддержки MVC\n                        Автоматическая загрузка языков\n                        Изменена процедура инициализации  -  модули  теперь  грузятся  до  проверки\n                        наличия  страниц.  Это  сделано  на  случай,  если  модуль  добавляет  свои\n                        собственные страницы как, например, модуль Премиума и модуль Рас\n                        Теперь можно указывать  в  качестве  страницы  загрузки  файла  локализации\n                        пустое множество '' - файлы в этом массиве будут загружаться всегда\n                        Поддержка  дерева  зависимости  модулей  -  теперь  можно  делать   модули,\n                        зависящие от других модулей\n                        Автоматическая загрузка зависимых модулей в правильном порядке\n                        Устранена ошибка на случай, если модуль не возвращает темплейта. Такого  не\n                        должно происходить в нормальной ситуации\n                        [@] Подсказки: Можно задавать ширину  подсказки  для  согласования  с  основной\n                        страницей\n                        [@] Константы типов юнитов приведены к единому формату \"UNIT_xxx\"\n                        [@] Всем юнитам прописаны типы\n                        [@] События флотов\n                        Переработана система событий флотов\n                        [@] Файлы\n                        Расширение файлов локализации изменено с  \".mo\"  на  \".mo.php\"  для  лучшей\n                        поддержки в различных IDE\n                        [@] Локализация\n                        В дополнение к стандартным путям  \"language/.mo.php\"  теперь так же  поддерживаются  пути  вида  \"language/.mo.php\".  Это сделано для упрощения структуры подкаталогов в модуле\n                        [@] Очереди\n                        Упразднена  константа  MAX_BUILDING_QUEUE_SIZE.   Теперь   размер   очереди\n                        построек зданий и верфи/обороны задается переменными из таблицы 'config'  -\n                        соотвественно 'server_que_length_structures' и  'server_que_length_hangar'.\n                        По умолчанию их значения равны 5\n                        [@] Классы\n                        Новый метод  'assign_recursive'  класса  \"template\"  -  позволяет  в  одном\n                        операторе заполнить  как  переменные  темплейта,  так  и  блоки  -  включая\n                        вложенные\n                        [@] Скины\n                        Изменена организация CSS-файлов в скинах. Файл \"formate.css\" переименован в\n                        \"skin.css\". К нему присоединен в конце файл  \"default.css\".  Таким  образом\n                        сохранена последовательность загрузки стилей и при  этом  все  стили  скина\n                        находятся теперь в одном файле\n                        Теперь движок подгружает файл \"/design/css/global_server.css\" .  Этот  файл\n                        может использоваться для добавления специфичных глобальных стилей сервера -\n                        он не входит в дистрибутив и не будет перезаписан  при  обновлении  движка.\n                        Файл  грузится  после  \"global.css\"  и,  следовательно,  может  перекрывать\n                        глобальные стили \"по умолчанию\". Однако он грузится  до  скинового  CSS  и,\n                        следовательно, будет перекрыт стилями скина\n                        Изменена система раскраски меню. Теперь каждому пункту  меню  присваиваются\n                        присваиваются собственные аттрибуты HTML ID и CLASS.  КРАЙНЕ  рекомендуется\n                        производить раскраску меню через аттрибут  ID  (см.  пример  в  formate.css\n                        скина EpicBlue). Список ID элементов меню  можно  узнать  либо  в  браузере\n                        (используя  функцию  \"Inspect  Element\"  или  аналогичную),  либо  в  файле\n                        \"/includes/template.php\", функция tpl_render_menu(), переменная $sn_menu\n                        В базовый CSS перенесено цветовое кодирование чисел  и  сообщений  (ошибка,\n                        предупреждение итд). При желании они  могут  быть  перекрыти  в  CSS-файлах\n                        стилей\n                        Выделение Администрации и  премиумных  аккаунтов  проводится  через  стили.\n                        Соответственно, в основной  скин  добавлены  стили  классов  \".nick_admin\",\n                        \".nick_operator\", \".nick_moderator\" и \".nick_premium\"\n                        Добавлены  классы  \".same_alliance\"  и  \".same_player\"  для   выделения   в\n                        статистике соответственно Альянса игрока и самого игрока\n                        supernova-ivash: Скин приведен в соответствие с текущим положением дел\n                        [@] MVC\n                        Базовая поддержка MVC - встроенная система моделей и видов\n                        Все страницы, переделанные под MVC, перемещены в /includes/pages\n                        Частичная поддержка структуры MVCv2 в init.php\n                        Добавлена поддержка анонимных MVC-страниц в common.php\n                        Добавлена поддержка MVC-страниц на страницы логина/регистрации\n                        [@] Реклама\n                        Добавлена возможность управлять мета-тегами 'description' и 'keywords'  без\n                        редактирования темплейта! Их содержимое хранится в таблице 'config' в полях\n                        'adv_seo_meta_description' и 'adv_seo_meta_keywords' соответственно\n                        [@] HTTPS\n                        Теперь СН нормально работает и по HTTPS протоколу\n                        [@] Рендерер имен\n                        Добавлен механизм рендеринга имени пользователя\n                        Чат, статистика, Вселенная и страница Императора  теперь  используют  общий\n                        механизм рендеринга имени пользователя\n                        [@] Код\n                        Из файла vars.php выделены три  отдельных  файла  со  структурами,  боевыми\n                        юнитами и всеми остальными\n                        Так же добавлена дополнительная служебная информация для  того,  что  бы  в\n                        симуляторе не пропадали защитные сооружения  при  добавлении  новых  юнитов\n                        через модули\n                        Убраны неиспользуемые данные \"скорострела\"\n                        Из информации о боевых юнитов убраны ненужные данные о единичных усилениях\n                        Численные значения для  защитных  сооружений  и  ракет  заменены  везде  на\n                        константы\n                        Везде  из  текста  убраны  ссылки  на  переменную  $GLOBALS  для  поддержки\n                        рефакторинга в IDE\n                        Библиотека \"tw-sack.js\" больше не используется - она заменена на jQuery\n                        Исправлена очепятка в названии константы технологии ионного двигателя\n                        Убран неиспользуемый код \"скорострела\"\n                        js_safe_string() теперь корректно работает со строками,  где  есть  перевод\n                        строки, включая Линуксовские и Маковские форматы файлов\n                        sn_function_call теперь корректно отрабатывает несуществующие функции\n                        Добавлена функция sn_get_groups()\n                        eco_bld_tech.php теперь не использует $sn_data\n                        Оптимизирован код Альянсов\n                        Все страницы интерфейса игроков переписаны без использования $parse\n                        Теперь в doquery() префикс {{table}} не используется и не обрабатывается\n                        Страницы login.php, phalanx.php переписаны без использования $parse\n                        JS: В объявлениях скриптов все конструкции  language=\"javascript\"  заменены\n                        на type=\"text/javascript\"\n                        Теперь  движок  может   работать   с   неограниченным   количеством   типов\n                        кораблей-переработчиков\n                        Переформатирован HTML-код обзора планеты\n                        Поддержка опций движка в глобальном объекте $supernova\n                        Поддержка опций на страницах в подмассиве 'pages'\n                        [@] Меню\n                        Заменены типы элементов меню на \"lang\" там, где это было возможно\n                        Стандартное меню вынесено из файла template.php в includes/vars_menu.php\n                        Парсер меню теперь понимает вложенные конструкции и константы для типа меню\n                        'lang' - т.е. конструкции вида 'info[STRUC_MINE_METAL][description]'\n                        [@] БД\n                        В таблицу флотов добавлены идентификаторы планет старта/назначения\n                        [@] Код\n                        Расчеты уровня премиума вынесены в модуль\n                        Изменены некоторые SQL-запросы\n                        Добавлен простенький бенчмарк\n                        infos.php  теперь  использует  прямое  обращение  к  production   юнита   и\n                        подмассиву modifiers\n                        eco_bld_structures.php теперь использует обращение к подмассиву modifiers\n                        mercenaries и plans перенесены из таблицы powerup в таблицу unit\n                        Константа MAX_OVERFLOW исключена из кода\n                        Обработан eco_get_planet_caps и связанные процедуры\n                        Добавлена функция вычисления случайного числа, распределенного нормально\n                        [@] Код/БД\n                        Артефакты перенесены из таблицы игроков в таблицу юнитов\n                        Удалены лишние поля Технологий из таблицы игрока\n                        Добавлены констраинты в некоторые таблицы\n                        Удалена колонка 'que' из таблицы 'users'\n                        premium перенесен из таблицы powerup в таблицу unit\n                        Исследования и очередь исследований перенесены в соответствующие таблицы\n                        [@] Код/JS\n                        Переписаны некоторые процедуры fleet.js на использование jQuery\n                        [@] MVC\n                        $sn_i18n['pages'] -&gt; $sn_mvc['i18n']\n                        [@] Обслуживание\n                        Процедура обслуживания теперь так же удаляет боевые отчеты  UBE  старше  60\n                        дней\n                        [@] Модули\n                        Изменен алгоритм слияния массивов переменных в модулях\n                    </div>\n                </li>\n                <li class=\"chapter\" id=\"chapter_112\">\n                    <h2 class=\"heading\">База данных</h2>\n                    <div class=\"desc\">\n                        Добавлен патч для mysql-серверов со включенным STRICT_TRANS_TABLES\n                    </div>\n                </li>\n                <li class=\"chapter\" id=\"chapter_113\">\n                    <h2 class=\"heading\">Модульная система</h2>\n                    <div class=\"desc\">\n                    </div>\n                </li>\n                <li class=\"chapter\" id=\"chapter_114\">\n                    <h2 class=\"heading\">Динамическое перекрытие функций</h2>\n                    <div class=\"desc\">\n                        <p>Любая стандартная функция СН может быть перекрыта пользователем с  минимальными\n                            изменениями в основном коде  движка.  Данный  функционал  обеспечивает  базовые\n                            возможности по созданию модулей. Ниже изложен алгоритм работы перекрытия\n                            При старте движка он грузит все PHP файлы из каталога /modules. В  этих  файлах\n                            должны  содержатся  альтернативные  функции  и  указания  движку  на  включения\n                            перекрытия.  См.  файл  /modules_example.php  для  примера  перекрытия  функции\n                            display() с последующим вызовом стандартной функции\n                        </p>\n                        <p> ВНИМАНИЕ! Движок всегда  грузит  все  файлы  из  каталога  перекрытия  функций,\n                            поэтому что бы не увеличивать размер процесса PHP в памяти  следует  держать  в\n                            каталоге только используемые модули\n                        </p>\n                        <div class=\"sub\">\n                            <h3 class=\"title\">Функции-оболочки</h3>\n                            <ul>\n                                <li>\n                                    Не все функции в настоящий момент поддерживают  перекрытие  (см.  список  таких\n                                    функций в следующем разделе), однако простой модификацией кода можно  перекрыть\n                                    любую функцию без потери обратной совместимости. Рассмотрим пример  модификации\n                                    кода  для  перекрытия  функции  расчета  максимальное   количество   полей   на\n                                    планете/луне     <code>eco_planet_fields_max().</code>     Она     находится     в     файле\n                                    /includes/general.php\n                                    Для начала нужно переименовать стандартную функцию. Открываем указанный файл\n                                    /includes/general.php\n                                    ищем строку\n                                    <code> function eco_planet_fields_max($planet)</code>\n                                    и заменяем её на строку\n                                    <code>function sn_eco_planet_fields_max($planet)</code>\n                                    Префикс \"sn_\" нужен для нормальной работы системы перекрытия в случае, если  не\n                                    будет найдена новая функция\n                                    Теперь добавляем в тот же файл функцию-оболочку:\n                                    <code>\n                                    function eco_planet_fields_max($planet){\n                                    $func_args = func_get_args(); // Для совместимости с PHP &lt;= 5.2.x\n                                    return sn_function_call('eco_planet_fields_max', $func_args);\n                                    }\n                                    </code>\n                                    В кавычках указывается имя перекрываемой  функции.  Параметры  функции-оболочки\n                                    должны быть идентичны параметрам  оригинальной  функции.  Теперь  движок  может\n                                    корректно работать с этой  функцией,  даже  если  отсутствует  пользовательская\n                                    функция\n                                    Допустим, мы хотим, что бы каждая  планета  или  луна  имела  на  500  полей\n                                    больше, чем позволяет её размер. Создаем файл модуля в каталоге\n                                    /modules\n                                    Имя файла может быть любым, но для облегчения дальнейших модификаций желательно\n                                    называть  модуль  именем  подменяемой  функции.  Поэтому  создаем  в  указанном\n                                    каталоге файл\n                                    eco_planet_fields_max_my.php\n                                    и копируем в него следующее содержимое\n                                    <code>\n                                    <?php\n                                        $GLOBALS['functions']['eco_planet_fields_max'] = 'eco_planet_fields_max_my';\n                                        function eco_planet_fields_max_my($planet)\n                                        {\n                                          return $planet['field_max'] + 500;\n                                        }\n                                        ?>\n                                    </code>\n                                    Пояснения по коду. Первая и последняя строка указывают интерпретатору, что файл\n                                    содержит код PHP. Вторая строка содержит указание движку по перекрытию  функции\n                                    eco_planet_fields_max() функцией eco_planet_fields_max_my(). Строки  с  третьей\n                                    по шестую содержат саму пользовательскую функцию\n                                    PROFIT!\n                                    ВНИМАНИЕ!!! PHP 5.3.1 содержит  баг,  который  делает  невозможной  полноценную\n                                    работу СН начиная с v33a12! Обновите PHP, или сделайте откат  до  более  ранней\n                                    версии PHP, или используйте предыдущую версию СН.\n                                    Описание бага: <a href=\"https://bugs.php.net/bug.php?id=50394\">https://bugs.php.net/bug.php?id=50394</a>\n                                    <h4>Функции, поддерживающие перекрытие</h4>\n                                    display()\n                                    mrc_get_value()\n                                    mrc_modify_value()\n                                </li>\n                            </ul>\n                        </div>\n                    </div>\n                </li>\n                <li class=\"chapter\" id=\"chapter_115\">\n                    <h2 class=\"heading\">Сервер обновлений</h2>\n                    <div class=\"desc\">\n                        <p>Запущен сервер обновлений. В настоящее  время  сервер  обновлений  поддерживает\n                            следующие функции: проверка версии движка и регистрация  сайта.  Доступ  к  ним\n                            осуществляется из админки со страницы настроек сервера\n                        </p>\n                    </div>\n                </li>\n                <li class=\"chapter\" id=\"chapter_116\">\n                    <h2 class=\"heading\">Технические детали</h2>\n                    <div class=\"desc\">\n                        <p>Движок сервера общается с сервером обновлений по протоколу HTTP.\n                            Если  установлен  CURL  и  подключен  к  PHP,  то  для  проверки  версии  будет\n                            использован именно он. Убедитесь, что CURL правильно настроен  и  ему  разрешен\n                            доступ к внешним ресурсам\n                        </p>\n                        <p>Если CURL не  установлен,  будет  осуществлена  попытка  получить  версию через\n                            file_get_contents(). Убедитесь, что в  PHP  разрешается  обращаться  к  внешним\n                            сайтам через соответствующую функцию\n                        </p>\n                    </div>\n                </li>\n                <li class=\"chapter\" id=\"chapter_117\">\n                    <h2 class=\"heading\">Проверка версии</h2>\n                    <div class=\"desc\">\n                        <p>При проверке версии передаются только анонимные данные  -  текущая  версия  БД,\n                            номер  релиза  и  версия  игры.  Результат  проверки  -  рекомендация   сервера\n                            обновлений о необходимости обновления текущей версии игры.\n                        </p>\n                        <p>Результат и время последней проверки выводится  в  левом  меню  и  на  странице\n                            настроек. Предусмотрено цветовое кодирование результатов  проверки:  зеленый  -\n                            обновление необязательно, желтый -  желательно  обновить  движок,  оранжевый  -\n                            крайне рекомендуется обновление, красный - ошибка проверки версии\n                            Есть два варианта проверки версии: ручная и автоматическая\n                            Ручная проверка версии выполняется в ручном режиме по нажатию кнопки \"Проверить\n                            версию\" на странице настроек.\n                        </p>\n                        <p>Автоматическая  проверка  версии  (по  умолчанию  -   отключена)   производится\n                            самостоятельно движком по расписанию.  Период  автоматической  проверки  версии\n                            задается в секундах в таблице config переменной server_updater_check_period. По\n                            умолчанию период проверки равен 24 часам (раз в сутки).\n                        </p>\n                    </div>\n                </li>\n                <li class=\"chapter\" id=\"chapter_118\">\n                    <h2 class=\"heading\">Регистрация сервера</h2>\n                    <div class=\"desc\">\n                        <p>Регистрация  сервера  нужна  для  ряда  запросов  к  серверу  обновлений.   При\n                            регистрации  передается  минимум  информации,  необходимой  для   идентификации\n                            сервера:\n                        </p>\n                        <ul>\n                            <li> 1. Полный URL сервера - т.е. HTTP-адрес и подкаталог  сервера.  Это  необходимо\n                                для первичной идентификации сервера. Полный путь необходим для того, что  бы\n                                различать несколько копий СуперНовы, установленных на одном IP или домене.\n                                Пример полного URL: http://myserver.com/myfolder/\n                            </li>\n                            <li> 2. Внутреннее название сервера. Используется для подстановки в сообщения.\n                                Идентификационная информация сервера хранится  в  таблице  \"config\"  -  ключ  в\n                                записи \"server_updater_key\", а идентификатор - в записи \"server_updater_id\".\n                            </li>\n                        </ul>\n                        <div class=\"sub\">\n                            <h3 class=\"title\">Регистрация - зачем она?</h3>\n                            <ul>\n                                <li>\n                                    <p>Зачем  вообще  регистрировать  свой   сервер?   В   будущем   планируется   ряд\n                                        возможностей, которые буду доступны только зарегистрированным  серверам.  В  их\n                                        число входит (отсортированы по запланированным срокам реализации):\n                                    </p>\n                                    <ul>\n                                        <li>1. Автоматическое получение чейнджлога</li>\n                                        <li>2. Автоматизированное обновление движка</li>\n                                        <li>3. Участие в рейтинге серверов</li>\n                                        <li>4. Багрепорты от администраторов серверов</li>\n                                        <li>5. Чат для администраторов серверов</li>\n                                        <li>6. По запросу - удаленная диагностика сервера</li>\n                                        <li>7. ...и многое, многое другое</li>\n                                    </ul>\n                                    <h4>Регистрация - почему сейчас?</h4>\n                                    Зачем регистрировать свой сервер прямо сейчас?\n                                    <ul>\n                                        <li>1. Запросы  от  администраторов  зарегестрированных  серверов  имеют    больший\n                                            приоритет при диагностике проблем и обработке багрепортов.\n                                        </li>\n                                        <li>2. При регистрации кроме  индивидуального  ключа  серверу  выдается  уникальный\n                                            идентификационный  номер,  который  будет   использоваться   при   первичной\n                                            сортировке  серверов.  Чем  раньше  будет  зарегистрирован  сервер  -   тем,\n                                            например, выше он будет в общем каталоге серверов...\n                                        </li>\n                                    </ul>\n                                </li>\n                            </ul>\n                        </div>\n                    </div>\n                </li>\n                <li class=\"chapter\" id=\"chapter_119\">\n                    <h2 class=\"heading\">Copyright</h2>\n                    <div class=\"desc\">\n                        <p> СуперНова copyright (c) 2009 Gorlum для http://supernova.ws\n                            Project \"SuperNova.WS\" copyright (c) 2009 by Gorlum for http://supernova.ws\n                            Lightly based on xNova 0.8b RageRepack v226\n                        </p>\n                    </div>\n                </li>\n                <li class=\"chapter\" id=\"chapter_120\">\n                    <h2 class=\"heading\"></h2>\n                    <div class=\"desc\">\n                        <p>Updated: 2013-10-13 22:27 Project \"SuperNova.WS\" Release 37 37c0</p>\n                    </div>\n                </li>\n            </ul>\n        </div>\n    </body>\n</html>\n"
  },
  {
    "path": "docs/html/stylesheet.css",
    "content": "/*\n\tThe original \"prosilver\" theme for phpBB3\n\tCreated by subBlue design :: http://www.subBlue.com\n*/\n\n* { margin: 0; padding: 0; }\n\nhtml { font-size: 100%; height: 100%; margin-bottom: 1px; }\n\nbody {\n\tfont-family: Verdana, Helvetica, Arial, sans-serif;\n\tcolor: #828282;\n\tbackground-color: #FFFFFF;\n\tfont-size: 12px;\n\tmargin: 0;\n\tpadding: 12px 0;\n}\n\nimg { border-width: 0; }\n\np {\n\tline-height: 1.3em;\n\tfont-size: 1.1em;\n\tmargin-bottom: 1.5em;\n}\n\nhr {\n\tborder: 0 none #FFFFFF;\n\tborder-top: 1px solid #CCCCCC;\n\theight: 1px;\n\tmargin: 5px 0;\n\tdisplay: block;\n\tclear: both;\n}\n\nhtml, body {\n\tcolor: #536482;\n\tbackground-color: #FFFFFF;\n}\n\n#doc-description h1 {\n\tfont-family: \"Trebuchet MS\", Arial, Helvetica, sans-serif;\n\tmargin-right: 200px;\n\tcolor: #FFFFFF;\n\tmargin-top: 15px;\n\tfont-weight: bold;\n\tfont-size: 2em;\n\tcolor: #fff;\n}\n\nh1 {\n\tfont-family: \"Trebuchet MS\", Arial, Helvetica, sans-serif;\n\tfont-weight: normal;\n\tcolor: #000;\n\tfont-size: 2em;\n\tmargin: 0.8em 0 0.2em 0;\n}\n\nh2 {\n\tfont-family: \"Trebuchet MS\", Arial, Helvetica, sans-serif;\n\tfont-weight: normal;\n\tcolor: #28313F;\n\tfont-size: 1.5em;\n\tmargin: 0.8em 0 0.2em 0;\n}\n\nh3 {\n\tfont-family: Arial, Helvetica, sans-serif;\n\tfont-weight: bold;\n\tborder-bottom: 1px solid #CCCCCC;\n\tmargin-bottom: 3px;\n\tpadding-bottom: 2px;\n\tfont-size: 1.05em;\n\tcolor: #115098;\n\tmargin-top: 20px;\n}\n\nh4 {\n\tfont-family: Arial, Helvetica, sans-serif;\n\tfont-weight: bold;\n\tmargin-bottom: 3px;\n\tpadding-bottom: 2px;\n\tfont-size: 1.05em;\n\tcolor: #115098;\n\tmargin-top: 20px;\n}\n\n.good { color: green; }\n.bad { color: red; }\n\n.version {\n\tmargin-top: 20px;\n\ttext-align: left;\n\tfont-size: 70%;\n\tcolor: #006600;\n\tborder-top: 1px solid #ccc;\n}\n\ncode { \n\tcolor: #006600; \n\tfont-weight: normal; \n\tfont-family: 'Courier New', monospace; \n\tborder-color: #D1D7DC; \n\tborder-width: 1px; \n\tborder-style: solid; \n\tbackground-color: #FAFAFA; \n}\n\n#wrap {\n\tpadding: 0 20px;\n\tmin-width: 650px;\n}\n\n#simple-wrap {\n\tpadding: 6px 10px;\n}\n\n#page-body {\n\tmargin: 4px 0;\n\tclear: both;\n}\n\n#page-footer {\n\tclear: both;\n}\n\n#logo {\n\tfloat: left;\n\twidth: auto;\n\tpadding: 10px 13px 0 10px;\n}\n\na#logo:hover {\n\ttext-decoration: none;\n}\n\n#doc-description {\n\tfloat: left;\n\twidth: 70%;\n}\n\n#doc-description h1 {\n\tmargin-right: 0;\n}\n\n.headerbar {\n\tbackground: #ebebeb none repeat-x 0 0;\n\tcolor: #FFFFFF;\n\tmargin-bottom: 4px;\n\tpadding: 0 5px;\n}\n\nspan.corners-top, span.corners-bottom, span.corners-top span, span.corners-bottom span {\n\tfont-size: 1px;\n\tline-height: 1px;\n\tdisplay: block;\n\theight: 5px;\n\tbackground-repeat: no-repeat;\n}\n\nspan.corners-top {\n\tbackground-image: none;\n\tbackground-position: 0 0;\n\tmargin: 0 -5px;\n}\n\nspan.corners-top span {\n\tbackground-image: none;\n\tbackground-position: 100% 0;\n}\n\nspan.corners-bottom {\n\tbackground-image: none;\n\tbackground-position: 0 100%;\n\tmargin: 0 -5px;\n\tclear: both;\n}\n\nspan.corners-bottom span {\n\tbackground-image: none;\n\tbackground-position: 100% 100%;\n}\n\n.paragraph {\n\tpadding: 0 10px;\n\tmargin-bottom: 4px;\n\tbackground-repeat: no-repeat;\n\tbackground-position: 100% 0;\n\tbackground-color: #ECF3F7;\n}\n\n.paragraph:target .content {\n\tcolor: #000000;\n}\n\n.paragraph:target h3 a {\n\tcolor: #000000;\n}\n\n.content {\n\tcolor: #333333;\n}\n\n.content h2, .panel h2 {\n\tcolor: #115098;\n\tborder-bottom-color:  #CCCCCC;\n}\n\na:link\t{ color: #898989; text-decoration: none; }\na:visited\t{ color: #898989; text-decoration: none; }\na:hover\t{ color: #d3d3d3; text-decoration: underline; }\na:active\t{ color: #d2d2d2; text-decoration: none; }\n\nhr {\n\tborder-color: #FFFFFF;\n\tborder-top-color: #CCCCCC;\n}\n\n.menu {\n\tbackground-color: #cadceb;\n}\n\n.headerbar {\n\tbackground-color: #12A3EB;\n\tbackground-image: url(\"bg_header.gif\");\n\tcolor: #FFFFFF;\n}\n\n.panel {\n\tbackground-color: #ECF1F3;\n\tcolor: #28313F;\n}\n\n\nspan.corners-top {\n\tbackground-image: url(\"corners_left.png\");\n}\n\nspan.corners-top span {\n\tbackground-image: url(\"corners_right.png\");\n}\n\nspan.corners-bottom {\n\tbackground-image: url(\"corners_left.png\");\n}\n\nspan.corners-bottom span {\n\tbackground-image: url(\"corners_right.png\");\n}\n\n.error {\n\tcolor: #BC2A4D;\n}\n\na:link\t{ color: #105289; }\na:visited\t{ color: #105289; }\na:hover\t{ color: #D31141; }\na:active\t{ color: #368AD2; }\n\n.paragraph span.corners-top, .paragraph span.corners-bottom {\n\tmargin: 0 -10px;\n}\n\n.content {\n\tpadding: 0;\n\tline-height: 1.48em;\n\tcolor: #333333;\n}\n\n.content h2, .panel h2 {\n\tcolor: #115098;\n\tborder-bottom-color:  #CCCCCC;\n}\n\n.notice {\n\tborder-top-color:  #CCCCCC;\n}\n\n.codebox {\n\tpadding: 3px;\n\tbackground-color: #FFFFFF;\n\tborder: 1px solid #C9D2D8;\n\tfont-size: 1em;\n\tmargin-bottom: 10px;\n\tdisplay: block;\n\tfont: 0.9em Monaco, \"Andale Mono\",\"Courier New\", Courier, mono;\n\tline-height: 1.3em;\n}\n\n* html hr { margin: 0; }\n* html span.corners-top, * html span.corners-bottom { background-image: url(\"corners_left.gif\"); }\n* html span.corners-top span, * html span.corners-bottom span { background-image: url(\"corners_right.gif\"); }\n\n.back2top {\n\tclear: both;\n\theight: 11px;\n\ttext-align: right;\n}\n\n.content ol, .content ul {\n\tmargin-left: 25px;\n\tmargin-top: 0;\n}\n\n.content ul + p, .content ul + div {\n\tmargin-top: 20px;\n}\n\n.comment {\n\tcolor: green;\n}\n\n.indent {\n\tmargin-left: 20px;\n}\n\n.paragraph table  {\n\tfont-size:\t8pt;\n\tborder-collapse: collapse;\n\tborder: 1px solid #cfcfcf;\n\tmargin-bottom: 20px;\n}\n\n.paragraph table caption  {\n\tdisplay: none;\n}\n\n.paragraph table thead  {\n\tbackground-color: #cadceb;\n\tcolor: #000;\n}\n\n.paragraph table td, .paragraph table th {\n\tborder: 1px solid #006699;\n\tpadding: 0.5em;\n\tbackground-color: #e1ebf2;\n}\n\n.paragraph table th {\n\tbackground-color: #cadceb;\n}\n\n.paragraph table td dl {\n\tmargin: 0;\n\tpadding: 0;\n}\n\n.paragraph table td dl dt {\n\tfloat: left;\n\tclear: both;\n\tmargin-right: 1em;\n}\n"
  },
  {
    "path": "docs/install-en.md",
    "content": "# Project \"SuperNova.WS\"\n## Disclaimer\nWARNING! The project is in alpha stage!  Currently,  he  is  not  designed  for\nproduction-use! Code is provided \"as-is\". You use at your own risk! The  author\nis not liable for material, moral, karmic, spiritual,  and  any  other  damages\ncaused you to use, non-, the very existence of this code or any other way.\n\nWARNING! Although Supernova is a oGame clone, it is not like offical  game!  Ie\nmany aspects of the game differ from the official oGame as well as from the RR.\nSlider changed, that would fit my understanding of an  interesting  game.  Take\nWith this in mind when deciding - to install this engine yourself or not.\n\nWARNING! Status of \"Supernova\" - the alpha  version.  Practically,  this  means\nthat the next update can completely change a certain aspect of the game.\n\nApproximate engine development plan contained in the file /docs/changelog.todo\n\nCode licensed under the GNU  GENERAL  PUBLIC  LICENSE  Version  2,  June  1991.\nLicense itself is in the file docs/license.txt distribution.\n\n\n### Advance notice of the need for training\nThese instructions assume the ability to  customize  and  use  third-party  Web\nhosting,  familiarity  with  MySQL  and  PHP,  access  to   the   tools   MySQL\nadministration and hosting. If you do not have the  experience  of  independent\nset of sites - this distribution you will not want\n\nWARNING! I can not test all possible combinations and versions of MySQL & PHP &\nXCache & Web Servers! This means that for  some  combinations  and  environment\nsettings engine can not work.  It  is  this  requires  skills  setting  up  and\nconfiguring web servers.\n\n\n## Requirements\n- MySQL 5.x - STRICT_TRANS_TABLES mode must be disabled\n- PHP >= 5.5\n- Web-server\n- XCache> = 1.2.h - optional, but very, very desirable. Without XCache not\n                  will work some chips and increase significantly the load\n                  to MySQL.\n\n\nSuperNova being developed on Windows with WAMP Server 3.0.8. WAMP Configuration:\n-  MySQL 5.7.14\n-  Apache 2.4.23\n-  PHP 5.6.25 + xCache 1.3.2\nLive servers works under CentOS + lighttpd + xCache\n\nOn localhost SuperNova ALWAYS works with display_errors=1\n\n\n## Location Engine\nThe engine can run from anywhere on the site, including subdirectories.  Ie  it\nmay be installed at the following addresses\n*  http://domain.com\n*  http://my.very.deep.domain.on.crappy.hoster.org\n*  http://hosting.com/ogame/\nand so on.\n\n\n## Permissions of the Web server\nIn general, the engine can run from the web server with access rights \"only  to\nreading. \"However, for the correct operation of individual  subsystems  account\nWeb server must be allowed to write in separate files/folders. Below list  of\nsubsystems and associated files/folders\n* Cache subsystem template - directory /cache. Without these templates  every\n    time will be rendered anew\n* Avatar subsystem - directory /images/avatar\n* For the subsystem warning about attempts to hack  the  web  server  account\n    should be allowed to write to the disk file/badqrys.txt.  Without  this\n    \"Bad\" requests will not be saved\n\n\n## Custom modifications\nIf you use your own skin, template, or localization, does not change\nBUILT-IN NOW! Subsequent changes in the repository can overwrite your\nadd! Make a copy of a skin/template/location under a different name and have\nwith a copy to have fun. Change the skin/template/default location can be at\nconfiguration page server.\n\n\n## Installation, configuration database\nWARNING! Mode STRICT_TRANS_TABLES (variable sql_mode) must be disabled!\n\nWARNING! All tables are stored in CH InnoDB! By default, MySQL does not\nConfigured for normal operation with the bases InnoDB! Default settings will suffice MySQL\nto work up to 50 users online. If you are planning more\nsimultaneous players, then you need to configure MySQL to suit your\nserver.\n\nAs an example, cite your MySQL server configuration (2GB memory, server\nallocated solely to heart failure and supportive forum)\n```\ninnodb_additional_mem_pool_size 20971520\ninnodb_buffer_pool_size 536870912\ninnodb_flush_log_at_trx_commit 0\ninnodb_flush_method O_DIRECT\ninnodb_log_buffer_size 8388608\n```\nEspecially pay attention to the variable innodb_flush_log_at_trx_commit!\n\nIt is also to facilitate disaster recovery, it is recommended to keep each\nInnoDB table in a separate file. To do this, add a configuration file\nDirective\n```\ninnodb_file_per_table\n```\n\n## Installing, configuring a Web server\nSN is designed for use keshera op-codes xCache. Although the engine can\nwork without it, this mode is not regular. No keshera op-codes\nSome features of the engine will be blocked, as well as increase the load\nto MySQL.\n\nLike any kesher op-codes, xCache require special configuration Web server.\nHow to configure a web server to work with xCache can be found on the Internet\n(Www.google.com) and on the home page xCache (http://xcache.lighttpd.net/)\n\n\n## The installation, base\n 1. Create a database \"supernova\" UTF-8\n 2. Create a MySQL user \"supernova_user\" and give it all right to\n    base \"supernova\"\n 3. Load the database file/docs/supernova.sql\n 4. Copy the file/docs/config.php.sample the root of the supernova and\n    rename it to config.php\n 5. In the file/config.php on line\n    \"Pass\" => \"MYSQL_PASSWORD\",// ??Password to access the MySQL server.\n    MYSQL_PASSWORD to replace the password database\n 6. In the file/config.php on line\n    \"Secretword\" => \"SUPERNOVA\",// ??keyword to create Cookies\n    SUPERNOVA replace any sequence of characters\n 7. Load distribution of the supernova in the root directory of the Web server\n 8. Turn game on in DB: in table `sn_config` change value of variable\n    `game_disable` to 0\n 9. Go into the game with admin rights. The default admin account:\n    Username: 'admin' (without the quotes)\n    Password: 'admin' (without the quotes)\n10. MUST change the administrator password on the page/options.php\n11. Set up the game on the page/admin/settings.php\n12. Supernova is ready to launch!\n13. If something went wrong - see below section \"Troubleshooting install/update\"\n\nWARNING! Scrap with team members (auth_level> 0) do not fall out!  It's  not  a\nbug, it's made specifically to enhance the security  of  the  server!  For  the\ngame, and Administration need to  use  different  accounts!  Also  server  team\nmembers do  not  participate  in  Records  calculation  (but  do  in  statistic\ncalculation)\n\n\n## Installation, an advanced version\nSettings for database access are in the file /config.php\nGame Settings  are  in  the  table  <db_prefix>  config.  Appointment  Settings\nintuitive names of variables\n\nJust some game settings are stored in file/includes/constants.php\n\nAll changes - at your own risk\n\nWARNING! If you have changed the table prefix ('sn_' by default), then the same\nneed   to   change   the   names   of   the   tables   before    pouring    the\nfile/docs/supernova.sql!\n\n### Multiple universes for a single server\nCH supports multiple universes on a single  server.  Different  universes  must\nhave distinct prefixes within the same space of variables xCache\n\n\n## Short educational program on the GIT\nCreate a local copy of the supernova in the current directory (do not forget to\npoint at end!)\n```\n  git clone git://github.com/supernova-ws/SuperNova.git .\n```\nIn the current directory will be the latest copy  of  the  supernova  from  the\nbranch master.\n\nNow you want to copy the contents to the root server. In general, encouraged to\ndo so once on the server - to avoid problems with copy (this  was  already  the\ncase). But not all hosting companies are allowed to run program locally, so you\ncan first make a copy on your local drive.\n\nIn fact, have nothing else:) However, if you do modifications engine useful  to\nyou sleduyushaya team. Roll back all changes made to Local copy:\n```\n  git reset -=hard\n```\n\n## Upgrading to the latest version\n0. WARNING! UPDATING TO MAKE A BACKUP DATABASE AND FILE ENGINE  THAT  would  be\n   possible to roll back if the update fails!\n1. Log in to the game through an account with administrator privileges\n2. WARNING! Before you upgrade a server MUST be stopped. Done this  way:  under\n   the Administrator in the left menu appears select \"Administrator\" - click on\n   it. In the admin menu, select \"Settings,\" put check  \"Turn  off  the  game\",\n   press \"Save\" button at the bottom of the page. Only  then  can  perform  the\n   update - otherwise the result may be far from expected.\n3. If you have template caching enabled, you must delete all the files in\n   directory/cache\n4. Now update the files to the engine\n   1. If you put the game out of GIT-repository, the server at the root\n        directory, run game\n        ```\n          git pull\n        ```\n   2. In any other case - to upload an updated version of the engine\n5. Wait for the NEW version of the engine is  on  the  server!  An  attempt  to\n   update in the  process  of  copying  files  or  downloading  them  from  the\n   repository can GIT lead to unpredictable results\n6. Switch to the browser in which you are logged in as administrator and select\n   \"Browse.\" Wait for the page - this time updates the database\n7. And finally, after all this is a game you can again turn (Admin ->  Options,\n   uncheck \"Turn off the game\" and keep changes).\n8. If something went wrong - see below section \"Troubleshooting install/update\"\n\n### Upgrade from RR\nAutomatic upgrade from bases in the development of RR. Partially  upgrade  done\nautomatically launch the file update.php\n\nIn heart failure with respect to RR changed location banner. Earlier  reference\nwas\n```\n /Scripts/createbanner.php\n```\nNew Links\n```\n /Banner.php\n```\n\nUse  the  tools  the  web  server  (mod_rewrite)  to  redirect   requests.   In\nSpecifically, the rule for lighttpd is as follows:\n\n```\n# Redirects old-style banners to new one\nserver.modules + = (\"mod_rewrite\")\nurl.rewrite-once = (\n  \"^/Scripts/createbanner.php (.*)\" =>\"/banner.php $ 1 \"\n)\n```\n\n## Troubleshooting install/update\n\n### Message \"Game is not configured yet\"\nIf you see this message, then you are currently in the game under a\nnon-administrator account. In order to change your account:\n1. Log out by opening the link `(URL of your server)/logout.php`\n   1. If you were logged into the game in Impersonator mode, the game will\n     immediately redirect you to the Administrator section.\n   2. Otherwise, the game will redirect you to the login page. Enter login\n     and Administrator password\n   3. The game will redirect you to the Administrator section.\n2. Configure the game as you wish.\n3. On the server settings page /admin/settings.php, the \"Status\" tab, change\n   the state games and save changes\n\n### Other issues\nIt is impossible to predict all possible combinations of settings,  PHP,  MySQL\nand Web server. CH focuses on the \"default settings\". Therefore, in some cases,\npossible failures in the upgrade procedure.\n \nAn algorithm for automatic Pack  is\ndesigned for a single run. However, in some configurations during the  upgrade,\nerrors occur. Some of them - critical, and some can  fix  restarting  procedure\n(with or without editing database). \n\nRun the update again be using select \"Force\nupdate\" menu \"Utilities\" page of the Administrator. Please pay attention! Using\nthe forced update may lead to damage to the database (if the update itself over\nthe previously successful)! Forced update  should  be  used  only  if  standard\nupdate procedure was not successful!  If  it  did  not  help  -  should  go  to\nDiagnostiek fault\n\n## Fault diagnosis engine\nThe latest and most current version of the engine (with  the  most  recent  and\nrelevant bugs) can be downloaded at this link:\n```\nhttps://github.com/supernova-ws/SuperNova/zipball/trunk\n```\nTo pay attention to the file name is not necessary.\n\n! WARNING! Diagnosis should be performed on a separate database and separate copy\nengine! DO NOT LIVE IN diagnosis SERVER!\n\nThe standard sequence of steps in the diagnosis of this:\n1. Set in an empty database clean dump a database from an archive. Problems in this\n   stage, say the following:\n   1. The most likely - an error in setting up the MySQL server\n   2. Errors in the dump database. Perhaps you are in a time of renewal dump.\n      \"Moment\" here relative concept - it can take several days\n2. Put on a clean server and a clean database, obtained at the last step, the engine\n   from the archive. Try to run. Problems at this stage, say the following:\n   1. Errors in the configuration engine. Check the file/config.php\n   2. Errors in the HTTP-server settings (especially if the first run\n   engine)\n   3 Errors in avtoapdeytere\n   4. The engine is in the process of writing, but you got to \"moment\" commit\n3. Replace the clean database dump of your working database (do not forget if you want to AGAIN\n   change the configuration - prefix tables there, logins/passwords, etc.).\n   Problems at this stage, say the following:\n   1. Errors in the configuration engine. Check the file/config.php\n   2. Errors in avtoapdeytere\n   3. Errors in the database itself. Then a deal themselves, or pay for my work\n4. If there are no problems - then something is not specifically as a live server. Here we have\n   have to understand more - either by themselves or pay for my work\n\nBefore each stage ALWAYS need to restart the web server and MySQL - otherwise\ncan be used by the old settings from the previous version.\nOnly a true and accurate diagnostic procedure to follow will help to effectively\nidentify problems in the SN on each individual server.\n\n## URLs\nThe main project site: http://supernova.ws\n\n### Forums\nForum Project: http://forum.supernova.ws\n\nSupport Forum: http://forum.supernova.ws/viewforum.php?f=73\n\nForum for bug reports: http://forum.supernova.ws/viewforum.php?f=65\n\n### Supernova on github\nProject page: http://github.com/supernova-ws/SuperNova\n\nDownload freshest release: https://github.com/supernova-ws/SuperNova/zipball/master\n\nRepository: git://github.com/supernova-ws/SuperNova.git\n\n### Supernova on sourceforge\nProject page: http://sourceforge.net/projects/supernova-ws/\n\nDownload Engine: http://sourceforge.net/projects/supernova-ws/files/\n\nRepository: git://supernova-ws.git.sourceforge.net/gitroot/supernova-ws/supernova-ws\n\n\n# Donations\nYou can help by sending a WebMoney to purse:\n  WMZ (WM-USD) Z409323360409\n  WMR (WM-RUB) R961266352219\n  WMU (WM-UAH) U726314912308\n\nIf you use WebMoney to buy various electronic products, you\ncan a) buy the usual electronic goods at a good price b) help the project.\nhttp://gorlum.plati.ru - recharge cell, Skype, WoW - the game and the TC and other\nelectrical goods for WM. Each purchase made from this link will bring\nI a small commission.\n\nRemember! Nothing strengthens the belief in the usefulness to work as a donation!\n\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\nUpdated: 2020-07-27 V45d0\n"
  },
  {
    "path": "docs/install.md",
    "content": "# Проект \"СуперНова\"\n\"СуперНова\"  (далее  -  СН)  -  многопользовательская  браузерная  космическая стратегия, клон oGame. СН базируется на  XNova  RageRepack  v.226  (далее  RR). Данный файл содержит инструкции по установке СН\n\n## Disclaimer aka Отмазка\n**ВНИМАНИЕ!** Проект находится в стадии  альфа-версии!  В  настоящее  время  он  не\nпредназначен для  production-использования!  Код  предоставляется  \"as-is\".  Вы\nиспользуете его на свой  страх  и  риск!  Автор  не  несет  ответственности  за\nматериальный,  моральный,  кармический,  душевный   и   любой   другой   ущерб,\nпричиненный вам от использования, неиспользования, самим  фактом  существования\nэтого кода или любым другим способом.\n\n**ВНИМАНИЕ!** Хотя СН является клоном оГейм, она НЕ ЯВЛЯЕТСЯ ОФФЛАЙК!  Т.е.  многие\nаспекты игры отличаются как  от  официального  оГейма,  так  и  от  RR.  Движок\nизменен, что бы соответствовать моему пониманию об интересной игре.  Принимайте\nэто во внимание, когда решаете - устанавливать этот движок себе или нет.\n\n**ВНИМАНИЕ!** Статус проекта \"СуперНова\" - альфа-версия. Практически это означает,\nчто очередной апдейт может полностью изменить отдельный аспект игры.\n\nПримерный план развития движка содержится в файле /docs/changelog.xls\n\nКод распространяется под лицензией GNU GENERAL PUBLIC LICENSE Version  2,  June\n1991. Сама лицензия находится в файле /docs/license.txt дистрибутива.\n\nДоолнение к лицензии: движок является полностью бесплатным  до  тех  пор,  пока\nсохраняются мои копирайты. Так же вы не имеете права  продавать  сам  движок  и\nлюбые продукты на его основе (однако имеете право взимать  плату  за  доступ  к\nигре и/или продавать внутриигровые ресурсы).\n\n\n### Предуведомления о необходимости наличия квалификации\nДанные инструкции предполагают умение самостоятельно настроить или использовать\nсторонний  веб-хостинг,  знакомство  с  MySQL  и  PHP,  доступ  к  инструментам\nуправленя MySQL и  хостингом.  Если  вы  не  обладаете  опытом  самостоятельной\nнастройки сайтов - этот дистрибутив вам не подойдет\n\n**ВНИМАНИЕ!** Я не могу протестировать все возможные комбинации и  версии  MySQL  &\nPHP & XCache & веб-серверов! Это означает,  что  при  некоторых  комбинациях  и\nнастройках среды движок может  не  работать.  Именно  для  этого  нужны  навыки\nнастройки и конфигурации веб-серверов.\n\n\n## Системные требования\n- *MySQL 5.x* - режим STRICT_TRANS_TABLES должен быть ОТКЛЮЧЕН\n- *PHP >= 5.5*\n- *Web-server* - рекомендуется Lighttpd или Apache\n- *XCache >= 1.2.х* - опционально, но крайне, крайне желательно.  Без  XCache  не\n                  будут работать некоторые фишки и заметно возрастет нагрузка\n                  на MySQL.\n\nСН может работать в окружении  PHP  с  включенными  Magic  Quotes  -  в  движке\nпредусмотрены механизмы компенсации для такого случая,  однако  они  далеко  не\nуниверсальны и не распространяются, например, на ключи массивов. Поэтому крайне\nжелательно  отключить  Magic  Quotes  на   стороне   сервера.   Подробнее   см.\nниже соответствующий подраздел в разделе \"Если возникли проблемы...\"\n\nДвижок разрабатывается под Windows на WAMP Server 3.0.8. Конфигурация:\n-  MySQL 5.7.14\n-  Apache 2.4.23\n-  PHP 5.6.25 + xCache 1.3.2\nПродакшн крутится под CentOS + lighttpd + xCache\n\nНа localhost движок **ВСЕГДА** работает в режиме display_errors=1\n\n\n## Расположение движка\nДвижок может работать из любого места сайта, включая подкаталоги. Т.е. он может\nбыть установлен по адресам\n*  http://domain.com\n*  http://my.very.deep.domain.on.crappy.hoster.org\n*  http://hosting.com/ogame/\nитд.\n\n\n## Права доступа веб-сервера\nВ целом, движок может работать из-под веб-сервера с правами доступа \"только  на\nчтение\".  Однако,  для  корректной   работы   отдельных   подсистем,   аккаунту\nвеб-сервера должна быть разрешена запись в отдельный файлы/папки. Ниже приведен\nсписок подсистем и соответствующих файлов/папок\n[*] Подсистема кэширования темплейтов - каталог  /cache.  Без  этого  темплейты\n    каждый раз будут рендериться заново\n[*] Для корректной работы подсистемы  аватаров  в  PHP  должен  быть  корректно\n    настроен временный каталог и движку должна быть разрешена запись в  каталог\n    /images/avatar\n[*] Для работы подсистемы предупреждения о попытках взлома аккаунту веб-сервера\n    должна быть разрешена  запись  на  диск  в  файл  /badqrys.txt.  Без  этого\n    \"плохие\" запросы не будут сохранятся\n\n\n## Пользовательские модификации\nЕсли  вы  используете  свой  скин,  темплейт  или  локализацию,  НЕ   ИЗМЕНЯЙТЕ\nВСТРОЕННЫЕ ФАЙЛЫ! Последующие изменения в репозитории могут  перезаписать  ваши\nдополнения! Сделайте копию скина/темплейта/локализации под другим именем и  уже\nс копией веселитесь. Изменить скин/темплейт/локализацию по умолчанию  можно  на\nстранице конфигурации сервера\n\nЕсли вы используете собственные модификации кода НЕ ИЗМЕНЯЙТЕ ВСТРОЕННЫЕ  ФАЙЛЫ\nпо тем же причинам! В  СН  есть  механизм  динамической  перегрузки  функций  -\nиспользуйте его для модификации кода.  Подробнее  о  данном  функционале  можно\nпрочесть в файле /docs/readme.txt - раздел \"Динамическое перекрытие функций\"\n\n\n## Инсталляция, настройки БД\nВНИМАНИЕ! Режим STRICT_TRANS_TABLES (переменная sql_mode) должен быть ОТКЛЮЧЕН!\n\nВНИМАНИЕ! Все таблицы СН хранятся в  формате  InnoDB!  По  умолчанию  MySQL  НЕ\nНАСТРОЕН на нормальную работу с базами InnoDB! Дефолтных настроек MySQL  хватит\nна работу до 50 пользователей онлайн. Если  вы  планируете  большее  количество\nодновременных игроков, то вам нужно настроить  MySQL  в  соответствии  с  вашим\nсервером.\n\nВ качестве ПРИМЕРА привожу настройки MySQL своего сервера (2Гб  памяти,  сервер\nвыделен исключительно для работы СН и поддерживающего форума)\n```\ninnodb_additional_mem_pool_size 20971520\ninnodb_buffer_pool_size         536870912\ninnodb_flush_log_at_trx_commit  0\ninnodb_flush_method             O_DIRECT\ninnodb_log_buffer_size          8388608\n```\n\nОсобо обращаю внимания на переменную **innodb_flush_log_at_trx_commit**!\n\nТак же для облегчения восстановления после сбоев рекомендуется  хранить  каждую\nтаблицу InnoDB в отдельном файле. Для этого  нужно  добавить  в  файл  настроек\nдирективу\n**innodb_file_per_table**\n\n\n## Инсталляция, настройки веб-сервера\nСН рассчитана на  использования  кэшера  оп-кодов  xCache.  Хотя  движок  может\nработать и без него, этот  режим  не  является  штатным.  Без  кэшера  оп-кодов\nнекоторые возможности движка будут заблокированы, а так же  возрастет  нагрузка\nна MySQL.\n\nКак и любой кэшер оп-кодов, xCache нуждается в особых  настройках  веб-сервера.\nКак правильно настроить веб-сервер для работы с xCache можно узнать в интернете\n(www.google.com) и на домашней  странице  xCache  (http://xcache.lighttpd.net/)\n\n\n## Инсталляция, базовый вариант\n 1. Создайте БД \"supernova\" в UTF-8\n 2. Создайте в MySQL пользователя \"supernova_user\" и дайте ему  все  права  на\n    базу \"supernova\"\n 3. Загрузите в БД файл /docs/supernova.sql\n 4. Скопируйте файл /docs/config.php.sample в  корневой  каталог  СуперНовы  и\n    переименуйте его в config.php\n 5. В файле /config.php в строке\n    \"pass\"       => \"MYSQL_PASSWORD\", // Пароль доступа MySQL сервера.\n    замените MYSQL_PASSWORD на пароль пользователя БД\n 6. В файле /config.php в строке\n    \"secretword\" => \"SUPERNOVA\",      // Ключевое слово для создания Cookies\n    замените SUPERNOVA какой-либо последовательностью символов\n 7. Загрузите дистрибутив СуперНовы в корневой каталог веб-сервера\n 8. Включите игру в БД: в таблице `sn_config` смените значение переменной\n    `game_disable` в 0\n 9. Зайдите в игру с админскими правами. По умолчанию админский аккаунт:\n    Логин:  'admin' (без кавычек)\n    Пароль: 'admin' (без кавычек)\n10. ОБЯЗАТЕЛЬНО смените пароль администратора на странице /options.php\n11. Настройте игру на странице /admin/settings.php\n12. СуперНова готова к запуску!\n13. Если что-то пошло не так - смотрите ниже раздел \"Если возникли проблемы с\nобновлением или инсталляцией\"\n\n**ВНИМАНИЕ!** Лом с членов команды (auth_level>0) НЕ  ВЫПАДАЕТ!  Это  не  баг,  это\nсделано  специально   для   усиления   безопасности   сервера!   Для   игры   и\nадминистрирования нужно использовать РАЗНЫЕ аккаунты! По той же  самой  причине\nчлены команды по  умолчанию  не  участвуют  в  построении  таблицы  рекордов  и\nстатистике\n\nЕсли после выполнения этих действий возникли проблемы - см.ниже возможные  пути\nих разрешения в разделе \"Если возникли проблемы...\"\n\n\n## Инсталляция, продвинутый вариант\nНастройки доступа к БД находятся в файле /config.php\n\nНастройки игры находятся в  таблице  <db_prefix>config.  Назначение  настроек\nинтуитивно понятны из названий переменных\n\nТак же некоторые настройки игры хранятся в файле /includes/constants.php\n\nВсе изменения - на ваш страх и риск\n\nВНИМАНИЕ!!! Если был изменен префикс таблиц ('sn_' по  умолчанию),  то  так  же\nнадо изменить названия таблиц перед заливкой файла /docs/supernova.sql !\n\n## Несколько Вселенных на одном сервере\nСН поддерживает работу нескольких Вселенных на одном сервере. Разные  вселенные\nдолжны иметь несовпадающие префиксы в пределах одного  пространства  переменных\nxCache\n\n\n## Краткий ликбез по GIT\nСоздать локальную копию СуперНовы в текущем  каталоге  (не  забываем  точку  в\nконце!):\n```\n  git clone git://github.com/supernova-ws/SuperNova.git .\n```\nВ текущем каталоге появится самая свежая  копия  СуперНовы  из  ветки  master.\nТеперь  содержимое  нужно  скопировать  в  корневой  каталог  сервера.  Вообще,\nрекомендуется  делать  это  сразу  на  сервере  -  для  избежания   проблем   с\nкопированием (был уже такой случай). Но не у всех хостеры  разрешают  запускать\nлокально программы, поэтому можно сначала сделать  копию  на  локальном  диске.\nСобственно, больше ничего не надо :) Впрочем, если вы занимаетесь модификациями\nдвижка, вам пригодится следуюшая команда. Откатить все изменения, сделанные  на\nлокальной копии:\n```\n  git reset --hard\n```\n\nОбновление до последней версии\n==============================\n0. **ВНИМАНИЕ!!! ПЕРЕД ОБНОВЛЕНИЕМ СДЕЛАЙТЕ РЕЗЕРВНУЮ КОПИЮ БАЗЫ ДАННЫХ И  ФАЙЛОВ\n   ДВИЖКА С ТЕМ, ЧТО БЫ МОЖНО БЫЛО ОТКАТИТЬСЯ В СЛУЧАЕ НЕУДАЧНОГО ОБНОВЛЕНИЯ!!!**\n1. Залогиньтесь в игру через аккаунт с правами Администратора\n2. **ВНИМАНИЕ!!!** Перед обновленем сервер **ОБЯЗАТЕЛЬНО** нужно  остановить.  Делается\n   это так: \n   1. Находясь в игре под аккаунтом Администратора в левом меню появится пункт  \"Администратор\"  -\n   кликните на него.\n   2. В админском меню  выберите  пункт  \"Настройки\",  поставить\n   галочку \"Отключить игру\", нажать кнопку \"Сохранить\" в самом  низу  страницы.\n   Только после этого можно производить обновление - иначе результат может быть\n   далеким от ожидаемого.\n3. Если у вас включено  кэширование  темплейтов,  нужно  удалить  все  файлы  в\n   каталоге **/cache**\n4. Теперь обновите файлы движка\n   1. Если вы ставили игру из  GIT-репозитория,  то  на  сервере  в  корневом\n        каталоге игры выполните команду\n        ```\n          git pull\n        ```\n   2. В любом другом случае - загрузите на сервер обновленную версию движка\n5. **ДОЖДИТЕСЬ, ПОКА НОВАЯ ВЕРСИЯ ДВИЖКА ОКАЖЕТСЯ НА СЕРВЕРЕ!** Попытка  обновления\n   в процессе копирования файлов или  загрузки  их  из  репозитория  GIT  могут\n   привести к непредсказуемым результатам\n6. Переключитесь в браузер, в  котором  вы  залогинены  под  Администратором  и\n   выберите пункт меню \"Обзор\". Дождитесь загрузки  страницы  -  в  этом  время\n   происходит обновление БД\n7. И,  наконец,  после  всех  этих  действий   игру   можно   опять   включить\n   (Администратор -> Настройки, снять  галочку  \"Отключить  игру\"  и  сохранить\n   изменения).\n8. Если после выполнения этих действий возникли проблемы - см.ниже возможные\n   пути их разрешения в разделе \"Если возникли проблемы с обновлением или\n   инсталляцией\"\n\n### Апгрейд с RR\nАвтоматический апгрейд с баз RR в  разработке.  Частично  апгрейд  производится\nавтоматически запуском файла update.php\n\nВ СН относительно RR изменено местоположение баннера. Раньше ссылка была\n```\n  /scripts/createbanner.php\n```\nНовая ссылка\n```\n  /banner.php\n```\nИспользуйте средства веб-сервера (mod_rewrite) для перенаправления запросов.  В\nчастности, правило для lighttpd будет следующим:\n```\n#Redirects old-style banners to new one\nserver.modules += ( \"mod_rewrite\" )\nurl.rewrite-once = (\n  \"^/scripts/createbanner.php(.*)\" => \"/banner.php$1\"\n)\n```\n\n## Если возникли проблемы с обновлением или инсталляцией\n\n### Сообщение \"Игра еще не сконфигурирована\"\nЕсли вы видиет это сообщение - значит вы сейчас в игре под не-администраторским\nаккаунтом. Для того, что бы сменить аккаунт:\n1. Выйдите из системы, открыв ссылку (УРЛ вашего сервера)/logout.php\n    1. Если вы были залогинены в игру в режиме Имперсонатора - игра сразу перенаправит\n     вас в раздел Администратора.\n    2. В противном случае игра перенаправит вас на страницу входа. Введите логин\n     и пароль Администратора\n    3. Игра перенаправит вас в раздел Администратора.\n2. Сконфигурируйте игру по вашему желанию.\n3. На странице настройки сервера /admin/settings.php вкладка \"Статус\" измените состояние\n   игры и сохраните изменения\n\n### Проверьте версию PHP\nВНИМАНИЕ! PHP версии 5.3.1 содержит баг, который препятствует корректной работе\nфункции-оболочки, если в параметрах оригинальной функции  содержится  параметр,\nпередаваемый по ссылке. Поэтому начиная с 33a12 СуперНова  не  может  работать\nпод PHP этой версии! Обновите PHP на более поздний, или сделайте откат до более\nранней версии, или используйте предыдущюю версию СН!\n\nСсылка для чтения: https://bugs.php.net/bug.php?id=50394\n\n### Magic Quotes\nЕсли в отправляемых сообщениях символы \\ (обратный слэш) и \" (двойная  кавычка)\nзаменяются на \\\\ и \\\", а символ ' (одинарная кавычка) - на символы \\'  или  '',\nэто говорит о том, что на сервере  в  настройках  PHP  включен  механизм  Magic\nQuotes. Для  нормальной  работы  движка  необходимо  отключить  этот  механизм.\nСделать  это  можно   несколькими   путями   (даются   в   порядке   уменьшения\nэффективности):\n1. Напрямую в файле конфигурации сервера PHP \"php.ini\":\n    ```\n    magic_quotes_gpc = Off\n    magic_quotes_runtime = Off\n    magic_quotes_sybase = Off\n    ```\n2.  Если  нет  доступа  к  основному  файлу  конфигурации,  то  многие  хостеры\n   поддерживают  локальное  изменение  настроек  через  \".htaccess\".   Добавить\n   указанный файл в корневой каталог движка и прописать в нем строки:\n   ```\n    php_flag magic_quotes_gpc Off\n    php_flag magic_quotes_runtime Off\n    php_flag magic_quotes_sybase Off\n   ```\n3. Некоторые хостеры не  поддерживают  ни  один  из  первых  двух  пунктах,  но\n   поддерживают локальные \"php.ini\". В этом случае  нужно  создать  в  корневом\n   каталоге движка файл \"php.ini\" и добавить в него строки:\n   ```\n     magic_quotes_gpc = Off\n     magic_quotes_runtime = Off\n     magic_quotes_sybase = Off\n   ```\n   \nЕсли хостер не поддерживает ни один  из  вышеуказанных  методов,  то  движок  с\nверсии 34a0 по мере сил  постарается  скомпенсировать  \"работу\"  Magic  Quotes.\nОднако  корректность работы движка НЕ ГАРАНТИРУЕТСЯ!\n\n### \"Белый экран\"\nОчень часты жалобы следующего вида: \"Я скачал и поставил движок, а при загрузке\nстраницы в браузере только белый экран!\". Данная  неисправность  в  большинстве\nслучаев означает, что есть какая-то проблема,  но  в  настройках  PHP  отключен\nвывод ошибок. Для диагностики такого рода  проблем  необходимо  включить  вывод\nошибок. Сделать это можно в настройках PHP,  в  .htaccess  или  в  движке.  Что\nбы включить вывод ошибок в движке,  нужно  в  файле  **/includes/init.php**  второй\nстрокой вписать:\n```\n  define('BE_DEBUG', true);\n```\n\n### Если используете Альфа-версии релизов...\n...то перед сообщением об ошибке нужно:\n1. Почитать новости\n2. Обновится до последней существующей  версии  -  возможно,  в  ней  глюк  уже\nисправлен,  потому  что  я  публикую  чейнджлог  пачками  и  могу   просто   не\nопубликовать еще изменения\n\n### Fatal error: Cannot redeclare...\nФайлы движка и функции в нем могут менять местоположение от  версии  к  версии.\nПоскольку движок  в  состоянии  Альфа  -  это  нормально.  Движок  расчитан  на\nобновление   через   репозитории   GIT/SVN,   при   которых   все   перемещения\nфайлов/функций отрабатываются автоматически.  Однако  при  простом  копировании\nстарой версии движка поверх новой может возникнуть следующая ошибка:\n```\n  Fatal error: Cannot redeclare (...) in (...) on line (...)\n```\nВместо (...) - названия функций, файлов, номер строки\n\nВ этом случае необходимо ПОЛНОСТЬЮ удалить старую версию движка (сохранив  файл\nconfig.php), залить новую версию движка и подсунуть её конфигурационный файл.\n\nТе, кто разбирается немного в PHP (а таким должен быть  любой,  кто  пользуется\nтекущими версиями движка) может просто удалить лишние файлы,  определив  их  по\nсообщению об ошибке.\n\n### Ошибки с CONTRAINTs\nПосле включения отладочного режима движок может выдать уведомление об ошибках в\nCONTRAINTs. Обычно это происходит при  интенсивном  использовании  альфа-версий\nрелизов  при  апгрейде  с  пропуском  нескольких  версий.  Иногда  такое  может\nпроизойти и при апгрейде альфа/бета версии  на  релиз.  Ниже  приводится  общий\nалгоритм решения такого типа проблем - если не вмочь ждать следующей альфы  или\nобновления релиза\n\nПриведу алгоритм решение проблемы с констраинтами на примере  таблицы  users  и\nполя ally_tag\n\nНам нужно добавить вот такой constraint:\n```mysql\nADD CONSTRAINT `FK_users_ally_tag` FOREIGN KEY (`ally_tag`) REFERENCES `{$config->db_prefix}alliance` (`ally_tag`) ON DELETE SET NULL ON UPDATE CASCADE\n```\n\nОн связывает поле ally_tag таблицы users (подчиненная) с полем ally_tag таблицы\nalliance (главная). Когда поле  ally_tag  изменяется  в  таблице  alliance,  то\nсвязанное поле автоматически изменяется в таблице users. Если запись  удаляется\nиз таблицы users  -  поле  ally_tag  в  соответствующей  записи  таблицы  users\nобнуляется.\n\nЧто бы можно было сделать  такую  связь,  нужна  логическая  целостность  обоих\nтаблиц. Это означает, что:\n1. Типы и кодировка связываемых полей должны совпадать.\n2. В подчиненной таблице в свойствах поля должно стоять DEFAULT NULL\n3. Для всех связываемых полей должны существовать индексы во всех  используемых\n   таблицах\n4. На момент связывания в подчиненной таблице НЕ ДОЛЖНО быть таких  значений  в\nсвязываемом поле, которых нет в главной таблице.\n\nВ нашем случае (самый сложный) нужно: во-первых обновить все значения  ally_tag\nдо текущих:\n```php\ndoquery(\"UPDATE {{users}} AS u LEFT JOIN {{alliance}} AS a ON a.id = u.ally_id SET u.ally_tag = a.ally_tag;\");\n```\n\nво-вторых - для тех записей, для  которых  Альянсы  не  существуют,  установить\nсоответствующее поле в NULL\n```php\ndoquery(\"UPDATE {{users}} AS u LEFT JOIN {{alliance}} AS a ON a.id = u.ally_id SET u.ally_id = NULL, u.ally_tag = NULL WHERE a.id is NULL;\");\n```\n\nЕсли бы в constraint стояло бы ON DELETE CASCADE, а не ON DELETE SET  NULL,  то\nнужно было бы просто удалить из подчиненной таблицы все записи, для которых нет\nсоответствия в главной таблице. На примере constraint для таблицы shortcut:\n```mysql\nADD CONSTRAINT `FK_shortcut_planet_id` FOREIGN KEY (`shortcut_planet_id`) REFERENCES `{$config->db_prefix}planets` (`id`) ON DELETE CASCADE ON UPDATE CASCADE\n```\nСоответствующий запрос:\n```php\ndoquery('DELETE FROM {{shortcut}} WHERE shortcut_planet_id NOT IN (SELECT id FROM {{planets}});');\n```\n\n*??????*\n\n**PROFIT!**\n\nСобственно, апдейтер сам делает все нужные изменения. НО ТОЛЬКО если ранее  все\nапдейты  проходили  нормально!  Если,  например,  ранее  сбился  constraint  на\nally_id, то апдейтер НЕ СМОЖЕТ нормально отработать constraint на ally_tag.\n\nМануал для курения:\nhttp://dev.mysql.com/doc/refman/5.5/en/constraints.html\n\n### Если используете релиз\nНевозможно предсказать заранее все возможные комбинации настроек PHP,  MySQL  и\nвеб-сервера. СН ориентируется на \"установки по умолчанию\". Поэтому в  отдельных\nслучаях  возможны  сбои  в  процедуре  обновления.\n\nАлгоритм автоматического обновления рассчитан на однократный запуск.  Однако  в\nслучае  некоторых  конфигураций,  в  процессе  обновления  происходят   ошибки.\nНекоторые из них - критичны, а некоторые  можно  исправить  повторным  запуском\nпроцедуры (с или без редактирования БД).\n\nЗапустить процедуру обновления  повторно  можно  используя  пункт  \"Форсировать\nобновление\" меню \"Утилиты\" на странице Администратора.\n\nПрошу обратить внимание! Использование форсированного обновления может привести\nк порче  БД  (в  случае,  если  само  обновление  закончилось  ранее  успешно)!\nПоэтому ОБЯЗАТЕЛЬНО сделайте копию движка и БД перед форсированным  обновлением\n(даже  если вы делали копию перед апгрейдом)!\n\nФорсированное  обновление  нужно  использовать  только  в  том   случае,   если\nстандартная процедура обновления прошла неуспешно! Если  и  это  не  помогло  -\nследует перейти к разделу \"Диагностика неисправностей движка\"\n\n\n### Диагностика неисправностей движка\nСамую свежую и актуальную версию движка (с самыми свежими и актуальными багами)\nможно скачать по этой ссылке:\n```\nhttps://github.com/supernova-ws/SuperNova/zipball/trunk\n```\nНа название файла внимание обращать не нужно -  будет  загружена  самая  свежая\nверсия из ветки trunk.\n\n!!!ВНИМАНИЕ!!! Диагностику нужно проводить на отдельной БД  и  отдельной  копии\nдвижка! НИ В КОЕМ СЛУЧАЕ НЕЛЬЗЯ ПРОВОДИТЬ ДИАГНОСТИКУ НА ЖИВОМ СЕРВЕРЕ!!!\n\nСтандартная последовательность действий при диагностике такая:\n1. Установить в пустую базу данных чистый дамп БД из архива. Проблемы  на  этом\n   этапе говорят о следующем:\n   1. Наиболее вероятно - ошибки в настройке сервера MySQL\n   2. Ошибки в дампе  БД.  Возможно,  вы  попали  в  момент  обновления  дампа.\n      \"Момент\" тут понятие условное - он может растянутся на несколько дней\n2. Поставить на чистый сервер и чистую БД, полученную на прошлом  шаге,  движок\n      из архива.  Попытаться  запустить.  Проблемы  на  этом  этапе  говорят  о\n      следующем:\n      1. Ошибки в настройках движка. Проверить файл /config.php\n      2. Ошибки в настройках HTTP-сервера  (особенно  если  движок  запускается\n         первый раз)\n      3. Ошибки в автоапдейтере\n      4. Движок находится в процессе написания, а вы попали на \"момент\" коммита\n3. Заменить чистую базу дампом своей рабочей базы (не забываем СНОВА если нужно\n   изменить конфигурацию  –  префиксы  таблиц  там,  логины/пароли  и  прочее).\n   Проблемы на этом этапе говорят о следующем:\n   1. Ошибки в настройках движка. Проверить файл /config.php\n   2. Ошибки в автоапдейтере\n   3. Ошибки в самой БД. Тут либо разбираться самим, либо оплачивать мою работу\n4. Если проблем нет – значит что-то конкретно не так на живом сервере. Тут надо\n   уже разбираться подробнее - либо самостоятельно, либо оплатить мою работу\n\nПеред КАЖДЫМ этапом ОБЯЗАТЕЛЬНО нужно перезапустить веб-сервер и MySQL –  иначе\nмогут использоваться старые настройки от предыдущей версии.\nТолько верное и аккуратное следование процедуре диагностики поможет  эффективно\nвыявить проблемы в работе СН на каждом отдельно взятом сервере.\n\n\n\n# Ссылки\nОсновной сайт проекта: http://supernova.ws\n\n\n## Форумы\nФорум проекта: http://forum.supernova.ws\n\nФорум поддержки сторонних серверов: http://forum.supernova.ws/viewforum.php?f=73\n\nФорумы для багрепортов\n*  Для ошибок, уникальных для сторонних серверов (ru/en)\n    http://forum.supernova.ws/viewforum.php?f=78\n*  Для ошибок, присутствующих в текущей версии СН на официальных серверах\n    http://forum.supernova.ws/viewforum.php?f=65\n\n\n## СуперНова на github\nСтраница проекта:\n  http://github.com/supernova-ws/SuperNova\n\nСамый свежий стабильный релиз:\n  https://github.com/supernova-ws/SuperNova/zipball/master\n\nnight build - самая свежая версия следующего релиза с глюками и багами:\n  https://github.com/supernova-ws/SuperNova/zipball/trunk\n\nРепозиторий:\n  git://github.com/supernova-ws/SuperNova.git\n\n\n## СуперНова на sourceforge\nСтраница проекта:\n  http://sourceforge.net/projects/supernova-ws/\n\nЗагрузка движка:\n  http://sourceforge.net/projects/supernova-ws/files/\n\nРепозиторий:\n  git://supernova-ws.git.sourceforge.net/gitroot/supernova-ws/supernova-ws\n\n\n\n# Помощь проекту\nВы можете помочь проекту, отправив WebMoney на кошельки:\n  WMZ (WM-USD) Z409323360409\n  WMR (WM-RUB) R961266352219\n  WMU (WM-UAH) U726314912308\n\nЕсли вы пользуетесь ВебМанями для покупки различных электронных товаров, то  вы\nможете одновременно\nа) купить привычный электронный товар по хорошей цене\nб) помочь проекту.\nhttp://gorlum.plati.ru - пополнения сотовых, Skype, WoW - игра и  ТК  и  прочие\nэлектрические товары за WM. Каждая покупка, сделанная с этого  линка,  принесет\nмне небольшие комиссионные.\n\nПомните! Ничто так не укрепляет веру в нужность работы, как пожертвование!\n\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\nUpdated: 2020-07-27 V45d0\n"
  },
  {
    "path": "docs/license.txt",
    "content": "        \t    GNU GENERAL PUBLIC LICENSE\n\t\t       Version 2, June 1991\n\n Copyright (C) 1989, 1991 Free Software Foundation, Inc.\n     59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\n Everyone is permitted to copy and distribute verbatim copies\n of this license document, but changing it is not allowed.\n\n\t\t\t    Preamble\n\n  The licenses for most software are designed to take away your\nfreedom to share and change it.  By contrast, the GNU General Public\nLicense is intended to guarantee your freedom to share and change free\nsoftware--to make sure the software is free for all its users.  This\nGeneral Public License applies to most of the Free Software\nFoundation's software and to any other program whose authors commit to\nusing it.  (Some other Free Software Foundation software is covered by\nthe GNU Library General Public License instead.)  You can apply it to\nyour programs, too.\n\n  When we speak of free software, we are referring to freedom, not\nprice.  Our General Public Licenses are designed to make sure that you\nhave the freedom to distribute copies of free software (and charge for\nthis service if you wish), that you receive source code or can get it\nif you want it, that you can change the software or use pieces of it\nin new free programs; and that you know you can do these things.\n\n  To protect your rights, we need to make restrictions that forbid\nanyone to deny you these rights or to ask you to surrender the rights.\nThese restrictions translate to certain responsibilities for you if you\ndistribute copies of the software, or if you modify it.\n\n  For example, if you distribute copies of such a program, whether\ngratis or for a fee, you must give the recipients all the rights that\nyou have.  You must make sure that they, too, receive or can get the\nsource code.  And you must show them these terms so they know their\nrights.\n\n  We protect your rights with two steps: (1) copyright the software, and\n(2) offer you this license which gives you legal permission to copy,\ndistribute and/or modify the software.\n\n  Also, for each author's protection and ours, we want to make certain\nthat everyone understands that there is no warranty for this free\nsoftware.  If the software is modified by someone else and passed on, we\nwant its recipients to know that what they have is not the original, so\nthat any problems introduced by others will not reflect on the original\nauthors' reputations.\n\n  Finally, any free program is threatened constantly by software\npatents.  We wish to avoid the danger that redistributors of a free\nprogram will individually obtain patent licenses, in effect making the\nprogram proprietary.  To prevent this, we have made it clear that any\npatent must be licensed for everyone's free use or not licensed at all.\n\n  The precise terms and conditions for copying, distribution and\nmodification follow.\n\f\n\t\t    GNU GENERAL PUBLIC LICENSE\n   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION\n\n  0. This License applies to any program or other work which contains\na notice placed by the copyright holder saying it may be distributed\nunder the terms of this General Public License.  The \"Program\", below,\nrefers to any such program or work, and a \"work based on the Program\"\nmeans either the Program or any derivative work under copyright law:\nthat is to say, a work containing the Program or a portion of it,\neither verbatim or with modifications and/or translated into another\nlanguage.  (Hereinafter, translation is included without limitation in\nthe term \"modification\".)  Each licensee is addressed as \"you\".\n\nActivities other than copying, distribution and modification are not\ncovered by this License; they are outside its scope.  The act of\nrunning the Program is not restricted, and the output from the Program\nis covered only if its contents constitute a work based on the\nProgram (independent of having been made by running the Program).\nWhether that is true depends on what the Program does.\n\n  1. You may copy and distribute verbatim copies of the Program's\nsource code as you receive it, in any medium, provided that you\nconspicuously and appropriately publish on each copy an appropriate\ncopyright notice and disclaimer of warranty; keep intact all the\nnotices that refer to this License and to the absence of any warranty;\nand give any other recipients of the Program a copy of this License\nalong with the Program.\n\nYou may charge a fee for the physical act of transferring a copy, and\nyou may at your option offer warranty protection in exchange for a fee.\n\n  2. You may modify your copy or copies of the Program or any portion\nof it, thus forming a work based on the Program, and copy and\ndistribute such modifications or work under the terms of Section 1\nabove, provided that you also meet all of these conditions:\n\n    a) You must cause the modified files to carry prominent notices\n    stating that you changed the files and the date of any change.\n\n    b) You must cause any work that you distribute or publish, that in\n    whole or in part contains or is derived from the Program or any\n    part thereof, to be licensed as a whole at no charge to all third\n    parties under the terms of this License.\n\n    c) If the modified program normally reads commands interactively\n    when run, you must cause it, when started running for such\n    interactive use in the most ordinary way, to print or display an\n    announcement including an appropriate copyright notice and a\n    notice that there is no warranty (or else, saying that you provide\n    a warranty) and that users may redistribute the program under\n    these conditions, and telling the user how to view a copy of this\n    License.  (Exception: if the Program itself is interactive but\n    does not normally print such an announcement, your work based on\n    the Program is not required to print an announcement.)\n\f\nThese requirements apply to the modified work as a whole.  If\nidentifiable sections of that work are not derived from the Program,\nand can be reasonably considered independent and separate works in\nthemselves, then this License, and its terms, do not apply to those\nsections when you distribute them as separate works.  But when you\ndistribute the same sections as part of a whole which is a work based\non the Program, the distribution of the whole must be on the terms of\nthis License, whose permissions for other licensees extend to the\nentire whole, and thus to each and every part regardless of who wrote it.\n\nThus, it is not the intent of this section to claim rights or contest\nyour rights to work written entirely by you; rather, the intent is to\nexercise the right to control the distribution of derivative or\ncollective works based on the Program.\n\nIn addition, mere aggregation of another work not based on the Program\nwith the Program (or with a work based on the Program) on a volume of\na storage or distribution medium does not bring the other work under\nthe scope of this License.\n\n  3. You may copy and distribute the Program (or a work based on it,\nunder Section 2) in object code or executable form under the terms of\nSections 1 and 2 above provided that you also do one of the following:\n\n    a) Accompany it with the complete corresponding machine-readable\n    source code, which must be distributed under the terms of Sections\n    1 and 2 above on a medium customarily used for software interchange; or,\n\n    b) Accompany it with a written offer, valid for at least three\n    years, to give any third party, for a charge no more than your\n    cost of physically performing source distribution, a complete\n    machine-readable copy of the corresponding source code, to be\n    distributed under the terms of Sections 1 and 2 above on a medium\n    customarily used for software interchange; or,\n\n    c) Accompany it with the information you received as to the offer\n    to distribute corresponding source code.  (This alternative is\n    allowed only for noncommercial distribution and only if you\n    received the program in object code or executable form with such\n    an offer, in accord with Subsection b above.)\n\nThe source code for a work means the preferred form of the work for\nmaking modifications to it.  For an executable work, complete source\ncode means all the source code for all modules it contains, plus any\nassociated interface definition files, plus the scripts used to\ncontrol compilation and installation of the executable.  However, as a\nspecial exception, the source code distributed need not include\nanything that is normally distributed (in either source or binary\nform) with the major components (compiler, kernel, and so on) of the\noperating system on which the executable runs, unless that component\nitself accompanies the executable.\n\nIf distribution of executable or object code is made by offering\naccess to copy from a designated place, then offering equivalent\naccess to copy the source code from the same place counts as\ndistribution of the source code, even though third parties are not\ncompelled to copy the source along with the object code.\n\f\n  4. You may not copy, modify, sublicense, or distribute the Program\nexcept as expressly provided under this License.  Any attempt\notherwise to copy, modify, sublicense or distribute the Program is\nvoid, and will automatically terminate your rights under this License.\nHowever, parties who have received copies, or rights, from you under\nthis License will not have their licenses terminated so long as such\nparties remain in full compliance.\n\n  5. You are not required to accept this License, since you have not\nsigned it.  However, nothing else grants you permission to modify or\ndistribute the Program or its derivative works.  These actions are\nprohibited by law if you do not accept this License.  Therefore, by\nmodifying or distributing the Program (or any work based on the\nProgram), you indicate your acceptance of this License to do so, and\nall its terms and conditions for copying, distributing or modifying\nthe Program or works based on it.\n\n  6. Each time you redistribute the Program (or any work based on the\nProgram), the recipient automatically receives a license from the\noriginal licensor to copy, distribute or modify the Program subject to\nthese terms and conditions.  You may not impose any further\nrestrictions on the recipients' exercise of the rights granted herein.\nYou are not responsible for enforcing compliance by third parties to\nthis License.\n\n  7. If, as a consequence of a court judgment or allegation of patent\ninfringement or for any other reason (not limited to patent issues),\nconditions are imposed on you (whether by court order, agreement or\notherwise) that contradict the conditions of this License, they do not\nexcuse you from the conditions of this License.  If you cannot\ndistribute so as to satisfy simultaneously your obligations under this\nLicense and any other pertinent obligations, then as a consequence you\nmay not distribute the Program at all.  For example, if a patent\nlicense would not permit royalty-free redistribution of the Program by\nall those who receive copies directly or indirectly through you, then\nthe only way you could satisfy both it and this License would be to\nrefrain entirely from distribution of the Program.\n\nIf any portion of this section is held invalid or unenforceable under\nany particular circumstance, the balance of the section is intended to\napply and the section as a whole is intended to apply in other\ncircumstances.\n\nIt is not the purpose of this section to induce you to infringe any\npatents or other property right claims or to contest validity of any\nsuch claims; this section has the sole purpose of protecting the\nintegrity of the free software distribution system, which is\nimplemented by public license practices.  Many people have made\ngenerous contributions to the wide range of software distributed\nthrough that system in reliance on consistent application of that\nsystem; it is up to the author/donor to decide if he or she is willing\nto distribute software through any other system and a licensee cannot\nimpose that choice.\n\nThis section is intended to make thoroughly clear what is believed to\nbe a consequence of the rest of this License.\n\f\n  8. If the distribution and/or use of the Program is restricted in\ncertain countries either by patents or by copyrighted interfaces, the\noriginal copyright holder who places the Program under this License\nmay add an explicit geographical distribution limitation excluding\nthose countries, so that distribution is permitted only in or among\ncountries not thus excluded.  In such case, this License incorporates\nthe limitation as if written in the body of this License.\n\n  9. The Free Software Foundation may publish revised and/or new versions\nof the General Public License from time to time.  Such new versions will\nbe similar in spirit to the present version, but may differ in detail to\naddress new problems or concerns.\n\nEach version is given a distinguishing version number.  If the Program\nspecifies a version number of this License which applies to it and \"any\nlater version\", you have the option of following the terms and conditions\neither of that version or of any later version published by the Free\nSoftware Foundation.  If the Program does not specify a version number of\nthis License, you may choose any version ever published by the Free Software\nFoundation.\n\n  10. If you wish to incorporate parts of the Program into other free\nprograms whose distribution conditions are different, write to the author\nto ask for permission.  For software which is copyrighted by the Free\nSoftware Foundation, write to the Free Software Foundation; we sometimes\nmake exceptions for this.  Our decision will be guided by the two goals\nof preserving the free status of all derivatives of our free software and\nof promoting the sharing and reuse of software generally.\n\n\t\t\t    NO WARRANTY\n\n  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY\nFOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN\nOTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES\nPROVIDE THE PROGRAM \"AS IS\" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED\nOR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF\nMERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS\nTO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE\nPROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,\nREPAIR OR CORRECTION.\n\n  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING\nWILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR\nREDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,\nINCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING\nOUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED\nTO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY\nYOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER\nPROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE\nPOSSIBILITY OF SUCH DAMAGES.\n\n\t\t     END OF TERMS AND CONDITIONS\n\f\n\t    How to Apply These Terms to Your New Programs\n\n  If you develop a new program, and you want it to be of the greatest\npossible use to the public, the best way to achieve this is to make it\nfree software which everyone can redistribute and change under these terms.\n\n  To do so, attach the following notices to the program.  It is safest\nto attach them to the start of each source file to most effectively\nconvey the exclusion of warranty; and each file should have at least\nthe \"copyright\" line and a pointer to where the full notice is found.\n\n    <one line to give the program's name and a brief idea of what it does.>\n    Copyright (C) <year>  <name of author>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program; if not, write to the Free Software\n    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\n\n\nAlso add information on how to contact you by electronic and paper mail.\n\nIf the program is interactive, make it output a short notice like this\nwhen it starts in an interactive mode:\n\n    Gnomovision version 69, Copyright (C) year  name of author\n    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.\n    This is free software, and you are welcome to redistribute it\n    under certain conditions; type `show c' for details.\n\nThe hypothetical commands `show w' and `show c' should show the appropriate\nparts of the General Public License.  Of course, the commands you use may\nbe called something other than `show w' and `show c'; they could even be\nmouse-clicks or menu items--whatever suits your program.\n\nYou should also get your employer (if you work as a programmer) or your\nschool, if any, to sign a \"copyright disclaimer\" for the program, if\nnecessary.  Here is a sample; alter the names:\n\n  Yoyodyne, Inc., hereby disclaims all copyright interest in the program\n  `Gnomovision' (which makes passes at compilers) written by James Hacker.\n\n  <signature of Ty Coon>, 1 April 1989\n  Ty Coon, President of Vice\n\nThis General Public License does not permit incorporating your program into\nproprietary programs.  If your program is a subroutine library, you may\nconsider it more useful to permit linking proprietary applications with the\nlibrary.  If this is what you want to do, use the GNU Library General\nPublic License instead of this License.\n"
  },
  {
    "path": "docs/readme-en.txt",
    "content": "New version 31a9 contains locale editor.  It's  accessible  via  \"Localization\"\nmenu item in admin interface\n1. Choosing \"Localization\" menu item will open page to select \"domain\" to edit.\nDomain is a set of string which belongs to one aspect of the  game.  Domain  is\nequivalent for language file with respect name\n2. After selecting domain opens page for  editing  locale  string.  Opening  of\nlarge files and/or slow connection can take pretty much time. Please be patient\n3. After editing locale  string  and  confirming  it  editor  will  make  files\n\"<domain name>.mo.new\" in each language folder\n4. When starting locale  string  editor  files  with  \".mo.new\"  extension  has\npriority over \".mo\" files. I.e. if in any folder exists  both  types  of  files\neditor will load values from \".mo.new\". This is made to make easier editing  of\nlarge files\n5. If you want to use  new  locale  strings  you  should  change  extension  of\n\".mo.new\" file to \".mo\". Usually it will effectivly  overwrite  current  locale\nfile - so you should make backup before this operation\n6. WARNING!!! You should be very carefull when changing old locale  files  with\nnewly generated ones! Editor will resolve constant's IDs to their real  values,\nwill not honor comments  nor  empty  lines,  will  ignore  extra  PHP  code  in\nlang-files - including substitution of values in  infos.mo!  As  result  simple\nreplacing old locale file with new one can somewhat cripple localization files!\nFor some files it may be necessary to make manual merge between old and new one\nfiles!\n7. Following domains contains additional PHP-code and  REQUIRE  manual  merging\nafter editing language files: fleet, infos, login, market,  messages,  options,\nsystem\n8. Following domains using constants: alliance,  tech,  quest.  Manual  editing\nrecommended to maintain compatibility with possible  future  constants  changes\nbut currently didn't required\n"
  },
  {
    "path": "docs/readme.txt",
    "content": "Проект \"СуперНова\"\n~~~~~~~~~~~~~~~~~~~\n\"СуперНова\"  (далее  -  СН)  -  многопользовательская  браузерная  космическая\nстратегия, клон oGame. СН базируется на  XNova  RageRepack  v.226  (далее  RR).\nДанный файл содержит исключительно отличия СН от RR\n\n\nАктуальность\n============\nФайл изменен 2013-10-13 22:27\nИнформация актуальна для Project \"SuperNova.WS\" Release 37 37c0\n\n\nDisclaimer aka Отмазка\n======================\nВНИМАНИЕ! Проект находится в стадии  альфа-версии!  В  настоящее  время  он  не\nпредназначен для  production-использования!  Код  предоставляется  \"as-is\".  Вы\nиспользуете его на свой  страх  и  риск!  Автор  не  несет  ответственности  за\nматериальный,  моральный,  кармический,  душевный   и   любой   другой   ущерб,\nпричиненный вам от использования, неиспользования, самим  фактом  существования\nэтого кода или любым другим способом.\n\nВНИМАНИЕ! Хотя СН является клоном оГейм, она НЕ ЯВЛЯЕТСЯ ОФФЛАЙК!  Т.е.  многие\nаспекты игры отличаются как  от  официального  оГейма,  так  и  от  RR.  Движок\nизменен, что бы соответствовать моему пониманию об интересной игре.  Принимайте\nэто во внимание, когда решаете - устанавливать этот движок себе или нет.\n\nВНИМАНИЕ! Статус проекта \"СуперНова\" - альфа-версия. Практически это означает,\nчто очередной апдейт может полностью изменить отдельный аспект игры.\n\nПримерный план развития движка содержится в файле /docs/changelog.xls\n\nКод распространяется под лицензией GNU GENERAL PUBLIC LICENSE Version  2,  June\n1991. Сама лицензия находится в файле /docs/license.txt дистрибутива.\n\nДоолнение к лицензии: движок является полностью бесплатным  до  тех  пор,  пока\nсохраняются мои копирайты. Так же вы не имеете права  продавать  сам  движок  и\nлюбые продукты на его основе (однако имеете право взимать  плату  за  доступ  к\nигре и/или продавать внутриигровые ресурсы).\n\n\nПредуведомления о необходимости наличия квалификации\n====================================================\nДанные инструкции предполагают умение самостоятельно настроить или использовать\nсторонний  веб-хостинг,  знакомство  с  MySQL  и  PHP,  доступ  к  инструментам\nуправленя MySQL и  хостингом.  Если  вы  не  обладаете  опытом  самостоятельной\nнастройки сайтов - этот дистрибутив вам не подойдет\n\nВНИМАНИЕ! Я не могу протестировать все возможные комбинации и  версии  MySQL  &\nPHP & XCache & веб-серверов! Это означает,  что  при  некоторых  комбинациях  и\nнастройках среды движок может  не  работать.  Именно  для  этого  нужны  навыки\nнастройки и конфигурации веб-серверов.\n\n\n\nПомощь проекту\n~~~~~~~~~~~~~~\nВы можете помочь проекту, отправив WebMoney на кошельки:\n  WMZ (WM-USD) Z409323360409\n  WMR (WM-RUB) R961266352219\n  WMU (WM-UAH) U726314912308\n\nЕсли вы пользуетесь ВебМанями для покупки различных электронных товаров, то  вы\nможете одновременно\nа) купить привычный электронный товар по хорошей цене\nб) помочь проекту.\nhttp://gorlum.plati.ru - пополнения сотовых, Skype, WoW - игра и  ТК  и  прочие\nэлектрические товары за WM. Каждая покупка, сделанная с этого  линка,  принесет\nмне небольшие комиссионные.\n\nПомните! Ничто так не укрепляет веру в нужность работы, как пожертвование!\n\n\n\nУстановка\n~~~~~~~~~\nВНИМАНИЕ!!! PHP 5.3.1 содержит  баг,  который  делает  невозможной  полноценную\nработу СН начиная с v33a12! Обновите PHP, или сделайте откат  до  более  ранней\nверсии PHP, или используйте предыдущую версию СН.\nОписание бага: https://bugs.php.net/bug.php?id=50394\n\nПодробные инструкции по установке содержатся в файлах:\n  /docs/install.md\n  /docs/install-en.md\n\n\nБлагодарности\n=============\nАктивные Помощники (в алфавитном порядке)\n-----------------------------------------\nIvash\nMahomed\nPIR\n\nСпонсоры\n--------\nМухит Гусманов\nandre777_86\nTourist\njilior\nwebsasha\nigorrnc\nnt\n\n\nПроект \"СуперНова\" в интернете\n===============================\nОсновной сайт проекта\n  http://supernova.ws\n\nGitHub\n------\nGitHub используется как основное хранилище кода в интернете. На нем вы  найдете\nсамую последнюю версию СуперНовы\n\n\"СуперНова\" на GitHub:\n  http://github.com/supernova-ws/SuperNova\n\nGIT-репозиторий:\n  git://github.com/supernova-ws/SuperNova.git\n\nОн же по HTTP:\n  https://github.com/supernova-ws/SuperNova#\n\nАрхивы инсталляции всех релизов через HTTPS:\n  https://github.com/supernova-ws/SuperNova/zipball/master\n(кнопка \"download .zip\" скачает самый свежий релиз)\n\nВ репозитории есть две ветки\nВетка \"master\" содержит последний стабильный релиз движка плюс самые актуальные\nбагфиксы.  Перед  выкладыванием  в  эту  ветку  движок  неделями   и   месяцами\nтестируется на нескольких серверах\nА  вот  ветка  \"trunk\"  -  для  самых  смелых,  храбрых  и  попросту  говоря  -\nбезрассудных. Это - копия моей рабочей  ветки.  Она  содержит  то,  над  чем  я\nработаю в данный момент. В общем - я даже не гарантирую,  что  чекаут  из  этой\nветки  вообще запустится. Используйте её на свой страх и риск. Я предупредил.\n\nЗагрузка исходников из ветки trunk одним архивом через HTTPS:\n  https://github.com/supernova-ws/SuperNova/zipball/trunk\n\nSourceForge\n-----------\nТак же есть репозиторий на SourceForge:\n  git://supernova-ws.git.sourceforge.net/gitroot/supernova-ws/supernova-ws\n\nАрхив инсталляции для загрузки - для тех, кто не осилил GIT\n  http://sourceforge.net/projects/supernova-ws/files/\n\nПоддержка\n---------\nФорум поддержки проекта:\n  http://forum.supernova.ws/viewforum.php?f=73\n\n\"Живые\" Вселенные\n-----------------\nСуперНова (х1):\n  http://supernova.supernova.ws\n\nоГейм (x2):\n  http://ogame.supernova.ws\n\nБета (x50):\n  http://beta.supernova.ws\n\n\nДокументация\n============\nВся документация и сопроводительные файлы находятся в каталоге \"docs\"  в  корне\nдвижка\n\nЛицензия (на английском):\n  /docs/license.txt\n\nДокументация на проект (этот файл, на русском):\n  /docs/readme.txt\n\nИстория изменения для пользователей (на русском, начиная с 25-го релиза):\n  /docs/changelog.txt\n  /docs/changelog.html\n\nПолная история изменений для администраторов и разработчиков (на русском):\n  /docs/changelog_dev.txt\n\nИнструкции по установке (на русском):\n  /docs/install.md\nИнструкции по установке (английский, google-translated):\n  /docs/install-en.md\n\nДокументация для администраторов и разработчиков (на русском):\n  /docs/html/developer.html\n\nПланы развития (на русском):\n  /docs/changelog.xls\n\nCoding Gudelines для разработчиков (на английском):\n  /docs/html/coding-guidelines.html\n\nБеллитризированная внутриигровая предыстория (на русском):\n  /docs/SuperNova - All you didn't want to know but forced to.docx\n\n\n\nТерминология\n~~~~~~~~~~~~\n\"Сервер\" - веб-сервер с установленным PHP, MySQL и xCache\n\"СН\" или \"движок\" - исполнимый код Проекта \"СуперНова\"\n\"Вселенная\"  -  экземпляр  движка,  установленный  на  сервер  и  имеющий  свою\nсобственную отдельну БД. Один сервер может поддерживать несколько Вселенных\n\"Топ\" - игрок, входящий в первые 5% игроков по статистике\n\n\n\nТёмная Материя\n~~~~~~~~~~~~~~\nТёмная Материя (ТМ)  -  особый  внутриигровой  ресурс,  почти  не  связанный  с\nостальной  экономикой.  ТМ  позволяет  получить  доступ  к   уникальным   и/или\nограниченным  юнитам,  возможностям  или  технологиям,   которые   сложно   или\nневозможно получить другим образом\nТМ служит нескольким целям:  ускорителем  в  развитии  начинающим  игрокам  (на\nранних этапах игры ТМ достается легче, а её использование дает больший  эффект,\nчем на поздних), поощрением постоянным игрокам (чем дольше  игрок  играет,  тем\nбольше ТМ у него будет), дополнительным фактором случайности (на поздних этапах\nразвития  ТМ  можно  получить  только  рискуя),  способом  рекламы  (встроенная\nпартнерская  программа  поощраяет  игроков  как   можно   шире   распространять\nинформацию о сервере  и  вознаграждет  их  за  эффектиную  рекламу)  и  методом\nмонетизации игры (владельцам серверов рекомендуется продавать именно ТМ,  а  не\nдругие ресурсы -  за  счет  этого  достигается  относительное  равенство  между\nдонаторами и обычными игроками, минимизируется вмешательство владельца в игру и\nупрощается менджмент платежей)\nТМ смасштабирована в отношении 1 к 1.000. Т.е. весь приход ТМ и все цены  в  ТМ\nувеличены в 1.000 раз по отношению к RR\nВ настоящий момент  существует  пять  внутриигровых  источников  получения  ТМ:\nпостройка зданий, исследования, рейды на врагов, экспедиции и квесты. По логике\nигры ТМ считается редким и ценным  ресурсом,  каждая  единица  которого  должна\nдобываться огромным трудом и вкладываться с умом и  перспективой.  Кроме  того,\nмножество вопросов вызывает ограничение в 150 уровней по экономике. Изменения в\nролевой системе призваны устранить оба этих недостатка\n\n\nЛог движения ТМ\n===============\nВсе изменения в количестве ТМ заносятся в специальную таблицу \"log_dark_matter\"\nдля  предотвращения   возможных   злоупотрблений   как   со   стороны   игроков\n(использование багов или дырок в движке),  так  и  со  стороны  членов  команды\nсервера. По этой причине таблица не доступна из админской части игры\n\n\n\nПокупка ТМ за внеигровые ресурсы\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\nДвижок СН содержит подсистему для  покупки  ТМ  игроками  за  реальные  деньги.\nОднако для её функционирования нужен хотя бы  один  модуль  платежной  системы,\nкоторые не входят в дистрибутив.  За  получением  этих  модулей  обращайтесь  к\nавтору движка\nВ настоящее  время  поддержка  платежных  модулей:  таблицу  платежей;  базовые\nнастройки подсистемы; строки локализации  и  константы;  интерфейс  покупки  ТМ\n(требуется хотя бы один модуль платежной системы);  поддержку  методов  'LINK',\n'GET' и 'POST' в системах платежа;  система  бонусов  при  одноразовой  покупки\nбольшого количества ТМ\n\n\nСтраница платежа \"Тёмная Материя\"\n=================================\nПонижена в 2,5 раза цена ТМ. Теперь за 1 гривну можно купить 2500 ТМ\nРазмер лота (шага покупки) установлен в 2500 ТМ\nИзменена система бонусов за оптовые покупки ТМ:\n  от 50.000 ТМ - бонус 2% к количеству ТМ\n  от 100.000 ТМ - бонус 4% к количеству ТМ\n  от 200.000 ТМ - бонус 7% к количеству ТМ\n  от 250.000 ТМ - бонус 11% к количеству ТМ\n  от 375.000 ТМ - бонус 15% к количеству ТМ\n  от 500.000 ТМ - бонус 22% к количеству ТМ\n  от 750.000 ТМ - бонус 33% к количеству ТМ\n  от 1.000.000 ТМ - бонус 44% к количеству ТМ\n  от 1.250.000 ТМ - бонус 55% к количеству ТМ\nСписок  доступных  цен  и  список  скидок  строится  теперь  по  данным  модуля\nsn_payment\nДобавлена поддержка модулей с более чем одним количеством шагов при покупке\nДобавлена поддержка мультивалютности\nДобавлена поддержка SUCCESS_URL в платежных системах\nДобавлена индикация внутренних курсов системы\nТеперь большую часть информационных элементов на странице можно свернуть\n\n\nМодуль платежной системы XSolla: payment_xsolla_currency\n========================================================\nРеализован протокол XSolla \"Виртуальная валюта\" (без отката платежей)\nПоддержка плагина XSolla PayStation\n\n\nМодуль платежной системы WebMoney: payment_webmoney\n===================================================\nРеализован приема платежей на кошельки WebMoney\nПоддержка мультивалютности\nПоддержка SUCCESS_URL\n\n\nМодуль платежной системы RoboKass: payment_robokassa\n====================================================\nРеализован прием платежей через агрегатора RoboKassa\n\n\n\nПартнерская программа\n~~~~~~~~~~~~~~~~~~~~~\nНаписан  с  нуля  модуль  Партнерской  программы.   Каждому   игроку   выдается\nперсонализированная ссылка, персонализированный  баннер  и  персонализированный\nюзербар, а так же коды для  размещения  всего  этого  добра  на  форумах  и/или\nдомашних страничках. Если на сервер по ссылке  приходит  новый  пользователь  и\nрегестрируется  на  сайте,  приведший  его  игрок  получает  10%  от  всех  ТМ,\nзаработанных   новым   пользователем   (включая   дополнительные   начисления).\nКоличество бонуса партнеру определяется переменной \"rpg_bonus_divisor\", которая\nпо умолчанию равна 10 (т.е. 10% - это 1/10 от ТМ реферрала)\nПартнерка имеет ограничение  по  минимальному  количеству  ТМ,  после  которого\nначинается начисление бонусов  реферралу  -  переменная  \"rpg_bonus_minimum\"\nНастройки партнерской программы осуществляются в таблице \"config\"\n\n\n\nРазвитие: опыт, уровни и ТМ\n~~~~~~~~~~~~~~~~~~~~~~~~~~~\nЗа  совершение  определенных  действий  в  игре  начисляется  опыт.  Накопление\nопределенного количество опыта отмечается увеличением уровня и  вознаграждением\nв  виде  ТМ.  Опыт,  необходимый  для  получения   нового   уровня   растет   в\nгеометрической прогрессии - т.е. чем выше уровень,  тем  больше  нужно  набрать\nопыта для перехода на следующий уровень\nОпыт можно получить за  постройку  и  разрушение  зданий  (строительный  опыт),\nисследование новых технологий (исследовательский опыт)  и  сражение  с  другими\nигроками (рейдерский опыт). Все три вида опыта независимы друг от  друга,  дают\nразные типы уровней и имеют разные шкалы развития\n\n\nСтроительство и исследование\n============================\nСтроительный и исследовательский опыт  начисляется  из  расчета  один  балл  на\nкаждую вложенную полную тысячу ресурсов в пересчете на металл по внутриигровому\nкурсу  в  соответственно  постройку  или  разрушение   зданий   и   исседование\nтехнологий. В отличии от xNova СН не имеет ограничений по уровням строительства\nи исследования\nШкала развития при строительстве и исследованиях имеет следующий вид:\n    1    50      50      50\n    2   100      52     102\n    3   150      53     155\n    4   200      55     209\n    5   250      56     265\n    6   300      58     323\n    7   350      60     383\n    8   400      61     445\n    9   450      63     508\n   10   500      65     573\n   20 1.000      88   1.344\n   30 1.500     118   2.379\n   40 2.000     158   3.770\n   50 2.500     213   5.640\n   60 3.000     286   8.153\n   70 3.500     384  11.530\n   80 4.000     517  16.068\n   90 4.500     694  22.167\n  100 5.000     933  30.364\n  110 5.500   1.254  41.380\n  120 6.000   1.685  56.185\n  130 6.500   2.264  76.081\n  140 7.000   3.043 102.820\n  150 7.500   4.090 138.754\n  160  НЕТ    5.497 187.048\n  170  НЕТ    7.387 251.950\n  180  НЕТ    9.927 339.172\n  190  НЕТ   13.342 456.392\n  200  НЕТ   17.930 613.926\nИТД\nПервая колонка - уровень\nВторая колонка - старое общее  количество  опыта,  необходимое  для  достижения\nуровня\nТретья колонка - новое количество опыта, необходимое для перехода на  следующий\nуровень (геометрическая прогрессия с первым членом 50 и коэффициентом 1,03)\nЧетвертая колонка - новое общее количество опыта,  необходимое  для  достижения\nуровня\n\n\nРейдерство\n==========\nЗа каждую выигранное сражение начисляется один балл рейдерского опыта\nШкала развития рейдера имеет следующий вид:\n  1    10    10      10\n  2    20    10      20\n  3    30    11      31\n  4    40    11      42\n  5    50    11      53\n  6    60    12      65\n  7    70    12      77\n  8    80    12      89\n  9    90    13     102\n 10   100    13     115\n 20   200    18     269\n 30   300    24     476\n 40   400    32     754\n 50   500    43   1.128\n 60   600    57   1.631\n 70   700    77   2.306\n 80   800   103   3.214\n 90   900   139   4.433\n100 1.000   187   6.073\n110 1.100   251   8.276\n120 1.200   337  11.237\n130 1.300   453  15.216\n140 1.400   609  20.564\n150 1.500   818  27.751\n160 1.600 1.099  37.410\n170 1.700 1.477  50.390\n180 1.800 1.985  67.834\n190 1.900 2.668  91.278\n200 2.000 3.586 122.785\nИТД\n\nПервая колонка - уровень\nВторая колонка - старое общее  количество  опыта,  необходимое  для  достижения\nуровня\nТретья колонка - новое количество опыта, необходимое для перехода на  следующий\nуровень (геометрическая прогрессия с первым членом 10 и коэффициентом 1,03)\nЧетвертая колонка - новое общее количество опыта,  необходимое  для  достижения\nуровня\n\n\n\nМодуль \"Премиумный аккаунт\": player_premium\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\nПремиумный аккаунт - особый вид аккаунта, дающий  бонус  игрокам,  которые  его\nприобрели\nПремиумный аккаунт покупается игроком за ТМ, поэтому приобрести его может любой\nигрок, вне зависимости от того, вкладывал ли он в игру реальные деньги или нет\nДобавлена индикация уровня премиума в меню\nДобавлена индикация остатка времени Премиума в пункт меню в виде  прогресс-бара\nс цветовым кодированием:\n  Зеленый - осталось не менее 50% времени пермиума\n  Желтый - осталось меньше 50%, но не менее 25%\n  Оранжевый - осталось меньше 25%, но не менее 10%\n  Красный - осталось меньше 10%\n  Цвет фона - нет Премиума\n\n\n\nПериод действия премиума\n========================\nПремиумный  аккаунт  покупается  на  определенный  срок.  По   умолчанию   срок\nприобретения премиума - 30 дней. Именно в расчете на этот  срок  дается  расчет\nцены ниже. Однако премиум может быть куплен на  больший  или  меньший  срок.  В\nчастности, полный список сроков покупки составляет:  1  неделя,  2  недели,  30\nдней, 60 дней и 90 дней. При приобретении премиума на  срок,  больший  30  дней\nпредусмотрены скидки, меньше 30 дней - пенальти на  цену  покупки  относительно\nцены премиума в пересчете  на  30  дней.  Список  коэфициентов  изменения  цены\nвыглядит следующим образом: 1,5 для 1й недели, 1,2 - для 2х недель, 0,9 для  60\nдней и 0,8 для 90 дней.\n\n\nУровень премиума\n================\nПремиумные аккаунты бывают различных уровней. По умолчанию доступно  5  уровней\nпремиума. БС составляет 25.000 ТМ, а фактор - 3. Это дает нам следующую линейку\nцен: 1й уровень - 25.000 ТМ, 2й уровень - 100.000 ТМ (25.000  с  0го  на  1й  +\n75.000 с 1го на 2й), 3й - 325.000 ТМ, 4й - 1.000.000 ТМ, 5й - 3.025.000 ТМ\nПример: премиум 3-го уровня на 1 неделю будет стоить\n  325.000 * (7 / 30) * 1,5 = 113.750 ТМ\n  где\n  325.000 - базовая цена ТМ премиума 3 уровня на 30 дней\n  (7 / 30) - коэфициент пересчета базовый цены на период покупки (7 дней)\n\n\nПреимущества премиума\n=====================\nНик обладателя премиумного аккаунта выделяется  в  чате  отдельным  стилем  (по\nумолчанию - желтым цветом. Настраивается в CSS - стиль \".nick_premium\")\nПремиумный  аккаунт  дает  бонус,  эквивалентный  своему  уровню,   к   уровням\nтехнологий и Наемников (не Губернаторов!) Империи, а так же к уровням  следущих\nзданий на планетах: ко всем шахтам и складам ресурсов, ко всем электростанциям,\nк Фабрике роботов, к Верфи, к Нанофабрике, к Лаборатории и к Нанолаборатории\nПремиум чрезвычайно эффективен - и чем выше  уровень  премиума,  тем  более  он\nэффективен. Именно поэтому на высокие  уровни  выставлены  такие  большие  цены\n(фактически, их можно назвать \"заградительными\" для премиума выше третьего).\nНапример, максимальный (5й уровень) премиума дает:\n1. +5 уровней к шахтам и складам -  это  почти  удвоение  добычи  и  увеличение\n   складов почти в 8 раз\n2. +5 уровней к технологиям и Наемникам - это по факту  +100%  ко  всем  боевым\n   характеристикам\n3. +5 уровней к технологиям и Наемникам - это от 50 до 150%% прирост к скорости\n   полета - в зависимости  от  типа  двигателя,  да  еще  +25%  к  скорости  от\n   Навигатора\n4. +5  к  производственным   зданиям  -  это  ускорение  всех  строительств   и\n   исследований  более  чем  в  32  раза  (на  планетах   с   нанофабриками   и\n   нанолабораториями соответственно)\n5. И еще множество мелких плюшек в виде увеличения количества слотов под  флоты\n   и экспедиции, увеличение количества колоний  (но  не  больше,  чем  максимум\n   сервера), открытие новых юнитов и т.д.\n\n\n\nПокупка секторов на планете\n~~~~~~~~~~~~~~~~~~~~~~~~~~~\nТеперь можно за ТМ докупать дополнительные сектора на планете - один сектор  за\nраз, максимальное количество секторов не ограничено\nCектор можно купить в нескольких местах:  в  \"Обзоре  планеты\",  в  \"Управлении\nпланетой\" и на экране строительства зданий\nСтоимость  сектора  для  планеты  -  геометрическая  прогрессия  с  количеством\nсекторов в качестве номера члена, БС = 1000 и Ф = 1.01\nСтоимость покупи 1 сектора на планете  составляет:  для  планеты  размером  100\nсекторов - 2.678 ТМ, 150 секторов - 4.404 ТМ,  163  сектора  -  5.013  ТМ,  200\nсекторов - 7.244 ТМ, 250 секторов - 11.913 ТМ, 300 секторов -  19.493  ТМ,  330\nсекторов - 26.508 ТМ\n\n\n\nТелепортация планеты\n~~~~~~~~~~~~~~~~~~~~\nТеперь  можно  телепортировать  планеты  в  пределах  Вселенной.   Телепортация\nдоступна на странице управления планетой\nТелепортация может производится только на свободное место -  там,  где  нет  ни\nпланет, ни лун, ни обломков (включая уничтоженные объекты)\nТелепортация  перемещает  в  новые  координаты   планету   вместе   с   флотом,\nнаходящимся на орбите планеты\nЕсли у планеты есть луна - она так же перемещается в новые координаты вместе  с\nфлотом\nТелепортация невозможна, если в окрестностях планеты есть  какая-то  активность\nфлотов (т.е. есть флоты, имеющие в качестве точки  отправления  или  назначения\nсаму планету, луну или поле обломков)\nПосле  телепортации  необходимо  выждать  некоторое   время   перед   следующей\nтелепортацией  -  нарушенная  метрика  пространства   вокруг   планеты   должна\nнормализироваться\nСтоимость телепортации и таймаут перед следующим  прыжком  задаются  в  таблице\n`config` соответственно  переменными  'planet_teleport_cost'  (по  умолчанию  -\n50.000 ТМ) и 'planet_teleport_timeout' (по умолчанию - 1 сутки)\n\n\n\nПеренос столицы\n~~~~~~~~~~~~~~~\nТеперь можно перенести столицу (основной мир) на любую планету. Перенос столицы\nосуществляется со страницы управления планетой\nСтоимость переноса столицы по умолчанию составляет 25.000 ТМ.  Она  задается  в\nтаблице `config` переменной 'planet_capital_cost'\n\n\n\nПлотность планеты\n~~~~~~~~~~~~~~~~~\nДобавлен новый параметр планеты - плотность. Он  определяет  химический  состав\nгеосферы планеты и влияет на добычу ресурсов на ней\nПлотность планеты лежит в диапазоне от  850  до  9250  кг/м3.  Плотность  новых\nпланет распределена случайным образом по нормальному распределению\nСуществует 7 классов плотности - с уникальным набором коэфициентов  добычи  для\nкаждого класса:\n  Ледяные планеты (<2000 кг/м3) - встречаются очень редко: очень низкая  добыча\n  металла, очень низкая добыча кристаллов, очень высокая добыча дейтерия\n  Силикатные планеты (2000=3250 кг/м3) - встречаются редко: очень низкая добыча\n  металла, очень высокая добыча кристаллов и еще хорошая добыча дейтерия\n  Каменные планеты  (3250-4500  кг/м3)  -  встречаются  часто:  хорошая  добыча\n  металлов, высокая добыча кристаллов и низкая добыча дейтерия\n  Стандарнтые планеты (4500-5750 кг/м3)  -  встречаются  очень  часто:  хорошая\n  добыча металлов, хорошая добыча кристаллов и хорошая добыча дейтерия\n  Железнорудные планеты (5750-7000 кг/м3) - встречаются  часто:  очень  хорошая\n  добыча металлов, низкая добыча кристаллов и низкая добыча дейтерия\n  Металлические планеты (5750-7000 кг/м3) - встречаются редко: отличная  добыча\n  металлов, низкая добыча кристаллов и низкая добыча дейтерия\n  Тяжелометаллические  планеты  (>7000  кг/м3)  -  встречаются   очень   редко:\n  великолепная добыча металлов, очень низкая добыча кристаллов и  очень  низкая\n  добыча дейтерия\nСтартовая планета имеет плотность  5500  кг/м3  и  принадлежит  к  4-му  классу\nплотности. Все луны имеют плотность 2500 кг/м3 и  принадлежат  ко  2-му  классу\nплотности\nТип  ядра  планеты  можно  изменить  за  ТМ.  Возможность  доступна  на  экране\nуправления  планетой   (Обзор   планеты   ->   Управление).   Стоимость   смены\nвысчитывается динамически  и  зависит  от  того,  насколько  сильно  отличается\nтекущий тип ядра от желаемого\nДобавлено отображение типа ядра планеты на страницу \"Обзор планеты\"\nНа страницу \"Обзор Империи\" добавлено отображение типа ядра планеты с  цветовым\nкодированием:\n  Зеленый - тип ядра встречается очень часто\n  Желтый - тип ядра встречается часто\n  Оранжевый - тип ядра встречается редко\n  Красный - тип ядра встречается очень редко\nВ Новапедию добавлена статья про плотность и типы ядер планет\n\n\n\nОфицеры (наемники и губернаторы) и Чертежи\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\nПолностью переработана система офицеров.  Произведено  разделение  офицеров  на\n\"наемников\" и \"губернаторов\". Стоимость нового уровня офицера рассчитывается по\nформуле:\nБС * (Фактор ^ Уровень), где\n   БС - базовая стоимость Наемника в ТМ. Наемники за ресурсы не поддеживаются\n   Фактор - заранее заданная величина\n   ^ - операция возведения в степень\n   Уровень - новый уровень Наемника\nБС  и  Фактор  прописываются  индивидуально  для  каждого  Наемника   в   файле\n\"/includes/vars.php\". По умолчанию БС = 3.000, Фактор = 1\n\n\nБонус к скорости постройки/исследования\n=======================================\nИзменена логика работы Академика, Фортификатора и Инженера. Все они  влияют  на\nскорость  постройки  юнитов,  однако  раньше   зависимость   была   практически\nэкспоненциальная. Вдобавок слишком большой  бонус  от  Академика  в  Альянсе  в\nсочетании с полностью прокачанным Наемником у игрока мог приводить к артефактам\nв работе исследований\nТеперь бонус указанных офицеров - это  процент  увеличения  скорости  постройки\nсоответствующих юнитов, а не процент, на который уменьшается  время  постройки.\nТ.е. это - слагаемое в знаменатели дроби\nЕсли говорить совсем просто: 100% бонуса от офицера уменьшают  время  постройки\nюнита в 2 раза, 200% - в три раза, 300% - в четыре раза и так далее\n\n\nНаемники\n========\nНаемники покупаются через общее меню слева и их бонусы распространяются на  всю\nИмперию\nСН поддерживает два режима работы с Наемниками - Постоянных  Наемников  (ПН)  и\nВременных Наемников (ВН). Режим Наемников переключается в настройках сервера  и\nможет быть изменен на лету (см. ниже пункт \"Переключение режима Наемников\")\n\nПостоянные Наемники\n-------------------\nВ режиме ПН  Наемники  разбиты  на  две  независимые  ветки  -  \"шахтерскую\"  и\n\"рейдерскую\". \"Ветка\" означает, что для рекрутирования  Наемника,  находящегося\nправее  по  списку,  небоходимо  сначала  нанять  один  или  несколько  уровней\nНаемника, находящегося левее\n\"Шахтерская\" ветка имеет вид: \"Карго-мастер\" => \"Шпион\" => \"Академик\"\n\"Рейдерская\" ветка имеет вид: \"Адмирал\" => \"Координатор\" => \"Навигатор\"\n\nВременные Наемники\n------------------\nВН  (как  следует  из  названия)  не  являются  постоянными,  а  нанимаются  на\nопределенный срок. По истечению срока Наемник исчезает\nВ режиме ВН отсутствует понятие \"веток развития\" и для найма доступны сразу все\nНаемники. Соответственно не отображаются требования  к  Наемникам  на  странице\n\"Технологии\"\nБазовая цена покупки ПН в режиме ВН становится ценой найма  на  базовый  период\nнайма (БПН). По умолчанию он равен одному  среднекаелндарному  месяцу(30  дней,\n2.592.000 секунд). Изменить БПН можно на странице настроек сервера\nПредусмотрена система скидок/наценок в зависимости от срока  покупки  Наемника.\nНастройки содержатся в массиве $mrc_hire_discount в файле /officer.php.  Индекс\nэлемента  -  количество  секунд  найма,  значение  -  коэффициент  скидки.  \"1\"\nозначает, что на данный  интервал  найма  нет  ни  наценки,  ни  скидки  и  при\nпересчете на количество секунд в БПН  его  стоимость  будет  в  точности  равна\nстоимости БПН. Если число меньше единицы - это означает скидку; больше  единицы\n- наценку\nВременного наемника можно увольнять до истечения  срока  найма.  ВНИМАНИЕ!  При\nувольнении наемников вся портаченная на найм ТМ будет утеряна!\nРежим Наемников отображается на странице \"Мировые константы\"\n\nПереключение режима Наемников\n-----------------------------\nПри включении ВН все постоянные Наемники будут преобразованы  во  временные  со\nсроком действия равному БПН. В случае необходимости изменить БПН нужно  СНАЧАЛА\nего изменить, а затем переключать режим работы Наемников\nПосле включения  ВН  изменение  базового  интервала  найма  не  влияет  на  уже\nрекрутированных Наемников, а влияет только на цену нового найма\nПри отключении ВН все активные на этот момент Наемники  будут  преобразованы  в\nпостоянные - вне зависимости от того, на какой срок они были наняты  и  сколько\nвремени осталось до срока истечения найма. Информация о сроках найма  при  этом\nтеряется\nПри отключении  ВН  активизируются  ограничения  по  рекрутированию  Наемников,\nоднако уже нанятые Наемники останутся активными и  будут  влиять  на  игру  вне\nзависимости от того, может игрок их купить или нет. Такой  способ  переключение\nвыбран для исключения потери ТМ, вложенных игроками в Наемников\n\nСтраница рекрутирования\n-----------------------\nПереработана страница рекрутирования Наемников:\n1. Добавлена поддержка временных Наемников\n2. Стоимость найма отображается динамически в зависимости  от  текущего  режима\n   Наемников, выбранного уровня и срока найма\n3. В режиме ПН видны  все  наемники  -  даже  недоступные  (с  соответствующими\n   пояснениями)\n4. В режиме ПН можно нанимать сразу несколько уровней Наемников\n5. При найме  постоянных  наемников  показывается  ровно  столько  уровней,  на\n   сколько хватает ТМ\n\n\nГубернаторы\n===========\nГубернаторы покупаются отдельно  на  каждую  планету  на  странице  \"Управление\nпланетой\" и их бонусы распространяются только на ту  планету/луну,  на  которой\nони куплены. Текущий губернатор и его уровен отображается  на  странице  \"Обзор\nпланеты\", а так же в виде индикатора на изображениях планет в \"Обзоре  планеты\"\nи \"Обзоре Империи\"\n\nГубернаторов  можно  сменять.  При  покупке  нового  губернатора  его  развитие\nначинается с первого  уровня,  а  вся  ТМ,  вложенные  в  предыдущего  офицера,\nтеряется. Страница покупки  губернаторов  требует  подтверждение  операции  при\nпокупке губернатора, отличного от текущего.\nСтраница покупки имеет защту от случайной покупки  губернатора  при  обновлении\nстраницы\n\nБалансировка губернаторов проведена из расчета на \"среднего игрока\", имеющего 6\nпланет при скорости х2. Такие игроки не получат  пенальти  при  оснащении  всех\nпланет губернаторами. Понятно, что общая производительность Империи  уменшится,\nоднако это те жертвы, на которые я готов пойти. В целом же изменение направлено\nна уменьшение среднего количества ТМ у игроков.  В  особенности  -  у  топов  и\nсаб-топов\n\n\"Технолог\" объединяет функции Геолога и Энергетика: каждый уровень дает  +5%  к\nдобыче металла, кристалла, дейтерия, а так  же  к  выработке  электроэнергии на\nсолнечной  и  термоядерной  электростанциях.  Технолог   5го  уровня  позволяет\nстроить термоядерные электростанции.\nВследствие  полной  бессмысленности  найма  Технолога  на  лунах  во  избежание\nнапрасных трат ТМ игроками он убран со страницы управления луной\nБС 800, Фактор 1,06, нет ограничения по уровням\n\nКаждый уровень \"Инженера\":\n1. Увеличивает скорость постройки зданий и кораблей на 10%\n2. Добавляет по одному слоту к очередям постройки кораблей и зданий\nБС 500, Фактор 1,65, не имеет ограничения по уровню\n\nКаждый уровень \"Фортификатора\":\n1. Увеличивает   скорость   постройки   защитных сооружений и ракет на 10%\n2. На 10% увеличивает все боевые характеристики кораблей на орбите и  оборонных\nсооружений на поверхности планеты\n3. Добавляет 1 слот к очереди постройки обронительных сооружений\nБС 2.000, Фактор 1,25, не имеет ограничения по уровню\n\n\nЧертежи\n=======\n\"Чертеж\"  -  это  программный  пакет,  дающий  доступ  к   производсту   юнитов\nопределенного  типа.  В  терминах  xNova  Чертеж  можно  считать  одноуровневым\nперманентным Наемником. Имеющиеся раннее  Наемники  \"Разрушитель\"  и  \"Ассасин\"\nсконвертированы соответственно в \"Чертеж ЗС\" и \"Чертеж СН\".  Остальные  четрежи\nнадо покупать самостоятельно\nДоступны следующие чертежи:\n1. Здания: термоядерная электростанция\n2. Корабли: супертранспорт, гипертранспорт, Звезда Смерти, \"СуперНова\"\n3. Защитные постройки: планетарная защита\nЧертеж является перманентым\nЧертежи заменяют Наемников в требованиях к постройке\nЧертеж покупается на Империю, после чего указанный юнит доступен к производству\nна всех планетах\nЕсли в движке установлен  модуль  \"Альянсы-Игроки\",  то  Чертежи  доступны  для\nпокупки Альянсами. Стоимость такого Чертежа равна базовой стоимости  умноженной\nна минимальное количество членов Альянса  -  но  такой  Чертеж  дает  доступ  к\nпостройке указанных юнитов сразу всем членам\n\n\nАртефакты\n=========\n\"Артефакты\" - редкие объекты с уникальными  свойствами,  приобретаемые  за  ТМ.\nАртефакты  являются  одноразовыми  -  после  использования  Артефакт  исчезает.\nНекоторые Артефакты  настолько  мощные,  что  их  количество  в  одной  Империи\nограничено\nИспользование некоторых Артефактов привязано к планетам - т.е. их эффект  будет\nраспространятся   только   на   эту   планету.   Эффекты   других    Артефактов\nраспространяются на всю Империю. Особо мощные Артефакты могут оказывать влияние\nна солнечную систему, галактику или даже Вселенную\n\nБольшой Адронный Колайдер (БАК)\n-------------------------------\nБАК генерирует поток гравитонного излучения, направленный в  место  наибольшего\nскопления обломков на  орбите,  притягивая  к  ним  другие  обломки.  Иногда  в\nрезультате может образоваться луна. Шанс создания луны - 1%  за  каждый  полный\nмиллион обломков, но не более 30%'\nВНИМАНИЕ! Использование БАК не гарантирует появления луны!\n\nАвтономный Колонизационный Комплекс (АКК)\n-----------------------------------------\nАКК является набором готовых  конструкций  и  программ,  позволяющий  мгновенно\nразвернуть на новооткрытой планете колонию. Если на планете  уже  есть  здания,\nвходящие в  АКК,  они  будут  либо  усовершенствованы  (если  их  уровень  ниже\nвозможностей АКК), либо останутся нетронутыми\nАКК может быть полностью развернут  на  планете  даже  при  нехватке  свободных\nсекторов, поэтому особо ценен при колонизации малых планет\nАКК не может быть развернут на луне\nСуществуют три вида АКК, различающихся по стоимости и возможностям:\n  Малый  АКК  -  колония  включает  в  себя  Рудник,  Синтезатор  кристаллов  и\n  Синтезатор дейтерия  10го  уровня,  Солнечную  электростанцию  14го  уровня и\n  Фабрику роботов 4го уровня\n  Средний АКК -  колония  включает  в  себя  Рудник,  Синтезатор  кристаллов  и\n  Синтезатор дейтерия  15го  уровня,  Солнечную  электростанцию  20го  уровня и\n  Фабрику роботов 8го уровня\n  Большой АКК -  колония  включает  в  себя  Рудник,  Синтезатор  кристаллов  и\n  Синтезатор  дейтерия  20го  уровня,  Солнечную  электростанцию  25го  уровня,\n  Фабрику роботов 10го уровня и Нанофабрику 1го уровня\n\n\"Эвристический чип\" и \"Наностроитель\"\n-------------------------------------\nАртефакты уменьшают на 1 час соответственно время текущего исследования и время\nпостройки/уничтожения текущего здания на текущей планете\nЕсли оставшееся время исследования/постройки/уничтожения меньше одного часа, то\nАртефакт обнуляет время. Разница не переходит на следующий слот в очереди\nСтоимость эвристического чипа составляет 20.000 ТМ\nСтоимость наностроителя составляет 5.000 ТМ\nВ очередь построек  добавлена  возможность  использовать  Наностроитель  -  при\nналичии Артефакта на складе\nВ очередь построек добавлена возможность использовать Эвристического чипа - при\nналичии Артефакта на складе\n\n\nМодуль \"Капитаны\": unit_captain\n===============================\nКапитан - это опытный командующий, который летает с флотами  и  за  счет  более\nтонкого управления флотами улучшает эффективные характеристики всех кораблей во\nфлоте\nС флотом можно отправить только одного Капитана\nКаждый Капитан привязан к определенной планете  или  луне.  Нельзя  иметь  двух\nКапитанов на  одном  небесном  теле.  Капитан,  летящий  с  флотом,  все  равно\nсчитается привязанным к планете\nКапитана можно перевозить с одной планеты на другую  миссией  \"Передислокация\".\nПри этом на время перелёта Капитан считается привязанным сразу к обоим планетам\n- стартовой и финишной\n\nНайм и гибель Капитана\n----------------------\nКапитан нанимается за ТМ\nНайм и управление Капитанами осуществляется через пункт меню \"Капитаны\"  (сразу\nпод \"Наемниками\")\nПри гибели флота Капитан так же погибает. Под \"гибелью  флота\"  подразумевается\nуничтожение всех кораблей флота. Это верно как для атакующих флотов, так и  для\nфлотов, стоящих в удержании\nКапитан на планете не участвует в защите планеты при атаке  -  этим  занимается\nФортификатор. Зато  при  полном  уничтожении  всего  планетарного  флота  такой\nКапитан не погибнет\n\nОпыт, уровни и навыки Капитана\n----------------------\nЗа каждый выигранный простой бой (САБы  и  миссия  \"Уничтожить\"  не  считаются)\nКапитан атакующего флота получает 1 пункт опыта. За  \"победы\"  над  неактивными\nигроками опыт не начисляется. Так же не начисляется опыт, если  бой  закончился\nвыигрышем атакующего за 1 раунд\nКапитаны всегда улучшают характеристики  кораблей  своего  флота  -  даже  если\nучаствуют в бою, за который они не получат опыта: атака на неактивных  игроков,\nудержание, САБ, уничтожение луны и т.д.\nПри наборе определенного количества опыта Капитан получает новый уровень\nЧем выше уровень - тем больше опыта нужно для получения следующего уровня\nПовышение в уровне дает возможность улучшать умения Капитанов.\nУмения Капитана включают бонусы к щитам, броне и атаке\nКаждый  уровень  умений   дает   1%   к   базовому   значению   соответствующей\nхарактеристики кораблей во флоте, которым управляет Капитан\nУровни Капитана вкладываются в умения один раз и  навсегда  -  поэтому  заранее\nтщательно планируйте развитие своего Капитана\n\nКапитаны в Обзоре Империи\n-------------------------\nУровни Капитанов указываются в списке юнитов на Обзоре Империи. На заднем  фоне\nячейки  с  уровнем  выводится  прогресс-бар  развития   Капитана   с   цветовым\nкодированием:\n  Пустая ячейка - Капитан не нанят, либо только что получил уровень\n  Красный прогресс-бар - до следующего уровня осталось больше 50% опыта\n  Оранжевый - не меньше 50% опыта, но меньше 80%\n  Желтый - не меньше 80% опыта\n  Зеленый - в следующем бою Капитан получит новый уровень\n\nКапитаны в летящих флотах\n-------------------------\nВ списке флотов на странице \"Флоты в полёте\" и для  своих  флотов  на  странице\n\"Обзор планеты\" если во флоте есть Капитан перед количеством кораблей во  флоте\nвысвечивается \"*\", а в попапе состава показывается его уровень\n\n\nСмена имени пользователя\n========================\nДобавлена возможность изменения имени пользователя за ТМ. Стоимость изменения -\n100.000 ТМ\nИгра сохраняет историю изменения имени  пользователя.  Только  бывший  владелец\nможет при желании вернуть себе старое имя - опять же за ТМ\nПоиск по имени так же производится по старым именам. В случае, если старое  имя\nпользователя соответствует критериям поиска, в результаты будет  добавлена  еще\nодна строка, в которой будет указано текущее имя пользователя, а после  него  в\nскобках и выделенное цветом - старое имя пользователя. Никто  не  спрячется  от\nсвоей истории!\nМаксимальная длина имени пользователя уменьшена до 32 символов\nПеременная настроек  сервера  'game_user_changename'  отвечает  за  возможность\nсмены имени пользователя самим пользователем:\n  0 - смена имени запрещена\n  1 - смена имени разрешена и свободна\n  2 - смена имени разрешена, но стоит  ТМ.  Стоимость  смены  имени  указана  в\n  переменной 'game_user_changename_cost' (100.000 ТМ по умолчанию)\nПо умолчанию включена смена пользователем своего имени за ТМ\n\n\n\nЧёрный Рынок\n~~~~~~~~~~~~\nПолностью переписан Чёрный Рынок. Теперь можно менять ТМ на  ресурсы  (Торговец\nРесурсами), продавать несколько типов  кораблей  за  одну  транзакцию  (Скупщик\nфлота). Так же добавлен \"Продавец кораблей\", где можно  выкупить  ранее  кем-то\nпроданные корабли\n\nТорговец ресурсами\n------------------\nТеперь можно поменять ТМ сразу на все ресурсы (опция \"Все ресурсы\" в  дропдауне\nвыбора ресурсов). При этом вводимая сумма будет разделена на  три  части  и  на\nкаждую из этих третей будет  куплено  соответствующее  количество  ресурсов  по\nкурсу.\nСтоимость такой операции -  в три раза больше базовой стоимость обмена.\n\n\nПродавец информации\n===================\nНа Чёрном Рынке доступна новая услуга: продажа информации.\nПисьма от Продавца Информации всегда приходят в почтовый ящик  -  даже  если  у\nигрока отключено получение шпионских отчетов. Мистика какая-то!\n\nИнформация об игроке\n--------------------\nТекущие уровни активных Наемников и (если есть) уровень премиума\n\n\n\n[#] Модуль player_race: Расы\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~\nДАННЫЙ МОДУЛЬ НЕ ВХОДИТ В СОСТАВ ПУБЛИЧНОЙ ВЕРСИИ!\n\nМодуль обеспечивает функциональность \"рас\" - дополнительные бонусы  игроку  при\nвыборе одной из предустановленных  \"Родных  миров\"  -  так  в  терминологии  СН\nназываются расы\n\nРаса выбирается после регистрации на странице  Императора.  Первый  выбор  расы\nпроизводится бесплатно, каждая смена расы стоит 100.000 ТМ\nВ модуле имеется шесть предустановленных рас  с  возможностью  изменения  любой\nрасы либо напрямую в модуле, либо отдельным модулем\nСписок  предустановленных  рас:  земляне,   луниты,   меркурианцы,   венериане,\nмарсиане, республиканцы (Астероидные Республики)\nИконка расы отображается в чате, в статистике, в попапе информации об игроке во\nВселенной и на странице Императора. Удержание курсора над иконкой расы вызывает\nтултип с её названием. Клик - открывает страницу с описанием всех рас\nКаждая раса имеет собственные бонусы.  Бонусы  рас  действуют  сразу  же  после\nвыбора родного мира - не нужно, например, исследовать техи, что бы  получить  к\nним бонус\nОписание текущей расы доступно на странице Императора. Там же  есть  ссылка  на\nописание всех рас в игре с указанием их символов\n\n[#] Модуль player_race_units: Расовые юниты\n===========================================\nДАННЫЙ МОДУЛЬ НЕ ВХОДИТ В СОСТАВ ПУБЛИЧНОЙ ВЕРСИИ!\nДЛЯ ФУНКЦИОНИРОВАНИЯ ДАННЫЙ МОДУЛЬ ТРЕБУЕТ НАЛИЧИЕ МОДУЛЯ PLAYER_RACE!\n\nМодуль добавляет по одному уникальному юниту каждой расе\nПакет так же содержит модуль-пример добавления новой расы и расового юнита\nШесть уникальных юнитов - по одной каждой из рас:\n  Земная \"Лень\" - боевой солнечный спутник\n  Лунная \"Зависть\" - легкий бомбардировщик\n  Меркурианское \"Обжорство\" - емкий переработчик\n  Венерианский \"Гнев\" - истребитель-перехватчик\n  Марсианская \"Гордыня\" - усовершенствованный линейный крейсер\n  Республиканская \"Жадность\" - боевой транспорт\n\n\n\n\nФлот: корабли, миссии и боевая система\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nБоевая система\n==============\nСН  использует  собственную  боевую  систему  UBE.  Все  расчеты,  касающиеся\nобработки  боя,  унифицированы.  Т.е.  обычный  бой,  симулятор,  удержание   и\nуничтожение луны - все они используют один и тот  же  алгоритм  расчета.  В  RR\nиспользовались три разных движка для разных расчетов\n\nОсобенности UBE\n---------------\n1. Своя система расчета повреждений\n2. Свой собственный вариант \"скорострела\" (НЕ ТАКОЙ, КАК НА ОФФЕ!)\n3. Свой алгоритм расчета добычи с планет\n4. Полностью рабочие САБы\n5. Работает удержание на лунах при задании \"Уничтожить\"\n\nВ UBE НЕТ понятия \"скорострела\". Вместо  него  используется  понятие  \"залповый\nогонь\". По функционалу \"залповый огонь\" и \"скорострел\" - понятия  одного  ряда.\nНо алгоритмы расчета у них РАЗНЫЕ.\n\nБоевая система работает следуюшим образом. Предполагается,  что  оба  флота  (в\nслучае боя один на один) летят в формации \"диск\". Что, в целом, логично -  надо\nмаксимизировать   возможность   каждого   корабля   дать   залп.   Залп   флота\nраспределяется между классами кораблей противника в зависимости от брони (ака -\nтоннажа, ака размера). Тоже логично  -  в  большую  цель  легче  попасть.  Щиты\nпринципиально отличаются от брони тем,  что  щиты  считаются  целиком  по  типу\nкораблея, а броня - индивидуально для каждого корабля. Т.е.  что  бы  повредить\nодин корабль,  сначала  надо  пробить  щиты  по  типу  кораблей.\n\nПри выстреле атакующего в противника, против которого у аттакера есть  залповый\nогонь, урон модифицируется согласно внутренним таблицам. Залповый  огонь  может\nкак увеличивать наносимые повреждения, так и уменьшать -  значения  в  таблицах\nзалпового огня подобраны таким образом, что бы с одной стороны  сделать  каждый\nкорабль уникальным и применимым в определенных условиях, а с другой  стороны  -\nсбалансировать все корабли между собой и не допустить появление суперкораблей.\n\nОбращаю внимание - залповый огонь применяется  сразу  по  щитам.  Дальше  часть\nурона поглощается  щитами,  а  если  что-то  прошло  сквозь  щиты  -  наносится\nповреждение по броне. А там сравниваем прошедший  урон  с  броней  и  списываем\nнужное количество корабликов.\n\nСлучайная составляющая\n----------------------\nБои в СН имеют случайную составляющую. Т.е. результаты двух проведенных боев  в\nодних и тех же начальных условиях могут различаться кардинально. С точки зрения\nигрового  мира,  случайная  составляющая  отражает  маневры  кораблей,   работу\nпилотов, наводчиков и всего остального экипажа корабля, а так  же  случайности,\nприсущие реальному миру.\n\nСимулятор боя\n-------------\nВ симуляторе убрана  случайная  составляющая.  Это  позволит  не  пересчитывать\nрезультаты по десять раз, что бы получить примерное представление о  бое,  а  с\nпервого раза увидеть результат \"в нормальных условиях\" без влияния случайностей.\nСимулятор поддерживает продвинутую систему ссылок. Теперь можно  давать  ссылку\nкак на результат симуляции, так и на форму с заполненными  входные  данные  для\nдальнейших экспериментов\n\n\n\nFAQ по боевой системе\n---------------------\nQ: Что означает в описании корабля Х строка  \"Одним  залпом  поражает  КорабльY\nZШтук\"?\nA: Она означает, что если корабль Х (при отсутсвии бонусов к  щитам/броне/урону\nи отсутсвии офицеров) встретится с ZШтук кораблей разновидности КорабльY, то за\nодин раунд корабль Х уничтожит все корабли противника.\nДля кораблей, которых нет в  списке  залпового  огня,  количество  убиваемых\nкораблей за раунд равно: (БроняЗащитника + ЩитыЗащитника) / АтакаНападающего.\n\nQ: Корабль Х уничтожил корабли противника, но ответным залпом его самого сбили!\nA: А так вполне может быть. Выживаемость корабля Х никто не гарантировал\n\nQ: Как-то странно выбираются цели при залпе...\nA: Увы, что бы корректно выбирать цели, необходимо обсчитывать каждый  корабль.\nТ.е. во-первых - иметь  индивидуальную  запись  каждого  корабля.  Во-вторых  -\nвычислять эффективность удара в зависимости от параметров каждого корабля, т.е.\nсовершать просмотр  огромного  количества  информации  каждый  ход.  Если  даже\nприменять всякие алгоритмы оптимизации и сортировки, то  на  величинах  порядка\nдесятков тысяч кораблей (обычная бойня между топ-флотами) количество вычислений\nбудет чрезвычайно велико. И это  не  считая  сложности  разработки  более-менее\nоптимального алгоритма. Поэтому ради  ускорения  расчетов  приходится  идти  на\nсознательное огрубление модели.\n\nQ: Просуммировал нанесенные повреждения, а затем посчитал, сколько должно  было\nбыть нанесено повреждений. Цифры не совпадают!!!\nA: Поскольку применяется достаточно грубая и упрощенная модель (см.  предыдущий\nответ), часть урона может пропасть. Например, если несколько атакующих стреляют\nпо одним и тем же кораблям и первым залпом  корабли  защитника  уничтожены,  то\nостальной урон прийдется на пустое место, т.е.  \"пропадет\".  Учет  \"пропавшего\"\nурона потребовал бы перерасчета всех оставшихся категорий  кораблей  на  каждой\nитерации, т.е.  существенно  увеличил  бы  нагрузку  (эксперимент  такого  рода\nпроводился). Кроме того, если урон приходится на последние корабли  противника,\nчасть его все равно пропадет.\n\nQ: Посчитал в симуляторе - СН не смогла убить туеву хучу  кораблей!  Корабль  -\nгавон!!!\nA: Еще раз - применяется достаточно грубая и  упрощенная  модель  обсчета  боя,\nсм. пред-предыдущий ответ. В частности, каждый  раунд  количество  уничтоженных\nкораблей округляется вниз до  ближайшего  целого.  Т.е.  если  в  одном  раунде\nкорабль атакующего нанес повреждений после щита на 0.99 кораблей защитник,  это\nзначение округляется до 0 и в следующем раунде количество кораблей  у  защтника\nбудет таким же. Грубо говоря, это означает, что в начале раунда  все  уцелевшие\nкорабли имеют полную броню. В  случае  симулятора,  где  отсутствует  случайная\nсоставляющая, при определенных условиях СН не сможет убить ни  одного  корабля.\nВпрочем, на  граничных  случаях  движок  офф-сервера  тоже  ведет  себя  крайне\nстранно. У каждого движка - свои особенности.\n\nРакетный удар\n-------------\nПолностью переписан скрипт ракетной атаки. Основные моменты:\n[*] Полностью устранена ошибка  с  отрицательными  защитными  строениями  после\n    атаки\n[*] Полностью устранена ошибка с размножением защитных строений после атаки\n[*] Теперь ракетами в принципе можно уничтожить планетарную защиту (раньше  это\n    было невозможно)\n[*] Учитывается уровень вооружений нападающего и уровень брони обороняющегося\n[*] Учитываются  щиты  оборонных  сооружений.  Это  должно  слегка    уменьшить\n    эффективность ракет и повысить  живучесть  защитных  сооружений  с  большим\n    количеством щитов\n[*] При ракетном  ударе  рандомизируются  параметры  атаки,  брони  и  щитов  у\n    соответствующих юнитов. Границы такие же, как и для сражений  флотов  -  от\n    80% до 120%\n[*] Максимально выбирается весь ракетный урон - ранее существенная часть  урона\n    могла быть потеряна\n[*] Полностью переписана логика случайной атаки - когда ракетам не задана цель.\n    Теперь она ДЕЙСТВИТЕЛЬНО случайная (в рамках предыдущего пункта)\n[*] При выборе конкретного сооружения для атаки ракета  бьет  ИСКЛЮЧИТЕЛЬНО  по\n    выбранному сооружению\n[*] Правильно и всегда срабатывают перехватчики\n[*] Теперь после атаки можно собирать обломки защитных сооружений  -  защитнику\n   автоматически добавляется половина стоимости сооружений в металле и четверть\n   - в кристаллах\n[*] Устранен глюк с многократным подсчетом одной атаки\n[*] Расширено  и  дополнено  сообщение  о  результатах  нападаения,  приходящее\n    защитнику\n[*] В результате изменений в алгоритме существенно повысилась живучесть ПЗ  при\n    ракетном ударе\n[*] Добавлена поддержка усиления залпа для МПР\n\n\nЛуна\n----\nТеперь при создании луны с орбиты списывается количество обломков,  из  которых\nсформировалась луна\n\n\nUBEv4\n=====\nВ 36м релизе была произведена замена UBEv3 на UBEv4: с нуля был написан  боевой\nдвижок и боевые отчеты. В этом  разделе  будут  перечислены  особенности  новой\nбоевой системы и её отличия от UBEv3\n\nОсобенности подготовки к бою\n----------------------------\nБой теперь считается не по $time_now, а по времени прилета флота -  на  случай,\nесли бой сильно отложенный. Например, при сбоях движка  или  низкой  активности\nсервера. Так будут отработаны корректно все удержания в правильное время\n\nОсобенности хода боя\n--------------------\nБроня не регенерируется между раундами\nЕсли броня упала ниже 75% - корабль имеет шанс взорваться  равный  проценту  от\nобщего здоровья\n\nSneak defense\n-------------\nНовый механизм боя: подлов атакующего или sneak defense. Если в САБе и в\nудержании участвуют флоты одного и того же  игрока,  то  прилетающие  флоты\nэтого игрока будут сражаться на стороне защитника. Аккуратно смотрите, кого\nприглашает в САБ. Хе-хе\n\nОсобенности подведения итогов боя\n---------------------------------\nЕсли в бою участвует хотя бы один  флот  Админов  с  любой  стороны  -  лом  не\nвыпадает ни с кого!\nВозвращение обломков с оборонных сооружений не производится\nВ миссии \"Уничтожить\" шанс уничтожения флота от взрыва одного из  кораблей  при\nпопытке уничтожить луну теперь так же зависит от количества гравидвигателей  во\nфлоте - чем их больше, тем шанс выше\nВ  миссии  \"Уничтожить\"  корабли  могут  взорваться  даже  в  случае  успешного\nуничтожения луны. Как и раньше, подрыв кораблей  с  гравидвигателем  уничтожает\nвесь флот\nТеперь  в  рейдовый  опыт  засчитываются  исключительно  одиночные  атаки.   Ни\n\"Удержание\", ни \"САБ\" не засчитывается. Т.е.  вообще  не  засчитываются  -  вне\nзависимости от результата боя\nТеперь атаки на неактивных игроков (\"i-шки\") не приносят рейдовый опыт\nКоличество свободных полей на луне зависит от  её  размера  и  определяется  по\nформуле Размер/1000 с округлением вверх до целого\nИзменен расчет поля обломков. Теперь  на  орбите  оказывается  от  30%  до  70%\nвыброшенных  за  борт  ресурсов  и  от  20%  до  40%   обломков   кораблей.   В\nдетерминированном симуляторе процент обломков на орбите  всегда  равен  30%,  а\nобломки, выброшенные из трюма всегда составляют 50% от потерь\n9. Шанс уничтожения луны теперь всегда лежит в пределах 1%-99%\n\nБоевой отчёт\n------------\nБоевой отчет теперь состоит  из  трёх  частей:  \"Основная  информация  о  бое\",\n\"Боевые потери\" и лог раундов\n\n\"Основная информация о бое\" показывает:\n  1. Время проведения боя (если доступно)\n  2. Место боя (если доступно) - координаты планеты, её тип и имя\n  3. Результат боя (выигрыш атакующего, ничья, проигрыш атакующего)\n  4. Обломки на орбите\n  5. Шанс образования луны и результат такой попытки\n  6. (Для миссии \"Уничтожить\") Состояние кораблей с гравидвигателями по  итогам\n     боя.  Шанс  уничтожения  луны  оставшимися  кораблями  и  результат  такой\n     попытки. Шанс взрыва кораблей и итог миссии\n\nРаздел \"Боевые потери\" показывает:\n  1. (На планетах) Количество восстановленных боевых сооружений\n  2. Общие потери боевых единиц каждого из участвующих в бою  игроков.  Если  у\n     одного  игрока  участвовало  в  бою  несколько  флотов  -  будут  показаны\n     суммарные потери по всем флотам. Это верно  для  всех  параметров  в  этом\n     разделе. Для планетарной обороны в потери  не  включаются  восстановленные\n     единицы\n  3. (В случае победы атакующих) Количество ресурсов, вывезенных с планеты. Для\n     планеты  это  будет  положительное   число,   для   атакующих   флотов   -\n     отрицательное\n  4. (Для флотов)  Количество  ресурсов  потерянных  из-за  уменьшения  емкости\n     трюмов вследствии уничтожения части флота. Эти ресурсы рассматриваются как\n     \"боевые потери\" - они плюсуются  к  обломкам  на  орбите  и  к  потерям  в\n     пересчете на ресурсы\n  5. Общие потери в пересчете на ресурсы. Включает стоимость боевых  единиц  на\n     момент боя, вывоз с планеты и ресурсы, потерянные из-за уменьшения трюмов\n  6. Общие потери в ресурсах в пересчете на металл по курсу  Черного  Рынка  на\n     момент проведения боя. Писькомерка для сравнения\n\n\"Лог раундов\" показывает результаты расчета каждого раунда для всех флотов\n  1.  Показывает  координаты  и  тип  планеты,  с   которой   прилетели   флоты\n     атакующих/защитников\n  2.  Расширено  количество  информации  о   боевых   подраздеениях   Добавлена\n      информация о \"Пробое\" и \"Уроне\". \"Пробой\" - атака,  которая  пришлась  на\n      щиты и была ими поглощена (или пропущена - см.  ниже).  \"Урон\"  -  атака,\n      которая пришлась на броню\n  3. Цветовое кодирование информации о подразделениях:\n     Зеленый означает, что вся атака в раунде поглощена щитами\n     Желтый - часть атаки пробила щиты (\"пробой\") и нанесла урон по  броне,  но\n     при этом ни одна боевая единица не уничтожена\n     Оранжевый - один или более боевых единиц уничтожено\n     Красный - все оставшиеся боевые единицы уничтожены в этом раунде\n     Число в скобках в столбце потерь - количество боевых единиц,  взорвавшихся\n     в раунде из-за фатальных повреждений\nВ боевом отчете координаты планет являются ссылками на Вселенную\n\nСимулятор боя\n-------------\nДоработан симулятор для поддержки изменений в UBEv4\nСтандартный режим работы  симулятора  -  полная  определенность  результатов  в\nзависимости от начальной конфигурации (галочка \"Симуляция\" включена)\nДобавлен  второй  режим  работы   -   недетрминированный   симулятор   (галочка\n\"Симуляция\" отключена). В этом режиме работы проводится  полная  симуляция  боя\n(включая образование луны) с применением генератора случайных чисел - т.е. так,\nкак происходил бы обычный бой. В этом режиме результаты могут сильно отличаться\nот симуляции к симуляции. Так же в этом режиме происходит запись боевого отчета\nс результатом симуляции в БД\nВ стандартном режиме если шанс образования луны больше 1 всегда образуется луна\nсо средним размером для данного шанса\n\n\nШпионаж\n=======\nПереписана  подсистема  шпионажа.  Корректно  выставляется   время   шпионского\nрапорта. Переписана процедура генерации шпионского рапорта\nАлгоритм  работы  шпионажа  -  почти  оффовский,  потому  что  он  меня  вполне\nустраивает:\n1. Эффективный уровень шпионажа (ЭУШ) на миссию определяется по формулам:\n   Для атакующего:\n     TechLevel + MercLevel + sqrt(ProbeNum) - 1\n   Для защитника:\n     TechLevel + MercLevel\n   где\n     TechLevel - уровень технологии шпионажа\n     MercLevel - уровень Наемника \"Шпион\"\n     ProbeNum - количество шпионских зондов, летящих в миссию\n     sqrt() - функция извлечения квадратного корня\n2. Вычисляется разница между ЭУШ  атакующего  и  ЭУШ  защитника.  Если  разница\n   уровней...\n     меньше 2 - атакующий видит только ресурсы\n     2 и выше - атакующий видит флот на орбите (только  флот  хозяина  планеты!\n                Удержание не видно!)\n     3 и выше - атакующий видит защитные сооружения\n     5 и выше - атакующий видит постройки\n     7 и выше - атакующий видит технологии\n3. Шанс (в процентах) обнаружения шпионского флота защитником  определяется  по\n   формуле:\n   К-во зондов*pow(2, ЭУШ защитника - ЭУШ аттакера)*К-во кораблей на орбите/4\n   где\n     pow(2, X) - функция степени двойки, т.е. \"2 в степени X\".\n   Обращаю внимание, что  в  некоторых  случаях  шанс  обнаружения  может  быть\n   больше 100% - например при большом флоте на орбите, при  большом  количестве\n   кораблей в шпионском флоте,  при  большой  разнице  между  ЭУШ  защитника  и\n   аттакующего\n4. После вычисления шанс обнаружения сравнивается со случайным числом от  0  до\n   99. Если шпионский флот обнаружен - он в полном составе уничтожается (да,  в\n   шпионаж все корабли летят со снятым оружием и броней!).\n\nИз вышеописанного алгоритма следует несколько интересных следствий:\n1. Количество зондов решает\n2. Флот на орбите решает зонды\n3. Защититься  от  шпионажа   невозможно,  но   можно   сделать   его   дорогим\n   удовольствием. Настолько дорогим, что летать в шпионские миссии  на  равного\n   по уровню шпионажа игрока будут только в endgame. Собственно,  для  этого  и\n   изменены цифры разницы уровней\n4. Посылать количество зондов, некратное квадрату - не имеет смысла. Все  равно\n   в расчетах округлится  вниз  до  ближайшего  квадрата,  а  риск  обнаружения\n   шпионажа существенно вырастет\n5. И уж тем более нет смысла посылать в шпионаж любые корабли кроме зондов\n6. Развивай шпионаж с молоду!\n7. Офицер \"Шпион\" - он не зря в условно-шахтерской ветке!\n\n\nУдержание\n=========\nВ СН ограничения защиты слабых игроков (см.) распространяется и  на  удержание.\nТ.е. в нормальном режиме невозможно стать в удержание к слабому игроку.  Данный\nподход выбран для того, что бы уменьшить шанс появления  доминирующего  Альянса\n(впрочем, как показывает практика ВСЕХ онлайн-игр -  это  все  равно  случается\nрано или поздно.  Монополизация  -  путь  к  глобальному  доминированию!).  Для\nнеразделяющих такую точку зрения администраторов  в  настройки  сервера  введен\nдополнительный пункт,  разрешающий  становится  в  Удержание  к  более  слабому\nсоаловцу\n\n\nПрочее\n======\nНа странице выбора миссии таблица загрузки ресурсов по умолчанию отключена\nДобавлено дополнительное сообщение при совпадении  планеты  отправки  и  пункта\nназначения\nДобавлено дополнительное сообщение при попытке отправить незагруженный  флот  с\nмиссией \"Транспорт\"\nДобавлено дополнительное сообщение при попытке отправить  флот  с  ресурсами  в\nмиссию, отличную от миссий \"Транспорт\", \"Передислокация\" и \"Колонизация\"\nИзменены ограничения на отправку Шпионов. Их можно посылать в одиночку в миссии\n\"Шпионаж\", \"Передислокация\" и \"Транспорт\". Во все остальные миссии Шпионов тоже\nможно отсылать - но только в сопровождении других кораблей\n\n\nКорабли\n=======\n\nСупертранспорт\n--------------\nБолее медленная и емкая версия транспортного корабля\n\nГипертранспорт\n--------------\nНовый корабль - Гипертранспорт. Предназначен для ТОП игроков  и/или  скоростных\nВселенных\n\n\nЭкономика\n=========\nНовая настройка сервера \"Масштабировать склады от скорости  добычи\".  Настройка\nдоступна в общих настройках сервера в разделе \"Прочие параметры\". По  умолчанию\nвозможность включена\n\nПри расчете времени постройки юнитов учитывается не только количество ресурсов,\nно и их качество. Время постройки нормированы  по дейтерию,  т.е.  постройки  с\nбольшей долей низкоуровневых ресурсов строятся быстрее\n\nИзменен алгоритм расчетов бонусов добычи ресурсов. Список изменений  приводится\nниже:\n  1. Бонусы на добычу ресурсов улучшают так же базовую добычу на планете\n  2. Бонусы на добычу ресурсов так  же  увеличивают  потребление  сопутствующих\n  ресурсов - дейтерия (для Термоядерной Электростанции)  и  энергии  (для  всех\n  остальных шахт)\n  3. Бонусы на добычу ресурсов улучшают так же выработку энергии на спутниках\n  4.  Недостаток  энергии  влияет  на  базовую  добычу  -   т.е.   она   теперь\n  масштабируется в зависимости эффективности производства\n  5. Естественное производство дает 100% ресурсов даже при недостатке энергии\nИзменен алгоритм работы Термоядерной электростанции. Теперь ТЭС  не  использует\nресурсы со склада, а оперирует только балансом производства дейтерия. Т.е.  ТЭС\nработает только при положительном балансе  производства  дейтерия  И  генерации\nэнергии одновременно. Это сделано для того, что бы оставленная \"без  присмотра\"\nТЭС с отрицательным  балансом  по  дейтерию  не выжрала весь ресурс со склада\nКак следствие - ТЭС  не  отключается  при  положительном  балансе  производства\nдейтерия и энергии, даже если количество  дейтерия  на  планете  равно  0.  Это\nупростит своз ресурсов с планет, на которых энергия генерируется только на ТЭС\nТеперь при эффективности  добычи  ресурсов  менее  100%  вместе  с  актуальными\nзначениями добычи в ячейку добавляется рассчетное  значение  добычи  в  круглых\nскобках. Это упростит балансировку производсва при недостатке ресурсов\nУбрана задержка в обновлении информации о производстве ресурсов\n\n\nЭнергия\n-------\nИзменена выработка энергии по сравнению с оффом и RR.\nВо-первых - модификатор скорости игры теперь не действует на выработку энергии.\nВо-вторых - на выработку энергии солнечными электростанциями влияет температура\nпланеты.\nВ-третьих  -  Производство  энергии  на  термоядерной   электростанции   теперь\nсчитается по формуле оффа:\n  30 * [Э] * (1,05 + [Т] * 0,01) ^ [Э]\nгде Э - уровень электростанции, Т - уровень энергетической технологии\nВ-четвертых - энергетическая технология больше не дает дополнительный  бонус  к\nпроизводительности электроэнергии. Однако  офицер  Энергетик  по-прежнему  дает\nбонус\nВыработка энергии изменена исходя из следующих правил:\n1. Электростанция может поддерживать одну шахту и один синтезатор рудника  того\nже уровня (взято с оффа)\n2. Формула выработки термоядерной электростанции взята с оффа\n3. Средняя температура на планете - 20  градусов  (это  планеты  с  минимальной\nтемпературой  0  градусов  и  максимальной  40  градусов).  На  такой   планете\nэффективность солнечной электростанции будет 100%\n\n\nИсследования\n============\nПересмотрены технологии. Устранены противоречия в  ветках  развития  (например,\nионный  двигатель  можно   было   исследовать   без   технологии).   Технологии\nпереупорядочены в более логичном порядке\n\nДобавлены подробные сообщения об ошибке в случае,  когда  технология  не  может\nбыть исследована (нехватка ресурсов, неудовлетворенные требования итд)\n\nЛаборатория\n-----------\nПолностью переписан интерфейс Лаборатории\nОчередь исследований приведена к стандартному виду\nОбработка  очереди  исследований  теперь  производится  при  каждом  обновлении\nстраницы, а не только при входе в интерфейс Лаборатории\n\nМежгалактическая Исследовательская Сеть\n---------------------------------------\nИзменена схема работы МИС. Теперь МИС работает следующим образом:\n1. По каждой планете вычисляется эффективный уровень исследования (ЭУИ)\n     ЭУИ = уровень лаборатории / (0,5 ^ уровень нанитки)\n2. Планеты сортируются по эффективному уровню\n3. Отсекаются планеты  с  уровенм  лаборатории,  недостаточным  для  проведения\n   данного исследования\n4. Выбирается верхние (уровень МИС + 1) планет в списке и суммируется ЭУИ  этих\n   планет\n5. Получившееся число подставляется в формулу вычисления времени исследования\n\nСледствия:\n1. Нанолаборатория теперь увеличивают эффективность лаборатории только  на  той\n   планете, на которой они расположены\n2. Время исследования теперь одинаково на всех планетах. На некоторых  планетах\n   чуть больше, на некоторых - чуть меньше, но в  среднем  -  лучше,  чем  было\n   раньше\n3. Имеет смысл держать  только  (уровень  МИС  +  1)  планет  с  лабораториями.\n   Остальные просто не будут подключаться.\n3.1. Примечание к следствию 3 - собственно, так  было  и  раньше  -  все  равно\n     исследование могло проводиться только на одной планете\n\nОчередь исследований\n--------------------\nОчередь исследований перенесена с планет на пользователя\nПри отмене исследования ресурсы возвращаются на ту планету, с которой были взяты\nНаграда за квесты на исследование теперь всегда начисляется на основную планету\nигрока\nИзменен  алгоритм  рассчета  эффективного  уровня  лаборатории  и  необходимого\nвремени исследования  при  настройке  сервера  \"Строить  лабораторию  во  время\nисследования: Нет\"\nТеперь при идущем исследовании блокируется  постройка/уничтожение  нано-  и/или\nлабораторий на все планетах\nТеперь  блокируется  попытка  начать  исследование   на   планете,   где   идет\nпостройка/уничтожение нано- и/или лабораторий\nОднако возможно начать исследование на другой планете. В таком исследовании  не\nбудут  участвовать  все  планеты  где  происходить  модификация   нано-   и/или\nлабораторий. При этом по окончании постройки/уничтожения время исследования  не\nпересчитывается\n\nТехнологии двигателей\n---------------------\nБонус к скорости полета кораблей  теперь  вычисляется  относительно  требуемого\nуровня технологии двигателя. При равной  технологии  пользователя  бонус  равен\nнулю,  при  отличной  -  разнице  уровней  между   требованиями   постройки   и\nпользовательской  умноженной  на  бонус  двигателя.  Если  уровень   технологии\nпользователя меньше, чем требуемый уровень (например, для  кораблей,  купленных\nна  Черном  Рынке),  то  корабль  получает  пенальти  к  скорости,  вычисляемое\nаналогично, но не более 95%\nПример. Бомбардировщик требует Ионный двигатель 6-го уровня.  Базовая  скорость\nполета корабля - 4.000. Каждый уровень технологии Ионных двигателей дает 20%  к\nскорости полета. Таким образом:\n  * При технологии Ионных двигателей 8-го уровня скорость полета составит:\n      4.000 * (1 + (8 - 6) * (20 / 100)) = 4.000 * (1 + 2 * 0,2) = 5.600\n  * При технологии 6-го уровня - 4.000\n  * При технологии 3-го уровня\n      4.000 * (1 + (3 - 6) * (20 / 100)) = 4.000 * (1 - 3 * 0,2) = 1.600\n  * Без технологии пенальти к уровню будет равно 120%, поэтому вступит  в  силу\n    ограничение:\n      4.000 * (1 + (0 - 6) * (20 / 100)) = 4.000 * (1 - 0,95) = 200\nТехнологии двигателей теперь так же влияют на расход топлива.  Каждый  уровень,\nвыше требуемого, уменьшает расход топлива  на  10%  от  бонуса  к  скорости  за\nуровень, но не больше чем 50% от расхода. Каждый  уровень,  ниже  требуемого  -\nувеличивает расход на 20% от бонуса.\nНапример, для Бомбардировщика каждый уровень Ионного двигателя, ниже 6-го будет\nувеличивать расход топлива на 4%  до  12%  при  полном  отсутствии  технологии.\nКаждый уровень, выше 6-го будет уменьшать расход топлива на 2%, вплоть до 25-го\nуровня, когда вступит в силу ограничение.\n\n\n\nСистема рейтингования и статистика\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\nСтатистика\n==========\nИзменен расчет статистики. Теперь в тратах  каждый  ресурс  считается  согласно\nкурсу обмена. Таким образом игроки с более  ценными  ресурсами  получат  больше\nочков\nДобавлена статистика по ресурсам\nДля игроков добавлены отображение следующих видов статистики: \"Проведено боев\",\n\"Выиграно  боев\",  \"Проиграно  боев\",  \"Уровень  за  постройки\",  \"Уровень   за\nисследования\", \"Уровень за рейдерство\". В качестве исходных данных используется\nинформация из записей игроков (т.е. актуальная информация на  момент  просмотра\nстатистики), поэтому изменение для данных типов статистики всегда  будет  равно\nнулю\nПолностью  переписана  страница  вывода  статистики  игроков   и   Альянсов   с\nиспользованием PTE\nТеперь можно управлять появлением игроков в статистике и рекордах. Для этого на\nстранице настроек сервера появились дополнительные настройки. Они размещаются в\nразделе \"Статистика и рекорды\"\nОтключение настройки \"Прятать админов\" добавит  в  статистику  и  рекорды  всех\nпользователей с  authlevel > 0. По умолчанию она включена\nНастройка  \"Прятать  игроков\"  позволяет  указать  через  запятую  перечень  ID\nигроков, которые не будут участвовать в статистике и рекордах. Это  может  быть\nполезно для создания NPC - ботов или  игроков,  которые  исполняют  их роли\nТак же в этот раздел вынесена настройка расписания  автоматического  обновления\nстатистики.  ВНИМАНИЕ!!!   КРАЙНЕ   НЕ   РЕКОМЕНДУЕТСЯ   МЕНЯТЬ   ЗНАЧЕНИЕ   ПО\nУМОЛЧАНИЮ!!!\nДобавлена опция \"Скрывать ссылки на ЛС\". При её включении в таблице  статистики\nне показывается URL на создание личного сообщения игрокам\nТеперь  переход  по  определенной  позиции  (например  со  страницы  Вселенной)\nскроллирует страницу сразу на эту позицию\nНемного уменьшен размер страницы статистики\n\n\n\nЗащита \"начинающих\" и \"слабых\" игроков\n======================================\nПолностью переписан алгоритм защиты игроков от нападения более сильного игрока.\n\nВозьмем двух игроков - Игрок1 и Игрок2. Если у Игрока1 количество  очков  общей\nстатистики в game_noob_factor (по умолчанию - 5) раз больше, чем у Игрока2,  то\nИгрок1 по  отношению  к  Игроку2  считается  \"сильным\"  игроком,  а  Игрок2  по\nотношению к Игроку1  считается  \"слабым\"  игроком.  \"Слабые\"  игроки  полностью\nзащищены от агрессивных действий со стороны \"сильных\" - на них нельзя нападать,\nнельзя шпионить, нельзя уничтожать луну итд. \"Слабый\" игрок может  нападать  на\n\"сильных\"\nУстановка game_noob_factor в 0 отключает защиту \"слабых\" игроков\n\nИгроки,  у  которых  количество  очков  в   общей   статистике   не   превышает\ngame_noob_points (по умолчанию  5.000)  считаются  \"начинающими\"  игроками.  На\n\"начинающего\" игрока может напасть только другой \"начинающий\"  (естественно,  с\nучетом соотношения \"слабый-сильный\", см. выше). \"Начинающий\" может нападать  на\nкого угодно.\nУстановка game_noob_points в 0 отключает защиту \"начинающих\" игроков\n\n\nПрокачка\n========\nПод \"прокачкой\" понимается пересылка ресурсов от слабого игрока к  сильному.  В\nСН встроена система  защиты  от  прямой  прокачки  (прямая  пересылка  ресурсов\nмиссией \"Транспорт\"). Однако вместе с  позитивными  моментами  защита  несет  и\nнегативные моменты - фактически исключен обмен ресурсами между игроками, потому\nчто в обмене в  подавляющем  большинстве  случаев  один  игрок  является  более\nсильным,  а  второй  -  более  слабым.  С  моей   точки   зрения   практическая\nневозможность обмена  ресурсами  вполне  компенсируется  невозможностью  прямой\nпрокачки (существуют, правда, способы непрямой  прокачки,  которые  я  не  буду\nздесь офишировать в силу  общедоступности  движка  вообще  и  данного  файла  в\nчастности любым игрокам). Однако  для  администраторов,  не  разделяющих  такую\nточку зрения, добавлена возможность разрешения прокачки в  настройках  сервера.\nСоответствующие установки настройки видны игрокам на странице сервера  \"Мировые\nконстанты\"\n\n\nКвесты\n======\nВ СН реализована подсистема квестов. Администратор сервера может самостоятельно\nдобавлять новые квесты и выбирать  награду  за  его  исполнение:  фиксированное\nколичество одного или любой комбинации ресурсов: металл, кристалл, дейтерий или\nТМ. Доступ к конструктору квестов  осуществляется  из  меню  \"Квесты\"  страницы\nадминистрирования. Создание  квестов  доступно  только  Администратору  сервера\n(auth_level = 3). Администратор может посмотреть выполненные квесты  игрока  по\nссылке в профиле игрока (поиск через админпанель)\nДоступны квесты на постройку зданий и обороны, на исследование и  на  постройку\nкораблей.   Игроки,   превысившие   условия   квеста,   автоматически   получат\nвознаграждение при следующей проверке на критерии  выполнения.  Например,  если\nцелью квеста является постройка шахты 10го уровня, то при постройке шахты  выше\n9го уровня на любой планете игрок получит квестовое вознаграждение. То же самое\nверно и по отношению к уничтожению шахты. Однако, если при уничтожении шахты её\nуровень окажется ниже 10го, то игрок вознаграждения не получит, хотя он  уже  и\nимел  шахту  10го  уровня.\nТриггер на условие квеста \"постройка кораблей\" является локальным и срабатывает\nпри постройке указанного корабля. Т.е. если есть квест на постройку 10 ЛИ, а на\nпланете есть 20  ЛИ,  то  вознаграждение  квеста  игрок  получит  только  после\nпостройки 1 ЛИ. \"Локальность\" означает, что  если  в  указанном  примере  игрок\nимеет 10 ЛИ на всех колониях, но на одной колонии их количество меньше  условия\nквеста, то вознаграждение за квест он получит только если в момент постройки ЛИ\nих количество на одной планете будет равно 10.\nИгрок может просматривать список доступных квестов и их статус (выполнен или не\nвыполнен). По выполнению квеста игроку высылается письмо с уведомлением.  Общее\nколичество и количество выполненных квестов видно игроку в навбаре\n\n\nМодуль новостей\n===============\nДобавлен полноценный модуль новостей, доступный по  ссылке  \"Новости\"  в  левом\nменю. Доступ к управлению новостями админы получают по той же  ссылке  -  права\nопределяются по authlevel\nК пункту меню \"Новости\" добавлен индикатор непрочитанных сообщений\nДобавлена дополнительная строка в админском интерфейсе, в которую  можно  вбить\nURL с подробностями новости. URL добавится в конце новости в  виде  гиперссылки\nсо слова \"Подробнее...\" из текста новости\nДобавлена возможность массовой рассылки новости всем игрокам\nДобавлена лента новостей на страницу обзора планеты. Выводятся только последние\nнепрочитанные новости. Количество выводимых новостей настраивается  на  сервере\n(по умолчанию - 3)\nКоличество новостей ограничено 20-ю самыми свежими\nНа странице Обзора планеты добавлена подсказка  как  закрыть  окно  со  свежими\nновостями\n\nРедактирование новости\n----------------------\nПри редактировании новости галочка рассылки новости по умолчанию отключена\nПри редактировании новости не изменяется глобальное время  написания  последней\nновости - т.е. отредактированная новость не включает список последних  новостей\nна обзоре планеты\n\n\nИгроки\n======\nДобавлено  отображение  пола  игрока  на  странице  \"Император\",  на   странице\nстатистики и в попапе игрока на странице \"Вселенная\"\n\nАватар\n------\nДобавлена поддержка серверных аватаров игроков\nАватары  могут  быть  загружены  с  локального  диска  на   странице   настроек\nпользователя.\nПоддерживаются файлы форматов JPG, GIF и PNG  размером  до  200КБ.  Загруженные\nкартинки будут отмасштабированы до размеров 128х128.\nАватар отображается на странице \"Император\"  и  в  попапе  игрока  на  странице\n\"Вселенная\".\n\n\nАльянсы\n=======\nПолностью  переписан  модуль  управления   Альянсами.   Кардинально   сокращено\nколичество прав, упрощен интерфейс, переписана система заявок и так далее.\n\nЛоготип Альянса\n---------------\nДобавлена поддержка серверных логотипов  Альянсов\nЛоготипы могут  быть  загружены  с  локального  диска  на  странице  управления\nАльянсом\nПоддерживаются файлы форматов JPG, GIF и PNG  размером  до  200КБ.  Загруженные\nкартинки будут отмасштабированы до размеров 128х128\nЛоготип отображается на странице информации об Альянсе и в  попапе  Альянса  на\nстранице \"Вселенная\"\n\nДипломатия\n----------\nСН содержит подсистему дипломатии Альянсов. Дипломатия позволяет  устанавливать\nразличные  типы  отношений  между  Альянсами.  Некоторые  отношения  влияют  на\nвнутриигровую механику, некоторые - нет\nИнформация  о  текущих  дипломатических  отношениях  Альянса  отображается   на\nстранице информации об Альянсе и доступна для просмотра любому игроку\nГлава Альянса может начинать  переговоры  и  принимать  предложения  от  других\nАльянсов, выбрав пункт \"Переговоры\" в  заголовке  таблицы  дипломатии.  Там  же\nможно сделать  предложение  об  изменении  отношений  другому  Альянсу.  Нельзя\nсделать предложение текущих отношений (т.е. если Альянсы находятся в отношениях\n\"Война\" нельзя  опять  предложить  отношение  \"Война\").\nВ общем случае что бы изменилось  отношения  между  Альянсами,  другая  сторона\nдолжна потвердить предложение об изменении по  ссылке  \"Переговоры\",  доступной\nглаве Альянса (исключения из данного правила изложены ниже).\nОтношения между Альянсами бывают следующие:\n1. Нейтралитет. Отношение по умолчанию. Нет никаких ограничений или бонусов\n2.  Война.  Отключается  система  защиты   башинга   между   членами   Альянса,\nнаходящимися в состоянии  войны.  Автоматически  принимается  второй  стороной.\nПодробнее см.ниже в описании защиты от башинга\n3.  Мир.  Рекомендуется  выставлять  этот  статус  после  заключения  пакта   о\nненападении. С точки зрения движка оно ничем не отличается от \"Нейтралитета\"  и\nнужно  для  информирования  остального  игрового  сообщества  о  неких   устных\nдоговоренностях - буде в таком информировании возникнет нужда.  Альянсы  вольны\nследовать или не следовать данной рекомендации, а так же  решать  -  хотят  они\nоповестить Вселенную об изменении своих отношений или нет\n\n\n[#] Технологии и Наемники Альянсов\n==================================\nДАННЫЙ МОДУЛЬ НЕ ВХОДИТ В СОСТАВ ПУБЛИЧНОЙ ВЕРСИИ!\n\nАльянсы теперь могут рекрутировать Наемников и исследовать технологии!\n\nСчет Альянса\n------------\nКаждый Альянс имеет счет с ресурсами металл/кристалл/дейтерий/ТМ\nРесурсы со счета Альянса могут расходоваться только  на  нужды  Альянса.  Вывод\nресурсов со счета Альянса невозможен\nЧлен Альянса может перевести ресурсы на счет  Альянса.  Сделать  это  можно  на\nглавной странице Альянса в разделе \"Ресурсы  Альянса\".  Там  же  можно  увидеть\nсостояние счета Альянса и бонусы, предоставляемые Альянсом (см. ниже).\nНа ряде страниц (в частности - на странице Исследований) в  верхнем  ресурсбаре\nпоказывается не количество ресурсов на планете игрока,  а  количество  ресурсов\nАльянса\nГлава  Альянса  с  его  счета  может  исследовать  технологии  и  рекрутировать\nНаемников - соответственно пункты \"Исследования Альянса\" и  \"Наемники  Альянса\"\nна странице управления\n\nИсследования Альянса\n--------------------\nПри исследовании технологии уровень  лаборатории  равен  количеству  игроков  в\nАльянсе на момент  начала  исследования.\nАктивные исследования видны членам Альянса на странице информации\n\nБонус Альянса\n-------------\nПосле  достижения  минимально  необходимого  размера  Альянса  (10  человек  по\nумолчанию, задается в таблице `config` записью 'ali_bonus_members') каждый член\nАльянса получает бонус к своим Наемникам и технологиям\n\nБонусы от Наемников и Технологий так же действуют  при  проверки  требований  к\nпостройкам/исследованиям. Например: игрок состоит в Альянсе, дающем бонус +2  к\nЛазерной технологии, а его собственный уровень технологии равен 4.  Эффективный\nуровень технологии этого игрока равен 6. Это означает, что находясь  в  Альянсе\nон имеет доступ к исследованию Ионной технологии (требуется ЛТ 5-го  уровня)  и\nможет строить Тяжелый Лазер (требуется ЛТ 6-го уровня). Очевидно, если  бы  он\nне находился в Альянсе, эти постройки были бы заблокированы\n\nСтандартный алгоритм расчета\n----------------------------\nДанный алгоритм включен по умолчанию (переменная ali_bonus_algorithm в  таблице\nconfig установлена в 0).\nЗначение бонуса зависит от  количества  игроков  в  Альянсе  и  вычисляется  по\nформуле:\n  Бонус = round(уровень технологии или Наемника / количество игроков),\nгде round() - операция математического округления.\n\nПримеры:\n  1. Альянс из 10 человек купил Технологию 4 уровня:\n       Бонус = round(4/10) = round(0,4) = 0\n  2. Альянс из 10 человек купил Технологию 7 уровня:\n       Бонус = round(7/10) = round(0,7) = 1\n\nПочему все работает именно так?\n-------------------------------\nВыбранная  механика  бонусов  Альянса  призван  обеспечить   достижение   сразу\nнескольких целей:\n1. Исключить   злоупотребление  фишкой,  когда  2-3  игрока  формируют   Альянс\n   исключительно для получения бонусов\n2. Активизировать межальянсную активность:  бонусы  от  ресусов  Альянса  можно\n   получить только начиная с определенного  количества  участников.  Ну  и  чем\n   больше игроков в Альянсе, тем больше у него ресурсов\n3. Усилить лояльность игроков к Альянсу - при выходе (или  выгоне)  из  Альянса\n   игрок теряет все бонусы и (самое неприятное) все ресурсы,  пожертвованные  в\n   Альянс\n4. Исключить появление мегаальянсов: чем больше игроков - тем  больше  ресурсов\n   они могут пожертвовать, но  тем  меньше  бонусов  получит  каждый  отдельный\n   игрок\n5. Слабые игроки в сильных Альянсах получают доступ к  end-game  юнитам  (если,\n   конечно, глава Альянса решит потратить ТМ на  соответствующих  Наемников)  и\n   бонус в развитии\n6. Сильные игроки смогут поднять эффективные уровни Технологий даже  в  больших\n   Альянсах. Например, если в Альянсе 15 человек,  то  исследовать  15  уровень\n   технологии всем Альянсом будет проще и дешевле, чем каждому игроку отдельно\n7. То же самое распространяется и на Наемников. При  этом  только  Альянс  дает\n   возможность получить эффективный уровень Наемников больше максимального\n\nВыбранный метод расчета  бонусов  позволяет  создавать  оптимальную  среду  для\nАльянсов. Регулируя минимальное  количество  участников  для  получения  бонуса\nможно косвенно регулировать размер Альянса.\nОптимальный размер Альянса будет равен:\n  floor(<минимальный размер> * 1,5)\n  где floor() - операция отбрасывания дробной части\nОднако, возможны варианты. В  движке  реализованы  три  метода  расчета  бонуса\nАльянса:  по  количеству  членов  Альянса  (описанный  выше,  используется   по\nумолчанию); по минимальному размеру Альянса; по очкам Альянса; по месту Альянса\nв статистике.\n\nРасчет по минимальному размеру Альянса\n--------------------------------------\nДанный алгоритм включается установкой переменной ali_bonus_algorithm в  таблице\nconfig в 1.\nЗначение бонуса вычисляется  по формуле:\n    Бонус = round(<уровень> * <участники> / sqr(<минимальный размер>)),\n  где\n    round() - операция математического округления,\n    sqr() - операция возведения в квадрат\n    <уровень> - уровень технологии или Наемника,\n    <участники> - количиство игроков в Альянсе,\n    <минимальный размер> - минимальный размер Альянса для получения бонуса.\n\nПримеры для параметров по умолчанию:\n  1. Альянс из 10 человек купил Технологию 4 уровня:\n     Бонус = round(4 * 10 / 100) = round(0,4) = 0\n  2. Альянс из 10 человек купил Технологию 17 уровня:\n     Бонус = round(10 * 10 / 100) = round(1,7) = 2\n  3. Альянс из 20 человек купил Технологию 17 уровня:\n     Бонус = floor(17 * 20 / 100) = round(3,4) = 3\n\nПри использовании этого  метода  нужно  уделять  особое  внимание  минимальному\nразмеру Альянса. Небольшой размер (6 и меньше)  будет  давать  ускоренный  рост\nбонуса в зависимости от количества членов Альянса -  и  спровоцирует  появление\nмегаальянсов. С другой стороны, большой размер (13 и выше) сделает  практически\nбесполезным  исследования  и  рекрутинг  Наемников,  поскольку  прирост  бонуса\nтребует  либа  создание   ГИГАальянса,   либо   недостижимо   высоких   уровней\nтехнологий/Наемников.\n\nЭтот метод рекомендуется:\n1. При стандартных настойках (скорость игры - 1х-3х, мин.размер  -  10)  -  для\n   небольших и средних серверов.\n2. Мин.размер порядка 8 - для быстрых (10х и выше) PvP серверов.\n3. Мин.размер порядка 12 - для больших серверов  с  околостандартной  скоростью\n   игры (1x-3x).\n\nРасчет по очкам Альянса\n-----------------------\nДанный алгоритм включается установкой переменной ali_bonus_algorithm в  таблице\nconfig в 2.\nЗначение бонуса вычисляется по формуле:\n    Бонус = round(<уровень> * <очки> / <количество игроков> / <делитель>),\n  где\n    round() - операция математического округления,\n    <уровень> - уровень технологии или Наемника,\n    <очки> - количество очков Альянса,\n    <количество игроков> - количество игроков в Альянсе,\n    <делитель> - задается переменной ali_bonus_divisor  в  таблице  config,  по\n    умолчанию равен 10.000.000\n\nДанный метод следует использовать с осторожностью и  перед  его  использованием\nтщательно  исследовать  статистику  сервер  для  выбора  оптимального  значения\nali_bonus_divisor. Кроме того, данный метод поощрает \"кучкование\" топов в одном\nАльянсе.\n\nРасчет по месту Альянса в статистике\n------------------------------------\nДанный алгоритм включается установкой переменной ali_bonus_algorithm в  таблице\nconfig в 3.\nЗначение бонуса  вычисляется  по  достаточно  сложной  формуле,  которую  можно\nпосмотреть в исходном коде. Здесь же я приведу словесное описание.\n1. Рейтинг Альянсов разбивается на  определенное  количество  \"категорий\".  Оно\n   задается переменной ali_bonus_brackets в таблице config и по умолчанию равно\n   10. Т.е. динамически строится таблица уровней.\n2. Определяется, в какую из категорий попал текущий Альянс и в  соответствии  с\n   этим ему присваивается \"уровень\".\n3. В зависимости от уровня назначается коэфициент бонуса. Если Альянс находится\n   в   первой   категории   -   ему   устанавливается   коэфициент   10   (т.е.\n   ali_bonus_brackets), во второй - 9 и так далее  до  последней  категории,  в\n   которой присваивается коэфициент 1.\n4. Полученный  коэфициент  делится  на  ali_bonus_brackets_divisor  из  таблицы\n   config   (50   по   умолчанию).    Фактически,    ali_bonus_brackets_divisor\n   контролирует   как   сильно   уровень   Альянса   влияет   на    бонус    от\n   технологии/Наемника.\n5. Бонус  Альянса  получается  умножением  результата  предыдущих  операций  на\n   уровень технологии или Наемника.\n\nПример 1\nНастройки по умолчанию. Альянс занмает 5 место из 80.\n1. Размер категории = 80/10 = 8. Т.е. первый уровень - Альянсы с рейтингом от 1\n   до 9, второй уровень - от 10 до 18 итд.\n2. Наш Альянс попал в первую категорию - его уровень равен 1.\n3. Его коэфициент бонуса равен 10.\n4. Множитель бонуса = 10/50 = 0,2.\n5. Таким образом бонус будет равен 20% от уровня технологии/Наемника.\n   До 2го уровня - бонус будет равен нулю.\n   С 3го по 7й - бонус равен 1.\n   С 8го по 12й - бонус равен 2.\n   И так далее\n\nПример 2\n5 категорий, делитель 50. Альянс занмает 50 место из 80.\n1. Размер категории = 80/5 = 16. Таблица уровней: 1-16, 17-32, 33-48, 49-64,\n   65-80.\n2. Альянс попал в 4ю категорию, уровень равен 4.\n3. Коэфициент бонуса равен 2.\n4. Множитель бонуса = 2/50 = 0,04\n5. Таким образом бонус будет равен 4% от уровня технологии/Наемника.\n   До 12го уровня - бонус будет равен нулю.\n   С 13го по 37й - бонус равен 1.\n   С 38го по 62й - бонус равен 2.\n   И так далее\n\nНа последнем примере демонстрируется важность правильного подбора делителя  под\nколичество категорий.\n\n\nБашинг\n======\n\nОпределения\n-----------\nАтака - отправка флота с  агрессивной  миссией  (\"Атака\",  \"Совместная  атака\",\n\"Уничтожение\") на объект Вселенной (планету или луну) другого игрока\nБашинг - многократная повторная атака одним игроком одного и  того  же  объекта\nВселенной за некоторый промежуток времени. Этот  промежуток  называется  \"окном\nбашинга\". Обычно окно башинга принимается равным 24 часам\nВолна - несколько флотов, посланные один  за  другим  за  небольшой  промежуток\nвремени. Этот промежуток называется \"окно волны\" и обычно его принимают  равным\n30 минутам.\nКак правило, количество флотов  в  одной  волне  так  же  ограничивают,  а  все\nследующие флоты относят к новой волне. Максимальное числов флотов в одной волне\nназывается \"длина волны\". Традиционно длину волны принимают равной 3 флотам.\n\nАнтибашинг\n----------\nСН содержит систему защиты от  башинга  \"Антибашинг\".  Защита  не  дает  игроку\nотправить на одну планету больше боевых флотов, чем предусмотрено правилами.\nИзменить параметры Антибашинга можно на странице  настроек  сервера.  Установка\nколичества атак в одной волне в 0 означает  полное отключение системы защиты\nНастройки системы по умолчанию: 3 волны по 3 атаки c промежутком  не  более  30\nминут между атаками в одной волне за 24 часа\nАнтибашинг учитывает флоты в полете. Т.е. если игрок уже запустил две  волны  и\nеще одна находится в полете - он больше не сможет запускать флоты\nАтаки засчитываются по факту - т.е. если полностью отменить волну,  находящуюся\nв полете, игрок сразу же сможет послать на планету новые  флоты,  не  дожидаясь\nвозвращения ранее отправленных флотов. Частичная отмена  волны  может  изменить\nраспределение следующих флотов по волнам.\nАтаки учитываются вне зависимости от результата (выигрыш, ничья, проигрыш)\nПри САБе атака засчитывается ВСЕМ нападающим - дабы избежать  \"карусели\",  т.е.\nкогда  несколько  игроков  по  очереди  запускают  САБы,  а  остальные  к   ним\nприсоединяются. При этом все флоты одного игрока в  одном  САБе  считаются  как\nодна атака\nДобавлена  возможность  настройки  системы  антибашинга  на  страницу  настроек\nсервера\nЕсли Альянсы находятся в отношении \"Война\",  защита  от  башинга  не  работает.\nОднако объявление войны отключает Антибашинг не сразу, а лишь спустя  12  часов\n(данный параметр настраивается по усмотрению администратора сервера)\nОбъявление войны  не  требует  согласия.  Это  означает,  что  когда  Альянс  А\nпредложил  Альянсу   Б   отношение   \"Война\",   это   предложение   принимается\nавтоматически и отношения устанавливаются сразу для обоих Альянсов. А вот выход\nиз состояния войны (т.е. смена  отношения  \"Война\"  на  любое  другое)  требует\nсогласия обоих сторон.\nВыход из состояния войны обратной силы  не  имеет!  Т.е.  если  было  объявлено\nперемирие когда планеты одного из Альянсов находятся под атакой, то  флоты  все\nравно долетят и совершат нападение - какое бы ни  было  новое  отношение  между\nАльянсами (если, конечно, атакующий их не отзовет)\n\nТривиа\n------\nА вы знаете почему такой относительно простой вещи (40+ человекочасов)  нет  на\nОффе? А что бы денежки снимать за разбан!\n\nАнтиРМФ\n=======\nЕсли флот атакующего уничтожен за один раунд, то:\n  1. Атакующий не получает отчета о бое\n  2. Флоты, находящиеся в удержании так и остаются на орбите\nИзменена процедура генерации писем с уведомлением  о  боевом  отчете  следующим\nобразом:\n  1. Если бой закончился за один раунд проигрышем атакующего,  то  он  получает\n  сообщение о том,  что  связь  с  флотом  прервалась  и  не  получает  никакой\n  дополнительной информации (включая ссылку на боевой отчет)\n  2. Теперь все  участники  боя  (включая  членов  САБа  и  хозяинов  флотов  в\n  удержании) получают одинаковые письма (кроме случая, описанного в п.1)\n  3. Уведомление о бое  всегда  содержит  потери  атакующих  и  оброняющихся  и\n  сведения о поле обломков\n  4. Сведения о вывозе ресурсов  с  планеты  добавляются  в  отчет  только  при\n  выигрыше атакующих\n  5.  Уведомления  теперь  корректно  окрашиваются  для  всех  участников  боя:\n  красным, если участник проиграл, зеленым - если выиграл, оранжевым - в случае\n  ничьи\n  6. Все числа в уведомлении теперь форматируются\n\n\nНоваПедия\n==========\n\"Ракетный двигатель\" переименован в \"Химический\", а \"Импульсный\" - в  \"Ионный\".\nДля них полностью изменено описание. Так же  изменено  соответствующе  описание\nкораблей\nПолностью написана с нуля страница информации о юнитах\nТеперь в Новапедии показываются требования для постройки/исследования юнита\nТеперь для корабля показываются  данные  для  всех  типов  двигателей,  которые\nвозможно на него установить\nУлучшено отображение информации о кораблях и обороне\n\nДерево технологий\n-----------------\nПолностью переписано дерево технологий (бывш. techtree.php)\nРядом с названиями юнитов там, где это имеет смысл, отображаются  их  уровни  в\nИмперии/на текущей планете\nТеперь вместо полного уровня с учетом  бонусов  отображаются  отдельно  базовые\nуровни и отдельно бонус к ним\nДобавлена поддержка дополнительных требований к строительству юнитов  (например\n- требование определенной расы для модуля расовых юнитов)\n\n\nЗаметки\n=======\nС нуля написаны заметки\n\n\nДрузья\n======\nСтраница друзей написана с нуля\nТеперь подробно сообщается обо всех ошибках и результатах операций с заявками\nВ личную почту отправляются сообщения по приходу, принятию и отверганию заявки,\nа так же при разрыве дружеских отношений\nЦветовое кодирование статуса друга: зеленый - онлайн, желтый - бездействие от 5\nдо 15 минут, оранжевый - оффлайн, красный - оффлайн более суток\n\n\nЗакладки\n========\nПолностью переписана система закладок. Теперь  закладки  хранятся  в  отдельной\nтаблице и не захламляют данные пользователя.  Кроме  того,  такое  расположение\nзакладок повышает устойчивость движка к SQL-injection.\nПолностью переделано редактирование закладок. Добавлена возможность копирования\nзакладок\n\n\nСообщения\n==========\nПолностью  переписана  система  сообщений\nМожно  писать  письма  любому  игроку!  Форма  создания  нового  письма  теперь\nкорректно  обрабатывает  имена  игроков,  введенных  в   строку   \"Кому\".   Эта\nвозможность  доступна  из  списка  категорий  сообщений  по  ссылке   \"Написать\nсообщение\" в  самом  низу  таблицы  категорий.  При  первом  открытии  страницы\nсоздания нового сообщения больше не выскакивают угрожающие красные надписи\nВ списке писем теперь работает чекбокс в заголовке.  Клик  на  нем  приведет  к\nвыбору всех сообщений. Повторный клик - к снятию всех отметок\nДобавлен дополнительный диапазон для удаления сообщений - \"Все сообщения данной\nкатегории\". Внимание! В категории \"Все сообщения\" его выбор приведет  к  полной\nочистке почтового ящика!\nСчетчик сообщений в навбаре работает без задержек. Т.е. если  игрок  перешел  в\nкатегорию  с  непрочитанными  сообщениями,  счетчик  изменится  соответствующим\nобразом сразу же после перехода, а не при следующей загрузке страницы\nКлик на индикаторе сообщений Администрации, Альянса или от другого игрока сразу\nоткрывает просмотр соответствующих сообщений\nОптимизированы алгоритмы работы подсистемы сообщений, а так же почти в два раза\nуменьшен объем передаваемой информации  от  клиента  к  серверу.  Особенно  это\nзаметно при удалении большого количества сообщений\nДобавлена возможность просмотра отправленных сообщений (только тех, которые  не\nудалил адресат)\nТеперь можно сразу из отчета  шпиона  запускать  симуляцию  боя  по  полученным\nразведданным\nПерекрашены сообщения. Цвета заголовков теперь соответствуют  цветам  сообщений\nи, в свою очередь, синхронизированны с раскраской флотов - там, где  это  имеет\nсмысл\nКатегории  сообщений переупорядочены\nДобавлена возможность очистить сообщения определенной категории, не открывая их\n- на случай переполнения  почтового ящика\nДобавлена подсказка\nВ настройках пользователя  можно  отключить  получения  определенных  категории\nсообщений. В этот список  входят:  Шпионские  отчёты,  Военные  отчёты,  Отчеты\nпереработки, Прибытие флота, Отчёты  экспедиций,  Сообщения  очереди  построек.\nНастройки автоматических уведомлений включены по умолчанию для новых игроков\nДобавлена новый класс сообщений \"Сообщения  Администрации\".  Уведомления  этого\nкласса  НЕ  МОГУТ  быть  отключены  в  настройках  пользователя.  К  сообщениям\nАдминистрации  относятся:  сообщения  системы  квестов,   новости   сервера   и\nсобственно сообщения Администрации (личные сообщения, отправленные от игроков с\nauth_level > 0)\nВосстановлена функциональноксть класса сообщений \"Сообщения очереди  построек\".\nУведомления этого класса могут быть отключены в настройках пользователя. К  ним\nотносятся:\n  1. Уведомления о завершении исследований. Уведомление высылается после  входа\n     на страницу исследований\n  2. Уведомления об окончании работы верфи на планете.  Уведомление  высылается\n     по окончании очереди строительства Верфи\n  3. Уведомление об окончалии строительных  работ  на  планете  (постройка  или\n     разрушение здания). Уведомление высылается  пакетно  в  полуавтоматическом\n     режиме. Это означает, что сообщение генерируется каждый раз при  обращении\n     к планете  (сканирование  шпионажом  или  игроком,  переключение  активной\n     планеты игроком итд). При этом  в  сообщение  указываются  все  изменения,\n     произошедшие на момент обращения\nДобавлена возможность пересылки личных сообщений на емейл  игрока.  Возможность\nвключается администратором сервера в настройках - опция \"Разрешить пересылку ЛС\nна email\". После этого в настройках игрока появляются дополнительные опции  для\nвсех категорий входящих сообщений\n\n\nЧат\n===\nПолностью переписан внутренний чат. JS-часть переведена  на  jQuery.  Добавлена\nзаплатка для корректной работы чата в FireFox\nИмена  членов  Команды  сервера  (Администраторов,  Операторов  и  Модераторов)\nвыделяются в чате, что бы отметить их среди сообщений обычных пользователей. По\nумолчанию имя Администратора выделяется пурпурным цветом, оператора -  красным,\nмодератора - зеленым. В таблице  config  можно  настроить  стили  выделения  по\nсвоему вкусу\nДобавлена  возможность  настраивать  таймаут  чата  через  интерфейс   настроек\nВселенной (раздел \"Настройки чата\"/\"Таймаут  по  неактивности\").  Значение  \"0\"\nозначает, что таймаут отключен (не рекомендуется, потому что  при  неправильных\nнастройках сервера большое одновременное количество запросов может  привести  к\nDoS)\nИстория чата теперь грузиться в виде  нормальной  страницы  СН,  а  не  в  виде\n\"обмылка\". Корректно показывает заголовок в истории чата - \"общий чат\"  и  \"чат\nальянса\" соответственно\nБоевые отчеты  теперь  преобразуются  в  ссылки.  Из  соображений  безопасности\nработают только ссылки на текущем сервере. По  клику  на  ссылку  боевой  отчет\nоткрывается в новом окне\nДобавлена защита на стороне клиента от слишком частых обновлений\nСодержимое языкового файла chat.mo отфильтровано и влито в system.mo\nТэг Альянса после имени игрока  теперь  указывается  в  квадратных  скобках,  а\nадресат сообщения - в круглых. Сделано для унификации написания тэга Альянса  в\nдвижке\nЧат теперь инкрементальный - с сервера передается не  всё  содержимое  чата,  а\nтолько новые сообщения. Чат корректно работает когда у игрока открыто несколько\nокон с чатом\nИсправлена проблема со скроллированием чата в Chrome v20+\nТеперь при отключении чата по таймауту содержимое окна не стирается, а  в  него\nдобавляется соответствующее сообщение. Так же прячутся  элементы  ввода:  выбор\nцветов, строка сообщения, кнопка \"Отправить\" и панель смайлов\nНовый код чата (как JS, так и PHP) заметно  компактнее,  аккуратнее  и  быстрее\nстарого\nУвеличена длина поля для ника в чате\nДобавился новый BBCode \"s\" - зачёркнутый текст\nВ чате Альянса в нике участника теперь не указывается Альянс\nПереформатирован вывод списка смайлов.  Список  смайликов  теперь  генерируется\nавтоматически из всего доступного списка\nПри открытии окна чата курсор позиционируется в строку набора сообщения\nРеформатирование HTML-кода страницы чата\nТри файла чата интегрированы в один\nМножество других добавлений и усовершенствований\n\n\nМодуль \"Продвинутый чат\": chat_advanced\n=======================================\nИмеется возможность установки модуля чата с расширенной функциональностью\nРасширенный чат имеет встроенную систему команд с поддержкой  алиасов.  Команды\nвводятся в строке ввода сообщения и начинаются символом \"/\"\nЧат оборудован встроенной системов помощи по командам чата - команда  /help.  В\nней  можно  узнать  более  подробно  о  досутпных   командах   и   формате   их\nиспользования. Здесь же описание будет дано  лишь  список  доступных  команд  и\nописание их функций\nДобавлен список игроков в чате с дополнительными иконками статуса  и  командами\nуправления для админов\nКоманда  /invisible  дает  возможность  игрокам  управлять   своим   состоянием\nвидимости в чате. Администрация сервера (authlevel > 0) всегда видит невидимок\nКоманда /whisper  позволяет  отправлять  приватные  сообщения  другим  игрокам.\nПриватные сообщения выделяются специальным образом, видны  во  всех  каналах  и\nсохраняются  в  истории  чата.  В  приватных  сообщенях   нельзя   использовать\nформатирование цветом\nАдминистраторы имеют возможность запретить игроку писать в чат на  определенный\nсрок или вернуть такую возможность - соответственно, команды /mute  и  /unmute.\nЗапрет распространяется на все каналы и на возможность писать личные сообщения.\nСоответствующая иконка в списке игроков лишает его права голоса на 1 час\nАдминистраторы имеют возможность блокировать и разблокировать игроков прямо  из\nчата - соответственно, команды /mute и /unmute. Иконка в списке  игроков  банит\nего на 1 неделю\nМаксимальное время нахождения игрока в списке онлайн совпадает с таймаутом чата\nна странице сервера - т.е. в списке онлайн  игрок  будет  виден  еще  некоторое\nвремя после выхода из чата\nДобавлена поддержка локального времени в чат и историю чата\nТеперь можно использовать команды при выбранном цвете  сообщения.  Ранее  такие\nкоманды не воспринимались системой чата\nПроизведена замена цветов для лучшей читаемости сообщений: red -> maroon,  blue\n-> cyan\nЦвет green оставлен для  пользвателей,  а  подтверждающие  системные  сообщения\nиспользуют цвет lime - как и в остальном интерфейсе сервера\nСистемные и приватные сообщения теперь выделяются жирным шрифтом\nКлик на имени игрока в списке онлайна теперь всегда добавляет  команду  \"/w\"  в\nначало сообщения - а не в конец, как ранее\nСкорость  обновления  в  AJAX  части  чата   теперь   регулируется   переменной\n'chat_refresh_rate'\nТеперь игроки из онлайн-списка исчезают сразу после выхода из  чата  -  таймаут\nпопадания в список установлен  как  удвоенный  'chat_refresh_rate',  а  не  как\n'chat_timeout'  ранее  и  вычисляется  по  дополнительному  полю,   а   не   по\n`chat_player_activity` как ранее\n\n\nДень Рождения\n=============\nИгрок может ввести свой ДР на своей странице настроек. ДР вводится один  раз  и\nпосле этого не может быть изменен. Дата проходит валидизацию в  соответствии  с\nсерверными настройками формата даты\nИгрок с ДР на текущую дату будет отмечен специальной иконкой в статистике и  на\nстранице Вселенной.  При  наведении  на  иконку  всплывает  подсказка  с  датой\nрождения\nАмдинистратор  сервера  может  назначить  количество  ТМ  в  подарок  на  ДР  в\nнастройках (опция  \"Подарок  игроку  на  день  рождения\").  Если  это  значение\nустановлено в 0 - подарки отключены.\nВыдача подарков происходит один раз в сутки всем игрокам, день рождения которых\nнаходится не далее чем в \"Ретро-рождение\" дней от текущей даты.\nПри этом подарки выдаются только игрокам, которые на момент  выдачи  уже  имели\nвведенную дату рождения. Движок гарантированно начислит подарки  даже  если  ДР\nпришелся на день неактивности сервера (неисправность или обслуживание).\nТакая система выбрана с одной стороны - что бы  не  обидеть  игроков  в  случае\nпроблем с сервером, а с  другой  стороны  -  что  бы  избежать  злоупотреблений\n(например - ввести послезавтрашнюю дату ДР, на следующий день  получить  ТМ  за\n\"прошлый ДР\", а через день - еще и  за  \"нынешний\".  Такой  вариант  в  текущей\nсистеме начисления подарков не прокатит)\n\n\n\nИнтерфейс\n~~~~~~~~~\n\nЛогин и регистрация\n===================\nПолностью переделаны экраны логина и регистрации\nПолностью переделана подсистема восстановления забытого пароля\nНезалогиненные пользователи имеют  возможность  предварительно  ознакомиться  с\nсервером: почитать новости, просмотреть настройки сервер, статистику игроков  и\nсписок банов, связаться с администраторами, ознакомится  с  правилами  и  ЧаВо,\nсходить на форум\n\n\nНавбар и планетбар\n==================\nПолностью переработан навбар\n\nСведения о количестве ресурсов на планете вынесены в планетбар (по умолчанию он\nавтоматически появляется на странице \"Обзор планеты\" и на  некоторых  страницах\nпостройки юнитов). В настройках пользователя можно включить планетбар постоянно\n- т.е. будет полностью восстановлен функционал старого навбара\n\nПереработана  ячейка  сообщений.  Теперь  в   ней   кроме   общего   количества\nнепрочитанных сообщений отдельно отображается  количество  новых  сообщений  от\nадминистрации, от игроков и от членов Альянса\n\nДобавлено отображения количества флотов и экспедиций - находящихся в  полете  и\nмаксимально доступных. Количество флотов и экспедиций  в  полете  интерактивно:\nоно  автоматически  изменяется  в  соответствие  с  происходящими  событиями  -\nприбытие, возвращение и окончание миссии флота (как они должны  были  произойти\nна момент загрузки страницы). При наведении курсора на  соответствующую  ячейку\nвсплывает  подсказка с описанием ближайшего события\n\nДобавлена информация о текущих исследованиях пользователя\n\nВся информация теперь  выводится  поверх  кликабельных  иконок  с  всплывающими\nподсказками\n\n\nВселенная\n=========\nСерьезно переработан код \"Вселенной\" (бывш. \"Галактика\").\nШаблоны вытащены из кода и перенесены в TPL-файл.\nПопапы (инфо о планетах, лунах итд) вынесены в отдельные  JS-процедуры,  что  в\nотдельных случаях позволило уменьшить финальный размер страницы в два-три раза,\nа количество запросов к БД снизить от двух до  десяти  (!)  раз  -  особенно  в\nсистеме, густо заселенной одним игроком.\nБиблиотека  попапов  изменена  с  overlib  на  jQuery  -  это   дало   отличную\nкросс-браузерную совместимость и устранение проблем с попапами в ИЕ.\nВ целом новая \"Вселенная\"  существенно  меньше  грузит  сервер  и  работает  на\nклиенте гораздо быстрее\nРакеты теперь отправляются через AJAX без обновления страницы\nНа  изображении  вражеских  планет,  на  которые  летят  флоты  игрока   теперь\nотображается соответствующая иконка и при наведении всплывает попап со  списком\nвсех входящих кораблей\nВ попап Альянсов добавлен его ранг\nНа попапе игрока отображается его текущее звание в Альянсе\nШаблоны попапов легенды, планет, лун, обломков, игроков и альянсов вынесены  из\nJS-скрипта в шаблон страницы\nУбрано количество летящих флотов - эта информация есть в навбаре\nПолностью  переписана  работа  AJAX-части,  отвечающей  за  отправку   шпионов,\nпереработчиков и ракет\nКоличество переработчиков теперь включает  все  виды  кораблей,  которые  могут\nперерабатывать обломки\nИнтерфейс запуска ракет использует группу защитных сооружений, а не  хард-кодед\nперечень, как было раньше\nВ попап легенды добавлены расшифровки для иконок действия\nВ попапе планеты показывается её диаметр\nВ попапе луны миссия \"Уничтожить\" показывается только если на  текущей  планете\nигрока есть ЗС\nИз попапа игрока убраны ссылки - все, что можно было сделать по ссылкам,  можно\nтеперь\nДобавлено новая иконка действия - \"Статистика\". Её тултип показывает статистику\nигрока\nРасширена подсказка\n\nПереработка\n-----------\nПолностью переделана работа с полем обломков\nПолностью переписан алгоритм запуска переработчиков\nВ попапе вместе с абсолютными теперь показываются и  относительные  значения  в\nпроцентах\nВ попапе добавилось три строки:\n  1. Строка \"В полете\" показывает емкость трюмов  переработчиков  пользователя,\n     которые уже летят на данное поле\n  2. Строка \"На орбите\" показывает емкость  переработчиков  на  орбите  текущей\n     планеты или луны\n  3. Строка \"К переработке\" показывает сумму двух предыдущих строк\nНа основном экране Вселенной к иконке обломков добавлена индикация  процентного\nзначения из строки \"В полете\". Она имеет цветовое кодирование:\n  1. Зеленый цвет означает, что прибывающие флоты игрока полностью переработают\n     поле обломков на ресурсы\n  2.  Желтый  цвет  означает,   что   к   полю   летит   некоторое   количество\n      переработчиков, которых не хватит что бы целиком переработать обломки, но\n      на текущей планете  есть  достаточно  переработчиков,  что  бы  полностью\n      обработать поле\n  3. Оранжевый означает, что к полю летит флот иргока с переработчиками, но  их\n     не хватит на полную обработку  обломков,  даже  включая  те  корабли,  что\n     находятся на орбите\n  4. Красный цвет значит, что к полю обломков не летит ни одного  переработчика\n     игрока\n\nИменование систем и галактик\n----------------------------\nГалактики и системы  могут  иметь  собственные  названия.  Увидеть  и  изменить\nтекущее имя галактики или системы можно на странице \"Вселенная\"\n\nПо умолчанию  галактики  и  системы  не  имеют  собственных  названий.  Назвать\nгалактику или систему можно  по  ссылке  \"Переименовать\"  соответственно  возле\nкоординат галактики или системы. Выбранное  имя  галактики/системы  видны  всем\nигрокам\n\nИменование галактики или системы имеет соотвествующую стоимость - по  умолчанию\n10.000 ТМ для галактики и 1.000  ТМ  для  системы  -  т.н.  \"базовую  стоимость\nименования\". Изменить базовую стоимость именования можно в настройках  сервера.\nИгроки могут видеть текущую базовую стоимость именования на  странице  \"Мировые\nконстанты\"\n\nПри именовании галактики или системы игрок  может  назначить  цену  именования.\nСтартовая цена именования равна базовой стоимости именования\nПри переименовании уже именованной галактики или системы, игрок должен уплатить\nранее назначенную стоимость именования плюс базовая стоимость именования.\n\nПример\nПусть базовая цена именования системы составляет 1.000 ТМ.\nПервый игрок решил переименовать систему  без  названия.  Он  мог  бы  уплатить\nбазовую стоимость в 1.000 ТМ, однако решил уплатить 2.500 ТМ\nТеперь цена переименования системы для следующего игрока составит\n  <Текущая цена имени> + <Базовая цена> = 2.500 + 1.000 = 3.500 ТМ\nТаким  образом  более  высокая  цена  именования  галактики   или   системы   в\nопределенной степени защищает объект от переименования\n\nВсе действия по переименованию галактик и  систем  записываются  в  лог  -  код\nсобытия  104  -  для  отслеживания  нарушителей  Правил  сервера  о  допустимых\nидентификаторах\n\n\nПереработка поля обломков\n=========================\nПолностью переписан интерфейс переработки  обломков.  Он  учитывает  количество\nпереработчиков, уже  отправленных  на  поле  обломков  и  позволяет  эффективно\nсобирать обломки с нескольких планет\nНа обломках показывается общее количество ресурсов\nНа иконке обломков отображается количество переработчиков пользователя, летящих\nна эти обломки\n\n\nОбзор планеты\n=============\nПолностью переработана страница \"Обзор планеты\".\n1. Список планет  с  картинками  перенесен  из  таблицы  направо.  На  картинки\n   добавлены иконки: активного строительства, активного исследования,  активной\n   верфи, индикатор прибывающего  собственного  флота,  индикатор  прибывающего\n   враждебного флота, статус застройки  планеты.  Так  же  указывается  текущее\n   строительство на каждой планете и таймер обратного отсчета\n   Справа  от  иконки  планеты  добавлены  три  колонки,  показывающие  процент\n   производительности шахт и синтезаторов: серый -  шахта  металла,  голубой  -\n   синтезатор кристаллов,  фиолетовый  -  синтезатор дейтерия.  Высота  колонки\n   пропорциональна проценту производства, а фон кодирует  диапазоны:  желтый  -\n   80-90%, оранжевый - 50-70%%, красный - меньее 50%. На высоту и  фон  колонки\n   влияет ИСКЛЮЧИТЕЛЬНО процент производства, выставленный на странице \"Ресурсы\"\n2. Полностью переработан график движения  флотов.  Теперь  отдельно  выделяются\n   такие  события,  как  \"Прибытие\",  \"Возвращение\"  и   \"Окончание   задания\".\n   Отделяются события для текущей выбранной планеты и для остальных\n3. Добавлены три таймера обратного отсчета для  строительства,  исследований  и\n   верфи. Таймер верфи и таймер зданий поддерживают очередь построек.\n4. Статистика игрока и баннеры перенесены на страницу \"Император\"\nПолностью переписано управление планетой\nДобавлен новый тип сортировки планет - по общему количество полей.  Учитываются\nтерраформеры (на планетах) и лунные базы (на лунах)\nИзменена цветовая кодировка полосы застройки: зеленый -  менее  50%  застройки,\nжелтый - не меньше 50% и меньше 80%, оранжевый - не меньше 80% и  меньше  100%,\nкрасный - 100% застройки\nНа полосы застройки добавилось застроенное и максимальное количество  полей  на\nпланете\nЕсли планет больше  5, то список планет форматируется в две колонки\nВид очереди построек верфи и очереди  исследований  теперь  аналогичен  очереди\nпостроек зданий\nТаймер очереди строительства зданий под иконкой планеты теперь переключается на\nследующее здание в очереди при окончании строительства текущего.  Ранее  таймер\nпоказывал  только  прогресс постройки первого здания в очереди\n\nВертикальная очередь построек. Включается в настройках  пользователя  в  секции\n\"Настройки интерфейса\"\n\nДобавлено текущее количество ресурсов на планете, текущий  размер  хранилищ,  а\nтак же - количество ресурсов на прилетающих флотах\n\n\nНастраиваемое количество колонок в списке планет\n------------------------------------------------\nНа странице настройке пользователя можно указать, сколько колонок должно быть в\nсписке планет - пункт \"Количество колонок в списке планет\" в разделе \"Настройки\nинтерфейса\"\n\nМожно выставить количество колонок в 0 и указать максимальное количество  рядов\nв списке - см. соответствующий пункт там же. В этом  случае  движок  рассчитает\nколичество колонок исходя из этого числа.\n\nОбращаю внимание - указывается именно максимальное количество рядов! Т.е.  если\nу игрока 6 планет, а количество рядов  указано  5,  то  количество  необходимых\nколонок для того,  что  бы  число  рядов  не  привысило  5  будет  равно  двум.\nСоответственно, список планет будет сформирован в  виде  двух  колонок  по  три\nряда. Если же колоний будет 12 - список планет будет выглядеть как таблица  три\nколонки по четыре ряда.\n\nДанная особенность связана с построением списка планет - слева направо и сверху\nвниз. Естественно, не составило бы никакого труда сделать вывод  списка  сверху\nвниз, а затем справо налево - это было бы  даже  легче.  Однако  при  выбранном\nспособе сохраняется пользовательская сортировка планет - более \"важные\" колонии\nвсегда будут \"выше\" в списке\n\n\nФаланга\n=======\nПереписан вывод  фаланги  с  использованием  функций  СН.  Теперь  он  выглядит\nаналогично списку событий флота на странице \"Обзор планеты\". Алгоритм работы  -\nпочти оффовский:\n1. Показываются все флоты, летящие от сканируемой планеты или же к ней\n2. Полет A --> B\n   a) скан B => можно увидеть время прибытия флота\n   b) скан A => можно увидеть время возвращения флота (но не его прибытия на B)\n3. Возвращение B --> A\n   a) скан B => не видно ничего\n   b) скан A => виден возвращающийся флот\nПолнота информации о флотах зависит от уровня шпионажа (см. ниже)\nОсобые случаи:\n1. Задание \"Передислокация\" A --> B\n   a) флот виден только на B, но не на A\n   b) после отзыва флот нигде не виден\n2. Флот, летящий с луны, фалангой не виден\n3. Флот, летящий на задание \"Удержание\" фалангой не виден\n4. Фаланга показывает так же ракетные атаки\n\n\nШпионаж в Обзоре планеты и Фаланге\n==================================\nНа количество отображаемой информации о летящих чужих флотах влияет эффективный\nуровень шпионажа (технология+наемник):\n  Меньше 4 - нет информации о летящем флоте\n  4 и выше - видно общее количество кораблей во флоте и везет ли флот ресурсы\n  6 и выше - виден качественный состав флота - т.е. сколько групп  кораблей  во\n  флоте и сколько кораблей в каждой группе\n  8 и выше - видно точное количество ресурсов в трюмах кораблей\n  10 и выше - виден количественный состав флота\n\n\nМежгалактические Врата\n======================\nИнтерфейс Врат вынесен на отдельную страницу и доступен с Обзора планеты  (куда\nвынесен таймер готовности врат)\nИнтерфейс переделан по примеру страницы \"Флоты на орбите\"\n\n\nСтроительство зданий\n====================\nНовый интерфейс строительства зданий  -  более  компактный  и  удобный\nНемного изменен  порядок  зданий  на  странице  постройки.  Теперь  все  здания\nсгруппированы  по  типам:  первая  линейка  -  добывающие,  вторая  линейка   -\nпроизводящие, третья линейка - склады. На луне и/или при не всех доступных  для\nпостройки зданиях порядок пока может меняться\nДополнительно показываются остатки ресурсов с  учетом  прибывающих  на  планету\nфлотов с грузом\nНаписанная с нуля очередь построек\n\n\nВерфь\n=====\nПереверстан интерфейс Верфи и Обороны. Вид очереди  построек  обновлен.  Теперь\nони выглядят так же, как и очередь постройки зданий\nДобавлена возможность удалить последний добавленный элемент из очереди\nКнопка \"Построить\" дублируется возле каждого юнита. Функционал сохранен - по её\nнажатию будут построены все выбранные юниты\n\n\nФлоты\n=====\nПолностью переработана страница флотов. Добавлено цветовое кодирование в списке\nфлотов\nДобавлены  слайдбары  для  выбора  количества  кораблей.  На  первой   странице\nуказываются предварительные параметры флота (грузоподъемность,  скорость,  если\nуказана точка назначения - время полета  и расход дейтерия)\nКоманда на возвращение флота теперь не ведет на промежуточную страницу, а сразу\nобрабатывается подсистемой флотов\nВ списках летящих флотов к количеству кораблей во  флоте  добавляется  в  конце\nзнак \"+\" если флот везет ресурсы\n\nСвоз ресурсов\n-------------\nДобавлена новая возможность - свезти ресурсы с  остальных  планет  на  текущую.\nИгрок выбирает колонии и луны, с которых нужно свезти  ресурсы  на  текущую,  а\nдвижок автоматически рассчитает  нужное  количество  транспортов,  загрузит  их\nресурсами и отправит флоты.\nВ перевозке ресурсов участвуют только транспортные  корабли:  Малый  транспорт,\nБольшой транспорт и Супертранспорт.  Корабли  загружаются  в  порядке  убывания\nемкости трюма\nЕсть возможность выбирать все ресурсы одного типа на всех планетах; все ресурсы\nна одной планете; все ресурсы на всех планетах\nИмеется JS-счетчик, показывающий общее количество свозимых ресурсов как по типу\nресурсов, так и по отдельной планете\nОткрыть страницу своза ресурсов можно из  списка  кораблей  на  орбите,  списка\nлетящих флотов, кликнув на желтой иконке  тележки  на  изображении  планеты  на\nстраницах \"Обзор планеты\" и  \"Империя\",  а  так  же  на  изображении  юнита  на\nстранице строительства. В последнем случае так же будет отображатся  количество\nресурсов, недостающих для создания данного юнита\n\n\nМеню\n====\nРедизайн меню\nИзменен порядок расположения пунктов\nВысота пункта меню увеличена до 16 пикселов\nДобавлены иконки. Размер иконки ограничен 14 пикселами в высоту\n\n\nПоиск\n=====\nПолностью переписан поиск\nДобавлена сортировка по Альянсу, имени игрока, имени планеты\nНастройка сервера \"Скрывать ссылки на  ЛС\"  распространяется  и  на  результаты\nпоиска\n\n\nИмперия\n=======\nНа изображения планет добавлены иконки, как на странице  \"Обзор  планеты\"  (см.\nвыше).\nУбрано многократно дублирующееся перечисление технологий, идентичных  для  всех\nпланет.\nДобавлена дополнительная колонка, в которой суммируются данные по всей Империи.\nВ списке построек отображаются строящиеся/уничтожаемые здания\nВ списке кораблей отображаются строящиеся и прилетающие корабли\nВ списке защитных сооружени отображаются строящаяся защита\nЦифра производства ресурсов теперь кодируется цветом  аналогично  фону  колонок\nпрозиводства ресурсов (см. выше)\nДобавлено цветовое кодирование для производящих структур. Уровень производства,\nвыставленный на странице  \"Ресурсы\",  кодируется  цветом  фона  соответствующей\nячейки: зеленый - 100%, желтый  -  80-90%%,  оранжевый  -  70-50%%,  красный  -\n40-10%%, цвет фона - 0% или структура не является производящей. Пропорционально\nуровню производительности меняется и длина кодированной полоски\nЗначительно оптимизирован HTML-код\nРазмер HTML-кода уменьшен на величину от 30% и  в  отдельных  случаях  до  80%.\nСреднему игроку оптимизация  даст  уменьшение  размера  загружаемого  файла  на\n40-50%% (включает так же выигрышь от оптимизации Списка планет - см.)\nВ колонку \"ИТОГО\" добавлена  сумма  по  строящимся  и  прибывающим  на  планеты\nюнитам\nТеперь юниты всегда  групируются  согласно  их  принадлежности.  Например,  при\nподключении модуля расовых юнитов они добавляются в категорию \"Флот\"\n\n\nИмператор\n=========\nНовая страница. Содержит небольшой блок новостей, блок статистики игрока и блок\nбаннеров/юзербаров\n\n\nМировые константы\n=================\nНовая страница. Показывает текущие настройки Вселенной пользователям\nДобавлен вывод информации о разешении прокачки и разрешении удержания на слабом\nсоаловце\nДобавлена информация о текущих настройках антибашинга\n\n\nСписок забаненных\n=================\nПолностью переписан список забаненных. Список теперь сортируется по возрастанию\nдаты бана - последние забаненные появляются  в  начале  списка.\nДобавлено отображение разбанов.\n\n\nПрочее\n~~~~~~\nСтраницы статистики, списка банов, списка контаков Вселенной,  списка  настроек\nдоступны для просмотра незалогиненным, находящимся в отпуске и забаненных\n\n\nАдминистрирование\n~~~~~~~~~~~~~~~~~\n\n[#] Модуль: Редактирование характеристик планеты\n================================================\nМодуль admin_planet_edit_extra не входит в основной  состав  дистрибутива.  Для\nполучения модуля обращайтесь к автору движка\nВ админке  добавляется  возможность  менять  основные  характеристики  планеты:\nназвание,  изображение,  размер,  температуру,  губернатора  и   его   уровень,\nколичество обломков на орбите\n\n\nПрава доступа к администраторской части\n=======================================\nПрава доступа к отдельным страницам админки устанавливаются  в  зависимости  от\nуровня игрока  (столбец  authlevel  в  таблице  users).  Выделяют  три  уровня\nдоступа:\n1. Модератор  (authlevel=1)  имеет  доступ  к  следующим  страницам:  overview,\nadm_planet_list, banned, changelog, statbuilder,  tools,  md5enc\nОн может: видеть  список  игроков  онлайн  и  их  активность,  видет  список  и\nактивность лун  и  планет,  вручную  обновлять  статистику  игроков,  банить  и\nразбанивать пользователей\n2. Оператор (authlevel=2) дополнительно имеет  доступ  к  следующим  страницам:\nplanet_edit, add_research, del_research, showfliyingfleets\nДополнительно к функциям модератора он может: редактировать юниты и ресурсы  на\nлунах и планетах; добавлять и  убирать  технологии  игрока;  добавлять  луны  к\nпланетам; видеть и редактировать флоты в полете\n3.  Администратор  (authlevel=3)  имеет  доступ  ко  всем  страницам,   включая\ndelete_user,   admin_darkmatter,   errors,    maintenance,    maintenance_ajax,\nmessagelist, messall,  admin_chat,  paneladmina,  planet_compensate,  settings,\nuserlist\nДополнительно к функциям оператора он может: добавлять и убирать ТМ у  игроков;\nвидеть  полный  список  игроков  с  IP-адресами;  удалять  игроков;   запускать\nпроцедуру  обслуживания  БД;  просматривать   и   удалять   личные   сообщения;\nпросматривать и удалять  сообщения  чата;  просматривать  и  удалять  сообщения\nсистемы логов; изменять права пользователей; изменять настройки игры; возмещать\nигроку стоимость затрат на планету\n\n\nВоплощение\n==========\nТеперь можно Воплотиться  в  любого  игрока,  посмотреть  игру  его  глазами  и\nпоуправлять игрой его ру... эээ...  интерфейсом!  Эта  возможность  чрезвычайна\nполезна для диагностики  редких  неисправностей  и  для  отслеживания  действий\nигроков, подзреваемых в читерстве или нарушении Правил серве\nВоплощение доступно только Администраторам сервера\nВоплотиться можно только в игрока меньшего уровня - т.е. нельзя  Воплотиться  в\nтакого же Администратора\nВложенные Воплощения недопустимы: нельзя Воплотиться, будучи уже Воплощенным  в\nкого-то. Сначала Развоплотитесь\nДля Воплощения используйте соответствующую иконку в \"Списке игроков\"\nПри Воплощении изменяется только onlinetime а. Вся  остальная  информация  (IP,\nUser-agent итд) сохраняется\nДля Развоплощения используйте соответствующий пункт меню или \"Выход\"\nЕсли на  аккаунте  игрока  есть  ошибки,  или  игрок  заблокировае,  или  игрок\nнаходится  в  отпуске,  то  попытке  Воплощения  будет  выведено  сообщение  об\nошибке/блокировке/отпуске, которое увидел бы игрок на вашем  месте.  Обновление\nстраницы вернет вас в ваш аккаунт\nПосле штатного Развоплощения (т.е. из меню, а не при ошибке и не  из  игрока  в\nотпуске) Администратора возвращает на страницу списка игроков\nВНИМАНИЕ! Перед использованием Воплощения  почистите  куки  в  браузере!  Из-за\nизменений в работе кукесов кэш браузера может содержать дубликаты куков\n\n\nСписок игроков\n==============\nПолностью переписан \"Список игроков\" с использованием PTE\nСокращено количество строк локализации\nПишется полный срок бана\nДля мультиаккаунтов подсвечиваются все адреса  с  одинаковым  IP  и  в  скобках\nдобавляется количество игроков с таким же адресом\nТеперь невозможно удалить игрока того же уровня - для  предотвращения  разборок\nмежду членами команды одного уровня\n\n\nGoogle AdSense\n==============\nВнизу левого меню добавлен блок для  вставки  рекламы  от  Гугля.  В  принципе,\nничего не мешает вставить туда рекламу от любого  другого  рекламного  сайта  -\nсоответствующий HTML-код задается в настройках Вселенной\n\n\nЛокализация\n===========\nСуперНова использует кодировку UTF-8 при работе с БД и  рендере  HTML-страниц.\nТаким  образом  поддерживаются  любые  наборы  символов.  Некоторые  непрвильно\nнастроенные сервера могут выставлять некорректную кодировку в HTTP-заголовке  -\nдля них в движке добавлен патч, устраняющий проблему\nСН рахрабатывается  на  русском  языке.  В  27  релизе  добавлена  англоязычная\nлокализация и включена возможность смены языков пользователем\nДобавлена полноценная информация о локализации  (файл  language.mo  в  каталоге\nлокализации). Существенно переработаны файлы локализаций: убраны неиспользуемые\nфайлы, а мелкие файлы, содержащие меньше десятка строк слиты с соответствующими\nфайлами. Все файлы локализации пропущены через редактор и  приведены  к  одному\nвиду. Добавлены заголовки в файлы локализации\n\nСистема отката языка\n--------------------\nДобавлена система отката языков в случае, если требуемый  файл  локализации  не\nнайден. Для встроенных файлов предусмотрена двухуровневая система проверок.\nПервый уровень - проверка по языку. Она происходит в следующем порядке:\n1. Язык из адреса страницы - параметр lang\n2. Язык пользователя\n3. Язык сервера по умолчанию\n4. Русский\n5. Английский\nДля  каждого  языка  происходит проверка на наличие локализации в двух местах:\n1. Ищется файл \"language/<код_языка>/<имя_домена>.mo.php\"\n2. Ищется файл \"language/<имя_домена>_<код_языка>.mo.php\"\nДля модулей добавляется еще один уровень проверки - поиск файлов из предыдущего\nпункта происходит сначала  относительно  каталога  \"/modules/<имя_модуля>/\",  и\nтолько если там файл не найден - происходит поиск относительно корня движка\nЕсли не найден ни один из указанных файлов, то происходит откат на более низкий\nуровень - ищется файлы языка, стоящие ниже по списку\n\nРедактор локализаций\n--------------------\nВ админке добавлен редактор локализаций - пункт меню  \"Локализация\"  в  разделе\n\"Утилиты\"\nВыбор  пункта  меню   \"Локализация\"   открывает   выбор   т.н.   \"домена\"   для\nредактирования. Домен -  это  совокупность  строк  локализации,  относящихся  к\nотдельному аспекту игры. Домен эквивалентен языковому файлу  с  соответствующим\nименем\nПосле выбора домена и подтверждения выбора открывается страница  редактирования\nстрок локализации. Открытие больших файлов может занимать существенное время  -\nпоэтому запаситесь терпением\nВ редакторе есть возможность добавлять и удалять строки  локализации.  Редактор\nлокализаций корректно работает с константами внутри доменов\nПосле редактирования строк локализации и подтверждения редактор  создаст  файлы\n\"<имя домена>.mo.new\" в каждой папке языка\nФайлы \".mo.new\" имеют для редактора  приоритет  перед  обычными  \".mo\"  файлами\nлокализации. Т.е. если в одном языковом каталоге присутствуют оба типа  файлов,\nредактор загрузит для редактирования \".mo.new\"\nДля того, что бы движок подгрузил новый файл  локализации,  требуется  изменить\nего расширение с .mo.new на .mo.  Обычно  это  перезапишет  файл  текущий  файл\nлокализации - поэтому следует заранее сделать его резервную копию\nВНИМАНИЕ! Следует соблюдать осторожность при замене  старых  файлов  на  новые!\nРедактор не сохраняет комментарии и  игнорирует  дополнительный  код  в  файлах\nлокализации! В результате простая перезапись файлов может  нарушить  нормальную\nработу подсистемы локализации движка!  Если  ваши  файлы  локализации  содержат\nдополнительный PHP-код, то они требуют ручного вмешательства после обработке  в\nредакторе! Ни один из встроенных файлов локализации SN не содержит лишнего кода\nи может быть безопасно обработано встроенным редактором\n\n\nУничтожение планет с компенсацией\n=================================\nДобавлена возможность  уничтожения  планет  игроков  с  компенсацией  стоимости\nзданий, строений и ресурсов\n\n\nРедактирование планет и лун\n===========================\nС нуля создан интерфейс редактирования юнитов/ресурсов на планете - пункт  меню\n\"Редактировать\" в разделе \"Планета\".  Он  доступен  членам  команды  начиная  с\nОператора (authlevel=2) и выше\n\n\nПрочее\n======\nВ админку на страницу утилит добавлен вывод информации о настройках и\nпараметрах MySQL сервера\n\nПолностью переписана утилита шифрования пароля в MD5\n\n\nПерсонализация\n~~~~~~~~~~~~~~\nДвижок СР имеет множество опций, используя которые можно настроить внешний  вид\nсервера и его функционал,  не  прибегая  к  изменению  исходного  кода.  КРАЙНЕ\nрекомендуется  использовать  встроенные  возможности  СН  по  настройке  -  для\nоблегчения дальнейших обновлений движка!\n\n\nМодули\n======\nМодуль - это некий часть функционала движка, вынесенного в  отдельный  каталог.\nСН обладает развитой поддержкой системы модулей: модули могут иметь собственные\nфайлы локализации, собственные темплейты, изменять главное меню  игры  и,  само\nсобой, содержать собственный исполнимый PHP-код\nК сожалению, в настоящее время система модулей все еще развивается и  интерфейс\nмодулей меняется от версии к версии. Актуальные примеры модулей можно найти  на\nфоруме движка. Так же некоторые детали реализации модулей можно  найти  ниже  в\nразделе \"Модульная система\"\n\n\nСерверные значения по умолчанию\n===============================\nТеперь администраторы сторонних серверов могут задавать скин, темплейт  и  язык\nпо умолчанию. Это сделано для поддержки авторских разработок при обновлении  из\nрепозитория\n\nАдминистраторы так же могут конфигурировать основные ссылки - ссылки  на  ЧаВо,\nна правила, на форум и на страницу  покупки  ТМ.  В  случае  отсутствии  ссылок\nсоответствующие пункты и ссылки скрываются\n\n\nСкины\n=====\nДобавлена  возможность  перекрыть  дефолтные  стили  элементов  jQueryUI  (файл\n/design/css/jquery.css) стилями, специфическими для скина. Для этого в корневой\nкаталог скина нужно положить файл jquerу.css  с  настройками  стилей  элемента.\nСгенерировать  файл  под  свою  тему  можно  на   сайте   jQuery   по   ссылке:\nhttp://jqueryui.com/themeroller/\n\nВ скины добавлена иконка пола в подкаталог \"images\" скина. Файлы для мужского и\nженского пола  называются  соответственно  \"sex_male.png\"  и  \"sex_female.png\".\nВстроенные скины обновлены автоматически\n\n\nВнутренние изменения\n~~~~~~~~~~~~~~~~~~~~\nБольшие числа\n=============\nИсправлена работа с большими числами. Все  числовые  значение  в  HTTP-запросах\nтрактуются как числа с плавающей запятой.  Все  идентификаторы  передаются  как\nстроки. Все идентификаторы в БД являются  BIGINT(20).  Соответствующим  образом\nпереконфигурированы (добавлены или изменены) FOREIGHN  KEYS.  Переработаны  все\nтаблицы,  что  бы  исключить   переполнение   при   любом   разумном   сценарии\nиспользования движка (скажем, вплоть до скоростей x1000000)\n\n\nСтатистика\n==========\nПолностью переписан модуль обновления статистики.  Максимум  вычислений  теперь\nпроводятся на стороне MySQL сервера без необходимости перекачивать данные в PHP\nи обратно. Обновление статистики завернуто в транзакции. Все эти меры привели к\nтому, что скорость обновления статистики на сервере с 500  аккаунтами  (P4  3,0\nGHz, 2 Gb RAM) составляет меньше секунды!\nДобавлена возможность автоматического  обновления  статистики  (настройка  пока\nтолько  через  БД).  Автоматическое  обновление  статистики   производится   по\nрасписанию через AJAX\n\n\nБаннерилка\n==========\nПолностью переписан код баннерилки. Добавлена возможность включить РО при бане.\nПри разбане режим РО остается, но  разрешается  из  него  выйти.  По  умолчанию\nвключена галочка РО и выставлен срок бана в 3 дня. Бан и  разбан  объединены  в\nодин пункт меню и на одну страницу интерфейса\n\n\nМинификатор темплейтов\n======================\nМинификатор уменьшает  размер  генерируемого  движком  HTML-кода  путем  замены\nнескольких идущих подряд пустых символов (перевод  строки,  табуляция,  пробел)\nодним символом пробела.\n\nМинификатор умеет сжимать HTML и встроенный  JS-код.  Для  JS-кода  он  так  же\nудаляет однострочные комментарии.\n\nМинификатор работает на уровне  темплейтов  и  если  включено  кэширование,  то\nминификатор вызывается только один раз при компиляции кода и дальше  кэшируется\nуменьшенный скомпилированный темплейт, что исключает необходимость в  повторном\nвызове минификатора. Этим он выгодно отличается от минификаторов, работающих на\nуровне сессии через ob_hanler()\n\nВ среднем по сайту минификатор дает выигрыш порядка  7-8%%  при  незначительном\nпадении производительности.\n\nПо умолчанию минификатор отключен. Включить его можно в  админке  в  настройках\nсервера - пункт \"Минификатор темплейтов\"\n\n\nОчереди\n-------\nПолностью переписана очередь построек\nИсправлена ошибка, позволяющая при наличии выше в  очереди  на  верфи  медленно\nстроящегося юнита использовать его время постройки для создания более  быстрого\nюнита  путем  добавления   последнего   в   очередь.   Например,   эта   ошибка\nиспользовалась для быстрой постройки множества ракет\nУнифицированы алгоритмы и файлы постройки флота и защиты. Это должно  полностью\nснять проблемы с отрицательными ресурсами после верфи  и  с  постройкой  лишних\nединиц флота/защиты на верфях\n\nМенеджер флотов в полете\n------------------------\nПолностью  переписан  менеджер  летящих  флотов  -  теперь   рядно-блокирующий,\nтразакционный и кэширующий! Обновленный код на порядок  уменьшает  нагрузку  на\nсервер за счет встроенной системе кэширования запросов b перехода  c  табличной\nблокировки на рядную.\nСистема \"событий\" гарантирует корректный порядок обработки флотов (с  точностью\nдо секунды - предела текущей  организации  таблиц).  Целостность  и  валидность\nрезультатов обеспечена добавлением  транзакций.  Всё  это  позволило  уменьшить\nдискретизацию обработки  флотов  до  4х  секунд  на  серверах  с  300+  онлайна\n(предыдущий менеджер при онлайне порядка 200 человек и дискретизацией 12 секунд\nпримерно через два часа работы \"выбивал\" MySQL сервер). При этом среднее  время\nожидания блокировки не превышает 250 мс.\n\n\nЛокальное (клиентское) и серверное время\n========================================\nИзменена процедура замера разницы между локальным и серверным временем.  Теперь\nона производится  не  каждый  раз  при  обращении  к  серверу,  а  один  раз  и\nсохраняется в БД\nПри заметном изменении разницы можно заново произвести эту операцию,  установив\nгалочку \"Замерить разницу между локальным (клиентским) и серверным временем\" на\nстранице настроек пользователя и сохранив настройки. Замер будет произведен при\nследующем открытии любой страницы игры\nТеперь вместо локального или серверного  времени  одновременно  показывается  и\nлокальное, и серверное время в следующих местах:\n  1. В навбаре - часы реального времени\n  2. При отправке флота на экране выбора точки назначения - в графе времени\n  прибытия и возвращения флота\n  3. При отправке флота на экране подтверждения отправки - в графе  времени\n  прибытия и возвращения флота\nТеперь вместо серверного времени показывается локальное в следующих местах:\n  1. В событиях навбара (флоты и экспедиции)\n  2. В новостях\n  3. На экране флотов в полете\n  4. На экране обзора планеты в списке летящих флотов\n  5. В чате и истории чата\n  6. В боевых отчетах\n  7. В сообщениях\nПереформатирован навбар для добавления локального и серверного времени\nПовышена устойчивость механизма к  ошибкам  на  стороне  клиента:  неправильный\nчасовой  пояс,  неправильные  настройки  DST  в  операционной  системе,  сильно\nотстающие/спешащие часы итд\n\n\n\nСистема темплейтов\n==================\nДобавлен движок рендеринга темплейтов от phpBB3. Движок доработан  до  обратной\nсовместимости с темплейтами XNova.\nВ темплейтах можно указывать префикс \"L_\" что бы взять  переменную  из  массива\n$lang и не  нужно  целиком  передавать  в  массив  $lang  в  процедуру  разбора\nтемплейтов. Так же можно  использовать  локализованные  значения  в  одномерных\nмассивах. Например, $lang['tech'][401] в темплейте превратится в {L_tech[401]}.\nПоддерживаются так же строковые индексы массивов.\nВ темплейтах можно указывать префикс \"C_\",  что  бы  использовать  значение  из\nконфигурации Вселенной. Не работает в условных операторах.\nВ темплейтах  можно  указывать  префикс  \"D_\",  что  бы  использовать  значения\nPHP-констант\n\n\nСистема отпусков\n================\nПереписана с нуля система отпусков.  Теперь  работает  опция  \"Отключить  режим\nотпуска\". На пользовательскую страницу настроек добавлен  таймер,  показывающий\nминимальный срок отпуска, если игрок уйдет в отпуск сейчас\n\nJS библиотека таймеров\n----------------------\nНаписана  с  нуля  универсальная  система  внутриигровых  таймеров  на  стороне\nклиента. Корректно обрабатываются нажатие кнопки Back в браузере (через  AJAX).\nКорреткно обрабатывается  рассинхронизация  часов  между  клиентом  и  сервером\n(включая часовые пояса)\n[@] sn_timer: Таймер корректно работает с очередью, в которой количество юнитов\n    больше 1\n\n/.local\n-------\nДобавлен каталог '/.local' для облегчения разработки.  Файлы  в  этом  каталоге\nигнорируются GIT-ом,  но  при  этом  корректно  подключают  внешние  файлы  для\nобработки и выполнения\n\nБаннер и юзербар\n----------------\nПолностью  переписана  система  генерации   баннеров/юзербаров.   Унифицированы\nпроцедуры их генерации,  слиты  в  один  файл  процедур  и  один  файл-враппер,\nвынесены PHP-файлы из каталога scripts в соответствующие места.\n\nАвтоматическое обновление\n-------------------------\nНаписан с нуля модуль автоматического обновления структуры БД\nАпдейтер использует собственные процедуры запросов к БД\nДобавлена возможность форсировать обновление в случае проблем с  автоматическим\nобновлением. Можно форсировать как все  обновления,  так  и  только  последнее.\nВозможность доступна в интерфейсе Администратора, пункт меню \"Утилиты\"\nАвтоапдейтер на время работы отключает отладку вне  зависимости  от  глобальных\nнастроек\n\nОбслуживание\n------------\nДобавлена автоматическая процедура  ежедневного  обслуживания:  чистка  таблицы\nбашинга, чистка таблицы САБ\n\nЛоги ошибок и событий\n---------------------\nПолностью переработана система внутренних логов сообытий. События сохраняются с\nдвумя  компонентами  -  пригодной  для  чтения  человеком  напрямую  из  БД   и\nmachine-readable, предназначенной для разбора встроенным  парсером.  Во  второй\nкомпоненте  сохраняется  backtrace,  переменные   окружения   ($_GET,   $_POST,\n$_SERVER,  $_COOKIE),  логи  запросов,  а  так  же  любые  другие  по   желанию\nразработчика. Встроенный парсер позволяет просматривать  подробности  о  каждой\nошибке на отдельной странице.\n\nПредусмотрено два уровня событий - \"Информационное сообщение\" и  \"Ошибка\".  Так\nже система событий имеет развитую систему кодов ошибок\n\nИгрокам показывается ID ошибки в БД, а не  её  порядковый  номер.  Это  заметно\nускоряет поиск нужной записи об ошибке\n\nИзменения Тёмной Материи вынесены из глобального лога в отдельную таблицу.  Это\nсущественно облегчило поиск неисправностей на сервере и подозрительных действий\nпользователей. Старые записи перенесены в отдельную таблицу\n\n\nxCache\n------\nДвижок  теперь   поддерживает   xCache.   Использование   xCache   настоятельно\nрекомендуется - это заметно разгрузит сервер и уменьшит количество обращений  к\nБД. Хотя движок может работать и без кэшера, в дальнейшем СН все  больше  будет\nоптимизироваться для использование opcode-cacher\n\n\nSypex Dumper\n------------\nВ дистрибутив добавлена копия Sypex Dumper v2.0.8 (freeware)  для  создания\nрезервных копий (и, естественно, их восстановления)\n\n\nПрочее\n------\nЗакрыты все обнаруженные дыры в  безопасности  (SQL-injection  через  параметры\nстраницы) на страницах, доступных пользователям.  Так  же  полностью  исключена\nвозможность начисления ТМ посредством взлома извне и изменение  прав  отдельных\nигроков\n\nОфицеры отвязаны от конкретных цифр и привязаны к константам-идентификаторам\n\nУдалено множество лишних файлов\n\nТаблицы 'lunas' и 'galaxy' больше  не  используются.  Вся  их  функциональность\nперенесена в таблицу 'planets'\n\nШаблоны даты и времени везде заменены константами.\n\nВсе PHP-deprecated функции заменены более современными аналогами\nУстаревшая функция SYS_mysqlSmartEscape заменена на соответствующие\nУстаревшие массивы $pricelist, $resources,  $reslist,  $sn_groups,  $CombatCaps\nзаменены в коде на $sn_data\n\nВведена дополнительная защита от взлома. Теперь  член  команды  игры  не  может\nназначить кому-либо уровень доступа, равный или больший своего.  Таким  образом\nчерез админку невозможно назначить второго  Администратора.  Однако  это  можно\nпроделать напрямую в БД\n\n\nПрочее\n======\n[@] ВНИМАНИЕ!!! Удален скин 'xnova'\n    Из дистрибутива игры удален скин 'xnova' из-за занимаемого им размера\n    Скачать скин можно с основного сайта проекта по ссылке\n    http://supernova.ws/files/skins/supernova-skin-xnova.zip\n    либо с SourceForge по ссылке\n    http://sourceforge.net/projects/supernova-ws/files/skins/\n[@] Среда разработки изменена на WAMP Server 2.2. Конфигурация:\n    MySQL 5.1.41\n    Apache 2.2\n    PHP 5.2.9-2 + xCache 1.3.2\n[@] JS: Обновлен jQuery до версии 1.7.1. Обновлен jQuery-UI до версии 1.8.17\n[@] $sn_data['groups']['prod'] => $sn_data['groups']['factories']\n[@] SYS: Устаревшие функции заменены актуальными аналогами:\n      int_buildCounter => tpl_parse_planet\n      GetTargetDistance, GetMissionDuration, GetFleetConsumption =>\n        flt_travel_data\n      GetShipConsumption, get_ship_speed => get_ship_data\n      GetFleetMaxSpeed => flt_fleet_speed\n[@] SYS: В описании структуры кораблей (vars.php) данные о двигателях  вынесены\n    в  отдельный  массив  'engine'.  Теперь  можно   указывать   неограниченное\n    количество двигателей для апгрейда корабля\n[@] Новости:  На  странице  новостей,  странице  Императора  и  обзоре  планеты\n    рендерятся одной процедурой и используют один  темплейт.  Индикатор  свежих\n    новостей теперь  ориентируется  на  дату  просмотра,  а  не  на  количество\n    новостей\n[@] ТМ: Изменение ТМ в переменной $user производится в теле rpg_points_change\n[@] Админка/Настройки:  Состояние  все  чекбоксов  (включен/выключен)   теперь\n    определются в темлейте\n[@] Все таймеры врат заменены на sn_timer\n[@] Вселенная: При обнаружении  планеты  с  отсутствующим  пользователем  в  БД\n    планета удаляется с отсрочкой 24 часа\n[@] Темплейты\n    Расширение файлов темплейтов изменено с \".tpl\" на \".tpl.html\" для  большего\n    удобства разработки\n    Теперь при использовании директивы <!--  INCLUDE  -->  НЕ  НУЖНО  указывать\n    расширение подключаемого файла\n    Рендерер страницы теперь подхватывает заголовок страницы, если  он  есть  -\n    переменная PTL {PAGE_HEADER}\n    Содержимое переменной $template_result автоматически загружаетя в  темплейт\n    в файле index.php\n    Файл темплейта _result_message автоматически  подгружается  при  рендеринге\n    темплейта, если в структуре переменных темплейта есть массив 'result'\n[@] Файлы: Удалены неиспользуемые файлы faq.php, faq1.php, faq2.php\n[@] Файлы: Сильно переработана организация файлов PHP\n    Многие процедуры поменяли свое местоположение\n    Множество файлов теперь не грузятся  автоматически  при  старте  движка,  а\n    грузятся лишь по потребности. В частности - все файлы  миссий  подгружаются\n    только в менеджере летящих флотов,  а  сам  менеджер  грузится  только  при\n    потребности в обработке флотов.  Кроме  того,  боевой  движок  подгружается\n    только в симуляторе и при обсчете боев (Миссии \"Атака\" и \"Уничтожить\")\n    Все это позволило заметно сократить размеры кода в памяти сервера\n[@] Модули: Загрузчики модулей теперь располагаются в каталоге /modules, а не в\n    /modules/_functions\n[@] Документация: README преобразован в UTF8\n[@] Добавлена компенсация  работы  механизма  Magic  Quotes.  Подробнее  -  см.\n    \"/docs/install.txt\", подраздел \"Magic Quotes\"\n[@] Юниты\n    Добавлена дополнительный аттрибут \"max\" ко всем юнитам и его общая проверка\n    в eco_get_build_data()\n[@] Меню\n    Меню теперь является динамическим\n    Добавлена возможность добавления иконки к пункту меню.  Иконки  берутся  из\n    подкаталога 'icons' текущего скина\n    Добавлена прямая поддержка CSS-стилей для элементов меню\n    Под логотип сервера в ALT вместо 'supernova.ws' подкладывается имя сервера\n[@] Модули\n    Автоматическая загрузка и регистрация модулей\n    Автоматическое перекрытие функций методами модуля из $manifest\n    Автоматическое подгрузка специфических пунктов меню\n    Автоматическая загрузка конфигурации модуля из файла\n    Теперь можно перекрывать функции методами из класса\n    Добавлена поддержка  \"цепи  перекрытий\".  Можно  протаскивать  сквозь  цепь\n    результат вычислений, модифицируя его на каждом шагу (см. пример реализации\n    перекрытия mrc_get_level)\n    Теперь в манифесте модуля можно задавать  список  констант,  которые  будут\n    автоматически назначены при его инициализации\n    Теперь  в  манифесте  модуля  можно  задавать  список  переменных,  которые\n    автоматически заменят (в случае обычных переменных) или дополнят (в  случае\n    одноуровневых массивов) соответствующие глобальные переменные.  Специальный\n    механизм гарантирует корректную работу с константами в таких  переменных  и\n    массивах - даже  тех,  которые  были  только  назначены  при  инициализации\n    модуля\n    Теперь  при  инициализации  модуля  в  цепочку  вызовов  функций  корректно\n    инсталлируется оригинальная основная функция из движкам\n    Система модулей переписана с учетом базовой поддержки MVC\n    Автоматическая загрузка языков\n    Изменена процедура инициализации  -  модули  теперь  грузятся  до  проверки\n    наличия  страниц.  Это  сделано  на  случай,  если  модуль  добавляет  свои\n    собственные страницы как, например, модуль Премиума и модуль Рас\n    Теперь можно указывать  в  качестве  страницы  загрузки  файла  локализации\n    пустое множество '' - файлы в этом массиве будут загружаться всегда\n    Поддержка  дерева  зависимости  модулей  -  теперь  можно  делать   модули,\n    зависящие от других модулей\n    Автоматическая загрузка зависимых модулей в правильном порядке\n    Устранена ошибка на случай, если модуль не возвращает темплейта. Такого  не\n    должно происходить в нормальной ситуации\n[@] Подсказки: Можно задавать ширину  подсказки  для  согласования  с  основной\n    страницей\n[@] Константы типов юнитов приведены к единому формату \"UNIT_xxx\"\n[@] Всем юнитам прописаны типы\n[@] События флотов\n    Переработана система событий флотов\n[@] Файлы\n    Расширение файлов локализации изменено с  \".mo\"  на  \".mo.php\"  для  лучшей\n    поддержки в различных IDE\n[@] Локализация\n    В дополнение к стандартным путям  \"language/<ISO2>/<domain>.mo.php\"  теперь\n    так же  поддерживаются  пути  вида  \"language/<domain>_<ISO2>.mo.php\".  Это\n    сделано для упрощения структуры подкаталогов в модуле\n[@] Очереди\n    Упразднена  константа  MAX_BUILDING_QUEUE_SIZE.   Теперь   размер   очереди\n    построек зданий и верфи/обороны задается переменными из таблицы `config`  -\n    соотвественно 'server_que_length_structures' и  'server_que_length_hangar'.\n    По умолчанию их значения равны 5\n[@] Классы\n    Новый метод  'assign_recursive'  класса  \"template\"  -  позволяет  в  одном\n    операторе заполнить  как  переменные  темплейта,  так  и  блоки  -  включая\n    вложенные\n[@] Скины\n    Изменена организация CSS-файлов в скинах. Файл \"formate.css\" переименован в\n    \"skin.css\". К нему присоединен в конце файл  \"default.css\".  Таким  образом\n    сохранена последовательность загрузки стилей и при  этом  все  стили  скина\n    находятся теперь в одном файле\n    Теперь движок подгружает файл \"/design/css/global_server.css\" .  Этот  файл\n    может использоваться для добавления специфичных глобальных стилей сервера -\n    он не входит в дистрибутив и не будет перезаписан  при  обновлении  движка.\n    Файл  грузится  после  \"global.css\"  и,  следовательно,  может  перекрывать\n    глобальные стили \"по умолчанию\". Однако он грузится  до  скинового  CSS  и,\n    следовательно, будет перекрыт стилями скина\n    Изменена система раскраски меню. Теперь каждому пункту  меню  присваиваются\n    присваиваются собственные аттрибуты HTML ID и CLASS.  КРАЙНЕ  рекомендуется\n    производить раскраску меню через аттрибут  ID  (см.  пример  в  formate.css\n    скина EpicBlue). Список ID элементов меню  можно  узнать  либо  в  браузере\n    (используя  функцию  \"Inspect  Element\"  или  аналогичную),  либо  в  файле\n    \"/includes/template.php\", функция tpl_render_menu(), переменная $sn_menu\n    В базовый CSS перенесено цветовое кодирование чисел  и  сообщений  (ошибка,\n    предупреждение итд). При желании они  могут  быть  перекрыти  в  CSS-файлах\n    стилей\n    Выделение Администрации и  премиумных  аккаунтов  проводится  через  стили.\n    Соответственно, в основной  скин  добавлены  стили  классов  \".nick_admin\",\n    \".nick_operator\", \".nick_moderator\" и \".nick_premium\"\n    Добавлены  классы  \".same_alliance\"  и  \".same_player\"  для   выделения   в\n    статистике соответственно Альянса игрока и самого игрока\n    supernova-ivash: Скин приведен в соответствие с текущим положением дел\n[@] MVC\n    Базовая поддержка MVC - встроенная система моделей и видов\n    Все страницы, переделанные под MVC, перемещены в /includes/pages\n    Частичная поддержка структуры MVCv2 в init.php\n    Добавлена поддержка анонимных MVC-страниц в common.php\n    Добавлена поддержка MVC-страниц на страницы логина/регистрации\n[@] Реклама\n    Добавлена возможность управлять мета-тегами 'description' и 'keywords'  без\n    редактирования темплейта! Их содержимое хранится в таблице `config` в полях\n    `adv_seo_meta_description` и `adv_seo_meta_keywords` соответственно\n[@] HTTPS\n    Теперь СН нормально работает и по HTTPS протоколу\n[@] Рендерер имен\n    Добавлен механизм рендеринга имени пользователя\n    Чат, статистика, Вселенная и страница Императора  теперь  используют  общий\n    механизм рендеринга имени пользователя\n[@] Код\n    Из файла vars.php выделены три  отдельных  файла  со  структурами,  боевыми\n    юнитами и всеми остальными\n    Так же добавлена дополнительная служебная информация для  того,  что  бы  в\n    симуляторе не пропадали защитные сооружения  при  добавлении  новых  юнитов\n    через модули\n    Убраны неиспользуемые данные \"скорострела\"\n    Из информации о боевых юнитов убраны ненужные данные о единичных усилениях\n    Численные значения для  защитных  сооружений  и  ракет  заменены  везде  на\n    константы\n    Везде  из  текста  убраны  ссылки  на  переменную  $GLOBALS  для  поддержки\n    рефакторинга в IDE\n    Библиотека \"tw-sack.js\" больше не используется - она заменена на jQuery\n    Исправлена очепятка в названии константы технологии ионного двигателя\n    Убран неиспользуемый код \"скорострела\"\n    js_safe_string() теперь корректно работает со строками,  где  есть  перевод\n    строки, включая Линуксовские и Маковские форматы файлов\n    sn_function_call теперь корректно отрабатывает несуществующие функции\n    Добавлена функция sn_get_groups()\n    eco_bld_tech.php теперь не использует $sn_data\n    Оптимизирован код Альянсов\n    Все страницы интерфейса игроков переписаны без использования $parse\n    Теперь в doquery() префикс {{table}} не используется и не обрабатывается\n    Страницы login.php, phalanx.php переписаны без использования $parse\n    JS: В объявлениях скриптов все конструкции  language=\"javascript\"  заменены\n    на type=\"text/javascript\"\n    Теперь  движок  может   работать   с   неограниченным   количеством   типов\n    кораблей-переработчиков\n    Переформатирован HTML-код обзора планеты\n    Поддержка опций движка в глобальном объекте $supernova\n    Поддержка опций на страницах в подмассиве 'pages'\n[@] Меню\n    Заменены типы элементов меню на \"lang\" там, где это было возможно\n    Стандартное меню вынесено из файла template.php в includes/vars_menu.php\n    Парсер меню теперь понимает вложенные конструкции и константы для типа меню\n    'lang' - т.е. конструкции вида 'info[STRUC_MINE_METAL][description]'\n[@] БД\n    В таблицу флотов добавлены идентификаторы планет старта/назначения\n[@] Код\n    Расчеты уровня премиума вынесены в модуль\n    Изменены некоторые SQL-запросы\n    Добавлен простенький бенчмарк\n    infos.php  теперь  использует  прямое  обращение  к  production   юнита   и\n    подмассиву modifiers\n    eco_bld_structures.php теперь использует обращение к подмассиву modifiers\n    mercenaries и plans перенесены из таблицы powerup в таблицу unit\n    Константа MAX_OVERFLOW исключена из кода\n    Обработан eco_get_planet_caps и связанные процедуры\n    Добавлена функция вычисления случайного числа, распределенного нормально\n[@] Код/БД\n    Артефакты перенесены из таблицы игроков в таблицу юнитов\n    Удалены лишние поля Технологий из таблицы игрока\n    Добавлены констраинты в некоторые таблицы\n    Удалена колонка `que` из таблицы `users`\n    premium перенесен из таблицы powerup в таблицу unit\n    Исследования и очередь исследований перенесены в соответствующие таблицы\n[@] Код/JS\n    Переписаны некоторые процедуры fleet.js на использование jQuery\n[@] MVC\n    $sn_i18n['pages'] -> $sn_mvc['i18n']\n[@] Обслуживание\n    Процедура обслуживания теперь так же удаляет боевые отчеты  UBE  старше  60\n    дней\n[@] Модули\n    Изменен алгоритм слияния массивов переменных в модулях\n\n\nБаза данных\n===========\nДобавлен патч для mysql-серверов со включенным STRICT_TRANS_TABLES\n\n\n\nМодульная система\n~~~~~~~~~~~~~~~~~\n\nДинамическое перекрытие функций\n===============================\nЛюбая стандартная функция СН может быть перекрыта пользователем с  минимальными\nизменениями в основном коде  движка.  Данный  функционал  обеспечивает  базовые\nвозможности по созданию модулей. Ниже изложен алгоритм работы перекрытия\n\nПри старте движка он грузит все PHP файлы из каталога /modules. В  этих  файлах\nдолжны  содержатся  альтернативные  функции  и  указания  движку  на  включения\nперекрытия.  См.  файл  /modules_example.php  для  примера  перекрытия  функции\ndisplay() с последующим вызовом стандартной функции\n\nВНИМАНИЕ! Движок всегда  грузит  все  файлы  из  каталога  перекрытия  функций,\nпоэтому что бы не увеличивать размер процесса PHP в памяти  следует  держать  в\nкаталоге только используемые модули\n\nФункции-оболочки\n----------------\nНе все функции в настоящий момент поддерживают  перекрытие  (см.  список  таких\nфункций в следующем разделе), однако простой модификацией кода можно  перекрыть\nлюбую функцию без потери обратной совместимости. Рассмотрим пример  модификации\nкода  для  перекрытия  функции  расчета  максимальное   количество   полей   на\nпланете/луне     eco_planet_fields_max().     Она     находится     в     файле\n/includes/general.php\n\nДля начала нужно переименовать стандартную функцию. Открываем указанный файл\n  /includes/general.php\nищем строку\n  function eco_planet_fields_max($planet)\nи заменяем её на строку\n  function sn_eco_planet_fields_max($planet)\nПрефикс \"sn_\" нужен для нормальной работы системы перекрытия в случае, если  не\nбудет найдена новая функция\n\nТеперь добавляем в тот же файл функцию-оболочку:\n  function eco_planet_fields_max($planet)\n  {\n    $func_args = func_get_args(); // Для совместимости с PHP <= 5.2.x\n    return sn_function_call('eco_planet_fields_max', $func_args);\n  }\nВ кавычках указывается имя перекрываемой  функции.  Параметры  функции-оболочки\nдолжны быть идентичны параметрам  оригинальной  функции.  Теперь  движок  может\nкорректно работать с этой  функцией,  даже  если  отсутствует  пользовательская\nфункция\n\nДопустим, мы хотим, что бы каждая  планета  или  луна  имела  на  500  полей\nбольше, чем позволяет её размер. Создаем файл модуля в каталоге\n  /modules\nИмя файла может быть любым, но для облегчения дальнейших модификаций желательно\nназывать  модуль  именем  подменяемой  функции.  Поэтому  создаем  в  указанном\nкаталоге файл\n  eco_planet_fields_max_my.php\nи копируем в него следующее содержимое\n  <?php\n  $GLOBALS['functions']['eco_planet_fields_max'] = 'eco_planet_fields_max_my';\n  function eco_planet_fields_max_my($planet)\n  {\n    return $planet['field_max'] + 500;\n  }\n  ?>\nПояснения по коду. Первая и последняя строка указывают интерпретатору, что файл\nсодержит код PHP. Вторая строка содержит указание движку по перекрытию  функции\neco_planet_fields_max() функцией eco_planet_fields_max_my(). Строки  с  третьей\nпо шестую содержат саму пользовательскую функцию\n\nPROFIT!\n\nВНИМАНИЕ!!! PHP 5.3.1 содержит  баг,  который  делает  невозможной  полноценную\nработу СН начиная с v33a12! Обновите PHP, или сделайте откат  до  более  ранней\nверсии PHP, или используйте предыдущую версию СН.\nОписание бага: https://bugs.php.net/bug.php?id=50394\n\nФункции, поддерживающие перекрытие\n----------------------------------\ndisplay()\nmrc_get_value()\nmrc_modify_value()\n\n\n\nСервер обновлений\n~~~~~~~~~~~~~~~~~\nЗапущен сервер обновлений. В настоящее  время  сервер  обновлений  поддерживает\nследующие функции: проверка версии движка и регистрация  сайта.  Доступ  к  ним\nосуществляется из админки со страницы настроек сервера\n\n\nТехнические детали\n==================\nДвижок сервера общается с сервером обновлений по протоколу HTTP.\nЕсли  установлен  CURL  и  подключен  к  PHP,  то  для  проверки  версии  будет\nиспользован именно он. Убедитесь, что CURL правильно настроен  и  ему  разрешен\nдоступ к внешним ресурсам\nЕсли CURL не  установлен,  будет  осуществлена  попытка  получить  версию через\nfile_get_contents(). Убедитесь, что в  PHP  разрешается  обращаться  к  внешним\nсайтам через соответствующую функцию\n\n\nПроверка версии\n===============\nПри проверке версии передаются только анонимные данные  -  текущая  версия  БД,\nномер  релиза  и  версия  игры.  Результат  проверки  -  рекомендация   сервера\nобновлений о необходимости обновления текущей версии игры.\nРезультат и время последней проверки выводится  в  левом  меню  и  на  странице\nнастроек. Предусмотрено цветовое кодирование результатов  проверки:  зеленый  -\nобновление необязательно, желтый -  желательно  обновить  движок,  оранжевый  -\nкрайне рекомендуется обновление, красный - ошибка проверки версии\n\nЕсть два варианта проверки версии: ручная и автоматическая\nРучная проверка версии выполняется в ручном режиме по нажатию кнопки \"Проверить\nверсию\" на странице настроек.\nАвтоматическая  проверка  версии  (по  умолчанию  -   отключена)   производится\nсамостоятельно движком по расписанию.  Период  автоматической  проверки  версии\nзадается в секундах в таблице config переменной server_updater_check_period. По\nумолчанию период проверки равен 24 часам (раз в сутки).\n\n\nРегистрация сервера\n===================\nРегистрация  сервера  нужна  для  ряда  запросов  к  серверу  обновлений.   При\nрегистрации  передается  минимум  информации,  необходимой  для   идентификации\nсервера:\n1. Полный URL сервера - т.е. HTTP-адрес и подкаталог  сервера.  Это  необходимо\n   для первичной идентификации сервера. Полный путь необходим для того, что  бы\n   различать несколько копий СуперНовы, установленных на одном IP или домене.\n   Пример полного URL: http://myserver.com/myfolder/\n2. Внутреннее название сервера. Используется для подстановки в сообщения.\n\nИдентификационная информация сервера хранится  в  таблице  \"config\"  -  ключ  в\nзаписи \"server_updater_key\", а идентификатор - в записи \"server_updater_id\".\n\nРегистрация - зачем она?\n------------------------\nЗачем  вообще  регистрировать  свой   сервер?   В   будущем   планируется   ряд\nвозможностей, которые буду доступны только зарегистрированным  серверам.  В  их\nчисло входит (отсортированы по запланированным срокам реализации):\n1. Автоматическое получение чейнджлога\n2. Автоматизированное обновление движка\n3. Участие в рейтинге серверов\n4. Багрепорты от администраторов серверов\n5. Чат для администраторов серверов\n6. По запросу - удаленная диагностика сервера\n7. ...и многое, многое другое\n\nРегистрация - почему сейчас?\n----------------------------\nЗачем регистрировать свой сервер прямо сейчас?\n1. Запросы  от  администраторов  зарегестрированных  серверов  имеют    больший\n   приоритет при диагностике проблем и обработке багрепортов.\n2. При регистрации кроме  индивидуального  ключа  серверу  выдается  уникальный\n   идентификационный  номер,  который  будет   использоваться   при   первичной\n   сортировке  серверов.  Чем  раньше  будет  зарегистрирован  сервер  -   тем,\n   например, выше он будет в общем каталоге серверов...\n\n\n\nCopyright\n~~~~~~~~~\nСуперНова copyright (c) 2009 Gorlum для http://supernova.ws\nProject \"SuperNova.WS\" copyright (c) 2009 by Gorlum for http://supernova.ws\n\nLightly based on xNova 0.8b RageRepack v226\n\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\nUpdated: 2013-10-13 22:27 Project \"SuperNova.WS\" Release 37 37c0\n"
  },
  {
    "path": "docs/similar_characters_detect.md",
    "content": "# Сравнение строк на визуальную идентичность в PHP\n\nЕсли нужно проверить, визуально ли две строки идентичны, в PHP нет встроенной функции, которая сразу решает все кейсы. Но есть подходы и библиотеки.\n\n---\n\n## 1️⃣ Использовать стандартную нормализацию + grapheme\n\n```php\nfunction normalizeString(string $s): string {\n    if (class_exists('Normalizer')) {\n        $s = Normalizer::normalize($s, Normalizer::FORM_C);\n    }\n    return $s;\n}\n\nfunction visuallyEqualBase(string $a, string $b): bool {\n    $a = normalizeString($a);\n    $b = normalizeString($b);\n    return grapheme_strlen($a) === grapheme_strlen($b)\n        && grapheme_strtolower($a) === grapheme_strtolower($b);\n}\n```\n\n> Улучшает обычное `===`, но не решает проблему похожих символов в разных скриптах.\n\n---\n\n## 2️⃣ Использовать таблицу Unicode Confusables\n\nUnicode публикует таблицы похожих символов (confusable characters). Их можно использовать для замены всех эквивалентов на базовые символы.\n\n### 📦 Composer-библиотеки\n\n* **voku/portable-utf8**\n* **patchwork/utf8**\n* **symfony/string**\n\nЭти библиотеки умеют:\n\n* нормализовать Unicode (NFC/NFD)\n* преобразовывать похожие символы\n* корректно сравнивать графемы\n\n### Пример с voku/portable-utf8\n\n```php\nuse function voku\\helper\\UTF8;\n\nfunction visuallyIdenticalUtf8(string $a, string $b): bool {\n    $a = UTF8::normalize($a);\n    $b = UTF8::normalize($b);\n\n    $a = UTF8::remove_bom($a);\n    $b = UTF8::remove_bom($b);\n\n    $a = UTF8::to_lower($a);\n    $b = UTF8::to_lower($b);\n\n    return $a === $b;\n}\n```\n\n### Symfony String (если используется Symfony)\n\n```php\nuse Symfony\\Component\\String\\u;\n\nif (u($str1)->folded()->toString() === u($str2)->folded()->toString()) {\n    echo \"Visually same\";\n}\n```\n\n`.folded()` убирает диакритические отличия и делает сравнение дружелюбным.\n\n---\n\n## 🔹 Итог\n\n| Подход             | Unicode нормализация | Похожие символы | Легко использовать |\n| ------------------ | -------------------- | --------------- | ------------------ |\n| Простые replace    | ❌                    | ❌               | ✅                  |\n| intl + grapheme    | ✔️                   | ❌               | ⚠️                 |\n| voku/portable-utf8 | ✔️                   | ✔️              | ✔️                 |\n| symfony/string     | ✔️                   | ✔️              | ✔️                 |\n\n> Рекомендация: для максимальной визуальной идентичности использовать `voku/portable-utf8` или `symfony/string`.\n\n---\n\nЕсли нужно, можно сделать готовую функцию с **Unicode confusables** для максимального визуального сравнения.\n"
  },
  {
    "path": "docs/supernova-data.sql",
    "content": "﻿SET FOREIGN_KEY_CHECKS = 0;\n\n-- ----------------------------\n-- Records of sn_server_patches\n-- ----------------------------\nINSERT INTO `sn_server_patches` VALUES (1, '2018-11-12 07:50:14');\nINSERT INTO `sn_server_patches` VALUES (2, '2018-11-12 07:50:14');\nINSERT INTO `sn_server_patches` VALUES (3, '2018-11-12 07:50:14');\nINSERT INTO `sn_server_patches` VALUES (4, '2018-11-12 07:50:14');\nINSERT INTO `sn_server_patches` VALUES (6, '2020-07-27 13:20:18');\nINSERT INTO `sn_server_patches` VALUES (7, '2020-07-27 13:20:18');\nINSERT INTO `sn_server_patches` VALUES (8, '2020-07-27 13:20:18');\nINSERT INTO `sn_server_patches` VALUES (9, '2020-07-27 13:20:19');\nINSERT INTO `sn_server_patches` VALUES (10, '2020-07-27 13:20:19');\n\n-- ----------------------------\n-- Default server configuration\n-- ----------------------------\nINSERT INTO `sn_config` VALUES ('advGoogleLeftMenuCode', '<script type=\\\"text/javascript\\\"><!--\\r\\ngoogle_ad_client = \\\"pub-1914310741599503\\\";\\r\\n/* oGame */\\r\\ngoogle_ad_slot = \\\"2544836773\\\";\\r\\ngoogle_ad_width = 125;\\r\\ngoogle_ad_height = 125;\\r\\n//-->\\r\\n</script>\\r\\n<script type=\\\"text/javascript\\\"\\r\\nsrc=\\\"http://pagead2.googlesyndication.com/pagead/show_ads.js\\\">\\r\\n</script>\\r\\n');\nINSERT INTO `sn_config` VALUES ('advGoogleLeftMenuIsOn', '1');\nINSERT INTO `sn_config` VALUES ('adv_conversion_code_payment', '');\nINSERT INTO `sn_config` VALUES ('adv_conversion_code_register', '');\nINSERT INTO `sn_config` VALUES ('adv_seo_javascript', '');\nINSERT INTO `sn_config` VALUES ('adv_seo_meta_description', '');\nINSERT INTO `sn_config` VALUES ('adv_seo_meta_keywords', '');\nINSERT INTO `sn_config` VALUES ('ali_bonus_algorithm', '0');\nINSERT INTO `sn_config` VALUES ('ali_bonus_brackets', '10');\nINSERT INTO `sn_config` VALUES ('ali_bonus_brackets_divisor', '50');\nINSERT INTO `sn_config` VALUES ('ali_bonus_divisor', '10000000');\nINSERT INTO `sn_config` VALUES ('ali_bonus_members', '10');\nINSERT INTO `sn_config` VALUES ('allow_buffing', '0');\nINSERT INTO `sn_config` VALUES ('ally_help_weak', '0');\nINSERT INTO `sn_config` VALUES ('avatar_max_height', '128');\nINSERT INTO `sn_config` VALUES ('avatar_max_width', '128');\nINSERT INTO `sn_config` VALUES ('BuildLabWhileRun', '0');\nINSERT INTO `sn_config` VALUES ('chat_highlight_admin', '<span class=\\\"nick_admin\\\">$1</span>');\nINSERT INTO `sn_config` VALUES ('chat_highlight_developer', '<span class=\\\"nick_developer\\\">$1</span>');\nINSERT INTO `sn_config` VALUES ('chat_highlight_moderator', '<font color=green>$1</font>');\nINSERT INTO `sn_config` VALUES ('chat_highlight_operator', '<font color=red>$1</font>');\nINSERT INTO `sn_config` VALUES ('chat_highlight_premium', '<span class=\\\"nick_premium\\\">$1</span>');\nINSERT INTO `sn_config` VALUES ('chat_refresh_rate', '5');\nINSERT INTO `sn_config` VALUES ('chat_timeout', 15 * 60);\nINSERT INTO `sn_config` VALUES ('COOKIE_NAME', 'SuperNova');\nINSERT INTO `sn_config` VALUES ('crystal_basic_income', '20');\nINSERT INTO `sn_config` VALUES ('db_manual_lock_enabled', '0');\nINSERT INTO `sn_config` VALUES ('db_prefix', 'sn_');\nINSERT INTO `sn_config` VALUES ('db_version', '45');\nINSERT INTO `sn_config` VALUES ('debug', '0');\nINSERT INTO `sn_config` VALUES ('Defs_Cdr', '30');\nINSERT INTO `sn_config` VALUES ('deuterium_basic_income', '0');\nINSERT INTO `sn_config` VALUES ('eco_planet_starting_crystal', '500');\nINSERT INTO `sn_config` VALUES ('eco_planet_starting_deuterium', '0');\nINSERT INTO `sn_config` VALUES ('eco_planet_starting_metal', '500');\nINSERT INTO `sn_config` VALUES ('eco_planet_storage_crystal', '500000');\nINSERT INTO `sn_config` VALUES ('eco_planet_storage_deuterium', '500000');\nINSERT INTO `sn_config` VALUES ('eco_planet_storage_metal', '500000');\nINSERT INTO `sn_config` VALUES ('eco_scale_storage', '1');\nINSERT INTO `sn_config` VALUES ('eco_stockman_fleet', '');\nINSERT INTO `sn_config` VALUES ('eco_stockman_fleet_populate', '1');\nINSERT INTO `sn_config` VALUES ('empire_mercenary_base_period', 30 * 24 * 60 * 60);\nINSERT INTO `sn_config` VALUES ('empire_mercenary_temporary', '1');\nINSERT INTO `sn_config` VALUES ('energy_basic_income', '0');\nINSERT INTO `sn_config` VALUES ('fleet_bashing_attacks', 3);\nINSERT INTO `sn_config` VALUES ('fleet_bashing_interval', 30 * 60);\nINSERT INTO `sn_config` VALUES ('fleet_bashing_scope', 24 * 60 * 60);\nINSERT INTO `sn_config` VALUES ('fleet_bashing_war_delay', 12 * 60 * 60);\nINSERT INTO `sn_config` VALUES ('fleet_bashing_waves', 3);\nINSERT INTO `sn_config` VALUES ('Fleet_Cdr', '30');\nINSERT INTO `sn_config` VALUES ('fleet_speed', '1');\nINSERT INTO `sn_config` VALUES ('fleet_update_interval', '4');\nINSERT INTO `sn_config` VALUES ('fleet_update_last', NOW());\nINSERT INTO `sn_config` VALUES ('fleet_update_lock', '');\nINSERT INTO `sn_config` VALUES ('fleet_update_max_run_time', '30');\nINSERT INTO `sn_config` VALUES ('game_adminEmail', 'root@localhost');\nINSERT INTO `sn_config` VALUES ('game_counter', '0');\nINSERT INTO `sn_config` VALUES ('game_default_language', 'ru');\nINSERT INTO `sn_config` VALUES ('game_default_skin', 'skins/EpicBlue/');\nINSERT INTO `sn_config` VALUES ('game_default_template', 'OpenGame');\nINSERT INTO `sn_config` VALUES ('game_disable', '0');\nINSERT INTO `sn_config` VALUES ('game_disable_reason', 'SuperNova is in maintenance mode! Please return later!');\nINSERT INTO `sn_config` VALUES ('game_email_pm', '0');\nINSERT INTO `sn_config` VALUES ('game_installed', '0');\nINSERT INTO `sn_config` VALUES ('game_maxGalaxy', '5');\nINSERT INTO `sn_config` VALUES ('game_maxPlanet', '15');\nINSERT INTO `sn_config` VALUES ('game_maxSystem', '199');\nINSERT INTO `sn_config` VALUES ('game_mode', '0');\nINSERT INTO `sn_config` VALUES ('game_multiaccount_enabled', '0');\nINSERT INTO `sn_config` VALUES ('game_name', 'SuperNova');\nINSERT INTO `sn_config` VALUES ('game_news_actual', '259200');\nINSERT INTO `sn_config` VALUES ('game_news_overview', '3');\nINSERT INTO `sn_config` VALUES ('game_news_overview_show', 2 * 7 * 24 * 60 * 60);\nINSERT INTO `sn_config` VALUES ('game_noob_factor', '5');\nINSERT INTO `sn_config` VALUES ('game_noob_points', '5000');\nINSERT INTO `sn_config` VALUES ('game_speed', '1');\nINSERT INTO `sn_config` VALUES ('game_speed_expedition', '1');\nINSERT INTO `sn_config` VALUES ('game_users_online_timeout', 15 * 60);\nINSERT INTO `sn_config` VALUES ('game_user_changename', '2');\nINSERT INTO `sn_config` VALUES ('game_user_changename_cost', '100000');\nINSERT INTO `sn_config` VALUES ('geoip_whois_url', 'https://who.is/whois-ip/ip-address/');\nINSERT INTO `sn_config` VALUES ('initial_fields', '163');\nINSERT INTO `sn_config` VALUES ('int_banner_background', 'design/images/banner.png');\nINSERT INTO `sn_config` VALUES ('int_banner_fontInfo', 'terminator.ttf');\nINSERT INTO `sn_config` VALUES ('int_banner_fontRaids', 'klmnfp2005.ttf');\nINSERT INTO `sn_config` VALUES ('int_banner_fontUniverse', 'cristal.ttf');\nINSERT INTO `sn_config` VALUES ('int_banner_showInOverview', '1');\nINSERT INTO `sn_config` VALUES ('int_banner_URL', 'banner.php?type=banner');\nINSERT INTO `sn_config` VALUES ('int_format_date', 'd.m.Y');\nINSERT INTO `sn_config` VALUES ('int_format_time', 'H:i:s');\nINSERT INTO `sn_config` VALUES ('int_userbar_background', 'design/images/userbar.png');\nINSERT INTO `sn_config` VALUES ('int_userbar_font', 'arialbd.ttf');\nINSERT INTO `sn_config` VALUES ('int_userbar_showInOverview', '1');\nINSERT INTO `sn_config` VALUES ('int_userbar_URL', 'banner.php?type=userbar');\nINSERT INTO `sn_config` VALUES ('LastSettedGalaxyPos', '1');\nINSERT INTO `sn_config` VALUES ('LastSettedPlanetPos', '1');\nINSERT INTO `sn_config` VALUES ('LastSettedSystemPos', '1');\nINSERT INTO `sn_config` VALUES ('locale_cache_disable', '0');\nINSERT INTO `sn_config` VALUES ('metal_basic_income', '40');\nINSERT INTO `sn_config` VALUES ('payment_currency_default', 'USD');\nINSERT INTO `sn_config` VALUES ('payment_currency_exchange_dm_', '20000');\nINSERT INTO `sn_config` VALUES ('payment_currency_exchange_eur', '0.9');\nINSERT INTO `sn_config` VALUES ('payment_currency_exchange_mm_', '20000');\nINSERT INTO `sn_config` VALUES ('payment_currency_exchange_rub', '60');\nINSERT INTO `sn_config` VALUES ('payment_currency_exchange_uah', '30');\nINSERT INTO `sn_config` VALUES ('payment_currency_exchange_usd', '1');\nINSERT INTO `sn_config` VALUES ('payment_currency_exchange_wmb', '18000');\nINSERT INTO `sn_config` VALUES ('payment_currency_exchange_wme', '0.9');\nINSERT INTO `sn_config` VALUES ('payment_currency_exchange_wmr', '60');\nINSERT INTO `sn_config` VALUES ('payment_currency_exchange_wmu', '30');\nINSERT INTO `sn_config` VALUES ('payment_currency_exchange_wmz', '1');\nINSERT INTO `sn_config` VALUES ('payment_lot_price', '1');\nINSERT INTO `sn_config` VALUES ('payment_lot_size', '2500');\nINSERT INTO `sn_config` VALUES ('planet_capital_cost', '25000');\nINSERT INTO `sn_config` VALUES ('planet_teleport_cost', '50000');\nINSERT INTO `sn_config` VALUES ('planet_teleport_timeout', 1 * 24 * 60 * 60);\nINSERT INTO `sn_config` VALUES ('player_delete_time', 45 * 24 * 60 * 60);\nINSERT INTO `sn_config` VALUES ('player_max_colonies', '9');\nINSERT INTO `sn_config` VALUES ('player_metamatter_immortal', '100000');\nINSERT INTO `sn_config` VALUES ('player_vacation_time', 7 * 24 * 60 * 60);\nINSERT INTO `sn_config` VALUES ('player_vacation_timeout', 7 * 24 * 60 * 60);\nINSERT INTO `sn_config` VALUES ('quest_total', '0');\nINSERT INTO `sn_config` VALUES ('resource_multiplier', '1');\nINSERT INTO `sn_config` VALUES ('rpg_bonus_divisor', '10');\nINSERT INTO `sn_config` VALUES ('rpg_bonus_minimum', '10000');\nINSERT INTO `sn_config` VALUES ('rpg_cost_banker', '1000');\nINSERT INTO `sn_config` VALUES ('rpg_cost_exchange', '1000');\nINSERT INTO `sn_config` VALUES ('rpg_cost_info', '10000');\nINSERT INTO `sn_config` VALUES ('rpg_cost_pawnshop', '1000');\nINSERT INTO `sn_config` VALUES ('rpg_cost_scraper', '1000');\nINSERT INTO `sn_config` VALUES ('rpg_cost_stockman', '1000');\nINSERT INTO `sn_config` VALUES ('rpg_cost_trader', '1000');\nINSERT INTO `sn_config` VALUES ('rpg_exchange_crystal', '2');\nINSERT INTO `sn_config` VALUES ('rpg_exchange_darkMatter', '400');\nINSERT INTO `sn_config` VALUES ('rpg_exchange_deuterium', '4');\nINSERT INTO `sn_config` VALUES ('rpg_exchange_metal', '1');\nINSERT INTO `sn_config` VALUES ('rpg_flt_explore', '1000');\nINSERT INTO `sn_config` VALUES ('rpg_scrape_crystal', '0.50');\nINSERT INTO `sn_config` VALUES ('rpg_scrape_deuterium', '0.25');\nINSERT INTO `sn_config` VALUES ('rpg_scrape_metal', '0.75');\nINSERT INTO `sn_config` VALUES ('secret_word', 'SuperNova');\nINSERT INTO `sn_config` VALUES ('security_ban_extra', '');\nINSERT INTO `sn_config` VALUES ('security_write_full_url_disabled', '1');\nINSERT INTO `sn_config` VALUES ('server_email', 'root@localhost');\nINSERT INTO `sn_config` VALUES ('server_log_online', '0');\nINSERT INTO `sn_config` VALUES ('server_que_length_hangar', '5');\nINSERT INTO `sn_config` VALUES ('server_que_length_research', '1');\nINSERT INTO `sn_config` VALUES ('server_que_length_structures', '5');\nINSERT INTO `sn_config` VALUES ('server_start_date', DATE_FORMAT(CURDATE(), '%d.%m.%Y'));\nINSERT INTO `sn_config` VALUES ('server_updater_check_auto', '0');\nINSERT INTO `sn_config` VALUES ('server_updater_check_last', '0');\nINSERT INTO `sn_config` VALUES ('server_updater_check_period', 24 * 60 * 60);\nINSERT INTO `sn_config` VALUES ('server_updater_check_result', '-1');\nINSERT INTO `sn_config` VALUES ('server_updater_id', '0');\nINSERT INTO `sn_config` VALUES ('server_updater_key', '');\nINSERT INTO `sn_config` VALUES ('stats_hide_admins', '1');\nINSERT INTO `sn_config` VALUES ('stats_hide_player_list', '');\nINSERT INTO `sn_config` VALUES ('stats_hide_pm_link', '0');\nINSERT INTO `sn_config` VALUES ('stats_history_days', '7');\nINSERT INTO `sn_config` VALUES ('stats_minimal_interval', 10 * 60);\nINSERT INTO `sn_config` VALUES ('stats_php_memory', '1024M');\nINSERT INTO `sn_config` VALUES ('stats_schedule', '04:00:00');\nINSERT INTO `sn_config` VALUES ('tpl_minifier', '1');\nINSERT INTO `sn_config` VALUES ('tutorial_first_item', '1');\nINSERT INTO `sn_config` VALUES ('ube_capture_points_diff', '2');\nINSERT INTO `sn_config` VALUES ('uni_galaxy_distance', '20000');\nINSERT INTO `sn_config` VALUES ('uni_price_galaxy', '10000');\nINSERT INTO `sn_config` VALUES ('uni_price_system', '1000');\nINSERT INTO `sn_config` VALUES ('upd_lock_time', '60');\nINSERT INTO `sn_config` VALUES ('url_dark_matter', '');\nINSERT INTO `sn_config` VALUES ('url_faq', 'http://faq.supernova.ws/');\nINSERT INTO `sn_config` VALUES ('url_forum', '');\nINSERT INTO `sn_config` VALUES ('url_purchase_metamatter', '');\nINSERT INTO `sn_config` VALUES ('url_rules', '');\nINSERT INTO `sn_config` VALUES ('users_amount', '1');\nINSERT INTO `sn_config` VALUES ('user_birthday_celebrate', '0');\nINSERT INTO `sn_config` VALUES ('user_birthday_gift', '0');\nINSERT INTO `sn_config` VALUES ('user_birthday_range', '30');\nINSERT INTO `sn_config` VALUES ('user_vacation_disable', '0');\nINSERT INTO `sn_config` VALUES ('var_db_update', '0');\nINSERT INTO `sn_config` VALUES ('var_db_update_end', '0');\nINSERT INTO `sn_config` VALUES ('var_news_last', '0');\nINSERT INTO `sn_config` VALUES ('var_online_user_count', 0);\nINSERT INTO `sn_config` VALUES ('var_online_user_time', 0);\nINSERT INTO `sn_config` VALUES ('var_stat_update', '0');\nINSERT INTO `sn_config` VALUES ('var_stat_update_end', '0');\nINSERT INTO `sn_config` VALUES ('var_stat_update_msg', '');\nINSERT INTO `sn_config` VALUES ('var_stat_update_next', '');\n\n-- ----------------------------\n-- Administrator's account\n-- Login: admin\n-- Password: admin\n-- ----------------------------\nINSERT INTO `sn_account`\nSET\n    `account_id`       = 1,\n    `account_name`     = 'admin',\n    `account_password` = '21232f297a57a5a743894a0e4a801fc3',\n    `account_email`    = 'root@localhost',\n    `account_language` = 'ru';\n\n-- ----------------------------\n-- Administrator's user record\n-- Login: admin\n-- Password: admin\n-- ----------------------------\nINSERT INTO `sn_users`\nSET\n    `id`             = 1,\n    `username`       = 'admin',\n    `password`       = '21232f297a57a5a743894a0e4a801fc3',\n    `email`          = 'root@localhost',\n    `email_2`        = 'root@localhost',\n    `authlevel`      = 3,\n    `id_planet`      = 1,\n    `galaxy`         = 1,\n    `system`         = 1,\n    `planet`         = 1,\n    `current_planet` = 1,\n    `register_time`  = UNIX_TIMESTAMP(NOW()),\n    `onlinetime`     = UNIX_TIMESTAMP(NOW()),\n    `noipcheck`      = 1;\n\n-- ----------------------------\n-- Administrator's account translation to user record\n-- ----------------------------\nREPLACE INTO `sn_account_translate`\nSET\n    `provider_id`         = 1,\n    `provider_account_id` = 1,\n    `user_id`             = 1,\n    `timestamp`           = NOW();\n\n-- ----------------------------\n-- Reserved 'admin' name\n-- ----------------------------\nINSERT INTO `sn_player_name_history`\nSET\n    player_id   = 1,\n    player_name = 'admin';\n\n-- ----------------------------\n-- Administrator's planet\n-- ----------------------------\nINSERT INTO `sn_planets`\nSET\n    `id`          = 1,\n    `name`        = 'Planet',\n    `id_owner`    = 1,\n    `id_level`    = 0,\n    `galaxy`      = 1,\n    `system`      = 1,\n    `planet`      = 1,\n    `planet_type` = 1,\n    `last_update` = UNIX_TIMESTAMP(NOW())\n-- 'normaltempplanet01'\n;\n\nSET FOREIGN_KEY_CHECKS = 1;\n"
  },
  {
    "path": "docs/supernova.sql",
    "content": "/*\n Navicat Premium Data Transfer\n\n Source Server         : localhost\n Source Server Type    : MySQL\n Source Server Version : 50714\n Source Host           : localhost:3306\n Source Schema         : supernova\n\n Target Server Type    : MySQL\n Target Server Version : 50714\n File Encoding         : 65001\n\n Date: 27/07/2020 18:05:32\n*/\n\nSET NAMES utf8mb4;\nSET FOREIGN_KEY_CHECKS = 0;\n\n-- ----------------------------\n-- Table structure for sn_account\n-- ----------------------------\nDROP TABLE IF EXISTS `sn_account`;\nCREATE TABLE `sn_account`\n(\n    `account_id`               bigint(20) UNSIGNED                                       NOT NULL AUTO_INCREMENT,\n    `account_name`             varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci    NOT NULL DEFAULT '',\n    `account_password`         char(32) CHARACTER SET latin1 COLLATE latin1_general_ci   NOT NULL DEFAULT '',\n    `account_salt`             char(16) CHARACTER SET latin1 COLLATE latin1_general_ci   NOT NULL DEFAULT '',\n    `account_email`            varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci    NOT NULL DEFAULT '',\n    `account_email_verified`   tinyint(1) UNSIGNED                                       NOT NULL DEFAULT 0,\n    `account_register_time`    timestamp(0)                                              NOT NULL DEFAULT CURRENT_TIMESTAMP(0),\n    `account_language`         varchar(5) CHARACTER SET latin1 COLLATE latin1_general_ci NOT NULL DEFAULT 'ru',\n    `account_metamatter`       bigint(20)                                                NOT NULL DEFAULT 0 COMMENT 'Metamatter amount',\n    `account_metamatter_total` bigint(20)                                                NOT NULL DEFAULT 0 COMMENT 'Total Metamatter amount ever bought',\n    `account_immortal`         timestamp(0)                                              NULL     DEFAULT NULL,\n    PRIMARY KEY (`account_id`) USING BTREE,\n    UNIQUE INDEX `I_account_name` (`account_name`) USING BTREE,\n    INDEX `I_account_email` (`account_email`) USING BTREE\n) ENGINE = InnoDB\n  AUTO_INCREMENT = 2\n  CHARACTER SET = utf8\n  COLLATE = utf8_general_ci\n  ROW_FORMAT = Dynamic;\n\n-- ----------------------------\n-- Table structure for sn_account_translate\n-- ----------------------------\nDROP TABLE IF EXISTS `sn_account_translate`;\nCREATE TABLE `sn_account_translate`\n(\n    `provider_id`         tinyint(3) UNSIGNED NOT NULL DEFAULT 1 COMMENT 'Account provider',\n    `provider_account_id` bigint(20) UNSIGNED NOT NULL COMMENT 'Account ID on provider',\n    `user_id`             bigint(20) UNSIGNED NOT NULL COMMENT 'User ID',\n    `timestamp`           timestamp(0)        NOT NULL DEFAULT CURRENT_TIMESTAMP(0),\n    PRIMARY KEY (`provider_id`, `provider_account_id`, `user_id`) USING BTREE,\n    INDEX `user_id` (`user_id`) USING BTREE,\n    CONSTRAINT `FK_account_translate_user_id` FOREIGN KEY (`user_id`) REFERENCES `sn_users` (`id`) ON DELETE CASCADE ON UPDATE CASCADE\n) ENGINE = InnoDB\n  CHARACTER SET = utf8\n  COLLATE = utf8_general_ci\n  ROW_FORMAT = Dynamic;\n\n-- ----------------------------\n-- Table structure for sn_ad_promo_codes\n-- ----------------------------\nDROP TABLE IF EXISTS `sn_ad_promo_codes`;\nCREATE TABLE `sn_ad_promo_codes`\n(\n    `id`          int(10) UNSIGNED                                        NOT NULL AUTO_INCREMENT,\n    `code`        varchar(64) CHARACTER SET utf8 COLLATE utf8_unicode_ci  NOT NULL COMMENT 'Promo code itself. Unique',\n    `description` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL DEFAULT '' COMMENT 'Promo code description',\n    `reg_only`    tinyint(1)                                              NOT NULL DEFAULT 1,\n    `from`        datetime(0)                                             NULL     DEFAULT NULL,\n    `to`          datetime(0)                                             NULL     DEFAULT NULL,\n    `max_use`     int(10) UNSIGNED                                        NOT NULL DEFAULT 0 COMMENT 'Max time code can be used. 0 - unlimited',\n    `used_times`  int(10) UNSIGNED                                        NOT NULL DEFAULT 0 COMMENT 'How many time code was used',\n    `adjustments` mediumtext CHARACTER SET utf8 COLLATE utf8_unicode_ci   NOT NULL,\n    PRIMARY KEY (`id`) USING BTREE,\n    UNIQUE INDEX `I_promo_code` (`code`) USING BTREE\n) ENGINE = InnoDB\n  AUTO_INCREMENT = 1\n  CHARACTER SET = utf8\n  COLLATE = utf8_unicode_ci\n  ROW_FORMAT = Dynamic;\n\n-- ----------------------------\n-- Table structure for sn_ad_promo_codes_uses\n-- ----------------------------\nDROP TABLE IF EXISTS `sn_ad_promo_codes_uses`;\nCREATE TABLE `sn_ad_promo_codes_uses`\n(\n    `id`            bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT,\n    `promo_code_id` int(10) UNSIGNED    NOT NULL,\n    `user_id`       bigint(20) UNSIGNED NOT NULL,\n    `use_time`      timestamp(0)        NOT NULL DEFAULT CURRENT_TIMESTAMP(0),\n    PRIMARY KEY (`id`) USING BTREE,\n    INDEX `FK_user_id` (`user_id`) USING BTREE,\n    INDEX `I_promo_code_id` (`promo_code_id`, `user_id`) USING BTREE\n) ENGINE = InnoDB\n  AUTO_INCREMENT = 1\n  CHARACTER SET = utf8\n  COLLATE = utf8_unicode_ci\n  ROW_FORMAT = Dynamic;\n\n-- ----------------------------\n-- Table structure for sn_aks\n-- ----------------------------\nDROP TABLE IF EXISTS `sn_aks`;\nCREATE TABLE `sn_aks`\n(\n    `id`             bigint(20) UNSIGNED                                    NOT NULL AUTO_INCREMENT,\n    `name`           varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NULL     DEFAULT NULL,\n    `teilnehmer`     text CHARACTER SET utf8 COLLATE utf8_general_ci        NULL,\n    `flotten`        text CHARACTER SET utf8 COLLATE utf8_general_ci        NULL,\n    `ankunft`        int(32)                                                NULL     DEFAULT NULL,\n    `galaxy`         int(2)                                                 NULL     DEFAULT NULL,\n    `system`         int(4)                                                 NULL     DEFAULT NULL,\n    `planet`         int(2)                                                 NULL     DEFAULT NULL,\n    `planet_type`    tinyint(1) UNSIGNED                                    NOT NULL DEFAULT 0,\n    `eingeladen`     varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NULL     DEFAULT NULL,\n    `fleet_end_time` int(11)                                                NOT NULL DEFAULT 0,\n    PRIMARY KEY (`id`) USING BTREE\n) ENGINE = InnoDB\n  AUTO_INCREMENT = 1\n  CHARACTER SET = utf8\n  COLLATE = utf8_general_ci\n  ROW_FORMAT = Dynamic;\n\n-- ----------------------------\n-- Table structure for sn_alliance\n-- ----------------------------\nDROP TABLE IF EXISTS `sn_alliance`;\nCREATE TABLE `sn_alliance`\n(\n    `id`                    bigint(20) UNSIGNED                                     NOT NULL AUTO_INCREMENT,\n    `ally_name`             varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci  NULL     DEFAULT '',\n    `ally_tag`              varchar(8) CHARACTER SET utf8 COLLATE utf8_general_ci   NULL     DEFAULT '',\n    `ally_owner`            bigint(20) UNSIGNED                                     NULL     DEFAULT NULL,\n    `ally_register_time`    int(11)                                                 NOT NULL DEFAULT 0,\n    `ally_description`      text CHARACTER SET utf8 COLLATE utf8_general_ci         NULL,\n    `ally_web`              varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL     DEFAULT '',\n    `ally_text`             text CHARACTER SET utf8 COLLATE utf8_general_ci         NULL,\n    `ally_image`            tinyint(1) UNSIGNED                                     NOT NULL DEFAULT 0,\n    `ally_request`          text CHARACTER SET utf8 COLLATE utf8_general_ci         NULL,\n    `ally_request_waiting`  text CHARACTER SET utf8 COLLATE utf8_general_ci         NULL,\n    `ally_request_notallow` tinyint(1) UNSIGNED                                     NOT NULL DEFAULT 0,\n    `ally_owner_range`      varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci  NULL     DEFAULT '',\n    `ally_ranks`            text CHARACTER SET utf8 COLLATE utf8_general_ci         NULL,\n    `ally_members`          int(11)                                                 NOT NULL DEFAULT 0,\n    `ranklist`              text CHARACTER SET utf8 COLLATE utf8_general_ci         NULL,\n    `total_rank`            int(10) UNSIGNED                                        NOT NULL DEFAULT 0,\n    `total_points`          bigint(20) UNSIGNED                                     NOT NULL DEFAULT 0,\n    `ally_user_id`          bigint(20) UNSIGNED                                     NULL     DEFAULT NULL,\n    PRIMARY KEY (`id`) USING BTREE,\n    UNIQUE INDEX `i_ally_name` (`ally_name`) USING BTREE,\n    UNIQUE INDEX `i_ally_tag` (`ally_tag`) USING BTREE,\n    INDEX `I_ally_user_id` (`ally_user_id`) USING BTREE,\n    INDEX `FK_alliance_owner` (`ally_owner`) USING BTREE,\n    CONSTRAINT `FK_alliance_owner` FOREIGN KEY (`ally_owner`) REFERENCES `sn_users` (`id`) ON DELETE SET NULL ON UPDATE CASCADE,\n    CONSTRAINT `FK_ally_ally_user_id` FOREIGN KEY (`ally_user_id`) REFERENCES `sn_users` (`id`) ON DELETE CASCADE ON UPDATE CASCADE\n) ENGINE = InnoDB\n  AUTO_INCREMENT = 1\n  CHARACTER SET = utf8\n  COLLATE = utf8_general_ci\n  ROW_FORMAT = Dynamic;\n\n-- ----------------------------\n-- Table structure for sn_alliance_diplomacy\n-- ----------------------------\nDROP TABLE IF EXISTS `sn_alliance_diplomacy`;\nCREATE TABLE `sn_alliance_diplomacy`\n(\n    `alliance_diplomacy_id`              bigint(20) UNSIGNED                                                                                                            NOT NULL AUTO_INCREMENT,\n    `alliance_diplomacy_ally_id`         bigint(20) UNSIGNED                                                                                                            NULL     DEFAULT NULL,\n    `alliance_diplomacy_contr_ally_id`   bigint(20) UNSIGNED                                                                                                            NULL     DEFAULT NULL,\n    `alliance_diplomacy_contr_ally_name` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci                                                                         NULL     DEFAULT '',\n    `alliance_diplomacy_relation`        set ('neutral','war','peace','confederation','federation','union','master','slave') CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT 'neutral',\n    `alliance_diplomacy_relation_last`   set ('neutral','war','peace','confederation','federation','union','master','slave') CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT 'neutral',\n    `alliance_diplomacy_time`            int(11)                                                                                                                        NOT NULL DEFAULT 0,\n    PRIMARY KEY (`alliance_diplomacy_id`) USING BTREE,\n    UNIQUE INDEX `alliance_diplomacy_id` (`alliance_diplomacy_id`) USING BTREE,\n    INDEX `alliance_diplomacy_ally_id` (`alliance_diplomacy_ally_id`, `alliance_diplomacy_contr_ally_id`,\n                                        `alliance_diplomacy_time`) USING BTREE,\n    INDEX `alliance_diplomacy_ally_id_2` (`alliance_diplomacy_ally_id`, `alliance_diplomacy_time`) USING BTREE,\n    INDEX `FK_diplomacy_contr_ally_id` (`alliance_diplomacy_contr_ally_id`) USING BTREE,\n    INDEX `FK_diplomacy_contr_ally_name` (`alliance_diplomacy_contr_ally_name`) USING BTREE,\n    CONSTRAINT `FK_diplomacy_ally_id` FOREIGN KEY (`alliance_diplomacy_ally_id`) REFERENCES `sn_alliance` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,\n    CONSTRAINT `FK_diplomacy_contr_ally_id` FOREIGN KEY (`alliance_diplomacy_contr_ally_id`) REFERENCES `sn_alliance` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,\n    CONSTRAINT `FK_diplomacy_contr_ally_name` FOREIGN KEY (`alliance_diplomacy_contr_ally_name`) REFERENCES `sn_alliance` (`ally_name`) ON DELETE CASCADE ON UPDATE CASCADE\n) ENGINE = InnoDB\n  AUTO_INCREMENT = 1\n  CHARACTER SET = utf8\n  COLLATE = utf8_general_ci\n  ROW_FORMAT = Dynamic;\n\n-- ----------------------------\n-- Table structure for sn_alliance_negotiation\n-- ----------------------------\nDROP TABLE IF EXISTS `sn_alliance_negotiation`;\nCREATE TABLE `sn_alliance_negotiation`\n(\n    `alliance_negotiation_id`              bigint(20) UNSIGNED                                                                                                            NOT NULL AUTO_INCREMENT,\n    `alliance_negotiation_ally_id`         bigint(20) UNSIGNED                                                                                                            NULL     DEFAULT NULL,\n    `alliance_negotiation_ally_name`       varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci                                                                         NULL     DEFAULT '',\n    `alliance_negotiation_contr_ally_id`   bigint(20) UNSIGNED                                                                                                            NULL     DEFAULT NULL,\n    `alliance_negotiation_contr_ally_name` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci                                                                         NULL     DEFAULT '',\n    `alliance_negotiation_relation`        set ('neutral','war','peace','confederation','federation','union','master','slave') CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT 'neutral',\n    `alliance_negotiation_time`            int(11)                                                                                                                        NOT NULL DEFAULT 0,\n    `alliance_negotiation_propose`         text CHARACTER SET utf8 COLLATE utf8_general_ci                                                                                NULL,\n    `alliance_negotiation_response`        text CHARACTER SET utf8 COLLATE utf8_general_ci                                                                                NULL,\n    `alliance_negotiation_status`          tinyint(1)                                                                                                                     NOT NULL DEFAULT 0,\n    PRIMARY KEY (`alliance_negotiation_id`) USING BTREE,\n    UNIQUE INDEX `alliance_negotiation_id` (`alliance_negotiation_id`) USING BTREE,\n    INDEX `alliance_negotiation_ally_id` (`alliance_negotiation_ally_id`, `alliance_negotiation_contr_ally_id`, `alliance_negotiation_time`) USING BTREE,\n    INDEX `alliance_negotiation_ally_id_2` (`alliance_negotiation_ally_id`, `alliance_negotiation_time`) USING BTREE,\n    INDEX `FK_negotiation_ally_name` (`alliance_negotiation_ally_name`) USING BTREE,\n    INDEX `FK_negotiation_contr_ally_id` (`alliance_negotiation_contr_ally_id`) USING BTREE,\n    INDEX `FK_negotiation_contr_ally_name` (`alliance_negotiation_contr_ally_name`) USING BTREE,\n    CONSTRAINT `FK_negotiation_ally_id` FOREIGN KEY (`alliance_negotiation_ally_id`) REFERENCES `sn_alliance` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,\n    CONSTRAINT `FK_negotiation_ally_name` FOREIGN KEY (`alliance_negotiation_ally_name`) REFERENCES `sn_alliance` (`ally_name`) ON DELETE CASCADE ON UPDATE CASCADE,\n    CONSTRAINT `FK_negotiation_contr_ally_id` FOREIGN KEY (`alliance_negotiation_contr_ally_id`) REFERENCES `sn_alliance` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,\n    CONSTRAINT `FK_negotiation_contr_ally_name` FOREIGN KEY (`alliance_negotiation_contr_ally_name`) REFERENCES `sn_alliance` (`ally_name`) ON DELETE CASCADE ON UPDATE CASCADE\n) ENGINE = InnoDB\n  AUTO_INCREMENT = 1\n  CHARACTER SET = utf8\n  COLLATE = utf8_general_ci\n  ROW_FORMAT = Dynamic;\n\n-- ----------------------------\n-- Table structure for sn_alliance_requests\n-- ----------------------------\nDROP TABLE IF EXISTS `sn_alliance_requests`;\nCREATE TABLE `sn_alliance_requests`\n(\n    `id_user`        bigint(20) UNSIGNED                             NOT NULL DEFAULT 0,\n    `id_ally`        bigint(20) UNSIGNED                             NOT NULL DEFAULT 0,\n    `request_text`   text CHARACTER SET utf8 COLLATE utf8_general_ci NULL,\n    `request_time`   int(11)                                         NOT NULL DEFAULT 0,\n    `request_denied` tinyint(1) UNSIGNED                             NOT NULL DEFAULT 0,\n    PRIMARY KEY (`id_user`, `id_ally`) USING BTREE,\n    INDEX `I_alliance_requests_id_ally` (`id_ally`, `id_user`) USING BTREE,\n    CONSTRAINT `FK_alliance_request_ally_id` FOREIGN KEY (`id_ally`) REFERENCES `sn_alliance` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,\n    CONSTRAINT `FK_alliance_request_user_id` FOREIGN KEY (`id_user`) REFERENCES `sn_users` (`id`) ON DELETE CASCADE ON UPDATE CASCADE\n) ENGINE = InnoDB\n  CHARACTER SET = utf8\n  COLLATE = utf8_general_ci\n  ROW_FORMAT = Dynamic;\n\n-- ----------------------------\n-- Table structure for sn_annonce\n-- ----------------------------\nDROP TABLE IF EXISTS `sn_annonce`;\nCREATE TABLE `sn_annonce`\n(\n    `id`       bigint(20) UNSIGNED                                    NOT NULL AUTO_INCREMENT,\n    `user`     varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NULL     DEFAULT NULL,\n    `galaxie`  int(11)                                                NOT NULL DEFAULT 0,\n    `systeme`  int(11)                                                NOT NULL DEFAULT 0,\n    `metala`   bigint(11)                                             NOT NULL DEFAULT 0,\n    `cristala` bigint(11)                                             NOT NULL DEFAULT 0,\n    `deuta`    bigint(11)                                             NOT NULL DEFAULT 0,\n    `metals`   bigint(11)                                             NOT NULL DEFAULT 0,\n    `cristals` bigint(11)                                             NOT NULL DEFAULT 0,\n    `deuts`    bigint(11)                                             NOT NULL DEFAULT 0,\n    PRIMARY KEY (`id`) USING BTREE,\n    UNIQUE INDEX `id` (`id`) USING BTREE,\n    INDEX `I_annonce_user` (`user`, `id`) USING BTREE,\n    CONSTRAINT `FK_annonce_user` FOREIGN KEY (`user`) REFERENCES `sn_users` (`username`) ON DELETE CASCADE ON UPDATE CASCADE\n) ENGINE = InnoDB\n  AUTO_INCREMENT = 1\n  CHARACTER SET = utf8\n  COLLATE = utf8_general_ci\n  ROW_FORMAT = Dynamic;\n\n-- ----------------------------\n-- Table structure for sn_announce\n-- ----------------------------\nDROP TABLE IF EXISTS `sn_announce`;\nCREATE TABLE `sn_announce`\n(\n    `idAnnounce`  bigint(11) UNSIGNED                                     NOT NULL AUTO_INCREMENT,\n    `tsTimeStamp` timestamp(0)                                            NOT NULL DEFAULT CURRENT_TIMESTAMP(0) COMMENT 'Date & Time of announce',\n    `strAnnounce` text CHARACTER SET utf8 COLLATE utf8_general_ci         NULL,\n    `detail_url`  varchar(250) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '' COMMENT 'Link to more details about update',\n    `user_id`     bigint(20) UNSIGNED                                     NOT NULL DEFAULT 0 COMMENT 'Announcer user ID',\n    `user_name`   varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci  NULL     DEFAULT NULL COMMENT 'Announcer user name',\n    PRIMARY KEY (`idAnnounce`) USING BTREE,\n    INDEX `indTimeStamp` (`tsTimeStamp`) USING BTREE\n) ENGINE = InnoDB\n  AUTO_INCREMENT = 1\n  CHARACTER SET = utf8\n  COLLATE = utf8_general_ci\n  ROW_FORMAT = Dynamic;\n\n-- ----------------------------\n-- Table structure for sn_auth_vkontakte_account\n-- ----------------------------\nDROP TABLE IF EXISTS `sn_auth_vkontakte_account`;\nCREATE TABLE `sn_auth_vkontakte_account`\n(\n    `user_id`      bigint(20) UNSIGNED                                     NOT NULL AUTO_INCREMENT,\n    `access_token` varchar(250) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '',\n    `expires_in`   timestamp(0)                                            NOT NULL DEFAULT CURRENT_TIMESTAMP(0),\n    `email`        varchar(250) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '',\n    `first_name`   varchar(250) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '',\n    `last_name`    varchar(250) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '',\n    `account_id`   bigint(20) UNSIGNED                                     NULL     DEFAULT NULL COMMENT 'Account ID',\n    PRIMARY KEY (`user_id`) USING BTREE,\n    INDEX `FK_vkontakte_account_id` (`account_id`) USING BTREE,\n    CONSTRAINT `FK_vkontakte_account_id` FOREIGN KEY (`account_id`) REFERENCES `sn_account` (`account_id`) ON DELETE CASCADE ON UPDATE CASCADE\n) ENGINE = InnoDB\n  AUTO_INCREMENT = 1\n  CHARACTER SET = utf8\n  COLLATE = utf8_general_ci\n  ROW_FORMAT = Dynamic;\n\n-- ----------------------------\n-- Table structure for sn_banned\n-- ----------------------------\nDROP TABLE IF EXISTS `sn_banned`;\nCREATE TABLE `sn_banned`\n(\n    `ban_id`           bigint(20) UNSIGNED                                     NOT NULL AUTO_INCREMENT,\n    `ban_user_id`      bigint(20) UNSIGNED                                     NULL     DEFAULT NULL COMMENT 'Banned user ID',\n    `ban_user_name`    varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci  NOT NULL DEFAULT '',\n    `ban_reason`       varchar(128) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '',\n    `ban_time`         int(11)                                                 NOT NULL DEFAULT 0,\n    `ban_until`        int(11)                                                 NOT NULL DEFAULT 0,\n    `ban_issuer_id`    bigint(20) UNSIGNED                                     NULL     DEFAULT NULL COMMENT 'Banner ID',\n    `ban_issuer_name`  varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci  NOT NULL DEFAULT '',\n    `ban_issuer_email` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci  NOT NULL DEFAULT '',\n    PRIMARY KEY (`ban_id`) USING BTREE,\n    INDEX `ID` (`ban_id`) USING BTREE,\n    INDEX `I_ban_user_id` (`ban_user_id`) USING BTREE,\n    INDEX `I_ban_issuer_id` (`ban_issuer_id`) USING BTREE,\n    CONSTRAINT `FK_ban_issuer_id` FOREIGN KEY (`ban_issuer_id`) REFERENCES `sn_users` (`id`) ON DELETE SET NULL ON UPDATE CASCADE,\n    CONSTRAINT `FK_ban_user_id` FOREIGN KEY (`ban_user_id`) REFERENCES `sn_users` (`id`) ON DELETE SET NULL ON UPDATE CASCADE\n) ENGINE = InnoDB\n  AUTO_INCREMENT = 1\n  CHARACTER SET = utf8\n  COLLATE = utf8_general_ci\n  ROW_FORMAT = Dynamic;\n\n-- ----------------------------\n-- Table structure for sn_bashing\n-- ----------------------------\nDROP TABLE IF EXISTS `sn_bashing`;\nCREATE TABLE `sn_bashing`\n(\n    `bashing_id`        bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT,\n    `bashing_user_id`   bigint(20) UNSIGNED NULL     DEFAULT NULL,\n    `bashing_planet_id` bigint(20) UNSIGNED NULL     DEFAULT NULL,\n    `bashing_time`      int(11)             NOT NULL DEFAULT 0,\n    PRIMARY KEY (`bashing_id`) USING BTREE,\n    UNIQUE INDEX `bashing_id` (`bashing_id`) USING BTREE,\n    INDEX `bashing_user_id` (`bashing_user_id`, `bashing_planet_id`, `bashing_time`) USING BTREE,\n    INDEX `bashing_planet_id` (`bashing_planet_id`) USING BTREE,\n    INDEX `bashing_time` (`bashing_time`) USING BTREE,\n    CONSTRAINT `FK_bashing_planet_id` FOREIGN KEY (`bashing_planet_id`) REFERENCES `sn_planets` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,\n    CONSTRAINT `FK_bashing_user_id` FOREIGN KEY (`bashing_user_id`) REFERENCES `sn_users` (`id`) ON DELETE CASCADE ON UPDATE CASCADE\n) ENGINE = InnoDB\n  AUTO_INCREMENT = 1\n  CHARACTER SET = utf8\n  COLLATE = utf8_general_ci\n  ROW_FORMAT = Dynamic;\n\n-- ----------------------------\n-- Table structure for sn_blitz_registrations\n-- ----------------------------\nDROP TABLE IF EXISTS `sn_blitz_registrations`;\nCREATE TABLE `sn_blitz_registrations`\n(\n    `id`                       bigint(20) UNSIGNED                                    NOT NULL AUTO_INCREMENT,\n    `server_id`                smallint(5) UNSIGNED                                   NULL     DEFAULT 0,\n    `round_number`             smallint(5) UNSIGNED                                   NULL     DEFAULT 0,\n    `user_id`                  bigint(20) UNSIGNED                                    NULL     DEFAULT NULL,\n    `timestamp`                timestamp(0)                                           NULL     DEFAULT CURRENT_TIMESTAMP(0) ON UPDATE CURRENT_TIMESTAMP(0),\n    `blitz_name`               varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '',\n    `blitz_password`           varchar(8) CHARACTER SET utf8 COLLATE utf8_unicode_ci  NOT NULL DEFAULT '',\n    `blitz_player_id`          bigint(20) UNSIGNED                                    NOT NULL DEFAULT 0,\n    `blitz_status`             tinyint(3) UNSIGNED                                    NOT NULL DEFAULT 0,\n    `blitz_place`              tinyint(3) UNSIGNED                                    NOT NULL DEFAULT 0,\n    `blitz_points`             decimal(65, 0) UNSIGNED                                NOT NULL DEFAULT 0,\n    `blitz_online`             int(10) UNSIGNED                                       NOT NULL DEFAULT 0,\n    `blitz_reward_dark_matter` bigint(20) UNSIGNED                                    NOT NULL DEFAULT 0,\n    PRIMARY KEY (`id`) USING BTREE,\n    UNIQUE INDEX `I_blitz_server_round_user` (`server_id`, `round_number`, `user_id`) USING BTREE,\n    INDEX `I_blitz_user_id` (`user_id`) USING BTREE,\n    CONSTRAINT `FK_user_id` FOREIGN KEY (`user_id`) REFERENCES `sn_users` (`id`) ON DELETE CASCADE ON UPDATE CASCADE\n) ENGINE = InnoDB\n  AUTO_INCREMENT = 1\n  CHARACTER SET = utf8\n  COLLATE = utf8_unicode_ci\n  ROW_FORMAT = Dynamic;\n\n-- ----------------------------\n-- Table structure for sn_blitz_statpoints\n-- ----------------------------\nDROP TABLE IF EXISTS `sn_blitz_statpoints`;\nCREATE TABLE `sn_blitz_statpoints`\n(\n    `stat_date`      int(11)                 NOT NULL DEFAULT 0,\n    `id_owner`       bigint(20) UNSIGNED     NULL     DEFAULT NULL,\n    `id_ally`        bigint(20) UNSIGNED     NULL     DEFAULT NULL,\n    `stat_type`      tinyint(3) UNSIGNED     NULL     DEFAULT 0,\n    `stat_code`      tinyint(3) UNSIGNED     NOT NULL DEFAULT 0,\n    `tech_rank`      int(11) UNSIGNED        NOT NULL DEFAULT 0,\n    `tech_old_rank`  int(11) UNSIGNED        NOT NULL DEFAULT 0,\n    `tech_points`    decimal(65, 0) UNSIGNED NOT NULL DEFAULT 0,\n    `tech_count`     decimal(65, 0) UNSIGNED NOT NULL DEFAULT 0,\n    `build_rank`     int(11) UNSIGNED        NOT NULL DEFAULT 0,\n    `build_old_rank` int(11) UNSIGNED        NOT NULL DEFAULT 0,\n    `build_points`   decimal(65, 0) UNSIGNED NOT NULL DEFAULT 0,\n    `build_count`    decimal(65, 0) UNSIGNED NOT NULL DEFAULT 0,\n    `defs_rank`      int(11) UNSIGNED        NOT NULL DEFAULT 0,\n    `defs_old_rank`  int(11) UNSIGNED        NOT NULL DEFAULT 0,\n    `defs_points`    decimal(65, 0) UNSIGNED NOT NULL DEFAULT 0,\n    `defs_count`     decimal(65, 0) UNSIGNED NOT NULL DEFAULT 0,\n    `fleet_rank`     int(11) UNSIGNED        NOT NULL DEFAULT 0,\n    `fleet_old_rank` int(11) UNSIGNED        NOT NULL DEFAULT 0,\n    `fleet_points`   decimal(65, 0) UNSIGNED NOT NULL DEFAULT 0,\n    `fleet_count`    decimal(65, 0) UNSIGNED NOT NULL DEFAULT 0,\n    `res_rank`       int(11) UNSIGNED        NULL     DEFAULT 0 COMMENT 'Rank by resources',\n    `res_old_rank`   int(11) UNSIGNED        NULL     DEFAULT 0 COMMENT 'Old rank by resources',\n    `res_points`     decimal(65, 0) UNSIGNED NULL     DEFAULT 0 COMMENT 'Resource stat points',\n    `res_count`      decimal(65, 0) UNSIGNED NULL     DEFAULT 0 COMMENT 'Resource count',\n    `total_rank`     int(11) UNSIGNED        NOT NULL DEFAULT 0,\n    `total_old_rank` int(11) UNSIGNED        NOT NULL DEFAULT 0,\n    `total_points`   decimal(65, 0) UNSIGNED NOT NULL DEFAULT 0,\n    `total_count`    decimal(65, 0) UNSIGNED NOT NULL DEFAULT 0,\n    `server_id`      smallint(5) UNSIGNED    NULL     DEFAULT 0,\n    `round_number`   smallint(5) UNSIGNED    NULL     DEFAULT 0\n) ENGINE = InnoDB\n  CHARACTER SET = utf8\n  COLLATE = utf8_unicode_ci\n  ROW_FORMAT = Dynamic;\n\n-- ----------------------------\n-- Table structure for sn_buddy\n-- ----------------------------\nDROP TABLE IF EXISTS `sn_buddy`;\nCREATE TABLE `sn_buddy`\n(\n    `BUDDY_ID`        bigint(20) UNSIGNED                                 NOT NULL AUTO_INCREMENT COMMENT 'Buddy table row ID',\n    `BUDDY_SENDER_ID` bigint(20) UNSIGNED                                 NULL     DEFAULT NULL COMMENT 'Buddy request sender ID',\n    `BUDDY_OWNER_ID`  bigint(20) UNSIGNED                                 NULL     DEFAULT NULL COMMENT 'Buddy request recipient ID',\n    `BUDDY_STATUS`    tinyint(1) UNSIGNED                                 NOT NULL DEFAULT 0 COMMENT 'Buddy request status',\n    `BUDDY_REQUEST`   tinytext CHARACTER SET utf8 COLLATE utf8_general_ci NULL COMMENT 'Buddy request text',\n    PRIMARY KEY (`BUDDY_ID`) USING BTREE,\n    UNIQUE INDEX `BUDDY_ID` (`BUDDY_ID`) USING BTREE,\n    INDEX `I_BUDDY_SENDER_ID` (`BUDDY_SENDER_ID`, `BUDDY_OWNER_ID`) USING BTREE,\n    INDEX `I_BUDDY_OWNER_ID` (`BUDDY_OWNER_ID`, `BUDDY_SENDER_ID`) USING BTREE,\n    CONSTRAINT `FK_BUDDY_OWNER_ID` FOREIGN KEY (`BUDDY_OWNER_ID`) REFERENCES `sn_users` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,\n    CONSTRAINT `FK_BUDDY_SENDER_ID` FOREIGN KEY (`BUDDY_SENDER_ID`) REFERENCES `sn_users` (`id`) ON DELETE CASCADE ON UPDATE CASCADE\n) ENGINE = InnoDB\n  AUTO_INCREMENT = 1\n  CHARACTER SET = utf8\n  COLLATE = utf8_general_ci\n  ROW_FORMAT = Dynamic;\n\n-- ----------------------------\n-- Table structure for sn_captain\n-- ----------------------------\nDROP TABLE IF EXISTS `sn_captain`;\nCREATE TABLE `sn_captain`\n(\n    `captain_id`      bigint(20) UNSIGNED     NOT NULL AUTO_INCREMENT COMMENT 'Record ID',\n    `captain_unit_id` bigint(20) UNSIGNED     NULL     DEFAULT NULL COMMENT 'Link to `unit` record',\n    `captain_xp`      decimal(65, 0) UNSIGNED NOT NULL DEFAULT 0 COMMENT 'Captain expirience',\n    `captain_level`   bigint(20) UNSIGNED     NOT NULL DEFAULT 0 COMMENT 'Captain level so far',\n    `captain_shield`  bigint(20) UNSIGNED     NOT NULL DEFAULT 0 COMMENT 'Captain shield bonus level',\n    `captain_armor`   bigint(20) UNSIGNED     NOT NULL DEFAULT 0 COMMENT 'Captain armor bonus level',\n    `captain_attack`  bigint(20) UNSIGNED     NOT NULL DEFAULT 0 COMMENT 'Captain defense bonus level',\n    PRIMARY KEY (`captain_id`) USING BTREE,\n    UNIQUE INDEX `captain_id` (`captain_id`) USING BTREE,\n    INDEX `I_captain_unit_id` (`captain_unit_id`) USING BTREE,\n    CONSTRAINT `FK_captain_unit_id` FOREIGN KEY (`captain_unit_id`) REFERENCES `sn_unit` (`unit_id`) ON DELETE CASCADE ON UPDATE CASCADE\n) ENGINE = InnoDB\n  AUTO_INCREMENT = 1\n  CHARACTER SET = utf8\n  COLLATE = utf8_general_ci\n  ROW_FORMAT = Dynamic;\n\n-- ----------------------------\n-- Table structure for sn_chat\n-- ----------------------------\nDROP TABLE IF EXISTS `sn_chat`;\nCREATE TABLE `sn_chat`\n(\n    `messageid`                   bigint(20) UNSIGNED                                    NOT NULL AUTO_INCREMENT,\n    `chat_message_sender_id`      bigint(20) UNSIGNED                                    NULL     DEFAULT NULL COMMENT 'Message sender ID',\n    `chat_message_sender_name`    varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NULL     DEFAULT '' COMMENT 'Message sender name',\n    `user`                        text CHARACTER SET utf8 COLLATE utf8_general_ci        NULL COMMENT 'Chat message user name',\n    `chat_message_recipient_id`   bigint(20) UNSIGNED                                    NULL     DEFAULT NULL COMMENT 'Message recipient ID',\n    `chat_message_recipient_name` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NULL     DEFAULT '' COMMENT 'Message sender name',\n    `message`                     text CHARACTER SET utf8 COLLATE utf8_general_ci        NULL,\n    `timestamp`                   int(11)                                                NOT NULL DEFAULT 0,\n    `ally_id`                     int(11)                                                NOT NULL DEFAULT 0,\n    PRIMARY KEY (`messageid`) USING BTREE,\n    UNIQUE INDEX `messageid` (`messageid`) USING BTREE,\n    INDEX `i_ally_idmess` (`ally_id`, `messageid`) USING BTREE,\n    INDEX `I_chat_message_sender_id` (`chat_message_sender_id`) USING BTREE,\n    INDEX `I_chat_message_recipient_id` (`chat_message_recipient_id`) USING BTREE,\n    CONSTRAINT `FK_chat_message_sender_recipient_id` FOREIGN KEY (`chat_message_recipient_id`) REFERENCES `sn_users` (`id`) ON DELETE SET NULL ON UPDATE CASCADE,\n    CONSTRAINT `FK_chat_message_sender_user_id` FOREIGN KEY (`chat_message_sender_id`) REFERENCES `sn_users` (`id`) ON DELETE SET NULL ON UPDATE CASCADE\n) ENGINE = InnoDB\n  AUTO_INCREMENT = 1\n  CHARACTER SET = utf8\n  COLLATE = utf8_general_ci\n  ROW_FORMAT = Dynamic;\n\n-- ----------------------------\n-- Table structure for sn_chat_player\n-- ----------------------------\nDROP TABLE IF EXISTS `sn_chat_player`;\nCREATE TABLE `sn_chat_player`\n(\n    `chat_player_id`           bigint(20) UNSIGNED                                     NOT NULL AUTO_INCREMENT COMMENT 'Record ID',\n    `chat_player_player_id`    bigint(20) UNSIGNED                                     NULL     DEFAULT NULL COMMENT 'Chat player record owner',\n    `chat_player_activity`     timestamp(0)                                            NOT NULL DEFAULT CURRENT_TIMESTAMP(0) ON UPDATE CURRENT_TIMESTAMP(0) COMMENT 'Last player activity in chat',\n    `chat_player_invisible`    tinyint(4)                                              NOT NULL DEFAULT 0 COMMENT 'Player invisibility',\n    `chat_player_muted`        int(11)                                                 NOT NULL DEFAULT 0 COMMENT 'Player is muted',\n    `chat_player_mute_reason`  varchar(256) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '' COMMENT 'Player mute reason',\n    `chat_player_refresh_last` int(11)                                                 NOT NULL DEFAULT 0 COMMENT 'Player last refresh time',\n    PRIMARY KEY (`chat_player_id`) USING BTREE,\n    UNIQUE INDEX `chat_player_id` (`chat_player_id`) USING BTREE,\n    INDEX `I_chat_player_id` (`chat_player_player_id`) USING BTREE,\n    INDEX `I_chat_player_refresh_last` (`chat_player_refresh_last`) USING BTREE,\n    CONSTRAINT `FK_chat_player_id` FOREIGN KEY (`chat_player_player_id`) REFERENCES `sn_users` (`id`) ON DELETE CASCADE ON UPDATE CASCADE\n) ENGINE = InnoDB\n  AUTO_INCREMENT = 1\n  CHARACTER SET = utf8\n  COLLATE = utf8_general_ci\n  ROW_FORMAT = Dynamic;\n\n-- ----------------------------\n-- Table structure for sn_config\n-- ----------------------------\nDROP TABLE IF EXISTS `sn_config`;\nCREATE TABLE `sn_config`\n(\n    `config_name`  varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '',\n    `config_value` text CHARACTER SET utf8 COLLATE utf8_general_ci        NULL,\n    PRIMARY KEY (`config_name`) USING BTREE,\n    INDEX `i_config_name` (`config_name`) USING BTREE\n) ENGINE = InnoDB\n  CHARACTER SET = utf8\n  COLLATE = utf8_general_ci\n  ROW_FORMAT = Dynamic;\n\n-- ----------------------------\n-- Table structure for sn_confirmations\n-- ----------------------------\nDROP TABLE IF EXISTS `sn_confirmations`;\nCREATE TABLE `sn_confirmations`\n(\n    `id`          bigint(11)                                             NOT NULL AUTO_INCREMENT,\n    `id_user`     bigint(11)                                             NOT NULL DEFAULT 0,\n    `type`        smallint(6)                                            NOT NULL DEFAULT 0,\n    `code`        varchar(16) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '',\n    `email`       varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '',\n    `create_time` timestamp(0)                                           NOT NULL DEFAULT CURRENT_TIMESTAMP(0) ON UPDATE CURRENT_TIMESTAMP(0),\n    `provider_id` tinyint(3) UNSIGNED                                    NOT NULL DEFAULT 0,\n    `account_id`  bigint(20) UNSIGNED                                    NOT NULL DEFAULT 0,\n    PRIMARY KEY (`id`) USING BTREE,\n    UNIQUE INDEX `I_confirmations_unique` (`provider_id`, `account_id`, `type`, `email`) USING BTREE,\n    INDEX `i_code_email` (`code`, `email`) USING BTREE\n) ENGINE = InnoDB\n  AUTO_INCREMENT = 1\n  CHARACTER SET = utf8\n  COLLATE = utf8_general_ci\n  ROW_FORMAT = Dynamic;\n\n-- ----------------------------\n-- Table structure for sn_counter\n-- ----------------------------\nDROP TABLE IF EXISTS `sn_counter`;\nCREATE TABLE `sn_counter`\n(\n    `counter_id`      bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT,\n    `visit_time`      timestamp(0)        NOT NULL DEFAULT CURRENT_TIMESTAMP(0),\n    `visit_length`    int(10) UNSIGNED    NOT NULL DEFAULT 0,\n    `hits`            int(10) UNSIGNED    NOT NULL DEFAULT 1,\n    `user_id`         bigint(20) UNSIGNED NULL     DEFAULT 0,\n    `player_entry_id` bigint(20) UNSIGNED NULL     DEFAULT NULL,\n    `page_url_id`     int(10) UNSIGNED    NULL     DEFAULT NULL,\n    `query_string_id` bigint(20) UNSIGNED NULL     DEFAULT NULL,\n    PRIMARY KEY (`counter_id`) USING BTREE,\n    UNIQUE INDEX `counter_id` (`counter_id`) USING BTREE,\n    INDEX `I_counter_page_url_id` (`page_url_id`) USING BTREE,\n    INDEX `I_counter_visit_time` (`visit_time`, `counter_id`) USING BTREE,\n    INDEX `I_counter_query_string_id` (`query_string_id`) USING BTREE,\n    INDEX `I_counter_player_entry_id` (`player_entry_id`, `user_id`) USING BTREE,\n    INDEX `I_counter_user_id` (`user_id`, `player_entry_id`) USING BTREE,\n    CONSTRAINT `FK_counter_page_url_id` FOREIGN KEY (`page_url_id`) REFERENCES `sn_security_url` (`url_id`) ON DELETE CASCADE ON UPDATE CASCADE\n) ENGINE = InnoDB\n  AUTO_INCREMENT = 1\n  CHARACTER SET = utf8\n  COLLATE = utf8_general_ci\n  ROW_FORMAT = Dynamic;\n\n-- ----------------------------\n-- Table structure for sn_festival\n-- ----------------------------\nDROP TABLE IF EXISTS `sn_festival`;\nCREATE TABLE `sn_festival`\n(\n    `id`     smallint(5) UNSIGNED                                    NOT NULL AUTO_INCREMENT,\n    `start`  datetime(0)                                             NOT NULL COMMENT 'Festival start datetime',\n    `finish` datetime(0)                                             NOT NULL COMMENT 'Festival end datetime',\n    `name`   varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL DEFAULT '' COMMENT 'Название акции/ивента',\n    PRIMARY KEY (`id`) USING BTREE,\n    INDEX `I_festival_date_range` (`start`, `finish`, `id`) USING BTREE\n) ENGINE = InnoDB\n  AUTO_INCREMENT = 1\n  CHARACTER SET = utf8\n  COLLATE = utf8_unicode_ci\n  ROW_FORMAT = Dynamic;\n\n-- ----------------------------\n-- Table structure for sn_festival_gifts\n-- ----------------------------\nDROP TABLE IF EXISTS `sn_festival_gifts`;\nCREATE TABLE `sn_festival_gifts`\n(\n    `id`          bigint(20) UNSIGNED                                      NOT NULL AUTO_INCREMENT,\n    `highspot_id` int(10) UNSIGNED                                         NULL     DEFAULT NULL,\n    `from`        bigint(20) UNSIGNED                                      NULL     DEFAULT NULL,\n    `to`          bigint(20) UNSIGNED                                      NULL     DEFAULT NULL,\n    `amount`      bigint(20) UNSIGNED                                      NOT NULL,\n    `disclosure`  tinyint(1) UNSIGNED                                      NOT NULL DEFAULT 0,\n    `message`     varchar(4096) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL DEFAULT '',\n    PRIMARY KEY (`id`) USING BTREE,\n    INDEX `I_highspot_id` (`highspot_id`, `from`, `to`) USING BTREE,\n    INDEX `I_to_from` (`highspot_id`, `to`, `from`) USING BTREE\n) ENGINE = InnoDB\n  AUTO_INCREMENT = 1\n  CHARACTER SET = utf8\n  COLLATE = utf8_unicode_ci\n  ROW_FORMAT = Dynamic;\n\n-- ----------------------------\n-- Table structure for sn_festival_highspot\n-- ----------------------------\nDROP TABLE IF EXISTS `sn_festival_highspot`;\nCREATE TABLE `sn_festival_highspot`\n(\n    `id`          int(10) UNSIGNED                                        NOT NULL AUTO_INCREMENT,\n    `festival_id` smallint(5) UNSIGNED                                    NULL     DEFAULT NULL,\n    `class`       tinyint(3) UNSIGNED                                     NOT NULL DEFAULT 0 COMMENT 'Highspot class',\n    `start`       datetime(0)                                             NOT NULL COMMENT 'Highspot start datetime',\n    `finish`      datetime(0)                                             NOT NULL COMMENT 'Highspot end datetime',\n    `name`        varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL DEFAULT '',\n    `params`      text CHARACTER SET utf8 COLLATE utf8_unicode_ci         NOT NULL COMMENT 'Параметры хайспота в виде JSON-encoded',\n    PRIMARY KEY (`id`) USING BTREE,\n    INDEX `I_highspot_order` (`start`, `finish`, `id`) USING BTREE,\n    INDEX `I_highspot_festival_id` (`festival_id`, `start`, `finish`, `id`) USING BTREE,\n    CONSTRAINT `FK_highspot_festival_id` FOREIGN KEY (`festival_id`) REFERENCES `sn_festival` (`id`) ON DELETE CASCADE ON UPDATE CASCADE\n) ENGINE = InnoDB\n  AUTO_INCREMENT = 1\n  CHARACTER SET = utf8\n  COLLATE = utf8_unicode_ci\n  ROW_FORMAT = Dynamic;\n\n-- ----------------------------\n-- Table structure for sn_festival_highspot_activity\n-- ----------------------------\nDROP TABLE IF EXISTS `sn_festival_highspot_activity`;\nCREATE TABLE `sn_festival_highspot_activity`\n(\n    `id`          int(10) UNSIGNED                                NOT NULL AUTO_INCREMENT,\n    `highspot_id` int(10) UNSIGNED                                NULL     DEFAULT NULL,\n    `class`       smallint(5) UNSIGNED                            NOT NULL DEFAULT 0 COMMENT 'Класс события - ID модуля события',\n    `type`        tinyint(1) UNSIGNED                             NOT NULL DEFAULT 0 COMMENT 'Тип активити: 1 - триггер, 2 - хук',\n    `start`       datetime(0)                                     NOT NULL COMMENT 'Запланированное время запуска',\n    `finish`      datetime(0)                                     NULL     DEFAULT NULL COMMENT 'Реальное время запуска',\n    `params`      text CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL COMMENT 'Параметры активити в виде сериализованного архива',\n    PRIMARY KEY (`id`) USING BTREE,\n    INDEX `I_festival_activity_order` (`start`, `finish`, `id`) USING BTREE,\n    INDEX `I_festival_activity_highspot_id` (`highspot_id`, `start`, `finish`, `id`) USING BTREE,\n    CONSTRAINT `FK_festival_activity_highspot_id` FOREIGN KEY (`highspot_id`) REFERENCES `sn_festival_highspot` (`id`) ON DELETE CASCADE ON UPDATE CASCADE\n) ENGINE = InnoDB\n  AUTO_INCREMENT = 1\n  CHARACTER SET = utf8\n  COLLATE = utf8_unicode_ci\n  ROW_FORMAT = Dynamic;\n\n-- ----------------------------\n-- Table structure for sn_festival_ois_player\n-- ----------------------------\nDROP TABLE IF EXISTS `sn_festival_ois_player`;\nCREATE TABLE `sn_festival_ois_player`\n(\n    `highspot_id` int(10) UNSIGNED    NOT NULL COMMENT 'Highspot ID',\n    `player_id`   bigint(20) UNSIGNED NOT NULL COMMENT 'Player ID',\n    `ois_count`   int(10) UNSIGNED    NULL DEFAULT NULL COMMENT 'OiS player controlled last tick',\n    PRIMARY KEY (`highspot_id`, `player_id`) USING BTREE,\n    INDEX `I_player_highspot` (`player_id`, `highspot_id`) USING BTREE,\n    CONSTRAINT `FK_ois_highspot` FOREIGN KEY (`highspot_id`) REFERENCES `sn_festival_highspot` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,\n    CONSTRAINT `FK_ois_player` FOREIGN KEY (`player_id`) REFERENCES `sn_users` (`id`) ON DELETE CASCADE ON UPDATE CASCADE\n) ENGINE = InnoDB\n  CHARACTER SET = utf8\n  COLLATE = utf8_unicode_ci\n  ROW_FORMAT = Dynamic;\n\n-- ----------------------------\n-- Table structure for sn_festival_unit\n-- ----------------------------\nDROP TABLE IF EXISTS `sn_festival_unit`;\nCREATE TABLE `sn_festival_unit`\n(\n    `id`          bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT,\n    `highspot_id` int(10) UNSIGNED    NULL     DEFAULT NULL,\n    `player_id`   bigint(20) UNSIGNED NULL     DEFAULT NULL,\n    `unit_id`     bigint(20)          NOT NULL DEFAULT 0,\n    `unit_level`  bigint(20) UNSIGNED NOT NULL DEFAULT 0,\n    PRIMARY KEY (`id`) USING BTREE,\n    INDEX `I_festival_unit_player_id` (`player_id`, `highspot_id`) USING BTREE,\n    INDEX `I_festival_unit_highspot_id` (`highspot_id`, `unit_id`, `player_id`) USING BTREE,\n    CONSTRAINT `FK_festival_unit_hispot` FOREIGN KEY (`highspot_id`) REFERENCES `sn_festival_highspot` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,\n    CONSTRAINT `FK_festival_unit_player` FOREIGN KEY (`player_id`) REFERENCES `sn_users` (`id`) ON DELETE CASCADE ON UPDATE CASCADE\n) ENGINE = InnoDB\n  AUTO_INCREMENT = 1\n  CHARACTER SET = utf8\n  COLLATE = utf8_unicode_ci\n  ROW_FORMAT = Dynamic;\n\n-- ----------------------------\n-- Table structure for sn_festival_unit_log\n-- ----------------------------\nDROP TABLE IF EXISTS `sn_festival_unit_log`;\nCREATE TABLE `sn_festival_unit_log`\n(\n    `id`          bigint(20) UNSIGNED                                     NOT NULL AUTO_INCREMENT,\n    `highspot_id` int(10) UNSIGNED                                        NULL     DEFAULT NULL,\n    `player_id`   bigint(20) UNSIGNED                                     NOT NULL COMMENT 'User ID',\n    `player_name` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci  NOT NULL DEFAULT '',\n    `unit_id`     bigint(20) UNSIGNED                                     NOT NULL DEFAULT 0,\n    `timestamp`   timestamp(0)                                            NOT NULL DEFAULT CURRENT_TIMESTAMP(0),\n    `unit_level`  int(11)                                                 NOT NULL DEFAULT 0,\n    `unit_image`  varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '',\n    PRIMARY KEY (`id`) USING BTREE,\n    INDEX `I_festival_unit_log_player_id` (`player_id`, `highspot_id`, `id`) USING BTREE,\n    INDEX `I_festival_unit_log_highspot_id` (`highspot_id`, `unit_id`, `player_id`) USING BTREE,\n    CONSTRAINT `FK_festival_unit_log_hispot` FOREIGN KEY (`highspot_id`) REFERENCES `sn_festival_highspot` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,\n    CONSTRAINT `FK_festival_unit_log_player` FOREIGN KEY (`player_id`) REFERENCES `sn_users` (`id`) ON DELETE CASCADE ON UPDATE CASCADE\n) ENGINE = InnoDB\n  AUTO_INCREMENT = 1\n  CHARACTER SET = utf8\n  COLLATE = utf8_general_ci\n  ROW_FORMAT = Dynamic;\n\n-- ----------------------------\n-- Table structure for sn_fleets\n-- ----------------------------\nDROP TABLE IF EXISTS `sn_fleets`;\nCREATE TABLE `sn_fleets`\n(\n    `fleet_id`                 bigint(20) UNSIGNED                                    NOT NULL AUTO_INCREMENT,\n    `fleet_owner`              bigint(20) UNSIGNED                                    NULL     DEFAULT NULL,\n    `fleet_mission`            int(11)                                                NOT NULL DEFAULT 0,\n    `fleet_amount`             bigint(11)                                             NOT NULL DEFAULT 0,\n    `fleet_array`              text CHARACTER SET utf8 COLLATE utf8_general_ci        NULL,\n    `fleet_start_time`         int(11)                                                NOT NULL DEFAULT 0,\n    `fleet_start_planet_id`    bigint(20) UNSIGNED                                    NULL     DEFAULT NULL COMMENT 'Fleet start planet ID',\n    `fleet_start_galaxy`       int(11)                                                NOT NULL DEFAULT 0,\n    `fleet_start_system`       int(11)                                                NOT NULL DEFAULT 0,\n    `fleet_start_planet`       int(11)                                                NOT NULL DEFAULT 0,\n    `fleet_start_type`         int(11)                                                NOT NULL DEFAULT 0,\n    `fleet_end_time`           int(11)                                                NOT NULL DEFAULT 0,\n    `fleet_end_stay`           int(11)                                                NOT NULL DEFAULT 0,\n    `fleet_end_planet_id`      bigint(20) UNSIGNED                                    NULL     DEFAULT NULL COMMENT 'Fleet end planet ID',\n    `fleet_end_galaxy`         int(11)                                                NOT NULL DEFAULT 0,\n    `fleet_end_system`         int(11)                                                NOT NULL DEFAULT 0,\n    `fleet_end_planet`         int(11)                                                NOT NULL DEFAULT 0,\n    `fleet_end_type`           int(11)                                                NOT NULL DEFAULT 0,\n    `fleet_resource_metal`     decimal(65, 0)                                         NULL     DEFAULT 0,\n    `fleet_resource_crystal`   decimal(65, 0)                                         NULL     DEFAULT 0,\n    `fleet_resource_deuterium` decimal(65, 0)                                         NULL     DEFAULT 0,\n    `fleet_target_owner`       int(11)                                                NOT NULL DEFAULT 0,\n    `fleet_group`              varchar(15) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '0',\n    `fleet_mess`               int(11)                                                NOT NULL DEFAULT 0,\n    `start_time`               int(11)                                                NULL     DEFAULT 0,\n    PRIMARY KEY (`fleet_id`) USING BTREE,\n    UNIQUE INDEX `fleet_id` (`fleet_id`) USING BTREE,\n    INDEX `fleet_origin` (`fleet_start_galaxy`, `fleet_start_system`, `fleet_start_planet`) USING BTREE,\n    INDEX `fleet_dest` (`fleet_end_galaxy`, `fleet_end_system`, `fleet_end_planet`) USING BTREE,\n    INDEX `fleet_start_time` (`fleet_start_time`) USING BTREE,\n    INDEX `fllet_end_time` (`fleet_end_time`) USING BTREE,\n    INDEX `fleet_owner` (`fleet_owner`) USING BTREE,\n    INDEX `i_fl_targ_owner` (`fleet_target_owner`) USING BTREE,\n    INDEX `fleet_both` (`fleet_start_galaxy`, `fleet_start_system`, `fleet_start_planet`, `fleet_start_type`,\n                        `fleet_end_galaxy`, `fleet_end_system`, `fleet_end_planet`) USING BTREE,\n    INDEX `fleet_mess` (`fleet_mess`) USING BTREE,\n    INDEX `fleet_group` (`fleet_group`) USING BTREE,\n    INDEX `I_fleet_start_planet_id` (`fleet_start_planet_id`) USING BTREE,\n    INDEX `I_fleet_end_planet_id` (`fleet_end_planet_id`) USING BTREE,\n    CONSTRAINT `FK_fleet_owner` FOREIGN KEY (`fleet_owner`) REFERENCES `sn_users` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,\n    CONSTRAINT `FK_fleet_planet_end` FOREIGN KEY (`fleet_end_planet_id`) REFERENCES `sn_planets` (`id`) ON DELETE SET NULL ON UPDATE CASCADE,\n    CONSTRAINT `FK_fleet_planet_start` FOREIGN KEY (`fleet_start_planet_id`) REFERENCES `sn_planets` (`id`) ON DELETE SET NULL ON UPDATE CASCADE\n) ENGINE = InnoDB\n  AUTO_INCREMENT = 1\n  CHARACTER SET = utf8\n  COLLATE = utf8_general_ci\n  ROW_FORMAT = Dynamic;\n\n-- ----------------------------\n-- Table structure for sn_iraks\n-- ----------------------------\nDROP TABLE IF EXISTS `sn_iraks`;\nCREATE TABLE `sn_iraks`\n(\n    `id`                 bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT,\n    `fleet_end_time`     int(11) UNSIGNED    NOT NULL DEFAULT 0,\n    `fleet_end_galaxy`   int(2) UNSIGNED     NULL     DEFAULT 0,\n    `fleet_end_system`   int(4) UNSIGNED     NULL     DEFAULT 0,\n    `fleet_end_planet`   int(2) UNSIGNED     NULL     DEFAULT 0,\n    `fleet_start_galaxy` int(2) UNSIGNED     NULL     DEFAULT 0,\n    `fleet_start_system` int(4) UNSIGNED     NULL     DEFAULT 0,\n    `fleet_start_planet` int(2) UNSIGNED     NULL     DEFAULT 0,\n    `fleet_owner`        bigint(20) UNSIGNED NULL     DEFAULT NULL,\n    `fleet_target_owner` bigint(20) UNSIGNED NULL     DEFAULT NULL,\n    `fleet_amount`       bigint(20) UNSIGNED NULL     DEFAULT 0,\n    `primaer`            int(32)             NULL     DEFAULT NULL,\n    `fleet_start_type`   smallint(6)         NOT NULL DEFAULT 1,\n    `fleet_end_type`     smallint(6)         NOT NULL DEFAULT 1,\n    PRIMARY KEY (`id`) USING BTREE,\n    INDEX `I_iraks_fleet_owner` (`fleet_owner`) USING BTREE,\n    INDEX `I_iraks_fleet_target_owner` (`fleet_target_owner`) USING BTREE,\n    CONSTRAINT `FK_iraks_fleet_owner` FOREIGN KEY (`fleet_owner`) REFERENCES `sn_users` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,\n    CONSTRAINT `FK_iraks_fleet_target_owner` FOREIGN KEY (`fleet_target_owner`) REFERENCES `sn_users` (`id`) ON DELETE CASCADE ON UPDATE CASCADE\n) ENGINE = InnoDB\n  AUTO_INCREMENT = 1\n  CHARACTER SET = utf8\n  COLLATE = utf8_general_ci\n  ROW_FORMAT = Dynamic;\n\n-- ----------------------------\n-- Table structure for sn_lng_usage_stat\n-- ----------------------------\nDROP TABLE IF EXISTS `sn_lng_usage_stat`;\nCREATE TABLE `sn_lng_usage_stat`\n(\n    `lang_code` char(2) CHARACTER SET utf8 COLLATE utf8_unicode_ci      NOT NULL,\n    `string_id` varchar(128) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL,\n    `file`      varchar(128) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL,\n    `line`      smallint(6)                                             NOT NULL,\n    `is_empty`  tinyint(1)                                              NOT NULL,\n    `locale`    mediumtext CHARACTER SET utf8 COLLATE utf8_unicode_ci   NULL,\n    PRIMARY KEY (`lang_code`, `string_id`, `file`, `line`, `is_empty`) USING BTREE\n) ENGINE = InnoDB\n  CHARACTER SET = utf8\n  COLLATE = utf8_unicode_ci\n  ROW_FORMAT = Dynamic;\n\n-- ----------------------------\n-- Table structure for sn_log_dark_matter\n-- ----------------------------\nDROP TABLE IF EXISTS `sn_log_dark_matter`;\nCREATE TABLE `sn_log_dark_matter`\n(\n    `log_dark_matter_id`        bigint(20) UNSIGNED                                     NOT NULL AUTO_INCREMENT,\n    `log_dark_matter_timestamp` timestamp(0)                                            NOT NULL DEFAULT CURRENT_TIMESTAMP(0) COMMENT 'Human-readable record timestamp',\n    `log_dark_matter_username`  varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci  NOT NULL DEFAULT '' COMMENT 'Username',\n    `log_dark_matter_reason`    int(10) UNSIGNED                                        NOT NULL DEFAULT 0 COMMENT 'Reason ID for dark matter adjustment',\n    `log_dark_matter_amount`    int(10)                                                 NOT NULL DEFAULT 0 COMMENT 'Amount of dark matter change',\n    `log_dark_matter_comment`   text CHARACTER SET utf8 COLLATE utf8_general_ci         NULL COMMENT 'Comments',\n    `log_dark_matter_page`      varchar(512) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '' COMMENT 'Page that makes entry to log',\n    `log_dark_matter_sender`    bigint(20) UNSIGNED                                     NOT NULL DEFAULT 0 COMMENT 'User ID which make log record',\n    PRIMARY KEY (`log_dark_matter_id`) USING BTREE,\n    UNIQUE INDEX `log_dark_matter_id` (`log_dark_matter_id`) USING BTREE,\n    INDEX `i_log_dark_matter_sender_id` (`log_dark_matter_sender`, `log_dark_matter_id`) USING BTREE,\n    INDEX `i_log_dark_matter_reason_sender_id` (`log_dark_matter_reason`, `log_dark_matter_sender`,\n                                                `log_dark_matter_id`) USING BTREE,\n    INDEX `i_log_dark_matter_amount` (`log_dark_matter_amount`) USING BTREE\n) ENGINE = InnoDB\n  AUTO_INCREMENT = 1\n  CHARACTER SET = utf8\n  COLLATE = utf8_general_ci\n  ROW_FORMAT = Dynamic;\n\n-- ----------------------------\n-- Table structure for sn_log_halloween_2015\n-- ----------------------------\nDROP TABLE IF EXISTS `sn_log_halloween_2015`;\nCREATE TABLE `sn_log_halloween_2015`\n(\n    `log_hw2015_id` bigint(20) UNSIGNED                                    NOT NULL AUTO_INCREMENT,\n    `player_id`     bigint(20) UNSIGNED                                    NOT NULL COMMENT 'User ID',\n    `player_name`   varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '',\n    `unit_snid`     bigint(20) UNSIGNED                                    NOT NULL DEFAULT 0,\n    `timestamp`     timestamp(0)                                           NOT NULL DEFAULT CURRENT_TIMESTAMP(0),\n    PRIMARY KEY (`log_hw2015_id`) USING BTREE,\n    INDEX `player_id` (`player_id`, `log_hw2015_id`) USING BTREE\n) ENGINE = InnoDB\n  AUTO_INCREMENT = 1\n  CHARACTER SET = utf8\n  COLLATE = utf8_general_ci\n  ROW_FORMAT = Dynamic;\n\n-- ----------------------------\n-- Table structure for sn_log_metamatter\n-- ----------------------------\nDROP TABLE IF EXISTS `sn_log_metamatter`;\nCREATE TABLE `sn_log_metamatter`\n(\n    `id`           bigint(20) UNSIGNED                                         NOT NULL AUTO_INCREMENT,\n    `timestamp`    timestamp(0)                                                NOT NULL DEFAULT CURRENT_TIMESTAMP(0) COMMENT 'Human-readable record timestamp',\n    `user_id`      bigint(20) UNSIGNED                                         NOT NULL DEFAULT 0 COMMENT 'User ID which make log record',\n    `username`     varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci      NOT NULL DEFAULT '' COMMENT 'Username',\n    `reason`       int(10) UNSIGNED                                            NOT NULL DEFAULT 0 COMMENT 'Reason ID for metamatter adjustment',\n    `amount`       bigint(10)                                                  NOT NULL DEFAULT 0 COMMENT 'Amount of metamatter change',\n    `comment`      text CHARACTER SET utf8 COLLATE utf8_general_ci             NULL COMMENT 'Comments',\n    `page`         varchar(512) CHARACTER SET utf8 COLLATE utf8_general_ci     NOT NULL DEFAULT '' COMMENT 'Page that makes entry to log',\n    `provider_id`  tinyint(3) UNSIGNED                                         NOT NULL DEFAULT 1 COMMENT 'Account provider',\n    `account_id`   bigint(20) UNSIGNED                                         NOT NULL DEFAULT 0,\n    `account_name` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci      NOT NULL DEFAULT '',\n    `server_name`  varchar(128) CHARACTER SET latin1 COLLATE latin1_general_ci NOT NULL DEFAULT 'http://localhost/supernova/',\n    PRIMARY KEY (`id`) USING BTREE,\n    UNIQUE INDEX `id` (`id`) USING BTREE,\n    INDEX `I_log_metamatter_sender_id` (`user_id`, `id`) USING BTREE,\n    INDEX `I_log_metamatter_reason_sender_id` (`reason`, `user_id`, `id`) USING BTREE\n) ENGINE = InnoDB\n  AUTO_INCREMENT = 1\n  CHARACTER SET = utf8\n  COLLATE = utf8_general_ci\n  ROW_FORMAT = Dynamic;\n\n-- ----------------------------\n-- Table structure for sn_log_users_online\n-- ----------------------------\nDROP TABLE IF EXISTS `sn_log_users_online`;\nCREATE TABLE `sn_log_users_online`\n(\n    `online_timestamp`  timestamp(0)         NOT NULL DEFAULT CURRENT_TIMESTAMP(0) COMMENT 'Measure time',\n    `online_count`      smallint(5) UNSIGNED NOT NULL DEFAULT 0 COMMENT 'Users online',\n    `online_aggregated` tinyint(1) UNSIGNED  NOT NULL DEFAULT 0,\n    PRIMARY KEY (`online_timestamp`, `online_aggregated`) USING BTREE\n) ENGINE = InnoDB\n  CHARACTER SET = utf8\n  COLLATE = utf8_general_ci\n  ROW_FORMAT = Dynamic;\n\n-- ----------------------------\n-- Table structure for sn_logs\n-- ----------------------------\nDROP TABLE IF EXISTS `sn_logs`;\nCREATE TABLE `sn_logs`\n(\n    `log_timestamp` timestamp(0)                                            NOT NULL DEFAULT CURRENT_TIMESTAMP(0) COMMENT 'Human-readable record timestamp',\n    `log_username`  varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci  NOT NULL DEFAULT '' COMMENT 'Username',\n    `log_title`     varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci  NOT NULL DEFAULT 'Log entry' COMMENT 'Short description',\n    `log_text`      text CHARACTER SET utf8 COLLATE utf8_general_ci         NULL,\n    `log_page`      varchar(512) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '' COMMENT 'Page that makes entry to log',\n    `log_code`      int(10) UNSIGNED                                        NOT NULL DEFAULT 0,\n    `log_sender`    bigint(20) UNSIGNED                                     NOT NULL DEFAULT 0 COMMENT 'User ID which make log record',\n    `log_time`      int(11) UNSIGNED                                        NOT NULL DEFAULT 0 COMMENT 'Machine-readable timestamp',\n    `log_dump`      mediumtext CHARACTER SET utf8 COLLATE utf8_general_ci   NOT NULL COMMENT 'Machine-readable dump of variables',\n    `log_id`        bigint(20) UNSIGNED                                     NOT NULL AUTO_INCREMENT,\n    PRIMARY KEY (`log_id`) USING BTREE,\n    UNIQUE INDEX `log_id` (`log_id`) USING BTREE,\n    INDEX `i_log_username` (`log_username`) USING BTREE,\n    INDEX `i_log_time` (`log_time`) USING BTREE,\n    INDEX `i_log_sender` (`log_sender`) USING BTREE,\n    INDEX `i_log_code` (`log_code`) USING BTREE,\n    INDEX `i_log_page` (`log_page`(255)) USING BTREE\n) ENGINE = InnoDB\n  AUTO_INCREMENT = 1\n  CHARACTER SET = utf8\n  COLLATE = utf8_general_ci\n  ROW_FORMAT = Dynamic;\n\n-- ----------------------------\n-- Table structure for sn_messages\n-- ----------------------------\nDROP TABLE IF EXISTS `sn_messages`;\nCREATE TABLE `sn_messages`\n(\n    `message_id`      bigint(11)                                             NOT NULL AUTO_INCREMENT,\n    `message_owner`   int(11)                                                NOT NULL DEFAULT 0,\n    `message_sender`  int(11)                                                NOT NULL DEFAULT 0,\n    `message_time`    int(11)                                                NOT NULL DEFAULT 0,\n    `message_type`    int(11)                                                NOT NULL DEFAULT 0,\n    `message_from`    varchar(48) CHARACTER SET utf8 COLLATE utf8_general_ci NULL     DEFAULT NULL,\n    `message_subject` varchar(48) CHARACTER SET utf8 COLLATE utf8_general_ci NULL     DEFAULT NULL,\n    `message_text`    text CHARACTER SET utf8 COLLATE utf8_general_ci        NULL,\n    `message_json`    tinyint(1) UNSIGNED                                    NOT NULL DEFAULT 0,\n    PRIMARY KEY (`message_id`) USING BTREE,\n    INDEX `i_owner_time` (`message_owner`, `message_time`) USING BTREE,\n    INDEX `i_sender_time` (`message_sender`, `message_time`) USING BTREE,\n    INDEX `i_time` (`message_time`) USING BTREE\n) ENGINE = InnoDB\n  AUTO_INCREMENT = 1\n  CHARACTER SET = utf8\n  COLLATE = utf8_general_ci\n  ROW_FORMAT = Dynamic;\n\n-- ----------------------------\n-- Table structure for sn_notes\n-- ----------------------------\nDROP TABLE IF EXISTS `sn_notes`;\nCREATE TABLE `sn_notes`\n(\n    `id`          bigint(20) UNSIGNED                                    NOT NULL AUTO_INCREMENT,\n    `owner`       bigint(20) UNSIGNED                                    NULL     DEFAULT NULL,\n    `time`        int(11)                                                NULL     DEFAULT NULL,\n    `priority`    tinyint(1)                                             NULL     DEFAULT NULL,\n    `title`       varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NULL     DEFAULT NULL,\n    `galaxy`      smallint(6) UNSIGNED                                   NOT NULL DEFAULT 0,\n    `system`      smallint(6) UNSIGNED                                   NOT NULL DEFAULT 0,\n    `planet`      smallint(6) UNSIGNED                                   NOT NULL DEFAULT 0,\n    `planet_type` tinyint(4) UNSIGNED                                    NOT NULL DEFAULT 1,\n    `text`        text CHARACTER SET utf8 COLLATE utf8_general_ci        NULL,\n    `sticky`      tinyint(1) UNSIGNED                                    NOT NULL DEFAULT 0,\n    PRIMARY KEY (`id`) USING BTREE,\n    UNIQUE INDEX `id` (`id`) USING BTREE,\n    INDEX `I_notes_owner` (`owner`) USING BTREE,\n    INDEX `I_owner_priority_time` (`owner`, `priority`, `time`) USING BTREE,\n    CONSTRAINT `FK_notes_owner` FOREIGN KEY (`owner`) REFERENCES `sn_users` (`id`) ON DELETE CASCADE ON UPDATE CASCADE\n) ENGINE = InnoDB\n  AUTO_INCREMENT = 1\n  CHARACTER SET = utf8\n  COLLATE = utf8_general_ci\n  ROW_FORMAT = Dynamic;\n\n-- ----------------------------\n-- Table structure for sn_payment\n-- ----------------------------\nDROP TABLE IF EXISTS `sn_payment`;\nCREATE TABLE `sn_payment`\n(\n    `payment_id`                 bigint(20) UNSIGNED                                    NOT NULL AUTO_INCREMENT COMMENT 'Internal payment ID',\n    `payment_status`             int(11)                                                NULL     DEFAULT 0 COMMENT 'Payment status',\n    `payment_user_id`            bigint(20) UNSIGNED                                    NULL     DEFAULT NULL,\n    `payment_user_name`          varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NULL     DEFAULT NULL,\n    `payment_amount`             decimal(60, 5)                                         NULL     DEFAULT 0.00000 COMMENT 'Amount paid',\n    `payment_currency`           varchar(3) CHARACTER SET utf8 COLLATE utf8_general_ci  NULL     DEFAULT '' COMMENT 'Payment currency',\n    `payment_dark_matter_paid`   decimal(65, 0)                                         NULL     DEFAULT 0 COMMENT 'Real DM paid for',\n    `payment_dark_matter_gained` decimal(65, 0)                                         NULL     DEFAULT 0 COMMENT 'DM gained by player (with bonuses)',\n    `payment_date`               timestamp(0)                                           NOT NULL DEFAULT CURRENT_TIMESTAMP(0) COMMENT 'Payment server timestamp',\n    `payment_comment`            text CHARACTER SET utf8 COLLATE utf8_general_ci        NULL COMMENT 'Payment comment',\n    `payment_module_name`        varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NULL     DEFAULT '' COMMENT 'Payment module name',\n    `payment_method_id`          smallint(6)                                            NULL     DEFAULT NULL,\n    `payment_external_id`        varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NULL     DEFAULT '' COMMENT 'External payment ID in payment system',\n    `payment_external_date`      datetime(0)                                            NULL     DEFAULT NULL COMMENT 'External payment timestamp in payment system',\n    `payment_external_lots`      decimal(65, 5)                                         NOT NULL DEFAULT 0.00000 COMMENT 'Payment system lot amount',\n    `payment_external_amount`    decimal(65, 5)                                         NOT NULL DEFAULT 0.00000 COMMENT 'Money incoming from payment system',\n    `payment_external_currency`  varchar(3) CHARACTER SET utf8 COLLATE utf8_general_ci  NOT NULL DEFAULT '' COMMENT 'Payment system currency',\n    `payment_test`               tinyint(3) UNSIGNED                                    NOT NULL DEFAULT 0 COMMENT 'Is this a test payment?',\n    `payment_provider_id`        tinyint(3) UNSIGNED                                    NOT NULL DEFAULT 1 COMMENT 'Payment account provider',\n    `payment_account_id`         bigint(20) UNSIGNED                                    NOT NULL,\n    `payment_account_name`       varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '',\n    PRIMARY KEY (`payment_id`) USING BTREE,\n    INDEX `I_payment_user` (`payment_user_id`, `payment_user_name`) USING BTREE,\n    INDEX `I_payment_module_internal_id` (`payment_module_name`, `payment_external_id`) USING BTREE,\n    INDEX `I_payment_method_id` (`payment_method_id`) USING BTREE\n) ENGINE = InnoDB\n  AUTO_INCREMENT = 1\n  CHARACTER SET = utf8\n  COLLATE = utf8_general_ci\n  ROW_FORMAT = Dynamic;\n\n-- ----------------------------\n-- Table structure for sn_planets\n-- ----------------------------\nDROP TABLE IF EXISTS `sn_planets`;\nCREATE TABLE `sn_planets`\n(\n    `id`                           bigint(20) UNSIGNED                                    NOT NULL AUTO_INCREMENT,\n    `name`                         varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT 'Planet',\n    `id_owner`                     bigint(20) UNSIGNED                                    NULL     DEFAULT NULL,\n    `galaxy`                       smallint(6)                                            NOT NULL DEFAULT 0,\n    `system`                       smallint(6)                                            NOT NULL DEFAULT 0,\n    `planet`                       smallint(6)                                            NOT NULL DEFAULT 0,\n    `planet_type`                  tinyint(4)                                             NOT NULL DEFAULT 1,\n    `metal`                        decimal(65, 5)                                         NOT NULL DEFAULT 0.00000,\n    `crystal`                      decimal(65, 5)                                         NOT NULL DEFAULT 0.00000,\n    `deuterium`                    decimal(65, 5)                                         NOT NULL DEFAULT 0.00000,\n    `energy_max`                   decimal(65, 0)                                         NOT NULL DEFAULT 0,\n    `energy_used`                  decimal(65, 0)                                         NOT NULL DEFAULT 0,\n    `last_jump_time`               int(11)                                                NOT NULL DEFAULT 0,\n    `metal_perhour`                int(11)                                                NOT NULL DEFAULT 0,\n    `crystal_perhour`              int(11)                                                NOT NULL DEFAULT 0,\n    `deuterium_perhour`            int(11)                                                NOT NULL DEFAULT 0,\n    `metal_mine_porcent`           tinyint(3) UNSIGNED                                    NOT NULL DEFAULT 10,\n    `crystal_mine_porcent`         tinyint(3) UNSIGNED                                    NOT NULL DEFAULT 10,\n    `deuterium_sintetizer_porcent` tinyint(3) UNSIGNED                                    NOT NULL DEFAULT 10,\n    `solar_plant_porcent`          tinyint(3) UNSIGNED                                    NOT NULL DEFAULT 10,\n    `fusion_plant_porcent`         tinyint(3) UNSIGNED                                    NOT NULL DEFAULT 10,\n    `solar_satelit_porcent`        tinyint(3) UNSIGNED                                    NOT NULL DEFAULT 10,\n    `last_update`                  int(11)                                                NULL     DEFAULT NULL,\n    `que_processed`                int(11) UNSIGNED                                       NOT NULL DEFAULT 0,\n    `image`                        varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT 'normaltempplanet01',\n    `points`                       bigint(20)                                             NULL     DEFAULT 0,\n    `ranks`                        bigint(20)                                             NULL     DEFAULT 0,\n    `id_level`                     tinyint(4)                                             NOT NULL DEFAULT 0,\n    `destruyed`                    int(11)                                                NOT NULL DEFAULT 0,\n    `diameter`                     int(11)                                                NOT NULL DEFAULT 12800,\n    `field_max`                    smallint(5) UNSIGNED                                   NOT NULL DEFAULT 163,\n    `field_current`                smallint(5) UNSIGNED                                   NOT NULL DEFAULT 0,\n    `temp_min`                     smallint(6)                                            NOT NULL DEFAULT 0,\n    `temp_max`                     smallint(6)                                            NOT NULL DEFAULT 40,\n    `metal_max`                    decimal(65, 0)                                         NULL     DEFAULT 100000,\n    `crystal_max`                  decimal(65, 0)                                         NULL     DEFAULT 100000,\n    `deuterium_max`                decimal(65, 0)                                         NULL     DEFAULT 100000,\n    `parent_planet`                bigint(20) UNSIGNED                                    NULL     DEFAULT 0,\n    `debris_metal`                 bigint(20) UNSIGNED                                    NULL     DEFAULT 0,\n    `debris_crystal`               bigint(20) UNSIGNED                                    NULL     DEFAULT 0,\n    `PLANET_GOVERNOR_ID`           smallint(6)                                            NOT NULL DEFAULT 0,\n    `PLANET_GOVERNOR_LEVEL`        smallint(6)                                            NOT NULL DEFAULT 0,\n    `planet_teleport_next`         int(11)                                                NOT NULL DEFAULT 0 COMMENT 'Next teleport time',\n    `ship_sattelite_sloth_porcent` tinyint(3) UNSIGNED                                    NOT NULL DEFAULT 10 COMMENT 'Terran Sloth production',\n    `density`                      smallint(6)                                            NOT NULL DEFAULT 5500 COMMENT 'Planet average density kg/m3',\n    `density_index`                tinyint(4)                                             NOT NULL DEFAULT 4 COMMENT 'Planet cached density index',\n    `position_original`            smallint(6)                                            NOT NULL DEFAULT 0,\n    `field_max_original`           smallint(6)                                            NOT NULL DEFAULT 0,\n    `temp_min_original`            smallint(6)                                            NOT NULL DEFAULT 0,\n    `temp_max_original`            smallint(6)                                            NOT NULL DEFAULT 0,\n    PRIMARY KEY (`id`) USING BTREE,\n    UNIQUE INDEX `id` (`id`) USING BTREE,\n    INDEX `owner_type` (`id_owner`, `planet_type`) USING BTREE,\n    INDEX `id_level` (`id_level`) USING BTREE,\n    INDEX `i_last_update` (`last_update`) USING BTREE,\n    INDEX `GSPT` (`galaxy`, `system`, `planet`, `planet_type`) USING BTREE,\n    INDEX `i_parent_planet` (`parent_planet`) USING BTREE\n) ENGINE = InnoDB\n  AUTO_INCREMENT = 2\n  CHARACTER SET = utf8\n  COLLATE = utf8_general_ci\n  ROW_FORMAT = Dynamic;\n\n-- ----------------------------\n-- Table structure for sn_player_award\n-- ----------------------------\nDROP TABLE IF EXISTS `sn_player_award`;\nCREATE TABLE `sn_player_award`\n(\n    `id`               bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT,\n    `award_type_id`    int(11)             NULL     DEFAULT NULL COMMENT 'Award type i.e. order, medal, pennant, rank etc',\n    `award_id`         int(11)             NULL     DEFAULT NULL COMMENT 'Global award unit ID',\n    `award_variant_id` int(11)             NULL     DEFAULT NULL COMMENT 'Multiply award subtype i.e. for same reward awarded early',\n    `player_id`        bigint(20) UNSIGNED NULL     DEFAULT NULL,\n    `awarded`          timestamp(0)        NOT NULL DEFAULT CURRENT_TIMESTAMP(0) COMMENT 'When was awarded',\n    `active_from`      datetime(0)         NULL     DEFAULT NULL,\n    `active_to`        datetime(0)         NULL     DEFAULT NULL,\n    `hide`             tinyint(1)          NOT NULL DEFAULT 0,\n    PRIMARY KEY (`id`) USING BTREE,\n    INDEX `I_award_player` (`player_id`, `award_type_id`) USING BTREE,\n    CONSTRAINT `FK_player_award_user_id` FOREIGN KEY (`player_id`) REFERENCES `sn_users` (`id`) ON DELETE CASCADE ON UPDATE CASCADE\n) ENGINE = InnoDB\n  AUTO_INCREMENT = 1\n  CHARACTER SET = utf8\n  COLLATE = utf8_general_ci\n  ROW_FORMAT = Dynamic;\n\n-- ----------------------------\n-- Table structure for sn_player_ignore\n-- ----------------------------\nDROP TABLE IF EXISTS `sn_player_ignore`;\nCREATE TABLE `sn_player_ignore`\n(\n    `id`         bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT,\n    `player_id`  bigint(20) UNSIGNED NOT NULL,\n    `ignored_id` bigint(20) UNSIGNED NOT NULL,\n    `subsystem`  tinyint(4)          NOT NULL DEFAULT 0,\n    PRIMARY KEY (`id`) USING BTREE,\n    UNIQUE INDEX `I_player_ignore_all` (`player_id`, `ignored_id`, `subsystem`) USING BTREE,\n    INDEX `I_player_ignore_ignored` (`ignored_id`) USING BTREE,\n    CONSTRAINT `FK_player_ignore_ignored` FOREIGN KEY (`ignored_id`) REFERENCES `sn_users` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,\n    CONSTRAINT `FK_player_ignore_player` FOREIGN KEY (`player_id`) REFERENCES `sn_users` (`id`) ON DELETE CASCADE ON UPDATE CASCADE\n) ENGINE = InnoDB\n  AUTO_INCREMENT = 1\n  CHARACTER SET = utf8\n  COLLATE = utf8_unicode_ci\n  ROW_FORMAT = Dynamic;\n\n-- ----------------------------\n-- Table structure for sn_player_name_history\n-- ----------------------------\nDROP TABLE IF EXISTS `sn_player_name_history`;\nCREATE TABLE `sn_player_name_history`\n(\n    `player_id`   bigint(20) UNSIGNED                                    NULL     DEFAULT NULL COMMENT 'Player ID',\n    `player_name` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT 'Historical player name',\n    `timestamp`   timestamp(0)                                           NOT NULL DEFAULT CURRENT_TIMESTAMP(0) ON UPDATE CURRENT_TIMESTAMP(0) COMMENT 'When player changed name',\n    PRIMARY KEY (`player_name`) USING BTREE,\n    INDEX `I_player_name_history_id_name` (`player_id`, `player_name`) USING BTREE,\n    CONSTRAINT `FK_player_name_history_id` FOREIGN KEY (`player_id`) REFERENCES `sn_users` (`id`) ON DELETE CASCADE ON UPDATE CASCADE\n) ENGINE = InnoDB\n  CHARACTER SET = utf8\n  COLLATE = utf8_general_ci\n  ROW_FORMAT = Dynamic;\n\n-- ----------------------------\n-- Table structure for sn_player_options\n-- ----------------------------\nDROP TABLE IF EXISTS `sn_player_options`;\nCREATE TABLE `sn_player_options`\n(\n    `player_id` bigint(20) UNSIGNED                                       NOT NULL DEFAULT 0,\n    `option_id` smallint(5) UNSIGNED                                      NOT NULL DEFAULT 0,\n    `value`     varchar(16000) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL DEFAULT '',\n    PRIMARY KEY (`player_id`, `option_id`) USING BTREE,\n    CONSTRAINT `FK_player_options_user_id` FOREIGN KEY (`player_id`) REFERENCES `sn_users` (`id`) ON DELETE CASCADE ON UPDATE CASCADE\n) ENGINE = InnoDB\n  CHARACTER SET = utf8\n  COLLATE = utf8_general_ci\n  ROW_FORMAT = Dynamic;\n\n-- ----------------------------\n-- Table structure for sn_powerup\n-- ----------------------------\nDROP TABLE IF EXISTS `sn_powerup`;\nCREATE TABLE `sn_powerup`\n(\n    `powerup_id`          bigint(20) UNSIGNED   NOT NULL AUTO_INCREMENT,\n    `powerup_user_id`     bigint(20) UNSIGNED   NULL     DEFAULT NULL,\n    `powerup_planet_id`   bigint(20) UNSIGNED   NULL     DEFAULT NULL,\n    `powerup_category`    smallint(6)           NOT NULL DEFAULT 0,\n    `powerup_unit_id`     mediumint(8) UNSIGNED NOT NULL DEFAULT 0,\n    `powerup_unit_level`  smallint(5) UNSIGNED  NOT NULL DEFAULT 0,\n    `powerup_time_start`  int(11)               NOT NULL DEFAULT 0,\n    `powerup_time_finish` int(11)               NOT NULL DEFAULT 0,\n    PRIMARY KEY (`powerup_id`) USING BTREE,\n    INDEX `I_powerup_user_id` (`powerup_user_id`) USING BTREE,\n    INDEX `I_powerup_planet_id` (`powerup_planet_id`) USING BTREE,\n    INDEX `I_user_powerup_time` (`powerup_user_id`, `powerup_unit_id`, `powerup_time_start`,\n                                 `powerup_time_finish`) USING BTREE,\n    INDEX `I_planet_powerup_time` (`powerup_planet_id`, `powerup_unit_id`, `powerup_time_start`,\n                                   `powerup_time_finish`) USING BTREE,\n    CONSTRAINT `FK_powerup_planet_id` FOREIGN KEY (`powerup_planet_id`) REFERENCES `sn_planets` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,\n    CONSTRAINT `FK_powerup_user_id` FOREIGN KEY (`powerup_user_id`) REFERENCES `sn_users` (`id`) ON DELETE CASCADE ON UPDATE CASCADE\n) ENGINE = InnoDB\n  AUTO_INCREMENT = 1\n  CHARACTER SET = utf8\n  COLLATE = utf8_general_ci\n  ROW_FORMAT = Dynamic;\n\n-- ----------------------------\n-- Table structure for sn_que\n-- ----------------------------\nDROP TABLE IF EXISTS `sn_que`;\nCREATE TABLE `sn_que`\n(\n    `que_id`                bigint(20) UNSIGNED                                     NOT NULL AUTO_INCREMENT COMMENT 'Internal que id',\n    `que_player_id`         bigint(20) UNSIGNED                                     NULL     DEFAULT NULL COMMENT 'Que owner ID',\n    `que_planet_id`         bigint(20) UNSIGNED                                     NULL     DEFAULT NULL COMMENT 'Which planet this que item belongs',\n    `que_planet_id_origin`  bigint(20) UNSIGNED                                     NULL     DEFAULT NULL COMMENT 'Planet spawner ID',\n    `que_type`              tinyint(1) UNSIGNED                                     NOT NULL DEFAULT 0 COMMENT 'Que type',\n    `que_time_left`         decimal(20, 5) UNSIGNED                                 NOT NULL DEFAULT 0.00000 COMMENT 'Build time left from last activity',\n    `que_unit_id`           bigint(20) UNSIGNED                                     NOT NULL DEFAULT 0 COMMENT 'Unit ID',\n    `que_unit_amount`       bigint(20) UNSIGNED                                     NOT NULL DEFAULT 0 COMMENT 'Amount left to build',\n    `que_unit_mode`         tinyint(1)                                              NOT NULL DEFAULT 0 COMMENT 'Build/Destroy',\n    `que_unit_level`        int(10) UNSIGNED                                        NOT NULL DEFAULT 0 COMMENT 'Unit level. Informational field',\n    `que_unit_time`         decimal(20, 5)                                          NOT NULL DEFAULT 0.00000 COMMENT 'Time to build one unit. Informational field',\n    `que_unit_price`        varchar(128) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '' COMMENT 'Price per unit - for correct trim/clear in case of global price events',\n    `que_unit_one_time_raw` decimal(20, 5)                                          NOT NULL DEFAULT 0.00000,\n    PRIMARY KEY (`que_id`) USING BTREE,\n    UNIQUE INDEX `que_id` (`que_id`) USING BTREE,\n    INDEX `I_que_player_type_planet` (`que_player_id`, `que_type`, `que_planet_id`, `que_id`) USING BTREE,\n    INDEX `I_que_player_type` (`que_player_id`, `que_type`, `que_id`) USING BTREE,\n    INDEX `I_que_planet_id` (`que_planet_id`) USING BTREE,\n    INDEX `FK_que_planet_id_origin` (`que_planet_id_origin`) USING BTREE,\n    CONSTRAINT `FK_que_planet_id` FOREIGN KEY (`que_planet_id`) REFERENCES `sn_planets` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,\n    CONSTRAINT `FK_que_planet_id_origin` FOREIGN KEY (`que_planet_id_origin`) REFERENCES `sn_planets` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,\n    CONSTRAINT `FK_que_player_id` FOREIGN KEY (`que_player_id`) REFERENCES `sn_users` (`id`) ON DELETE CASCADE ON UPDATE CASCADE\n) ENGINE = InnoDB\n  AUTO_INCREMENT = 1\n  CHARACTER SET = utf8\n  COLLATE = utf8_general_ci\n  ROW_FORMAT = Dynamic;\n\n-- ----------------------------\n-- Table structure for sn_quest\n-- ----------------------------\nDROP TABLE IF EXISTS `sn_quest`;\nCREATE TABLE `sn_quest`\n(\n    `quest_id`          bigint(20) UNSIGNED                                     NOT NULL AUTO_INCREMENT,\n    `quest_name`        varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL     DEFAULT NULL,\n    `quest_description` text CHARACTER SET utf8 COLLATE utf8_general_ci         NULL,\n    `quest_conditions`  text CHARACTER SET utf8 COLLATE utf8_general_ci         NULL,\n    `quest_rewards`     text CHARACTER SET utf8 COLLATE utf8_general_ci         NULL,\n    `quest_type`        tinyint(4)                                              NULL     DEFAULT NULL,\n    `quest_order`       int(11)                                                 NOT NULL DEFAULT 0,\n    PRIMARY KEY (`quest_id`) USING BTREE,\n    UNIQUE INDEX `quest_id` (`quest_id`) USING BTREE,\n    INDEX `quest_type` (`quest_type`, `quest_order`) USING BTREE\n) ENGINE = InnoDB\n  AUTO_INCREMENT = 1\n  CHARACTER SET = utf8\n  COLLATE = utf8_general_ci\n  ROW_FORMAT = Dynamic;\n\n-- ----------------------------\n-- Table structure for sn_quest_status\n-- ----------------------------\nDROP TABLE IF EXISTS `sn_quest_status`;\nCREATE TABLE `sn_quest_status`\n(\n    `quest_status_id`       bigint(20) UNSIGNED                                     NOT NULL AUTO_INCREMENT,\n    `quest_status_quest_id` bigint(20) UNSIGNED                                     NULL     DEFAULT NULL,\n    `quest_status_user_id`  bigint(20) UNSIGNED                                     NOT NULL DEFAULT 0,\n    `quest_status_progress` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '',\n    `quest_status_status`   tinyint(4)                                              NOT NULL DEFAULT 1,\n    PRIMARY KEY (`quest_status_id`) USING BTREE,\n    UNIQUE INDEX `quest_status_id` (`quest_status_id`) USING BTREE,\n    INDEX `quest_status_user_id` (`quest_status_user_id`, `quest_status_quest_id`, `quest_status_status`) USING BTREE,\n    INDEX `FK_quest_status_quest_id` (`quest_status_quest_id`) USING BTREE,\n    CONSTRAINT `FK_quest_status_quest_id` FOREIGN KEY (`quest_status_quest_id`) REFERENCES `sn_quest` (`quest_id`) ON DELETE CASCADE ON UPDATE CASCADE,\n    CONSTRAINT `FK_quest_status_user_id` FOREIGN KEY (`quest_status_user_id`) REFERENCES `sn_users` (`id`) ON DELETE CASCADE ON UPDATE CASCADE\n) ENGINE = InnoDB\n  AUTO_INCREMENT = 1\n  CHARACTER SET = utf8\n  COLLATE = utf8_general_ci\n  ROW_FORMAT = Dynamic;\n\n-- ----------------------------\n-- Table structure for sn_referrals\n-- ----------------------------\nDROP TABLE IF EXISTS `sn_referrals`;\nCREATE TABLE `sn_referrals`\n(\n    `id`          bigint(20) UNSIGNED NOT NULL DEFAULT 0,\n    `id_partner`  bigint(20) UNSIGNED NULL     DEFAULT NULL,\n    `dark_matter` decimal(65, 0)      NOT NULL DEFAULT 0,\n    PRIMARY KEY (`id`) USING BTREE,\n    INDEX `id_partner` (`id_partner`) USING BTREE,\n    CONSTRAINT `FK_referrals_id` FOREIGN KEY (`id`) REFERENCES `sn_users` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,\n    CONSTRAINT `FK_referrals_id_partner` FOREIGN KEY (`id_partner`) REFERENCES `sn_users` (`id`) ON DELETE CASCADE ON UPDATE CASCADE\n) ENGINE = InnoDB\n  CHARACTER SET = utf8\n  COLLATE = utf8_general_ci\n  ROW_FORMAT = Dynamic;\n\n-- ----------------------------\n-- Table structure for sn_security_browser\n-- ----------------------------\nDROP TABLE IF EXISTS `sn_security_browser`;\nCREATE TABLE `sn_security_browser`\n(\n    `browser_id`         bigint(20) UNSIGNED                                     NOT NULL AUTO_INCREMENT,\n    `browser_user_agent` varchar(250) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL DEFAULT '',\n    `timestamp`          timestamp(0)                                            NOT NULL DEFAULT CURRENT_TIMESTAMP(0),\n    PRIMARY KEY (`browser_id`) USING BTREE,\n    INDEX `I_browser_user_agent` (`browser_user_agent`) USING BTREE\n) ENGINE = InnoDB\n  AUTO_INCREMENT = 1\n  CHARACTER SET = latin1\n  COLLATE = latin1_bin\n  ROW_FORMAT = Dynamic;\n\n-- ----------------------------\n-- Table structure for sn_security_device\n-- ----------------------------\nDROP TABLE IF EXISTS `sn_security_device`;\nCREATE TABLE `sn_security_device`\n(\n    `device_id`     bigint(20) UNSIGNED                              NOT NULL AUTO_INCREMENT,\n    `device_cypher` char(16) CHARACTER SET latin1 COLLATE latin1_bin NOT NULL DEFAULT '',\n    `timestamp`     timestamp(0)                                     NOT NULL DEFAULT CURRENT_TIMESTAMP(0),\n    PRIMARY KEY (`device_id`) USING BTREE,\n    INDEX `I_device_cypher` (`device_cypher`) USING BTREE\n) ENGINE = InnoDB\n  AUTO_INCREMENT = 1\n  CHARACTER SET = latin1\n  COLLATE = latin1_bin\n  ROW_FORMAT = Dynamic;\n\n-- ----------------------------\n-- Table structure for sn_security_player_entry\n-- ----------------------------\nDROP TABLE IF EXISTS `sn_security_player_entry`;\nCREATE TABLE `sn_security_player_entry`\n(\n    `id`          bigint(20) UNSIGNED                                  NOT NULL AUTO_INCREMENT,\n    `device_id`   bigint(20) UNSIGNED                                  NOT NULL DEFAULT 0,\n    `browser_id`  bigint(20) UNSIGNED                                  NOT NULL DEFAULT 0,\n    `user_ip`     int(10) UNSIGNED                                     NOT NULL DEFAULT 0,\n    `user_proxy`  varchar(255) CHARACTER SET latin1 COLLATE latin1_bin NOT NULL DEFAULT '',\n    `first_visit` timestamp(0)                                         NOT NULL DEFAULT CURRENT_TIMESTAMP(0),\n    PRIMARY KEY (`id`) USING BTREE,\n    UNIQUE INDEX `I_player_entry_unique` (`device_id`, `browser_id`, `user_ip`, `user_proxy`) USING BTREE,\n    INDEX `I_player_entry_browser_id` (`browser_id`) USING BTREE\n) ENGINE = InnoDB\n  AUTO_INCREMENT = 1\n  CHARACTER SET = latin1\n  COLLATE = latin1_bin\n  ROW_FORMAT = Dynamic;\n\n-- ----------------------------\n-- Table structure for sn_security_query_strings\n-- ----------------------------\nDROP TABLE IF EXISTS `sn_security_query_strings`;\nCREATE TABLE `sn_security_query_strings`\n(\n    `id`           bigint(20) UNSIGNED                                     NOT NULL AUTO_INCREMENT,\n    `query_string` varchar(250) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '',\n    PRIMARY KEY (`id`) USING BTREE,\n    UNIQUE INDEX `I_query_string` (`query_string`) USING BTREE\n) ENGINE = InnoDB\n  AUTO_INCREMENT = 1\n  CHARACTER SET = utf8\n  COLLATE = utf8_unicode_ci\n  ROW_FORMAT = Dynamic;\n\n-- ----------------------------\n-- Table structure for sn_security_url\n-- ----------------------------\nDROP TABLE IF EXISTS `sn_security_url`;\nCREATE TABLE `sn_security_url`\n(\n    `url_id`     int(10) UNSIGNED                                        NOT NULL AUTO_INCREMENT,\n    `url_string` varchar(250) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '',\n    PRIMARY KEY (`url_id`) USING BTREE,\n    UNIQUE INDEX `I_url_string` (`url_string`) USING BTREE\n) ENGINE = InnoDB\n  AUTO_INCREMENT = 1\n  CHARACTER SET = latin1\n  COLLATE = latin1_bin\n  ROW_FORMAT = Dynamic;\n\n-- ----------------------------\n-- Table structure for sn_server_patches\n-- ----------------------------\nDROP TABLE IF EXISTS `sn_server_patches`;\nCREATE TABLE `sn_server_patches`\n(\n    `id`      int(10) UNSIGNED NOT NULL COMMENT 'Patch internal ID',\n    `applied` timestamp(0)     NOT NULL DEFAULT CURRENT_TIMESTAMP(0),\n    PRIMARY KEY (`id`) USING BTREE,\n    INDEX `I_applied` (`applied`) USING BTREE\n) ENGINE = InnoDB\n  CHARACTER SET = utf8\n  COLLATE = utf8_unicode_ci\n  ROW_FORMAT = Dynamic;\n\n-- ----------------------------\n-- Table structure for sn_statpoints\n-- ----------------------------\nDROP TABLE IF EXISTS `sn_statpoints`;\nCREATE TABLE `sn_statpoints`\n(\n    `stat_date`      int(11)                 NOT NULL DEFAULT 0,\n    `id_owner`       bigint(20) UNSIGNED     NULL     DEFAULT NULL,\n    `id_ally`        bigint(20) UNSIGNED     NULL     DEFAULT NULL,\n    `stat_type`      tinyint(3) UNSIGNED     NULL     DEFAULT 0,\n    `stat_code`      tinyint(3) UNSIGNED     NOT NULL DEFAULT 0,\n    `tech_rank`      int(11) UNSIGNED        NOT NULL DEFAULT 0,\n    `tech_old_rank`  int(11) UNSIGNED        NOT NULL DEFAULT 0,\n    `tech_points`    decimal(65, 0) UNSIGNED NOT NULL DEFAULT 0,\n    `tech_count`     decimal(65, 0) UNSIGNED NOT NULL DEFAULT 0,\n    `build_rank`     int(11) UNSIGNED        NOT NULL DEFAULT 0,\n    `build_old_rank` int(11) UNSIGNED        NOT NULL DEFAULT 0,\n    `build_points`   decimal(65, 0) UNSIGNED NOT NULL DEFAULT 0,\n    `build_count`    decimal(65, 0) UNSIGNED NOT NULL DEFAULT 0,\n    `defs_rank`      int(11) UNSIGNED        NOT NULL DEFAULT 0,\n    `defs_old_rank`  int(11) UNSIGNED        NOT NULL DEFAULT 0,\n    `defs_points`    decimal(65, 0) UNSIGNED NOT NULL DEFAULT 0,\n    `defs_count`     decimal(65, 0) UNSIGNED NOT NULL DEFAULT 0,\n    `fleet_rank`     int(11) UNSIGNED        NOT NULL DEFAULT 0,\n    `fleet_old_rank` int(11) UNSIGNED        NOT NULL DEFAULT 0,\n    `fleet_points`   decimal(65, 0) UNSIGNED NOT NULL DEFAULT 0,\n    `fleet_count`    decimal(65, 0) UNSIGNED NOT NULL DEFAULT 0,\n    `res_rank`       int(11) UNSIGNED        NULL     DEFAULT 0 COMMENT 'Rank by resources',\n    `res_old_rank`   int(11) UNSIGNED        NULL     DEFAULT 0 COMMENT 'Old rank by resources',\n    `res_points`     decimal(65, 0) UNSIGNED NULL     DEFAULT 0 COMMENT 'Resource stat points',\n    `res_count`      decimal(65, 0) UNSIGNED NULL     DEFAULT 0 COMMENT 'Resource count',\n    `total_rank`     int(11) UNSIGNED        NOT NULL DEFAULT 0,\n    `total_old_rank` int(11) UNSIGNED        NOT NULL DEFAULT 0,\n    `total_points`   decimal(65, 0) UNSIGNED NOT NULL DEFAULT 0,\n    `total_count`    decimal(65, 0) UNSIGNED NOT NULL DEFAULT 0,\n    INDEX `TECH` (`tech_points`) USING BTREE,\n    INDEX `BUILDS` (`build_points`) USING BTREE,\n    INDEX `DEFS` (`defs_points`) USING BTREE,\n    INDEX `FLEET` (`fleet_points`) USING BTREE,\n    INDEX `TOTAL` (`total_points`) USING BTREE,\n    INDEX `i_stats_owner` (`id_owner`, `stat_type`, `stat_code`, `tech_rank`, `build_rank`, `defs_rank`, `fleet_rank`,\n                           `total_rank`) USING BTREE,\n    INDEX `I_stats_id_ally` (`id_ally`, `stat_type`, `stat_code`) USING BTREE,\n    INDEX `I_stats_type_code` (`stat_type`, `stat_code`) USING BTREE,\n    CONSTRAINT `FK_stats_id_ally` FOREIGN KEY (`id_ally`) REFERENCES `sn_alliance` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,\n    CONSTRAINT `FK_stats_id_owner` FOREIGN KEY (`id_owner`) REFERENCES `sn_users` (`id`) ON DELETE CASCADE ON UPDATE CASCADE\n) ENGINE = InnoDB\n  CHARACTER SET = utf8\n  COLLATE = utf8_general_ci\n  ROW_FORMAT = Dynamic;\n\n-- ----------------------------\n-- Table structure for sn_survey\n-- ----------------------------\nDROP TABLE IF EXISTS `sn_survey`;\nCREATE TABLE `sn_survey`\n(\n    `survey_id`          int(10) UNSIGNED                                        NOT NULL AUTO_INCREMENT,\n    `survey_announce_id` bigint(11) UNSIGNED                                     NULL DEFAULT NULL,\n    `survey_question`    varchar(250) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,\n    `survey_until`       datetime(0)                                             NULL DEFAULT NULL,\n    PRIMARY KEY (`survey_id`) USING BTREE,\n    INDEX `I_survey_announce_id` (`survey_announce_id`) USING BTREE,\n    CONSTRAINT `FK_survey_announce_id` FOREIGN KEY (`survey_announce_id`) REFERENCES `sn_announce` (`idAnnounce`) ON DELETE CASCADE ON UPDATE CASCADE\n) ENGINE = InnoDB\n  AUTO_INCREMENT = 1\n  CHARACTER SET = utf8\n  COLLATE = utf8_general_ci\n  ROW_FORMAT = Dynamic;\n\n-- ----------------------------\n-- Table structure for sn_survey_answers\n-- ----------------------------\nDROP TABLE IF EXISTS `sn_survey_answers`;\nCREATE TABLE `sn_survey_answers`\n(\n    `survey_answer_id`   int(10) UNSIGNED                                        NOT NULL AUTO_INCREMENT,\n    `survey_parent_id`   int(10) UNSIGNED                                        NULL DEFAULT NULL,\n    `survey_answer_text` varchar(250) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,\n    PRIMARY KEY (`survey_answer_id`) USING BTREE,\n    INDEX `I_survey_answers_survey_parent_id` (`survey_parent_id`) USING BTREE,\n    CONSTRAINT `FK_survey_answers_survey_parent_id` FOREIGN KEY (`survey_parent_id`) REFERENCES `sn_survey` (`survey_id`) ON DELETE CASCADE ON UPDATE CASCADE\n) ENGINE = InnoDB\n  AUTO_INCREMENT = 1\n  CHARACTER SET = utf8\n  COLLATE = utf8_general_ci\n  ROW_FORMAT = Dynamic;\n\n-- ----------------------------\n-- Table structure for sn_survey_votes\n-- ----------------------------\nDROP TABLE IF EXISTS `sn_survey_votes`;\nCREATE TABLE `sn_survey_votes`\n(\n    `survey_vote_id`          int(10) UNSIGNED                                       NOT NULL AUTO_INCREMENT,\n    `survey_parent_id`        int(10) UNSIGNED                                       NULL DEFAULT NULL,\n    `survey_parent_answer_id` int(10) UNSIGNED                                       NULL DEFAULT NULL,\n    `survey_vote_user_id`     bigint(20) UNSIGNED                                    NULL DEFAULT NULL,\n    `survey_vote_user_name`   varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,\n    PRIMARY KEY (`survey_vote_id`) USING BTREE,\n    INDEX `I_survey_votes_survey_parent_id` (`survey_parent_id`) USING BTREE,\n    INDEX `I_survey_votes_survey_parent_answer_id` (`survey_parent_answer_id`) USING BTREE,\n    INDEX `I_survey_votes_user_id` (`survey_vote_user_id`) USING BTREE,\n    CONSTRAINT `FK_survey_votes_survey_parent_answer_id` FOREIGN KEY (`survey_parent_answer_id`) REFERENCES `sn_survey_answers` (`survey_answer_id`) ON DELETE CASCADE ON UPDATE CASCADE,\n    CONSTRAINT `FK_survey_votes_survey_parent_id` FOREIGN KEY (`survey_parent_id`) REFERENCES `sn_survey` (`survey_id`) ON DELETE CASCADE ON UPDATE CASCADE,\n    CONSTRAINT `FK_survey_votes_user_id` FOREIGN KEY (`survey_vote_user_id`) REFERENCES `sn_users` (`id`) ON DELETE SET NULL ON UPDATE CASCADE\n) ENGINE = InnoDB\n  AUTO_INCREMENT = 1\n  CHARACTER SET = utf8\n  COLLATE = utf8_general_ci\n  ROW_FORMAT = Dynamic;\n\n-- ----------------------------\n-- Table structure for sn_text\n-- ----------------------------\nDROP TABLE IF EXISTS `sn_text`;\nCREATE TABLE `sn_text`\n(\n    `id`       bigint(20) UNSIGNED                                     NOT NULL AUTO_INCREMENT,\n    `parent`   bigint(20) UNSIGNED                                     NULL     DEFAULT NULL COMMENT 'Parent record. NULL - no parent',\n    `context`  bigint(20) UNSIGNED                                     NULL     DEFAULT NULL COMMENT 'Tutorial context. NULL - main screen',\n    `prev`     bigint(20) UNSIGNED                                     NULL     DEFAULT NULL COMMENT 'Previous text part. NULL - first part',\n    `next`     bigint(20) UNSIGNED                                     NULL     DEFAULT NULL COMMENT 'Next text part. NULL - final part',\n    `next_alt` bigint(20) UNSIGNED                                     NULL     DEFAULT NULL COMMENT 'Alternative next text part. NULL - no alternative',\n    `title`    varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL DEFAULT '' COMMENT 'Text title',\n    `content`  text CHARACTER SET utf8 COLLATE utf8_unicode_ci         NULL COMMENT 'Content - 64k fits to all!',\n    PRIMARY KEY (`id`) USING BTREE,\n    INDEX `I_text_parent` (`parent`) USING BTREE,\n    INDEX `I_text_prev` (`prev`) USING BTREE,\n    INDEX `I_text_next` (`next`) USING BTREE,\n    INDEX `I_text_next_alt` (`next_alt`) USING BTREE,\n    CONSTRAINT `FK_text_next` FOREIGN KEY (`next`) REFERENCES `sn_text` (`id`) ON DELETE SET NULL ON UPDATE CASCADE,\n    CONSTRAINT `FK_text_next_alt` FOREIGN KEY (`next_alt`) REFERENCES `sn_text` (`id`) ON DELETE SET NULL ON UPDATE CASCADE,\n    CONSTRAINT `FK_text_parent` FOREIGN KEY (`parent`) REFERENCES `sn_text` (`id`) ON DELETE SET NULL ON UPDATE CASCADE,\n    CONSTRAINT `FK_text_prev` FOREIGN KEY (`prev`) REFERENCES `sn_text` (`id`) ON DELETE SET NULL ON UPDATE CASCADE\n) ENGINE = InnoDB\n  AUTO_INCREMENT = 1\n  CHARACTER SET = utf8\n  COLLATE = utf8_unicode_ci\n  ROW_FORMAT = Dynamic;\n\n-- ----------------------------\n-- Table structure for sn_ube_report\n-- ----------------------------\nDROP TABLE IF EXISTS `sn_ube_report`;\nCREATE TABLE `sn_ube_report`\n(\n    `ube_report_id`                      bigint(20) UNSIGNED                                    NOT NULL AUTO_INCREMENT COMMENT 'Report ID',\n    `ube_report_cypher`                  char(32) CHARACTER SET utf8 COLLATE utf8_general_ci    NOT NULL DEFAULT '' COMMENT '16 char secret report ID',\n    `ube_report_time_combat`             datetime(0)                                            NOT NULL COMMENT 'Combat time',\n    `ube_report_time_process`            timestamp(0)                                           NOT NULL DEFAULT CURRENT_TIMESTAMP(0) COMMENT 'Time when combat was processed',\n    `ube_report_time_spent`              decimal(11, 8) UNSIGNED                                NOT NULL DEFAULT 0.00000000 COMMENT 'Time in seconds spent for combat calculations',\n    `ube_report_mission_type`            tinyint(1) UNSIGNED                                    NOT NULL DEFAULT 0 COMMENT 'Mission type',\n    `ube_report_combat_admin`            tinyint(1) UNSIGNED                                    NOT NULL DEFAULT 0 COMMENT 'Does admin participates in combat?',\n    `ube_report_combat_result`           tinyint(1)                                             NOT NULL DEFAULT 0 COMMENT 'Combat outcome',\n    `ube_report_combat_sfr`              tinyint(1)                                             NOT NULL DEFAULT 0 COMMENT 'Small Fleet Reconnaissance',\n    `ube_report_planet_id`               bigint(20) UNSIGNED                                    NOT NULL DEFAULT 0 COMMENT 'Player planet ID',\n    `ube_report_planet_name`             varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT 'Planet' COMMENT 'Player planet name',\n    `ube_report_planet_size`             smallint(5) UNSIGNED                                   NOT NULL DEFAULT 0 COMMENT 'Player diameter',\n    `ube_report_planet_galaxy`           smallint(5) UNSIGNED                                   NOT NULL DEFAULT 0 COMMENT 'Player planet coordinate galaxy',\n    `ube_report_planet_system`           smallint(5) UNSIGNED                                   NOT NULL DEFAULT 0 COMMENT 'Player planet coordinate system',\n    `ube_report_planet_planet`           smallint(5) UNSIGNED                                   NOT NULL DEFAULT 0 COMMENT 'Player planet coordinate planet',\n    `ube_report_planet_planet_type`      tinyint(4)                                             NOT NULL DEFAULT 1 COMMENT 'Player planet type',\n    `ube_report_moon`                    tinyint(1)                                             NOT NULL DEFAULT 0 COMMENT 'Moon result: was, none, failed, created, destroyed',\n    `ube_report_moon_chance`             decimal(9, 6) UNSIGNED                                 NOT NULL DEFAULT 0.000000 COMMENT 'Moon creation chance',\n    `ube_report_moon_size`               smallint(5) UNSIGNED                                   NOT NULL DEFAULT 0 COMMENT 'Moon size',\n    `ube_report_moon_reapers`            tinyint(1)                                             NOT NULL DEFAULT 0 COMMENT 'Moon reapers result: none, died, survived',\n    `ube_report_moon_destroy_chance`     tinyint(1)                                             NOT NULL DEFAULT 0 COMMENT 'Moon destroy chance',\n    `ube_report_moon_reapers_die_chance` tinyint(1)                                             NOT NULL DEFAULT 0 COMMENT 'Moon reapers die chance',\n    `ube_report_debris_metal`            decimal(65, 0) UNSIGNED                                NOT NULL DEFAULT 0 COMMENT 'Metal debris',\n    `ube_report_debris_crystal`          decimal(65, 0) UNSIGNED                                NOT NULL DEFAULT 0 COMMENT 'Crystal debris',\n    `ube_report_capture_result`          tinyint(3) UNSIGNED                                    NOT NULL DEFAULT 0,\n    `ube_report_debris_total_in_metal`   decimal(65, 0) UNSIGNED                                NOT NULL DEFAULT 0 COMMENT 'Total debris in metal',\n    PRIMARY KEY (`ube_report_id`) USING BTREE,\n    UNIQUE INDEX `ube_report_id` (`ube_report_id`) USING BTREE,\n    INDEX `I_ube_report_cypher` (`ube_report_cypher`) USING BTREE,\n    INDEX `I_ube_report_time_combat` (`ube_report_time_combat`) USING BTREE,\n    INDEX `I_ube_report_time_debris_id` (`ube_report_time_process`, `ube_report_debris_total_in_metal`,\n                                         `ube_report_id`) USING BTREE\n) ENGINE = InnoDB\n  AUTO_INCREMENT = 1\n  CHARACTER SET = utf8\n  COLLATE = utf8_general_ci\n  ROW_FORMAT = Dynamic;\n\n-- ----------------------------\n-- Table structure for sn_ube_report_fleet\n-- ----------------------------\nDROP TABLE IF EXISTS `sn_ube_report_fleet`;\nCREATE TABLE `sn_ube_report_fleet`\n(\n    `ube_report_fleet_id`                 bigint(20) UNSIGNED                                    NOT NULL AUTO_INCREMENT COMMENT 'Record DB ID',\n    `ube_report_id`                       bigint(20) UNSIGNED                                    NOT NULL DEFAULT 0 COMMENT 'Report ID',\n    `ube_report_fleet_player_id`          bigint(20) UNSIGNED                                    NOT NULL DEFAULT 0 COMMENT 'Owner ID',\n    `ube_report_fleet_fleet_id`           bigint(20) UNSIGNED                                    NOT NULL DEFAULT 0 COMMENT 'Fleet ID',\n    `ube_report_fleet_planet_id`          bigint(20) UNSIGNED                                    NOT NULL DEFAULT 0 COMMENT 'Player attack bonus',\n    `ube_report_fleet_planet_name`        varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT 'Planet' COMMENT 'Player planet name',\n    `ube_report_fleet_planet_galaxy`      smallint(5) UNSIGNED                                   NOT NULL DEFAULT 0 COMMENT 'Player planet coordinate galaxy',\n    `ube_report_fleet_planet_system`      smallint(5) UNSIGNED                                   NOT NULL DEFAULT 0 COMMENT 'Player planet coordinate system',\n    `ube_report_fleet_planet_planet`      smallint(5) UNSIGNED                                   NOT NULL DEFAULT 0 COMMENT 'Player planet coordinate planet',\n    `ube_report_fleet_planet_planet_type` tinyint(4)                                             NOT NULL DEFAULT 1 COMMENT 'Player planet type',\n    `ube_report_fleet_bonus_attack`       decimal(11, 2)                                         NOT NULL DEFAULT 0.00 COMMENT 'Fleet attack bonus',\n    `ube_report_fleet_bonus_shield`       decimal(11, 2)                                         NOT NULL DEFAULT 0.00 COMMENT 'Fleet shield bonus',\n    `ube_report_fleet_bonus_armor`        decimal(11, 2)                                         NOT NULL DEFAULT 0.00 COMMENT 'Fleet armor bonus',\n    `ube_report_fleet_resource_metal`     decimal(65, 0) UNSIGNED                                NOT NULL DEFAULT 0 COMMENT 'Fleet metal amount',\n    `ube_report_fleet_resource_crystal`   decimal(65, 0) UNSIGNED                                NOT NULL DEFAULT 0 COMMENT 'Fleet crystal amount',\n    `ube_report_fleet_resource_deuterium` decimal(65, 0) UNSIGNED                                NOT NULL DEFAULT 0 COMMENT 'Fleet deuterium amount',\n    PRIMARY KEY (`ube_report_fleet_id`) USING BTREE,\n    UNIQUE INDEX `ube_report_fleet_id` (`ube_report_fleet_id`) USING BTREE,\n    INDEX `FK_ube_report_fleet_ube_report` (`ube_report_id`) USING BTREE,\n    CONSTRAINT `FK_ube_report_fleet_ube_report` FOREIGN KEY (`ube_report_id`) REFERENCES `sn_ube_report` (`ube_report_id`) ON DELETE CASCADE ON UPDATE CASCADE\n) ENGINE = InnoDB\n  AUTO_INCREMENT = 1\n  CHARACTER SET = utf8\n  COLLATE = utf8_general_ci\n  ROW_FORMAT = Dynamic;\n\n-- ----------------------------\n-- Table structure for sn_ube_report_outcome_fleet\n-- ----------------------------\nDROP TABLE IF EXISTS `sn_ube_report_outcome_fleet`;\nCREATE TABLE `sn_ube_report_outcome_fleet`\n(\n    `ube_report_outcome_fleet_id`                         bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 'Record DB ID',\n    `ube_report_id`                                       bigint(20) UNSIGNED NOT NULL DEFAULT 0 COMMENT 'Report ID',\n    `ube_report_outcome_fleet_fleet_id`                   bigint(20) UNSIGNED NOT NULL DEFAULT 0 COMMENT 'Fleet ID',\n    `ube_report_outcome_fleet_resource_lost_metal`        decimal(65, 0)      NOT NULL DEFAULT 0 COMMENT 'Fleet metal loss from units',\n    `ube_report_outcome_fleet_resource_lost_crystal`      decimal(65, 0)      NOT NULL DEFAULT 0 COMMENT 'Fleet crystal loss from units',\n    `ube_report_outcome_fleet_resource_lost_deuterium`    decimal(65, 0)      NOT NULL DEFAULT 0 COMMENT 'Fleet deuterium loss from units',\n    `ube_report_outcome_fleet_resource_dropped_metal`     decimal(65, 0)      NOT NULL DEFAULT 0 COMMENT 'Fleet metal dropped due reduced cargo',\n    `ube_report_outcome_fleet_resource_dropped_crystal`   decimal(65, 0)      NOT NULL DEFAULT 0 COMMENT 'Fleet crystal dropped due reduced cargo',\n    `ube_report_outcome_fleet_resource_dropped_deuterium` decimal(65, 0)      NOT NULL DEFAULT 0 COMMENT 'Fleet deuterium dropped due reduced cargo',\n    `ube_report_outcome_fleet_resource_loot_metal`        decimal(65, 0)      NOT NULL DEFAULT 0 COMMENT 'Looted/Lost from loot metal',\n    `ube_report_outcome_fleet_resource_loot_crystal`      decimal(65, 0)      NOT NULL DEFAULT 0 COMMENT 'Looted/Lost from loot crystal',\n    `ube_report_outcome_fleet_resource_loot_deuterium`    decimal(65, 0)      NOT NULL DEFAULT 0 COMMENT 'Looted/Lost from loot deuterium',\n    `ube_report_outcome_fleet_resource_lost_in_metal`     decimal(65, 0)      NOT NULL DEFAULT 0 COMMENT 'Fleet total resource loss in metal',\n    PRIMARY KEY (`ube_report_outcome_fleet_id`) USING BTREE,\n    UNIQUE INDEX `ube_report_outcome_fleet_id` (`ube_report_outcome_fleet_id`) USING BTREE,\n    INDEX `I_ube_report_outcome_fleet_report_fleet` (`ube_report_id`, `ube_report_outcome_fleet_fleet_id`) USING BTREE,\n    CONSTRAINT `FK_ube_report_outcome_fleet_ube_report` FOREIGN KEY (`ube_report_id`) REFERENCES `sn_ube_report` (`ube_report_id`) ON DELETE CASCADE ON UPDATE CASCADE\n) ENGINE = InnoDB\n  AUTO_INCREMENT = 1\n  CHARACTER SET = utf8\n  COLLATE = utf8_general_ci\n  ROW_FORMAT = Dynamic;\n\n-- ----------------------------\n-- Table structure for sn_ube_report_outcome_unit\n-- ----------------------------\nDROP TABLE IF EXISTS `sn_ube_report_outcome_unit`;\nCREATE TABLE `sn_ube_report_outcome_unit`\n(\n    `ube_report_outcome_unit_id`         bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 'Record DB ID',\n    `ube_report_id`                      bigint(20) UNSIGNED NOT NULL DEFAULT 0 COMMENT 'Report ID',\n    `ube_report_outcome_unit_fleet_id`   bigint(20) UNSIGNED NOT NULL DEFAULT 0 COMMENT 'Fleet ID',\n    `ube_report_outcome_unit_unit_id`    bigint(20) UNSIGNED NOT NULL DEFAULT 0 COMMENT 'Unit ID',\n    `ube_report_outcome_unit_restored`   decimal(65, 0)      NOT NULL DEFAULT 0 COMMENT 'Unit restored',\n    `ube_report_outcome_unit_lost`       decimal(65, 0)      NOT NULL DEFAULT 0 COMMENT 'Unit lost',\n    `ube_report_outcome_unit_sort_order` bigint(20) UNSIGNED NOT NULL DEFAULT 0 COMMENT 'Unit pass-through sort order to maintain same output',\n    PRIMARY KEY (`ube_report_outcome_unit_id`) USING BTREE,\n    UNIQUE INDEX `ube_report_outcome_unit_id` (`ube_report_outcome_unit_id`) USING BTREE,\n    INDEX `I_ube_report_outcome_unit_report_order` (`ube_report_id`, `ube_report_outcome_unit_sort_order`) USING BTREE,\n    CONSTRAINT `FK_ube_report_outcome_unit_ube_report` FOREIGN KEY (`ube_report_id`) REFERENCES `sn_ube_report` (`ube_report_id`) ON DELETE CASCADE ON UPDATE CASCADE\n) ENGINE = InnoDB\n  AUTO_INCREMENT = 1\n  CHARACTER SET = utf8\n  COLLATE = utf8_general_ci\n  ROW_FORMAT = Dynamic;\n\n-- ----------------------------\n-- Table structure for sn_ube_report_player\n-- ----------------------------\nDROP TABLE IF EXISTS `sn_ube_report_player`;\nCREATE TABLE `sn_ube_report_player`\n(\n    `ube_report_player_id`           bigint(20) UNSIGNED                                    NOT NULL AUTO_INCREMENT COMMENT 'Record ID',\n    `ube_report_id`                  bigint(20) UNSIGNED                                    NOT NULL DEFAULT 0 COMMENT 'Report ID',\n    `ube_report_player_player_id`    bigint(20) UNSIGNED                                    NOT NULL DEFAULT 0 COMMENT 'Player ID',\n    `ube_report_player_name`         varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '' COMMENT 'Player name',\n    `ube_report_player_attacker`     tinyint(1)                                             NOT NULL DEFAULT 0 COMMENT 'Is player an attacker?',\n    `ube_report_player_bonus_attack` decimal(11, 2)                                         NOT NULL DEFAULT 0.00 COMMENT 'Player attack bonus',\n    `ube_report_player_bonus_shield` decimal(11, 2)                                         NOT NULL DEFAULT 0.00 COMMENT 'Player shield bonus',\n    `ube_report_player_bonus_armor`  decimal(11, 2)                                         NOT NULL DEFAULT 0.00 COMMENT 'Player armor bonus',\n    PRIMARY KEY (`ube_report_player_id`) USING BTREE,\n    UNIQUE INDEX `ube_report_player_id` (`ube_report_player_id`) USING BTREE,\n    INDEX `I_ube_report_player_player_id` (`ube_report_player_player_id`) USING BTREE,\n    INDEX `FK_ube_report_player_ube_report` (`ube_report_id`) USING BTREE,\n    CONSTRAINT `FK_ube_report_player_ube_report` FOREIGN KEY (`ube_report_id`) REFERENCES `sn_ube_report` (`ube_report_id`) ON DELETE CASCADE ON UPDATE CASCADE\n) ENGINE = InnoDB\n  AUTO_INCREMENT = 1\n  CHARACTER SET = utf8\n  COLLATE = utf8_general_ci\n  ROW_FORMAT = Dynamic;\n\n-- ----------------------------\n-- Table structure for sn_ube_report_unit\n-- ----------------------------\nDROP TABLE IF EXISTS `sn_ube_report_unit`;\nCREATE TABLE `sn_ube_report_unit`\n(\n    `ube_report_unit_id`          bigint(20) UNSIGNED     NOT NULL AUTO_INCREMENT COMMENT 'Record DB ID',\n    `ube_report_id`               bigint(20) UNSIGNED     NOT NULL DEFAULT 0 COMMENT 'Report ID',\n    `ube_report_unit_player_id`   bigint(20) UNSIGNED     NOT NULL DEFAULT 0 COMMENT 'Owner ID',\n    `ube_report_unit_fleet_id`    bigint(20) UNSIGNED     NOT NULL DEFAULT 0 COMMENT 'Fleet ID',\n    `ube_report_unit_round`       tinyint(4)              NOT NULL DEFAULT 0 COMMENT 'Round number',\n    `ube_report_unit_unit_id`     bigint(20) UNSIGNED     NOT NULL DEFAULT 0 COMMENT 'Unit ID',\n    `ube_report_unit_count`       decimal(65, 0) UNSIGNED NOT NULL DEFAULT 0 COMMENT 'Unit count',\n    `ube_report_unit_boom`        smallint(5) UNSIGNED    NOT NULL DEFAULT 0 COMMENT 'Unit booms',\n    `ube_report_unit_attack`      decimal(65, 0) UNSIGNED NOT NULL DEFAULT 0 COMMENT 'Unit attack',\n    `ube_report_unit_shield`      decimal(65, 0) UNSIGNED NOT NULL DEFAULT 0 COMMENT 'Unit shield',\n    `ube_report_unit_armor`       decimal(65, 0) UNSIGNED NOT NULL DEFAULT 0 COMMENT 'Unit armor',\n    `ube_report_unit_attack_base` decimal(65, 0) UNSIGNED NOT NULL DEFAULT 0 COMMENT 'Unit base attack',\n    `ube_report_unit_shield_base` decimal(65, 0) UNSIGNED NOT NULL DEFAULT 0 COMMENT 'Unit base shield',\n    `ube_report_unit_armor_base`  decimal(65, 0) UNSIGNED NOT NULL DEFAULT 0 COMMENT 'Unit base armor',\n    `ube_report_unit_sort_order`  bigint(20) UNSIGNED     NOT NULL DEFAULT 0 COMMENT 'Unit pass-through sort order to maintain same output',\n    PRIMARY KEY (`ube_report_unit_id`) USING BTREE,\n    UNIQUE INDEX `ube_report_unit_id` (`ube_report_unit_id`) USING BTREE,\n    INDEX `I_ube_report_unit_report_round_fleet_order` (`ube_report_id`, `ube_report_unit_round`,\n                                                        `ube_report_unit_fleet_id`,\n                                                        `ube_report_unit_sort_order`) USING BTREE,\n    INDEX `I_ube_report_unit_report_unit_order` (`ube_report_id`, `ube_report_unit_sort_order`) USING BTREE,\n    INDEX `I_ube_report_unit_order` (`ube_report_unit_sort_order`) USING BTREE,\n    CONSTRAINT `FK_ube_report_unit_ube_report` FOREIGN KEY (`ube_report_id`) REFERENCES `sn_ube_report` (`ube_report_id`) ON DELETE CASCADE ON UPDATE CASCADE\n) ENGINE = InnoDB\n  AUTO_INCREMENT = 1\n  CHARACTER SET = utf8\n  COLLATE = utf8_general_ci\n  ROW_FORMAT = Dynamic;\n\n-- ----------------------------\n-- Table structure for sn_unit\n-- ----------------------------\nDROP TABLE IF EXISTS `sn_unit`;\nCREATE TABLE `sn_unit`\n(\n    `unit_id`            bigint(20) UNSIGNED     NOT NULL AUTO_INCREMENT COMMENT 'Record ID',\n    `unit_player_id`     bigint(20) UNSIGNED     NULL     DEFAULT NULL COMMENT 'Unit owner',\n    `unit_location_type` tinyint(4)              NOT NULL DEFAULT 0 COMMENT 'Location type: universe, user, planet (moon?), fleet',\n    `unit_location_id`   bigint(20) UNSIGNED     NOT NULL DEFAULT 0 COMMENT 'Location ID',\n    `unit_type`          bigint(20) UNSIGNED     NOT NULL DEFAULT 0 COMMENT 'Unit type',\n    `unit_snid`          bigint(20) UNSIGNED     NOT NULL DEFAULT 0 COMMENT 'Unit SuperNova ID',\n    `unit_level`         decimal(65, 0) UNSIGNED NOT NULL DEFAULT 0 COMMENT 'Unit level or count - dependent of unit_type',\n    `unit_time_start`    datetime(0)             NULL     DEFAULT NULL COMMENT 'Unit activation start time',\n    `unit_time_finish`   datetime(0)             NULL     DEFAULT NULL COMMENT 'Unit activation end time',\n    PRIMARY KEY (`unit_id`) USING BTREE,\n    INDEX `I_unit_player_location_snid` (`unit_player_id`, `unit_location_type`, `unit_location_id`,\n                                         `unit_snid`) USING BTREE,\n    INDEX `I_unit_record_search` (`unit_snid`, `unit_player_id`, `unit_level`, `unit_id`) USING BTREE,\n    INDEX `I_unit_location` (`unit_location_type`, `unit_location_id`) USING BTREE,\n    INDEX `I_unit_type_snid` (`unit_type`, `unit_snid`) USING BTREE,\n    CONSTRAINT `FK_unit_player_id` FOREIGN KEY (`unit_player_id`) REFERENCES `sn_users` (`id`) ON DELETE CASCADE ON UPDATE CASCADE\n) ENGINE = InnoDB\n  AUTO_INCREMENT = 1\n  CHARACTER SET = utf8\n  COLLATE = utf8_general_ci\n  ROW_FORMAT = Dynamic;\n\n-- ----------------------------\n-- Table structure for sn_universe\n-- ----------------------------\nDROP TABLE IF EXISTS `sn_universe`;\nCREATE TABLE `sn_universe`\n(\n    `universe_galaxy` smallint(5) UNSIGNED                                   NOT NULL DEFAULT 0,\n    `universe_system` smallint(5) UNSIGNED                                   NOT NULL DEFAULT 0,\n    `universe_name`   varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '',\n    `universe_price`  bigint(20)                                             NOT NULL DEFAULT 0,\n    PRIMARY KEY (`universe_galaxy`, `universe_system`) USING BTREE\n) ENGINE = InnoDB\n  CHARACTER SET = utf8\n  COLLATE = utf8_general_ci\n  ROW_FORMAT = Dynamic;\n\n-- ----------------------------\n-- Table structure for sn_users\n-- ----------------------------\nDROP TABLE IF EXISTS `sn_users`;\nCREATE TABLE `sn_users`\n(\n    `id`                       bigint(20) UNSIGNED                                         NOT NULL AUTO_INCREMENT,\n    `username`                 varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci      NOT NULL DEFAULT '' COMMENT 'Player name',\n    `authlevel`                tinyint(3) UNSIGNED                                         NOT NULL DEFAULT 0,\n    `vacation`                 int(11) UNSIGNED                                            NULL     DEFAULT 0,\n    `banaday`                  int(10) UNSIGNED                                            NOT NULL DEFAULT 0 COMMENT 'User ban status',\n    `dark_matter`              bigint(20)                                                  NULL     DEFAULT 0,\n    `dark_matter_total`        bigint(20)                                                  NOT NULL DEFAULT 0 COMMENT 'Total Dark Matter amount ever gained',\n    `player_rpg_explore_xp`    bigint(20) UNSIGNED                                         NOT NULL DEFAULT 0,\n    `player_rpg_explore_level` bigint(20) UNSIGNED                                         NOT NULL DEFAULT 0,\n    `ally_id`                  bigint(20) UNSIGNED                                         NULL     DEFAULT NULL,\n    `ally_tag`                 varchar(8) CHARACTER SET utf8 COLLATE utf8_general_ci       NULL     DEFAULT NULL,\n    `ally_name`                varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci      NULL     DEFAULT NULL,\n    `ally_register_time`       int(11)                                                     NOT NULL DEFAULT 0,\n    `ally_rank_id`             int(11)                                                     NOT NULL DEFAULT 0,\n    `lvl_minier`               bigint(20) UNSIGNED                                         NOT NULL DEFAULT 1,\n    `xpminier`                 bigint(20) UNSIGNED                                         NULL     DEFAULT 0,\n    `player_rpg_tech_xp`       bigint(20) UNSIGNED                                         NOT NULL DEFAULT 0,\n    `player_rpg_tech_level`    bigint(20) UNSIGNED                                         NOT NULL DEFAULT 0,\n    `lvl_raid`                 bigint(20) UNSIGNED                                         NOT NULL DEFAULT 1,\n    `xpraid`                   bigint(20) UNSIGNED                                         NULL     DEFAULT 0,\n    `raids`                    bigint(20) UNSIGNED                                         NULL     DEFAULT 0,\n    `raidsloose`               bigint(20) UNSIGNED                                         NULL     DEFAULT 0,\n    `raidswin`                 bigint(20) UNSIGNED                                         NULL     DEFAULT 0,\n    `new_message`              int(11)                                                     NOT NULL DEFAULT 0,\n    `mnl_alliance`             int(11)                                                     NOT NULL DEFAULT 0,\n    `mnl_joueur`               int(11)                                                     NOT NULL DEFAULT 0,\n    `mnl_attaque`              int(11)                                                     NOT NULL DEFAULT 0,\n    `mnl_spy`                  int(11)                                                     NOT NULL DEFAULT 0,\n    `mnl_exploit`              int(11)                                                     NOT NULL DEFAULT 0,\n    `mnl_transport`            int(11)                                                     NOT NULL DEFAULT 0,\n    `mnl_expedition`           int(11)                                                     NOT NULL DEFAULT 0,\n    `mnl_buildlist`            int(11)                                                     NOT NULL DEFAULT 0,\n    `msg_admin`                bigint(11) UNSIGNED                                         NULL     DEFAULT 0,\n    `bana`                     int(11)                                                     NULL     DEFAULT NULL,\n    `deltime`                  int(10) UNSIGNED                                            NULL     DEFAULT 0,\n    `news_lastread`            int(10) UNSIGNED                                            NULL     DEFAULT 0,\n    `total_rank`               int(10) UNSIGNED                                            NOT NULL DEFAULT 0,\n    `total_points`             bigint(20) UNSIGNED                                         NOT NULL DEFAULT 0,\n    `password`                 varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci      NOT NULL DEFAULT '',\n    `salt`                     char(16) CHARACTER SET latin1 COLLATE latin1_general_ci     NOT NULL DEFAULT '',\n    `email`                    varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci      NOT NULL DEFAULT '',\n    `email_2`                  varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci      NOT NULL DEFAULT '',\n    `lang`                     varchar(8) CHARACTER SET utf8 COLLATE utf8_general_ci       NOT NULL DEFAULT 'ru',\n    `avatar`                   tinyint(1) UNSIGNED                                         NOT NULL DEFAULT 0,\n    `sign`                     mediumtext CHARACTER SET utf8 COLLATE utf8_general_ci       NULL,\n    `id_planet`                int(11)                                                     NOT NULL DEFAULT 0,\n    `galaxy`                   int(11)                                                     NOT NULL DEFAULT 0,\n    `system`                   int(11)                                                     NOT NULL DEFAULT 0,\n    `planet`                   int(11)                                                     NOT NULL DEFAULT 0,\n    `current_planet`           int(11)                                                     NOT NULL DEFAULT 0,\n    `user_lastip`              varchar(250) CHARACTER SET utf8 COLLATE utf8_general_ci     NULL     DEFAULT NULL COMMENT 'User last IP',\n    `user_last_proxy`          varchar(250) CHARACTER SET utf8 COLLATE utf8_general_ci     NOT NULL DEFAULT '',\n    `user_last_browser_id`     bigint(20) UNSIGNED                                         NULL     DEFAULT NULL,\n    `register_time`            int(10) UNSIGNED                                            NULL     DEFAULT 0,\n    `onlinetime`               int(10) UNSIGNED                                            NULL     DEFAULT 0,\n    `que_processed`            int(11) UNSIGNED                                            NOT NULL DEFAULT 0,\n    `template`                 varchar(64) CHARACTER SET utf8 COLLATE utf8_unicode_ci      NOT NULL DEFAULT 'OpenGame',\n    `skin`                     varchar(64) CHARACTER SET utf8 COLLATE utf8_unicode_ci      NOT NULL DEFAULT 'EpicBlue',\n    `design`                   tinyint(4) UNSIGNED                                         NOT NULL DEFAULT 1,\n    `noipcheck`                tinyint(4) UNSIGNED                                         NOT NULL DEFAULT 1,\n    `options`                  mediumtext CHARACTER SET utf8 COLLATE utf8_general_ci       NULL COMMENT 'Packed user options',\n    `user_as_ally`             bigint(20) UNSIGNED                                         NULL     DEFAULT NULL,\n    `metal`                    decimal(65, 5)                                              NOT NULL DEFAULT 0.00000,\n    `crystal`                  decimal(65, 5)                                              NOT NULL DEFAULT 0.00000,\n    `deuterium`                decimal(65, 5)                                              NOT NULL DEFAULT 0.00000,\n    `user_birthday`            date                                                        NULL     DEFAULT NULL COMMENT 'User birthday',\n    `user_birthday_celebrated` date                                                        NULL     DEFAULT NULL COMMENT 'Last time where user got birthday gift',\n    `player_race`              int(11)                                                     NOT NULL DEFAULT 0 COMMENT 'Player\\'s race',\n    `vacation_next`            int(11)                                                     NOT NULL DEFAULT 0 COMMENT 'Next datetime when player can go on vacation',\n    `metamatter`               bigint(20)                                                  NOT NULL DEFAULT 0 COMMENT 'Metamatter amount',\n    `metamatter_total`         bigint(20)                                                  NOT NULL DEFAULT 0 COMMENT 'Total Metamatter amount ever bought',\n    `admin_protection`         tinyint(3) UNSIGNED                                         NOT NULL DEFAULT 0 COMMENT 'Protection of administration planets',\n    `user_bot`                 tinyint(1) UNSIGNED                                         NOT NULL DEFAULT 0,\n    `gender`                   tinyint(1) UNSIGNED                                         NOT NULL DEFAULT 0,\n    `immortal`                 timestamp(0)                                                NULL     DEFAULT NULL,\n    `parent_account_id`        bigint(20) UNSIGNED                                         NOT NULL DEFAULT 0,\n    `parent_account_global`    bigint(20) UNSIGNED                                         NOT NULL DEFAULT 0,\n    `server_name`              varchar(128) CHARACTER SET latin1 COLLATE latin1_general_ci NOT NULL DEFAULT '',\n    PRIMARY KEY (`id`) USING BTREE,\n    INDEX `i_ally_id` (`ally_id`) USING BTREE,\n    INDEX `i_ally_name` (`ally_name`) USING BTREE,\n    INDEX `i_username` (`username`) USING BTREE,\n    INDEX `i_ally_online` (`ally_id`, `onlinetime`) USING BTREE,\n    INDEX `onlinetime` (`onlinetime`) USING BTREE,\n    INDEX `i_register_time` (`register_time`) USING BTREE,\n    INDEX `FK_users_ally_tag` (`ally_tag`) USING BTREE,\n    INDEX `I_user_user_as_ally` (`user_as_ally`) USING BTREE,\n    INDEX `I_user_birthday` (`user_birthday`, `user_birthday_celebrated`) USING BTREE,\n    INDEX `I_user_id_name` (`id`, `username`) USING BTREE,\n    INDEX `I_users_last_browser_id` (`user_last_browser_id`) USING BTREE,\n    INDEX `I_users_parent_account_id` (`parent_account_id`) USING BTREE,\n    INDEX `I_users_parent_account_global` (`parent_account_global`) USING BTREE,\n    CONSTRAINT `FK_user_user_as_ally` FOREIGN KEY (`user_as_ally`) REFERENCES `sn_alliance` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,\n    CONSTRAINT `FK_users_ally_id` FOREIGN KEY (`ally_id`) REFERENCES `sn_alliance` (`id`) ON DELETE SET NULL ON UPDATE CASCADE,\n    CONSTRAINT `FK_users_ally_name` FOREIGN KEY (`ally_name`) REFERENCES `sn_alliance` (`ally_name`) ON DELETE SET NULL ON UPDATE CASCADE,\n    CONSTRAINT `FK_users_ally_tag` FOREIGN KEY (`ally_tag`) REFERENCES `sn_alliance` (`ally_tag`) ON DELETE SET NULL ON UPDATE CASCADE,\n    CONSTRAINT `FK_users_browser_id` FOREIGN KEY (`user_last_browser_id`) REFERENCES `sn_security_browser` (`browser_id`) ON DELETE SET NULL ON UPDATE CASCADE\n) ENGINE = InnoDB\n  AUTO_INCREMENT = 2\n  CHARACTER SET = utf8\n  COLLATE = utf8_general_ci\n  ROW_FORMAT = Dynamic;\n\n-- ----------------------------\n-- Records of sn_server_patches\n-- ----------------------------\nINSERT INTO `sn_server_patches` VALUES (1, '2018-11-12 07:50:14');\nINSERT INTO `sn_server_patches` VALUES (2, '2018-11-12 07:50:14');\nINSERT INTO `sn_server_patches` VALUES (3, '2018-11-12 07:50:14');\nINSERT INTO `sn_server_patches` VALUES (4, '2018-11-12 07:50:14');\nINSERT INTO `sn_server_patches` VALUES (6, '2020-07-27 13:20:18');\nINSERT INTO `sn_server_patches` VALUES (7, '2020-07-27 13:20:18');\nINSERT INTO `sn_server_patches` VALUES (8, '2020-07-27 13:20:18');\nINSERT INTO `sn_server_patches` VALUES (9, '2020-07-27 13:20:19');\nINSERT INTO `sn_server_patches` VALUES (10, '2020-07-27 13:20:19');\n\n-- ----------------------------\n-- Default server configuration\n-- ----------------------------\nINSERT INTO `sn_config` VALUES ('advGoogleLeftMenuCode', '<script type=\\\"text/javascript\\\"><!--\\r\\ngoogle_ad_client = \\\"pub-1914310741599503\\\";\\r\\n/* oGame */\\r\\ngoogle_ad_slot = \\\"2544836773\\\";\\r\\ngoogle_ad_width = 125;\\r\\ngoogle_ad_height = 125;\\r\\n//-->\\r\\n</script>\\r\\n<script type=\\\"text/javascript\\\"\\r\\nsrc=\\\"http://pagead2.googlesyndication.com/pagead/show_ads.js\\\">\\r\\n</script>\\r\\n');\nINSERT INTO `sn_config` VALUES ('advGoogleLeftMenuIsOn', '1');\nINSERT INTO `sn_config` VALUES ('adv_conversion_code_payment', '');\nINSERT INTO `sn_config` VALUES ('adv_conversion_code_register', '');\nINSERT INTO `sn_config` VALUES ('adv_seo_javascript', '');\nINSERT INTO `sn_config` VALUES ('adv_seo_meta_description', '');\nINSERT INTO `sn_config` VALUES ('adv_seo_meta_keywords', '');\nINSERT INTO `sn_config` VALUES ('ali_bonus_algorithm', '0');\nINSERT INTO `sn_config` VALUES ('ali_bonus_brackets', '10');\nINSERT INTO `sn_config` VALUES ('ali_bonus_brackets_divisor', '50');\nINSERT INTO `sn_config` VALUES ('ali_bonus_divisor', '10000000');\nINSERT INTO `sn_config` VALUES ('ali_bonus_members', '10');\nINSERT INTO `sn_config` VALUES ('allow_buffing', '0');\nINSERT INTO `sn_config` VALUES ('ally_help_weak', '0');\nINSERT INTO `sn_config` VALUES ('avatar_max_height', '128');\nINSERT INTO `sn_config` VALUES ('avatar_max_width', '128');\nINSERT INTO `sn_config` VALUES ('BuildLabWhileRun', '0');\nINSERT INTO `sn_config` VALUES ('chat_highlight_admin', '<span class=\\\"nick_admin\\\">$1</span>');\nINSERT INTO `sn_config` VALUES ('chat_highlight_developer', '<span class=\\\"nick_developer\\\">$1</span>');\nINSERT INTO `sn_config` VALUES ('chat_highlight_moderator', '<font color=green>$1</font>');\nINSERT INTO `sn_config` VALUES ('chat_highlight_operator', '<font color=red>$1</font>');\nINSERT INTO `sn_config` VALUES ('chat_highlight_premium', '<span class=\\\"nick_premium\\\">$1</span>');\nINSERT INTO `sn_config` VALUES ('chat_refresh_rate', '5');\nINSERT INTO `sn_config` VALUES ('chat_timeout', 15 * 60);\nINSERT INTO `sn_config` VALUES ('COOKIE_NAME', 'SuperNova');\nINSERT INTO `sn_config` VALUES ('crystal_basic_income', '20');\nINSERT INTO `sn_config` VALUES ('db_manual_lock_enabled', '0');\nINSERT INTO `sn_config` VALUES ('db_prefix', 'sn_');\nINSERT INTO `sn_config` VALUES ('db_version', '45');\nINSERT INTO `sn_config` VALUES ('debug', '0');\nINSERT INTO `sn_config` VALUES ('Defs_Cdr', '30');\nINSERT INTO `sn_config` VALUES ('deuterium_basic_income', '0');\nINSERT INTO `sn_config` VALUES ('eco_planet_starting_crystal', '500');\nINSERT INTO `sn_config` VALUES ('eco_planet_starting_deuterium', '0');\nINSERT INTO `sn_config` VALUES ('eco_planet_starting_metal', '500');\nINSERT INTO `sn_config` VALUES ('eco_planet_storage_crystal', '500000');\nINSERT INTO `sn_config` VALUES ('eco_planet_storage_deuterium', '500000');\nINSERT INTO `sn_config` VALUES ('eco_planet_storage_metal', '500000');\nINSERT INTO `sn_config` VALUES ('eco_scale_storage', '1');\nINSERT INTO `sn_config` VALUES ('eco_stockman_fleet', '');\nINSERT INTO `sn_config` VALUES ('eco_stockman_fleet_populate', '1');\nINSERT INTO `sn_config` VALUES ('empire_mercenary_base_period', 30 * 24 * 60 * 60);\nINSERT INTO `sn_config` VALUES ('empire_mercenary_temporary', '1');\nINSERT INTO `sn_config` VALUES ('energy_basic_income', '0');\nINSERT INTO `sn_config` VALUES ('fleet_bashing_attacks', 3);\nINSERT INTO `sn_config` VALUES ('fleet_bashing_interval', 30 * 60);\nINSERT INTO `sn_config` VALUES ('fleet_bashing_scope', 24 * 60 * 60);\nINSERT INTO `sn_config` VALUES ('fleet_bashing_war_delay', 12 * 60 * 60);\nINSERT INTO `sn_config` VALUES ('fleet_bashing_waves', 3);\nINSERT INTO `sn_config` VALUES ('Fleet_Cdr', '30');\nINSERT INTO `sn_config` VALUES ('fleet_speed', '1');\nINSERT INTO `sn_config` VALUES ('fleet_update_interval', '4');\nINSERT INTO `sn_config` VALUES ('fleet_update_last', NOW());\nINSERT INTO `sn_config` VALUES ('fleet_update_lock', '');\nINSERT INTO `sn_config` VALUES ('fleet_update_max_run_time', '30');\nINSERT INTO `sn_config` VALUES ('game_adminEmail', 'root@localhost');\nINSERT INTO `sn_config` VALUES ('game_counter', '0');\nINSERT INTO `sn_config` VALUES ('game_default_language', 'ru');\nINSERT INTO `sn_config` VALUES ('game_default_skin', 'skins/EpicBlue/');\nINSERT INTO `sn_config` VALUES ('game_default_template', 'OpenGame');\nINSERT INTO `sn_config` VALUES ('game_disable', '0');\nINSERT INTO `sn_config` VALUES ('game_disable_reason', 'SuperNova is in maintenance mode! Please return later!');\nINSERT INTO `sn_config` VALUES ('game_email_pm', '0');\nINSERT INTO `sn_config` VALUES ('game_installed', '0');\nINSERT INTO `sn_config` VALUES ('game_maxGalaxy', '5');\nINSERT INTO `sn_config` VALUES ('game_maxPlanet', '15');\nINSERT INTO `sn_config` VALUES ('game_maxSystem', '199');\nINSERT INTO `sn_config` VALUES ('game_mode', '0');\nINSERT INTO `sn_config` VALUES ('game_multiaccount_enabled', '0');\nINSERT INTO `sn_config` VALUES ('game_name', 'SuperNova');\nINSERT INTO `sn_config` VALUES ('game_news_actual', '259200');\nINSERT INTO `sn_config` VALUES ('game_news_overview', '3');\nINSERT INTO `sn_config` VALUES ('game_news_overview_show', 2 * 7 * 24 * 60 * 60);\nINSERT INTO `sn_config` VALUES ('game_noob_factor', '5');\nINSERT INTO `sn_config` VALUES ('game_noob_points', '5000');\nINSERT INTO `sn_config` VALUES ('game_speed', '1');\nINSERT INTO `sn_config` VALUES ('game_speed_expedition', '1');\nINSERT INTO `sn_config` VALUES ('game_users_online_timeout', 15 * 60);\nINSERT INTO `sn_config` VALUES ('game_user_changename', '2');\nINSERT INTO `sn_config` VALUES ('game_user_changename_cost', '100000');\nINSERT INTO `sn_config` VALUES ('geoip_whois_url', 'https://who.is/whois-ip/ip-address/');\nINSERT INTO `sn_config` VALUES ('initial_fields', '163');\nINSERT INTO `sn_config` VALUES ('int_banner_background', 'design/images/banner.png');\nINSERT INTO `sn_config` VALUES ('int_banner_fontInfo', 'terminator.ttf');\nINSERT INTO `sn_config` VALUES ('int_banner_fontRaids', 'klmnfp2005.ttf');\nINSERT INTO `sn_config` VALUES ('int_banner_fontUniverse', 'cristal.ttf');\nINSERT INTO `sn_config` VALUES ('int_banner_showInOverview', '1');\nINSERT INTO `sn_config` VALUES ('int_banner_URL', 'banner.php?type=banner');\nINSERT INTO `sn_config` VALUES ('int_format_date', 'd.m.Y');\nINSERT INTO `sn_config` VALUES ('int_format_time', 'H:i:s');\nINSERT INTO `sn_config` VALUES ('int_userbar_background', 'design/images/userbar.png');\nINSERT INTO `sn_config` VALUES ('int_userbar_font', 'arialbd.ttf');\nINSERT INTO `sn_config` VALUES ('int_userbar_showInOverview', '1');\nINSERT INTO `sn_config` VALUES ('int_userbar_URL', 'banner.php?type=userbar');\nINSERT INTO `sn_config` VALUES ('LastSettedGalaxyPos', '1');\nINSERT INTO `sn_config` VALUES ('LastSettedPlanetPos', '1');\nINSERT INTO `sn_config` VALUES ('LastSettedSystemPos', '1');\nINSERT INTO `sn_config` VALUES ('locale_cache_disable', '0');\nINSERT INTO `sn_config` VALUES ('metal_basic_income', '40');\nINSERT INTO `sn_config` VALUES ('payment_currency_default', 'USD');\nINSERT INTO `sn_config` VALUES ('payment_currency_exchange_dm_', '20000');\nINSERT INTO `sn_config` VALUES ('payment_currency_exchange_eur', '0.9');\nINSERT INTO `sn_config` VALUES ('payment_currency_exchange_mm_', '20000');\nINSERT INTO `sn_config` VALUES ('payment_currency_exchange_rub', '60');\nINSERT INTO `sn_config` VALUES ('payment_currency_exchange_uah', '30');\nINSERT INTO `sn_config` VALUES ('payment_currency_exchange_usd', '1');\nINSERT INTO `sn_config` VALUES ('payment_currency_exchange_wmb', '18000');\nINSERT INTO `sn_config` VALUES ('payment_currency_exchange_wme', '0.9');\nINSERT INTO `sn_config` VALUES ('payment_currency_exchange_wmr', '60');\nINSERT INTO `sn_config` VALUES ('payment_currency_exchange_wmu', '30');\nINSERT INTO `sn_config` VALUES ('payment_currency_exchange_wmz', '1');\nINSERT INTO `sn_config` VALUES ('payment_lot_price', '1');\nINSERT INTO `sn_config` VALUES ('payment_lot_size', '2500');\nINSERT INTO `sn_config` VALUES ('planet_capital_cost', '25000');\nINSERT INTO `sn_config` VALUES ('planet_teleport_cost', '50000');\nINSERT INTO `sn_config` VALUES ('planet_teleport_timeout', 1 * 24 * 60 * 60);\nINSERT INTO `sn_config` VALUES ('player_delete_time', 45 * 24 * 60 * 60);\nINSERT INTO `sn_config` VALUES ('player_max_colonies', '9');\nINSERT INTO `sn_config` VALUES ('player_metamatter_immortal', '100000');\nINSERT INTO `sn_config` VALUES ('player_vacation_time', 7 * 24 * 60 * 60);\nINSERT INTO `sn_config` VALUES ('player_vacation_timeout', 7 * 24 * 60 * 60);\nINSERT INTO `sn_config` VALUES ('quest_total', '0');\nINSERT INTO `sn_config` VALUES ('resource_multiplier', '1');\nINSERT INTO `sn_config` VALUES ('rpg_bonus_divisor', '10');\nINSERT INTO `sn_config` VALUES ('rpg_bonus_minimum', '10000');\nINSERT INTO `sn_config` VALUES ('rpg_cost_banker', '1000');\nINSERT INTO `sn_config` VALUES ('rpg_cost_exchange', '1000');\nINSERT INTO `sn_config` VALUES ('rpg_cost_info', '10000');\nINSERT INTO `sn_config` VALUES ('rpg_cost_pawnshop', '1000');\nINSERT INTO `sn_config` VALUES ('rpg_cost_scraper', '1000');\nINSERT INTO `sn_config` VALUES ('rpg_cost_stockman', '1000');\nINSERT INTO `sn_config` VALUES ('rpg_cost_trader', '1000');\nINSERT INTO `sn_config` VALUES ('rpg_exchange_crystal', '2');\nINSERT INTO `sn_config` VALUES ('rpg_exchange_darkMatter', '400');\nINSERT INTO `sn_config` VALUES ('rpg_exchange_deuterium', '4');\nINSERT INTO `sn_config` VALUES ('rpg_exchange_metal', '1');\nINSERT INTO `sn_config` VALUES ('rpg_flt_explore', '1000');\nINSERT INTO `sn_config` VALUES ('rpg_scrape_crystal', '0.50');\nINSERT INTO `sn_config` VALUES ('rpg_scrape_deuterium', '0.25');\nINSERT INTO `sn_config` VALUES ('rpg_scrape_metal', '0.75');\nINSERT INTO `sn_config` VALUES ('secret_word', 'SuperNova');\nINSERT INTO `sn_config` VALUES ('security_ban_extra', '');\nINSERT INTO `sn_config` VALUES ('security_write_full_url_disabled', '1');\nINSERT INTO `sn_config` VALUES ('server_email', 'root@localhost');\nINSERT INTO `sn_config` VALUES ('server_log_online', '0');\nINSERT INTO `sn_config` VALUES ('server_que_length_hangar', '5');\nINSERT INTO `sn_config` VALUES ('server_que_length_research', '1');\nINSERT INTO `sn_config` VALUES ('server_que_length_structures', '5');\nINSERT INTO `sn_config` VALUES ('server_start_date', DATE_FORMAT(CURDATE(), '%d.%m.%Y'));\nINSERT INTO `sn_config` VALUES ('server_updater_check_auto', '0');\nINSERT INTO `sn_config` VALUES ('server_updater_check_last', '0');\nINSERT INTO `sn_config` VALUES ('server_updater_check_period', 24 * 60 * 60);\nINSERT INTO `sn_config` VALUES ('server_updater_check_result', '-1');\nINSERT INTO `sn_config` VALUES ('server_updater_id', '0');\nINSERT INTO `sn_config` VALUES ('server_updater_key', '');\nINSERT INTO `sn_config` VALUES ('stats_hide_admins', '1');\nINSERT INTO `sn_config` VALUES ('stats_hide_player_list', '');\nINSERT INTO `sn_config` VALUES ('stats_hide_pm_link', '0');\nINSERT INTO `sn_config` VALUES ('stats_history_days', '7');\nINSERT INTO `sn_config` VALUES ('stats_minimal_interval', 10 * 60);\nINSERT INTO `sn_config` VALUES ('stats_php_memory', '1024M');\nINSERT INTO `sn_config` VALUES ('stats_schedule', '04:00:00');\nINSERT INTO `sn_config` VALUES ('tpl_minifier', '1');\nINSERT INTO `sn_config` VALUES ('tutorial_first_item', '1');\nINSERT INTO `sn_config` VALUES ('ube_capture_points_diff', '2');\nINSERT INTO `sn_config` VALUES ('uni_galaxy_distance', '20000');\nINSERT INTO `sn_config` VALUES ('uni_price_galaxy', '10000');\nINSERT INTO `sn_config` VALUES ('uni_price_system', '1000');\nINSERT INTO `sn_config` VALUES ('upd_lock_time', '60');\nINSERT INTO `sn_config` VALUES ('url_dark_matter', '');\nINSERT INTO `sn_config` VALUES ('url_faq', 'http://faq.supernova.ws/');\nINSERT INTO `sn_config` VALUES ('url_forum', '');\nINSERT INTO `sn_config` VALUES ('url_purchase_metamatter', '');\nINSERT INTO `sn_config` VALUES ('url_rules', '');\nINSERT INTO `sn_config` VALUES ('users_amount', '1');\nINSERT INTO `sn_config` VALUES ('user_birthday_celebrate', '0');\nINSERT INTO `sn_config` VALUES ('user_birthday_gift', '0');\nINSERT INTO `sn_config` VALUES ('user_birthday_range', '30');\nINSERT INTO `sn_config` VALUES ('user_vacation_disable', '0');\nINSERT INTO `sn_config` VALUES ('var_db_update', '0');\nINSERT INTO `sn_config` VALUES ('var_db_update_end', '0');\nINSERT INTO `sn_config` VALUES ('var_news_last', '0');\nINSERT INTO `sn_config` VALUES ('var_online_user_count', 0);\nINSERT INTO `sn_config` VALUES ('var_online_user_time', 0);\nINSERT INTO `sn_config` VALUES ('var_stat_update', '0');\nINSERT INTO `sn_config` VALUES ('var_stat_update_end', '0');\nINSERT INTO `sn_config` VALUES ('var_stat_update_msg', '');\nINSERT INTO `sn_config` VALUES ('var_stat_update_next', '');\n\n-- ----------------------------\n-- Administrator's account\n-- Login: admin\n-- Password: admin\n-- ----------------------------\nINSERT INTO `sn_account`\nSET\n    `account_id`       = 1,\n    `account_name`     = 'admin',\n    `account_password` = '21232f297a57a5a743894a0e4a801fc3',\n    `account_email`    = 'root@localhost',\n    `account_language` = 'ru';\n\n-- ----------------------------\n-- Administrator's user record\n-- Login: admin\n-- Password: admin\n-- ----------------------------\nINSERT INTO `sn_users`\nSET\n    `id`             = 1,\n    `username`       = 'admin',\n    `password`       = '21232f297a57a5a743894a0e4a801fc3',\n    `email`          = 'root@localhost',\n    `email_2`        = 'root@localhost',\n    `authlevel`      = 3,\n    `id_planet`      = 1,\n    `galaxy`         = 1,\n    `system`         = 1,\n    `planet`         = 1,\n    `current_planet` = 1,\n    `register_time`  = UNIX_TIMESTAMP(NOW()),\n    `onlinetime`     = UNIX_TIMESTAMP(NOW()),\n    `noipcheck`      = 1;\n\n-- ----------------------------\n-- Administrator's account translation to user record\n-- ----------------------------\nREPLACE INTO `sn_account_translate`\nSET\n    `provider_id`         = 1,\n    `provider_account_id` = 1,\n    `user_id`             = 1,\n    `timestamp`           = NOW();\n\n-- ----------------------------\n-- Reserved 'admin' name\n-- ----------------------------\nINSERT INTO `sn_player_name_history`\nSET\n    player_id   = 1,\n    player_name = 'admin';\n\n-- ----------------------------\n-- Administrator's planet\n-- ----------------------------\nINSERT INTO `sn_planets`\nSET\n    `id`          = 1,\n    `name`        = 'Planet',\n    `id_owner`    = 1,\n    `id_level`    = 0,\n    `galaxy`      = 1,\n    `system`      = 1,\n    `planet`      = 1,\n    `planet_type` = 1,\n    `last_update` = UNIX_TIMESTAMP(NOW())\n-- 'normaltempplanet01'\n;\n\nSET FOREIGN_KEY_CHECKS = 1;\n"
  },
  {
    "path": "docs/todo.txt",
    "content": "Stay out of my shed\n\nСжимать счётчик\n\nСобирать статистику по онлайну игроков по дням каждый маинтенанс. Подумать какую (см. ToDo)\n\nИстория отпусков\nДинамический срок отпуска - vacation_history\n\nАнтишпионская сеть - на время, за ТМ? Сбивает всех шпионов, так что игрок не получает инфы о планете\n\n\n\nXXX:\n\"боеприпас повышенного могущества\". Это нормальный термин?\nYYY:\nЭто артиллерийский термин. Там боеприпасы разделяются по могуществу, властности и блистательности.\nZZZ:\nЕсть еще градация по напыщенности и ослепительности\n\n\nThere is not really much difference between the three.\ngzencode() uses the fully self-contained gzip format, same as the gzip command line tool\n    gzdecode() is defined only for PHP 5.4.0 or newer\n    gzencode() - is OK\ngzcompress() uses the raw ZLIB format. It is similar to gzencode but with less header data, etc. I think it was intended for streaming.\ngzdeflate() uses the raw DEFLATE algorithm on its own, which is the basis for both the other formats.\n\nbasically gzcompress follows rfc1951 which contains a bunch of other data along with the compressed data that is used when decompressing. It includes an ADLER32 checksum, compression level, compression method, dictionary, the compressed data, and a bunch of other data used to decompress the data.\ngzdeflate is rfc1950 which is only the compression data.\ngzcompress is probably better to use, as it can verify the integrity of the data, and is more portable.\n\nNOT gzencode\ngzdefalte() !\n\nThis is what PHP functions return:\ngzencode() == gzip\ngzcompress() == zlib (aka. HTTP deflate)\ngzdeflate()  == *raw* deflate encoding\n\nСветить ДР в Императоре?\n\n\nhttp://pouchdb.com/learn.html - PouchDB is an in-browser database that allows applications to save data locally, so that users can enjoy all the features of an app even when they're offline. Plus, the data is synchronized between clients, so users can stay up-to-date wherever they go.\nhttp://nparashuram.com/IndexedDBShim/ - IndexedDB Polyfill over WebSql\n\n\nАдминка по ивенту:\nSELECT username, game_unit.* FROM `game_unit`\nleft join game_users on id = unit_player_id\nwhere unit_type = 3001 and unit_snid = 3002;\n\nselect unit_snid, sum(unit_level) as count from game_unit\nwhere unit_type = 3001 group by unit_snid;\n\nselect unit_snid, sum(unit_level) as count from game_unit\nwhere unit_type = 3001 ;\n\nhappening\n\nВраппер для добавления методов в рилтайме\nclass stdObject {\n    public function __call($method, $arguments) {\n        return call_user_func_array(Closure::bind($this->$method, $this, get_called_class()), $arguments);\n    }\n}\n\n$obj = new stdObject();\n$obj->test = function() {\n    echo \"<pre>\" . print_r($this, true) . \"</pre>\";\n};\n$obj->test();\n\n\n<?php\ntrait DynamicDefinition {\n\n    public function __call($name, $args) {\n        if (is_callable($this->$name)) {\n            return call_user_func($this->$name, $args);\n        }\n        else {\n            throw new \\RuntimeException(\"Method {$name} does not exist\");\n        }\n    }\n\n    public function __set($name, $value) {\n        $this->$name = is_callable($value)?\n            $value->bindTo($this, $this):\n            $value;\n    }\n}\n\nclass Foo {\n    use DynamicDefinition;\n    private $privateValue = 'I am private';\n}\n\n$foo = new Foo;\n$foo->bar = function() {\n    return $this->privateValue;\n};\n\n// prints 'I am private'\nprint $foo->bar();\n\n?>\n\n\nРандомизатор онлайна активности\n  update lh_users set onlinetime = UNIX_TIMESTAMP() - FLOOR(1 + RAND() * (30 - 1)) * 24*60*60 ;\n\nИвент \"Черная Луна\"\n  - вбрасывается несколько планет с лунами от нескольких пользователй разных уровней для разных интервалов\n  - каждая луна генерит ТМ. Каждая луна - своё количество\n  - пока на планете стоит чужой флот - ТМ капает на этого игрока\n  - если флотов несколько - ТМ распределяется между всеми игроками пропорционально цене флота\n  - обсчет ЧЛ происходит каждые 10 минут\n  - виртуальный игрок имеет две планеты. Первую - столицу, вторую - с ЧЛ. Столица обязательно появляется в населенном секторе (активный игрок), а ЧЛ - в ненаселенном (i-шки или пустые?)\n    $user_activity = floor((SN_TIME_NOW - $uni_galaxyRowUser['onlinetime'])/(60*60*24));\n     'USER_ACTIVITY' => $user_activity,\n     'USER_ATTACKABLE' => $user_activity >= 7,\n     'USER_INACTIVE' => $user_activity >= 28,\n  - посмотреть - можно ли просто поставить пользователю статистику и она будет сохранятся между пересчетами, или надо хитровыебаться\n  - (???) частота тиков, максимальная длительность Удержания и количество флотов зависит от брэкета, для которого стоит ЧЛ\n  - несколько лун для маленьких брэкетов\n\n\nselect u.id, u.onlinetime, UNIX_TIMESTAMP(NOW()),\nIF((UNIX_TIMESTAMP(NOW()) - u.onlinetime)/24/60/60 < 7, 0,\nIF((UNIX_TIMESTAMP(NOW()) - u.onlinetime)/24/60/60 < 28, 1, 2))\nfrom lh_planets as p\njoin lh_users as u on u.id = p.id_owner\norder by u.id\nlimit 1000;\n\n\n\nЭксперимент по краудфандингу: ивент \"Черная Луна\"\n\nВ чате на Альфе предложили ивент. Как предложили, конечно, слабореализуемый бред, но, как ни странно, идея имеет здравое зерно. Оригинал звучал так:\n[quote]\"[07.12.2014 23:22:58]  Spirt> я тут подсмотрел идейку, в общем ивент. Его суть: Администрация создаёт 1 плантку которую можно захватывать, с неё можно добывать ТМ, и сообщает кординаты этой планетки всем присутствующим. Альянсы воюют за эту планету)\"[/quote]\nОчевидно, что это - слабореализуемая утопия. Конечно, в планах есть и захват планет, и Бои Альянсов, но каждый из этих компонентов сам по себе требует немало времени на реализацию. А затем это всё надо скомпоновать и сделать ивент сам по себе - что тоже потребует некислых модификаций кода. Например - добавление структуры, которая добывает ТМ. Напомню, что сейчас такие структуры в движке в принципе не предусмотрены, т.е. их надо писать с нуля  В общем - работы на многие десятки часов\n\nНо в том же чате в очередной раз предложили \"скинутся\"... Что ж, я давно вынашиваю идею краудфандинга фишек. Ибо хочется больше времени уделять движку, но и кушать что-то надо. Давайте проведем эксперимент. Как знать - может соберется и с миру Горлуму на пиво? И, возможно, это станет хорошей традицией - подкреплять свои \"хотелки\" баблом, а не просто кидая их в чат\n\nОбъявляю краудфандинг на фишку \"ивент 'Черная Луна'\". Краудфандинг - это когда много людей жертвует на проект некую сумму, а в результате общий итог (в идеале) дает разработчику возможность воплотить фишку в коде и запустить её в игру\n\nНемного по сути ивента. Он состоит в том, что во Вселенную вбрасывается планеты, которые имеют особенную луну - Чёрную Луну (ЧЛ). При установке флота в Удержание на ЧЛ, игроку каждый тик будет капать ТМ.\n\nИ немного технических деталей:\n- в каждом брэкете (диапазоны разбиения игроков в зависимости от их баллов развития) будет собственная ЧЛ. Т.е. игроки всех уровней смогут поучаствовать в ивенте. Кроме, возможно, топов - там слишком узкие брэкеты, так что на каждого топа может прийтись по ЧЛ. Что лишает интригу смысла\n- установка флота в Удержание на ЧЛ приносит владельцу флота ТМ каждый тик\n- если в Удержании стоит 1 флот любого размера - всю ТМ получает владелец флота\n- если в удержании стоит несколько флотов - ТМ распределяется по владельцам пропорционально полным стоимостям флотов (т.е. включая стоимость в дейтерии)\n- ЧЛ можно уничтожить\n\nЯ уже составил на ТЗ на этот ивент, посчитал вчерне математику и готов его реализовать. Теперь дело за игроками. Как это принято в краудфандинге, установим цели:[list]\n[*] <100 USD в эквиваленте - эксперимент будет признан провальным. Все \"хотельщики\", чьи хотелки не подкреплены конкретной суммой будут отправляться в пешее эротическое, вместе со своими хотелками. Если не наберется даже 100 USD, то всем, кто проспонсирует эту фишку, в компенсацию за пассивность остальных игроков будет начислен эквивалент суммы в ММ на любом выбранном ими сервере supernova.ws (это верно ТОЛЬКО ЕСЛИ не наберется даже 100 USD в эквиваленте);\n[*] >=100 USD в эквиваленте - результат эксперимента - удовлетворительный. Я сделаю эту фишку в базовом варианте и она будет выпущена в рамках новогодней акции к Новому Году-2015;\n[*] >=200 USD в эквиваленте - результат эксперимента - хороший. Примерно в такую сумму я оцениваю базовый функционал этого ивента для СН[list]\n    [*] Каждый пожертвованный доллар после 200 USD будет увеличивать добычу с ЧЛ во время новогодней акции на 1%. Т.е. 250 USD - +50% от запланированной скорости добычи;[/list]\n[*] >=300 USD в эквиваленте - результат эксперимента - очень хороший[list]\n    [*] Каждый пожертвованный доллар после 200 USD будет увеличивать добычу с ЧЛ во время новогодней акции на 2%. Т.е. 350 USD - +300% от запланированной скорости добычи;\n    [*] Все, кто спонсировал фишку, получат 5% в виде ТМ на любом игровом сервере supernova.ws (это и подобные предложения распространяются и на игроков из приложений под Андроид, Windows и Windows Phone);[/list]\n[*] >=400 USD в эквиваленте - результат эксперимента - отличный![list]\n    [*] После НГ в игру на постоянно основе будет добавлена дорогая лунная спецпостройка, которая будет фармить ТМ на Луне (будут ограничения и допусловия на работу этой постройки!);\n    [*] Каждый пожертвованный доллар после 200 USD будет увеличивать добычу с ЧЛ во время новогодней акции на 4%. Т.е. 450 USD - +1000% от запланированной скорости добычи\n    [*] Все, кто спонсировал фишку, получат 10% в виде ТМ на любом игровом сервере supernova.ws;[/list]\n[*] >=500 USD в эквиваленте - результат эксперимента - великолепный!! Примерно столько стоил бы базовый функционал (без зданий, свистелок и перделок), если бы пришлось заказывать его \"на стороне\". Это даст возможность мне написать дополнительный интерфейс и сделать такие ивенты РЕГУЛЯРНЫМИ![list]\n    [*] Ивент станет РЕГУЛЯРНЫМ!\n    [*] В Новый Год гарантированно будет скидка на покупку ММ\n    [*] После НГ в игру на постоянно основе будет добавлена дорогая лунная спецпостройка, которая будет фармить ТМ на Луне (часть ограничений будет снято, добыча будет увеличена)\n    [*] Каждый пожертвованный доллар после 200 USD будет увеличивать добычу с ЧЛ во время новогодней акции на 10%. Т.е. 550 USD - +3500% от запланированной скорости добычи\n    [*] Все, кто спонсировал фишку, получат 25% в виде ТМ на любом игровом сервере supernova.ws;[/list][/list]\n\n\nЕстественно, предусмотрены индивидуальные бонусы! Они начисляются тем, чья сумма пожертвования достигла указанной величины в дополнение к тем бонусам, которые начислены при достижении общей цели:[list]\n[*] 50 USD - Премиум-аккаунт +3 на 3 месяца на ЛЮБОМ из серверов Альфа, Бета, Гамма, Дельта. Игровая медаль \"Бронзовый спонсор\". +10% к следующей покупке ММ на любом сервере\n[*] 100 USD - Премиум-аккаунт +3 на 6 месяцев на ЛЮБОМ из серверов Альфа, Бета, Гамма, Дельта. Игровая медаль \"Серебряный спонсор\". +20% к следующей покупке ММ на любом сервере\n[*] 200 USD - Премиум-аккаунт +4 на 3 месяца на ЛЮБОМ из серверов Альфа, Бета, Гамма, Дельта. Игровая медаль \"Золотой спонсор\". +30% к следующей покупке ММ на любом сервере\n[*] 300 USD - Премиум-аккаунт +4 на 1 год на ЛЮБОМ из серверов Альфа, Бета, Гамма, Дельта. Игровая медаль \"Платиновый спонсор\". +50% к следующей покупке ММ на любом сервере\n[*] 500 USD - Премиум-аккаунт +5 на 3 месяца, максимальный уровень всех Наемников на 1 год на ЛЮБОМ из серверов Альфа, Бета, Гамма, Дельта. Игровая медаль \"Алмазный спонсор\". +75% ММ к следующей покупке на любом сервере\n[*] 1000 USD - Премиум-аккаунт +5 на 6 месяцев, максимальный уровень всех Наемников +1 на 1 год на ЛЮБОМ из серверов Альфа, Бета, Гамма, Дельта. Игровая медаль \"Бриллиантовый спонсор\". +100% ММ к следующей покупке на любом сервере\n[*] 2000 USD - Премиум-аккаунт +5 на 1 год, максимальный уровень всех Наемников +2 на 1 год на ЛЮБОМ из серверов Альфа, Бета, Гамма, Дельта. Игровая медаль \"Тёмный спонсор\". Выделение имени игрока в чате. +150% ММ к следующей покупке на любом сервере\n[*] 5000 USD - Премиум-аккаунт +6 на 1 год, максимальный уровень всех Наемников +5 на 1 год на ЛЮБОМ из серверов Альфа, Бета, Гамма, Дельта. Игровая медаль \"Метаспонсор\". Выделение имени игрока в чате. +300% ММ к следующей покупке на любом сервере[/list]\n\nЕсли платежи разнесены во времени - то награды всё равно кумулятивны. Т.е. если кто-то пожертвовал 50 WMZ и получил +3 премиум на 3 месяца, а затем пожертвовал еще 50 WMZ - его премиум будет проапгрейжен с 3 месяцев до 6 месяцев. Ну и никто не мешает проапгрейдить свой прем на любой другой уровень\n\nПодчеркиваю: данное предложение - это не акция. Это - сбор средств на новую фишку. Основная цель - собрать деньги на фишку. Бонусы - это чисто бонусы. Тупо купить ММ за ту же сумму будет дешевле. Но участие в сборе средств даст мне понимание, насколько игроки ДЕЙСТВИТЕЛЬНО заинтересованы в игре и насколько имеет смысл обращать на их хотелки\n\nПервоначальный контрольный срок - среда, 10 декабря 2015 года. Там станет понятно - стоит ли продолжать краудфандинг\n\nДля получения реквизитов для платежа со мной можно связаться по контактам, указанным тут: http://forum.supernova.ws/viewtopic.php?f=3&t=2474\n\n\n\nИтак, на СуперНове начали отмечать Новогодние праздники. Первые залпы феерверка акций и ивентов игроки уже почувствовали на себе:[list]\n[*] Новогодний фон\n[*] Новогодний набор смайликов\n[*] И самое интересное - увеличение рейтов добычи ресурсов в 1,5 раза![/list]\n\nНо это - только начало! Что бы игроки ориентировались в грядущем разгуле, публикуется очень кратенькая выжимка запланированных на НГ мероприятий:[list]\n[*] 24 декабря 2014 года - 18 января 2015 года 23:59:59: Новогодние Праздники на СуперНове!\n[*] 24 декабря 2014 года: скорость добычы увеличивается в 1,5 раза. [color=#008000]УЖЕ В СУПЕРНОВЕ![/color]\n[*] 24 декабря 2014 года - 1 января 2015 года 23:59:59: МЕГАскидки на премиум-аккаунты ЗА ТМ! (см. ниже)\n[*] 25 декабря 2014 года: скорости постройки и исследований увеличиваются в 1,5 раза\n[*] ?? декабря 2014 года - 31 декабря 2014 года: (тут ЕЩЕ МОЖЕТ быть ивент, если до 28 декабря наберется 500 эквивалентов на [url=http://forum.supernova.ws/viewtopic.php?f=4&t=2981&start=10#p35948]ЧЛ[/url]\n[*] 31 декабря 2014 года - 1 января 2015 года 23:59:59: [url=http://forum.supernova.ws/viewtopic.php?f=4&t=2987]мораторий[/url] на агрессивные миссии\n[*] Новогодняя Ночь - отмечаем Новый Год в чате СН! На время моратория (включая Новогоднюю Ночь) будет снято ограничение на неигровые разговоры в чате. Однако помним - нарушать Правила по-прежнему нельзя: никаких обсуждений политики, религии, личных данных игроков и прочего\n[*] 2 января 2015 года 00:00:00 - в честь 55-летия первого полёта Человечества к Луне - ивент \"Восхождение [url=http://forum.supernova.ws/viewtopic.php?f=4&t=2981]Чёрной Луны[/url]\". Окончание ивента - [color=#800000]6 января 2015 года 23:59:59[/color]\n[*] ?? декабря 2014 года - 31 декабря 2014 года: (тут ЕЩЕ МОЖЕТ быть ивент, если до 28 декабря наберется 500 эквивалентов на [url=http://forum.supernova.ws/viewtopic.php?f=4&t=2981&start=10#p35948]ЧЛ[/url]\n[*] ?? января 2015 года: (тут ЕЩЕ МОЖЕТ появится [url=http://forum.supernova.ws/viewtopic.php?f=4&t=2981&start=10#p35948]спецпостройка для лун[/url], которая будет фармить ТМ, если наберется 500 эквивалентов на ивент ЧЛ)\n[*] 18 января 2015 года 23:59:59: Окончание Новогодних Праздников. Заканчиваются все ивенты, акции. Рейты возвращаются к изначальным уровням\n[/list]\n\nВ распиании событий указаны скидки на Премиум-аккаунт! И они УЖЕ В ИГРЕ! Скидки - на любой вкус и любое количество ТМ! Да-да, вы не ослышались! В Новогодние Праздники скидки на Премиум-аккаунт даются просто за ТМ! Скидки от приятных 2% до огромных 60%!\nУже на +3 премиуме за до-акционную цену 60 дней можно теперь взять тот же самый уровень премиума на 90 дней - месяц премиума в подарок!\nДальше - больше! 60 дневный Премиум +4 сейчас лишь чуточку дороже, чем раньше стоил 30 дневный аккаунт того же уровня! Двухкратная длительность - по практически той же цене!\nХочется ЕЩЕ больше? Есть их у меня! Месячный прем +5 - дешевле, чем доакционный двухнедельный прем! А +6 уровень отдается практически даром - 90 дней премиума стоят дешевле, чем 30 дней до Новогодних Праздников! Два месяца мощнейшего премиума - в подарок!\nВпрочем, все детали вы можете посмотреть сами - как писалось выше, скидки УЖЕ В ИГРЕ! Лишь выберите пункт \"Премиум\" в меню слева - и сами выбирайте себе скидку!\n\n\n\nЭкспедиции - левел должен влиять на что-то. На добычу?\n\nhttp://habrahabr.ru/post/244815/\n  Оптимизация CSS - комменты\n  http://perfectionkills.com/profiling-css-for-fun-and-profit-optimization-notes/\nhttp://perfectionkills.com/profiling-css-for-fun-and-profit-optimization-notes/\nhttp://frontender.info/css-performance-revisited-selectors-bloat-expensive-styles/\n\n\n\n\nМой ник Сингулярность на серверах:\nhttp://alpha.supernova.ws/ (он же http://ogame.supernova.ws/ , на том же сервере играют все игроки через приложения Андроид, Windows 8 и Windows Phone)\nhttp://delta.supernova.ws/\n\nМой ник Gorlum на серверах:\nhttp://gamma.supernova.ws/ (он же http://supernova.supernova.ws/ )\nhttp://beta.supernova.ws/\n\n\n\n\n\nНовое здание - Монолит (Одиссея-2001)\n  - строится за ресы и ТМ (или только за ресы, но огромное количество?)\n  - генерит ТМ\n  - работает, только если на планете нет других построек и обороны, а на орбите - нет флотов\n\n\nНовое здание - \"Голограмный проектор\" за ТМ. Показывает в шпионаже увеличенное количество юнитов на планете. Режимы работы:\n  - Отключить\n  - Уменьшение уровней всех зданий\n  - Уменьшение количество боевых юнитов (раздельно защиты и обороны?)\n  - Увеличение количества боевых юнитов (раздельно защиты и обороны?)\n  - Спрятать полностью одно здание - тогда видно проектор. В случае если это шахты - прячет и ракеты. Можно спрятать здания на сумму своих уровней, на себя - прячет свой остаток\n  - Работать в + к ЭУИ планеты\n  - От уровня зависит доступное изменение. Каждый уровень - +10% (5%?)\n  - Прячет ли себя в режимах работы?\n\n\nТелепортация, использование Крюка и БАК, использование Монолита, использование Ворот - увеличивает шанс превращения солнца системы в СуперНову. Как в исходнике, так и в пункте Назначения\nЭвент СуперНова\n  - есть время убраться, телепортироваться и продаться\n  - при телепорта заново кидается шанс СуперНовы\n  - Телепорт - +1%, Ворота - +0.1%, Монолит - +0.001% (?) за левел каждый тик. Тик - каждые 60 минут\n  - при взрыве СН сносится % зданий, флота и обороны, равный проценту шанса взрыва с округлением вверх\n\n\nПланировщик бюджета ТМ\n\nКитайсы\n  http://habrahabr.ru/post/237725/\n  http://habrahabr.ru/post/245027/\n\n  Я использовал вот этот сервис tethras.com. Переводил приложение для windows phone, у меня было около 600-100 слов, кажется. Примерно вышло около $50 за один язык. Кстати там очень удобно сразу загружаешь ресурсный фаил и получаешь так же ресурсный фаил, но уже в другом языке.\n\n  если заказ разовый и объём небольшой, то можно воспользоваться сервисами типа Gengo или Alconost Nitro. По обеим ссылкам есть расценки. Если же проект подразумевает постоянные обновления и развитие, то лучше найти переводческое агентство (лучше в самом Китае). Расценки агентств (в паре английский-китайский упрощённый/традиционный) от 0,04 до 0,20 долларов за слово исходного языка в зависимости многих параметров (формат исходного текста, тематика, необходимость вычитки, тестирования, вид тестирования, ведение глоссариев, консультаций по интернационализации и т.д.)\n\nАльянсы\n  Увеличивать/Уменьшать бонус игроку, пропорционально разнице со средним уровнем алла - надо ли?\n\nDuplicate entry '3267-1-60088-1500' for key 'I_unit_player_location_snid'<br />/* tID 155<br />classSupernova::db_upd_record_by_id() - 'includes/db/db_queries_unit.php' Line 50<br />db_unit_set_by_id() - 'modules/unit_captain/unit_captain.php' Line 159<br />(unit_captain)unit_captain->unit_captain_restore_fleet_to_planet() - '' Line <br />call_user_func_array() - 'includes/general.php' Line 32<br />sn_function_call() - 'includes/includes/flt_flying_fleet_handler2.php' Line 22<br />RestoreFleetToPlanet() - 'includes/includes/flt_mission_relocate.php' Line 35<br />flt_mission_relocate() - 'includes/includes/flt_flying_fleet_handler2.php' Line 371<br />flt_flying_fleet_handler() - 'includes/init.php' Line 360<br />require_once() - 'common.php' Line 10<br />require_once() - 'index.php' Line 5<br /> */ UPDATE beta_unit SET `unit_location_type` = 1, `unit_location_id` = 60088 WHERE `unit_id` = 94837<br />\n\n- ПЕРЕДЕЛАТЬ СОВСЕМ\n  register_time - это вообще TIMESTAMP! И ПОМЕНЯТЬ В ЗАПРОСЕ НА УДАЛЕНИЕ И НА ОБСЛУЖИВАНИЕ!!!\n  player_que - вроде нигде не используется - удалить нахрен\n  deltime - нужно только при обслуживании сервера, не работает, можно удалить пока\n\n- СИСТЕМНОЕ\n  - ПОСТОЯННАЯ ЧАСТЬ. То, что не изменяется регулярно\n    id\n    username\n    authlevel\n    user_as_ally - переделать в user_type: 0 - юзер, 1 - альянс, 2 - бот итд\n    ally_id\n    id_planet\n    galaxy - надо ли держать это в users ???\n    system - надо ли держать это в users ???\n    planet - надо ли держать это в users ???\n\n  - Переменная часть. Из-за неё, собственно, и лочится таблица USER\n    onlinetime\n    que_processed\n\n- ЮНИТЫ\n  metal\n  crystal\n  deuterium\n  dark_matter\n  metamatter\n  metamatter_total\n  player_race\n\n- СТАТИСТИКА - ОТДЕЛЬНАЯ ЗАПИСЬ В НЕБЛОКИРУЕМОЙ ТАБЛИЦЕ?\n  player_rpg_explore_xp\n  player_rpg_explore_level\n  lvl_minier\n  xpminier\n  player_rpg_tech_xp\n  player_rpg_tech_level\n  lvl_raid\n  xpraid\n  raids\n  raidsloose\n  raidswin\n  total_rank - кэш из статистики?\n  total_points - кэш из статистики?\n\n\n--- СОМНИТЕЛЬНОЕ СИСТЕМНОЕ\n  password                  - нужно только при логинах и спецоперациях\n  email                     - нужно только при отправке сообщений\n  email_2                   - нужно только при отправке сообщений\n\n  ally_tag                  - просто кэширует инфу из Альянса, нахуй надо ???\n  ally_name                 - просто кэширует инфу из Альянса, нахуй надо ???\n\n  ally_rank_id              - нужно очень редко\n  ally_register_time        - нужно очень редко\n  current_planet            - нужно очень редко\n  banaday                   - нужно очень редко\n  vacation                  - нужно очень редко\n  vacation_next             - нужно очень редко\n\n\n- ПЕРЕМЕННЫЕ\n  user_agent   - изменяется только при входе\n  user_lastip  - изменяется только при входе\n  user_proxy   - изменяется только при входе\n  ip_int       - изменяется только при входе\n  user_birthday_celebrated  - вообще переменная должна быть\n\n  - Замеры времени\n    user_time_measured\n    user_time_diff\n    user_time_utc_offset\n    user_time_diff_forced\n  - СООБЩЕНИЯ И НОВОСТИ\n    news_lastread\n    mnl_alliance\n    mnl_joueur\n    mnl_attaque\n    mnl_spy\n    mnl_exploit\n    mnl_transport\n    mnl_expedition\n    mnl_buildlist\n    msg_admin\n    new_message\n\n\n- НАСТРОЙКИ\n  user_birthday             - нужно очень редко\n  sign - подпись в сообщениях. Не участвует нигде\n  lang\n  sex - gender\n  avatar\n  dpath\n  design\n  noipcheck\n  options\n  planet_sort\n  planet_sort_order\n  spio_anz\n  settings_tooltiptime\n  settings_fleetactions\n  settings_esp\n  settings_wri\n  settings_bud\n  settings_mis\n  settings_rep\n  settings_statistics\n  settings_info\n  admin_protection          - нужно очень редко\n\n\nГенерация кристаллов\n\nlexa\nПервые клеточные автоматы были как раз моделями роста кристаллов (Станислав Улам). Самое известное правило такого автомата - \"один из восьми\". Ну то есть буквально - новая клетка возникает там, где ровно один живой сосед, в остальных случаях состояние не меняется. Из одной точки вырастает вот такая квадратная снежинка:\nhttp://fuga.ru/shelley/pautina/cell7.gif\nЯ экспериментировал с чередованием окрестностей, чтобы уйти от квадратности:\nhttp://fuga.ru/shelley/pautina/cell1.gif\nНо более натуральные кристаллы -дендриты можно получить иначе: моделируется случайное блуждание с налипанием, тоже на клеточном автомате - но тут есть элемент случайности, потому и картинка более реалистичная:\nhttp://fuga.ru/shelley/pautina/cell13.gif\nВообще там среди иллюстраций Паутины много таких явлений, смоделированных на клеточных автоматах:\nhttp://fuga.ru/shelley/pautina/cell18.gif\nЛучшая книжка на эту тему на русском называлась \"Машины клеточных автоматов\" Тоффоли и Марголуса. Там и все эти правила есть. Впрочем, не исключено, что за последние 20 лет могли еще издать поинтересней. Стивен Вольфрам (который поисковик ВольфрамАльфа) много занимался этой темой тоже.\n\nstealthy_shadow\nЯ уже дошел до гексагональных снежинок со случайным \"налипанием\" и фронтальных клеточных автоматов, но результат по-прежнему неудовлетворительный. Хочется получить не просто красивый стационарный кристалл, но и красивую картинку процесса в динамике. Да не монохромную, а с ростом \"прожилок\", псевдо-трехмерной фактуры и прочих красивостей. Но здесь уже, похоже, нужно конкретное ноу-хау со вполне определенными формулами и сидами\n\nlexa\nНу в общем да, я не специализировался на кристаллах, так что у меня примеры простенькие.\nХотя трехмерные картинки можно получить на трехмерном клеточном автомате точно так же, как на плоском: моделируется случайное блуждение с сохранением числа частиц (то есть окрестность каждой клетки просто вращается в случайную сторону) - ну и дальше налипание.\nМожно забавно менять законы налипания. В картинке дендрита, что приведён выше, налипает любая частица, которая оказалась рядом с \"замороженной\". Но можно задать правило, что на концах снежинки (при одном соседе) вероятность налипания слабая, а вот при двух-трёх побольше. В результате дендриты получаются менее/более разреженные.\nЛибо вот правило \"налипать только на концах\" - тогда получаются вообще не дендриты, а паутины:\nhttp://fuga.ru/shelley/pautina/cell10.gif\nУ меня на сайте валяется старая демка под Винды, там есть и рост дендрита. Правда, она написана для IBM PC 286, для которой это был мультик на полчаса. На современном ноуте она должна пролетать со свистом :)\nhttp://fuga.ru/misc/selfor.com\n\nstealthy_shadow\nДа тут бы хотя бы плоские правдоподобные кристаллы получить... Идеи уже закончились - хреново у меня с ними, не идут у меня КА, не вижу я \"картинку\". Поэтому за предложение поиграться с вероятностью налипания в зависимости от населенности - спасибо.\nА вот \"жилки\" в кристаллах как раз можно сделать дендритами (еще одно \"спасибо\" за идею). Или просто выделить \"поколения\" кристаллов и тупо растить один в пределах другого - так что задача получения сложных кристаллов сводится к предыдущей.\nА всю эту красоту сгладить и расцветить можно чем-то типа Diamond Square или просто \"плазмой\" - надо посмотреть по результатам.\nВ общем - идеи теперь есть, надо с ними поиграться... Спасибо за советы.\n\n\n\nCSS и JS - релоадер:\n  touch update.php каждый раз\n  Вытягивать время изменения апдейта\n  Добавлять это время ко всем JS и CSS\n  Или просто копировать JS-CSS в каталог cache - но тут нужен центральный репозиторий кэшей и права на запись\n\nАпгрейд офицеров\n  Карго-мастер - убрать или повысить ёмкость трюмов. Проверить нужность по статистике использования\n  Переделать с повышением цены на трюмы+склады\n  Академик - добавить +1 слот/левел. Проверить нужность по статистике использования\n\n\n\nАртефакты в экспе - \"Премиум\" на 1+ дней. Можно хранить, можно использовать для апгрейда\n\nПлатежи - переписать централь а-ля иксолла с дефолтным сервером\nПроверить работу robots.txt\n\nCenter vertically\n\nxsolla - передавать описание платежа\n\nПопереименовывать константы во всех платёжных модулях - привести их к одному стандарту\n\nскин/темплейт console\n\nПеределать платный выбор расы в сброс расы\n\nСтатистика\n  Проверить, почему при обновлении после удаления данных извне пользователям неправильно назначаются места\n\nПремиум - субсекундная точность. 1 - 0.5 сек, 2 - 0.25 сек, 3 - 0.125 сек итд\n  Можно практически всё. Есть в планах - называется \"субсекундная точность\". Но будет в игре как спец-фишка за ТМ или для высоких уровней премиума - пока не решил, поскольку еще даже не думал над реализацией, поскольку явно неприоритетная вещь, нужная в основном топ- и саб-топам, у которых всё и так более чем кучеряво. Кроме того в сочетании с наниткой на луне - очевидный геймбрейкер: можно моментально наностроителями выкачать нанитку, после чего за считанные часы наклепать неубиваемую оборону. А затем просто свозить ресы на луну и свистеть в три дырочки\n\n[*] Покупка отдельно возможности настраивать меню за ТМ (5к на 1 мес) и навсегда (20кММ)\n[*] Вынести весь код, все стили, весь темплейт в menu_customize\nМодуль иконок андроида - сделать их системными\nПроверить, почему изменения в менюшке отрабатываются только на второй раз\nПрикрепленное меню с галочкой \"Показывать открепленное меню поверх\" так же показывается поверх\n\nМеню\n  Сделать глаз больше на мобильных устройствах\n  И вообще - делать шрифт больше (?)\n\nКвесты - в меню добавить выполненных/всего\n\nДальность флота расчитывать по отдельному кораблю - а не по пакам. Что бы не получилось, что пак ЛИ может пролететь 10 галактик, потому что с ними летит пак МТ\n\nНовое лекарство Выапрол\n\nНа странице постройки в таблице изменений левела писать +левел +добавки\n  Отключать кнопку \"Свезти ресурсы\", если у игрока только одна планета без луны\n  Переделать дропдаун смены ядра\n  Покинуть колонию - написать, что это пароль от игры\n\n\n#chat_message_smiles2 div {\n  width: 54px;\n  height: 54px;\n\n  -webkit-transform-style: preserve-3d;\n  -moz-transform-style: preserve-3d;\n  transform-style: preserve-3d;\n}\n#chat_message_smiles2 div img {\n/*  display: inline-block;*/\n  position: relative;\n  top: 50%;\n  -ms-transform: translateY(-51%);\n  -webkit-transform: translateY(-51%);\n  -o-transform: translateY(-51%);\n  -moz-transform: translateY(-51%);\n  transform: translateY(-51%);\n}\n\n\n\nТУДУ\n====\n  Видное\n  ------\n    [*] Чат - выделять цветом, если идет обращение к игроку в сообщении\n    [*] Таблицы - выдать \"черезполосицу\", что бы было можно различать строки легче - в частности при подборе флота\n    [*] Премиум - плюс к Губернаторам\n    [*] Режим наблюдателя - когда можно летать только между своими планетами и нельзя ничего делать остального\n        За ТМ?\n        Для прем от 3 (?) давать возможность делать \"пассивный\" режим?\n        С Таймаутом(?)\n        Не давать включать при летящих агрессивных флотах\n\n\n  Важное внутреннее\n  -----------------\n    [*] Еще раз пройтись по вводу-выводу данных. Это уже когда будет модульная система вывода\n    [*] Позаменять везде для ID преобразования из intval() в idval(). ID сделать строкой!\n\n\n  Быстрое внутреннее\n  ------------------\n\n\n  Завлекалочки\n  ------------\n    [*] Модуль \"Счастливчик\" - за ММ повышает всё, что можно\n        Серебряная ложка\n        Родился в рубашке 10 баксов\n        Седьмой сын 100 баксов\n        Седьмой сын седьмого сына 1000 баксов\n    [*] Модуль \"Эвентов\"\n    [*] ОТДЕЛЬНОЕ СТРОЕНИЕ - ПЛАНЕТАРНЫЙ ЩИТ, который уменьшает всю атаку на некоторое количество единиц\n    [*] Модуль \"Апгрейдов\" - улучшает любой аспект любого юнита\n    [*] Артефакт \"Планетарный сеятель\" - разбрасывает планеты по галактикам. Свои? Чужие?\n\n\n  Не сломалось - не чини!\n  -----------------------\n    [*] Ракеты - переписать. Проверить, что бы влияли все бонусы - техи, форты, наемники, премиум\n    [*] Альянсы: переписать, в особенности работу с ранками!\n    [*] flt_t_send_fleet - переписать и сделать через него отправку всех флотов: своз ресурсов, через подбор флотов, через AJAX\n    [*] Флоты - внести в список кораблей флота ресурсы вместо отдельных полей\n    [*] // TODO ПЕРЕДЕЛАТЬ! function sys_stat_get_user_skip_list()\n    [*] infos.php - переписать\n    [*] Вселенная: отправка флотов через AJAX - И ВООБЩЕ РАЗОБРАТЬСЯ, КАК ЭТА ХУЕТА РАБОТАЕТ! И ПЕРЕПИСАТЬ!\n    [*] Кэширование языков http://ru.wikipedia.org/wiki/Коды_языков\n    [*] Параметр \"re\" при \"cp\" вообще не нужен - поубирать везде\n    [*] поискать _perhour как в metal_perhour - что бы убрать лишние строки из зданий-факторий\n    [*] Ракетные шахты - в строения! +переработка щитов\n    [*] Отвязать опции пользователя от пользователя\n    [*] Отвязать ресы от планет, флотов, юзера\n    [*] Сообщения - бывают \"отложенные\" и \"инстант\" - чат и ПМ, например. Переделать отправку сообщений по типам\n    [*] Переделать отправку сообщений со стандартного mail() на какой-нибудь мейлер PHPMailer, к примеру. Сделать отправку сообщений пакетами - что бы не попадать в спам-лист\n    [*] Отдельный таск КРОНом - или опционально в шедулере\n    [*] Добавить в темплейт поддержку префикса Fx_ - форматирование строки, скажем, в целое - FI и в число - FF\n\n  Навороты - ненужное и/или невидимое усложнение\n  ----------------------------------------------\n    [*] Интеграция с phpBB3 - капча на входе в игру ИЛИ специальные права для зареганных через движок\n    [*] Перебалансировать наемников, переделать Академика\n    [*] Шпионаж: точность шпионажа от разности уровней для УИШ; защита зондами (?)\n    [*] Статистика: сделать еще учет планов и наёмников в стате\n    [*] Капитан - скилл для экспы, скилл на ускорение, скилл на сборку ресов\n    [*] Диверсификация результатов по экспе:\n        - Всегда - ТМ и арты\n        - Только транспорты - ресы\n        - Только суперхеви - флот\n        - Только лайты, миды и хеви - ТМ и арты\n\n  Нутрянное - нужно только мне\n  ----------------------------\n    [*] ОТКЛЮЧЕНИЕ МОДУЛЯ ДОЛЖНО БЫТЬ ПРОПИСАНО ВО ВСЕХ ФУНКЦИЯХ МОДУЛЯ!\n    [*] Плательщики, никогда не закончившие платеж\n        SELECT payment_user_id, payment_user_name, max(payment_date), sum(payment_dark_matter_paid)\n        FROM game_payment WHERE payment_status <> 1 AND payment_user_id NOT IN (SELECT payment_user_id FROM game_payment WHERE payment_status = 1)\n        GROUP BY payment_user_id ORDER BY max(payment_date) DESC\n    [*] Бенчмарк. Разделить время обсчета страницы и время генерации собственно HTML-текста\n\n  Фигня всякая, неважная\n  ----------------------\n    [*] ОШИБКА: проверить - бьется ли get_total_cost($level) с суммой по левелам  НЕ БЬЕТСЯ!!! но совсем чутку // TODO как-нибудь переделать на встроенные циклы\n    [*] ОШИБКА: Черный рынок - фигня с обменом ресурсов: не совпадают данные в JS (сколько будет) и в PHP (сколько стало) - очевидно ошибка округления\n    [*] Артефакты:\n        Инфа с ЧР - в артефакты (?)\n        Сканирование удержания - общее количество кораблей на орбите. Возможно - с задержкой по времени\n\n\n  HyperNova\n  ---------\n    [*] Сверхбыстрый режим - работа только с xcache - но это только после того, как разберусь с $supernova\n    [*] xcache\n        Для блокирования записи использовать xcache_inc/xcache_dec:\n        xcache_inc = 1 => мы блокировали данную запись, можно работать\n        xcache_inc > 1 => запись уже кем-то блокирована, xcache_dec, usleep, повторяем\n          usleep — Delay execution in microseconds. A micro second is one millionth of a second.\n        Двухуровневая блокировка - на уровне \"таблиц\" и на уровне \"записей\". Впрочем, нужна ли блокировка на уровне записей? Может нужна \"общая блокировка\"\n    [*] memcached\n        Так же может использоваться Memcache::add()\n    [*] HyperNova\n        При сохраннении индексов (например - кэш запросов) делать это через implode/explode - потому что всё равно потом прийдется восстанавливать указатели на объекты\n        Так же можно для массивов использовать serialize/unserialize и паковать, скажем, gzip - память дешевле тактов\n        Индексы можно сворачивать через MD5\n    [*] !!!! Поделать мелкие объектики, которые будут инкапсулировать методы и сами следить за своей когерентностью - игроки, планеты, юниты, очереди итд\n        Таки да - нативное обращение типа sn->$user->get()\n        А дальше уже внутри гета пусть сам ебеться с вытягиванием из базы и блокировкой - или потом как-нибудь? Это же нас к КК не приблизит?\n\n\n  Ссылкота\n  --------\n    [*] ioncube http://habrahabr.ru/post/191176/\n    [*] http://www.php.net/manual/ru/function.version-compare.php\n    [*] http://explainextended.com/2009/10/07/in-list-vs-range-condition-mysql/ - поиск по двум диапазонам\n    [*] http://www.smashingmagazine.com/2014/04/24/rebuilding-an-html5-game-in-unity/\n\n\n    [*] phonegap ручной билд требует только android.permission.INTERNET\n        http://www.smashingmagazine.com/2014/02/11/four-ways-to-build-a-mobile-app-part3-phonegap/  пример приложения\n        http://propertycross.com/jquerymobile/ пример приложения\n        http://tech.pro/blog/1704/2-ways-to-get-started-with-phonegapapache-cordova   пример приложения\n        http://habrahabr.ru/post/118059/\n        http://stackoverflow.com/questions/14818138/loading-remote-html-in-phonegap-or-cleaver-cordova-on-ios\n        http://stackoverflow.com/questions/15343647/using-phonegap-cordova-on-ios-with-external-url\n        http://blog.safaribooksonline.com/2012/02/29/phonegap-storing-and-retrieving-with-the-filesystem/\n        https://github.com/apache/cordova-plugin-file/blob/master/doc/index.md\n        http://docs.phonegap.com/en/1.3.0/phonegap_connection_connection.md.html#Connection - БОЛЕЕ НОВАЯ ВЕРСИЯ НУЖНА!\n        http://cordova.apache.org/docs/en/3.0.0/guide_cli_index.md.html#The%20Command-line%20Interface_customize_each_platform настройка вида под разные платформы\n        http://www.adamwadeharris.com/android-automation/   build and publish\n        http://fusiongrokker.com/post/follow-up-on-phonegap-build-compiled-file-sizes  reduce apk size\n        http://www.justbeck.com/three-ways-to-encrypt-phonegap-and-cordova-mobile-applications/\n        http://stackoverflow.com/questions/12177134/supporting-multiple-resolution-and-density-of-images-in-phonegap\n        http://knockoutjs.com/examples/simpleList.html - MVVM framework в помощь jqmobile\n    [*] paypal api http://habrahabr.ru/post/128198/\n    [*] google payment\n        http://developer.android.com/google/play/billing/gp-purchase-status-api.html\n        https://developers.google.com/discovery/libraries\n        http://docs.madewithmarmalade.com/display/MD/S3E+Android+Google+Play+Billing%3A+Using+the+API\n\n        http://hashcode.ru/questions/286954/php-как-сделать-закрытый-веб-api-для-андроид-приложения\n        https://groups.google.com/forum/#!topic/google-api-php-client/13N4rJ-zT2w\n        http://habrahabr.ru/post/160911/\n        https://github.com/thetutlage/Google-Play-Store-API\n        http://code.google.com/p/google-playstore-api/\n        http://grokbase.com/t/gg/android-market-api/1256vwc5js/access-google-play-apps-with-appaware-public-api\n    [*] http://rmcreative.ru/blog/post/s-yii-1.1-na-yii-2.0--chast-1-php#comments\n        <p>Итак, для перехода на 2.0 стоит изучить:</p>\n        <li><a href=\"http://ru2.php.net/manual/ru/language.namespaces.php\">Пространства имён</a>.</li>\n        <li><a href=\"http://ru2.php.net/manual/ru/functions.anonymous.php\">Анонимные функции</a>.</li>\n        <li>Короткий синтаксис массивов. Тут всё просто, вместо <code>array()</code> теперь <code>[]</code>.</li>\n        <li>Короткий <code>echo</code> в виде <code>&lt;?=</code>. Доступен всегда. Используется во всех view-шаблонах фреймворка. <a href=\"http://www.php.net/manual/ru/language.basic-syntax.phptags.php\">При этом <code>&lt;?</code> пользоваться, как и ранее, небезопасно</a>.</li>\n        <li><a href=\"http://ru2.php.net/manual/ru/book.spl.php\">SPL</a>.</li>\n        <li><a href=\"http://ru2.php.net/manual/ru/language.oop5.late-static-bindings.php\">Позднее статическое связывание</a>.</li>\n        <li><a href=\"http://ru2.php.net/manual/ru/book.datetime.php\">datetime</a>.</li>\n        <li><a href=\"http://ru2.php.net/manual/ru/language.oop5.traits.php\">Трейты</a>.</li>\n        <li><a href=\"http://ru2.php.net/manual/ru/book.intl.php\">intl</a>. В Yii2 многое из intl завёрнуто в компонент i18n, но не всё.</li>\n    [*] force reload\n        http://www.w3schools.com/jsref/met_loc_reload.asp\n        http://www.stefanhayden.com/blog/2006/04/03/css-caching-hack/\n        http://en.wikipedia.org/wiki/Wikipedia:Bypass_your_cache\n        http://stackoverflow.com/questions/118884/what-is-an-elegant-way-to-force-browsers-to-reload-cached-css-js-files - заставить браузер перечитывать CSS и JS\n    [*] http://www.merzo.net/index.html WELCOME TO STARSHIP DIMENSIONS... A Museum of Speculative Fiction inspired Spaceships\n    [*] Рост кристаллов\n        https://ru.wikipedia.org/wiki/Фронтальный_клеточный_автомат\n        http://www.mat.net.ua/autor/julia01.htm\n        http://cyberleninka.ru/article/n/modelirovanie-rosta-dvuhkomponentnogo-kristalla-so-sloistym-raspredeleniem-primesi\n        http://www.rsdn.ru/article/livepixels/LivePixels.xml\n        http://neoneuro.com/ru/live-pixels-ru/app\n\n\n\n    [*] Тексты: реализация \"демона максвелла\"\n        Сколько я помню школьную физику - дальше физика в моей жизни как-то не\n        встречалась - отводить придется, поскольку иначе кораблик начнет немного\n        сильно греться, там же выполняется работа. А отводить тепло в вакууме\n        можно либо просто излучением, либо выкидывая горячее наружу, например по\n        принципу демона Максвелла, что, сколько понимаю, вы и предлагаете.\n\n        Проблема с направленным отведением, на мой взгляд, состоит в том, что\n        тепло имеет свойство рассеиваться. То есть, мы все равно получим тепловой\n        след, причем интенсивность этого следа будет, как минимум, не меньше, чем\n        от работающего двигателя, так? Так-то можно было бы просто лететь носом к\n        врагам, дюзы-то все равно сзади.\n\n        Допустим, корабль взял с собой запас холода в виде жидкого воздуха - и\n        дышать есть чем, и тепло наружу не сбрасывается..\n\n        Я предлагаю не рассеивать горячее вещество, а излучать тепловые лучи. Но\n        излучать в одном направлении, окружив горячий радиатор зеркалом. Если\n        излучать строго от Солнца - ничего не заметят на его фоне.\n\n        Тепловой след от газа в вакууме почти не образуется, т.к. молекулы быстро\n        разлетаются и не сталкиваются.\n    [*] Текст: Крисберги - кристалл работает не с сознанием, а с подсознанием\n        Аморальный подсознанием\n        Поэтому крисберги эффективнее людей с кристаллическими компьютерами - и поэтому же опаснее\n        Крисберг - гибрид человека и кристалла, который перешел Предел Человечности\n    [*] Текст: Микробиотики - антибиотики из микроботов\n\n\nРЕЛИЗ 40\n========\n  [*] mysqli !!!!!!!! ПРОВЕРИТЬ ЕЩЕ РАЗ ВСЕ ВВОДИМЫЕ ДАННЫЕ !!!!!!!\n\n  [*] Инсталлятор\n  [*] Апдейтер\n\n  [*] Артефакт: \"планетарный телепортер\"\n  [*] Автоматическая конвертация ТМ в ММ по опции\n  [*] Админка - добавить в список игроков начисление ТМ и ММ\n  [*] АРТЕФАКТЫ - КУПИТЬ НЕСКОЛЬКО\n  [*] ЛОГ ТМ / ММ для игрока\n  [*] В ЧР ограничивать количество покупаемых кораблей ресурсами на планете\n  [*] todo - в капитане дропнуть поле captain_level - оно всегда равно unit_level\n  [*] Ачивки, интегрированные с наградами\n  [*] Модуль наград\n\n\n\nКК\n  [*] Новая характеристика - теплоёмкость. Щиты увеличивают теплоёмкость. Нужно для баланса энергооружия и объема, а так же для использования щитов, а не брони\n\n\nРЕЛИЗ 39\n========\n  !!! Сменить лицензионную политику. Модули под ионкубе - см. ссылкоту\n\n\n  [*] МОДУЛИ: проверить работу\n  [%] шпионаж не дает нормальный ID fleet_end_planet_id 'dst_planet'\n  [*] Модуль КК\n  [*] Модуль квестов \"любые награды + мультиусловия\"\n  [*] Не отсылаются сообщения очередей\n  [*] Отключено удаление аккаунта\n\n  [?] Убрать автопополнение стокмана из overview.php. Или, может, оставить? Добавить только rand()\n  [*] // FK_referrals_id  'DELETE FROM `{{referrals}}` WHERE `id`             not in (select id from {{users}});',\n  [*] // FK_referrals_id_partner  'DELETE FROM `{{referrals}}` WHERE `id_partner`     not in (select id from {{users}});',\n  [*] // FK_stats_id_owner  'DELETE FROM {{statpoints}} WHERE stat_type=1 AND id_owner not in (select id from {{users}});',\n  [*] БД таблица `unit` индекс I_unit_location - НЕ НУЖЕН, если везде проносить user_id - что, в целом, не сложно, наверное - потому что здоровый очень индекс\n\n\n  Под вопоросом в 39-м релизе\n  ---------------------------\n    [*] Переписать ранки Альянсов - кроме ID сделать еще sort_list для упорядочивания без смены ID\n    [*] !!!!!!!! GlobalMessage\n    [*] !!!!!!!! Переименовать юзеров, планеты, альянсы, тэги в более безопасные - без символов %?\"'<>/\\[](){};:,.^\n    [*] !!!!!!!! В чате - включать пользователям-премиум на месяц и выше разные фишки. Так же включать \"бессмертным\"\n\n    [*] Отвязать корабли флотов от флотов\n    [*] Отвязать губера PLANET_GOVERNOR_ID PLANET_GOVERNOR_LEVEL\n\n    [*] Refactor: change signature - попоменять порядок переменных. Например - mrc_get_level($unit_id, $user, $planet итд)\n    [*] рекорды - переписать на отдельную таблицу и что бы обновлялась вместе со статистикой\n    [*] Экспы: добавить нахождение Артефактов\n    [*] GeoIP - в админке и на странице Императора //todo - проблема с IP - берет IP_v6 - надо настраивать php или апач/lighttpd\n    [*] Унифицировать интерфейс постройки/конструирования/исследования\n    [*] ДЛЯ РАКЕТ СДЕЛАТЬ ПАРАМЕТР P_CONTAINS_IN - указывать ссылку сооружения, где они могут находиться\n    [*] Заменить артефакты строительства/исследования на простое ускорение за ТМ. Добавить ускорение постройки кораблей (?)\n    [*] Опция - автоконвертирование ММ в ТМ при нехватке ТМ\n    [*] СДЕЛАТЬ ЗДАНИЕ ДЛЯ ОБОРОНЫ - И ПРОВЕРЯТЬ ЕГО НАЛИЧИЕ\n    [*] СДЕЛАТЬ РАЗРУШЕНИЕ РАКЕТ - в шахте и в полёте\n    [+] Альянсы: лог транзакций\n    [*] Проверить поля для ввода данных: ЧР, экраны Альянса - подабавлять мультиэлементы\n    [*] ОЧЕРЕДЬ ИССЛЕДОВАНИЯ - расширять академиком\n    [*] Констраинт на planets.id_owner => users.id ON UPDATE CASCADE ON DELETE CASCADE - НИХУЯ, ставить в 0 и пусть летять\n    [*] Разрешить грабить удалённые планеты и луны тем флотам, что в полете! Проверить, как вообще работает destroyed поле\n    [*] Поменять название всем файлам, вызываемым из АЯКСА на ajax_\n\n\n  Good Enough\n    [*] Спасательная капсула - капитан спасается, одноразовая, тыщ 20-30кТМ\n    [*] sys_o_get_updated слишком много хочет обновлять подряд юзеров при нескольких планетах - а нада один раз - но эта тока при апдейте\n    [*] сеты - доделать\n    [*] отложенные ченйджсеты - доделать\n    [*] Готовить чейнджсеты - и применять их по _commit, отменять при _rollback\n    [*] Паковать чейнджсеты\n    [*] Отдетектить updates/delete/replace/insert вне транзакций! И обернуть\n    [*] Заменить все обращения к unit - выставлять отдельные флаги, что вынуто ВСЁ\n    [*] Заменить все обращения в que - выставлять отдельные флаги, что вынуто ВСЁ\n    [*] МОЖНО КЭШИРОВАТЬ ЦЕЛЫМИ ЗАПРОСАМИ!!!! Т.е. get_user_list_by_id(huemoe); - кэшировать. Строить индексы соответствия - апдейтить данные\n    [*] SET-функции могут сами в случае нужды запускать транзакции\n    [*] переделать позже db_gеt_record_list($location_type, $filter = '')\n    [*] НЕ НУЖНО АЛЬЯНС КАЖДЫЙ РАЗ ОБНОВЛЯТЬ!!! сommon.php line 109 and further\n    [*] !!! Некоторые групповые запросы не обязательно спускать в БД! Достаточно пройтись и позаменять по кэшу! Все равно если нужно будет что-то не из кэша - его потом можно будет подтянуть!\n    [*] Все set_ функции заставить в качестве сета давать список полей а-ля db_prepare_changeset\n    [*] В db_changeset_apply - сделать так же изменение соответствующей информации в кэше\n    [*] $supernova: оптимизировать количество SQL-запросов\n    [*] Всегда приводить емейлы к нижнему регистру!\n    [*] Пересчитывать скорости постройки при достройке верфи/нанофабрики\n    [*] //todo $fields\n    [*] ОБНОВЛЕНИЕ - сделать разбор {{table}} при upd_alter_table\n    [*] db_change_units()  // TODO: THIS FUNCTION IS OBSOLETE AND SHOULD BE REPLACED!  // TODO - ТОЛЬКО ДЛЯ РЕСУРСОВ  проверить по модулям  переименовать в db_change_resources()  И вообще избавиться от неё\n    [*] Возвращать в пакетах индексы - и уже затем получать инфу по индексам, что бы не засирать память\n\n\n\nФАЗА ? - Админка\n================\n[!] ВНИИМАНИЕ! ДАННЫЙ ПАТЧ ЯВЛЯЕТСЯ ПРОМЕЖУТОЧНЫМ! В НЁМ НЕ РАБОТАЮТ НЕКОТОРЫЕ ФИШКИ\n    В админке отключена \"Панель админа\" и \"Редактирование планеты\"\n[!] Кстати, сейчас в Верфи и Обороне строится только первое из выбранных сооружений. Забыл написать\n[!] Админка\n    Отключены: панель админа, компенсация планет - может что-то еще\n    В начислении ТМ и ММ поиск производится только по имени пользователя\n    Убрана возможность начислять ТМ по планете\n\n\n  [*] Админка: редактирование планеты: переписать и добавить функционал бесплатного модуля - что бы включался только при наличии хоть какого-нибудь модуля (?)\n  [*] Админка: панель админа - переделать\n  [*] Админка: компенсацию планет - проверить\n\n\nФАЗА ?\n======\n   [*] интерфейс КК\n\n\n\nФАЗА ?\n======\n  [*] добавление корпусов и модулей кораблей, балансировка\n  [*] Количество залпов в ракете/пушке\n  [*] Переделать UBE для поддержки (?) - динамический расчёт данных по амплифай для всех участвующих флотов\n\n\n\nФАЗА ?\n======\n  [*] перенос $sn_data в БД\n  http://php.net/manual/en/function.create-function.php\n\n  [*] Писать в логи если ММ или ТМ меньше 0\n  [*] Индексы по новому менеджеру флотов\n  [*] Какая-то непонятная хуйня в админке при переключении типа наёмников - проверить\n  [*] Имя последнего зарегестрированного пользователя вообще-то надо хранить в конфигурации\n  // TODO Заменить на que_tpl_parse_element($que_element); tpl_helpers.php->tpl_parse_planet() 198 и 200\n  sys_o_get_updated - разнести, все-таки, процессинг юзеров и юнитов\n  [%] Ошибка Статистика - 24:00:00 ошибка, а 24:: - нормально\n  [%] Не работает сортировка по размеру планеты! Не учитывает террформер/лунную базу. Сделать отдельное поле для базового стартового количества секторов, максимального и текущего. И вообще разобраться с полями\n\n\n\n\nФАЗА ? - Интерфейс\n==================\nНАДО ЕЩЕ ИЗМЕНЯТЬ ДАКМАТТЕР И МЕТАМАТТЕР В КЭШЕ В СООТВЕТСТВУЮЩИХ ПРОЦЕДУРАХ\nИ вообще - сделать вызов по чейнджсету\nА еще лочить всех реферрал-юзеров - что бы не было дедлоков !!! Просто лочить - их данные нам не нужны\nПо начислению ТМ с рефераллами - тупо блокировать все ИД юзеров, которые есть в таблице реферралов - возможно прийдется сделать сбоку припёку\n\nЮниты - ВЫБИРАТЬ ЮНИТЫ ПО ВРЕМЕНИ! Т.е. что бы наемники действовали на момент боя, например\n\n\n\n\nДля выливания на живой\n======================\n\n\nТекущий билд #45d0#\n~~~~~~~~~~~~~~~~~~~~~\n\n// НА ЛОКАЛХОСТЕ ФЛОТЫ ЛЕТАЮТ !!!! Поменять fleet_update_last\n\nПоследовательность блокировок user-planet-unit-que-fleet-остальное\n\n\nПервый дедлок появился в 15692\t2014-05-11 19:43:28 - посмотреть, что было сделано между этим временем и 16 часами - когда дедлока еще вроде не было\nЕсли не поможет - добавить предвыборки в двух местах\nЕсли не поможет - включить постоянный лог\nПо финалу можно сделать SERIALIZABLE\nВРОДЕ ПОМОГЛО\nЛИБО function db_planet_count_by_type($user_id, $planet_type = PT_PLANET) убрал FOR UPDATE\nЛИБО public static function db_transaction_start($level = '') добавил     static::$locator = array(); static::$queries = array();\n\n\n\ndelete FROM `tst_logs` where log_text like \"%Update in progress. Step%\";\ndelete FROM `tst_logs` where log_text like \"Running stat updates%\";\n\nсделать I_unit_player_location_snid UNIQUE - перед этим почистив таблицу от дубликатов\n\nв настройках сервера сделать редирект - что бы сразу всё отражалось\n\nпосмотреть баг в статистике с прасингом данных\n\n\nсделать бэкап и потереть всё на тесте - оставить только планеты. Потереть ресы на юзерах и их экспириеныс\n\n\n// todo\nТам есть очень хитрый баг, который всплывает только при большом количестве игроков. Надо его отловить\n\n\n!!! Запустить перевод !!!\n1. Перевод локализации:\n   0. Допереводить на английский\n   1. Оптимизировать локализацию - убрать дубликаты строк (выгрузить всё в БД, собрать из БД список дубликатов, проверить, что дубликаты - это реально дубликаты)\n   2. Посчитать количество страниц в переводе - стандартная страница 1860 символов\n2. Узнать цены и сроки\n   1. На вычитку английского\n   2. На перевод\n  3. Пока ссуть да тело - пофиксить выпадение языков, т.е. разбить языки на серверный, текущий и контекстный\n\n\nОТКЛЮЧИТЬ ЛОГ ЗАПРОСОВ!\n\n\nПроверить - уменьшаются ли корректно при ракетной атаке:\n1. Ракеты - при полном уничтожении атаки (когда интерсепторов больше, чем МПР)\n2. Ракеты - при частичном уничтожении атаки (когда интерсепторов меньше, чем МПР)\n3. Оборонные сооружения - при прошедшей по планете атаке\n\n\nТолько атмосферные двигатели - требуют наличие еще и межпланетных, иначе корабль не может перелетать между планетами !\nОтдельно - атмосферная скорость, космическая скорость, варп-скорость\n\nУМЕНЬШИТЬ ДОХОД ДЕЙТЕРИЕВЫХ ШАХТ! Потому что меньше расход топлива у всех!\n\nДедлоки:\n1. При отправке флота лочить одним запросов юзеров-владельцев планеты-отправления и планеты-назначения - как в свозе, в аяксе и просто при отправке\n    16730\t2014-05-16 19:18:58\n2. Когда лочатся юзеры - лочить и Альянсы! Проверить - как лочаться Альянсы, лочат ли они пользователей\n    16873\t2014-05-18 20:41:05\n    16872\t2014-05-18 16:39:22\n\n\n\nИз емкости трюма вычитать стоимость топлива для полёта - добавочное поле в таблице fleets\n\n\nselect username, FROM_UNIXTIME(register_time), FROM_UNIXTIME(onlinetime), onlinetime - register_time\n\ndelete\nfrom delta_users where onlinetime - register_time  < 60*60 and register_time < UNIX_TIMESTAMP(NOW()) - 24 * 60*60*45\nand metamatter_total = 0\n\nЭкспа - каждый корабль может утащить только то же количество кораблей своего типа - или больше, если другого\n\nЮЗЕР в sys_o_get_updated() МОЖЕТ быть пустой, когда идет апдейт УДАЛЕННОЙ планеты - сделать проверку по owner_id планеты\n\ngit branch -f branch-b branch-a\nWill update branch-b to point to the head of branch-a.\n\nНе показывать рассовые корабли; не показывать корабли других игроков\n\n\n\n\n\n\nДобавлять в каждое письмо футер - \"Настроить отправку уведомлений на емейл можно в настройках\"\n\n\nНовости - не отсылает уведомление об\n\n\n\nОграничивать дальность видимости с планеты, убрать цену за скан, если еще была. Артефакты, все дела\nОтсылка шпиона позволяет открывать планету (?)\n\n\nОчереди - пересчитывать при постройке нанитки/верфи/лабы/нанолабы - И ПРИ РАЗРУШЕНИИ!\nДописать подсказку для исследований и постройки юнитов/обороны\nсделать кнопку замера времени\nПоказывать день рождения на странице Император\n\n\n* Посчитаться и расплатиться за отработанный хостинг\n* Сделать страничку сбора бабла на КК\n* ПейПол\n* Оплата через ГугльПлей - КУЧА нюансов и геммора\n* Андроид-приложение\n\n\nАльфа - 2-2-10\nБета - 100-50-500 ???\nГамма - 1-1-1\nДельта - 10000-100-10000\n\n\nЦентрирование модального окна\n jQuery.fn.center = function () {\n    this.css(\"position\",\"absolute\");\n    this.css(\"top\", ( jQuery(window).height() - this.height() ) / 2+jQuery(window).scrollTop() + \"px\");\n    this.css(\"left\", ( jQuery(window).width() - this.width() ) / 2+jQuery(window).scrollLeft() + \"px\");\n    return this;\n  }\n $('#seller_review').show().center();\n\n\n\n\n\nПремиум - добавлять к левелу хранилищ\n\nНастройка - \"предупреждать, если в очереди больше 30 минут постройки текущей прошло\"\n\nО! Ценный артефакт - запрет шпионажа\n[14:27:20] Project \"SuperNova.WS\": 30 ТМ (3 бакса) на 24 часа на все планеты\n\n\nПланеты и луны - приносить ТМ от застройки или спецсзадние (?)  - сбалансировать с ценой сектора\nПодарки на ТМ - совочек, грабельки и грузовичок\nДобавить пароль на передачу и роспуск Альянса\n\n\n\n\n\n\n\n\nХранить хэши пользователей в таблице - аналог сессий. Т.е. кукесы содержат только полностью обезличенную информацию о пользователе\nКонвертер старых кукесов\nИмперсонация не поддерживается в АПИ\narray_replace() - теперь у нас код 5.3.0, поэтому можно использовать вместо своей собственной мерджи или + операций\n\n\n\nКЛЮЧ В КОНФИГУРАЦИЮ И НАСТРОЙКА НА set_memory_limit для рассчета статистики!!! Пока - хардкод 256М\n\n\n\n\n\nКапитаны - прокачка по кораблям: эффективность вождения кораблей, эффективность против\n\n\nSELECT FROM_UNIXTIME(onlinetime), FROM_UNIXTIME(register_time), (onlinetime - register_time)/60, dark_matter, metamatter_total\nFROM `game_users` where\nmetamatter_total = 0 and dark_matter = 0\nand onlinetime - register_time < 1 * 60\nand FROM_UNIXTIME(register_time) < DATE_SUB(date(now()),INTERVAL 1 week);\n\nSELECT FROM_UNIXTIME(onlinetime), FROM_UNIXTIME(register_time), (onlinetime - register_time)/60, dark_matter, metamatter_total\nFROM `game_users` where\nmetamatter_total = 0 and dark_matter = 0\nand onlinetime - register_time < 10 * 60\nand FROM_UNIXTIME(register_time) < DATE_SUB(date(now()),INTERVAL 2 week);\n\nSELECT FROM_UNIXTIME(onlinetime), FROM_UNIXTIME(register_time), (onlinetime - register_time)/60, dark_matter, metamatter_total\nFROM `game_users` where\nmetamatter_total = 0 and dark_matter = 0\nand onlinetime - register_time < 15 * 60\nand FROM_UNIXTIME(register_time) < DATE_SUB(date(now()),INTERVAL 3 week);\n\nSELECT FROM_UNIXTIME(onlinetime), FROM_UNIXTIME(register_time), (onlinetime - register_time)/60, dark_matter, metamatter_total\nFROM `game_users` where\nmetamatter_total = 0 and dark_matter = 0\nand onlinetime - register_time < 60 * 60\nand FROM_UNIXTIME(register_time) < DATE_SUB(date(now()),INTERVAL 1 month);\n\nSELECT FROM_UNIXTIME(onlinetime), FROM_UNIXTIME(register_time), (onlinetime - register_time)/60, dark_matter, metamatter_total\nFROM `game_users` where\nmetamatter_total = 0 and dark_matter = 0\n-- and onlinetime - register_time < 60 * 60\nand FROM_UNIXTIME(register_time) < DATE_SUB(date(now()),INTERVAL 1 month)\nand FROM_UNIXTIME(onlinetime) < DATE_SUB(date(now()),INTERVAL 1 month) ORDER BY onlinetime;\n\n\nSELECT FROM_UNIXTIME(onlinetime), FROM_UNIXTIME(register_time), (onlinetime - register_time)/60, dark_matter, metamatter_total\nFROM `game_users` where\nmetamatter_total = 0 and dark_matter = 0 and (\n(onlinetime - register_time < 1 * 60 and FROM_UNIXTIME(register_time) < DATE_SUB(date(now()),INTERVAL 1 week))\nOR\n(onlinetime - register_time < 10 * 60 and FROM_UNIXTIME(register_time) < DATE_SUB(date(now()),INTERVAL 2 week))\nOR\n(onlinetime - register_time < 15 * 60 and FROM_UNIXTIME(register_time) < DATE_SUB(date(now()),INTERVAL 3 week))\nOR\n(onlinetime - register_time < 30 * 60 and FROM_UNIXTIME(register_time) < DATE_SUB(date(now()),INTERVAL 1 month))\nOR\n(FROM_UNIXTIME(register_time) < DATE_SUB(date(now()),INTERVAL 1 month) and FROM_UNIXTIME(onlinetime) < DATE_SUB(date(now()),INTERVAL 1 month))\n) ORDER BY onlinetime, register_time;\n\n\n\n\n\n\nУвеличение очереди за ТМ\n\n\nПисьмо с предложением дружить - ссылка на страницу или описание, как туда попасть\n\n\n\n\n\nДля админов - попап над каждым ником с общей суммой платежей - через ММ в баксы по курсу\n\n\nGIF анимация может использовать прозрачность для того чтобы не сохранять очередной кадр целиком, а только изменения относительно предыдущего.\nРазличные реализации алгоритма Deflate дают разную степень сжатия, поэтому были созданы программы для пережатия изображений с несколькими вариантами настроек в целях получения наилучшего сжатия — например, форк pngcrush OptiPNG и advpng из комплекта AdvanceCOMP (использует 7-Zip).\n\n\nПоказывать стоимость флота при отправке\n\nСТАТИСТИКА - быть экономней с памятью при расчете! Удалять ненужные данные\n\n\n#ctv  - КК, Фаза 10. Балансировочная\n\nКЛИЕНТ!!!!\nПроверка на бан, проверка на отпуск\n\n\nбронежилет\nexoskeleton\npowered armor\nadaptive armor\n\n\n\nhttps://ardelfi.livejournal.com/137467.html?nc=51#comments\nМотор Эпштейна -- это термоядерный реактор на полностью безнейтронной реации синтеза гелия-3 (³He+³He → 4He+2p+12.86 MeV ). Других вариантов нет, иначе такие корабли были бы несовместимы с жизнью. Даже протон-бор с долями процента энергии в побочных нейтронах убил бы всё живое при гигаваттных мощностях реакторов. Также полностью заряженные продукты реакции дают возможность производства электроэнергии в сравнимых количествах через МГД-генератор, а может и через фотопреобразователь рентгена в электричество. Выхлоп у него конечно плазма, но это не ионный мотор, а термоядерный.\nСмотря какие рельсы. Планетарные терранские рельсы, что расстреляли марсианские СЯС где-то на орбите Марса, с подлётным временем на глаз минут пять максимум. Это дальность порядка диаметра орбиты Марса -- 450е6км, значит скорость такого снаряда в пять раз выше скорости света. Вот так я наткнулся на предел реалистичности в Expanse. :) Но допустим снаряд летит полчаса -- это скорость 0.8с, всё ещё много. Потому пусть будет час -- 0.4с, или условный предел реалистичности. Ладно, это планетарные -- огромная машина пуляет небольшой снаряд. Пусть тяжёлые (как у Донаджера) в 10 раз слабее по энергии, средние ещё в 10 раз, лёгкие ещё в 10. Зависимость скорости от энергии квадратична, потому пусть разница в энергии снаряда между планетарным и лёгким рельсотроном будет 1е3, значит в скорости порядка 30. Зависимость там сложнее, потому что у планетарного снаряд кластерный, а у корабельных цельный, но пусть будет 30. В результате, при равных условиях, эффективная дальность лёгкого корабельного рельсотрона будет порядка 15е6км, и если эту дистанцию снаряд преодолеет за полчаса, это 8333км/с. Километров в секунду. С метрами в секунду в Expanse делать нечего -- там ракеты могут долететь из пояса астероидов до Солнца за несколько часом максимум (постоянное ускорение 100ж, или порядка 1км/с², за час даёт скорость 3.6км/с, и подлётное время 35ч; при постоянном ускорении 100ж подлётное время ракеты меньше 6 часов ), а это те же 450е6км, то есть они лишь в разы медленнее снаряда рельсотрона.\nC Донаджером у марсианского командира получилась полная жопа. Она думала что самый мощный корабль в системе делает её непобедимой (последние её слова перед самоуничтожением). Она никогда не воевала с равными силами -- на её счету только пираты и подобная мелочь. А в последнем бою она встретилась в равным по технологиями и превосходящим по боевому духу противником, готовым воевать насмерть и жечь свои корабли в атаке. Поэтому контр-атака Донаджера была неправильной. Ракетный залп они пропустили (ядерная ракета попала в двигатель, там видно), а могли сжечь весь залп ракет парой-тройкой своих ракет на траектории перехвата, как потом сделал командир \"Роси\" в очень неравном бою с терранским крейсером. Дальше, вместо пуляния ракет, марсианский командир должен был сделать два дела одновременно: запустить оба фрегата (\"Тахи\" и ещё один в ангаре), у которых свои PDC и куча ракет, и началь пулять рельсами с максимальной дистанции -- траектория сближения с противником была выгодна для такой атаки. У марсиан были тяжёлые рельсы, у терранов лёгкие -- марсиане могли выбить все корабли до их выхода на рубеж огня лёгкими рельсами. Вместо этого марсиане вышли на рубеж ракетной атаки, когда в них уже летели десятки ракет. Потом они начали пулять своими тяжёлыми рельсами с рубежа огня лёгкими рельсами противника, которых там было 6 или 7 кораблей -- получилось два рельса против 6 или 7, что предсказуемо закончилось для марсиан повреждениями. В конце концов ни одна из десятков PDC марсиан не расстреляла десантные капсулы терранов, когда они были буквально рядом. Закончилось всё идиотски, практически рукопашной бойней пехоты. Потом, когда началась война, марсиане начали правильно делать, и соотношение потерь было очень в их пользу. Также в эпизоде с потрошением половины марсианского кораблика, порвало его попаданием рельсы, а они даже не знали что там кто-то в них пуляет -- PDC не были активированы. Дальность стрельбы рельсой без коррекции определяется скоростью снаряда, потому тяжёлый рельсотрон качественно превосходит более лёгкий -- может пулять дальше, а дырка такая же.\n\n\n\nTODO - ФАЗА 0\ntodo - сообщение о бане или отпуске\nfunction sn_autologin($abort = true) - переделать вызовы\nsys_user_options_unpack($user); - разобраться, где нужно\nавтогенерация пароля?!\nпрописать строки локализации\n    LOGIN_SUCCESS => '',\n    REGISTER_ERROR_USERNAME_WRONG => '',\n    REGISTER_ERROR_USERNAME_EXISTS => '',\n    REGISTER_ERROR_PASSWORD_INSECURE => '',\n    REGISTER_SUCCESS => '',\n$skip_ban_check - проверить что и зачем\n    // TODO - GLOBAL_MESSAGES\n    /*\n    //$result = sec_login_username($username_unsafe, $password_raw, sys_get_param_int('rememberme'));\n    //$user = $user['user_row'];\n\n    $Message = $lang['thanksforregistry'];\n    if(sendpassemail($username, $password, $email))\n    {\n      $Message .= \" (\" . htmlentities($email) . \")\";\n    }\n    else\n    {\n      $Message .= \" (\" . htmlentities($email) . \")\";\n      $Message .= \"<br><br>{$lang['error_mailsend']} <br /><b>{$password}</b><br />\";\n    }\n\n    message($Message . $config->adv_conversion_code_register, \"{$lang['reg_welldone']}<b>{$password}</b> <script>document.getElementById('sn_navbar').style.display='none';document.getElementById('page_body').style.marginLeft='0px'; document.getElementById('page_body').style.marginTop='0px';jQuery(document).ready(function(){setTimeout(function(){parent.location='overview.php';}, 10000);});</script>\");\n    */\n\nВЫНЕСТИ ДОБАВЛЕНИЕ ПОЛЬЗОВАТЕЛЯ ИЗ sec_login_register В ОТДЕЛЬНУЮ ПРОЦЕДУРУ - ИЛИ ЦЕЛИКМО ПЕРЕНЕСТИ КУДА-НИБУДЬ\nПо таймдиффу тоже всё не очень радостно - посмотреть, может позже его замерять?\n\nПОЧТОВЫЙ АДРЕС:\n1. Логин по почтовому адресу (основному)\n2. Проверка при создании почтового адреса - основного и вторичного\n3. Проверка при смене почтового адреса - всех адресов\n4. Бесплатная смена ника при реге через емайл\n\n\nПри восстановлении пароля - прописывать его сразу в ячейку пароля. Или логиниться сразу?\n    $result[F_PASSWORD_NEW] = $new_password;\n    $result[F_LOGIN_STATUS] = $operation_result ? PASSWORD_RESTORE_SUCCESS_PASSWORD_SENT : PASSWORD_RESTORE_SUCCESS_PASSWORD_SEND_ERROR;\n    $result[F_LOGIN_MESSAGE] = $message . $operation_result ? $lang['log_lost_sent_pass'] : $lang['log_lost_err_sending'];\nsec_restore_password_confirm($confirm_safe, $result); - кукесы тут выставлять?\n\n\n[10.07.2014 22:43:06] Sabretooth> тогда надо описание к Ракетной шахте сменить =)\n[10.07.2014 22:43:17] Sabretooth> \"С каждым уровнем можно хранить на четыре межпланетных или двенадцать ракет-перехватчиков больше\" - как-то не правильно\n\n\n\nПереименование планеты - 1кТМ. Или 100 ТМ. Глянуть по системе\n\n[20:29:48 | Изменены 20:30:29] igorrnc Капустьянов Игорь Сергеевич:\nhttp://robertomurray.co.uk/blog/2013/server-side-google-play-in-app-billing-receipt-validation-and-testing/\nhttps://code.google.com/p/android-market-license-verification/\nhttp://habrahabr.ru/post/160911/\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n[14:48:02] Project \"SuperNova.WS\": Механизм подтверждения - это потенциально -50% рег\n[14:48:12] Project \"SuperNova.WS\": Я вообще такой, что автоматом впускать игрока в игру\n[14:48:32] igorrnc Капустьянов Игорь Сергеевич: ну после входа уже просить, при определенном действии\n[14:48:44] Project \"SuperNova.WS\": Нажал \"Войти\" - сгенерировался логин, пароль и в бой\n[14:49:35] Project \"SuperNova.WS\": А чо тебе по рассылке не нравится?\n[14:49:39] Project \"SuperNova.WS\": Возвратов - 7%\n[14:49:42] igorrnc Капустьянов Игорь Сергеевич: типа, \"хоппа! вы решили колонизировать вторую планету? Ваша империя растёт, и для защиты её от вашей забывчивости давайте ка закрепим секурность почтой!\"\n\n\n[*] SELECT round(year(user_birthday) / 5) * 5, count(*) FROM `game_users` group by round(year(user_birthday) / 5) * 5;\n[*] SELECT round(year(user_birthday) / 5) * 5, sum(payment_amount) FROM\n    game_payment join\n    `game_users` on payment_user_id = id\n    where payment_status = 1\n    group by round(year(user_birthday) / 5) * 5;\n\nБрандер (нем. Brander) — корабль, нагруженный легкогорючими либо взрывчатыми веществами, используемый для поджога и уничтожения вражеских судов. Мог управляться экипажем, покидавшим судно в середине пути, либо сплавляться по течению или по ветру в сторону вражеского флота.\n\n[*] Подумать еще раз: дейтерий на поддержание флота. По евентам разобрать\n\n[*] Блиц-вселенная: все видят всех\n\n[*] Арена: бои между флотами на заранее заданную сумму\n\n[*] Спиральная галактика - 1 это ядро, остальные - ветки\n\n[*] Покупка отпуска на меньший срок за ТМ\n\n[*] Поправить апдейт, что бы внатуре только админы могли что-то сделать\n\n[*] Добавить НОВОСТИ на все страницы\n\n[*] Чат. Добавить встроенные ББкоде для разделение смайла и имени игрока - пока только в хоронилище\n\n[*] Страница Тёмной Материи - вывести  ссылки  на  все  способы  потратить  ТМ,\n    полный динамический список с привязкой к модулям\n\n[*] http://php.net/manual/en/class.splfixedarray.php\n\n[*] !!!!!!!!!!!!!!!!!!!!!!!! http://php.net/manual/en/class.arrayaccess.php\n    !!!!! Объект как массив !!!!!!\n    $lang, $planet, $user... ДА ВСЁ ПОДРЯД!!!\n    И ЮНИТЫ ТАК МОЖНО БУДЕТ ВЫНИМАТЬ ИЗ БД!\n    http://stackoverflow.com/questions/2881431/arrayaccess-multidimensional-unset\n\n\n[*] Вынести поддержку мультикошельковых систем из webmoney в module_payment\n    И исправить ошибку: pursue -> purse или вообще wallet\n\n[*] Механизм добавления локализаций для JS-строк - еще посмотреть language\n\n[*] Сделать метаматерию полностью модулем\n\n[*] Конверсия MM -> TM - на странице ТМ\n\n\n[*] Спасательная капсула для населения\n\n[*] admin_planet_edit_extra - плотность\n\n[*] Переработать констраинты\n\n[*] Добавить в отчет по платежам аллы и фильтр по ним\n\n[*] Считать экспу/левел по факту, а не на странице обзора планеты/император\n\n[*] Награды\n    Орден: за Выдаюзиеся Заслуги. Например - спонсорство\n    Медаль: за Серьезные Достижения. Например - победа в конкурсе\n    Значок - он же \"ачивка\": \"построил 10000 кораблей\"\n    Памятный знак - за существование и участие. \"4 года в игре\". \"Был онлайн в новогоднюю ночь 2013\". итд\n    Переходящий вымпел - индикация статуса на сервере: \"Топ-1\", \"Топ\", \"Сабтоп\", \"Самый большой флот\" итд\n\n\n[*] Расширенные закладки - добавлять так же состав флота и тип миссии,  что  бы\n    отправлять одним-двумя кликами\n\n[*] In Ender's Game, the Formics were like this due to a Hive Mind, and humans compensated by learning how to create instant communication technology.  Ender in fact commented on how the game he was playing was unrealistic because of the instant communication, when unbeknownst to him, it was actually real. Bean realizes the game is real for this very reason in a later book, but never informs others It also does filter through a chain of command, albeit brief, once he started training with his squad leaders. This is usually not a problem. Bean first realizes the truth after an instructor asks him about a book Bean supposedly checked out from the Battle School library involving ancient battle tactics of building multiple layers of defense structures. Bean, having used the book as a cover and only skimmed it (he has eidetic memory, though), tries to justify his interest in it, only to realize that it's nigh-impossible to defend a planet without a truly massive fleet (which the Buggers actually have), so the only viable option to humans is attack.\n\n[*] Сценарии боя - можно задать юнитам порядок убийства и/или обороны\n\n[*] Вернуть рекламу и ссылки на движок/триолан в меню\n\n[*] Сделать выбор расы на экране описания рас\n\n[*] Еще один уровень АКК\n\n\n[*] Астрофизика\n    http://board.ru.ogame.gameforge.com/board494-ogame-ru-игра/board496-энциклопедия/board259-огейм/144947-редизайн/\n    http://board.ru.ogame.gameforge.com/board494-ogame-ru-игра/board496-энциклопедия/board262-флоты-и-оборона/52313-всё-о-колониях-и-колонизации/\n    http://board.en.ogame.gameforge.com/board1-news/board958-old-news/board959-old-gamenews/537929-redesign-news-reset-and-features/\n\n[*] It's dangerous to go alone! Take this.\n[*] There is nothing to see here. Move along!\n\n[*] OpenID/OAuth?: https://github.com/openid/php-openid\n\n\n[21:07:23] igorrnc Капустьянов Игорь Сергеевич: http://www.tinymce.com/\n[21:07:26] igorrnc Капустьянов Игорь Сергеевич: в движок\n[21:07:37] igorrnc Капустьянов Игорь Сергеевич: чтобы было выравнивание, встаква урлов и тд.\n[21:07:42] Project \"SuperNova.WS\": НАХУЙ!\n[21:08:05] Project \"SuperNova.WS\": Только - хардкор. Только - bbcode\n[21:09:07] igorrnc Капустьянов Игорь Сергеевич: тебе виднее\n[21:09:13] igorrnc Капустьянов Игорь Сергеевич: <head><!-- CDN hosted by Cachefly -->\n<script src=\"//tinymce.cachefly.net/4.0/tinymce.min.js\"></script>\n<script>\n        tinymce.init({selector:'textarea'});\n</script>\n</head>\n<body>\n        <textarea>Your content here.</textarea>\n</body>\n\n[*] Сообщение главе о запросах на дипломатию/смену статуса\n[*] rpg_exchange_darkMatter => rpg_exchange_darkmatter\n[*] Дневной бонус\n\n[*] «Магическое число семь плюс-минус два» («кошелёк Миллера») — закономерность, обнаруженная американским учёным-психологом Джорджем Миллером, согласно которой кратковременная человеческая память, как правило, не может запомнить и повторить более 7 ± 2 элементов. Эта закономерность была изложена в его работе The Magical Number Seven, Plus or Minus Two: Some Limits on our Capacity for Processing Information, увидевшей свет в 1956 году в Psychological Review.\n[*] Меню\n    1. Настройка персонального меню - опции по разделам\n    2. Свернуть/развернуть по разделам с сохранением опций и восстановлением при рендере\n[*] Надо придумать, как отделить шахтеров от рейдеров\n[*] Начисление ресурсов за активность (каждый день заход в игру) и за возвращение\n[*] Нормальный яваскрипт с биндами на мессджлисте в админке\n\n[*] Поискать $parse, $PageTPL и $RowsTPL - это покажет необработанные страницы. gettemplate(*, false) или без второго параметра, parsetemplate()\n[*] admin/overview -> admin/adm_overview\n[*] simple_table.tpl.html - ????\n[*] .tpl.html => .tpl.php\n\n\nИдентификация браузера\nhttps://panopticlick.eff.org/index.php?action=log&js=yes\n\n\n[*] Статистика - медиана\nFor medians in almost any SQL (assumes (COUNT(*)+1)/2 returns an int, if not then use INT((COUNT(*)+1)/2))\nSELECT x.val from data x, data y\nGROUP BY x.val\nHAVING SUM(SIGN(1-SIGN(y.val-x.val))) = (COUNT(*)+1)/2\n\n[*] readme.txt -> readme.html\n\n[*] Заменить переменные на константы\n    define('SN_TIME_NOW', SN_TIME_NOW);\n\n\n[*] Возможность отключить локальное время\n[*] Добавить настройку скорости экспы в админку и в инфу о сервере\n[*] Добавить в Утилиты отчет о проделанных действиях\n[*] Период полураспада игрока. Период полураспада - хороший показатель интересности игры. Это - интегральная оценка качества аудитории. Надо сделать лог\n[*] Экспедиции. Фаза 2 - Астрофизика\n    Добавить влияние левела Астрофизики на вариабельность качества ресурсов\n\n[*] Выбор по скока записей на странице в мессджлисте в админке И переделать инпуты-сабмиты в кнопки!\n\n\n[*] Не производить замер времени, если имперсонейт стоит\n[*] Сделать перерасчет статистики сильно чаще\n[*] Пенальти в бою при покупке корабля с большими боевыми техами, чем открыто сейчас\n\n[*] Медаль за победу в конкурсе аффилейтов\n\n[*] Чат - неправильное время мьюта\n    > у админа надпись неправильная на рожице красенькой - счас время 21:48 - а у него тама до 19:53 - закончилося?\n\nselect DATE_FORMAT(payment_date,'%Y-%m'), sum(payment_amount) from game_payment where payment_status = 1 group  by DATE_FORMAT(payment_date,'%Y-%m');a\n\n[*] Не удалять древние акки. Ввести дополнительно в список онлайна параметр активности\n[*] Не делать замер времени, если под имперсонейтом!\n\n[*] Супертранспорты - для боя (перебалансить защиту), гипертранспорты - для сейва\n\n[*] Планетарный щит - отключается атакой\n\n[*] Скрипт - собрать всю инфу из архивов об игроках\n\n[*] Реферралка - доработать механизм картинки-реферралки: что бы с миинимальной нагрузкой и подкрепить анализом запросов\n\nстатистика - время обновления - не учитывает часовой пояс игрока - не удобно\n\nРазбан за ММ. Сделать доступной страницу бана и покупки ММ\n\nНе удалять старые акки. Добавить новый показатель - активное ядро (количество логинившихся за последнюю неделю)ы\n\nСделать-таки бэкап по мэинтенансу. Вместе с регулярным мэинтенансом\n\nОтправка флота - подбор флота - риски (ака лейблы, ака черточки) для определения части флота. Плюс кнопка \"все\"\n\nКраткие названия для построек в очередях типа МИС\n\nВ Обзоре Империи сделать ссылку на Новапедию с названий юнитов. И вообще - переверстать со стилями, делегейтами итд итп\n\n\nSELECT\n\tdate_format(payment_date, '%Y-%m'),\n\tsum(payment_amount)\nFROM\n\t`game_payment`\nWHERE\n\tpayment_status = 1\nGROUP BY\n\tdate_format(payment_date, '%Y-%m');\n\n\nSELECT\n\tpayment_user_id,\n\tusername,\n\tsum(payment_amount)\nFROM\n\t`game_payment`\nLEFT JOIN game_users ON id = payment_user_id\nWHERE\n\tpayment_status = 1\nAND payment_date >= \"2013-09\"\nGROUP BY\n\tpayment_user_id\nORDER BY\n\tsum(payment_amount) DESC;\n\n[*] Корабль класса ККК - Командование-Контроль-Коммуникация\n    Дает бонус флоту, пока есть хоть один корабль данного типа во флоте\n    Много дейтра, много Кристаллов\n    Хорошая живучесть - слабая атака\n    Капитанский корабль - если хоть один такой корабль был во флоте, то капитан вернется на планету\n\n[*] http://api.yandex.ru/share/doc/dg/concepts/share-button-ov.xml - на логине, на сайтах и в ЗАработай ТМ\n\n[*] Конфигураторы скина: прописывать не только путь к изображениям, но и путь к темплейтам в данных о скине\n\n[*] При чистке безусловно удалять аккаунты, которые выставлены на удаление (?)\n\nЧат - архив чата (?)\n\nСобирать данные геолокации\n\nЗвание - \"Горец\" - в живых должен остаться один - для топ-1. Переходящий вымпел\n\nПамятный знак \"тролль\"!\n\n\nСпецдиректива __PATH__ __FILE__ для темплейтов для загрузки относительных файлов\n\nля чего нужно определять размер окна браузера, для тех случаев когда нам необходимо сделать например резиновый дизайн или чтобы блок целиком закрывал всю область окна браузера\n на jQuery это делается так:\ndoc_w = $(document).width();\ndoc_h = $(document).height();\n//или\nwin_w = $(window).width();\nwin_h = $(window).height();\nно если же надо знать размеры каждый раз при ресайзе окна то код будет следующим:\nfunction window_resize(){\nvar page_h = $(\"html\").height();\nvar page_h = $(\"html\").width();\n}\n\n\nРадио - давать пользователям набирать свой плейлист. Только премиумам\n\n1. Акки все станут неудаляемыми\n2. Будет добавлен новый показатель активности - \"игроков в сутки\"\n3. Будут выставлены новые лимиты на i-шки  и I-шки\n4. Будут введены новые правила наследования Альянсов\n5. Для зареганых до начала изменения правил - предусмотреть opt-out\n\n\nШпионаж таки доделать - и впихнуть левел наемников для++ шпионажа имперского. Уведичить стоимость покупки инфы в Информаторе. Добавить сведения о губере - туда и туда\n\nАпгрейды для Императора - возможность выбирать другие % скорости, т.е. перки\n[13:08:46] Project \"SuperNova.WS\": 1 уровень - 5-10-15-20\n2 уровеня - 3-6-9-12\n3 уровня - с шагом 1%\n4 уровня - с шагом 1% до 110%\n[13:09:02] Project \"SuperNova.WS\": Цены - 25, 50, 100, 200 кТМ\n\nСчитать в статистику флоты в сейве\n\n\nЗамер времени - привязать к юзерагенту и ИД компа (который надо присваивать)\n\nРадио - отдельный фрейм, который можно привязать, с элементами управления\n\nУвеличить длину сообщения - свериться с базой\n\nРабота сообщений очереди построек - высылать только тогда, когда полностью закончилась очередь\n\nПеренести админский отчет по платежам в модуль ММ\n\nСостав флота в Удержании должен быть всегда виден хозяину планеты\n\nСАБ - сохранять ники и алы игроков и показывать их\n\nПеределать перегрузку через __call... Как?\n\nГуберы - cursor:pointer на ссылку выбора/покупки. + к левелу высвечивать и на обзоре и на покупке. Проверить отработку +к левелу с модуля при индикации - на планетах в обзоре и империи\nГуберы - переписать под mrc_get_level. Искать PLANET_GOVERNOR_LEVEL PLANET_GOVERNOR_ID\n\n\nПривязать макс время экспедиции к количеству экпедиций\n\nадминка - вынести просмотр платежей в модуль метаматерии\nадминка - вынести статистику по юзерам отдельно в модуль\n\nМассовые операции по флотам - только прем-юзерам или по перку или по офицеру\n\n\"Совместная оборона\" - не отправлять отчёт игрокам, стоящим в \"Удержании\"\n\nЭкспедиции - писать максимальное количество ресов, найденных в экспе (?)\n\n\nmysql -> mysqli\n"
  },
  {
    "path": "docs/tricks.txt",
    "content": "Список трюков и техник, которые пользуются редко, но полезные\n\n- Объявление объекта в JS ---------------------------------------------------------------\nvar TheObject = function() {\n    this.init();\n};\nTheObject.prototype = {\n    init: function() {\n        this.options = {\n            myProperty: 'value'\n        };\n    },\n    method: function(s) {\n        s = s + 1;\n\n        return s;\n    }\n};\nvar myObject = new TheObject();\n\n- CSS clear all properties (limited support) ---------------------------------------------------------------\n#someselector {\n  all: initial;\n  * {\n    all: unset;\n  }\n}\n\n-----------------------------------------------------------------------------------------------------------------\n  (function (window, navigator, $) {\n    \"use strict\";\n\n    console.log('test');\n\n  } (window, navigator, window.jQuery || window.$));\n\n-----------------------------------------------------------------------------------------------------------------\nhttps://www.sitepoint.com/jquery-document-ready-plain-javascript/\nprint \"<script type='text/javascript'>document.addEventListener('DOMContentLoaded', function() {\n   console.log('document is ready. I can sleep now');\n});</script>\";\n"
  },
  {
    "path": "docs/txt2html.php",
    "content": "<?php\n\nfunction dump($value, $varname = \"\", $level = 0, $dumper = \"\") {\n  if ($varname) {\n    $varname .= \" = \";\n  }\n\n  if ($level == -1) {\n    $trans[' '] = '&there4;';\n    $trans[\"\\t\"] = '&rArr;';\n    $trans[\"\\n\"] = '&para;;';\n    $trans[\"\\r\"] = '&lArr;';\n    $trans[\"\\0\"] = '&oplus;';\n\n    return strtr(htmlspecialchars($value), $trans);\n  }\n  if ($level == 0) {\n    $dumper = '<pre>' . $varname;\n  }\n\n  $type = gettype($value);\n  $dumper .= $type;\n\n  if ($type == 'string') {\n    $dumper .= '(' . strlen($value) . ')';\n    $value = dump($value, \"\", -1);\n  } elseif ($type == 'boolean') {\n    $value = ($value ? 'true' : 'false');\n  } elseif ($type == 'object') {\n    $props = get_class_vars(get_class($value));\n    $dumper .= '(' . count($props) . ') <u>' . get_class($value) . '</u>';\n    foreach ($props as $key => $val) {\n      $dumper .= \"\\n\" . str_repeat(\"\\t\", $level + 1) . $key . ' => ';\n      $dumper .= dump($value->$key, \"\", $level + 1);\n    }\n    $value = '';\n  } elseif ($type == 'array') {\n    $dumper .= '(' . count($value) . ')';\n    foreach ($value as $key => $val) {\n      $dumper .= \"\\n\" . str_repeat(\"\\t\", $level + 1) . dump($key, \"\", -1) . ' => ';\n      $dumper .= dump($val, \"\", $level + 1);\n    }\n    $value = '';\n  }\n  $dumper .= \" <b>$value</b>\";\n  if ($level == 0) {\n    $dumper .= '</pre>';\n  }\n\n  return $dumper;\n}\n\nfunction pdump($value, $varname = '') {\n  print('<span style=\"text-align: left\">' . dump($value, $varname) . '</span>');\n}\n\nfunction debug($value, $varname = '') {\n  return pdump($value, $varname);\n}\n\nfunction buf_print($string) {\n  global $output_buffer;\n\n  $output_buffer .= $string;\n}\n\nif (substr(getcwd(), -4) != 'docs') {\n  $path_prefix = 'docs/';\n} else {\n  $path_prefix = '';\n}\n\n$output_buffer = '';\n\n$filename = 'changelog';\n\n$input = file_get_contents($path_prefix . $filename . '.txt');\n//$input = iconv('CP1251', 'UTF-8', $input);\n\n$input = preg_replace(\"/\\r\\n\\d\\d\\d\\d\\-\\d\\d\\-\\d\\d\\ \\d\\d\\:\\d\\d/\", \"[D] $0\", $input);\n\nwhile (strpos($input, \"\\r\\n\\r\\n\") !== false) {\n  $input = str_replace(\"\\r\\n\\r\\n\", \"\\r\\n\", $input);\n}\nwhile (strpos($input, \"~~~\") !== false) {\n  $input = str_replace(\"~~~\", \"~~\", $input);\n}\n$input = str_replace(\"\\r\\n~~\", \"~~\", $input);\n\nwhile (strpos($input, \"===\") !== false) {\n  $input = str_replace(\"===\", \"==\", $input);\n}\n$input = str_replace(\"\\r\\n==\", \"==\", $input);\n\n$input = preg_split(\"/\\r\\n(.+)[\\~\\=]{2}/\", $input, -1, PREG_SPLIT_DELIM_CAPTURE + PREG_SPLIT_NO_EMPTY); // \n\n$prev_chapter_is_header = false;\n$output = array();\n$buffer = array();\nforeach ($input as &$chapter) {\n  $chapter = preg_split(\"/(\\r\\n[\\[])/\", $chapter, -1, PREG_SPLIT_NO_EMPTY); // , PREG_SPLIT_DELIM_CAPTURE\n\n  if (count($chapter) == 1 && !$prev_chapter_is_header) {\n    if (!empty($chapter)) {\n      $output[] = $buffer;\n      $buffer = array();\n      $buffer['name'] = $chapter[0];\n    }\n    $prev_chapter_is_header = true;\n  } else {\n    $prev_chapter_is_header = false;\n    foreach ($chapter as &$note) {\n      $note = explode(\"\\r\\n\", $note);\n      $new_note = true;\n      $buf_str = '';\n\n      $note_out = array();\n\n      foreach ($note as &$line) {\n        if (!$line) {\n          continue;\n        }\n        if ($new_note) {\n          // 78 - 3 = 75\n          $note_out['style'] = $line[0];\n          $line = substr($line, 3);\n        }\n\n        $buf_str .= $line;\n        if (mb_strlen($line, 'utf-8') < ($new_note ? 75 : 79)) {\n          if (!isset($note_out['name'])) {\n            $note_out['name'] = $buf_str;\n          } else {\n            $note_out['lines'][] = $buf_str;\n          }\n          $buf_str = '';\n        }\n\n        $new_note = false;\n      }\n      $buffer['content'][] = $note_out;\n    }\n  }\n}\n$output[] = $buffer;\n\nbuf_print('<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">\n<html xmlns=\"http://www.w3.org/1999/xhtml\" lang=\"ru\" xml:lang=\"ru\" dir=\"LTR\">\n<head>\n<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\" />\n<style type=\"text/css\"><!--\nbody {font-family: monospace}\nli, ol, ul, pre {margin: 0}\ndiv {margin-bottom: 10px}\n\n.important {color: green; font-weight: bold;}\n.added {color: green;}\n.removed {font-style: italic; color: red}\n.changed {color: blue}\n.fixed {color: red}\n.admin {font-style: italic;}\n.module {color: purple; font-weight: bold;}\n.todo {}\n.date {margin-bottom: 0; color: grey}\n--></style>\n</head>\n<body>\n');\n// ; text-decoration: underline;\n$styles = array(\n  '!' => 'important',\n  '+' => 'added',\n  '-' => 'removed',\n  '~' => 'changed',\n  '%' => 'fixed',\n  '@' => 'admin',\n  '#' => 'module',\n  '*' => 'todo',\n  'D' => 'date',\n);\n\nforeach ($output as $chapter) {\n  if (!$chapter) {\n    continue;\n  }\n\n  buf_print(\"<h1>{$chapter['name']}</h1>\\r\\n\");\n  foreach ($chapter['content'] as $block) {\n    buf_print(\"<div class=\\\"{$styles[$block['style']]}\\\">\" . ($block['style'] != 'D' ? \"[{$block['style']}]&nbsp;\" : ''));\n    buf_print(preg_replace(\"/\\s{2,10}/\", \" \", $block['name']) . '<br />');\n    if (isset($block['lines'])) {\n      $last_spaces = '';\n      $depth = array();\n      foreach ($block['lines'] as $line) {\n        if (preg_match(\"/^(\\s+)(\\d*|\\s)\\.*\\s*(.*)/\", $line, $matches)) {\n          //$line = strlen($matches[1]) . '/' . $matches[2] . '/' . $matches[3];\n          $line = $matches[3];\n          if (strlen($matches[1]) > strlen($last_spaces)) {\n            if ($matches[2]) {\n              buf_print(\"<ol>\\r\\n\");\n            } else {\n              buf_print(\"<ul>\\r\\n\");\n            }\n            buf_print('<li>');\n            $last_spaces = $matches[1];\n            $depth[] = $matches[2];\n          } elseif (strlen($matches[1]) < strlen($last_spaces) && count($depth)) {\n            if (array_pop($depth)) {\n              buf_print(\"</ol>\\r\\n\");\n            } else {\n              buf_print(\"</ul>\\r\\n\");\n            }\n            $last_spaces = $matches[1];\n            buf_print('<li>');\n          } elseif (strlen($last_spaces) == strlen($matches[1])) {\n            if ($matches[2] == '' && $depth[count($depth) - 1] != '') {\n              buf_print(\"</ol>\\r\\n\");\n              buf_print(\"<ul>\\r\\n\");\n            } elseif ($matches[2] != '' && $depth[count($depth) - 1] == '') {\n              buf_print(\"</ul>\\r\\n\");\n              buf_print(\"<ol>\\r\\n\");\n            }\n            $depth[count($depth) - 1] = $matches[2];\n            buf_print('<li>');\n          }\n        }\n        $line = preg_replace(\"/\\s{2,10}/\", \" \", $line);\n        buf_print($line . \"<br />\\r\\n\");\n      }\n      while (count($depth)) {\n        if (array_pop($depth)) {\n          buf_print(\"</ol>\\r\\n\");\n        } else {\n          buf_print(\"</ul>\\r\\n\");\n        }\n      }\n    }\n    buf_print(\"</div>\\r\\n\");\n  }\n}\nbuf_print(\"</body>\\r\\n</html>\\r\\n\");\n\n$html = file_get_contents($path_prefix . 'html/' . $filename . '.html');\nif ($html != $output_buffer) {\n  file_put_contents($path_prefix . 'html/' . $filename . '.html', $output_buffer);\n  if (!$path_prefix) {\n    print($output_buffer);\n  }\n  exit(1);\n}\nexit(0);\n"
  },
  {
    "path": "docs/work-fleets.txt",
    "content": "- .bind replaced with .on\n  .unbind replaced with .off\n  .delegate replaced with .on\n\n\n\n\n- JS\n    - вызовы confirm(), alert() - позаменять на вызовы УИшных функций попапа\n    - пример конфирма - при покупке сектора\n    - пример алерта - АХЕЗ\n\n\n\n- build_unit.js - refactor\n    - Переместить признак юнита STACKABLE в сам юнит\n    -!!! Нет элемента unit_info_extra_switch - хотя для него есть код\n        - Он должен прятать/показывать таблицу баланса. Вставить во враппер\n\n    - TODO ОШИБКА\n        - Если требования неудовлетворены, то всё равно горит \"доступна автоковертация\" и можно жать кнопку\n        - А если нажать кнопу - на следующей странице выдаёт\n            - {Требования не удовлетворены} x[Нанолаборатория]\n\ndoUpdateAdjust !!!!!!!!!!! - find what happens\n\n\nUnify\n    doInsertValues\n    doInsertSet\n\nReplaces also\n\n\n\n\n\n\n\n\ndb_mysql -> operator\n    - Использовать его для доступа к БД\n\n    - Operator НЕ ДОЛЖЕН ИМЕТЬ $db как проперть - это модель, он должен работать с инфой из энтитей\n        - Разве что - как дефолтную БД через getDb()\n            - А хотя фигли - у каждой БД свой Operator. Энтити сами выбирают себе БД и операторов\n\n\n\n- ЧР\n    - Покупка/продажа кораблей\n        - Если их много - количество получаемых/отдаваемых ресурсов далеко за пределами видимости и очень сложно понять, сколько ресов в итоге уйдет/получится\n\n\n\n\n\n\n\n\n\n\n\n- Добавлено на живую Альфу. Последить за дедлоками\n- function fleet_list_by_planet_coords($galaxy, $system, $planet = 0, $planet_type = PT_ALL, $for_phalanx = false) {\n    - на самом деле function db_fleet_list($where_safe, $for_update = true)\n    - плохо себя ведет, если флоты пересекаются с теми, что лочаться в fleet_handler - см. альфа\n        - получаются дедлоки\n        - наверное, из-за того, что там используется FOR UPDATE\n        - поэкспериментировать\n            - или просто прокинуть включение for_update\n\n\n\n\n\n\n\n\nin php 5,  what is the best way to destroy an object instance?\nFor example:\n$myObj = new clsMyClass();\n... use the object\n\nunset($myObj) ;     // is this the best way?  Is  $myObj = NULL;  better?\n\nDo destructors defined in the class always run in either method above?\n\nThanks in advance\n\nWhat you should do is add this Destructor to your class:\n\n   function DESTROY() {\n     settype(&$this, 'null');\n   }\n\n\nThen when you want to destroty the class you could just do:\n\n $myClass->DESTROY();\n\nOr if you're not worried about function cleanup then you could just do:\n\n settype($myClass,'null');\n\n\nPHP OOP is very strange.\n\n\n\n\n\n\n\n\n\n\n- EntityContainer\n    - Теперь добавить методы для получения дельты и списка измененных полей\n        - Затем их использовать в dbSave()\n\n- KeyedModel\n    - implement UPDATE\n    - check other implementations\n\n\n\n- EntityModel\n    - properties - надо сделать это отдельным объектом - что бы можно было сделать ссылку в контейнере\n\n\n\n\n\n- Entity\\EntityModel\n    - Базовый класс для персистентной энтити\n    - Знает\n        - структуру данных контейнера - $properties\n        - как сохранить контейнер в базу, используя rowOperator\n    - Умеет:\n        - экспортнуть контейнер в массив\n        - импортнуть контейнер из массива\n        - прочитаться из базы по ID, используя rowOperator в контейнер\n        - сохранить в базу контейнер:\n            - вставится в базу, если энтити новая, используя rowOperator\n            - обновится в базе, если энтити существует, используя rowOperator\n            - удалится из базы, если энтити пустая, используя rowOperator\n\n\n- Сложные энтити:\n    - Данные об энтити содержатся более чем в одной таблицы\n    - К \"данным\" относится то, без чего энтити не имеет смысл\n        - Например - юниты у флота. Без юнитов флот является пустым\n\n- ВСЕ ДАННЫЕ ВСЕГДА В КОНТЕЙНЕРЕ. В модели - только метаинформация\n    - т.е. юниты - они в контейнере. Возможно - в своей отдельной подструктуре\n\n\n\n\n\n\n\n\n\n\n\n\n- Сделать flletvalidator DI - внутре контейнер для методов, а методы - анонимные функции, вызывающие другие методы же\n\n\n// TODO - Друзья\n    - BuddyModel - вынести все обращения к rowOperator из модели\n        - Наверное, возвращать специальный тип объекта, который будет указывать, что именно делать с моделью\n            - Хотя тут просто - по isNew, isChanged, isDeleted можно всякое определить\n    - Ну и заодно - избавиться от контейнера - вынести его наружу\n\n// TODO -    Транзакции внести в DB\n        - remove transaction related function from db.php\n            - global transaction start function - for all DBs\n                - WHy WE FUCKING SHOULD EVER NEED IT?!!!\n                    - Each Entity\\EntityModel should handle transaction by themselves !!!!!!!!!!\n                    - Otherwise - it's wrong architecture design\n                    - Yep, it's wrong design - but currently we working with wat we have\n                - So we NEED this functions if working across DBs - i.e. Global Auth\n                    - OR - we should redesign architecture\n\n\nclassSupernova::$gc->snCache->\n    - check where used and make DI\n    - also check for other modules\n\nclassSupernova::$gc->cacheOperator\n    - check where used and make DI\n    - also check for other modules\n\n\n\n- buyable\n    - can be bought\n    - have a price\n    - have time to build\n- upgradable\n- buildable\n    - Implies\n        - buyable\n- combat\n    - have combat stats\n- defense\n    - Implies\n- bonus\n    - Grants a bonus on certain level - user, planet, fleet, etc\n- ship\n    - flying\n        - have consumption\n        - have speed\n        - have cargo size\n        - ? fuel tanks - усложнение. Ненужное?\n    - transport\n        - have resource capability\n    - recycler\n        - can recycle\n    - colonizer\n        - can create colony\n    - spy\n        - can fly to espionage\n    - carrier\n        - can carry other ships\n        - have max carry size\n        - have max carry amount\n- missile\n\n- structure\n    - factory\n    - mine\n    - storage\n\n- resource\n    - debris\n    - lootable\n    - transportable\n\n- Фича - выполняет только одну функцию или может быть много?\n\n\n    !! Посмотреть - нельзя ли обойтись без итератора и сделать всё в пределах V2UnitList ?\n\n    !! accessors is REALLY PART OF MODEL!\n        - or should be static inside container\n            - better - model\n        - properties, table, idfieldname - back to model!\n\n    - Добавить $model в Entity\\EntityContainer что бы не надо было иметь собственные переменные и заполнять их?\n    - isEmpty, isNew перенести в модель - только модель может оценивать пустой контейнер или новый\n    - clearProperties() -> назад в clear()\n        - Entity\\EntityContainer всегда содержит только пропертя. Всё остальное - либо в полях, либо в DI\n\n    - INSERT INTO ON DUPLICATE KEY UPDATE - вместо REPLACE!!!!!!!!!!\n        - http://php.net/manual/en/mysqli.affected-rows.php - comments\n\n\n    - Move doXXX functions from db_mysql to dbRowOperator\n\n\n    ? Тесты для entity и контейнеров\n        - Common\\ContainerAccessors - тесты\n            - __unset - добавить ансеттеры????\n\n    - Дефолтные значения для полей в Entity\n\n\n    - ЕЩЕ РАЗ ПРОВЕРИТЬ, что бы просто $where, приходящие в DbQuery не содержали опасных условий\n        - и убрать потом перепаковку в DbQuery::buildWhere() и там же убрать ненужную packIntKeyed\n    - и по $values\n\n    - doUpdate\n        - doUpdateReallyComplex - DANGER\n            - rewrite to danger\n        - db_user_set_by_id - дедупликация\n        - doUpdateWhere ??? - protected\n\n    - Потом doUpdateWhere - в dbQuery\n\n    - db.php::function db_change_resources(&$user, &$planet, $unit_list)\n\n    ? class DbQuery\n        - Для выноса функций, которые не должны быть в DbQuery\n        + doDelete\n            + doDeleteDanger\n            + doDeleteWhere\n            + doDeleteRow\n            + doDeleteSimple\n        - doReplace\n            - doValuesDeprecated -> doValues\n        - doInsert\n        - doUpdate\n\n    - Пересмотреть вызовы update-insert-replace-delete\n        - и там где в $where есть опасные конструкции - заменить на отдельные вызовы\n        - опасные конструкции - там, где не указано поле, а берется условие целиком\n    - DBQuery\n        - добавить в код\n\n\n    - Добавить возврат db_affected_rows() и db_last_insert_id\n        - Insert\n        - Delete\n        - Update\n\n\n    - uni_create_moon $RetValue = classSupernova::$gc->cacheOperator->db_ins_record(LOC_PLANET, $planet);\n        - переделать на по месту генерируемый массив, а не на использование переменной $planet\n\n\n    // TODO - redo as callable usage with array_map/array_walk\n        - public function safeValues($values) {\n        - public function safeFields($fields) {\n        - protected function safeFieldsAndValues($fields) {\n\n\n    - SnCache\n        - move out unit-related\n        - make simple SnCache implementation - just read from DB\n        - // TODO - looks like cache not working\n\npublic function db_del_record_list($location_type, $condition) {\n    - отследить использование $condition и устранить по возможности\n    - под сложные кондишны сделать отдельную функцию, которую потом легко можно будет депрекейтить\n\n// TODO\n    - каждая таблица должна сама уметь управлять своим кэшем. Наверное\n\nclassSupernova::$db - replace with GC calls\n\nclassSupernova::$gc->snCache->\n    - check where used and make DI\n    - also check for other modules\n\nclassSupernova::$gc->cacheOperator\n    - check where used and make DI\n    - also check for other modules\n\n\n- function sys_o_get_updated($user, $planet, $UpdateTime, $simulation = false, $no_user_update = false) {\n    - make it use ID\n    - развязать апдейт юзера и апдейт планеты\n        - но проследить, что апдейт планеты ПОТЕНЦИАЛЬНО может влиять на юзера - хотя нигде это, вроде, не используется\n\n- admin/add_moon.php - какие-то непонятки\n    - $PlanetID = sys_get_param_id('user');\n    - $PlanetSelected = DBStatic\\DBStaticPlanet::db_planet_by_id($PlanetID, true, '`galaxy`, `system`, `planet`, `id_owner`');\n    - хотя вроде работаем с планетой\n\n\nclassSupernova::$gc->cacheOperator->db_del_record_list - unsafe params\n\nfunction idval($value, $default = 0) { - переделать под int|string\n    - может даже ввести новый тип numeric\n\n    - log_file - тоже вынести из classSupernova\n\n    - wrap cache items in objects - to rid off &\n\n    - Fleet\n        - сделать группы динамическими, что бы избавится от порнографии типа\n                if (empty(Fleet::$snGroupFleet)) {\n                  Fleet::$snGroupFleet = sn_get_groups('fleet');\n                  Fleet::$snGroupFleetAndMissiles = sn_get_groups(array('fleet', GROUP_STR_MISSILES));\n                  Fleet::$snGroupRecyclers = sn_get_groups('flt_recyclers');\n                }\n    - И вообще - посмотреть на статические переменные, которые нужно инициализировать!\n\n\n    - public static function dbUpdateOrInsertUnit($unit_id, $unit_value, $user, $planet_id = null) {\n        - Эксепшны и обработка\n\n    - Entity\\EntityContainer\n        - SETted values cannot be ADJUSTed\n        - ADJUSTed values cannot be SETted\n\n    - OPERATOR is in essence an API - try to rename\n\n    - SnCache\n        - сделать проперти ???????? динамическими\n        - сделать DI в $db\n            - каждая ДБ обладает своим кэшем\n        - переделать систему кэширования\n\n    - self: -> static: в объектах\n        - внимательно посмотреть, где опечатка и где это действительно нужно\n\n    - Fleet - доделать/переделать\n\n\n\n    - classLocale => SnLocale\n\n    - doquery()\n         - db->doDeleteRow\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n// TODO WORKING ON -----------------------------------------------------------------------------------------------------\n\n\n$unitList = new \\V2Unit\\V2UnitList();\n$model = classSupernova::$gc->unitModel;\n\n$unit = $model->buildContainer();\n$unit->importRow(array('unit_snid' => RES_METAL));\n$unitList->attach($unit);\nunset($unit);\n\n$unit = $model->buildContainer();\n$unit->importRow(array('unit_snid' => STRUC_ALLY_DEPOSIT));\n$unitList->attach($unit);\nunset($unit);\n\n$unit = $model->buildContainer();\n$unit->importRow(array('unit_snid' => RES_CRYSTAL));\n$unitList->attach($unit);\nunset($unit);\n\n$iterator = new \\V2Unit\\V2UnitIterator($unitList);\n$iterator->setFilterType(UNIT_STRUCTURES);\n$iterator->setFilterType(UNIT_RESOURCES);\n\n$i = 0;\nforeach($iterator as $key => $value) {\n//  pdump($value->type, '$value->type');\n  pdump($value->snId, '$value->snId');\n\n//  pdump($iterator->valid(), '$iterator->valid()');\n//  if($i++ > 10) die();\n//  var_dump($i);\n//  var_dump($key);\n//  var_dump($value);\n}\n\ndie();\n\n\n$model = new \\V2Fleet\\V2FleetModel(classSupernova::$gc);\n$fleet = $model->loadById(8);\nvar_dump($fleet->units);\npdie();\n\n\n\n$unit = classSupernova::$gc->unit;\n//var_dump($unit);\n$unit->dbId = 1;\n$row = classSupernova::$gc->dbRowOperator->getById($unit);\npdump($row);\n$unit->importRow($row);\npdump($unit->timeStart, '$unit->timeStart');\npdump($unit->playerOwnerId, '$unit->playerOwnerId');\npdump($unit->dbId, '$unit->dbId');\npdump($unit->exportRowWithId());\npdie();\n\n\n\n- Должно фиксироваться только время отправления\n    - Время полёта и время миссии должно быть в секундах\n\n- В DB заменить везде planet_type на просто type\n\n- Preemptive optimization!\n    - unitInfo - кэшировать как-то...\n        - Уже парсенный, что ле?\n            - как раз в objectCollection!\n            - производный класс, который по offsetGet будет вытягивать нужное свойство и создавать класс с инфой, возвращя и кэшируя его\n\n\n\n\n\n\n\n\n\n"
  },
  {
    "path": "fleet.php",
    "content": "<?php\n\n/*\n  fleet.php\n  Fleet manager\n\n  V3.3 copyright (c) 2009-2010 by Gorlum for http://supernova.ws\n    [~] Imploded fleet_back.php code\n\n  V3.2 copyright (c) 2009-2010 by Gorlum for http://supernova.ws\n    [~] separate independent chunks in INC-files\n\n  V3.1 copyright (c) 2009-2010 by Gorlum for http://supernova.ws\n    [~] Security checked & tested\n\n  V3.0 Updated by Gorlum Sep 2009\n    [!] extracting templates from code\n    [~] some redundant code cleaning\n\n  V2.0 Updated by Chlorel. 16 Jan 2008 (String extraction, bug corrections, code uniformisation\n\n  V1.0 Created by Perberos. All rights reversed (C) 2006\n*/\n\nglobal $user, $planetrow, $lang, $template_result;\n\nuse Fleet\\DbFleetStatic;\nuse Planet\\DBStaticPlanet;\n\ninclude('common.' . substr(strrchr(__FILE__, '.'), 1));\n$template_result = is_array($template_result) ? $template_result : array();\n\ndefine('SN_IN_FLEET', true);\n\nrequire_once('includes/includes/flt_functions.php');\n\nlng_include('fleet');\n\n//$fleet_page = ($fleet_page = sys_get_param_int('fleet_page')) ? $fleet_page : sys_get_param_int('mode');\n$fleet_page = sys_get_param_int('fleet_page', sys_get_param_int('mode'));\n\n$galaxy = sys_get_param_int('galaxy', $planetrow['galaxy']);\n$system = sys_get_param_int('system', $planetrow['system']);\n$planet = sys_get_param_int('planet', $planetrow['planet']);\n\n$target_mission = sys_get_param_int('target_mission');\nif($target_mission == MT_COLONIZE || $target_mission == MT_EXPLORE) {\n  $planet_type = PT_PLANET;\n} elseif($target_mission == MT_RECYCLE) {\n  $planet_type = PT_DEBRIS;\n} elseif($target_mission == MT_DESTROY) {\n  $planet_type = PT_MOON;\n} else {\n  $planet_type = sys_get_param_int('planet_type');\n  if (!$planet_type) {\n    $planet_type = sys_get_param_int('planettype', $planetrow['planet_type']);\n  }\n}\n\n$options = array();\n$options['fleets_max'] = GetMaxFleets($user);\n\n$MaxFleets = GetMaxFleets($user);\n//$FlyingFleets = doquery(\"SELECT COUNT(fleet_id) as Number FROM {{fleets}} WHERE `fleet_owner`='{$user['id']}'\", true);\n//$FlyingFleets = $FlyingFleets['Number'];\n$FlyingFleets = DbFleetStatic::fleet_count_flying($user['id']);\nif($MaxFleets <= $FlyingFleets && $fleet_page && $fleet_page != 4) {\n  SnTemplate::messageBox($lang['fl_noslotfree'], $lang['fl_error'], \"fleet.\" . PHP_EX, 5);\n}\n\n$MaxExpeditions = get_player_max_expeditons($user);\nif($MaxExpeditions) {\n//  $FlyingExpeditions  = doquery(\"SELECT COUNT(fleet_owner) AS `expedi` FROM {{fleets}} WHERE `fleet_owner` = {$user['id']} AND `fleet_mission` = '\" . MT_EXPLORE . \"';\", '', true);\n//  $FlyingExpeditions  = $FlyingExpeditions['expedi'];\n  $FlyingExpeditions  = DbFleetStatic::fleet_count_flying($user['id'], MT_EXPLORE);\n} else {\n  $FlyingExpeditions = 0;\n}\n\nswitch ($fleet_page) {\n  case 3:\n\n  case 2:\n    $fleet_group_mr = sys_get_param_id('fleet_group');\n    $fleetarray     = json_decode(base64_decode(str_rot13(sys_get_param('usedfleet'))), true);\n    $fleetarray = is_array($fleetarray) ? $fleetarray : array();\n\n    foreach($fleetarray as $ship_id => &$ship_amount) {\n      if(!in_array($ship_id, sn_get_groups('fleet')) || (string)floatval($ship_amount) != $ship_amount || $ship_amount < 1) {\n        $debug->warning('Supplying wrong ship in ship list on fleet page', 'Hack attempt', 302, array('base_dump' => true));\n        die();\n      }\n      $ship_amount = floatval($ship_amount);\n    }\n\n    $UsedPlanet = false;\n    $YourPlanet = false;\n    $missiontype = array();\n    if ($planet > SN::$config->game_maxPlanet) {\n      $target_mission = MT_EXPLORE;\n      $missiontype[MT_EXPLORE] = $lang['type_mission'][MT_EXPLORE];\n    } elseif ($galaxy && $system && $planet) {\n      $check_type = $planet_type == PT_MOON ? PT_MOON : PT_PLANET;\n\n      $TargetPlanet = DBStaticPlanet::db_planet_by_gspt($galaxy, $system, $planet, $check_type);\n\n      if ($TargetPlanet['id_owner']) {\n        $UsedPlanet = true;\n        if ($TargetPlanet['id_owner'] == $user['id']) {\n          $YourPlanet = true;\n        }\n      }\n\n      if (!$UsedPlanet) {\n        if ($fleetarray[SHIP_COLONIZER]) {\n          $missiontype[MT_COLONIZE] = $lang['type_mission'][MT_COLONIZE];\n          $target_mission = MT_COLONIZE;\n          $planet_type = PT_PLANET;\n        } else {\n          SnTemplate::messageBox(\"<font color=\\\"red\\\"><b>\" . $lang['fl_no_planet_type'] . \"</b></font>\", $lang['fl_error']);\n        }\n      } else {\n        $recyclers = 0;\n        foreach(sn_get_groups('flt_recyclers') as $recycler_id) {\n          $recyclers += $fleetarray[$recycler_id];\n        }\n        if ($recyclers > 0 && $planet_type == PT_DEBRIS) {\n          $target_mission = MT_RECYCLE;\n          $missiontype[MT_RECYCLE] = $lang['type_mission'][MT_RECYCLE];\n        } elseif ($planet_type == PT_PLANET || $planet_type == PT_MOON) {\n          if ($YourPlanet) {\n            $missiontype[MT_RELOCATE] = $lang['type_mission'][MT_RELOCATE];\n            $missiontype[MT_TRANSPORT] = $lang['type_mission'][MT_TRANSPORT];\n          } else {\n            // Not Your Planet\n            if ($fleetarray[SHIP_SPY]) {\n              // Only spy missions if any spy\n              $missiontype[MT_SPY] = $lang['type_mission'][MT_SPY];\n            } else {\n              // If no spies...\n              if ($fleet_group_mr) {\n                $missiontype[MT_AKS] = $lang['type_mission'][MT_AKS];\n              } else {\n                $missiontype[MT_ATTACK] = $lang['type_mission'][MT_ATTACK];\n                $missiontype[MT_TRANSPORT] = $lang['type_mission'][MT_TRANSPORT];\n\n                $missiontype[MT_HOLD] = $lang['type_mission'][MT_HOLD];\n\n                if($planet_type == PT_MOON && $fleetarray[SHIP_HUGE_DEATH_STAR]) {\n                  $missiontype[MT_DESTROY] = $lang['type_mission'][MT_DESTROY];\n                }\n              }\n            }\n          }\n        }\n      }\n    }\n\n    if (!$target_mission && is_array($missiontype)) {\n      $target_mission = MT_ATTACK;\n    }\n\n//    $sn_group_missions = sn_get_groups('missions');\n//    foreach($sn_group_missions as $mission_id => $cork) {\n//      $missiontype[$mission_id] = $lang['type_mission'][$mission_id];\n//    }\n//\n//\n    ksort($missiontype);\n\n    $speed_percent = sys_get_param_int('speed', 10);\n    $travel_data   = flt_travel_data($user, $planetrow, array('galaxy' => $galaxy, 'system' => $system, 'planet' => $planet), $fleetarray, $speed_percent);\n\n//    $fleet_speed   = flt_fleet_speed($user, $fleetarray);\n    $fleet_speed   = $travel_data['fleet_speed'];\n    $distance      = $travel_data['distance'];\n    $duration      = $travel_data['duration'];\n    $consumption   = $travel_data['consumption'];\n  // No Break\n\n  case 1:\n    if ($galaxy && $system && $planet) {\n      $check_type = $planet_type == PT_MOON ? PT_MOON : PT_PLANET;\n\n      $TargetPlanet = DBStaticPlanet::db_planet_by_gspt($galaxy, $system, $planet, $check_type);\n    }\n\n  case 0:\n    $template_result += array(\n      'thisgalaxy'      => $planetrow['galaxy'],\n      'thissystem'      => $planetrow['system'],\n      'thisplanet'      => $planetrow['planet'],\n      'thisplanet_type' => $planetrow['planet_type'],\n    );\n  // no break\n\n}\n\n$template_result += array(\n  'galaxy' => $galaxy,\n  'system' => $system,\n  'planet' => $planet,\n  'planet_type' => $planet_type,\n  'target_mission'  => $target_mission ? $target_mission : 0,\n  'MISSION_NAME'\t\t=> $target_mission ? $lang['type_mission'][$target_mission] : '',\n);\n\n$is_transport_missions = false;\nif($missiontype) {\n  $sn_group_missions = sn_get_groups('missions');\n  foreach($missiontype as $mission_data_id => $mission_data) {\n    $is_transport_missions = $is_transport_missions || (isset($sn_group_missions[$mission_data_id]['transport']) && $sn_group_missions[$mission_data_id]['transport']);\n  }\n}\n\nswitch($fleet_page) {\n  case 1:\n    require('includes/includes/flt_page1.inc');\n  break;\n\n  case 2:\n    require_once('includes/includes/flt_page2.inc');\n    sn_fleet_page2();\n  break;\n\n  case 3:\n    require_once('includes/includes/flt_page3.inc');\n    sn_fleet_page3();\n  break;\n\n  case 4:\n    require('includes/includes/flt_page4.inc');\n  break;\n\n  case 5:\n    $template = SnTemplate::gettemplate('fleet5', true);\n    $pageFleet5Gathering = new \\Pages\\Deprecated\\PageFleet5Gathering();\n    $pageFleet5Gathering->modelFleet5Gathering($user, $planetrow, $template);\n    // Building list of own planets & moons\n    $pageFleet5Gathering->viewPage5Gathering($user, $planetrow, $template);\n  break;\n\n  default:\n    define('SN_RENDER_NAVBAR_PLANET', true);\n\n    require('includes/includes/flt_page0.inc');\n  break;\n}\n"
  },
  {
    "path": "flotenajax.php",
    "content": "<?php\n\n/**\n * flotenajax.php\n *\n * Fleet manager on Ajax (to work in Galaxy view)\n *\n * @version   2.0 Security checks by Gorlum for http://supernova.ws\n *  [!] Full rewrite\n *  [+] Added missile attack launch sequience\n *  [-] Moved almost all check code to flt_can_attack\n * @version   1.1 Security checks by Gorlum for http://supernova.ws\n * @version   1\n * @copyright 2008 By Chlorel for XNova\n **/\n\nuse DBAL\\db_mysql;\nuse DBAL\\OldDbChangeSet;\nuse Fleet\\DbFleetStatic;\nuse Planet\\DBStaticPlanet;\n\ninclude('common.' . substr(strrchr(__FILE__, '.'), 1));\n\nglobal $lang, $user;\n\ndefine('IN_AJAX', true);\n\nsetHeader(\"Content-type: text/html; charset=utf-8\");\nlng_include('universe');\nlng_include('fleet');\nrequire_once('includes/includes/flt_functions.php');\n\n$target_coord = array(\n  'galaxy' => $target_galaxy = sys_get_param_int('galaxy'),\n  'system' => $target_system = sys_get_param_int('system'),\n  'planet' => $target_planet = sys_get_param_int('planet'),\n);\n\nif (!uni_coordinates_valid($target_coord)) {\n  die($lang['gs_c02']);\n}\n\n$target_mission    = sys_get_param_int('mission');\n$sn_group_missions = sn_get_groups('missions');\nif (!isset($sn_group_missions[$target_mission]['AJAX']) || !$sn_group_missions[$target_mission]['AJAX']) {\n  die($lang['gs_c00']);\n}\n\n$target_planet_type  = sys_get_param_int('planet_type');\n$target_planet_check = $target_planet_type == PT_DEBRIS ? PT_PLANET : $target_planet_type;\n$target_coord['planet_type'] = $target_planet_check;\n\n// Now gathering info on db records we should lock\n$target_row = DBStaticPlanet::db_planet_by_vector($target_coord);\n// Finding enemy player record - if target planet exists\n$enemy = !empty($target_row['id_owner']) ? db_user_by_id($target_row['id_owner']) : null;\n\ndb_mysql::db_transaction_start();\n// Locking all necessary user/planet records\nSN::$gc->db->lockRecords([\n  'users'   => [$user['id'],] + (!empty($enemy['id']) ? [1 => $enemy['id'],] : []),\n  'planets' => [$user['current_planet'],] + (!empty($target_row['id']) ? [1 => $target_row['id'],] : []),\n]);\n\n$user      = db_user_by_id($user['id'], true);\n$planetrow = DBStaticPlanet::db_planet_by_id($user['current_planet'], true);\n\n// If target row is present - refreshing records to be absolutely sure that nothing happens with it\nif (!empty($target_row)) {\n  $target_row = DBStaticPlanet::db_planet_by_id($target_row['id']);\n}\n\nif (empty($target_row)) {\n  $target_row = array(\n    'galaxy'      => $target_coord['galaxy'],\n    'system'      => $target_coord['system'],\n    'planet'      => $target_coord['planet'],\n    'planet_type' => $target_planet_check,\n    'id_owner'    => 0\n  );\n}\n\n$fleet_array = array();\nswitch ($target_mission) {\n  case MT_SPY:\n    // $fleet_array[SHIP_SPY] = min(mrc_get_level($user, $planetrow, SHIP_SPY), abs($user['spio_anz']));\n    $fleet_array[SHIP_SPY] = min(mrc_get_level($user, $planetrow, SHIP_SPY), abs(SN::$user_options[PLAYER_OPTION_FLEET_SPY_DEFAULT]));\n    $unit_group            = 'flt_spies';\n  break;\n\n  case MT_RECYCLE:\n    foreach (sn_get_groups('flt_recyclers') as $unit_id) {\n      if ($unit_count = mrc_get_level($user, $planetrow, $unit_id)) {\n        $fleet_array[$unit_id] = $unit_count;\n      }\n    }\n    $transport_data = flt_calculate_fleet_to_transport($fleet_array, $target_row['debris_metal'] + $target_row['debris_crystal'], $planetrow, $target_row);\n    $fleet_array    = $transport_data['fleet'];\n    $unit_group     = 'flt_recyclers';\n  break;\n\n  case MT_MISSILE:\n    $fleet_array[UNIT_DEF_MISSILE_INTERPLANET] = min(mrc_get_level($user, $planetrow, UNIT_DEF_MISSILE_INTERPLANET), abs(sys_get_param_float('missiles')));\n    $unit_group                                = 'missile';\n  break;\n\n}\n\n$options     = [P_FLEET_ATTACK_TARGET_STRUCTURE => $target_structure = sys_get_param_int('structures')];\n$cant_attack = flt_can_attack($planetrow, $target_row, $fleet_array, $target_mission, $options);\n\n\nif ($cant_attack != ATTACK_ALLOWED) {\n  die($lang['fl_attack_error'][$cant_attack]);\n}\n\n$FleetDBArray = array();\n$db_changeset = array();\nforeach ($fleet_array as $unit_id => $unit_count) {\n  $FleetDBArray[]         = \"{$unit_id},{$unit_count}\";\n  $db_changeset['unit'][] = OldDbChangeSet::db_changeset_prepare_unit($unit_id, -$unit_count, $user, $planetrow);\n}\n$FleetDBArray = implode(';', $FleetDBArray);\n\n$fleet_ship_count = array_sum($fleet_array);\n\nif ($target_mission == MT_MISSILE) {\n  $distance                   = abs($target_coord['system'] - $planetrow['system']);\n  $duration                   = round((30 + (60 * $distance)) / Universe::flt_server_flight_speed_multiplier());\n  $arrival                    = SN_TIME_NOW + $duration;\n  $travel_data['consumption'] = 0;\n\n  doquery(\n    \"INSERT INTO `{{iraks}}` SET\n     `fleet_target_owner` = '{$target_row['id_owner']}', `fleet_end_galaxy` = '{$target_coord['galaxy']}', `fleet_end_system` = '{$target_coord['system']}', `fleet_end_planet` = '{$target_coord['planet']}',\n     `fleet_owner` = '{$user['id']}', `fleet_start_galaxy` = '{$planetrow['galaxy']}', `fleet_start_system` = '{$planetrow['system']}', `fleet_start_planet` = '{$planetrow['planet']}',\n     `fleet_end_time` = '{$arrival}', `fleet_amount` = '{$fleet_ship_count}', `primaer` = '{$target_structure}';\"\n  );\n} else {\n  $travel_data = flt_travel_data($user, $planetrow, $target_coord, $fleet_array, 10);\n\n  if ($planetrow['deuterium'] < $travel_data['consumption']) {\n    die($lang['gs_c13']);\n  }\n\n  $fleet_start_time = SN_TIME_NOW + $travel_data['duration'];\n  $fleet_end_time   = $fleet_start_time + $travel_data['duration'];\n\n  $fleet_set = [\n    'fleet_owner'           => $user['id'],\n    'fleet_mission'         => $target_mission,\n    'fleet_amount'          => $fleet_ship_count,\n    'fleet_array'           => $FleetDBArray,\n    'fleet_start_time'      => $fleet_start_time,\n    'fleet_start_planet_id' => !empty($planetrow['id']) ? $planetrow['id'] : null,\n    'fleet_start_galaxy'    => $planetrow['galaxy'],\n    'fleet_start_system'    => $planetrow['system'],\n    'fleet_start_planet'    => $planetrow['planet'],\n    'fleet_start_type'      => $planetrow['planet_type'],\n    'fleet_end_time'        => $fleet_end_time,\n    'fleet_end_planet_id'   => !empty($target_row['id']) ? $target_row['id'] : null,\n    'fleet_end_galaxy'      => $target_coord['galaxy'],\n    'fleet_end_system'      => $target_coord['system'],\n    'fleet_end_planet'      => $target_coord['planet'],\n    'fleet_end_type'        => $target_planet_type,\n    'fleet_target_owner'    => $target_row['id_owner'],\n    'start_time'            => SN_TIME_NOW,\n  ];\n  DbFleetStatic::fleet_insert_set_dbq($fleet_set);\n}\n\nDBStaticPlanet::db_planet_set_by_id($planetrow['id'], \"`deuterium` = `deuterium` - {$travel_data['consumption']}\");\nOldDbChangeSet::db_changeset_apply($db_changeset);\ndb_mysql::db_transaction_commit();\n\n$ships_sent = array();\n//$ships_sent_js = array();\n$ships_sent_js = 0;\nforeach ($fleet_array as $unit_id => $unit_count) {\n  $ships_sent[]  = \"{$unit_count} {$lang['tech'][$unit_id]}\";\n  $ships_sent_js += mrc_get_level($user, $planetrow, $unit_id, false, true);\n}\n$ships_sent = implode(', ', $ships_sent);\n//$ships_sent_js = implode(',', $ships_sent_js);\n$ships_sent_js = \"{$unit_group}={$ships_sent_js}\";\n\n$ResultMessage = \"{$lang['gs_sending']} {$ships_sent} {$lang['gs_to']} {$target_coord['galaxy']}:{$target_coord['system']}:{$target_coord['planet']}|{$ships_sent_js}\";\n\ndie($ResultMessage);\n"
  },
  {
    "path": "flying_fleets.php",
    "content": "<?php\n\nuse DBAL\\db_mysql;\nuse Fleet\\DbFleetStatic;\n\ninclude('common.' . substr(strrchr(__FILE__, '.'), 1));\n\nglobal $user, $debug;\n\nlng_include('overview');\nlng_include('fleet');\n\n/**\n * @param int|string $userId\n * @param debug      $debug\n *\n * @throws Exception\n */\nfunction flyingFleetsModel($userId, $debug) {\n  if (empty($_POST['return']) || !is_array($_POST['return'])) {\n    return;\n  }\n\n  foreach ($_POST['return'] as $fleet_id) {\n    if (empty($fleet_id = idval($fleet_id))) {\n      continue;\n    }\n\n    db_mysql::db_transaction_start();\n    if (empty($fleet = SN::$gc->repoV2->getFleet($fleet_id))) {\n      db_mysql::db_transaction_rollback();\n      continue;\n    }\n\n    if (!$fleet->returnForce($userId)) {\n      $debug->warning('Trying to return fleet that not belong to user', 'Hack attempt', 302, ['base_dump' => true, 'fleet_row' => $fleet->asArray()]);\n      db_mysql::db_transaction_rollback();\n      die('Hack attempt 302');\n    }\n    db_mysql::db_transaction_commit();\n  }\n}\n\n/** @noinspection PhpUnhandledExceptionInspection */\nflyingFleetsModel($user['id'], $debug);\n\nif (!$planetrow) {\n  SnTemplate::messageBox($lang['fl_noplanetrow'], $lang['fl_error']);\n}\n\n$template = SnTemplate::gettemplate('flying_fleets', true);\n\n$i = 0;\n$fleet_list = DbFleetStatic::fleet_list_by_owner_id($user['id']);\nforeach ($fleet_list as $fleet_id => $fleet_row) {\n  $i++;\n  $fleet_data = tpl_parse_fleet_db($fleet_row, $i, $user);\n\n  $template->assign_block_vars('fleets', $fleet_data['fleet']);\n\n  foreach ($fleet_data['ships'] as $ship_data) {\n    $template->assign_block_vars('fleets.ships', $ship_data);\n  }\n}\n\n$MaxExpeditions = get_player_max_expeditons($user);\n$FlyingExpeditions = DbFleetStatic::fleet_count_flying($user['id'], MT_EXPLORE);\n$fleet_flying_amount = DbFleetStatic::fleet_count_flying($user['id'], MT_NONE);\n\n$template->assign_vars(array(\n  'FLEETS_FLYING'      => $fleet_flying_amount,\n  'FLEETS_MAX'         => GetMaxFleets($user),\n  'EXPEDITIONS_FLYING' => $FlyingExpeditions,\n  'EXPEDITIONS_MAX'    => $MaxExpeditions,\n));\n\nSnTemplate::display($template, $lang['fl_title']);\n"
  },
  {
    "path": "galaxy.php",
    "content": "<?php\n\n/**\n * galaxy.php\n *\n * Galaxy view\n *\n * History version\n *   2.1 - 'galaxy' table replaced with 'planets' by Gorlum for http://supernova.ws\n *   2.0 - Rewrote by Gorlum for http://supernova.ws\n *     [+] Template-related parts cutted from PHP and moved to TPL-code\n *   1.4 - Security checks & tests by Gorlum for http://supernova.ws\n *   1.3 - 2eme Nettoyage Chlorel ... Mise en fonction et debuging complet\n *   1.2 - 1er Nettoyage Chlorel ...\n *   1.1 - Modified by -MoF- (UGamela germany)\n *   1.0 - Created by Perberos\n * @copyright 2008 by Chlorel for XNova\n */\n\nuse Fleet\\DbFleetStatic;\nuse Planet\\DBStaticPlanet;\n\ninclude('common.' . substr(strrchr(__FILE__, '.'), 1));\n\nglobal $config, $template_result, $planetrow, $debug, $lang;\n\nlng_include('universe');\nlng_include('stat');\n\n$mode = sys_get_param_str('mode');\n$scan = sys_get_param_str('scan');\n$uni_galaxy = sys_get_param_int('galaxy', $planetrow['galaxy']);\n$uni_system = sys_get_param_int('system', $planetrow['system']);\n$planet = sys_get_param_int('planet', $planetrow['planet']);\n\nif ($mode == 'name') {\n  require_once('includes/includes/uni_rename.php');\n}\n\nrequire_once('includes/includes/flt_functions.php');\n\n$CurrentPlanetID = sys_get_param_id('current');\n$POST_galaxyLeft = sys_get_param_str('galaxyLeft');\n$POST_galaxyRight = sys_get_param_str('galaxyRight');\n$POST_systemLeft = sys_get_param_str('systemLeft');\n$POST_systemRight = sys_get_param_str('systemRight');\n\n$fleetmax = GetMaxFleets($user);\n$CurrentPlID = $planetrow['id'];\n$CurrentMIP = mrc_get_level($user, $planetrow, UNIT_DEF_MISSILE_INTERPLANET, false, true);\n$HavePhalanx = mrc_get_level($user, $planetrow, STRUC_MOON_PHALANX);\n$CurrentSystem = $planetrow['system'];\n$CurrentGalaxy = $planetrow['galaxy'];\n\n//$maxfleet       = doquery(\"SELECT COUNT(*) AS flying_fleet_count FROM {{fleets}} WHERE `fleet_owner` = '{$user['id']}';\", '', true);\n//$maxfleet_count = $maxfleet['flying_fleet_count'];\n$flying_fleet_count = DbFleetStatic::fleet_count_flying($user['id']);\n\nif ($mode == 1) {\n} elseif ($mode == 2 || $mode == 3) {\n  $planet = $planetrow['planet'];\n} else {\n  $uni_galaxy = $planetrow['galaxy'];\n  $uni_system = $planetrow['system'];\n  $planet = $planetrow['planet'];\n}\n\n$uni_galaxy = (int)$uni_galaxy;\n$uni_system = (int)$uni_system;\n\n$uni_galaxy = $uni_galaxy < 1 ? 1 : ($uni_galaxy > SN::$config->game_maxGalaxy ? SN::$config->game_maxGalaxy : $uni_galaxy);\n$uni_system = $uni_system < 1 ? 1 : ($uni_system > SN::$config->game_maxSystem ? SN::$config->game_maxSystem : $uni_system);\n$planet = $planet < 1 ? 1 : ($planet > SN::$config->game_maxPlanet + 1 ? SN::$config->game_maxPlanet + 1 : $planet);\n\n$planetcount = 0;\n$lunacount = 0;\n$CurrentRC = $planetrow['recycler'];\n$cached = array('users' => array(), 'allies' => array());\n\n\n$template = SnTemplate::gettemplate('universe', true);\n\n$CurrentPoints = $user['total_points'];\n\n$MissileRange = flt_get_missile_range($user);\n$PhalanxRange = GetPhalanxRange($HavePhalanx);\n\n$planet_precache_query = DBStaticPlanet::db_planet_list_in_system($uni_galaxy, $uni_system);\nif (!empty($planet_precache_query)) {\n  foreach ($planet_precache_query as $planet_row) {\n    if (CheckAbandonPlanetState($planet_row)) {\n      continue;\n    }\n    $planet_list[$planet_row['planet']][$planet_row['planet_type']] = $planet_row;\n  }\n}\n\n$system_fleet_list = DbFleetStatic::fleet_list_by_planet_coords($uni_galaxy, $uni_system);\nforeach ($system_fleet_list as $fleet_row) {\n  $fleet_planet = $fleet_row['fleet_mess'] == 0 ? $fleet_row['fleet_end_planet'] : $fleet_row['fleet_start_planet'];\n  $fleet_type = $fleet_row['fleet_mess'] == 0 ? $fleet_row['fleet_end_type'] : $fleet_row['fleet_start_type'];\n  $fleet_list[$fleet_planet][$fleet_type][] = $fleet_row;\n}\n\n$time_now_parsed = getdate(SN_TIME_NOW);\n\n$recycler_info = array();\n$planet_recyclers_orbiting = 0;\n$recyclers_fleet = array();\nforeach (sn_get_groups('flt_recyclers') as $recycler_id) {\n  $recycler_info[$recycler_id] = get_ship_data($recycler_id, $user);\n  $recyclers_fleet[$recycler_id] = mrc_get_level($user, $planetrow, $recycler_id);\n  $planet_recyclers_orbiting += $recyclers_fleet[$recycler_id];\n}\n\n$user_skip_list = sys_stat_get_user_skip_list();\n$fleetsTotalIncomeOwn = array();\n$config_game_max_planet = SN::$config->game_maxPlanet + 1;\nfor ($Planet = 1; $Planet < $config_game_max_planet; $Planet++) {\n  unset($uni_galaxyRowPlanet);\n  unset($uni_galaxyRowMoon);\n  unset($uni_galaxyRowUser);\n  unset($uni_galaxyRowAlly);\n\n  $templatizedMoon = [];\n\n  if (\n    empty($planet_list[$Planet][PT_PLANET]['id'])\n    ||\n    !($uni_galaxyRowPlanet = $planet_list[$Planet][PT_PLANET])\n    ||\n    (!empty($uni_galaxyRowPlanet['destruyed']) && CheckAbandonPlanetState($uni_galaxyRowPlanet))\n  ) {\n    $template->assign_block_vars('galaxyrow', ['PLANET_NUM' => $Planet,]);\n    continue;\n  }\n\n  $planet_fleet_id = 0;\n  if (!isset($cached['users'][$uni_galaxyRowPlanet['id_owner']])) {\n    $cached['users'][$uni_galaxyRowPlanet['id_owner']] = db_user_by_id($uni_galaxyRowPlanet['id_owner']);\n  }\n  $uni_galaxyRowUser = $cached['users'][$uni_galaxyRowPlanet['id_owner']];\n\n  // Checking if there is planet owner record\n  if (empty($uni_galaxyRowUser)) {\n    // If there is planet owner but planet not destroyed - marking as destroyed\n    if ($uni_galaxyRowPlanet['id_owner'] && empty($uni_galaxyRowPlanet['destruyed'])) {\n      $debug->warning(\"Planet '{$uni_galaxyRowPlanet['name']}' [{$uni_galaxy}:{$uni_system}:{$Planet}] has no owner!\", 'Userless planet', 503);\n      $uni_galaxyRowPlanet['destruyed'] = SN_TIME_NOW + 60 * 60 * 24;\n      $uni_galaxyRowPlanet['id_owner'] = 0;\n      DBStaticPlanet::db_planet_set_by_id($uni_galaxyRowPlanet['id'], \"id_owner = 0, destruyed = {$uni_galaxyRowPlanet['destruyed']}\");\n    }\n  } else {\n    if ($uni_galaxyRowUser['ally_id'] && !isset($cached['allies'][$uni_galaxyRowUser['ally_id']])) {\n      /** @noinspection SqlResolve */\n      $cached['allies'][$uni_galaxyRowUser['ally_id']] = doquery(\"SELECT * FROM `{{alliance}}` WHERE `id` = '{$uni_galaxyRowUser['ally_id']}';\", '', true);\n    }\n  }\n\n  $planetcount++;\n\n  $fleets_to_planet = flt_get_fleets_to_planet(false, $fleet_list[$Planet][PT_PLANET]);\n  if (!empty($fleets_to_planet['own']['count'])) {\n    $planet_fleet_id = getUniqueFleetId($planet_list[$Planet][PT_PLANET]);\n    $fleetsTotalIncomeOwn[$planet_list[$Planet][PT_PLANET]['id']] = tpl_parse_fleet_sn($fleets_to_planet['own']['total'], $planet_fleet_id);\n  }\n\n  if (\n    !empty($planet_list[$Planet][PT_MOON])\n    &&\n    ($uni_galaxyRowMoon = $planet_list[$Planet][PT_MOON])\n    &&\n    (\n      empty($uni_galaxyRowMoon['destruyed'])\n      ||\n      !CheckAbandonPlanetState($uni_galaxyRowMoon)\n    )\n  ) {\n    $fleets_to_planet = flt_get_fleets_to_planet(false, $fleet_list[$Planet][PT_MOON]);\n    if (!empty($fleets_to_planet['own']['count'])) {\n      $moon_fleet_id = getUniqueFleetId($uni_galaxyRowMoon);\n      $fleetsTotalIncomeOwn[$uni_galaxyRowMoon['id']] = tpl_parse_fleet_sn($fleets_to_planet['own']['total'], $moon_fleet_id);\n    } else {\n      $moon_fleet_id = 0;\n    }\n    $templatizedMoon = [\n      'MOON_NAME_JS'  => js_safe_string($uni_galaxyRowMoon['name']),\n      'MOON_IMAGE'    => $uni_galaxyRowMoon['image'],\n      'MOON_DIAMETER' => number_format($uni_galaxyRowMoon['diameter'], 0, '', '.'),\n      'MOON_TEMP'     => number_format($uni_galaxyRowMoon['temp_min'], 0, '', '.'),\n      'MOON_FLEET_ID' => $moon_fleet_id,\n    ];\n  }\n\n  $debrisTemplatized = [];\n  if ($debrisTotal = $uni_galaxyRowPlanet['debris_metal'] + $uni_galaxyRowPlanet['debris_crystal']) {\n    $recyclers_incoming_capacity = 0;\n    if ($fleet_list[$Planet][PT_DEBRIS]) {\n      foreach ($fleet_list[$Planet][PT_DEBRIS] as $fleet_row) {\n        if ($fleet_row['fleet_owner'] == $user['id']) {\n          $fleet_data = sys_unit_str2arr($fleet_row['fleet_array']);\n          foreach ($recycler_info as $recycler_id => $recycler_data) {\n            $recyclers_incoming_capacity += $fleet_data[$recycler_id] * $recycler_data['capacity'];\n          }\n        }\n      }\n    }\n\n    $debris_reserved = $uni_galaxyRowPlanet['debris_reserved'] = $recyclers_incoming_capacity;\n    $debris_reserved_percent = min(100, floor($debris_reserved / $debrisTotal * 100));\n\n    $debris_to_gather = max(0, $debrisTotal - $recyclers_incoming_capacity);\n\n    $recyclers_fleet_data = flt_calculate_fleet_to_transport($recyclers_fleet, $debris_to_gather, $planetrow, $uni_galaxyRowPlanet);\n\n    $debrisTemplatized = [\n      'DEBRIS'         => $debrisTotal,\n      'DEBRIS_METAL'   => $uni_galaxyRowPlanet['debris_metal'],\n      'DEBRIS_CRYSTAL' => $uni_galaxyRowPlanet['debris_crystal'],\n\n      'DEBRIS_RESERVED'             => $debris_reserved,\n      'DEBRIS_RESERVED_PERCENT'     => $debris_reserved_percent,\n      'DEBRIS_WILL_GATHER'          => $debris_will_gather = max(0, min($recyclers_fleet_data['capacity'], $debris_to_gather)),\n      'DEBRIS_WILL_GATHER_PERCENT'  => $debris_to_gather\n        ? floor($debris_will_gather / $debris_to_gather * (100 - $debris_reserved_percent))\n        : 0,\n      'DEBRIS_GATHER_TOTAL'         => $debris_gather_total = max(0, $debris_will_gather + $debris_reserved),\n      'DEBRIS_GATHER_TOTAL_PERCENT' => min(100, floor($debris_gather_total / $debrisTotal * 100)),\n    ];\n  }\n\n  $RowUserPoints = $uni_galaxyRowUser['total_points'];\n  $birthday_array = $uni_galaxyRowUser['user_birthday'] ? date_parse($uni_galaxyRowUser['user_birthday']) : array();\n  $playerSecondsInactive = SN_TIME_NOW - $uni_galaxyRowUser['onlinetime'];\n  $user_activity_days = floor(($playerSecondsInactive) / (60 * 60 * 24));\n\n  $templatizedPlanet = array_merge([\n    'PLANET_ID'        => $uni_galaxyRowPlanet['id'],\n    'PLANET_NUM'       => $Planet,\n    'PLANET_NAME'      => $uni_galaxyRowPlanet['name'],\n    'PLANET_NAME_JS'   => js_safe_string($uni_galaxyRowPlanet['name']),\n    'PLANET_DESTROYED' => $uni_galaxyRowPlanet[\"destruyed\"],\n    'PLANET_TYPE'      => $uni_galaxyRowPlanet[\"planet_type\"],\n    'PLANET_ACTIVITY'  => floor((SN_TIME_NOW - $uni_galaxyRowPlanet['last_update']) / 60),\n    'PLANET_IMAGE'     => $uni_galaxyRowPlanet['image'],\n    'PLANET_FLEET_ID'  => $planet_fleet_id,\n    'PLANET_DIAMETER'  => number_format($uni_galaxyRowPlanet['diameter'], 0, '', '.'),\n\n    'IS_CAPITAL'      => $uni_galaxyRowUser['id_planet'] == $uni_galaxyRowPlanet['id'],\n\n    'USER_ID'         => $uni_galaxyRowUser['id'],\n    'USER_NAME'       => $renderedNick = player_nick_render_to_html($uni_galaxyRowUser, ['icons' => true,]),\n    'USER_NAME_JS'    => js_safe_string($renderedNick),\n    'USER_RANK'       => in_array($uni_galaxyRowUser['id'], $user_skip_list) ? '-' : $uni_galaxyRowUser['total_rank'],\n    'USER_BANNED'     => $uni_galaxyRowUser['banaday'],\n    'USER_VACATION'   => $uni_galaxyRowUser['vacation'],\n    'USER_ACTIVITY'   => $user_activity_days,\n    'USER_ATTACKABLE' => $playerSecondsInactive >= PLAYER_INACTIVE_TIMEOUT,\n    'USER_INACTIVE'   => $playerSecondsInactive >= PLAYER_INACTIVE_TIMEOUT_LONG,\n    'USER_PROTECTED'  => SN::$gc->general->playerIsNoobByPoints($RowUserPoints),\n    'USER_NOOB'       => SN::$gc->general->playerIs1stStrongerThen2nd($CurrentPoints, $RowUserPoints),\n    'USER_STRONG'     => SN::$gc->general->playerIs1stStrongerThen2nd($RowUserPoints, $CurrentPoints),\n    'USER_AUTH'       => $uni_galaxyRowUser['authlevel'],\n    'USER_ADMIN'      => $lang['user_level_shortcut'][$uni_galaxyRowUser['authlevel']],\n    'USER_BIRTHDAY'   => $birthday_array['month'] == $time_now_parsed['mon'] && $birthday_array['day'] == $time_now_parsed['mday'] ? date(FMT_DATE, SN_TIME_NOW) : 0,\n\n    'ALLY_ID'  => $uni_galaxyRowUser['ally_id'],\n    'ALLY_TAG' => $uni_galaxyRowUser['ally_tag'],\n  ], $templatizedMoon, $debrisTemplatized);\n  $template->assign_block_vars('galaxyrow', $templatizedPlanet);\n}\n\ntpl_assign_fleet($template, $fleetsTotalIncomeOwn);\n\nforeach (sn_get_groups('defense_active') as $unit_id) {\n  $template->assign_block_vars('defense_active', array(\n    'ID'   => $unit_id,\n    'NAME' => $lang['tech'][$unit_id],\n  ));\n}\n\nforeach ($cached['users'] as $PlanetUser) {\n  if (!$PlanetUser) {\n    continue;\n  }\n\n  $user_ally = $cached['allies'][$PlanetUser['ally_id']];\n  if (isset($user_ally)) {\n    if ($PlanetUser['id'] == $user_ally['ally_owner']) {\n      $user_rank_title = $user_ally['ally_owner_range'];\n    } else {\n      $ally_ranks = explode(';', $user_ally['ranklist']);\n      list($user_rank_title) = explode(',', $ally_ranks[$PlanetUser['ally_rank_id']]);\n    }\n  } else {\n    $user_rank_title = '';\n  }\n\n  $birthday_array = $PlanetUser['user_birthday'] ? date_parse($PlanetUser['user_birthday']) : array();\n  $template->assign_block_vars('users', array(\n    'ID'         => $PlanetUser['id'],\n    'NAME'       => $renderedNick = player_nick_render_to_html($PlanetUser, true),\n    'NAME_JS'    => js_safe_string($renderedNick),\n    'RANK'       => in_array($PlanetUser['id'], $user_skip_list) ? '-' : $PlanetUser['total_rank'],\n    'AVATAR'     => $PlanetUser['avatar'],\n    'ALLY_ID'    => $PlanetUser['ally_id'],\n    'ALLY_TAG'   => js_safe_string($user_ally['ally_tag']),\n    'ALLY_TITLE' => str_replace(' ', '&nbsp', js_safe_string($user_rank_title)),\n  ));\n}\n\nforeach ($cached['allies'] as $PlanetAlly) {\n  if ($PlanetAlly) {\n    $template->assign_block_vars('alliances', array(\n      'ID'      => $PlanetAlly['id'],\n      'NAME_JS' => js_safe_string($PlanetAlly['ally_name']),\n      'MEMBERS' => $PlanetAlly['ally_members'],\n      'URL'     => $PlanetAlly['ally_web'],\n      'RANK'    => $PlanetAlly['total_rank'],\n      'AVATAR'  => $PlanetAlly['ally_image'],\n    ));\n  }\n}\n\n$is_missile = SN::$user_options[PLAYER_OPTION_UNIVERSE_ICON_MISSILE] && ($CurrentMIP > 0) && ($uni_galaxy == $CurrentGalaxy) && ($uni_system >= $CurrentSystem - $MissileRange) && ($uni_system <= $CurrentSystem + $MissileRange);\n$colspan = SN::$user_options[PLAYER_OPTION_UNIVERSE_ICON_SPYING] + SN::$user_options[PLAYER_OPTION_UNIVERSE_ICON_PM] + SN::$user_options[PLAYER_OPTION_UNIVERSE_ICON_BUDDY] + $is_missile;\n\n/** @noinspection SqlResolve */\n$ally_count = doquery(\"SELECT COUNT(*) AS ally_count FROM `{{alliance}}`;\", '', true);\n/** @noinspection SqlResolve */\n$galaxy_name = doquery(\"select `universe_name` from `{{universe}}` where `universe_galaxy` = {$uni_galaxy} and `universe_system` = 0 limit 1;\", true);\n/** @noinspection SqlResolve */\n$system_name = doquery(\"select `universe_name` from `{{universe}}` where `universe_galaxy` = {$uni_galaxy} and `universe_system` = {$uni_system} limit 1;\", true);\n\n$template->assign_vars(array(\n//     'rows'                => $Result,\n    'userCount'             => SN::$config->users_amount,\n    'ALLY_COUNT'            => $ally_count['ally_count'],\n    'PLANET_EXPEDITION'     => SN::$config->game_maxPlanet + 1,\n    'curPlanetID'           => $planetrow['id'],\n    'curPlanetG'            => $planetrow['galaxy'],\n    'curPlanetS'            => $planetrow['system'],\n    'curPlanetP'            => $planetrow['planet'],\n    'curPlanetPT'           => $planetrow['planet_type'],\n    'deathStars'            => mrc_get_level($user, $planetrow, SHIP_HUGE_DEATH_STAR, false, true),\n    'galaxy'                => $uni_galaxy,\n    'system'                => $uni_system,\n    'planet'                => $planet,\n    'MIPs'                  => round($CurrentMIP),\n    'MODE'                  => $mode,\n    'planets'               => $planetcount,\n    'SPs'                   => HelperString::numberFloorAndFormat(mrc_get_level($user, $planetrow, SHIP_SPY, false, true)),\n    'SHOW_ADMIN'            => SHOW_ADMIN,\n    'fleet_count'           => $flying_fleet_count,\n    'fleet_max'             => $fleetmax,\n    'ALLY_ID'               => $user['ally_id'],\n    'USER_ID'               => $user['id'],\n    'ACT_SPIO'              => SN::$user_options[PLAYER_OPTION_FLEET_SPY_DEFAULT],\n    'ACT_SPY'               => SN::$user_options[PLAYER_OPTION_UNIVERSE_ICON_SPYING],\n    'ACT_WRITE'             => SN::$user_options[PLAYER_OPTION_UNIVERSE_ICON_PM],\n    'ACT_STATISTICS'        => SN::$user_options[PLAYER_OPTION_UNIVERSE_ICON_STATS],\n    'ACT_INFO'              => SN::$user_options[PLAYER_OPTION_UNIVERSE_ICON_PROFILE],\n    'ACT_FRIEND'            => SN::$user_options[PLAYER_OPTION_UNIVERSE_ICON_BUDDY],\n    'opt_uni_tooltip_time'  => SN::$user_options[PLAYER_OPTION_TOOLTIP_DELAY],\n    'opt_uni_avatar_user'   => $user['opt_uni_avatar_user'],\n    'opt_uni_avatar_ally'   => $user['opt_uni_avatar_ally'],\n    'ACT_MISSILE'           => $is_missile,\n    'PLANET_PHALANX'        => $HavePhalanx && $uni_galaxy == $CurrentGalaxy && $uni_system >= $CurrentSystem - $PhalanxRange && $uni_system <= $CurrentSystem + $PhalanxRange,\n    'PAGE_HINT'             => $lang['gal_sys_hint'],\n    'PLANET_RECYCLERS'      => $planet_recyclers_orbiting,\n    'PLANET_RECYCLERS_TEXT' => HelperString::numberFloorAndFormat($planet_recyclers_orbiting),\n    'GALAXY_NAME'           => $galaxy_name['universe_name'],\n    'SYSTEM_NAME'           => $system_name['universe_name'],\n    'COL_SPAN'              => $colspan + 9,\n    'COL_SPAN_PLUS'         => $colspan + 3,\n\n    'COL_SPAN_NEW'          => $colspan + 4,\n    'COL_SPAN_NEW_COLONIZE' => $colspan - 2,\n\n    'PLAYER_OPTION_UNIVERSE_OLD'              => SN::$user_options[PLAYER_OPTION_UNIVERSE_OLD],\n    'PLAYER_OPTION_UNIVERSE_DISABLE_COLONIZE' => SN::$user_options[PLAYER_OPTION_UNIVERSE_DISABLE_COLONIZE],\n  )\n);\n\nif ($scan) {\n  $template_result = array_merge($template_result, array(\n    'GLOBAL_DISPLAY_MENU'   => false,\n    'GLOBAL_DISPLAY_NAVBAR' => false,\n    'UNIVERSE_SCAN_MODE'    => true,\n  ));\n\n  $template->assign_vars(array(\n    'UNIVERSE_SCAN_MODE' => true,\n  ));\n}\n\nSnTemplate::display($template, $lang['sys_universe']);\n"
  },
  {
    "path": "includes/.htaccess",
    "content": "<Files *>\n  order allow,deny\n  deny from all\n</Files>\n"
  },
  {
    "path": "includes/alliance/ali_external_create_ally.inc",
    "content": "<?php\n// Pretty Safe\n// TODO: Add ally_tag to usertable\n\nif(!defined('SN_IN_ALLY') || SN_IN_ALLY !== true)\n{\n  $debug->error(\"Attempt to call ALLIANCE page mode {$mode} directly - not from alliance.php\", 'Forbidden', 403);\n}\n\n$ally_tag_raw = sys_get_param_str_unsafe('tag');\n$ally_tag     = SN::$db->db_escape($ally_tag_raw);\n$ally_name_raw = sys_get_param_str_unsafe('name');\n$ally_name     = SN::$db->db_escape($ally_name_raw);\n\nif($ally_tag)\n{\n  if(!$ally_name_raw || !$ally_tag_raw)\n  {\n    SnTemplate::messageBox($lang['have_not_name'], $lang['make_alliance']);\n  }\n\n  $query = doquery(\"SELECT ally_tag FROM {{alliance}} WHERE `ally_tag` = '{$ally_tag}' or `ally_name` = '{$ally_name}' LIMIT 1;\", true);\n  if($query)\n  {\n    SnTemplate::messageBox(str_replace('%s', $query['ally_tag'] == $ally_tag_raw ? $ally_tag_raw : $ally_name_raw, $lang['always_exist']), $lang['make_alliance']);\n  }\n\n  $ally = doquery(\"INSERT INTO {{alliance}} SET\n    `ally_name` = '{$ally_name}',\n    `ally_tag` = '{$ally_tag}',\n    `ally_owner` = '{$user['id']}',\n    `ally_owner_range` = '{$lang['ali_leaderRank']}',\n    `ally_members` = 1,\n    `ranklist` = '{$lang['ali_defaultRankName']},0,0,0,0,0',\n    `ally_register_time`= \" . SN_TIME_NOW\n  );\n  $ally_id = SN::$db->db_insert_id();\n  db_user_set_by_id($user['id'], \"`ally_tag` = '{$ally_tag}', `ally_id`= {$ally_id}, `ally_name`='{$ally_name}', `ally_register_time`= \" . SN_TIME_NOW . \"\");\n\n  $ally_user = SN::db_ins_record(LOC_USER, \"`username` = '[{$ally_tag}]', `register_time` = \" . SN_TIME_NOW . \", `user_as_ally` = {$ally_id}\");\n  // $ally_user_id = db_insert_id();\n  $ally_user_id = is_array($ally_user) ? $ally_user['id'] : 'NULL';\n  doquery(\"UPDATE {{alliance}} SET ally_user_id = {$ally_user_id} WHERE id = {$ally_id} LIMIT 1;\");\n\n  SnTemplate::messageBox(str_replace('%s', $ally_tag_raw, $lang['ally_been_maked']), str_replace('%s', $ally_tag_raw, $lang['ally_maked']));\n}\n\n$template = SnTemplate::gettemplate('ali_external_make', true);\nSnTemplate::display($template, $lang['make_alliance']);\n"
  },
  {
    "path": "includes/alliance/ali_external_request.inc",
    "content": "<?php\n\nif(!defined('SN_IN_ALLY') || SN_IN_ALLY !== true)\n{\n  $debug->error(\"Attempt to call ALLIANCE page mode {$mode} directly - not from alliance.php\", 'Forbidden', 403);\n}\n\nif ($user['ally_id'])\n{\n  SnTemplate::messageBox($lang['ali_req_inAlly'], $lang['ali_req_title']);\n}\n\nif ($user_request['id_ally'])\n{\n  $ally = doquery(\"SELECT * FROM {{alliance}} WHERE `id` ='{$user_request['id_ally']}'\", true);\n\n  if (sys_get_param_str('bcancel'))\n  {\n    doquery(\"DELETE FROM {{alliance_requests}} WHERE `id_user` = {$user['id']};\");\n    sys_redirect('alliance.php');\n  }\n\n  if($user_request['request_denied'])\n  {\n    $lang['request_text'] = sprintf($lang['ali_req_deny_msg'], $ally['ally_tag'], $user_request['request_text']);\n  }\n  else\n  {\n    $lang['request_text'] = sprintf($lang['ali_req_waiting'], $ally['ally_tag']);\n  }\n\n  $template = SnTemplate::gettemplate('ali_request_waiting', true);\n  SnTemplate::display($template, $lang['ali_req_title']);\n}\n\n$id_ally = sys_get_param_id('a');\n$POST_text = sys_get_param_str('text');\nif ($POST_text)\n{\n  doquery(\"INSERT INTO {{alliance_requests}} SET `id_user` = {$user['id']}, `id_ally`='{$id_ally}', request_text ='{$POST_text}', request_time=\" . SN_TIME_NOW . \";\");\n  sys_redirect('alliance.php');\n}\n\n$ally = doquery(\"SELECT * FROM {{alliance}} WHERE `id` ='{$id_ally}'\", true);\n\nif(!$ally)\n{\n  SnTemplate::messageBox($lang['ali_sys_notFound'], $lang['ali_req_title']);\n}\n\nif($ally['ally_request_notallow'])\n{\n  SnTemplate::messageBox($lang['ali_req_not_allowed'], $lang['ali_req_title']);\n}\n\n$text_apply = $ally['ally_request'] ? $ally['ally_request'] : $lang['ali_req_template'];\n\n$template = SnTemplate::gettemplate('ali_request', true);\n\n$template->assign_vars(array(\n  'allyid' => $id_ally,\n  'chars_count' => strlen($text_apply),\n  'text_apply' => $text_apply,\n  'ali_req_title' => \"{$lang['ali_req_title']} [{$ally['ally_tag']}]\",\n));\n\nSnTemplate::display($template, $lang['ali_req_title']);\n"
  },
  {
    "path": "includes/alliance/ali_info.inc",
    "content": "<?php\n\nuse Alliance\\AllianceHelper;\nuse Alliance\\DBStaticAlly;\nuse DBAL\\db_mysql;\n\nif(!defined('SN_IN_ALLY') || SN_IN_ALLY !== true)\n{\n  $debug->error(\"Attempt to call ALLIANCE page mode {$mode} directly - not from alliance.php\", 'Forbidden', 403);\n}\n\n$template = SnTemplate::gettemplate('ali_info', true);\n\nif($mode == 'exit')\n{\n  if ($ally['ally_owner'] == $user['id'])\n  {\n    SnTemplate::messageBox($lang['Owner_cant_go_out'], $lang['Alliance']);\n  }\n\n  if (sys_get_param_int('ali_info_leave_confirm'))\n  {\n    db_mysql::db_transaction_start();\n    db_user_set_by_id($user['id'], \"`ally_id` = null, `ally_name` = null, `ally_tag` = null, `ally_register_time` = 0, `ally_rank_id` = 0\");\n    DBStaticAlly::db_ally_list_recalc_counts();\n    db_mysql::db_transaction_commit();\n    $lang['Go_out_welldone'] = str_replace(\"%s\", $ally_name, $lang['Go_out_welldone']);\n    SnTemplate::messageBox(sprintf($lang['ali_info_leave_success'], $ally['ally_name']), $lang['sys_alliance']);\n  }\n}\nelseif($mode == 'ainfo')\n{\n  $tag = sys_get_param_str('tag');\n  $id_ally = sys_get_param_id('a');\n  if($tag)\n  {\n    $ally = doquery(\"SELECT * FROM {{alliance}} WHERE ally_tag='{$tag}' LIMIT 1;\", '', true);\n  }\n  elseif($id_ally)\n  {\n    $ally = doquery(\"SELECT * FROM {{alliance}} WHERE id='{$id_ally}' LIMIT 1;\", '', true);\n  }\n\n  if(!$ally)\n  {\n    SnTemplate::messageBox($lang['ali_sys_notFound'], $lang['Ally_info_1']);\n  }\n\n  if(!$ally['ally_description'])\n  {\n    $ally['ally_description'] = $lang['Ally_nodescription'];\n  }\n\n  $template->assign_vars(array(\n    'EXTERNAL'     => true,\n    'USER_ALLY_ID' => $user['ally_id'],\n  ));\n  $page_header          = $lang['sys_alliance'];\n}\nelse\n{\n  $page_header = $lang['your_alliance'];\n\n  if($ally['ally_owner'] == $user['id'])\n  {\n    $range = $ally['ally_owner_range'] ? $ally['ally_owner_range'] : $lang['Founder'];\n  }\n  elseif($user['ally_rank_id'] != 0 && isset($ranks[$user['ally_rank_id']]['name']))\n  {\n    $range = $ranks[$user['ally_rank_id']]['name'];\n  }\n  else\n  {\n    $range = $lang['member'];\n  }\n\n  $request = doquery(\"SELECT COUNT(*) AS request_count FROM {{alliance_requests}} WHERE `id_ally` ='{$ally['id']}'\", '', true);\n\n  $template->assign_vars(array(\n    'range' => $range,\n    'ALLY_REQUESTS' => $request['request_count'],\n\n    'ALLY_ADMIN' => $user_admin,\n    'ALLY_CAN_KICK' => $user_can_kick,\n    'MASS_MAIL' => $user_can_send_mails,\n    'MANAGE_REQUESTS' => $user_admin_applications,\n    'ALLY_NEGOTIATE' => $user_can_negotiate,\n  ));\n}\n\n$template->assign_vars([\n  'ALLY_DESCRIPTION' => AllianceHelper::formatText($ally['ally_description']),\n  'ALLY_TEXT'        => AllianceHelper::formatText($ally['ally_text']),\n  'ally_id'          => $ally['id'],\n  'ALLY_MEMBERS'     => $ally['ally_members'],\n  'ally_web'         => $ally['ally_web'],\n  'ally_tag'         => $ally['ally_tag'],\n  'ally_image'       => $ally['ally_image'],\n  'ally_name'        => $ally['ally_name'],\n]);\n\n$relations = ali_relations($ally['id']);\nforeach($relations as $relation)\n{\n  if($relation['alliance_diplomacy_contr_ally_id'] && $relation['alliance_diplomacy_ally_id'])\n  {\n    $template->assign_block_vars('relation', array(\n      'NAME'     => $relation['alliance_diplomacy_contr_ally_name'],\n      'RELATION' => $lang['ali_dip_relations'][$relation['alliance_diplomacy_relation']],\n      'TIME'     => date(FMT_DATE_TIME, $relation['alliance_diplomacy_time']),\n    ));\n  }\n}\n\nSN::$gc->pimp->allyInfoView();\n\nSnTemplate::display($template, \"{$lang['alliance']} [{$ally['ally_name']}]\");\n"
  },
  {
    "path": "includes/alliance/ali_internal_admin.inc",
    "content": "<?php\n\nuse Alliance\\DBStaticAlly;\nuse DBAL\\db_mysql;\nuse Old\\Avatar;\n\nif(!defined('SN_IN_ALLY') || SN_IN_ALLY !== true) {\n  $debug->error(\"Attempt to call ALLIANCE page mode {$mode} directly - not from alliance.php\", 'Forbidden', 403);\n}\n\nif(!$user_admin) {\n  SnTemplate::messageBox($lang['Denied_access'], $lang['ally_admin']);\n}\n\n$template = SnTemplate::gettemplate('ali_admin', $template);\n\n$text_list = array(\n  1 => array ('db_field' => 'ally_description', 'text_type' => 'Public_text_of_alliance'),\n  2 => array ('db_field' => 'ally_text', 'text_type' => 'Internal_text'),\n  3 => array ('db_field' => 'ally_request', 'text_type' => 'Show_of_request_text'),\n);\n\n$allyTextID = sys_get_param_int('t', 1);\n$allyTextID = ($allyTextID<1 || $allyTextID>3) ? 1 : $allyTextID;\n\nif(sys_get_param_str('isSaveOptions')) {\n  $new_image = $ally['ally_image'];\n  $avatar_upload_result = Avatar::sys_avatar_upload($ally['id'], $new_image, 'ally');\n  $template->assign_vars([\n    'AVATAR_UPLOAD_STATUS' => $avatar_upload_result['STATUS'],\n    'AVATAR_UPLOAD_MESSAGE' => $avatar_upload_result['MESSAGE'],\n  ]);\n\n  $ally_changeset = array();\n  if(($new_tag = sys_get_param_str_unsafe('tag', $ally['ally_tag'])) && $new_tag != $ally['ally_tag']) {\n    $new_tag_unsafe = $new_tag;\n    $new_tag = SN::$db->db_escape($new_tag);\n    $isTaggedAllyExists = DBStaticAlly::db_ally_get_by_tag($new_tag);\n    if(!empty($isTaggedAllyExists)) {\n      SnTemplate::messageBox(sprintf($lang['ally_message_tag_exists'], $new_tag_unsafe), '', 'alliance.php?mode=admin&edit=ally');\n    }\n    $ally_changeset[] = \"`ally_tag`='{$new_tag}'\";\n    db_user_set_by_id($ally['ally_user_id'], \"`username`='[{$new_tag}]'\");\n  }\n\n  if(($new_name = sys_get_param_str_unsafe('name', $ally['ally_name'])) && $new_name != $ally['ally_name']) {\n    $new_name_unsafe = $new_name;\n    $new_name = SN::$db->db_escape($new_name);\n    $isTaggedAllyExists = DBStaticAlly::db_ally_get_by_name($new_name);\n    if(!empty($isTaggedAllyExists)) {\n      SnTemplate::messageBox(sprintf($lang['ally_message_name_exists'], $new_name_unsafe), '', 'alliance.php?mode=admin&edit=ally');\n    }\n    $ally_changeset[] = \"`ally_name`='{$new_name}'\";\n  }\n\n  if(($new_owner_rank = sys_get_param_str_unsafe('owner_range', $ally['ally_owner_range'])) && $new_owner_rank != $ally['ally_owner_range']) {\n    $new_owner_rank = SN::$db->db_escape($new_owner_rank);\n    $ally_changeset[] = \"`ally_owner_range` = '{$new_owner_rank}'\";\n  }\n  if(($new_web = sys_get_param_str_unsafe('web', $ally['ally_web'])) && $new_web != $ally['ally_web']) {\n    $new_web = SN::$db->db_escape($new_web);\n    $ally_changeset[] = \"`ally_web` = '{$new_web}'\";\n  }\n  $new_request = sys_get_param_int('request_notallow', $ally['ally_request_notallow']);\n  $ally_changeset[] = \"`ally_request_notallow` = '{$new_request}'\";\n//  if(($new_request = sys_get_param_int('request_notallow', $ally['ally_request_notallow'])) && $new_request != $ally['ally_request_notallow']) {\n//    $ally_changeset[] = \"`ally_request_notallow` = '{$new_request}'\";\n//  }\n  if($new_image != $ally['ally_image']) {\n    $new_image = intval($new_image);\n    $ally_changeset[] = \"`ally_image` = '{$new_image}'\";\n  }\n\n  if(!empty($ally_changeset)) {\n    doquery(\"UPDATE {{alliance}} SET \" . implode(',', $ally_changeset) . \" WHERE `id`='{$ally['id']}' LIMIT 1;\");\n    sys_redirect('alliance.php?mode=admin&edit=ally');\n  }\n}\nelseif(sys_get_param_str('isSaveText'))\n{\n  $text = sys_get_param_str_both('text');\n  doquery(\"UPDATE {{alliance}} SET `{$text_list[$allyTextID]['db_field']}`='{$text['safe']}' WHERE `id`='{$ally['id']}';\");\n  $ally[$text_list[$allyTextID]['db_field']] = $text['unsafe'];\n}\nelseif(sys_get_param_str('isTransfer') && $idNewLeader = sys_get_param_id('idNewLeader'))\n{\n  if(!$isAllyOwner)\n  {\n    SnTemplate::messageBox($lang['Denied_access'], $lang['ally_admin']);\n  }\n\n  $newLeader = db_user_by_id($idNewLeader, false);\n  if($newLeader['ally_id'] == $user['ally_id'])\n  {\n    db_mysql::db_transaction_start();\n    db_user_set_by_id($user['id'], \"`ally_rank_id`='0'\");\n    doquery(\"UPDATE {{alliance}} SET `ally_owner`='{$idNewLeader}' WHERE `id`={$user['ally_id']};\");\n    db_user_set_by_id($idNewLeader, \"`ally_rank_id`='0'\");\n    db_mysql::db_transaction_commit();\n    sys_redirect('alliance.php');\n  }\n}\nelseif(sys_get_param_str('isDisband') && sys_get_param_str('isConfirmDisband'))\n{\n  if(!$isAllyOwner)\n  {\n    SnTemplate::messageBox($lang['Denied_access'], $lang['ally_admin']);\n  }\n  db_mysql::db_transaction_start();\n  doquery(\"DELETE FROM {{alliance}} WHERE id='{$ally['id']}';\");\n  db_user_list_set_by_ally_and_rank($ally['id'], -20, \"`ally_id` = null, `ally_name` = null, `ally_rank_id` = 0, `ally_register_time` = 0\");\n  db_mysql::db_transaction_commit();\n  sys_redirect('alliance.php');\n};\n\n$request = doquery(\"SELECT COUNT(*) AS request_count FROM {{alliance_requests}} WHERE `id_ally` ='{$ally['id']}'\", '', true);\n\n$template->assign_vars(array(\n  'request_count' => $request['request_count'] ? $lang['ali_req_requestCount'] . ': ' . intval($request['request_count']) . '. ' . $lang['ali_req_check'] : $lang['ali_req_emptyList'],\n  'text'         => $ally[$text_list[$allyTextID]['db_field']],\n  'request_type' => $lang[$text_list[$allyTextID]['text_type']],\n  't' => $allyTextID,\n  'ALLY_ID' => $ally['id'],\n  'ALLY_IMAGE' => $ally['ally_image'],\n  'ally_name' => htmlspecialchars($ally['ally_name']),\n  'ally_tag' => htmlspecialchars($ally['ally_tag']),\n  'ally_web' => htmlspecialchars($ally['ally_web']),\n  'ally_request_notallow_0' => (( $ally['ally_request_notallow']) ? ' SELECTED' : ''),\n  'ally_request_notallow_1' => ((!$ally['ally_request_notallow']) ? ' SELECTED' : ''),\n  'ally_owner_range' => htmlspecialchars($ally['ally_owner_range']),\n  'hideNotOwner' => $ally['ally_owner'] != $user['id'] ? 'display: hide;' : '',\n));\n\n{\n  $userAllyAdmins = db_user_list(\"`ally_id`= {$ally['id']}\", false, '`id`, `username`');\n  unset($tmp);\n  foreach($userAllyAdmins as $userAllyAdmin)\n  {\n    $tmp .= \"<option value={$userAllyAdmin['id']}>{$userAllyAdmin['username']}</option>\";\n  }\n\n  $template->assign_var('adminMembers', $tmp);\n}\n\nforeach($sn_ali_admin_internal as $sn_ali_admin_action => $sn_ali_admin_action_locale)\n{\n  if(!$sn_ali_admin_action_locale['title'])\n  {\n    continue;\n  }\n  $template->assign_block_vars('admin_actions', array(\n    'ACTION' => $sn_ali_admin_action,\n    'LOCALE' => $lang[$sn_ali_admin_action_locale['title']],\n  ));\n}\n\nSnTemplate::display($template, $lang['ally_admin']);\n"
  },
  {
    "path": "includes/alliance/ali_internal_admin_diplomacy.inc",
    "content": "<?php\n\nuse Alliance\\DBStaticAlly;\nuse DBAL\\db_mysql;\nuse \\DBAL\\DbQuery;\n\nif(!defined('SN_IN_ALLY') || SN_IN_ALLY !== true)\n{\n  $debug->error(\"Attempt to call ALLIANCE page mode {$mode} directly - not from alliance.php\", 'Forbidden', 403);\n}\n\nif (!$user_can_negotiate) {\n  SnTemplate::messageBox($lang['Denied_access'], $lang['ali_dip_title']);\n}\n\n$template = SnTemplate::gettemplate('ali_admin_diplomacy', true);\n$page_title = $lang['ali_dip_title'];\n\n$ally_name_safe = SN::$db->db_escape($user['ally_name']);\n\n$autoAccept = false;\nif(sys_get_param_str('ali_dip_offer_make'))\n{\n  $alliance_negotiation_relation = sys_get_param_str('alliance_negotiation_relation');\n  if(!array_key_exists($alliance_negotiation_relation, $sn_diplomacy_relation_list))\n  {\n    SnTemplate::messageBox($lang['ali_dip_err_wrong_offer'], $page_title);\n  }\n\n  $alliance_negotiation_contr_ally_id = sys_get_param_id('alliance_negotiation_contr_ally_id');\n  if($alliance_negotiation_contr_ally_id == $user['ally_id'])\n  {\n    SnTemplate::messageBox($lang['ali_dip_err_same_ally'], $page_title);\n  }\n\n  $contr_ally_row = doquery(\"SELECT `ally_name` FROM {{alliance}} WHERE `id` = {$alliance_negotiation_contr_ally_id} LIMIT 1;\", '', true);\n  if(!$contr_ally_row)\n  {\n    SnTemplate::messageBox($lang['ali_dip_err_no_ally'], $page_title);\n  }\n\n  $relation_current_id = ali_relation($user['ally_id'], $alliance_negotiation_contr_ally_id);\n  if($alliance_negotiation_relation == $relation_current_id)\n  {\n    SnTemplate::messageBox(sprintf($lang['ali_dip_err_offer_same'], $lang['ali_dip_relations'][$alliance_negotiation_relation]), $page_title);\n  }\n\n  $alliance_negotiation_propose = sys_get_param_str('alliance_negotiation_propose');\n\n  // If there is already offer for this alliance - delete it\n  doquery(\"DELETE FROM {{alliance_negotiation}} WHERE alliance_negotiation_ally_id = {$user['ally_id']} AND alliance_negotiation_contr_ally_id = {$alliance_negotiation_contr_ally_id} LIMIT 1;\");\n\n  $relation_new = $sn_diplomacy_relation_list[$alliance_negotiation_relation];\n  $relation_current = $sn_diplomacy_relation_list[$relation_current_id];\n  if($relation_new['enter_delay'] == -1 || $relation_current['exit_delay'] == -1)\n  {\n    DbQuery::build()\n      ->setTable('alliance_negotiation')\n      ->setValues(array(\n        'alliance_negotiation_ally_id'         => $user['ally_id'],\n        'alliance_negotiation_ally_name'       => $user['ally_name'],\n        'alliance_negotiation_contr_ally_id'   => $alliance_negotiation_contr_ally_id,\n        'alliance_negotiation_contr_ally_name' => $contr_ally_row['ally_name'],\n        'alliance_negotiation_relation'        => $alliance_negotiation_relation,\n        'alliance_negotiation_time'            => SN_TIME_NOW,\n        'alliance_negotiation_propose'         => $alliance_negotiation_propose,\n        'alliance_negotiation_status'          => ALLY_PROPOSE_SEND\n      ))\n      ->doInsert();\n\n    $accept_offer = false;\n  }\n  else\n  {\n    $accept_offer = true;\n    $autoAccept = true;\n    $negotiation = array(\n      'alliance_negotiation_ally_id' => $alliance_negotiation_contr_ally_id,\n      'alliance_negotiation_ally_name' => $contr_ally_row['ally_name'],\n      'alliance_negotiation_contr_ally_id' => $user['ally_id'],\n      'alliance_negotiation_contr_ally_name' => $user['ally_name'],\n      'alliance_negotiation_relation' => $alliance_negotiation_relation,\n    );\n  }\n}\nelse\n{\n  $offer_id = sys_get_param_id('offer_id');\n  if($offer_id)\n  {\n    $offer_answer = sys_get_param_str('answer');\n\n    $negotiation = doquery(\"SELECT * FROM {{alliance_negotiation}} WHERE alliance_negotiation_id = {$offer_id} LIMIT 1;\", '', true);\n    if(!$negotiation)\n    {\n      SnTemplate::messageBox($lang['ali_dip_err_offer_none'], $page_title);\n    }\n    elseif($negotiation['alliance_negotiation_ally_id'] != $user['ally_id'] && $negotiation['alliance_negotiation_contr_ally_id'] != $user['ally_id'])\n    {\n      // TODO: Add log of hack attempt\n      SnTemplate::messageBox($lang['ali_dip_err_offer_alien'], $page_title);\n    }\n    elseif($negotiation['alliance_negotiation_ally_id'] == $user['ally_id'])\n    {\n      if($offer_answer == 'accept')\n      {\n        // TODO: Add log of hack attempt\n        SnTemplate::messageBox($lang['ali_dip_err_offer_accept_own'], $page_title);\n      }\n      elseif($offer_answer == 'deny')\n      {\n        doquery(\"DELETE FROM {{alliance_negotiation}} WHERE alliance_negotiation_id = {$offer_id} LIMIT 1;\");\n      }\n    }\n    else\n    {\n      if($offer_answer == 'accept')\n      {\n        $accept_offer = true;\n      }\n      elseif($offer_answer == 'deny')\n      {\n        DBStaticAlly::db_ally_negotiation_update_status_1($offer_id);\n      }\n    }\n  }\n}\n\nif($accept_offer)\n{\n  db_mysql::db_transaction_start();\n\n  DbQuery::build()\n    ->setTable('alliance_diplomacy')\n    ->setValues(array(\n      'alliance_diplomacy_ally_id'         => $user['ally_id'],\n      'alliance_diplomacy_contr_ally_id'   => $negotiation['alliance_negotiation_ally_id'],\n      'alliance_diplomacy_contr_ally_name' => $negotiation['alliance_negotiation_ally_name'],\n      'alliance_diplomacy_relation'        => $negotiation['alliance_negotiation_relation'],\n      'alliance_diplomacy_time'            => SN_TIME_NOW,\n    ))\n    ->doInsert();\n\n  DbQuery::build()\n    ->setTable('alliance_diplomacy')\n    ->setValues(array(\n      'alliance_diplomacy_ally_id'         => $negotiation['alliance_negotiation_ally_id'],\n      'alliance_diplomacy_contr_ally_id'   => $user['ally_id'],\n      'alliance_diplomacy_contr_ally_name' => $user['ally_name'],\n      'alliance_diplomacy_relation'        => $negotiation['alliance_negotiation_relation'],\n      'alliance_diplomacy_time'            => SN_TIME_NOW,\n    ))\n    ->doInsert();\n\n  doquery(\n    \"DELETE FROM {{alliance_negotiation}}\n  \t WHERE\n        (alliance_negotiation_ally_id = {$negotiation['alliance_negotiation_ally_id']} AND alliance_negotiation_contr_ally_id = {$user['ally_id']})\n        OR\n        (alliance_negotiation_ally_id = {$user['ally_id']} AND alliance_negotiation_contr_ally_id = {$negotiation['alliance_negotiation_ally_id']});\"\n  );\n\n  if ($autoAccept) {\n    $messages = [\n      'own'   => 'ali_dip_relation_change_auto_accept',\n      'other' => 'ali_dip_relation_change_auto_accept',\n    ];\n  } else {\n    $messages = [\n      'own'   => 'ali_dip_relation_change_own',\n      'other' => 'ali_dip_relation_change_other',\n    ];\n  }\n\n  foreach ($messages as &$theMessage) {\n    $theMessage = vsprintf(SN::$lang[$theMessage], [\n      $negotiation['alliance_negotiation_contr_ally_name'],\n      $negotiation['alliance_negotiation_ally_name'],\n      $lang['ali_dip_relations'][$negotiation['alliance_negotiation_relation']],\n    ]);\n  }\n\n  msg_ali_send($messages['own'], \"{$lang['sys_alliance']} [{$negotiation['alliance_negotiation_ally_name']}]\");\n  msg_ali_send($messages['other'], \"{$lang['sys_alliance']} [{$negotiation['alliance_negotiation_contr_ally_name']}]\", -1, $negotiation['alliance_negotiation_ally_id']);\n\n  db_mysql::db_transaction_commit();\n}\n\nforeach($sn_diplomacy_relation_list as $diplomacy_relation_id => $diplomacy_relation)\n{\n  $template->assign_block_vars('relation', array(\n    'ID'   => $diplomacy_relation_id,\n    'TEXT' => $lang['ali_dip_relations'][$diplomacy_relation_id],\n  ));\n}\n\n$query = doquery(\"SELECT id, ally_name, ally_tag FROM {{alliance}} WHERE `id` != {$user['ally_id']} ORDER BY ally_name;\");\nwhile($alliance = db_fetch($query))\n{\n  $template->assign_block_vars('alliance', array(\n    'ID'   => $alliance['id'],\n    'NAME' => js_safe_string($alliance['ally_name']),\n    'TAG'  => js_safe_string($alliance['ally_tag']),\n  ));\n}\n\n$query = doquery(\n  \"SELECT\n    *,\n    if(alliance_negotiation_ally_id = {$user['ally_id']}, 1, 0) AS owner,\n    if(alliance_negotiation_ally_id = {$user['ally_id']}, alliance_negotiation_contr_ally_name, alliance_negotiation_ally_name) AS ally_name\n  FROM\n    {{alliance_negotiation}}\n  WHERE\n    alliance_negotiation_ally_id = {$user['ally_id']} OR alliance_negotiation_contr_ally_id = {$user['ally_id']};\"\n);\nwhile($offer = db_fetch($query))\n{\n  $template->assign_block_vars('offer', array(\n    'ID'       => $offer['alliance_negotiation_id'],\n    'NAME'     => $offer['ally_name'],\n    'RELATION' => $lang['ali_dip_relations'][$offer['alliance_negotiation_relation']],\n    'TIME'     => date(FMT_DATE_TIME, $offer['alliance_negotiation_time']),\n    'TEXT'     => SN::$gc->bbCodeParser->expandBbCode($offer['alliance_negotiation_propose'], AUTH_LEVEL_REGISTERED, HTML_ENCODE_MULTILINE),\n    'RESPONSE' => SN::$gc->bbCodeParser->expandBbCode($offer['alliance_negotiation_response'], AUTH_LEVEL_REGISTERED, HTML_ENCODE_MULTILINE),\n    'STATUS'   => $offer['alliance_negotiation_status'],\n    'OWNER'    => $offer['owner'],\n  ));\n}\n\nSnTemplate::display($template, $page_title);\n"
  },
  {
    "path": "includes/alliance/ali_internal_admin_mail.inc",
    "content": "<?php\n\nglobal $lang;\n\nif(!defined('SN_IN_ALLY') || SN_IN_ALLY !== true)\n{\n  $debug->error(\"Attempt to call ALLIANCE page mode {$mode} directly - not from alliance.php\", 'Forbidden', 403);\n}\n\nif (!$user_can_send_mails) {\n  SnTemplate::messageBox($lang['Denied_access'], $lang['Send_circular_mail']);\n}\n\n$POST_text = sys_get_param_str('text');\nif ($POST_text)\n{\n  SnTemplate::messageBox($lang['members_who_recived_message'] . msg_ali_send($POST_text, $lang['ali_adm_msg_subject'], sys_get_param_int('r')), $lang['Circular_sended'], \"alliance.php\", '');\n}\n\n$page = SnTemplate::gettemplate('ali_admin_mail', true);\n\nif ($ranks) {\n  foreach($ranks as $id => $array) {\n    $page->assign_block_vars('ranks', array(\n      'NAME' => $array['name'],\n      'VALUE' => $id,\n    ));\n  }\n}\n\n$page->assign_vars(array(\n  'PAGE_HEADER' => $lang['Send_circular_mail'],\n));\n\nSnTemplate::display($page);\n"
  },
  {
    "path": "includes/alliance/ali_internal_admin_request.inc",
    "content": "<?php\n\nuse Alliance\\DBStaticAlly;\n\nif(!defined('SN_IN_ALLY') || SN_IN_ALLY !== true) {\n  $debug->error(\"Attempt to call ALLIANCE page mode {$mode} directly - not from alliance.php\", 'Forbidden', 403);\n}\n\nif (!$user_admin_applications) {\n  SnTemplate::messageBox($lang['Denied_access'], $lang['requests_admin']);\n}\n\n$d = sys_get_param_id('d');\nif($d) {\n  doquery(\"UPDATE {{alliance_requests}} SET `request_denied` = 1, `request_text` = '{$lang['ali_req_deny_reason']}' WHERE `id_user`= {$d} LIMIT 1;\");\n}\n\n$id_user = sys_get_param_id('id_user');\nif($id_user) {\n  $ally_name_safe = SN::$db->db_escape($ally['ally_name']);\n  $ally_tag_safe = SN::$db->db_escape($ally['ally_tag']);\n  db_user_set_by_id($id_user, \"`ally_id` = '{$ally['id']}', `ally_name` = '{$ally_name_safe}', `ally_tag` = '{$ally_tag_safe}', `ally_register_time` = \" . SN_TIME_NOW . \", `ally_rank_id` = 0\");\n  doquery(\"UPDATE {{alliance}} SET `ally_members`= `ally_members` + 1 WHERE `id`='{$ally['id']}'\");\n  doquery(\"DELETE FROM {{alliance_requests}} WHERE `id_user`= '{$id_user}' LIMIT 1;\");\n}\n\n$template = SnTemplate::gettemplate('ali_admin_request', true);\n\n$query = DBStaticAlly::db_ally_request_list($ally['id']);\nwhile ($ally_request_row = db_fetch($query)) {\n  $template->assign_block_vars('alliance_request', array(\n    'USER_ID'   => $ally_request_row['id_user'],\n    'USER_NAME' => $ally_request_row['username'],\n    'TIME'      => date(FMT_DATE_TIME, $ally_request_row['request_time']),\n    'TEXT'      => HelperString::nl2br($ally_request_row['request_text']),\n    'DENIED'    => $ally_request_row['request_denied'],\n  ));\n}\n\n$template->assign_vars(array(\n  'ally_tag' => $ally['ally_tag'],\n));\n\nSnTemplate::display($template, $lang['requests_admin']);\n"
  },
  {
    "path": "includes/alliance/ali_internal_admin_rights.inc",
    "content": "<?php\n\nglobal $lang, $debug;\n\nif(!defined('SN_IN_ALLY') || SN_IN_ALLY !== true)\n{\n  $debug->error(\"Attempt to call ALLIANCE page mode {$mode} directly - not from alliance.php\", 'Forbidden', 403);\n}\n\nif (!$user_admin) {\n  SnTemplate::messageBox($lang['Denied_access'], $lang['ali_adm_rights_title']);\n};\n\n$template = SnTemplate::gettemplate('ali_admin_rights', true);\n\nif ($new_rank_name = sys_get_param_str('newRankName'))\n{\n  foreach($ally_rights as $fieldName)\n  {\n    $newRank[$fieldName] = 0;\n  }\n  $newRank['name'] = $new_rank_name;\n\n  $ranks[] = $newRank;\n}\n\nif (is_array($rankListInput = sys_get_param('u')))\n{\n  unset($ranks);\n\n  foreach($rankListInput as $rankID => $rank)\n  {\n    foreach($ally_rights as $rightName)\n    {\n      $ranks[$rankID][$rightName] = $rank[$rightName] ? 1 : 0;\n    }\n    $ranks[$rankID]['name'] = strip_tags($rank['name']);\n  }\n}\n\n$d = sys_get_param_int('d');\nif ($d && isset($ranks[$d]))\n{\n  if(count($ranks) == 1)\n  {\n    SnTemplate::messageBox($lang['ali_adm_lastRank'], $lang['ali_adm_rights_title']);\n  }\n  array_splice($ranks, $d, 1);\n  db_user_list_set_by_ally_and_rank($ally['id'], $d, '`ally_rank_id`=`ally_rank_id` - 1');\n}\n\nali_rank_list_save($ranks);\n\nif (count($ranks))\n{\n  foreach($ranks as $rankID => $rank)\n  {\n    $rank_data = array(\n      'ID'   => $rankID,\n      'NAME' => $rank['name'],\n    );\n\n    for($i = 1; $i < count($rank); $i++)\n    {\n      $rank_data['R' . $i] = (($rank[$ally_rights[$i]] == 1) ? ' checked' : '') ;\n      $rank_data['N' . $i] = $ally_rights[$i];\n    }\n\n    $template->assign_block_vars('rank', $rank_data);\n  }\n}\n\nSnTemplate::display($template, $lang['ali_adm_rights_title']);\n"
  },
  {
    "path": "includes/alliance/ali_internal_members.inc",
    "content": "<?php\n\nif(!defined('SN_IN_ALLY') || SN_IN_ALLY !== true)\n{\n  $debug->error(\"Attempt to call ALLIANCE page mode {$mode} directly - not from alliance.php\", 'Forbidden', 403);\n}\n/*\nif (!$user_can_kick)\n  messageBox($lang['Denied_access'], $lang['Members_list']);\n*/\n// Changing rank for single user\n$id_user = sys_get_param_id('id_user');\nif(isset($_GET['id_rank']))\n{\n  $id_rank = sys_get_param_int('id_rank');\n}\nif($id_user && isset($id_rank) && $user_admin){\n  db_user_set_by_id($id_user, \"`ally_rank_id` = {$id_rank}\");\n}\n\n$id_kick = sys_get_param_id('kick');\nif ($id_kick && $user_can_kick)\n{\n  $u = db_user_by_id($id_kick, true);\n  if ($u['ally_id'] == $ally['id'] && $u['id'] != $ally['ally_owner'])\n  {\n    db_user_set_by_id($id_kick, '`ally_id`= null, `ally_name` = null, `ally_tag` = null, ally_register_time = 0, ally_rank_id = 0');\n    doquery(\"UPDATE {{alliance}} SET `ally_members`= `ally_members` - 1 WHERE `id`='{$ally['id']}' LIMIT 1;\");\n  }\n}\n\n$sort2 = sys_get_param_int('sort2');\nif ($sort2)\n{\n  $sort2s = \"DESC\";\n  $sort2 = 0;\n}\nelse\n{\n  $sort2s = \"ASC\";\n  $sort2 = 1;\n}\n\n$sort1 = sys_get_param_int('sort1');\nif($sort1>5 || $sort1<0) $sort1 = 0;\n$sort1s = array(\n  0 => '`ally_rank_id` %1$s;',\n  1 => '`username` %1$s;',\n  2 => '`total_points` %1$s;',\n  3 => '`galaxy` %1$s, `system` %1$s, `planet` %1$s;',\n  4 => '`ally_register_time` %1$s;',\n  5 => '`onlinetime` %1$s;'\n);\n\n$template = SnTemplate::gettemplate('ali_members', true);\n\n$userList = db_user_list(\"`ally_id`= {$user['ally_id']} ORDER BY \" . sprintf($sort1s[$sort1], $sort2s), false,\n  'id, username, galaxy, system, planet, onlinetime, ally_rank_id, ally_register_time, total_points');\n\n// while ($userRow = db_fetch($userList))\nforeach($userList as $userRow)\n{\n  $i++;\n  if (!isset($ranks[$userRow['ally_rank_id']]))\n  {\n    $userRow['ally_rank_id'] = 0;\n  }\n\n  if ($ally['ally_owner'] == $userRow['id'])\n  {\n    $ally_range = ($ally['ally_owner_range'])?$ally['ally_owner_range']:$lang['Founder'];\n  }\n  else\n  {\n    if($user_admin)\n    {\n      $ally_range = '<select onchange=\"window.location=\\'alliance.php?mode=admin&edit=members&id_user=' . $userRow['id'] . '&id_rank=\\' + this.value\">';\n\n      foreach($ranks as $rankID => $rankArray){\n        $ally_range .= '<option value=\"' . $rankID . '\"';\n        if($rankID == $userRow['ally_rank_id'])\n          $ally_range .= \" selected\";\n        $ally_range .= '>' . $rankArray['name'];\n        $ally_range .= '</option>';\n      }\n      $ally_range .= '</select>';\n    }\n    else\n    {\n      $ally_range = $ranks[$userRow['ally_rank_id']]['name'];\n    }\n  }\n\n  $last_active = time() - $userRow[\"onlinetime\"];\n  if($user_admin)\n  {\n    if ( $last_active < 60 )\n    {\n      $tmp = \"lime>{$lang['On']}\";\n    }\n    elseif ($last_active < 60 * 60)\n    {\n      $last_active = round($last_active / 60);\n      $tmp = \"lime>{$last_active} {$lang['sys_min_short']}\";\n    }\n    elseif ($last_active < 60 * 60 * 24)\n    {\n      $last_active = round( $last_active / (60 * 60));\n      $tmp = \"green>{$last_active} {$lang['sys_hrs_short']}\";\n    }\n    else\n    {\n      $last_active = round( $last_active / (60 * 60 * 24));\n      if ($last_active < 7)\n      {\n        $tmp = \"yellow\";\n      }\n      elseif ($last_active < 30)\n      {\n        $tmp = \"orange\";\n      }\n      else\n      {\n        $tmp = \"red\";\n      }\n      $tmp .= \">{$last_active} {$lang['sys_day_short']}\";\n    }\n  }\n  else\n  {\n    if($user_onlinestatus)\n    {\n      if ( $last_active < 60 * 5 )\n      {\n        $tmp = \"lime>{$lang['On']}\";\n      }\n      elseif ($last_active < 60 * 15)\n      {\n        $tmp = \"yellow>{$lang['ali_lessThen15min']}\";\n      }\n      else\n      {\n        $tmp = \"red>{$lang['Off']}\";\n      }\n    }\n    else\n    {\n      $sort1 = max($sort1, 4);\n      $tmp = \"orange>-\";\n    }\n  }\n\n\n  $template->assign_block_vars('member', array(\n    'COUNT' => $i,\n    'USER_ID' => $userRow['id'],\n    'USER_NAME' => $userRow['username'],\n    'GALAXY' => $userRow['galaxy'],\n    'SYSTEM' => $userRow['system'],\n    'PLANET' => $userRow['planet'],\n    'POINTS' => HelperString::numberFloorAndFormat($userRow['total_points']),\n    'ONLINE' => '<font color=' . $tmp . '</font>',\n    'RANK' => $ally_range,\n    'REGISTER_TIME' => date(FMT_DATE_TIME, $userRow['ally_register_time']),\n    'SHOW_KICK' => $user['id'] != $userRow['id'] && $ally['ally_owner'] != $userRow['id'],\n  ));\n}\n\nif ($i != $ally['ally_members'])\n{\n  doquery(\"UPDATE {{alliance}} SET `ally_members`='{$i}' WHERE `id`='{$ally['id']}'\");\n}\n\n$template->assign_vars(array(\n  'mode' => $mode . ($edit ? \"&edit=\" . $edit : ''),\n  'ADMIN_MODE' => $user_can_kick,\n  'onlineMessage' => $user_admin ? $lang['ali_sys_lastActive'] : $lang['Online'],\n  'memberCount' => count($userList),\n  'memberslist' => $page_list,\n  's' => $sort2,\n));\n\nSnTemplate::display($template, $lang['members_admin']);\n"
  },
  {
    "path": "includes/constants/constants.php",
    "content": "<?php\n\n/** @noinspection PhpDefineCanBeReplacedWithConstInspection */\n/** @noinspection PhpUnused */\n\nif(defined('__SN_CONSTANTS_DEFINED') && __SN_CONSTANTS_DEFINED === true) {\n  return;\n}\n\ndefine('__SN_CONSTANTS_DEFINED', true);\n\ndefined('INSIDE') or die('Hacking attempt');\n\ndefine('DB_VERSION_MIN', '40'); // Minimal supported version of DB\ndefine('DB_VERSION', '46');\ndefine('SN_RELEASE', '46');\ndefine('SN_VERSION', '46d0');\ndefine('SN_RELEASE_STABLE', '46d0'); // Latest stable release\n\ndefine('SN_TIME_NOW', intval(SN_TIME_MICRO));\ndefine('SN_TIME_ZONE_OFFSET', date('Z'));\n\ndefine('FMT_DATE_SQL', 'Y-m-d');\ndefine('FMT_TIME_SQL', 'H:i:s');\ndefine('FMT_DATE_TIME_SQL', 'Y-m-d H:i:s');\ndefine('SN_TIME_SQL', date(FMT_DATE_TIME_SQL, SN_TIME_NOW));\ndefine('SN_TODAY_SQL', date('Y-m-d', SN_TIME_NOW));\ndefine('SN_TODAY_UNIX', strtotime(SN_TODAY_SQL));\n\nconst SN_DATE_PREHISTORIC_SQL = '2000-01-01';\ndefine('SN_DATE_PREHISTORIC_UNIX', strtotime(SN_DATE_PREHISTORIC_SQL));\n\ndefine('SN_TIME_NOW_GMT_STRING', gmdate(DATE_ATOM, SN_TIME_NOW));\n\ndefine('FLEET_ID_TEMPLATE', 'f%sown');\n\n// PHP extension on this server\ndefine('PHP_EX', strpos($temp = substr(strrchr(__FILE__, '.'), 1), '/') === false ? $temp : 'php');\n// Dotted PHP extension on this server\ndefine('DOT_PHP_EX', '.' . PHP_EX);\n\ndefine(\n  'INITIAL_PAGE',\n  isset($_GET['page'])\n    ? trim(strip_tags($_GET['page']))\n    : str_replace(DOT_PHP_EX, '', str_replace(SN_ROOT_RELATIVE, '', str_replace('\\\\', '/', $_SERVER['SCRIPT_NAME'])))\n);\n\nrequire_once \"constants_params.php\";\nrequire_once \"constants_units.php\";\nrequire_once \"constants_rpg.php\";\nrequire_once \"constants_ube.php\";\nrequire_once \"constants_payments.php\";\n\n// Game type constants starts with GAME_\ndefine('GAME_SUPERNOVA', 0);\ndefine('GAME_OGAME'    , 1);\ndefine('GAME_BLITZ'    , 2);\n\n// Date & time range constants\ndefine('DATE_FOREVER', 2000000000);\n\ndefine('PERIOD_MINUTE', 60);\ndefine('PERIOD_HOUR', PERIOD_MINUTE * 60);\ndefine('PERIOD_DAY', PERIOD_HOUR * 24);\ndefine('PERIOD_WEEK', PERIOD_DAY * 7);\ndefine('PERIOD_MONTH', PERIOD_DAY * 30);\ndefine('PERIOD_YEAR', PERIOD_DAY * 365);\ndefine('PERIOD_FOREVER', PERIOD_YEAR * 100);\n\ndefine('PERIOD_MINUTE_2' , PERIOD_MINUTE * 2);\ndefine('PERIOD_MINUTE_3' , PERIOD_MINUTE * 3);\ndefine('PERIOD_MINUTE_5' , PERIOD_MINUTE * 5);\ndefine('PERIOD_MINUTE_10', PERIOD_MINUTE * 10);\ndefine('PERIOD_MINUTE_15', PERIOD_MINUTE * 15);\ndefine('PERIOD_DAY_3'    , PERIOD_DAY * 3);\ndefine('PERIOD_WEEK_2'   , PERIOD_WEEK * 2);\ndefine('PERIOD_WEEK_4'   , PERIOD_WEEK * 4);\ndefine('PERIOD_MONTH_2'  , PERIOD_MONTH * 2);\ndefine('PERIOD_MONTH_3'  , PERIOD_MONTH * 3);\n\ndefine('FONT_SIZE_PERCENT_MIN', 56.25);\ndefine('FONT_SIZE_PERCENT_DEFAULT', 68.75);\ndefine('FONT_SIZE_PERCENT_MAX', 131.25);\ndefine('FONT_SIZE_PERCENT_STEP', 12.5);\ndefine('FONT_SIZE_PERCENT_DEFAULT_STRING', FONT_SIZE_PERCENT_DEFAULT . '%');\n\ndefine('FONT_SIZE_PIXELS_BROWSER_BASE', 16);\ndefine('FONT_SIZE_PIXELS_MIN', 9);\ndefine('FONT_SIZE_PIXELS_DEFAULT', 11);\ndefine('FONT_SIZE_PIXELS_MAX', 21);\ndefine('FONT_SIZE_PIXELS_STEP', 1);\ndefine('FONT_SIZE_PIXELS_DEFAULT_STRING', FONT_SIZE_PIXELS_DEFAULT . 'px');\n\ndefine('DEFAULT_PICTURE_EXTENSION_DOTTED', '.jpg');\n\n// Operation error status HARDCODED!\ndefine('ERR_NONE'               , 0); // No error\ndefine('ERR_WARNING'            , 1); // There is warning - something altering normal operation process\ndefine('ERR_ERROR'              , 2); // There is error - something permits operation from process\ndefine('ERR_HACK'               , 4); // Operation is qualified as hack attempt\ndefine('ERR_NOTICE'             , 8); // There is notice - nothing really critical but operator should know\n// New GLOBAL operation results\n//define('RESULT_DEFAULT' , 0); // Default result - all went OK or result really doesn't matter\n//define('RESULT_WARNING' , 1);\n//define('RESULT_ERROR'   , 2);\n//define('RESULT_HACKING' , 3);\n//define('RESULT_PAYMENT_ERR_REQUEST_UNSUPPORTED', 5);\n\n\n// ****************************************************************************************************************\n// SHOULD BE REPLACED WITH CONFIG!\ndefine('MAX_FLEET_OR_DEFS_PER_ROW', 2000);\ndefine('MAX_ATTACK_ROUNDS', 10);\n//define('MAX_OVERFLOW', 1);\ndefine('BASE_STORAGE_SIZE', 500000);\ndefine('HIDE_1ST_FROM_STATS', 0);\ndefine('STATS_RUN_INTERVAL_MINIMUM', PERIOD_MINUTE_10); // Minimal interval to run stat updates\ndefine('HIDE_BUILDING_RECORDS', 0);\ndefine('SHOW_ADMIN', 1);\n\ndefine('UNIVERSE_RANDOM_PLANET_START', 16); // Позиция начала рандомизации планет\ndefine('UNIVERSE_RANDOM_PLANET_TEMPERATURE_DECREASE', 5); // Шаг изменения минимальной температуры случайной планеты\n\ndefine('PLANET_DENSITY_TO_DARK_MATTER_RATE', 10);\n\n// Pattern to parse planet coordinates like [1:123:14] - no expedition [x:x:16] will pass!\ndefine('PLANET_COORD_PREG', '/^\\[([1-9]):([1-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5]):(1[0-5]|[1-9])\\]$/i');\n// Pattern to parse scheduler '[[[[[YYYY-]MM-]DD ]HH:]MM:]SS'\ndefine('SCHEDULER_PREG', '/^(?:(?:(?:(?:(?:(2\\d\\d\\d)-)?(1[0-2]|0[1-9])-)?(?:(3[01]|[0-2]\\d)\\ ))?(?:(2[0-3]|[01]\\d):))?(?:([0-5]\\d):))?([0-5]\\d)$/i');\ndefine('SCHEDULER_PREG2', '/^(?:\\w\\@)?(?:(?:(?:(?:(?:(\\d*)-)?(\\d*)-)?(?:(\\d*)\\ ))?(?:(\\d*):))?(?:(\\d*):))?(\\d*)?$/i');\ndefine('PREG_DATE_SQL', '/(20[1-9][0-9])\\-(1[0-2]|0[1-9])\\-(3[01]|[12]\\d|0[1-9]) (2[0-3]|[01][0-9]):([0-5][0-9]):([0-5][0-9])/');\ndefine('PREG_DATE_SQL_FULL', '/(20[1-9][0-9]|19[0-9][0-9])\\-(1[0-2]|0[1-9])\\-(3[01]|[12]\\d|0[1-9]) (2[0-3]|[01][0-9]):([0-5][0-9]):([0-5][0-9])/');\ndefine('PREG_DATE_SQL_RELAXED', '/(20[1-9][0-9])(?:\\-(1[0-2]|0[1-9])(?:\\-(3[01]|[12]\\d|0[1-9])(?: (2[0-3]|[01][0-9])(?::([0-5][0-9])(?::([0-5][0-9]))?)?)?)?)?/');\n\ndefine('LOGIN_PASSWORD_RESET_CONFIRMATION_LENGTH', 6);\ndefine('SN_SYS_SEC_CHARS_CONFIRMATION', '0123456789');\ndefine('AUTH_PASSWORD_RESET_CONFIRMATION_EXPIRE', PERIOD_DAY);\ndefine('AUTH_PLAYER_NAME_LENGTH', 32);\n\n// define('LOGIN_REGISTER_CHARACTERS_PROHIBITED', '/\\\\ |^&\\'?\"`<>[]{}()%');\ndefine('LOGIN_REGISTER_CHARACTERS_PROHIBITED', \"`'\\\"\\\\/ |^&?<>[]{}()%;\\n\\r\\t\\v\\f\\x00\\x1a\");\n\n// Default allowed chars for random string\ndefine('SN_SYS_SEC_CHARS_ALLOWED', 'ABCDEFGHJKLMNPQRSTUVWXYZabcdefghkmnpqrstuvwxyz0123456789');\n\n//global $ListCensure;\n//$ListCensure = array ( '/</', '/>/', '/script/i', '/doquery/i', '/http/i', '/javascript/i');\n\n// Confirmation record types\ndefine('CONFIRM_REGISTRATION'  , 1);\ndefine('CONFIRM_PASSWORD_RESET', 2);\ndefine('CONFIRM_DELETE'        , 3);\n\ndefine('AFFILIATE_MM_TO_REFERRAL_DM', 2);\n\n// Ally diplomacy statuses\ndefine('ALLY_DIPLOMACY_SELF'         , 'self');\ndefine('ALLY_DIPLOMACY_NEUTRAL'      , 'neutral');\ndefine('ALLY_DIPLOMACY_WAR'          , 'war');\ndefine('ALLY_DIPLOMACY_PEACE'        , 'peace');\ndefine('ALLY_DIPLOMACY_CONFEDERATION', 'confederation');\ndefine('ALLY_DIPLOMACY_FEDERATION'   , 'federation');\ndefine('ALLY_DIPLOMACY_UNION'        , 'union');\ndefine('ALLY_DIPLOMACY_MASTER'       , 'master');\ndefine('ALLY_DIPLOMACY_SLAVE'        , 'slave');\n\ndefine('ALLY_PROPOSE_SEND', 0);\n\n// Quest types\ndefine('QUEST_TYPE_BUILD'   , 1);\ndefine('QUEST_TYPE_RESEARCH', 2);\ndefine('QUEST_TYPE_COMBAT'  , 3);\n\ndefine('QUEST_STATUS_EXCEPT_COMPLETE' , -2);\ndefine('QUEST_STATUS_ALL' , -1);\ndefine('QUEST_STATUS_NOT_STARTED' , 0);\ndefine('QUEST_STATUS_STARTED'     , 1);\ndefine('QUEST_STATUS_COMPLETE'    , 2);\n\ndefine('TYPE_EMPTY', '');\ndefine('TYPE_INTEGER', 'integer');\ndefine('TYPE_DOUBLE', 'double');\ndefine('TYPE_FLOAT', 'double'); // Just a nickname to match PHP used type 'float'\ndefine('TYPE_BOOLEAN', 'boolean');\ndefine('TYPE_NULL', 'NULL');\ndefine('TYPE_ARRAY', 'array');\ndefine('TYPE_STRING', 'string');\n\n// *** Combat-related constants\n// *** Mission Type constants starts with MT_\ndefine('MT_NONE'     ,  0);\ndefine('MT_ATTACK'   ,  1);\ndefine('MT_AKS'      ,  2);\ndefine('MT_TRANSPORT',  3);\ndefine('MT_RELOCATE' ,  4);\ndefine('MT_HOLD'     ,  5);\ndefine('MT_SPY'      ,  6);\ndefine('MT_COLONIZE' ,  7);\ndefine('MT_RECYCLE'  ,  8);\ndefine('MT_DESTROY'  ,  9);\ndefine('MT_MISSILE'  , 10);\ndefine('MT_EXPLORE'  , 15);\n\n// *** Planet Target constants starts with PT_\ndefine('PT_NONE', 0);\ndefine('PT_ALL', 0);\ndefine('PT_PLANET', 1);\ndefine('PT_DEBRIS', 2);\ndefine('PT_MOON'  , 3);\n\n// *** Unit locations - shows db table where unit belong\n// Also cache indexes\ndefine('LOC_AUTODETECT', -2);\ndefine('LOC_NONE',    -1); // Deprecated\ndefine('LOC_UNIVERSE', 0);\ndefine('LOC_PLANET',   1);\ndefine('LOC_DEBRIS',   2); // Translates to `planets` table planet_type = 1, `debris_*` fields\ndefine('LOC_MOON',     3); // Translates to `planets` table planet_type = 3\ndefine('LOC_PLAYER',   4);\ndefine('LOC_USER',     LOC_PLAYER); // Deprecated alias for LOC_PLAYER\ndefine('LOC_FLEET',    5);\ndefine('LOC_ALLY',     6);\ndefine('LOC_SERVER',   7); // Located on server\n\n// ТОЛЬКО ВНУТРЕНЕЕ!!!\ndefine('LOC_UNIT',    'LOC_UNIT');\ndefine('LOC_QUE',     'LOC_QUE');\ndefine('LOC_LOCATION','LOC_LOCATION');\ndefine('LOC_LOCKS','LOC_LOCKS');\n\n// *** Caching masks\ndefine('CACHE_NOTHING'    ,  0);\ndefine('CACHE_FLEET'      ,  1);\ndefine('CACHE_PLANET'     ,  2);\ndefine('CACHE_USER'       ,  4);\ndefine('CACHE_SOURCE'     ,  8);\ndefine('CACHE_DESTINATION', 16);\ndefine('CACHE_EVENT'      , 32);\n\ndefine('CACHE_USER_SRC'  , CACHE_USER | CACHE_SOURCE);\ndefine('CACHE_USER_DST'  , CACHE_USER | CACHE_DESTINATION);\ndefine('CACHE_PLANET_SRC', CACHE_PLANET | CACHE_SOURCE);\ndefine('CACHE_PLANET_DST', CACHE_PLANET | CACHE_DESTINATION);\ndefine('CACHE_COMBAT'    , CACHE_FLEET | CACHE_PLANET | CACHE_USER | CACHE_SOURCE | CACHE_DESTINATION);\n\ndefine('CACHE_ALL'       , CACHE_FLEET | CACHE_PLANET | CACHE_USER | CACHE_SOURCE | CACHE_DESTINATION | CACHE_EVENT);\n\ndefine('CACHE_NONE'      , CACHE_NOTHING); // Alias for me\n\n// Fleet status aka `fleet_mess`\nconst FLEET_STATUS_FLYING = 0;\nconst FLEET_STATUS_RETURNING = 1;\n\n// *** Event types\nconst EVENT_FLEET_NONE   = 0;\nconst EVENT_FLEET_ARRIVE = 1;\nconst EVENT_FLEET_STAY   = 2;\nconst EVENT_FLEET_RETURN = 3;\n\n// Обязательно оставить, что бы arrive < accomplish < return\n/** @var string Fleet arrived to destination */\nconst EVENT_FLT_ARRIVE     = 'EVENT_FLT_ARRIVE';\n/** @var string Fleet ended his mission by timer */\nconst EVENT_FLT_ACCOMPLISH = 'EVENT_FLT_ACCOMPLISH'; //\n/** @var string Fleet returned to starting planet */\nconst EVENT_FLT_RETURN     = 'EVENT_FLT_RETURN'; //\n\n\n/** @formatter:off */\nconst CONST_1K    = 1000;\nconst CONST_10K   = 10000;\nconst CONST_100K  = 100000;\nconst CONST_1M    = 1000000;\nconst CONST_10M   = 10000000;\nconst CONST_100M  = 100000000;\nconst CONST_1B    = 1000000000;\nconst CONST_10B   = 10000000000;\nconst CONST_100B  = 100000000000;\n/** @formatter:on */\n\n// Log system codes\ndefine('LOG_DEFAULT', 0); // Код по умолчанию\n// 1xx - Информационные коды\ndefine('LOG_INFORMATION', 100);\ndefine('LOG_INFO_DM_CHANGE', 102); // Изменение количества Тёмной Материи\ndefine('LOG_INFO_DB_CHANGE', 103); // Изменение структуры БД\ndefine('LOG_INFO_UNI_RENAME', 104); // Переименование объекта Вселенной\ndefine('LOG_INFO_PREMIUM_CANCEL', 105); // Пользователь отменил премиум аккаунт\ndefine('LOG_INFO_PAYMENT', 110); // Записи системы платежей\ndefine('LOG_INFO_MAINTENANCE', 180); // Записи системы платежей\ndefine('LOG_INFO_STAT_START', 190); // Запуск обновления статистики игроков\ndefine('LOG_INFO_STAT_PROCESS', 191); // Сообщения системы обновления статистики\ndefine('LOG_INFO_STAT_FINISH', 192); // Обновление статистики игроков выполнено успешно\n// 2xx - Операция завершена успешно\ndefine('LOG_OK', 200); // OK\n//define('LOG_OK_CREATED', 201); // Created\n//define('LOG_OK_ACCEPTED', 202); // Accepted\n//define('LOG_OK_NON_AUTHORITATE', 203); // Non-Authoritative Information\n//define('LOG_OK_NOTHING', 204); // No Content\n//define('LOG_OK_RESET', 205); // Reset Content\n//define('LOG_OK_PARTIAL', 206); // Partial Content\n// 3xx - Предупреждения системы логов\ndefine('LOG_WARN', 300); // Общий код по умолчанию\ndefine('LOG_WARN_BUGUSE', 301); // Возможный багоюз. Когда-то данное действие пользователя приводило к ошибке, дающей ему неоправданное преимущество в игре (удвоение флота, бесплатная или моментальная постройка итд), т.е. являлось хаком. Сейчас эта ошибка устранена, но стоит присмотрется к пользователю - возможно он багоюзер или хакер.\ndefine('LOG_WARN_NO_USER', 303); // Пользователь не найден в системе. Это может означать, что пользователь на странице логина или регестрируется. Или это может означать ошибку\ndefine('LOG_WARN_MULTICOLONIZE', 304); // Колонизация с несколькими кораблями\ndefine('LOG_WARN_HACK', 302); // Попытка взлома. Пользователь передал серверу данные, не совпадаю реальными (например - другой ID пользователя вместо своего, другой ID альянса, вместо своего итд). Обычно это означает попытку взлома. Очень редко это может означать о наличии ошибки в коде игры\ndefine('LOG_WARN_HACK_NEGATIVE_RESOURCE', 305); // Попытка взлома. Пользователь передал отрицательное количество ресурсов на странице Чёрный Рынок->Торговец ресурсами. В нормальных условиях это сделать невозможно. До фикса это приводило к увеличению ресурса на указанную величину.\ndefine('LOG_WARN_HACK_WRONG_UNIT', 306); // Попытка взлома. Пользователь передал в списке кораблей на странице Чёрный Рынок не-корабль. В нормальных условиях это сделать невозможно.\ndefine('LOG_WARN_HACK_NEGATIVE_UNIT', 307); // Попытка взлома. Пользователь передал отрицательное количество кораблей на странице Чёрный Рынок. В нормальных условиях это сделать невозможно.\ndefine('LOG_WARN_BIG_BROTHER', 399); // Запись системы слежения за игроками\n// 4xx - Ошибки при запросе клиента\ndefine('LOG_ERR_UNAUTHORIZED', 401); // Unauthorized. Пользователь попытался получить доступ к части сайта, не доступной для его уровня доступа\ndefine('LOG_ERR_DARK_MATTER', 402); // Ошибка изменения количества Тёмной Материи\ndefine('LOG_ERR_FORBIDDEN', 403); // Forbidden. Попытка взлома - пользователь попытался выполнять отдельный файл вне нормального хода операции. Например - попытался выполнить файл, предназначенный только для include\n// 5xx - Ошибки сервера - сбой в БД или ошибки в коде движка\ndefine('LOG_ERR_INT', 500); // Ошибки сервера - сбой в БД или ошибки в коде движка\ndefine('LOG_ERR_INT_NEGATIVE_RESOURCE', 501); // У игрока отрицательное количество ресурсов\ndefine('LOG_ERR_INT_NO_PLANET', 502); // Ошибка записи пользователя: у пользователя в качестве родного мира назначена несуществующая планета\ndefine('LOG_ERR_INT_ORPHANE_PLANET', 503); // У планеты нет хозяина\ndefine('LOG_ERR_INT_FLEET_TIMOUT', 504); // Таймаут менеджера флотов\ndefine('LOG_ERR_INT_CAPTAIN_DUPLICATE', 505); // Таймаут менеджера флотов\ndefine('LOG_ERR_INT_NOT_ENOUGH_DARK_MATTER', 506); // Ошибка снятия ТМ\n// 9xx - Отладка\n// define('LOG_DEBUG', 900); // Отладка Странный глюк - не хочет делать define LOG_DEBUG без ошибки!\ndefine('LOG_DEBUG_SQL', 910); // Отладка SQL\n\n\n\ndefine('PASSWORD_LENGTH_MIN', 4);\ndefine('LOGIN_LENGTH_MIN', 4);\n\ndefine('AUTH_COOKIE_DELIMETER', '_');\ndefine('AUTH_COOKIE_IMPERSONATE_SUFFIX', '_I');\n\ndefine('AUTH_FEATURE_EMAIL_CHANGE', 1);\ndefine('AUTH_FEATURE_PASSWORD_RESET', 2);\ndefine('AUTH_FEATURE_FORCE_PLAYER_NAME_SELECT', 3);\ndefine('AUTH_FEATURE_PASSWORD_CHANGE', 4);\ndefine('AUTH_FEATURE_HAS_PASSWORD', 5);\n\n// Login statuses\ndefine('LOGIN_UNDEFINED', 0);\ndefine('LOGIN_SUCCESS', 1);\n// define('LOGIN_SUCCESS_CREATE_PROFILE', 2);\ndefine('LOGIN_ERROR_PASSWORD', 3);\ndefine('LOGIN_ERROR_USERNAME', 4);\n// define('LOGIN_ERROR_ACTIVE'          , 5);\n// define('LOGIN_ERROR_EXTERNAL_AUTH'   , 6);\n// define('LOGIN_ERROR_COOKIE'          , 7);\n\ndefine('REGISTER_ERROR_USERNAME_WRONG', 8);\ndefine('REGISTER_ERROR_ACCOUNT_NAME_EXISTS', 9);\ndefine('REGISTER_ERROR_PASSWORD_INSECURE', 10);\ndefine('REGISTER_SUCCESS', 11);\n\ndefine('PASSWORD_RESTORE_ERROR_EMAIL_NOT_EXISTS', 12);\ndefine('PASSWORD_RESTORE_ERROR_TOO_OFTEN', 13);\ndefine('PASSWORD_RESTORE_SUCCESS_CODE_SENT', 14);\ndefine('PASSWORD_RESTORE_ERROR_SENDING', 15);\ndefine('PASSWORD_RESTORE_ERROR_CODE_WRONG', 16);\ndefine('PASSWORD_RESTORE_ERROR_CODE_TOO_OLD', 17);\n\ndefine('AUTH_ERROR_INTERNAL_PASSWORD_CHANGE_ON_RESTORE', 18);\ndefine('PASSWORD_RESTORE_SUCCESS_PASSWORD_SENT', 19);\ndefine('PASSWORD_RESTORE_SUCCESS_PASSWORD_SEND_ERROR', 20);\ndefine('REGISTER_ERROR_PASSWORD_DIFFERENT', 21);\ndefine('REGISTER_ERROR_EMAIL_EXISTS', 22);\ndefine('REGISTER_ERROR_BLITZ_MODE', 23);\ndefine('LOGIN_ERROR_USERNAME_RESTRICTED_CHARACTERS', 24);\ndefine('LOGIN_ERROR_USERNAME_EMPTY', 25);\ndefine('LOGIN_ERROR_PASSWORD_EMPTY', 26);\ndefine('LOGIN_ERROR_SYSTEM_ACCOUNT_TRANSLATION', 27);\ndefine('LOGIN_ERROR_USERNAME_ALLY_OR_BOT', 28);\ndefine('LOGIN_ERROR_PASSWORD_TRIMMED', 29);\ndefine('REGISTER_ERROR_EMAIL_EMPTY', 30);\ndefine('REGISTER_ERROR_EMAIL_WRONG', 31);\ndefine('REGISTER_ERROR_USERNAME_SHORT', 32);\n\ndefine('IMPERSONATOR_OK', 33);\n\ndefine('REGISTER_ERROR_ACCOUNT_CREATE', 34);\n\ndefine('PASSWORD_RESTORE_ERROR_ADMIN_ACCOUNT', 35);\ndefine('PASSWORD_RESTORE_ERROR_ACCOUNT_NOT_EXISTS', 36);\ndefine('AUTH_LOGIN_INSIDE_ERROR_ACCOUNT_NOT_EXISTS', 37);\ndefine('AUTH_PASSWORD_RESET_INSIDE_ERROR_NO_ACCOUNT_FOR_CONFIRMATION', 38);\n\ndefine('REGISTER_ERROR_PLAYER_NAME_TRIMMED', 39);\ndefine('REGISTER_ERROR_PLAYER_NAME_EMPTY', 40);\ndefine('REGISTER_ERROR_PLAYER_NAME_RESTRICTED_CHARACTERS', 41);\ndefine('REGISTER_ERROR_PLAYER_NAME_SHORT', 42);\ndefine('REGISTER_ERROR_PLAYER_NAME_EXISTS', 43);\n\ndefine('LOGIN_ERROR_NO_ACCOUNT_FOR_COOKIE_SET', 44);\ndefine('PASSWORD_RESTORE_ERROR_CODE_OK_BUT_NO_ACCOUNT_FOR_EMAIL', 45);\ndefine('PASSWORD_RESTORE_ERROR_CODE_EMPTY', 46);\n\ndefine('REGISTER_EXTERNAL_AUTH_ERROR', 1000);\n\n\n\n\n\n\n//throw new exception(REGISTER_ERROR_PLAYER_NAME_TRIMMED, ERR_ERROR);\n//throw new exception(REGISTER_ERROR_PLAYER_NAME_EMPTY, ERR_ERROR);\n//throw new exception(REGISTER_ERROR_PLAYER_NAME_RESTRICTED_CHARACTERS, ERR_ERROR);\n//throw new exception(REGISTER_ERROR_PLAYER_NAME_SHORT, ERR_ERROR);\n//throw new exception(REGISTER_ERROR_PLAYER_NAME_EXISTS, ERR_ERROR);\n\n\ndefine('AUTH_LEVEL_ANONYMOUS', -10);\ndefine('AUTH_LEVEL_GUEST', -5);\ndefine('AUTH_LEVEL_REGISTERED', 0);\ndefine('AUTH_LEVEL_MODERATOR', 1);\ndefine('AUTH_LEVEL_OPERATOR', 2);\ndefine('AUTH_LEVEL_ADMINISTRATOR', 3);\ndefine('AUTH_LEVEL_DEVELOPER', 4);\ndefine('AUTH_LEVEL_SYSTEM', 99);\n\ndefine('ACCOUNT_PROVIDER_NONE', 0);\ndefine('ACCOUNT_PROVIDER_LOCAL', 1);\ndefine('ACCOUNT_PROVIDER_CENTRAL', 2);\ndefine('ACCOUNT_PROVIDER_VKONTAKTE', 3);\n\n// F_INPUT - constants\n// define('F_INPUT',                      'F_INPUT');\n// define('F_IS_REGISTER',                'F_IS_REGISTER');\n// define('F_LOGIN_UNSAFE',               'F_LOGIN_UNSAFE');\n//define('F_LOGIN_SAFE',                 'F_LOGIN_SAFE');\n//define('F_LOGIN_PASSWORD_RAW',         'F_LOGIN_PASSWORD_RAW');\n//define('F_LOGIN_PASSWORD_RAW_TRIMMED', 'F_LOGIN_PASSWORD_RAW_TRIMMED');\n// define('F_LOGIN_PASSWORD_REPEAT_RAW',  'F_LOGIN_PASSWORD_REPEAT_RAW');\n// define('F_EMAIL_UNSAFE',               'F_EMAIL_UNSAFE');\n// define('F_LANGUAGE_SAFE',              'F_LANGUAGE_SAFE');\n// define('F_REMEMBER_ME_SAFE',           'F_REMEMBER_ME_SAFE');\n\n// define('F_PASSWORD_RESET_CODE_SAFE',        'F_PASSWORD_RESET_CODE');\n\ndefine('F_HIDDEN', 'F_HIDDEN');\n\n// Global template_result fields\ndefine('AUTH_LEVEL', 'AUTH_LEVEL');\n\n//define('F_DEVICE_ID',     'F_DEVICE_ID');\n//define('F_DEVICE_CYPHER', 'F_DEVICE_CYPHER');\n\ndefine('F_PROVIDER_ID',   'F_PROVIDER_ID');\n// define('F_PROVIDER_LIST', 'F_PROVIDER_LIST');\n\ndefine('F_IMPERSONATE_STATUS', 'F_IMPERSONATE_STATUS');\ndefine('F_IMPERSONATE_OPERATOR', 'F_IMPERSONATE_OPERATOR');\n\ndefine('F_LOGIN_STATUS',  'F_LOGIN_STATUS');\ndefine('F_LOGIN_MESSAGE', 'F_LOGIN_MESSAGE');\n\ndefine('F_PLAYER_REGISTER_STATUS',  'F_PLAYER_REGISTER_STATUS');\ndefine('F_PLAYER_REGISTER_MESSAGE', 'F_PLAYER_REGISTER_MESSAGE');\n\ndefine('F_USER_ID', 'F_USER_ID');\ndefine('F_USER', 'F_USER');\ndefine('F_USER_IS_AUTHORIZED', 'F_USER_IS_AUTHORIZED');\ndefine('F_ACCOUNT_IS_AUTHORIZED', 'F_ACCOUNT_IS_AUTHORIZED');\n\n\n// define('F_ACCOUNT_ID', 'F_ACCOUNT_ID');\n// define('F_ACCOUNT', 'F_ACCOUNT');\ndefine('F_ACCOUNTS_AUTHORISED', 'F_ACCOUNTS_AUTHORISED');\n// define('F_PASSWORD_MATCHED', 'F_ACCOUNT_PASSWORD_MATCH');\n\n\ndefine('F_PASSWORD', 'F_PASSWORD');\n\ndefine('F_LOGIN_SUGGESTED_NAME', 'F_LOGIN_SUGGESTED_NAME');\n\ndefine('F_LOGIN_ACCOUNT', 'F_LOGIN_ACCOUNT');\ndefine('F_LOGIN_ACCOUNT_NAME', 'F_LOGIN_ACCOUNT');\ndefine('F_LOGIN_ACCOUNT_GLOBAL', 'F_LOGIN_ACCOUNT_GLOBAL');\ndefine('F_PASSWORD_NEW', 'F_PASSWORD_NEW');\n\n// define('F_BROWSER', 'F_BROWSER');\n// define('F_BROWSER_ID', 'F_BROWSER_ID');\n\n// define('F_PAGE', 'F_PAGE');\n// define('F_PAGE_ID', 'F_PAGE_ID');\n// define('F_URL', 'F_URL');\n// define('F_URL_ID', 'F_URL_ID');\n\ndefine('F_BANNED_STATUS', 'F_BANNED_STATUS');\ndefine('F_BANNED_MESSAGE', 'F_BANNED_MESSAGE');\n\ndefine('F_VACATION_STATUS', 'F_VACATION_STATUS');\n\n\ndefine('F_GAME_DISABLE', 'F_GAME_DISABLE');\ndefine('F_GAME_DISABLE_REASON', 'F_GAME_DISABLE_REASON');\n\n\n\n// Option groups\ndefine('OPT_ALL',      0);\ndefine('OPT_MESSAGE',  1);\ndefine('OPT_UNIVERSE', 2);\ndefine('OPT_INTERFACE', 3);\n\n// Message classes\ndefine('MSG_TYPE_OUTBOX'   ,  -1);\ndefine('MSG_TYPE_SPY'      ,   0);\ndefine('MSG_TYPE_PLAYER'   ,   1);\ndefine('MSG_TYPE_ALLIANCE' ,   2);\ndefine('MSG_TYPE_COMBAT'   ,   3);\ndefine('MSG_TYPE_RECYCLE'  ,   4);\ndefine('MSG_TYPE_TRANSPORT',   5);\ndefine('MSG_TYPE_ADMIN'    ,   6);\ndefine('MSG_TYPE_EXPLORE'  ,  15);\ndefine('MSG_TYPE_QUE'      ,  99);\ndefine('MSG_TYPE_NEW'      , 100);\n\n// Attack verification statuses\ndefine('ATTACK_ALLOWED'           ,  0);\ndefine('ATTACK_NO_TARGET'         ,  1);\ndefine('ATTACK_OWN'               ,  2);\ndefine('ATTACK_WRONG_MISSION'     ,  3);\ndefine('ATTACK_NO_ALLY_DEPOSIT'   ,  4);\ndefine('ATTACK_NO_DEBRIS'         ,  5);\ndefine('ATTACK_VACATION'          ,  6);\ndefine('ATTACK_SAME_IP'           ,  7);\ndefine('ATTACK_BUFFING'           ,  8);\ndefine('ATTACK_ADMIN'             ,  9);\ndefine('ATTACK_NOOB'              , 10);\ndefine('ATTACK_OWN_VACATION'      , 11);\ndefine('ATTACK_NO_SILO'           , 12);\ndefine('ATTACK_NO_MISSILE'        , 13);\ndefine('ATTACK_NO_FLEET'          , 14);\ndefine('ATTACK_NO_SLOTS'          , 15);\ndefine('ATTACK_NO_SHIPS'          , 16);\ndefine('ATTACK_NO_RECYCLERS'      , 17);\ndefine('ATTACK_NO_SPIES'          , 18);\ndefine('ATTACK_NO_COLONIZER'      , 19);\ndefine('ATTACK_MISSILE_TOO_FAR'   , 20);\ndefine('ATTACK_WRONG_STRUCTURE'   , 21);\ndefine('ATTACK_NO_FUEL'           , 22);\ndefine('ATTACK_NO_RESOURCES'      , 23);\ndefine('ATTACK_NO_ACS'            , 24);\ndefine('ATTACK_ACS_MISSTARGET'    , 25);\ndefine('ATTACK_WRONG_SPEED'       , 26);\ndefine('ATTACK_ACS_TOO_LATE'      , 27);\ndefine('ATTACK_BASHING'           , 28);\ndefine('ATTACK_BASHING_WAR_DELAY' , 29);\ndefine('ATTACK_ACS_WRONG_TARGET'  , 30);\ndefine('ATTACK_SAME'              , 31);\ndefine('ATTACK_RESOURCE_FORBIDDEN', 32);\ndefine('ATTACK_TRANSPORT_EMPTY'   , 33);\ndefine('ATTACK_SPIES_LONLY'       , 34);\ndefine('ATTACK_TOO_FAR'           , 35);\ndefine('ATTACK_OVERLOADED'        , 36);\ndefine('ATTACK_MISSION_ABSENT'    , 37);\ndefine('ATTACK_WRONG_UNIT'        , 38);\ndefine('ATTACK_ZERO_SPEED'        , 39);\ndefine('ATTACK_SHIP_COUNT_WRONG'  , 40);\ndefine('ATTACK_RESOURCE_COUNT_WRONG', 41);\ndefine('ATTACK_MORATORIUM', 42);\ndefine('ATTACK_CHILD_PROTECTION', 43);\ndefine('ATTACK_ACS_MAX_FLEETS', 44);\n\n\n// *** Races - Homeworlds\ndefine('RACE_NONE'    , 0);\ndefine('RACE_EARTH'   , 1);\ndefine('RACE_MOON'    , 2);\ndefine('RACE_MERCURY' , 3);\ndefine('RACE_VENUS'   , 4);\ndefine('RACE_MARS'    , 5);\ndefine('RACE_ASTEROID', 6);\n// define('MARKET_INFO'         , 7);\n\n\n\n// *** Market variables\n// === Market blocks\ndefine('MARKET_ENTRY'        , 0);\ndefine('MARKET_RESOURCES'    , 1);\ndefine('MARKET_SCRAPPER'     , 2);\ndefine('MARKET_STOCKMAN'     , 3);\ndefine('MARKET_EXCHANGE'     , 4);\ndefine('MARKET_BANKER'       , 5);\ndefine('MARKET_PAWNSHOP'     , 6);\ndefine('MARKET_INFO'         , 7);\n\n// === Market error statuses\ndefine('MARKET_NOTHING'              ,  0);\ndefine('MARKET_DEAL'                 ,  1);\ndefine('MARKET_DEAL_TRADE'           ,  2);\ndefine('MARKET_NO_DM'                ,  3);\ndefine('MARKET_NO_RESOURCES'         ,  4);\ndefine('MARKET_ZERO_DEAL'            ,  5);\ndefine('MARKET_NO_SHIPS'             ,  6);\ndefine('MARKET_NOT_A_SHIP'           ,  7);\ndefine('MARKET_NO_STOCK'             ,  8);\ndefine('MARKET_ZERO_RES_STOCK'       ,  9);\ndefine('MARKET_NEGATIVE_SHIPS'       , 10);\n\ndefine('MARKET_INFO_PLAYER'          , 12);\ndefine('MARKET_INFO_WRONG'           , 11);\ndefine('MARKET_INFO_PLAYER_NOT_FOUND', 13);\ndefine('MARKET_INFO_PLAYER_WRONG'    , 14);\ndefine('MARKET_INFO_PLAYER_SAME'     , 15);\n\n\n\n\n// *** Mercenary/talent bonus types\ndefine('BONUS_NONE'    ,            0);  // No bonus\ndefine('BONUS_PERCENT' ,            1);  // Percent on base value\ndefine('BONUS_ADD'     ,            2);  // Add\ndefine('BONUS_ABILITY' ,            3);  // Some ability\ndefine('BONUS_MULTIPLY',            4);  // Multiply by value\n//define('BONUS_PERCENT_CUMULATIVE' , 5);  // Cumulative percent on base value\n//define('BONUS_PERCENT_DEGRADED' ,   6);  // Bonus amount degraded with increase as pow(bonus, level) (?)\n//define('BONUS_SPEED',               7);  // Speed bonus\n\n// *** Action constant (build should be replaced with ACTION)\ndefine('BUILD_CREATE' ,  1);\ndefine('BUILD_DESTROY', -1);\ndefine('BUILD_AUTOCONVERT', 2);\n\ndefine('ACTION_SELL'       , -1);\ndefine('ACTION_NOTHING'    ,  0);\ndefine('ACTION_BUY'        ,  1);\ndefine('ACTION_USE'        ,  2);\ndefine('ACTION_DELETE'     ,  3);\n\n// *** Check unit availability codes\ndefine('BUILD_ALLOWED'         , 0); // HARDCODED! DO NOT CHANGE!\ndefine('BUILD_REQUIRE_NOT_MEET', 1);\ndefine('BUILD_AMOUNT_WRONG'    , 2);\ndefine('BUILD_QUE_WRONG'       , 3);\ndefine('BUILD_QUE_UNIT_WRONG'  , 4);\ndefine('BUILD_INDESTRUCTABLE'  , 5);\ndefine('BUILD_NO_RESOURCES'    , 6);\ndefine('BUILD_NO_UNITS'        , 7);\ndefine('BUILD_UNIT_BUSY'       , 8);\ndefine('BUILD_QUE_FULL'        , 9);\ndefine('BUILD_SILO_FULL'       ,10);\ndefine('BUILD_MAX_REACHED'     ,11);\ndefine('BUILD_SECTORS_NONE'    ,12);\ndefine('BUILD_AUTOCONVERT_AVAILABLE', 13);\ndefine('BUILD_HIGHSPOT_NOT_ACTIVE', 14);\n\n\n// *** Que types\ndefine('QUE_STRUCTURES', 1);\ndefine('QUE_HANGAR'    , 4);\ndefine('QUE_RESEARCH'  , 7);\ndefine('QUE_MERCENARY' , 600); // UNIT_MERCENARIES\n// *** Subque types\ndefine('SUBQUE_PLANET'  , 1);\ndefine('SUBQUE_MOON'    , 3);\ndefine('SUBQUE_FLEET'   , 4);\ndefine('SUBQUE_DEFENSE' , 6);\ndefine('SUBQUE_RESEARCH', 7);\n\n// *** Que items\ndefine('QI_UNIT_ID'   , 0);\ndefine('QI_AMOUNT'    , 1);\ndefine('QI_TIME'      , 2);\ndefine('QI_MODE'      , 3);\ndefine('QI_QUE_ID'    , 4);\ndefine('QI_QUE_TYPE'  , 4);\ndefine('QI_PLANET_ID' , 5);\n\n\n// *** Units\n\n// *** Sort options\ndefine('SORT_ASCENDING' , 0);\ndefine('SORT_DESCENDING', 1);\n\ndefine('SORT_NONE'           , 0);\ndefine('SORT_ID'             , 12);\ndefine('SORT_LOCATION'       , 1);\ndefine('SORT_NAME'           , 2);\ndefine('SORT_SIZE'           , 3);\ndefine('SORT_EMAIL'          , 4);\ndefine('SORT_IP'             , 5);\ndefine('SORT_TIME_REGISTERED', 6);\ndefine('SORT_TIME_LAST_VISIT', 7);\ndefine('SORT_TIME_BAN_UNTIL' , 8);\ndefine('SORT_REFERRAL_COUNT' , 9);\ndefine('SORT_REFERRAL_DM'    , 10);\ndefine('SORT_VACATION'       , 11);\n\n\ndefine('HULL_SIZE_TINY', 1);\ndefine('HULL_SIZE_SMALL', 2);\ndefine('HULL_SIZE_MEDIUM', 3);\ndefine('HULL_SIZE_LARGE', 4);\ndefine('HULL_SIZE_HUGE', 5);\n\n\ndefine('SNC_VER_NEVER', -1);\ndefine('SNC_VER_ERROR_CONNECT', 0);\ndefine('SNC_VER_EXACT', 1);\ndefine('SNC_VER_LESS', 2);\ndefine('SNC_VER_FUTURE', 3);\ndefine('SNC_VER_RELEASE_EXACT', 10);\ndefine('SNC_VER_RELEASE_MINOR', 11);\ndefine('SNC_VER_RELEASE_MAJOR', 12);\ndefine('SNC_VER_RELEASE_ALPHA', 13);\ndefine('SNC_VER_MAINTENANCE', 96);\ndefine('SNC_VER_UNKNOWN_RESPONSE', 97);\ndefine('SNC_VER_INVALID', 98);\ndefine('SNC_VER_STRANGE', 99);\ndefine('SNC_VER_ERROR_SERVER', 100);\n\ndefine('SNC_VER_REGISTER_UNREGISTERED', 200);\ndefine('SNC_VER_REGISTER_ERROR_MULTISERVER', 201);\ndefine('SNC_VER_REGISTER_ERROR_REGISTERED', 202);\ndefine('SNC_VER_REGISTER_ERROR_NO_NAME', 203);\ndefine('SNC_VER_REGISTER_ERROR_WRONG_URL', 204);\ndefine('SNC_VER_REGISTER_REGISTERED', 299);\n\ndefine('SNC_VER_ERROR_INCOMPLETE_REQUEST', 301);\ndefine('SNC_VER_ERROR_UNKNOWN_KEY', 302);\ndefine('SNC_VER_ERROR_MISSMATCH_KEY_ID', 303);\n\ndefine('SNC_MODE_VERSION_CHECK', 0);\ndefine('SNC_MODE_REGISTER', 1);\ndefine('SNC_MODE_CHANGELOG', 2);\n\ndefine('ALI_BONUS_BY_PLAYERS', 0);\ndefine('ALI_BONUS_BY_SIZE', 1);\ndefine('ALI_BONUS_BY_POINTS', 2);\ndefine('ALI_BONUS_BY_RANK', 3);\n\n// Admin tools constants\ndefine('ADM_TOOL_CONFIG_RELOAD', 1);\ndefine('ADM_TOOL_MD5', 2);\ndefine('ADM_TOOL_FORCE_ALL', 3);\ndefine('ADM_TOOL_FORCE_LAST', 4);\ndefine('ADM_TOOL_INFO_PHP', 5);\ndefine('ADM_TOOL_INFO_SQL', 6);\ndefine('ADM_PTL_TEST', 7);\ndefine('ADM_COUNTER_RECALC', 8);\n\ndefine('STAT_TOTAL', 0);\ndefine('STAT_FLEET', 1);\ndefine('STAT_TECH', 2);\ndefine('STAT_BUILDING', 3);\ndefine('STAT_DEFENSE', 4);\ndefine('STAT_RESOURCE', 5);\ndefine('STAT_RAID_TOTAL', 6);\ndefine('STAT_RAID_WON', 7);\ndefine('STAT_RAID_LOST', 8);\ndefine('STAT_LVL_BUILDING', 9);\ndefine('STAT_LVL_TECH', 10);\ndefine('STAT_LVL_RAID', 11);\n\ndefine('CHAT_MODE_COMMON', 0);\ndefine('CHAT_MODE_ALLY', 1);\n\ndefine('BUDDY_REQUEST_WAITING', 0);\ndefine('BUDDY_REQUEST_ACTIVE', 1);\ndefine('BUDDY_REQUEST_DENIED', 2);\n\ndefine('REQUIRE_MET_NOT', 0);\ndefine('REQUIRE_MET', 1);\n\n\ndefine('CHAT_OPTION_SWITCH', 1);\n\n// Modifier constants\ndefine('MODIFIER_NONE', 0);\ndefine('MODIFIER_RESOURCE_CAPACITY', 1);\ndefine('MODIFIER_RESOURCE_PRODUCTION', 2);\n// define('MODIFIER_FUSION_OUTPUT', 3);\n\ndefine('SQL_OP_DELETE', -1);\ndefine('SQL_OP_UPDATE', 0);\ndefine('SQL_OP_INSERT', 1);\ndefine('SQL_OP_REPLACE', 3);\n\n\ndefine('SERVER_PLAYER_NAME_CHANGE_NONE', 0);\ndefine('SERVER_PLAYER_NAME_CHANGE_FREE', 1);\ndefine('SERVER_PLAYER_NAME_CHANGE_PAY', 2);\n\n\n\n\ndefine('USER_OPTIONS_SPLIT', '|');\n\n// define('NICK_ID',               -1);\ndefine('NICK_HTML',              0);\n\ndefine('NICK_FIRST',             1);\ndefine('NICK_RACE',           1000);\ndefine('NICK_GENDER',         2000);\ndefine('NICK_AWARD',          3000);\ndefine('NICK_VACATION',       3500);\ndefine('NICK_BIRTHDAY',       4000);\ndefine('NICK_RANK',           4500);\ndefine('NICK_RANK_NO_TEXT',   4750);\ndefine('NICK_PREMIUM',        5000);\ndefine('NICK_AUTH_LEVEL',     6000);\n\ndefine('NICK_HIGHLIGHT',      6300);\ndefine('NICK_CLASS',          6450);\n\ndefine('NICK_NICK_CLASS',     6600);\ndefine('NICK_NICK',           7000);\ndefine('NICK_NICK_CLASS_END', 7300);\n\ndefine('NICK_ALLY_CLASS',     7600);\ndefine('NICK_ALLY',           8000);\ndefine('NICK_ALLY_CLASS_END', 8300);\n\ndefine('NICK_CLASS_END',      8450);\ndefine('NICK_HIGHLIGHT_END',  8600);\n\ndefine('NICK_LAST',           9999);\ndefine('NICK_SORT',          10000);\n\n// Настройки игрока\ndefine('PLAYER_OPTION_MENU_SORT', 1);\ndefine('PLAYER_OPTION_MENU_HIDE_SHOW_BUTTON', 2);\ndefine('PLAYER_OPTION_MENU_SHOW_ON_BUTTON', 3);\ndefine('PLAYER_OPTION_MENU_HIDE_ON_BUTTON', 4);\ndefine('PLAYER_OPTION_MENU_HIDE_ON_LEAVE', 5);\ndefine('PLAYER_OPTION_MENU_UNPIN_ABSOLUTE', 6);\ndefine('PLAYER_OPTION_MENU_ITEMS_AS_BUTTONS', 7);\ndefine('PLAYER_OPTION_SOUND_ENABLED', 8);\ndefine('PLAYER_OPTION_FLEET_SHIP_SORT', 9);\ndefine('PLAYER_OPTION_FLEET_SHIP_SORT_INVERSE', 10);\ndefine('PLAYER_OPTION_CURRENCY_DEFAULT', 11);\ndefine('PLAYER_OPTION_FLEET_SPY_DEFAULT', 12);\n// define('PLAYER_OPTION_FLEET_MESS_AMOUNT_MAX', 13);\ndefine('PLAYER_OPTION_UNIVERSE_ICON_SPYING', 14);\ndefine('PLAYER_OPTION_UNIVERSE_ICON_MISSILE', 15);\ndefine('PLAYER_OPTION_UNIVERSE_ICON_PM', 16);\ndefine('PLAYER_OPTION_UNIVERSE_ICON_STATS', 17);\ndefine('PLAYER_OPTION_UNIVERSE_ICON_PROFILE', 18);\ndefine('PLAYER_OPTION_UNIVERSE_ICON_BUDDY', 19);\ndefine('PLAYER_OPTION_PLANET_SORT', 20);\ndefine('PLAYER_OPTION_PLANET_SORT_INVERSE', 21);\ndefine('PLAYER_OPTION_TOOLTIP_DELAY', 22);\n// define('PLAYER_OPTION_UNIVERSE_PLAYER_AVATAR_SHOW', 23);\ndefine('PLAYER_OPTION_BASE_FONT_SIZE', 24);\ndefine('PLAYER_OPTION_BUILD_AUTOCONVERT_HIDE', 25);\ndefine('PLAYER_OPTION_BUILDING_SORT', 26);\ndefine('PLAYER_OPTION_BUILDING_SORT_INVERSE', 27);\ndefine('PLAYER_OPTION_NAVBAR_DISABLE_PLANET', 28);\ndefine('PLAYER_OPTION_NAVBAR_DISABLE_HANGAR', 29);\ndefine('PLAYER_OPTION_NAVBAR_DISABLE_QUESTS', 30);\ndefine('PLAYER_OPTION_ANIMATION_DISABLED', 31);\ndefine('PLAYER_OPTION_MENU_WHITE_TEXT', 32);\ndefine('PLAYER_OPTION_MENU_OLD', 33);\ndefine('PLAYER_OPTION_UNIVERSE_OLD', 34);\ndefine('PLAYER_OPTION_UNIVERSE_DISABLE_COLONIZE', 35);\ndefine('PLAYER_OPTION_DESIGN_DISABLE_BORDERS', 36);\ndefine('PLAYER_OPTION_TECH_TREE_TABLE', 37);\ndefine('PLAYER_OPTION_PROGRESS_BARS_DISABLED', 38);\ndefine('PLAYER_OPTION_NAVBAR_DISABLE_RESEARCH', 39);\ndefine('PLAYER_OPTION_NAVBAR_DISABLE_FLYING_FLEETS', 40);\ndefine('PLAYER_OPTION_NAVBAR_DISABLE_EXPEDITIONS', 41);\ndefine('PLAYER_OPTION_NAVBAR_RESEARCH_WIDE', 42);\ndefine('PLAYER_OPTION_NAVBAR_DISABLE_META_MATTER', 43);\ndefine('PLAYER_OPTION_NAVBAR_PLANET_VERTICAL', 44);\ndefine('PLAYER_OPTION_FLEET_SHIP_SELECT_OLD', 45);\ndefine('PLAYER_OPTION_FLEET_SHIP_HIDE_SPEED', 46);\ndefine('PLAYER_OPTION_FLEET_SHIP_HIDE_CAPACITY', 47);\ndefine('PLAYER_OPTION_FLEET_SHIP_HIDE_CONSUMPTION', 48);\ndefine('PLAYER_OPTION_TUTORIAL_DISABLED', 49);\ndefine('PLAYER_OPTION_TUTORIAL_WINDOWED', 50);\ndefine('PLAYER_OPTION_TUTORIAL_CURRENT', 51);\ndefine('PLAYER_OPTION_TUTORIAL_FINISHED', 52);\ndefine('PLAYER_OPTION_NAVBAR_PLANET_OLD', 53);\ndefine('PLAYER_OPTION_NAVBAR_PLANET_DISABLE_STORAGE', 54);\ndefine('PLAYER_OPTION_QUEST_LIST_FILTER', 55);\ndefine('PLAYER_OPTION_LOGIN_REWARDED_LAST', 56);\ndefine('PLAYER_OPTION_LOGIN_REWARDED_LAST_VIEWED', 57);\ndefine('PLAYER_OPTION_LOGIN_REWARD_STREAK_BEGAN', 58);\ndefine('PLAYER_OPTION_NAVBAR_DISABLE_DEFENSE', 59);\n\n// -------------------\ndefine('PLAYER_OPTION_MENU_HIDE_SHOW_BUTTON_FIXED', 0);\ndefine('PLAYER_OPTION_MENU_HIDE_SHOW_BUTTON_NORMAL', 1);\ndefine('PLAYER_OPTION_MENU_HIDE_SHOW_BUTTON_HIDDEN', 2);\n\n\n\n\ndefine('PLAYER_OPTION_SORT_ORDER_PLAIN', 0);\ndefine('PLAYER_OPTION_SORT_ORDER_REVERSE', 1);\n\ndefine('PLAYER_OPTION_SORT_DEFAULT', 1); // Starting from 1 - because '0' is \"not selected\" or \"no data\"\ndefine('PLAYER_OPTION_SORT_NAME', 2);\ndefine('PLAYER_OPTION_SORT_SPEED', 3);\ndefine('PLAYER_OPTION_SORT_COUNT', 4);\ndefine('PLAYER_OPTION_SORT_ID', 5);\ndefine('PLAYER_OPTION_SORT_CREATE_TIME_LENGTH', 6);\n\n\n\ndefine('GENDER_UNKNOWN', 0);\ndefine('GENDER_MALE', 1);\ndefine('GENDER_FEMALE', 2);\n\ndefine('GAME_DISABLE_NONE', 0);\ndefine('GAME_DISABLE_REASON', 1);\ndefine('GAME_DISABLE_UPDATE', 2);\ndefine('GAME_DISABLE_STAT', 3);\ndefine('GAME_DISABLE_INSTALL', 4);\ndefine('GAME_DISABLE_EVENT_BLACK_MOON', 5);\ndefine('GAME_DISABLE_MAINTENANCE', 6);\ndefine('GAME_DISABLE_EVENT_OIS', 7);\n\ndefine('USER_BOT_PLAYER', 0);\ndefine('USER_BOT_BLACK_MOON', 1);\ndefine('USER_BOT_OIS', 2);\n\ndefine('LOG_ONLIINE_AGGREGATE_NONE', 0);\ndefine('LOG_ONLIINE_AGGREGATE_PERIOD_MINUTE_10', 1);\n\ndefine('BLITZ_REGISTER_DISABLED', 0);\ndefine('BLITZ_REGISTER_OPEN'    , 1);\ndefine('BLITZ_REGISTER_CLOSED'  , 2);\ndefine('BLITZ_REGISTER_SHOW_LOGIN', 3);\ndefine('BLITZ_REGISTER_DISCLOSURE_NAMES', 4);\n\ndefine('EVENT_HALLOWEEN_LOCKED', 1);\ndefine('EVENT_HALLOWEEN_CODE', 2);\n\ndefine('SKIN_IMAGE_MISSED_FIELD', '_no_image');\ndefine('SKIN_IMAGE_MISSED_FILE_PATH', 'design/images/_no_image.png');\n/**\n * Оригинальный вид тэга из темплейта без ведущей I_\n */\ndefine('SKIN_IMAGE_TAG_RAW', 0);\n/**\n * Тэг, в котором отресолвлены зависимости, а так же отсотированы и переупорядочены опции\n * Этот вид тэга будет использоваться как ключ для поиска в контейнере компилированных строк\n */\ndefine('SKIN_IMAGE_TAG_RESOLVED', 1);\n/**\n * ID изображения для адресации через skin.ini\n */\ndefine('SKIN_IMAGE_TAG_IMAGE_ID', 2);\n/**\n * Параметры тэга в массиве - отсортированные и переупорядоченные\n */\ndefine('SKIN_IMAGE_TAG_PARAMS', 3);\n\ndefine('DB_SELECT_PLAIN', false);\ndefine('DB_SELECT_FOR_UPDATE', true);\n\n/**\n * Defining some constants\n */\ndefine('AUTH_LOGIN_EXTERNAL_NAME', 'auth_ext');\ndefine('AUTH_LOGIN_EXTERNAL_MODE', 'auth_mode');\n\ndefine('AUTH_VKONTAKTE', 'vkontakte');\ndefine('AUTH_VKONTAKTE_PARAM_STATE', 'state');\ndefine('AUTH_VKONTAKTE_PARAM_CODE', 'code');\ndefine('AUTH_VKONTAKTE_PARAM_ERROR', 'code');\ndefine('AUTH_VKONTAKTE_CODE_GET', 1);\n\ndefine('URL_PARAM_SEPARATOR', '*');\n\ndefine('ACCESSOR_NORMAL', false);\ndefine('ACCESSOR_SHARED', true);\n\ndefine(\"P_ACCESSOR_SET\", '__set'); // DO NOT CHANGE!!!\ndefine(\"P_ACCESSOR_GET\", '__get'); // DO NOT CHANGE!!!\ndefine(\"P_ACCESSOR_UNSET\", '__unset'); // DO NOT CHANGE!!!\ndefine(\"P_ACCESSOR_ISSET\", '__isset'); // DO NOT CHANGE!!!\ndefine(\"P_ACCESSOR_IMPORT\", 'import');\ndefine(\"P_ACCESSOR_EXPORT\", 'export');\n\ndefine('HTML_ENCODE_NONE', 0); // Do nothing - just bypass\ndefine('HTML_ENCODE_PREFORM', 1); // perform HTML encoding\ndefine('HTML_ENCODE_NL2BR', 2); // should line breaks be converted to <br />\ndefine('HTML_ENCODE_STRIP_HTML', 4); // should HTML be cut from text\ndefine('HTML_ENCODE_JS_SAFE', 8); // should be string encoded for use in JavaScript\ndefine('HTML_ENCODE_SPACE', 16); // should be spaces replaced with $nbsp;\n\ndefine('HTML_ENCODE_MULTILINE', HTML_ENCODE_PREFORM | HTML_ENCODE_NL2BR);\ndefine('HTML_ENCODE_MULTILINE_JS', HTML_ENCODE_MULTILINE | HTML_ENCODE_JS_SAFE);\n\ndefine('PAGE_OPTION_EARLY_HEADER', 'early_header');\ndefine('PAGE_OPTION_TITLE', 'page_title');\n\ndefine('FIELD_MVC', 'mvc');\ndefine('FIELD_MODEL', 'model');\ndefine('FIELD_VIEW', 'view');\ndefine('MVC_OPTIONS', 'options');\ndefine('MVC_HOOK_LOCKS', 'MVC_HOOK_LOCKS');\n\ndefine('THIS_STRING', '$this');\n\ndefine('MENU_SERVER_LOGO_DEFAULT', 'design/images/supernova.png');\n\n// define('GAME_FLEET_HANDLER_MAX_TIME', 3); // How long Flying Fleet Handler can work\n\ndefine('ALLIANCE_HEAD_INACTIVE_TIMEOUT', PERIOD_DAY * 30);\nconst PLAYER_INACTIVE_TIMEOUT = PERIOD_WEEK; // Player inactivity timeout to become 'i'-marked player\nconst PLAYER_INACTIVE_TIMEOUT_LONG = PERIOD_WEEK_4; // Player inactivity to become 'I'-marked player\n\n\n\ndefine('EVENT_ANY', -2);\ndefine('EVENT_ALL', -1);\ndefine('EVENT_NONE', 0);\n\ndefine('STR_OBSERVER_ENTRY_METHOD_NAME', '_update');\n\ndefine('SN_SQL_TYPE_NAME_TIMESTAMP', 'timestamp');\ndefine('SN_SQL_DEFAULT_CURRENT_TIMESTAMP', 'CURRENT_TIMESTAMP');\ndefine('SN_SQL_EXTRA_AUTO_INCREMENT', 'auto_increment');\n\ndefine('STRING_IS_ESCAPED', true);\ndefine('STRING_NEED_ESCAPING', false);\ndefine('STRING_IS_JSON_ENCODED', true);\n\ndefine('GROUP_MODIFIERS_NAME', 'modifiers');\n\n// Paging constants\ndefine('PAGING_PAGE_SIZE_MINIMUM', 5); // Minimum page size\ndefine('PAGING_PAGE_SIZE_DEFAULT', 10); // Default page size\ndefine('PAGING_PAGE_SIZE_DEFAULT_MESSAGES', 10); // Default page size for messaging\ndefine('PAGING_PAGE_SIZE_DEFAULT_PAYMENTS', 25); // Default page size for payments\ndefine('PAGING_SIZE_MAX_DELTA', 3); // Maximum delta from current page to write all pages numbers when paging\n\ndefine('TEMPLATE_EXTRA_ARRAY', 'template_extra');\n\ndefine('PATCH_REGISTER', false);\ndefine('PATCH_PRE_CHECK', true);\n\nconst PLAYER_RANK_20 = 20;\nconst PLAYER_RANK_MAX = PLAYER_RANK_20;\n\nconst GROUP_DESIGN_OPTION_BLOCKS = 'GROUP_DESIGN_OPTION_BLOCKS';\nconst GROUP_DESIGN_BLOCK_TUTORIAL = 0; //+\nconst GROUP_DESIGN_BLOCK_FLEET_COMPOSE = 1; //+\nconst GROUP_DESIGN_BLOCK_UNIVERSE = 2; //+\nconst GROUP_DESIGN_BLOCK_NAVBAR = 3; //+\nconst GROUP_DESIGN_BLOCK_RESOURCEBAR = 4; //+\nconst GROUP_DESIGN_BLOCK_PLANET_SORT = 6; //+\nconst GROUP_DESIGN_BLOCK_COMMON_ONE = 7; //+\nconst GROUP_DESIGN_BLOCK_COMMON_TWO = 8; //+\n\nconst MODULE_LOAD_ORDER_CORE_AUTH             = 1;\nconst MODULE_LOAD_ORDER_AUTH_LOCAL            = 2;\nconst MODULE_LOAD_ORDER_AUTH_VKONTAKTE        = 3;\nconst MODULE_LOAD_ORDER_PAYMENT_SECONDARY     = 90000;\nconst MODULE_LOAD_ORDER_UNIT_RES_METAMATTER   = 99999;\nconst MODULE_LOAD_ORDER_DEFAULT               = 100000;     // HARDCODED\nconst MODULE_LOAD_ORDER_CORE_SHIP_CONSTRUCTOR = 999999;     // RESERVED\nconst MODULE_LOAD_ORDER_MENU_CUSTOMIZE        = 200000000;\nconst MODULE_LOAD_ORDER_LATEST                = 2000000000; // HARDCODED\nconst MODULE_LOAD_ORDER_GAME_SKIRMISH         = 2000100000;\n\n// Template block names\nconst TPL_BLOCK_REQUIRE = 'require';\n\nconst PAGE_OPTION_FLEET_UPDATE_SKIP = 'fleet_update_skip';\nconst PAGE_OPTION_ADMIN = 'admin_page';\n\nconst MENU_FIELD_AUTH_LEVEL = 'AUTH_LEVEL';\n\nconst UNIVERSE_GALAXY_DISTANCE = 20000;\n"
  },
  {
    "path": "includes/constants/constants_fake.php",
    "content": "<?php\n\n/**\n * This file is needed only for phpStorm hints constants from modules\n * This file is never included into project and no actual actions taken here\n */\n\nreturn;\n\ndefine('UNUSED_TEST_CONSTANT_REALLY_NEVER_USED', true);\n"
  },
  {
    "path": "includes/constants/constants_params.php",
    "content": "<?php\n/**\n * Created by Gorlum 27.03.2018 10:41\n */\n\n// Unit params\n// define('GROUP_PARAMS', 1000000000);\n// Зарезервировано для параметров: 1.000.000.000-2.000.000.000\ndefine('P_MAX_STACK', 'max');\ndefine('P_NAME', 'name'); // Вот тут будет следующая фаза - избавится вообще от обращения к P_NAME и перевести все обращения к UNIT_ID\ndefine('P_UNIT_TYPE', 'type');\ndefine('P_UNIT_TEMPORARY', 'temporary');\ndefine('P_COST', 'cost');\ndefine('P_COST_METAL', 'metal_cost');\ndefine('P_FACTOR', 'factor');\ndefine('P_REQUIRE', 'require');\ndefine('P_STORAGE', 'storage');\ndefine('P_STACKABLE', 'stackable'); // COMPLETE\ndefine('P_DEPLOY', 'deploy');\ndefine('P_BONUS_VALUE', 'bonus');\ndefine('P_BONUS_TYPE', 'bonus_type');\ndefine('P_CAPACITY', 'capacity');\ndefine('P_UNIT_SIZE', 'size');\ndefine('P_SPEED', 'speed');\ndefine('P_UNIT_PRODUCTION', 'production');\ndefine('P_UNIT_HIDE_FROM_BUILD', 'P_UNIT_HIDE_FROM_BUILD');\n\ndefine('P_CHANCE', 'chance');\n\ndefine('P_CHAT', 'chat');\ndefine('P_CHAT_COMMANDS', 'commands');\ndefine('P_CHAT_ALIASES', 'aliases');\n\ndefine('P_RACE', 'player_race');\n\ndefine('P_UNIT_GRANTS', 'P_UNIT_GRANTS'); // Что дает этот юнит\n\ndefine('P_ATTACK', 'attack');\ndefine('P_SHIELD', 'shield');\ndefine('P_ARMOR', 'armor');\ndefine('P_AMPLIFY', 'amplify');\ndefine('P_DEFENSE', 'defense');\ndefine('P_STRUCTURE', 'structure');\ndefine('P_LOCATION', 'location');\ndefine('P_CONSUMPTION', 'consumption');\ndefine('P_UNIT_ENGINE', 'engine');\nconst P_REQUIRE_HIGHSPOT = 'festival_highspot';\n/** @var string Param name in ship description to identify race to which this ship belongs */\nconst P_RACE_SHIP        = 'player_race';\n\ndefine('P_ID', 'id');\ndefine('P_SNID', 'snid');\n\ndefine('P_TABLE_NAME', 'table_name');\ndefine('P_OWNER_INFO', 'owner_info');\ndefine('P_OWNER_FIELD', 'owner_field');\n\ndefine('P_WHERE', 'P_WHERE');\ndefine('P_WHERE_STR', 'P_WHERE_STR');\ndefine('P_FIELDS_STR', 'P_FIELDS_STR');\ndefine('P_QUERY_STR', 'P_QUERY_STR');\ndefine('P_ACTION_STR', 'P_ACTION_STR');\ndefine('P_VERSION', 'P_VERSION');\n\ndefine('P_THRUST', 'P_THRUST');\ndefine('P_HULL_SIZE', 'P_HULL_SIZE');\ndefine('P_CALC', 'P_CALC');\ndefine('P_WEIGHT', 'P_WEIGHT');\ndefine('P_PART_TYPE', 'P_PART_TYPE');\ndefine('P_HULL_CAPACITY', 'P_HULL_CAPACITY');\ndefine('P_COST_TOTAL', 'unit_cost');\n\ndefine('P_OPTIONS', 'options');\ndefine('P_ONLY_DARK_MATTER', 'P_ONLY_DARK_MATTER');\ndefine('P_TIME_RAW', 'P_TIME_RAW');\n\ndefine('P_MINING_IS_MANAGED', 'P_MINING_IS_MANAGED'); // Флаг, означающий что добычей ресурсов можно управлять\n\nconst P_MULTIPLIER = 'multiplier';\nconst P_MESSAGE_ID = 'messageId';\n\nconst P_FLEET_ATTACK_RES_LIST = 'resource_list';\nconst P_FLEET_ATTACK_SPEED_PERCENT_TENTH = 'fleet_speed_percent_tenth';\nconst P_FLEET_ATTACK_STAY_TIME = 'stay_time';\nconst P_FLEET_ATTACK_TARGET_STRUCTURE = 'target_structure';\nconst P_FLEET_ATTACK_FLYING_COUNT = 'flying_fleets';\nconst P_FLEET_ATTACK_FLEET_GROUP = 'fleet_group';\nconst P_FLEET_ATTACK_RESOURCES_SUM = 'resources';\n"
  },
  {
    "path": "includes/constants/constants_payments.php",
    "content": "<?php\n/**\n * Created by Gorlum 27.03.2018 10:47\n */\n\ndefine('PAYMENT_STATUS_ALL', -1); // All statuses\ndefine('PAYMENT_STATUS_NONE', 0); // No status\ndefine('PAYMENT_STATUS_COMPLETE', 1); // Money received, DM sent to user\ndefine('PAYMENT_STATUS_CANCELED', 2); // Payment cancelled, MM deduced from user\ndefine('PAYMENT_STATUS_EXPIRED', 3); // Payment cancelled, MM deduced from user\n\nconst PAYMENT_TEST_ALL   = -1;\nconst PAYMENT_TEST_REAL  = 0;\nconst PAYMENT_TEST_PROBE = 1;\n\nconst PAYMENT_FILTER_STAT_NORMAL = 0;\nconst PAYMENT_FILTER_STAT_MONTH  = 1;\nconst PAYMENT_FILTER_STAT_YEAR   = 2;\nconst PAYMENT_FILTER_STAT_ALL    = 3;\n\ndefine('PAYMENT_EXPIRE_TIME', 0);\n\ndefine('SN_PAYMENT_REQUEST_UNDEFINED_ERROR', -1);\ndefine('SN_PAYMENT_REQUEST_OK', 0);\ndefine('SN_PAYMENT_REQUEST_ERROR_UNIT_AMOUNT', 1);\ndefine('SN_PAYMENT_REQUEST_ERROR_PAYLINK_UNSUPPORTED', 2);\ndefine('SN_PAYMENT_REQUEST_IP_WRONG', 3);  // Неправильный IP входящей системы - обычно хак\ndefine('SN_PAYMENT_REQUEST_COMMAND_UNSUPPORTED', 4); // Неподдерживаемая команда - обычно хак\ndefine('SN_PAYMENT_REQUEST_SIGNATURE_INVALID', 5); // Неправильная подпись или не сошлась контрольная сумма - обычно хак\ndefine('SN_MODULE_DISABLED', 6); // Модуль отключен // УНИВЕРСАЛЬНЫЙ ОТВЕТ!\ndefine('SN_PAYMENT_REQUEST_SERVER_WRONG', 7); // Не совпадает УРЛ сервера\ndefine('SN_PAYMENT_REQUEST_USER_NOT_FOUND', 8); // Пользователь не найден\ndefine('SN_PAYMENT_REQUEST_EXTERNAL_ID_WRONG', 9); // Остуствует или неправильный ИД операции в платежной системе\ndefine('SN_PAYMENT_REQUEST_CURRENCY_AMOUNT_INVALID', 10); // Неправильная сумма платежа\ndefine('SN_PAYMENT_REQUEST_DATE_INVALID', 11); // Неправильная дата платежа\ndefine('SN_DB_ERROR_WRITE', 12); // Ошибка записи в БД // УНИВЕРСАЛЬНЫЙ ОТВЕТ!\ndefine('SN_METAMATTER_ERROR_ADJUST', 13); // Ошибка начисления ММ // УНИВЕРСАЛЬНЫЙ ОТВЕТ!\ndefine('SN_PAYMENT_REQUEST_INTERNAL_ID_WRONG', 14); // Остуствует или неправильный внутренний ИД операции\ndefine('SN_PAYMENT_REQUEST_MM_AMOUNT_INVALID', 15); // Неправильное количеств ММ в платеже\ndefine('SN_PAYMENT_REQUEST_ORDER_WRONG', 16); // Неправильно составлен ордер от платежной системы\ndefine('SN_PAYMENT_REQUEST_INVOICE_ALREADY_CANCELLED', 17); // Ошибка отмены платежа - платеж уже отменен\ndefine('SN_PAYMENT_REQUEST_ORDER_NOT_FOUND', 18); // Ошибка\ndefine('SN_PAYMENT_REQUEST_PAYMENT_NOT_COMPLETE', 19); // Ошибка отмены платежа - платеж еще не закончен\n// define('SN_PAYMENT_REQUEST_CANCEL_COMPLETE', 20); //\ndefine('SN_PAYMENT_REQUEST_INTERNAL_CONFIG_ERROR', 21); // Ошибка конфигурации\ndefine('SN_PAYMENT_REQUEST_DB_ERROR_PAYMENT_CREATE', 22); // Ошибка создания платежа\ndefine('SN_PAYMENT_REQUEST_ERROR_TEST_PAYMENT', 23); // Ошибка - внешний статус платежа не совпадает с запомненным статусом платежа\ndefine('SN_PAYMENT_ERROR_INTERNAL_NO_EXTERNAL_CURRENCY_SET', 24); // Ошибка - не установлена внешняя валюта\n\ndefine('PAYMENT_DESCRIPTION_MAX', 0);\ndefine('PAYMENT_DESCRIPTION_50', 50);\ndefine('PAYMENT_DESCRIPTION_100', 100);\ndefine('PAYMENT_DESCRIPTION_250', 250);\n\n// Smallint -32768..32767\nconst PAYMENT_METHOD_EMONEY              = 1000;\nconst PAYMENT_METHOD_EMONEY_WEBMONEY_WMZ = 1001;\nconst PAYMENT_METHOD_EMONEY_WEBMONEY_WME = 1002;\nconst PAYMENT_METHOD_EMONEY_WEBMONEY_WMU = 1003;\nconst PAYMENT_METHOD_EMONEY_WEBMONEY_WMR = 1004;\nconst PAYMENT_METHOD_EMONEY_WEBMONEY_WMB = 1005;\nconst PAYMENT_METHOD_EMONEY_YANDEX       = 1006;\nconst PAYMENT_METHOD_EMONEY_QIWI         = 1007;\nconst PAYMENT_METHOD_EMONEY_MAILRU       = 1008;\nconst PAYMENT_METHOD_EMONEY_ELECSNET     = 1009;\nconst PAYMENT_METHOD_EMONEY_EASYPAY      = 1010;\nconst PAYMENT_METHOD_EMONEY_RUR_W1R      = 1011;\nconst PAYMENT_METHOD_EMONEY_TELEMONEY    = 1012;\nconst PAYMENT_METHOD_EMONEY_PAYPAL       = 1013;\n\nconst PAYMENT_METHOD_BANK_CARD                  = 2000;\nconst PAYMENT_METHOD_BANK_CARD_STANDARD         = 2001;\nconst PAYMENT_METHOD_BANK_CARD_LIQPAY           = 2002;\nconst PAYMENT_METHOD_BANK_CARD_EASYPAY          = 2003;\nconst PAYMENT_METHOD_BANK_CARD_AMERICAN_EXPRESS = 2004;\nconst PAYMENT_METHOD_BANK_CARD_JCB              = 2005;\nconst PAYMENT_METHOD_BANK_CARD_UNIONPAY         = 2006;\n\nconst PAYMENT_METHOD_MOBILE             = 3000;\nconst PAYMENT_METHOD_MOBILE_MEGAPHONE   = 3001;\nconst PAYMENT_METHOD_MOBILE_MTS         = 3002;\nconst PAYMENT_METHOD_MOBILE_KYIVSTAR    = 3003;\nconst PAYMENT_METHOD_MOBILE_SMS         = 3004;\nconst PAYMENT_METHOD_MOBILE_PAYPAL_ZONG = 3005;\nconst PAYMENT_METHOD_MOBILE_XSOLLA      = 3006;\nconst PAYMENT_METHOD_MOBILE_BEELINE     = 3007;\n\nconst PAYMENT_METHOD_TERMINAL            = 4000;\nconst PAYMENT_METHOD_TERMINAL_QIWI       = 4001;\nconst PAYMENT_METHOD_TERMINAL_ELECSNET   = 4002;\nconst PAYMENT_METHOD_TERMINAL_ELEMENT    = 4003;\nconst PAYMENT_METHOD_TERMINAL_KASSIRANET = 4004;\nconst PAYMENT_METHOD_TERMINAL_TELEPAY    = 4005;\nconst PAYMENT_METHOD_TERMINAL_IBOX       = 4006;\nconst PAYMENT_METHOD_TERMINAL_UKRAINE    = 4007;\nconst PAYMENT_METHOD_TERMINAL_RUSSIA     = 4008;\nconst PAYMENT_METHOD_TERMINAL_EASYPAY    = 4009;\n\nconst PAYMENT_METHOD_BANK_INTERNET                  = 5000;\nconst PAYMENT_METHOD_BANK_INTERNET_ALFA_BANK        = 5001;\nconst PAYMENT_METHOD_BANK_INTERNET_RUSSKIY_STANDART = 5002;\nconst PAYMENT_METHOD_BANK_INTERNET_PROSMVYAZBANK    = 5003;\nconst PAYMENT_METHOD_BANK_INTERNET_VTB24            = 5004;\nconst PAYMENT_METHOD_BANK_INTERNET_OCEAN_BANK       = 5005;\nconst PAYMENT_METHOD_BANK_INTERNET_HANDY_BANK       = 5006;\nconst PAYMENT_METHOD_BANK_INTERNET_007              = 5007;\nconst PAYMENT_METHOD_BANK_INTERNET_008              = 5008;\nconst PAYMENT_METHOD_BANK_INTERNET_009              = 5009;\nconst PAYMENT_METHOD_BANK_INTERNET_010              = 5010;\nconst PAYMENT_METHOD_BANK_INTERNET_011              = 5011;\nconst PAYMENT_METHOD_BANK_INTERNET_012              = 5012;\nconst PAYMENT_METHOD_BANK_INTERNET_013              = 5013;\nconst PAYMENT_METHOD_BANK_INTERNET_014              = 5014;\nconst PAYMENT_METHOD_BANK_INTERNET_015              = 5015;\nconst PAYMENT_METHOD_BANK_INTERNET_016              = 5016;\nconst PAYMENT_METHOD_BANK_INTERNET_017              = 5017;\nconst PAYMENT_METHOD_BANK_INTERNET_018              = 5018;\nconst PAYMENT_METHOD_BANK_INTERNET_019              = 5019;\nconst PAYMENT_METHOD_BANK_INTERNET_020              = 5020;\nconst PAYMENT_METHOD_BANK_INTERNET_021              = 5021;\nconst PAYMENT_METHOD_BANK_INTERNET_BANK24           = 5022;\nconst PAYMENT_METHOD_BANK_INTERNET_PRIVAT24         = 5023;\nconst PAYMENT_METHOD_BANK_INTERNET_SBERBANK         = 5024;\n\nconst PAYMENT_METHOD_BANK_TRANSFER = 6000;\n\nconst PAYMENT_METHOD_OTHER                  = 9000;\nconst PAYMENT_METHOD_OTHER_EVROSET          = 9001;\nconst PAYMENT_METHOD_OTHER_SVYAZNOY         = 9002;\nconst PAYMENT_METHOD_OTHER_ROBOKASSA_MOBILE = 9003;\n\nconst PAYMENT_METHOD_GENERIC            = 10000;\nconst PAYMENT_METHOD_GENERIC_XSOLLA     = 10001;\nconst PAYMENT_METHOD_GENERIC_ROBOKASSA  = 10002;\nconst PAYMENT_METHOD_GENERIC_INTERKASSA = 10003;\nconst PAYMENT_METHOD_GENERIC_UNITPAY    = 10004;\nconst PAYMENT_METHOD_GENERIC_PAYU       = 10005;\n"
  },
  {
    "path": "includes/constants/constants_rpg.php",
    "content": "<?php\n/**\n * Created by Gorlum 27.03.2018 10:45\n */\n\n// *** Constants for changing DM\ndefine('RPG_NONE', 0);\ndefine('RPG_STRUCTURE', 1);\ndefine('RPG_RAID', 2);\ndefine('RPG_TECH', 3);\ndefine('RPG_ADMIN', 4);\ndefine('RPG_BUY', 5);\ndefine('RPG_MARKET', 6);\ndefine('RPG_MERCENARY', 7);\ndefine('RPG_QUEST', 8);\ndefine('RPG_EXPEDITION', 9);\ndefine('RPG_REFERRAL', 10);\ndefine('RPG_ARTIFACT', 11);\ndefine('RPG_RENAME', 12);\ndefine('RPG_ALLY', 13);\ndefine('RPG_BIRTHDAY', 14);\ndefine('RPG_PURCHASE', 15);\ndefine('RPG_PLANS', 16);\ndefine('RPG_PREMIUM', 17);\ndefine('RPG_SECTOR', 18);\ndefine('RPG_TELEPORT', 19);\ndefine('RPG_CAPITAL', 20);\ndefine('RPG_RACE', 21);\ndefine('RPG_CAPTAIN', 22);\ndefine('RPG_NAME_CHANGE', 23);\ndefine('RPG_PLANET_DENSITY_CHANGE', 24);\ndefine('RPG_CONVERT_MM', 25);\ndefine('RPG_EXPLORE', 26);\ndefine('RPG_PURCHASE_CANCEL', 27);\ndefine('RPG_MERCENARY_DISMISSED', 28);\ndefine('RPG_EVENT_CHRISTMAS', 29);\ndefine('RPG_EVENT_CHRISTMAS_TREE_BURN', 30);\ndefine('RPG_EVENT_BLACK_MOON_TICK', 31);\ndefine('RPG_CUMULATIVE', 32);\ndefine('RPG_GOVERNOR', 33);\ndefine('RPG_BLITZ', 34);\ndefine('RPG_MARKET_EXCHANGE', 35);\ndefine('RPG_MARKET_FLEET', 36);\ndefine('RPG_MARKET_INFO_MERCENARY', 37);\ndefine('RPG_BLITZ_REGISTRATION', 38);\ndefine('RPG_BLITZ_REGISTRATION_CANCEL', 39);\ndefine('RPG_BUILD_AUTOCONVERT', 40);\ndefine('RPG_EVENT_SUPERBORN', 41);\ndefine('RPG_REFERRAL_BOUGHT_MM', 42);\ndefine('RPG_HALLOWEEN', 43);\ndefine('RPG_CHRISTMAS', 44);\ndefine('RPG_EVENT_BIRTHDAY', 45);\ndefine('RPG_EVENT_BIRTHDAY_COMPILED', 46);\ndefine('RPG_BATCH_OPERATION', 47);\ndefine('RPG_EVENT_MARCH8', 48);\ndefine('RPG_LOGIN_TOKEN', 49);\ndefine('RPG_EVENT_MARCH8_REWARD', 50);\ndefine('RPG_PROMO_CODE', 51);\ndefine('RPG_EVENT_MM_RACE_REWARD', 52);\ndefine('RPG_STORY_REWARD', 53);\n\n// Next 54\n"
  },
  {
    "path": "includes/constants/constants_ube.php",
    "content": "<?php\n/**\n * Created by Gorlum 27.03.2018 10:39\n */\n\ndefine('UBE_COMBAT_RESULT_DRAW_END', -1);\ndefine('UBE_COMBAT_RESULT_DRAW', 0);\ndefine('UBE_COMBAT_RESULT_WIN', 1);\ndefine('UBE_COMBAT_RESULT_LOSS', 2);\n\ndefine('UBE_MOON_WAS', -1);\ndefine('UBE_MOON_NONE', 0);\ndefine('UBE_MOON_CREATE_SUCCESS', 1);\ndefine('UBE_MOON_CREATE_FAILED', 2);\ndefine('UBE_MOON_DESTROY_SUCCESS', 3);\ndefine('UBE_MOON_DESTROY_FAILED', 4);\ndefine('UBE_MOON_REAPERS_NONE', 5);\ndefine('UBE_MOON_REAPERS_DIED', 6);\ndefine('UBE_MOON_REAPERS_RETURNED', 7);\n\ndefine('UBE_REPORT_NOT_FOUND', 'UBE_REPORT_NOT_FOUND');\n\ndefine('UBE_REPORT_CYPHER', 'UBE_REPORT_CYPHER');\ndefine('UBE_REPORT_ID', 'UBE_REPORT_ID');\ndefine('UBE_OBJ_PREPARATOR', 'UBE_OBJ_PREPARATOR');\ndefine('UBE_OBJ_CALCULATOR', 'UBE_OBJ_CALCULATOR');\ndefine('UBE_TIME', 'UBE_TIME');\ndefine('UBE_TIME_SPENT', 'UBE_TIME_SPENT');\n\ndefine('UBE_OPTIONS', 'UBE_OPTIONS');\ndefine('UBE_COMBAT_ADMIN', 'UBE_COMBAT_ADMIN');\ndefine('UBE_DEFENDER_ACTIVE', 'UBE_DEFENDER_ACTIVE');\ndefine('UBE_MISSION_TYPE', 'UBE_MISSION_TYPE');\ndefine('UBE_LOADED', 'UBE_LOADED');\ndefine('UBE_SIMULATOR', 'UBE_SIMULATOR');\ndefine('UBE_SIMULATOR_STATIC', 'UBE_SIMULATOR_STATIC');\ndefine('UBE_EXCHANGE', 'UBE_EXCHANGE');\ndefine('UBE_METHOD', 'UBE_METHOD');\n\n\ndefine('UBE_OUTCOME', 'UBE_OUTCOME');\ndefine('UBE_COMBAT_RESULT', 'UBE_COMBAT_RESULT');\ndefine('UBE_SFR', 'UBE_SFR');\ndefine('UBE_DEBRIS', 'UBE_DEBRIS');\ndefine('UBE_DEBRIS_ORIGINAL', 'UBE_DEBRIS_ORIGINAL');\ndefine('UBE_DEBRIS_TOTAL', 'UBE_DEBRIS_TOTAL');\n\ndefine('UBE_CAPTURE_RESULT', 'UBE_CAPTURE_RESULT');\ndefine('UBE_CAPTURE_DISABLED', 0);\ndefine('UBE_CAPTURE_NON_PLANET', 1);\ndefine('UBE_CAPTURE_NOT_A_WIN_IN_1_ROUND', 2);\ndefine('UBE_CAPTURE_TOO_MUCH_FLEETS', 3);\ndefine('UBE_CAPTURE_NO_ATTACKER_USER_ID', 4);\ndefine('UBE_CAPTURE_NO_DEFENDER_USER_ID', 5);\ndefine('UBE_CAPTURE_CAPITAL', 6);\ndefine('UBE_CAPTURE_TOO_LOW_POINTS', 7);\ndefine('UBE_CAPTURE_NOT_ENOUGH_SLOTS', 8);\n\ndefine('UBE_CAPTURE_SUCCESSFUL', 100);\n\ndefine('UBE_PLANET', 'UBE_PLANET');\ndefine('PLANET_ID', 'PLANET_ID');\ndefine('PLANET_NAME', 'PLANET_NAME');\ndefine('PLANET_SIZE', 'PLANET_SIZE');\ndefine('PLANET_GALAXY', 'PLANET_GALAXY');\ndefine('PLANET_SYSTEM', 'PLANET_SYSTEM');\ndefine('PLANET_PLANET', 'PLANET_PLANET');\ndefine('PLANET_TYPE', 'PLANET_TYPE');\n\ndefine('UBE_MOON', 'UBE_MOON');\ndefine('UBE_MOON_NAME', 'UBE_MOON_NAME');\ndefine('UBE_MOON_CHANCE', 'UBE_MOON_CHANCE');\ndefine('UBE_MOON_SIZE', 'UBE_MOON_SIZE');\ndefine('UBE_MOON_REAPERS', 'UBE_MOON_REAPERS');\ndefine('UBE_MOON_DESTROY_CHANCE', 'UBE_MOON_DESTROY_CHANCE');\ndefine('UBE_MOON_REAPERS_DIE_CHANCE', 'UBE_MOON_REAPERS_DIE_CHANCE');\n\n\ndefine('UBE_PLAYERS', 'UBE_PLAYERS');\ndefine('UBE_NAME', 'UBE_NAME');\ndefine('UBE_ATTACKER', 'UBE_ATTACKER');\ndefine('UBE_AUTH_LEVEL', 'UBE_AUTH_LEVEL');\ndefine('UBE_PLAYER_DATA', 'UBE_PLAYER_DATA');\n\n\ndefine('UBE_BONUSES', 'UBE_BONUSES');\ndefine('UBE_ATTACK', 'UBE_ATTACK');\ndefine('UBE_SHIELD', 'UBE_SHIELD');\ndefine('UBE_ARMOR', 'UBE_ARMOR');\ndefine('UBE_SHIELD_REST', 'UBE_SHIELD_REST');\ndefine('UBE_ARMOR_REST', 'UBE_ARMOR_REST');\n\n\ndefine('UBE_FLEETS', 'UBE_FLEETS');\ndefine('UBE_OWNER', 'UBE_OWNER');\ndefine('UBE_FLEET_GROUP', 'UBE_FLEET_GROUP');\ndefine('UBE_PRICE', 'UBE_PRICE');\ndefine('UBE_AMPLIFY', 'UBE_AMPLIFY');\ndefine('UBE_CAPACITY', 'UBE_CAPACITY');\ndefine('UBE_TYPE', 'UBE_TYPE');\ndefine('UBE_RESOURCES', 'UBE_RESOURCES');\ndefine('UBE_RESOURCES_LOST', 'UBE_RESOURCES_LOST');\ndefine('UBE_CARGO_DROPPED', 'UBE_CARGO_DROPPED');\ndefine('UBE_RESOURCES_LOOTED', 'UBE_RESOURCES_LOOTED');\ndefine('UBE_RESOURCES_LOST_IN_METAL', 'UBE_RESOURCES_LOST_IN_METAL');\ndefine('UBE_FLEET_TYPE', 'UBE_FLEET_TYPE');\n\n\ndefine('UBE_COUNT', 'UBE_COUNT');\ndefine('UBE_UNITS_LOST', 'UBE_UNITS_LOST');\ndefine('UBE_DEFENCE_RESTORE', 'UBE_DEFENCE_RESTORE');\n\n\ndefine('UBE_ROUNDS', 'UBE_ROUNDS');\ndefine('UBE_FLEET_INFO', 'UBE_FLEET_INFO');\ndefine('UBE_UNITS_BOOM', 'UBE_UNITS_BOOM');\ndefine('UBE_ATTACK_BASE', 'UBE_ATTACK_BASE');\ndefine('UBE_SHIELD_BASE', 'UBE_SHIELD_BASE');\ndefine('UBE_ARMOR_BASE', 'UBE_ARMOR_BASE');\ndefine('UBE_ATTACKERS', 'UBE_ATTACKERS');\ndefine('UBE_DEFENDERS', 'UBE_DEFENDERS');\ndefine('UBE_TOTAL', 'UBE_TOTAL');\ndefine('UBE_DAMAGE_PERCENT', 'UBE_DAMAGE_PERCENT');\n\ndefine('UBE_CAPTAIN', 'UBE_CAPTAIN');\n"
  },
  {
    "path": "includes/constants/constants_units.php",
    "content": "<?php\n/**\n * Created by Gorlum 27.03.2018 10:48\n */\n\n// === UNITS\ndefine('GROUP_GROUP_ID_TO_NAMES', 'group_id_to_names');\ndefine('GROUP_CAPITAL_BUILDING_BONUS_GROUPS', 'GROUP_CAPITAL_BUILDING_BONUS_GROUPS');\n\ndefine('UNIT_ANY', 0);\n\n// === Structures\ndefine('UNIT_STRUCTURES_STR', 'structures');\ndefine('UNIT_STRUCTURES', 99);\ndefine('UNIT_STRUCTURES_PLANET', 98);\ndefine('UNIT_STRUCTURES_ALL', 96);\ndefine('STRUC_MINE_METAL', 1);\ndefine('STRUC_MINE_CRYSTAL', 2);\ndefine('STRUC_MINE_DEUTERIUM', 3);\ndefine('STRUC_MINE_SOLAR', 4);\ndefine('STRUC_MINE_FUSION', 12);\ndefine('STRUC_FACTORY_ROBOT', 14);\ndefine('STRUC_FACTORY_NANO', 15);\ndefine('STRUC_FACTORY_HANGAR', 21);\ndefine('STRUC_STORE_METAL', 22);\ndefine('STRUC_STORE_CRYSTAL', 23);\ndefine('STRUC_STORE_DEUTERIUM', 24);\ndefine('STRUC_LABORATORY', 31);\ndefine('STRUC_LABORATORY_NANO', 35);\ndefine('STRUC_TERRAFORMER', 33);\ndefine('STRUC_ALLY_DEPOSIT', 34);\ndefine('STRUC_SILO', 44);\n\ndefine('UNIT_STRUCTURES_SPECIAL', 40);\ndefine('STRUC_MOON_STATION', 41);\ndefine('STRUC_MOON_PHALANX', 42);\ndefine('STRUC_MOON_GATE', 43);\n\n// === Techs\ndefine('UNIT_TECHNOLOGIES', 100); // 101-105\ndefine('TECH_SPY', 106); // 107\ndefine('TECH_COMPUTER', 108);\ndefine('TECH_WEAPON', 109);\ndefine('TECH_SHIELD', 110);\ndefine('TECH_ARMOR', 111); // 112\ndefine('TECH_ENERGY', 113);\ndefine('TECH_HYPERSPACE', 114);\ndefine('TECH_ENGINE_CHEMICAL', 115); // 116\ndefine('TECH_ENGINE_ION', 117);\ndefine('TECH_ENGINE_HYPER', 118);\ndefine('TECH_ENGINE_NUCLEAR', 119);\ndefine('TECH_LASER', 120);\ndefine('TECH_ION', 121);\ndefine('TECH_PLASMA', 122);\ndefine('TECH_RESEARCH', 123);\ndefine('TECH_EXPEDITION', 124);\ndefine('TECH_NUCLEAR', 125); // 126-149\ndefine('TECH_COLONIZATION', 150);\ndefine('TECH_ASTROTECH', 151); // 152-198\ndefine('TECH_GRAVITON', 199);\n\n// === Hangar units\n// --- Ships\ndefine('UNIT_SHIPS_STR', 'fleet');\ndefine('UNIT_SHIPS', 200);\n\ndefine('SHIP_CARGO_SMALL', 202);\ndefine('SHIP_CARGO_BIG', 203);\ndefine('SHIP_CARGO_SUPER', 201);\ndefine('SHIP_CARGO_HYPER', 218);\ndefine('SHIP_COLONIZER', 208);\ndefine('SHIP_RECYCLER', 209);\ndefine('SHIP_RECYCLER_GLUTTONY', 223);\ndefine('SHIP_SPY', 210);\ndefine('SHIP_SATTELITE_SOLAR', 212);\ndefine('SHIP_CARGO_GREED', 220);\ndefine('SHIP_SATTELITE_SLOTH', 221);\n\n\ndefine('SHIP_SMALL_FIGHTER_LIGHT', 204);\ndefine('SHIP_SMALL_FIGHTER_WRATH', 219);\ndefine('SHIP_SMALL_FIGHTER_HEAVY', 205);\ndefine('SHIP_SMALL_FIGHTER_ASSAULT', 217);\ndefine('SHIP_MEDIUM_DESTROYER', 206);\ndefine('SHIP_MEDIUM_FRIGATE', 226); // Not used\ndefine('SHIP_MEDIUM_BOMBER_ENVY', 224);\ndefine('SHIP_LARGE_CRUISER', 207);\ndefine('SHIP_LARGE_BOMBER', 211);\ndefine('SHIP_LARGE_BATTLESHIP', 215);\ndefine('SHIP_LARGE_BATTLESHIP_PRIDE', 222);\ndefine('SHIP_LARGE_ORBITAL_HEAVY', 225);\ndefine('SHIP_LARGE_DESTRUCTOR', 213);\ndefine('SHIP_HUGE_DEATH_STAR', 214);\ndefine('SHIP_HUGE_SUPERNOVA', 216);\n\nconst SHIP_SATELLITE_SPUTNIK = 228; // Anti-spy satellite THAT CAN FLY!!!\nconst SHIP_CARGO_FIREFLY = 229; // Fastest medium transport\nconst SHIP_RECYCLER_BURAN = 230; // Fastest recycler\nconst SHIP_SMALL_FIGHTER_SOYUZ = 231; // SHIP_SMALL_FIGHTER_SOYUZ ? с гауссовкой (!)\nconst SHIP_MEDIUM_TORPEDO_SPIRAL = 232; // Торпедоносец против больших и сверхбольших кораблей\n\nconst SHIP_NEXT = 233;\n\n// --- Defense\ndefine('UNIT_DEFENCE_STR', 'defense');\ndefine('UNIT_DEFENCE', 400);\ndefine('UNIT_DEF_TURRET_MISSILE', 401);\ndefine('UNIT_DEF_TURRET_LASER_SMALL', 402);\ndefine('UNIT_DEF_TURRET_LASER_BIG', 403);\ndefine('UNIT_DEF_TURRET_GAUSS', 404);\ndefine('UNIT_DEF_TURRET_ION', 405);\ndefine('UNIT_DEF_TURRET_PLASMA', 406);\ndefine('UNIT_DEF_SHIELD_SMALL', 407);\ndefine('UNIT_DEF_SHIELD_BIG', 408);\ndefine('UNIT_DEF_SHIELD_PLANET', 409);\n\n// --- Missiles\ndefine('UNIT_DEF_MISSILES_STR', 'missile');\ndefine('UNIT_DEF_MISSILES', 500);\ndefine('UNIT_DEF_MISSILE_INTERCEPTOR', 502);\ndefine('UNIT_DEF_MISSILE_INTERPLANET', 503);\n\n// === Mercenaries\n// --- Mercenary list\ndefine('UNIT_MERCENARIES', 600);\ndefine('MRC_ACADEMIC', 606);\ndefine('MRC_ADMIRAL', 602);\ndefine('MRC_STOCKMAN', 607); // OK MODIFIER\ndefine('MRC_SPY', 610);\ndefine('MRC_COORDINATOR', 611);\ndefine('MRC_DESTRUCTOR', 612);\ndefine('MRC_NAVIGATOR', 613);\ndefine('MRC_ASSASIN', 614);\ndefine('MRC_EMPEROR', 615);\n\n// --- Governors list\ndefine('UNIT_GOVERNORS', 680);\ndefine('MRC_TECHNOLOGIST', 601); // OK MODIFIER\ndefine('MRC_ENGINEER', 605);\ndefine('MRC_FORTIFIER', 608);\n\ndefine('UNIT_GOVERNOR_PRIMARY', 695);\ndefine('UNIT_GOVERNOR_SECONDARY', 696);\n\n// Bonus category\ndefine('BONUS_SERVER', 0);\ndefine('BONUS_MERCENARY', UNIT_MERCENARIES); // DO NOT MOVE ABOVE MERCENARIES SECTION!\n\n// === Resources\ndefine('UNIT_RESOURCES_STR', 'resources');\ndefine('UNIT_RESOURCES_STR_LOOT', 'resources_loot');\ndefine('UNIT_RESOURCES_STR_TRADER', 'resources_trader');\ndefine('UNIT_RESOURCES', 900);\ndefine('RES_METAL', 901);\ndefine('RES_CRYSTAL', 902);\ndefine('RES_DEUTERIUM', 903);\ndefine('RES_ENERGY', 904);\ndefine('RES_DARK_MATTER', 905);\ndefine('RES_METAMATTER', 950);\ndefine('RES_TIME', 999);\n\n// === Artifacts\ndefine('UNIT_ARTIFACTS_STR', 'artifacts');\ndefine('UNIT_ARTIFACTS', 1000);\ndefine('ART_LHC', 1001);      // Additional moon chance\ndefine('ART_RCD_SMALL', 1002);   // Rapid Colony Deployment - Set of buildings up to 10th level - 10/14/ 3/0 -   405 DM\ndefine('ART_RCD_MEDIUM', 1003);  // Rapid Colony Deployment - Set of buildings up to 15th level - 15/20/ 8/0 -  4704 DM\ndefine('ART_RCD_LARGE', 1004);   // Rapid Colony Deployment - Set of buildings up to 20th level - 20/25/10/1 - 39790 DM\ndefine('ART_HEURISTIC_CHIP', 1005); // Speed up research\ndefine('ART_NANO_BUILDER', 1006); // Speed up building\ndefine('ART_NANO_CONSTRUCTOR', 1007); // RESERVED Speed up hangar constructions\ndefine('ART_HOOK_SMALL', 1008);\ndefine('ART_HOOK_MEDIUM', 1009);\ndefine('ART_HOOK_LARGE', 1010);\ndefine('ART_DENSITY_CHANGER', 1011); // RESERVED\n// 1012 RESERVED\ndefine('ART_PLANET_GATE', 1013); // RESERVED Planet gate\n\n// === Blueprints\ndefine('UNIT_PLANS', 1100);\ndefine('UNIT_PLAN_STRUC_MINE_FUSION', 1101);\ndefine('UNIT_PLAN_SHIP_CARGO_SUPER', 1102);\ndefine('UNIT_PLAN_SHIP_CARGO_HYPER', 1103);\ndefine('UNIT_PLAN_SHIP_DEATH_STAR', 1104);\ndefine('UNIT_PLAN_SHIP_SUPERNOVA', 1105);\ndefine('UNIT_PLAN_DEF_SHIELD_PLANET', 1106);\ndefine('UNIT_PLAN_SHIP_ORBITAL_HEAVY', 1107);\n\ndefine('UNIT_PREMIUM', 1200);\ndefine('UNIT_LOGIN_TOKEN', 1210);\ndefine('UNIT_MASS_OPERATIONS', 1290);\ndefine('UNIT_MASS_OPERATIONS_TEST_DRIVE', 1291);\ndefine('UNIT_SECTOR', 1300);\ndefine('UNIT_RACE', 1400);\ndefine('UNIT_CAPTAIN', 1500);\n\ndefine('UNIT_PLANET_DENSITY', 1601);\ndefine('UNIT_PLANET_DENSITY_INDEX', 1602);\ndefine('UNIT_PLANET_DENSITY_RARITY', 1603);\ndefine('UNIT_PLANET_DENSITY_RICHNESS', 1604);\ndefine('UNIT_PLANET_DENSITY_MAX_SECTORS', 1605);\ndefine('UNIT_PLANET_DENSITY_PROBABILITY', 1606);\ndefine('UNIT_PLANET_DENSITY_MIN_ASTROTECH', 1607);\n\ndefine('PLANET_DENSITY_NONE', 0);\ndefine('PLANET_DENSITY_ICE_HYDROGEN', 8); // New\ndefine('PLANET_DENSITY_ICE_METHANE', 1); // Old ICE\ndefine('PLANET_DENSITY_ICE_WATER', 9); // New\ndefine('PLANET_DENSITY_CRYSTAL_RAW', 10); // New\ndefine('PLANET_DENSITY_CRYSTAL_SILICATE', 2);\ndefine('PLANET_DENSITY_CRYSTAL_STONE', 3);\ndefine('PLANET_DENSITY_STANDARD', 4);\ndefine('PLANET_DENSITY_METAL_ORE', 5);\ndefine('PLANET_DENSITY_METAL_PERIDOT', 6);\n// define('PLANET_DENSITY_METAL_HEAVY', 7); // deprecated\ndefine('PLANET_DENSITY_METAL_RAW', 11); // New\n// MAXIMUM PLANET_DENSITY_METAL_RAW 11\n\ndefine('PLANET_DENSITY_RICHNESS_NORMAL', 0);\ndefine('PLANET_DENSITY_RICHNESS_AVERAGE', 1);\ndefine('PLANET_DENSITY_RICHNESS_GOOD', 2);\ndefine('PLANET_DENSITY_RICHNESS_PERFECT', 3);\n\n\n// Награды игрока 2.000-2.999\ndefine('UNIT_AWARD', 2000); // Награды игрока 2.000-2.999\ndefine('UNIT_AWARD_ORDER', 2100); // Ордена за Выдающиеся Достижения - например, за спонсорство\ndefine('UNIT_AWARD_ORDER_SPONSOR_BRONZE', 2101);\ndefine('UNIT_AWARD_ORDER_SPONSOR_SILVER', 2102);\ndefine('UNIT_AWARD_ORDER_SPONSOR_GOLD', 2103);\ndefine('UNIT_AWARD_ORDER_SPONSOR_PLATINUM', 2104);\ndefine('UNIT_AWARD_ORDER_SPONSOR_DIAMOND', 2105);\ndefine('UNIT_AWARD_ORDER_SPONSOR_DARK', 2106);\ndefine('UNIT_AWARD_ORDER_SPONSOR_META', 2107);\ndefine('UNIT_AWARD_ORDER_SPONSOR', 2109);\n// 2110 - следующая группа орденов\n\ndefine('UNIT_AWARD_MEDAL', 2200); // Медали за Серъезные Достижения - например, за победу в конкурсе\ndefine('UNIT_AWARD_MEDAL_BLITZ_R0_PLACE1', 2201); // Блиц-сервер, участник 0-го раунда, 1-е место\ndefine('UNIT_AWARD_MEDAL_BLITZ_R0_PLACE2', 2202); // Блиц-сервер, участник 0-го раунда, 2-е место\ndefine('UNIT_AWARD_MEDAL_BLITZ_R0_PLACE3', 2203); // Блиц-сервер, участник 0-го раунда, 3-е место\ndefine('UNIT_AWARD_MEDAL_2016_WOMEN_DAY_BEST', 2204);  // Медаль Лучшему Кавалеру за максимум потраченной ММ/максимум одаренных женщин Женщине от Мужчины во время ивента 8 марта 2016 года\ndefine('UNIT_AWARD_MEDAL_2017_WOMEN_DAY_BEST', 2205);  // Медаль Лучшему Кавалеру за максимум потраченной ММ/максимум одаренных женщин Женщине от Мужчины во время ивента 8 марта 2017 года\ndefine('UNIT_AWARD_MEDAL_2017_WOMEN_DAY_QUEEN', 2206);  // Медаль Королевы Весны за максимум полученной ММ/максимум полученных подарков от Мужчины во время ивента 8 марта 2017 года\ndefine('UNIT_AWARD_MEDAL_8_MARCH_BEST_CAVALIER_AMOUNT', 2207); // Медаль Лучшему Кавалеру за максимум потраченной ММ Женщине от Мужчины во время ивента 8 марта 2017 года\ndefine('UNIT_AWARD_MEDAL_8_MARCH_BEST_CAVALIER_COUNT', 2208); // Медаль Лучшему Кавалеру за максимум одаренных женщин Женщине от Мужчины во время ивента 8 марта 2017 года\ndefine('UNIT_AWARD_MEDAL_8_MARCH_SPRING_QUEEN_AMOUNT', 2209); // Медаль Королевы Весны за максимум полученной ММ от Мужчины во время ивента 8 марта 2017 года\ndefine('UNIT_AWARD_MEDAL_8_MARCH_SPRING_QUEEN_COUNT', 2210); // Медаль Королевы Весны за максимум полученных подарков от Мужчины во время ивента 8 марта 2017 года\n\ndefine('UNIT_AWARD_MEMORY', 2300); // Памятные знаки за существование и участие - например \"4 года в игре\". \"Был онлайн в новогоднюю ночь 2013\". итд\ndefine('UNIT_AWARD_MEMORY_IMMORTAL', 2301);  // Бессмертный\ndefine('UNIT_AWARD_MEMORY_2015_WOMEN_DAY', 2302);  // Значек за подарок Женщине от Мужчины во время ивента 8 марта 2015 года\ndefine('UNIT_AWARD_MEMORY_BLITZ_R0', 2303); // Блиц-сервер, участник 0-го раунда\ndefine('UNIT_AWARD_MEMORY_SUPER_BORN_2015_SIMPLE', 2304); // День Рождения СН\ndefine('UNIT_AWARD_MEMORY_SUPER_BORN_2015_BRONZE', 2305); // День Рождения СН\ndefine('UNIT_AWARD_MEMORY_SUPER_BORN_2015_SILVER', 2306); // День Рождения СН\ndefine('UNIT_AWARD_MEMORY_SUPER_BORN_2015_GOLD', 2307); // День Рождения СН\ndefine('UNIT_AWARD_MEMORY_SUPER_BORN_2015_PLATINUM', 2308); // День Рождения СН\ndefine('UNIT_AWARD_MEMORY_2016_WOMEN_DAY', 2309);  // Значек за подарок Женщине от Мужчины во время ивента 8 марта 2016 года\ndefine('UNIT_AWARD_MEMORY_2017_WOMEN_DAY', 2310);  // Значек за подарок Женщине от Мужчины во время ивента 8 марта 2017 года\ndefine('UNIT_AWARD_MEMORY_SUPER_BORN_2017_SIMPLE', 2311); // День Рождения СН - 2017\ndefine('UNIT_AWARD_MEMORY_SUPER_BORN_2017_BRONZE', 2312); // День Рождения СН - 2017\ndefine('UNIT_AWARD_MEMORY_SUPER_BORN_2017_SILVER', 2313); // День Рождения СН - 2017\ndefine('UNIT_AWARD_MEMORY_SUPER_BORN_2017_GOLD', 2314); // День Рождения СН - 2017\ndefine('UNIT_AWARD_MEMORY_SUPER_BORN_2017_PLATINUM', 2315); // День Рождения СН - 2017\ndefine('UNIT_AWARD_MEMORY_SUPER_BORN_2017_DIAMOND', 2316); // День Рождения СН - 2017\n\ndefine('UNIT_AWARD_MEMORY_SUPER_BORN_2016_SIMPLE', 2317); // День Рождения СН - 2016\ndefine('UNIT_AWARD_MEMORY_SUPER_BORN_2016_BRONZE', 2318); // День Рождения СН - 2016\ndefine('UNIT_AWARD_MEMORY_SUPER_BORN_2016_SILVER', 2319); // День Рождения СН - 2016\ndefine('UNIT_AWARD_MEMORY_SUPER_BORN_2016_GOLD', 2320); // День Рождения СН - 2016\ndefine('UNIT_AWARD_MEMORY_SUPER_BORN_2016_PLATINUM', 2321); // День Рождения СН - 2016\ndefine('UNIT_AWARD_MEMORY_SUPER_BORN_2016_DIAMOND', 2322); // День Рождения СН - 2016\n\ndefine('UNIT_AWARD_MEMORY_SUPER_BORN_2017_BEST_3RD', 2323); // Лучший Гость - День Рождения СН - 2016\ndefine('UNIT_AWARD_MEMORY_SUPER_BORN_2017_BEST_2ND', 2324); // Лучший Гость - День Рождения СН - 2016\ndefine('UNIT_AWARD_MEMORY_SUPER_BORN_2017_BEST_1ST', 2325); // Лучший Гость - День Рождения СН - 2016\n\nconst UNIT_AWARD_MEMORY_NEW_YEAR_2018_SIMPLE   = 2326; // СуперНовый Год - 2018\nconst UNIT_AWARD_MEMORY_NEW_YEAR_2018_BRONZE   = 2327; // СуперНовый Год - 2018\nconst UNIT_AWARD_MEMORY_NEW_YEAR_2018_SILVER   = 2328; // СуперНовый Год - 2018\nconst UNIT_AWARD_MEMORY_NEW_YEAR_2018_GOLD     = 2329; // СуперНовый Год - 2018\nconst UNIT_AWARD_MEMORY_NEW_YEAR_2018_PLATINUM = 2330; // СуперНовый Год - 2018\nconst UNIT_AWARD_MEMORY_NEW_YEAR_2018_DIAMOND  = 2331; // СуперНовый Год - 2018\nconst UNIT_AWARD_MEMORY_NEW_YEAR_2018_BEST_1ST = 2332; // Лучший Гость - СуперНовый Год - 2018\nconst UNIT_AWARD_MEMORY_NEW_YEAR_2018_BEST_2ND = 2333; // Лучший Гость - СуперНовый Год - 2018\nconst UNIT_AWARD_MEMORY_NEW_YEAR_2018_BEST_3RD = 2334; // Лучший Гость - СуперНовый Год - 2018\n\nconst UNIT_AWARD_MEMORY_8_MARCH_MAN_MEMORIAL = 2335; // 2310 old  // Значек за подарок Женщине от Мужчины во время ивента 8 марта - универсальный\n\nconst UNIT_AWARD_MEMORY_SUPER_BORN_2018_BRONZE   = 2336; // День Рождения СН - 2018\nconst UNIT_AWARD_MEMORY_SUPER_BORN_2018_SILVER   = 2337; // День Рождения СН - 2018\nconst UNIT_AWARD_MEMORY_SUPER_BORN_2018_SIMPLE   = 2338; // День Рождения СН - 2018\nconst UNIT_AWARD_MEMORY_SUPER_BORN_2018_GOLD     = 2339; // День Рождения СН - 2018\nconst UNIT_AWARD_MEMORY_SUPER_BORN_2018_PLATINUM = 2340; // День Рождения СН - 2018\nconst UNIT_AWARD_MEMORY_SUPER_BORN_2018_DIAMOND  = 2341; // День Рождения СН - 2018\nconst UNIT_AWARD_MEMORY_SUPER_BORN_2018_BEST_1ST = 2342; // Лучший Гость - День Рождения СН - 2018\nconst UNIT_AWARD_MEMORY_SUPER_BORN_2018_BEST_2ND = 2343; // Лучший Гость - День Рождения СН - 2018\nconst UNIT_AWARD_MEMORY_SUPER_BORN_2018_BEST_3RD = 2344; // Лучший Гость - День Рождения СН - 2018\n\nconst UNIT_AWARD_MEMORY_NEW_YEAR_2019_SIMPLE   = 2345; // СуперНовый Год - 2019\nconst UNIT_AWARD_MEMORY_NEW_YEAR_2019_BRONZE   = 2346; // СуперНовый Год - 2019\nconst UNIT_AWARD_MEMORY_NEW_YEAR_2019_SILVER   = 2347; // СуперНовый Год - 2019\nconst UNIT_AWARD_MEMORY_NEW_YEAR_2019_GOLD     = 2348; // СуперНовый Год - 2019\nconst UNIT_AWARD_MEMORY_NEW_YEAR_2019_PLATINUM = 2349; // СуперНовый Год - 2019\nconst UNIT_AWARD_MEMORY_NEW_YEAR_2019_DIAMOND  = 2350; // СуперНовый Год - 2019\nconst UNIT_AWARD_MEMORY_NEW_YEAR_2019_BEST_1ST = 2351; // Лучший Гость - СуперНовый Год - 2019\nconst UNIT_AWARD_MEMORY_NEW_YEAR_2019_BEST_2ND = 2352; // Лучший Гость - СуперНовый Год - 2019\nconst UNIT_AWARD_MEMORY_NEW_YEAR_2019_BEST_3RD = 2353; // Лучший Гость - СуперНовый Год - 2019\n\nconst UNIT_AWARD_MEMORY_SUPER_BORN_2019_SIMPLE   = 2354; // День Рождения СН - 2019\nconst UNIT_AWARD_MEMORY_SUPER_BORN_2019_BRONZE   = 2355; // День Рождения СН - 2019\nconst UNIT_AWARD_MEMORY_SUPER_BORN_2019_SILVER   = 2356; // День Рождения СН - 2019\nconst UNIT_AWARD_MEMORY_SUPER_BORN_2019_GOLD     = 2357; // День Рождения СН - 2019\nconst UNIT_AWARD_MEMORY_SUPER_BORN_2019_PLATINUM = 2358; // День Рождения СН - 2019\nconst UNIT_AWARD_MEMORY_SUPER_BORN_2019_DIAMOND  = 2359; // День Рождения СН - 2019\nconst UNIT_AWARD_MEMORY_SUPER_BORN_2019_BEST_1ST = 2360; // Лучший Гость - День Рождения СН - 2019\nconst UNIT_AWARD_MEMORY_SUPER_BORN_2019_BEST_2ND = 2361; // Лучший Гость - День Рождения СН - 2019\nconst UNIT_AWARD_MEMORY_SUPER_BORN_2019_BEST_3RD = 2362; // Лучший Гость - День Рождения СН - 2019\n\nconst UNIT_AWARD_MEMORY_NEW_YEAR_2020_SIMPLE   = 2363; // СуперНовый Год - 2020\nconst UNIT_AWARD_MEMORY_NEW_YEAR_2020_BRONZE   = 2364; // СуперНовый Год - 2020\nconst UNIT_AWARD_MEMORY_NEW_YEAR_2020_SILVER   = 2365; // СуперНовый Год - 2020\nconst UNIT_AWARD_MEMORY_NEW_YEAR_2020_GOLD     = 2366; // СуперНовый Год - 2020\nconst UNIT_AWARD_MEMORY_NEW_YEAR_2020_PLATINUM = 2367; // СуперНовый Год - 2020\nconst UNIT_AWARD_MEMORY_NEW_YEAR_2020_DIAMOND  = 2368; // СуперНовый Год - 2020\nconst UNIT_AWARD_MEMORY_NEW_YEAR_2020_BEST_1ST = 2369; // Лучший Гость - СуперНовый Год - 2020\nconst UNIT_AWARD_MEMORY_NEW_YEAR_2020_BEST_2ND = 2370; // Лучший Гость - СуперНовый Год - 2020\nconst UNIT_AWARD_MEMORY_NEW_YEAR_2020_BEST_3RD = 2371; // Лучший Гость - СуперНовый Год - 2020\n\nconst UNIT_AWARD_MEMORY_SUPER_BORN_2020_SIMPLE   = 2380; // День Рождения СН - 2020\nconst UNIT_AWARD_MEMORY_SUPER_BORN_2020_BRONZE   = 2381; // День Рождения СН - 2020\nconst UNIT_AWARD_MEMORY_SUPER_BORN_2020_SILVER   = 2382; // День Рождения СН - 2020\nconst UNIT_AWARD_MEMORY_SUPER_BORN_2020_GOLD     = 2383; // День Рождения СН - 2020\nconst UNIT_AWARD_MEMORY_SUPER_BORN_2020_PLATINUM = 2384; // День Рождения СН - 2020\nconst UNIT_AWARD_MEMORY_SUPER_BORN_2020_DIAMOND  = 2385; // День Рождения СН - 2020\nconst UNIT_AWARD_MEMORY_SUPER_BORN_2020_BEST_1ST = 2386; // Лучший Гость - День Рождения СН - 2020\nconst UNIT_AWARD_MEMORY_SUPER_BORN_2020_BEST_2ND = 2387; // Лучший Гость - День Рождения СН - 2020\nconst UNIT_AWARD_MEMORY_SUPER_BORN_2020_BEST_3RD = 2388; // Лучший Гость - День Рождения СН - 2020\n\nconst UNIT_AWARD_MEMORY_NEW_YEAR_2021_SIMPLE   = 2390; // СуперНовый Год - 2021\nconst UNIT_AWARD_MEMORY_NEW_YEAR_2021_BRONZE   = 2391; // СуперНовый Год - 2021\nconst UNIT_AWARD_MEMORY_NEW_YEAR_2021_SILVER   = 2392; // СуперНовый Год - 2021\nconst UNIT_AWARD_MEMORY_NEW_YEAR_2021_GOLD     = 2393; // СуперНовый Год - 2021\nconst UNIT_AWARD_MEMORY_NEW_YEAR_2021_PLATINUM = 2394; // СуперНовый Год - 2021\nconst UNIT_AWARD_MEMORY_NEW_YEAR_2021_DIAMOND  = 2395; // СуперНовый Год - 2021\nconst UNIT_AWARD_MEMORY_NEW_YEAR_2021_BEST_1ST = 2396; // Лучший Гость - СуперНовый Год - 2021\nconst UNIT_AWARD_MEMORY_NEW_YEAR_2021_BEST_2ND = 2397; // Лучший Гость - СуперНовый Год - 2021\nconst UNIT_AWARD_MEMORY_NEW_YEAR_2021_BEST_3RD = 2398; // Лучший Гость - СуперНовый Год - 2021\n\nconst UNIT_AWARD_MEMORY_SUPER_BORN_2021_SIMPLE   = 2400; // День Рождения СН - 2021\nconst UNIT_AWARD_MEMORY_SUPER_BORN_2021_BRONZE   = 2401; // День Рождения СН - 2021\nconst UNIT_AWARD_MEMORY_SUPER_BORN_2021_SILVER   = 2402; // День Рождения СН - 2021\nconst UNIT_AWARD_MEMORY_SUPER_BORN_2021_GOLD     = 2403; // День Рождения СН - 2021\nconst UNIT_AWARD_MEMORY_SUPER_BORN_2021_PLATINUM = 2404; // День Рождения СН - 2021\nconst UNIT_AWARD_MEMORY_SUPER_BORN_2021_DIAMOND  = 2405; // День Рождения СН - 2021\nconst UNIT_AWARD_MEMORY_SUPER_BORN_2021_BEST_1ST = 2406; // Лучший Гость - День Рождения СН - 2021\nconst UNIT_AWARD_MEMORY_SUPER_BORN_2021_BEST_2ND = 2407; // Лучший Гость - День Рождения СН - 2021\nconst UNIT_AWARD_MEMORY_SUPER_BORN_2021_BEST_3RD = 2408; // Лучший Гость - День Рождения СН - 2021\n\nconst UNIT_AWARD_MEMORY_NEW_YEAR_2022_SIMPLE   = 2410; // СуперНовый Год - 2022\nconst UNIT_AWARD_MEMORY_NEW_YEAR_2022_BRONZE   = 2411; // СуперНовый Год - 2022\nconst UNIT_AWARD_MEMORY_NEW_YEAR_2022_SILVER   = 2412; // СуперНовый Год - 2022\nconst UNIT_AWARD_MEMORY_NEW_YEAR_2022_GOLD     = 2413; // СуперНовый Год - 2022\nconst UNIT_AWARD_MEMORY_NEW_YEAR_2022_PLATINUM = 2414; // СуперНовый Год - 2022\nconst UNIT_AWARD_MEMORY_NEW_YEAR_2022_DIAMOND  = 2415; // СуперНовый Год - 2022\nconst UNIT_AWARD_MEMORY_NEW_YEAR_2022_BEST_1ST = 2416; // Лучший Гость - СуперНовый Год - 2022\nconst UNIT_AWARD_MEMORY_NEW_YEAR_2022_BEST_2ND = 2417; // Лучший Гость - СуперНовый Год - 2022\nconst UNIT_AWARD_MEMORY_NEW_YEAR_2022_BEST_3RD = 2418; // Лучший Гость - СуперНовый Год - 2022\n\nconst UNIT_AWARD_MEMORY_SUPER_BORN_2022_SIMPLE   = 2420; // День Рождения СН - 2022\nconst UNIT_AWARD_MEMORY_SUPER_BORN_2022_BRONZE   = 2421; // День Рождения СН - 2022\nconst UNIT_AWARD_MEMORY_SUPER_BORN_2022_SILVER   = 2422; // День Рождения СН - 2022\nconst UNIT_AWARD_MEMORY_SUPER_BORN_2022_GOLD     = 2423; // День Рождения СН - 2022\nconst UNIT_AWARD_MEMORY_SUPER_BORN_2022_PLATINUM = 2424; // День Рождения СН - 2022\nconst UNIT_AWARD_MEMORY_SUPER_BORN_2022_DIAMOND  = 2425; // День Рождения СН - 2022\nconst UNIT_AWARD_MEMORY_SUPER_BORN_2022_BEST_1ST = 2426; // Лучший Гость - День Рождения СН - 2022\nconst UNIT_AWARD_MEMORY_SUPER_BORN_2022_BEST_2ND = 2427; // Лучший Гость - День Рождения СН - 2022\nconst UNIT_AWARD_MEMORY_SUPER_BORN_2022_BEST_3RD = 2428; // Лучший Гость - День Рождения СН - 2022\n\nconst UNIT_AWARD_MEMORY_NEW_YEAR_2023_SIMPLE   = 2430; // СуперНовый Год - 2023\nconst UNIT_AWARD_MEMORY_NEW_YEAR_2023_BRONZE   = 2431; // СуперНовый Год - 2023\nconst UNIT_AWARD_MEMORY_NEW_YEAR_2023_SILVER   = 2432; // СуперНовый Год - 2023\nconst UNIT_AWARD_MEMORY_NEW_YEAR_2023_GOLD     = 2433; // СуперНовый Год - 2023\nconst UNIT_AWARD_MEMORY_NEW_YEAR_2023_PLATINUM = 2434; // СуперНовый Год - 2023\nconst UNIT_AWARD_MEMORY_NEW_YEAR_2023_DIAMOND  = 2435; // СуперНовый Год - 2023\nconst UNIT_AWARD_MEMORY_NEW_YEAR_2023_BEST_1ST = 2436; // Лучший Гость - СуперНовый Год - 2023\nconst UNIT_AWARD_MEMORY_NEW_YEAR_2023_BEST_2ND = 2437; // Лучший Гость - СуперНовый Год - 2023\nconst UNIT_AWARD_MEMORY_NEW_YEAR_2023_BEST_3RD = 2438; // Лучший Гость - СуперНовый Год - 2023\n\nconst UNIT_AWARD_MEMORY_SUPER_BORN_2023_SIMPLE   = 2440; // День Рождения СН - 2023\nconst UNIT_AWARD_MEMORY_SUPER_BORN_2023_BRONZE   = 2441; // День Рождения СН - 2023\nconst UNIT_AWARD_MEMORY_SUPER_BORN_2023_SILVER   = 2442; // День Рождения СН - 2023\nconst UNIT_AWARD_MEMORY_SUPER_BORN_2023_GOLD     = 2443; // День Рождения СН - 2023\nconst UNIT_AWARD_MEMORY_SUPER_BORN_2023_PLATINUM = 2444; // День Рождения СН - 2023\nconst UNIT_AWARD_MEMORY_SUPER_BORN_2023_DIAMOND  = 2445; // День Рождения СН - 2023\nconst UNIT_AWARD_MEMORY_SUPER_BORN_2023_BEST_1ST = 2446; // Лучший Гость - День Рождения СН - 2023\nconst UNIT_AWARD_MEMORY_SUPER_BORN_2023_BEST_2ND = 2447; // Лучший Гость - День Рождения СН - 2023\nconst UNIT_AWARD_MEMORY_SUPER_BORN_2023_BEST_3RD = 2448; // Лучший Гость - День Рождения СН - 2023\n\nconst UNIT_AWARD_MEMORY_NEW_YEAR_2024_SIMPLE   = 2450; // СуперНовый Год - 2024\nconst UNIT_AWARD_MEMORY_NEW_YEAR_2024_BRONZE   = 2451; // СуперНовый Год - 2024\nconst UNIT_AWARD_MEMORY_NEW_YEAR_2024_SILVER   = 2452; // СуперНовый Год - 2024\nconst UNIT_AWARD_MEMORY_NEW_YEAR_2024_GOLD     = 2453; // СуперНовый Год - 2024\nconst UNIT_AWARD_MEMORY_NEW_YEAR_2024_PLATINUM = 2454; // СуперНовый Год - 2024\nconst UNIT_AWARD_MEMORY_NEW_YEAR_2024_DIAMOND  = 2455; // СуперНовый Год - 2024\nconst UNIT_AWARD_MEMORY_NEW_YEAR_2024_BEST_1ST = 2456; // Лучший Гость - СуперНовый Год - 2024\nconst UNIT_AWARD_MEMORY_NEW_YEAR_2024_BEST_2ND = 2457; // Лучший Гость - СуперНовый Год - 2024\nconst UNIT_AWARD_MEMORY_NEW_YEAR_2024_BEST_3RD = 2458; // Лучший Гость - СуперНовый Год - 2024\n\nconst UNIT_AWARD_MEMORY_SUPER_BORN_2024_SIMPLE = 2460;   // День Рождения СН - 2024\nconst UNIT_AWARD_MEMORY_SUPER_BORN_2024_BRONZE = 2461;   // День Рождения СН - 2024\nconst UNIT_AWARD_MEMORY_SUPER_BORN_2024_SILVER = 2462;   // День Рождения СН - 2024\nconst UNIT_AWARD_MEMORY_SUPER_BORN_2024_GOLD = 2463;     // День Рождения СН - 2024\nconst UNIT_AWARD_MEMORY_SUPER_BORN_2024_PLATINUM = 2464; // День Рождения СН - 2024\nconst UNIT_AWARD_MEMORY_SUPER_BORN_2024_DIAMOND = 2465;  // День Рождения СН - 2024\nconst UNIT_AWARD_MEMORY_SUPER_BORN_2024_BEST_1ST = 2466; // Лучший Гость - День Рождения СН - 2024\nconst UNIT_AWARD_MEMORY_SUPER_BORN_2024_BEST_2ND = 2467; // Лучший Гость - День Рождения СН - 2024\nconst UNIT_AWARD_MEMORY_SUPER_BORN_2024_BEST_3RD = 2468; // Лучший Гость - День Рождения СН - 2024\n\ndefine('UNIT_AWARD_BADGE', 2600); // Бейджики/значки за ачивки - например, \"Построил 1000 кораблей\"\ndefine('UNIT_AWARD_BADGE_BLITZ', 2601); // Медали за Блиц-сервер\ndefine('UNIT_AWARD_PENNANT', 2900); // Переходящий вымпел - индикация статуса на сервере: \"Топ-1\", \"Топ\", \"Сабтоп\", \"Самый большой флот\" итд\n// 2602-2999 // Reserved for Awards\n\n// 3000-3019 - 20 // Christmas Highspot Units\n// 3020-3039 - 20 // SuperNova Birthday Units\n// 3040-3099 - 50 // Halloween Units\n// 3100-3149 - 50 // SuperNova Christmas Gather Units\n// 3150-3899 // Reserved for Festival\n// 3900-3999 // Reserved for Stories\n\ndefine('UNIT_NEXT', 4000); // !!! Next unit start on 4000 !!!\n\n// WHEN CHANGING CONSTANT STRING VALUE TO INTEGER IT SHOULD BE CONSISTENT WITH UNIT_xxx FAMILY OF CONSTANTS!!!\ndefine('UNIT_INTERNAL', 700000);\n\ndefine('UNIT_SERVER_SPEED_BUILDING', 'UNIT_SERVER_SPEED_BUILDING');\ndefine('UNIT_SERVER_SPEED_MINING', 'UNIT_SERVER_SPEED_MINING');\ndefine('UNIT_SERVER_SPEED_FLEET', 'UNIT_SERVER_SPEED_FLEET');\ndefine('UNIT_SERVER_SPEED_EXPEDITION', 'UNIT_SERVER_SPEED_EXPEDITION');\ndefine('UNIT_SERVER_FLEET_NOOB_POINTS', 'UNIT_SERVER_FLEET_NOOB_POINTS');\ndefine('UNIT_SERVER_FLEET_NOOB_FACTOR', 'UNIT_SERVER_FLEET_NOOB_FACTOR');\ndefine('UNIT_SERVER_PAYMENT_MM_PER_CURRENCY', 'UNIT_SERVER_PAYMENT_MM_PER_CURRENCY');\n\ndefine('UNIT_FESTIVAL_SPEED_BUILDING', 'UNIT_FESTIVAL_SPEED_BUILDING');\ndefine('UNIT_FESTIVAL_SPEED_MINING', 'UNIT_FESTIVAL_SPEED_MINING');\ndefine('UNIT_FESTIVAL_SPEED_FLEET', 'UNIT_FESTIVAL_SPEED_FLEET');\ndefine('UNIT_FESTIVAL_SPEED_EXPEDITION', 'UNIT_FESTIVAL_SPEED_EXPEDITION');\n\ndefine('UNIT_PLAYER_EMPIRE_SPY', 'UNIT_PLAYER_EMPIRE_SPY');\n\ndefine('UNIT_PLANET_MINING_METAL', 'UNIT_PLANET_MINING_METAL');\ndefine('UNIT_PLANET_MINING_CRYSTAL', 'UNIT_PLANET_MINING_CRYSTAL');\ndefine('UNIT_PLANET_MINING_DEUTERIUM', 'UNIT_PLANET_MINING_DEUTERIUM');\n\ndefine('UNIT_FLEET_PLANET_SPY', 'UNIT_FLEET_PLANET_SPY');\n\ndefine('GROUP_HIGHSPOTS', 790000);\nconst UNIT_OBJECTS_IN_SPACE = 790001;\nconst GROUP_UNIT_OBJECTS_IN_SPACE = 790002;\n\n\ndefine('GROUP_PART',         800000); // Зарезервировано для запчастей: 800.001 - 899.999\n define('GROUP_PART_HULL',    801000); // Корпуса - 1000 штук\n define('GROUP_PART_ARMOR',   802000); // Броня - 1000 штук\n define('GROUP_PART_SHIELD',  803000); // Щиты - 1000 штук\n define('GROUP_PART_WEAPON',  810000); // Оружие - 10000 штук\n\n\ndefine('UNIT_GROUP', 'groups'); // 900.000 // Зарезервировано для груп юнитов: 900.001 - 999.999\ndefine('GROUP_UNIT_USER', 1000000);// Зарезервировано для пользовательских юнитов: 1.000.001 - 1.999.999\ndefine('GROUP_ID_RESERVED', 2000000);// Зарезервировано для прочих нужд: 2.000.000 - 1.999.999.999\nconst UNIT_CAN_NOT_BE_BUILD = 2000001; // Юнит не может быть построен - для спецюнитов\ndefine('GROUP_PARAMS', 1000000000);// Зарезервировано для параметров: 1.000.000.001 - 1.999.999.999\ndefine('GROUP_DEVELOPERS', 2000000000);// Пространство для разработчиков: 2.000.000.001 - 2.147.483.647\n\ndefine('UNIT_PLAYER_COLONIES_CURRENT', 'COLONIES_CURRENT');\ndefine('UNIT_PLAYER_COLONIES_MAX', 'COLONIES_MAX');\ndefine('UNIT_PLAYER_EXPEDITIONS_MAX', 'EXPEDITIONS_MAX');\n\nconst GROUP_UNIT_COMBAT_SORT_ORDER = 'unitCombatSortOrder';\n"
  },
  {
    "path": "includes/db/db_helpers.php",
    "content": "<?php\n/**\n * Created by PhpStorm.\n * User: Gorlum\n * Date: 26.12.2015\n * Time: 17:19\n */\n\nuse Planet\\DBStaticPlanet;\n\n/**\n * Normalize and make ID safe\n *\n * @param $db_row\n *\n * @return float|int\n */\nfunction db_normalize_id($db_row, $field_name = 'id') {\n  return idval(is_array($db_row) && !empty($db_row[$field_name]) ? $db_row[$field_name] : $db_row);\n}\n\n/**\n * Makes set safe\n *\n * @param array $set\n * @param bool $delta - Is it delta set?\n *\n * @return string\n */\nfunction db_set_make_safe_string($set, $delta = false) {\n  $set_safe = array();\n  foreach($set as $field => $value) {\n    if(empty($field)) {\n      continue;\n    }\n\n    $field = '`' . SN::$db->db_escape($field) . '`';\n    $new_value = $value;\n    if($value === null) {\n      $new_value = 'NULL';\n    } elseif(is_string($value) && (string)($new_value = floatval($value)) != (string)$value) {\n      // non-float\n      $new_value = '\"' . SN::$db->db_escape($value) . '\"';\n    } elseif($delta) {\n      // float and DELTA-set\n      $new_value = \"{$field} + ({$new_value})\";\n    }\n    $set_safe[] = \"{$field} = {$new_value}\";\n  }\n\n  $set_safe = implode(',', $set_safe);\n\n  return $set_safe;\n}\n\n/**\n * Converts IRAK table record to FLEET one\n *\n * @param array $missile_db_list\n * @param array $fleet_db_list\n */\nfunction missile_list_convert_to_fleet(&$missile_db_list, &$fleet_db_list) {\n  // Missile attack\n  foreach($missile_db_list as $irak) {\n    if($irak['fleet_end_time'] >= SN_TIME_NOW) {\n      $irak['fleet_start_type'] = PT_PLANET;\n      $planet_start = DBStaticPlanet::db_planet_by_vector($irak, 'fleet_start_');\n      $irak['fleet_id'] = -$irak['id'];\n      $irak['fleet_mission'] = MT_MISSILE;\n      $irak['fleet_array'] = UNIT_DEF_MISSILE_INTERPLANET . \",{$irak['fleet_amount']};\";\n      $irak['fleet_start_name'] = $planet_start['name'];\n    }\n    $fleet_db_list[] = $irak;\n  }\n}\n\n/**\n * Get current DB patch version\n *\n * @return int|null\n */\nfunction dbPatchGetCurrent() {\n  return SN::$db->selectValue(\"SELECT MAX(`id`) FROM {{server_patches}}\");\n}"
  },
  {
    "path": "includes/db/db_queries.php",
    "content": "<?php\n\nuse DBAL\\db_mysql;\nuse DBAL\\DbQuery;\n\nrequire_once('db_helpers.php');\n\nrequire_once('db_queries_users.php');\n\n\nfunction db_planet_list_admin_list($table_parent_columns, $planet_active, $active_time, $planet_type) {\n  return doquery(\n    \"SELECT p.*, u.username\" . ($table_parent_columns ? ', p1.name AS parent_name' : '') .\n    \" FROM {{planets}} AS p\n      LEFT JOIN {{users}} AS u ON u.id = p.id_owner\" .\n    ($table_parent_columns ? ' LEFT JOIN {{planets}} AS p1 ON p1.id = p.parent_planet' : '') .\n    \" WHERE \" . ($planet_active ? \"p.last_update >= {$active_time}\" : \"p.planet_type = {$planet_type}\"));\n}\n\nfunction db_planet_list_search($searchtext) {\n  return doquery(\n    \"SELECT\n      p.galaxy, p.system, p.planet, p.planet_type, p.name as planet_name,\n      u.id as uid, u.username, u.ally_id, u.id_planet,\n      u.total_points, u.total_rank,\n      u.ally_tag, u.ally_name\n    FROM\n      {{planets}} AS p\n      LEFT JOIN {{users}} AS u ON u.id = p.id_owner\n    WHERE\n      name LIKE '%{$searchtext}%' AND u.user_as_ally IS NULL\n    ORDER BY\n      ally_tag, username, planet_name\n    LIMIT 30;\"\n  );\n}\n\n\nfunction db_user_list_search($searchtext) {\n  return doquery(\n    \"SELECT\n      pn.player_name, u.id as uid, u.username, u.ally_id, u.id_planet, u.total_points, u.total_rank,\n      p.galaxy, p.system, p.planet, p.planet_type, p.name as planet_name,\n      u.ally_tag, u.ally_name\n    FROM\n      {{player_name_history}} AS pn\n      JOIN {{users}} AS u ON u.id = pn.player_id\n      LEFT JOIN {{planets}} AS p ON p.id_owner = u.id AND p.id=u.id_planet\n    WHERE\n      player_name LIKE '%{$searchtext}%' AND u.user_as_ally IS NULL AND user_bot = \" . USER_BOT_PLAYER . \"\n    ORDER BY\n      ally_tag, username, planet_name\n    LIMIT 30;\"\n  );\n}\n\nfunction db_buddy_list_by_user($user_id) {\n//  return ($user_id = intval($user_id)) ? doquery(\n  return ($user_id = idval($user_id)) ? doquery(\n    \"SELECT\n      b.*,\n      IF(b.BUDDY_OWNER_ID = {$user_id}, b.BUDDY_SENDER_ID, b.BUDDY_OWNER_ID) AS BUDDY_USER_ID,\n      u.username AS BUDDY_USER_NAME,\n      p.name AS BUDDY_PLANET_NAME,\n      p.galaxy AS BUDDY_PLANET_GALAXY,\n      p.system AS BUDDY_PLANET_SYSTEM,\n      p.planet AS BUDDY_PLANET_PLANET,\n      a.id AS BUDDY_ALLY_ID,\n      a.ally_name AS BUDDY_ALLY_NAME,\n      u.onlinetime\n    FROM {{buddy}} AS b\n      LEFT JOIN {{users}} AS u ON u.id = IF(b.BUDDY_OWNER_ID = {$user_id}, b.BUDDY_SENDER_ID, b.BUDDY_OWNER_ID)\n      LEFT JOIN {{planets}} AS p ON p.id_owner = u.id AND p.id = id_planet\n      LEFT JOIN {{alliance}} AS a ON a.id = u.ally_id\n    WHERE (`BUDDY_OWNER_ID` = {$user_id}) OR `BUDDY_SENDER_ID` = {$user_id}\n    ORDER BY BUDDY_STATUS, BUDDY_ID\"\n  ) : false;\n}\n\n\nfunction db_unit_records_sum($unit_id, $user_skip_list_unit) {\n  return doquery(\n    \"SELECT unit_player_id, username, sum(unit_level) as unit_level\n          FROM {{unit}} JOIN {{users}} AS u ON u.id = unit_player_id\n          WHERE unit_player_id != 0 AND unit_snid = {$unit_id} {$user_skip_list_unit}\n          GROUP BY unit_player_id\n          ORDER BY sum(unit_level) DESC, unit_player_id\n          LIMIT 1;\"\n    , true);\n}\n\nfunction db_unit_records_plain($unit_id, $user_skip_list_unit) {\n  return doquery(\n    \"SELECT unit_player_id, username, unit_level\n          FROM {{unit}} JOIN {{users}} AS u ON u.id = unit_player_id\n          WHERE unit_player_id != 0 AND unit_snid = {$unit_id} {$user_skip_list_unit}\n          ORDER BY unit_level DESC, unit_id\n          LIMIT 1;\"\n    , true);\n}\n\nfunction db_stat_list_statistic($who, $is_common_stat, $Rank, $start, $source = false) {\n  if (!$source) {\n    $source = array(\n      'statpoints' => 'statpoints',\n      'users'      => 'users',\n      'id'         => 'id',\n      'username'   => 'username',\n\n      'alliance' => 'alliance',\n\n    );\n  } else {\n    $source = array(\n      'statpoints' => 'blitz_statpoints',\n      'users'      => 'blitz_registrations',\n      'id'         => 'blitz_player_id',\n      'username'   => 'blitz_name',\n\n      'alliance' => 'blitz_alliance', // TODO\n    );\n  }\n\n  if ($who == 1) {\n    if ($is_common_stat) { // , UNIX_TIMESTAMP(CONCAT(YEAR(CURRENT_DATE), DATE_FORMAT(`user_birthday`, '-%m-%d'))) AS `nearest_birthday`\n      $query_str =\n        \"SELECT\n      @rownum:=@rownum+1 rownum, subject.{$source['id']} as `id`, sp.{$Rank}_rank as rank, sp.{$Rank}_old_rank as rank_old, sp.{$Rank}_points as points, subject.{$source['username']} as `name`, subject.*\n    FROM\n      (SELECT @rownum:={$start}) r,\n      {{{$source['statpoints']}}} as sp\n      LEFT JOIN {{{$source['users']}}} AS subject ON subject.{$source['id']} = sp.id_owner\n      LEFT JOIN {{{$source['statpoints']}}} AS sp_old ON sp_old.id_owner = subject.{$source['id']} AND sp_old.`stat_type` = 1 AND sp_old.`stat_code` = 2\n    WHERE\n      sp.`stat_type` = 1 AND sp.`stat_code` = 1\n    ORDER BY\n      sp.`{$Rank}_rank`, subject.{$source['id']}\n    LIMIT\n      \" . $start . \",100;\";\n    } else { // , UNIX_TIMESTAMP(CONCAT(YEAR(CURRENT_DATE), DATE_FORMAT(`user_birthday`, '-%m-%d'))) AS `nearest_birthday`\n      $query_str =\n        \"SELECT\n      @rownum:=@rownum+1 AS rank, subject.{$source['id']} as `id`, @rownum as rank_old, subject.{$Rank} as points, subject.{$source['username']} as name, subject.*\n    FROM\n      (SELECT @rownum:={$start}) r,\n      {{{$source['users']}}} AS subject\n    WHERE\n      subject.user_as_ally is null\n    ORDER BY\n      subject.{$Rank} DESC, subject.{$source['id']}\n    LIMIT\n      \" . $start . \",100;\";\n    }\n  } else {\n    // TODO\n    $query_str =\n      \"SELECT\n    @rownum:=@rownum+1 as rownum, subject.id as `id`, sp.{$Rank}_rank as rank, sp.{$Rank}_old_rank as rank_old, sp.{$Rank}_points as points, subject.ally_name as name, subject.ally_tag, subject.ally_members\n  FROM\n    (SELECT @rownum:={$start}) r,\n    {{{$source['statpoints']}}} AS sp\n    LEFT JOIN {{{$source['alliance']}}} AS subject ON subject.id = sp.id_ally\n    LEFT JOIN {{{$source['statpoints']}}} AS sp_old ON sp_old.id_ally = subject.id AND sp_old.`stat_type` = 2 AND sp_old.`stat_code` = 2\n  WHERE\n    sp.`stat_type` = 2 AND sp.`stat_code` = 1\n  ORDER BY\n    sp.`{$Rank}_rank`, subject.id\n  LIMIT\n    \" . $start . \",100;\";\n  }\n\n  return doquery($query_str);\n}\n\n\nfunction db_stat_list_delete_ally_player() {\n  return doquery('DELETE s FROM `{{statpoints}}` AS s JOIN `{{users}}` AS u ON u.id = s.id_owner WHERE s.id_ally IS NULL AND u.user_as_ally IS NOT NULL');\n}\n\n\nfunction db_chat_player_list_online($chat_refresh_rate, $ally_add) {\n  $sql_date = SN_TIME_NOW - $chat_refresh_rate * 2;\n\n  return doquery(\n    \"SELECT u.*, cp.*\n    FROM {{chat_player}} AS cp\n      JOIN {{users}} AS u ON u.id = cp.chat_player_player_id\n    WHERE\n      `chat_player_refresh_last` >= '{$sql_date}'\n      AND (`banaday` IS NULL OR `banaday` <= \" . SN_TIME_NOW . \")\n      {$ally_add}\n    ORDER BY authlevel DESC, `username`\");\n}\n\nfunction db_referrals_list_by_id($user_id) {\n  return doquery(\"SELECT r.*, u.username, u.register_time FROM {{referrals}} AS r LEFT JOIN {{users}} AS u ON u.id = r.id WHERE id_partner = {$user_id}\");\n}\n\nfunction db_message_list_admin_by_type($int_type_selected, $StartRec) {\n  return doquery(\"SELECT\n  message_id as `ID`,\n  message_from as `FROM`,\n  message_owner as `OWNER_ID`,\n  u.username as `OWNER_NAME`,\n  message_text as `TEXT`,\n  FROM_UNIXTIME(message_time) as `TIME`\nFROM\n  {{messages}} AS m\n  LEFT JOIN {{users}} AS u ON u.id = m.message_owner \" .\n    ($int_type_selected >= 0 ? \"WHERE `message_type` = {$int_type_selected} \" : '') .\n    \"ORDER BY\n  `message_id` DESC\nLIMIT\n  {$StartRec}, 25;\");\n}\n\n\nfunction db_message_insert_all($message_type, $from, $subject, $text) {\n  return doquery($QryInsertMessage = 'INSERT INTO {{messages}} (`message_owner`, `message_sender`, `message_time`, `message_type`, `message_from`, `message_subject`, `message_text`) ' .\n    \"SELECT `id`, 0, unix_timestamp(now()), {$message_type}, '{$from}', '{$subject}', '{$text}' FROM {{users}}\");\n}\n\n\n/**\n * Хелпер для работы с простыми хэш-таблицами в БД\n *\n * @param string $db_table_name\n * @param string $db_id_field_name\n * @param array  $conditions\n *\n * @return int\n */\nfunction db_get_set_unique_id_value($db_table_name, $db_id_field_name, $conditions) {\n  $isTransactionStarted = db_mysql::db_transaction_check(db_mysql::DB_TRANSACTION_WHATEVER);\n  if (!$isTransactionStarted) {\n//    SN::db_transaction_start();\n    doquery(\"LOCK TABLES {{\" . $db_table_name . \"}} WRITE;\");\n  }\n\n  $dbq    = new DbQuery(SN::$gc->db);\n  $record = $dbq\n    ->setTable($db_table_name)\n    ->setWhereArray($conditions)\n    ->setForUpdate()\n    ->doSelectFetch();\n\n  if (empty($record)) {\n    $dbq = new DbQuery(SN::$gc->db);\n    $dbq\n      ->setTable($db_table_name)\n      ->setValues($conditions)\n      ->doInsert();\n\n    $variable_id = SN::$gc->db->db_insert_id();\n  } else {\n    $variable_id = $record[$db_id_field_name];\n  }\n\n  if (!$isTransactionStarted) {\n    doquery(\"UNLOCK TABLES;\");\n//    SN::db_transaction_commit();\n  }\n\n  return $variable_id;\n}\n\n/**\n * Функция проверяет наличие имени игрока в базе\n *\n * @param $player_name_unsafe\n *\n * @return bool\n */\n// OK v4.7\nfunction db_player_name_exists($player_name_unsafe) {\n  db_mysql::db_transaction_check(true);\n\n  $player_name_safe = SN::$db->db_escape($player_name_unsafe);\n\n  $player_name_exists = SN::$db->doQueryAndFetch(\"SELECT * FROM `{{player_name_history}}` WHERE `player_name` = '{$player_name_safe}' LIMIT 1 FOR UPDATE\");\n\n  return !empty($player_name_exists);\n}\n\n/**\n * Получение максимального ID игрока\n *\n * @return int\n */\n// OK v4.7\nfunction db_player_get_max_id() {\n  $max_user_id = SN::$db->doQueryAndFetch(\"SELECT MAX(`id`) as `max_user_id` FROM `{{users}}`\");\n\n  return !empty($max_user_id['max_user_id']) ? $max_user_id['max_user_id'] : 0;\n}\n"
  },
  {
    "path": "includes/db/db_queries_users.php",
    "content": "<?php\n/** @noinspection PhpDeprecationInspection */\n/** @noinspection SqlResolve */\n/** @noinspection PhpUnnecessaryCurlyVarSyntaxInspection */\n\nuse Player\\PlayerStatic;\n\n/**\n * Возвращает информацию о пользователе по его ID\n *\n * @param int   $user_id_unsafe  ID пользователя\n * @param bool  $for_update      @deprecated\n * @param ?bool $player          Признак выбора записи пользователь типа \"игрок\"\n *                               <p>null - Можно выбрать запись любого типа</p>\n *                               <p>true - Выбирается только запись типа \"игрок\"</p>\n *                               <p>false - Выбирается только запись типа \"альянс\"</p>\n *\n * @return array|false\n *    <p>false - Нет записи с указанным ID и $player</p>\n *    <p>array - запись типа $user</p>\n * @deprecated\n * @noinspection PhpUnusedParameterInspection\n */\nfunction db_user_by_id($user_id_unsafe, $for_update = false, $player = null) {\n  $user  = PlayerStatic::dbSelectOne(\"SELECT * FROM {{users}} WHERE `id` = \" . idval($user_id_unsafe));\n\n  return (is_array($user) &&\n    (\n      $player === null\n      ||\n      ($player === true && !$user['user_as_ally'])\n      ||\n      ($player === false && $user['user_as_ally'])\n    )) ? $user : false;\n}\n\n/**\n * @deprecated\n */\nfunction db_user_by_username($username_unsafe, $like = false) {\n  if (!($username_unsafe = trim($username_unsafe))) {\n    return null;\n  }\n\n  // тут на самом деле strtolower() лишняя, но пусть будет\n  $username_safe = SN::$db->db_escape($like ? strtolower($username_unsafe) : $username_unsafe);\n\n  // Вытаскиваем запись\n  $operand = $like ? 'LIKE' : '=';\n\n   $user  = PlayerStatic::dbSelectOne(\"SELECT * FROM {{users}} WHERE `username` {$operand} '{$username_safe}'\");\n\n  return $user;\n}\n\n/**\n * @param        $playerIdOrName\n * @param null   $player\n * @param bool   $like\n *\n * @return array|false\n * @deprecated\n */\nfunction dbPlayerByIdOrName($playerIdOrName, $player = null, $like = false) {\n  $row = db_user_by_id($playerIdOrName, false, $player);\n  if (empty($row['id'])) {\n    $row = db_user_by_username($playerIdOrName, $like);\n  }\n\n  return !is_array($row) || empty($row['id']) ? false : $row;\n}\n\n/**\n * @deprecated\n * @noinspection PhpUnusedParameterInspection\n */\nfunction db_user_list($filter = '', $for_update = false, $fields = '*') {\n  return SN::db_get_record_list(LOC_USER, $filter);\n}\n\n\n/**\n * @deprecated\n */\nfunction db_user_set_by_id($user_id, $set) {\n  return SN::db_upd_record_by_id(LOC_USER, $user_id, $set);\n}\n\n\n/**\n * @deprecated\n */\nfunction db_user_list_set_mass_mail($owners_list, $set) {\n  return SN::db_upd_record_list(LOC_USER, !empty($owners_list) ? '`id` IN (' . implode(',', $owners_list) . ');' : '', $set);\n}\n\n/**\n * @deprecated\n */\nfunction db_user_list_set_by_ally_and_rank($ally_id, $ally_rank_id, $set) {\n  return SN::db_upd_record_list(LOC_USER, \"`ally_id`={$ally_id} AND `ally_rank_id` >= {$ally_rank_id}\", $set);\n}\n\n/**\n * @deprecated\n */\nfunction db_user_list_set_ally_deprecated_convert_ranks($ally_id, $i, $rank_id) {\n  return SN::db_upd_record_list(LOC_USER, \"`ally_id` = {$ally_id} AND `ally_rank_id`={$rank_id}\", \"`ally_rank_id` = {$i}\");\n}\n\n\nfunction db_user_change_active_planet_to_capital($user_id, $captured_planet) {\n  return doquery(\"UPDATE {{users}} SET `current_planet` = `id_planet` WHERE `id` = {$user_id} AND `current_planet` = {$captured_planet};\");\n}\n\n\n// TODO Внести это всё в supernova для HyperNova\n/**\n * @return string\n * @deprecated\n * TODO - это вообще-то надо хранить в конфигурации\n */\nfunction db_user_last_registered_username() {\n  $user = PlayerStatic::dbSelectOne('SELECT * FROM `{{users}}` WHERE `user_as_ally` IS NULL ORDER BY `id` DESC');\n\n  return isset($user['username']) ? $user['username'] : '';\n}\n\nfunction db_user_count($online = false) {\n  $result = doquery('SELECT COUNT(`id`) AS user_count FROM `{{users}}` WHERE `user_as_ally` IS NULL AND `user_bot` = ' . USER_BOT_PLAYER . ($online ? ' AND onlinetime > ' . (SN_TIME_NOW - SN::$config->game_users_online_timeout) : ''), true);\n\n  return isset($result['user_count']) ? $result['user_count'] : 0;\n}\n\nfunction db_user_list_to_celebrate($config_user_birthday_range) {\n  // TODO\n  /** @noinspection SqlAggregates */\n  return doquery(\n    \"SELECT\n      `id`, `username`, `user_birthday`, `user_birthday_celebrated`\n      , CONCAT(YEAR(CURRENT_DATE), DATE_FORMAT(`user_birthday`, '-%m-%d')) AS `current_birthday`\n      , DATEDIFF(CURRENT_DATE, CONCAT(YEAR(CURRENT_DATE), DATE_FORMAT(`user_birthday`, '-%m-%d'))) AS `days_after_birthday`\n    FROM\n      `{{users}}`\n    WHERE\n      `user_birthday` IS NOT NULL\n      AND (`user_birthday_celebrated` IS NULL OR DATE_ADD(`user_birthday_celebrated`, INTERVAL 1 YEAR) < CURRENT_DATE)\n      AND `user_as_ally` IS NULL\n    HAVING\n      `days_after_birthday` >= 0 AND `days_after_birthday` < {$config_user_birthday_range} FOR UPDATE;\");\n}\n\nfunction db_user_list_admin_multi_accounts() {\n  return doquery(\"SELECT COUNT(*) as ip_count, `user_lastip` FROM `{{users}}` WHERE user_as_ally IS NULL GROUP BY user_lastip HAVING COUNT(*) > 1;\");\n}\n\nfunction db_user_list_admin_sorted($sort, $online = false, $desc = false) {\n  global $config;\n\n  return doquery(\"SELECT u.*, COUNT(r.id) AS referral_count, SUM(r.dark_matter) AS referral_dm FROM {{users}} as u\n    LEFT JOIN {{referrals}} as r on r.id_partner = u.id\n    WHERE\" .\n    ($online ? \" `onlinetime` >= \" . (SN_TIME_NOW - $config->game_users_online_timeout) : ' user_as_ally IS NULL') .\n    \" GROUP BY u.id\n    ORDER BY user_as_ally, {$sort}\" . ($desc ? \" DESC\" : \" ASC\"));\n}\n\n/**\n * Выбирает записи игроков по списку их ID\n *\n * @param $user_id_list\n *\n * @return array\n */\nfunction db_user_list_by_id($user_id_list) {\n  if (!is_array($user_id_list)) {\n    $user_id_list = array($user_id_list);\n  }\n\n  $user_list = array();\n  foreach ($user_id_list as $user_id_unsafe) {\n    if (!empty($user = db_user_by_id($user_id_unsafe))) {\n      $user_list[$user_id_unsafe] = $user;\n    }\n  }\n\n  return $user_list;\n}\n"
  },
  {
    "path": "includes/db.php",
    "content": "<?php /** @noinspection PhpUnnecessaryCurlyVarSyntaxInspection */\n\n/**\n * @version   2015-04-11 11:47:49 39b14.2\n * @copyright 2008-2015 Gorlum for Project \"SuperNova.WS\"\n */\n\nif (!defined('INSIDE')) {\n  die();\n}\n\nrequire_once('db/db_queries.php');\n\n/**\n * @param $query\n * @param $tableName\n * @param $object_id\n *\n * @return array|bool|mysqli_result|null\n */\nfunction db_change_units_perform($query, $tableName, $object_id) {\n  $query = implode(',', $query);\n  if ($query && $object_id) {\n    return SN::db_upd_record_by_id($tableName == 'users' ? LOC_USER : LOC_PLANET, $object_id, $query);\n  }\n\n  return null;\n}\n\n/**\n * @param $user\n * @param $planet\n * @param $unit_list\n * @param $query\n *\n// TODO: THIS FUNCTION IS OBSOLETE AND SHOULD BE REPLACED!\n// TODO - ТОЛЬКО ДЛЯ РЕСУРСОВ\n// $unit_list should have unique entrances! Recompress non-uniq entrances before pass param!\n * @return void\n */\n\nfunction db_change_units(&$user, &$planet, $unit_list = [], $query = null) {\n  $query = is_array($query) ? $query : [\n    LOC_USER   => [],\n    LOC_PLANET => [],\n  ];\n\n  $group = sn_get_groups('resources_loot');\n\n  foreach ($unit_list as $unit_id => $unit_amount) {\n    if (!in_array($unit_id, $group)) {\n      // TODO - remove later\n      print('<h1>СООБЩИТЕ ЭТО АДМИНУ: db_change_units() вызван для не-ресурсов!</h1>');\n      pdump(debug_backtrace());\n      die('db_change_units() вызван для не-ресурсов!');\n    }\n\n    if (!$unit_amount) {\n      continue;\n    }\n\n    $unit_db_name = pname_resource_name($unit_id);\n\n    $unit_location = sys_get_unit_location($user, $planet, $unit_id);\n\n    // Changing value in object\n    switch ($unit_location) {\n      case LOC_USER:\n        $user[$unit_db_name] += $unit_amount;\n      break;\n      case LOC_PLANET:\n        $planet[$unit_db_name] += $unit_amount;\n      break;\n    }\n\n    $unit_amount                     = $unit_amount < 0 ? $unit_amount : \"+{$unit_amount}\"; // Converting positive unit_amount to string '+unit_amount'\n    $query[$unit_location][$unit_id] = \"`{$unit_db_name}`=`{$unit_db_name}`{$unit_amount}\";\n  }\n\n  db_change_units_perform($query[LOC_USER], 'users', $user['id']);\n  db_change_units_perform($query[LOC_PLANET], 'planets', $planet['id']);\n}\n\n/* Pass-through functions ******************************************************************************************* */\n/**\n * @param        $query\n * @param string $table\n * @param bool   $fetch\n * @param bool   $skip_query_check\n *\n * @return array|bool|mysqli_result|null\n *\n * @deprecated\n */\nfunction doquery($query, $table = '', $fetch = false, $skip_query_check = false) {\n  if (!is_string($table)) {\n    $fetch = $table;\n  }\n\n  return SN::$db->doquery($query, $fetch, $skip_query_check);\n}\n\n/**\n * @param $query\n *\n * @return array|null\n *\n * @deprecated\n */\nfunction db_fetch(&$query) {\n  return SN::$db->db_fetch($query);\n}\n\n/**\n * @param $unescaped_string\n *\n * @return string\n *\n * @deprecated\n */\nfunction db_escape($unescaped_string) {\n  return SN::$db->db_escape($unescaped_string);\n}\n"
  },
  {
    "path": "includes/debug.tools.php",
    "content": "<?php\n\n/** Created by Gorlum 21.10.2025 18:05 */\n\nif ( ! function_exists('pre')) {\n  // Debug tools v2025-10-21.01\n\n  define('START', microtime(true)); // SHOULD NEVER BE REMOVED!\n\n  function dieHere($msg = '')\n  {\n    $p = debug_backtrace()[1];\n    print(\"<br />\\nDied: {$p['file']}@{$p['line']}<br />\\n\" . ($msg ? 'Die message: ' . $msg . \"<br />\\n\" : ''));\n    die();\n  }\n\n  /**\n   * @param mixed $value     <p>The variable you want to export.</p>\n   * @param mixed ...$values [optional]\n   *\n   * @return void\n   */\n  function pre()\n  {\n    if (func_num_args() <= 0) {\n      return;\n    }\n\n    foreach (func_get_args() ?: [] as $var) {\n      print \"<pre>\";\n      var_export($var);\n//                print_r(\n//                    $var === null ? 'NULL' :\n//                        (($type = gettype($var)) == 'object' || $type == 'array'\n//                            ? $var :\n//                            ($type === 'string'\n//                                ? $type . '(' . strlen($var) . ') `' . $var . '`' :\n//                                ($type == 'boolean'\n//                                    ? ($var ? 'true' : 'false')\n//                                    : $type . ' ' . print_r($var, true)\n//                                )\n//                            )\n//                        )\n//                );\n      print \"</pre>\";\n    }\n    here();\n  }\n\n  /**\n   * @return void\n   */\n  function here()\n  {\n    $trace = debug_backtrace();\n    $i     = 0;\n    while (in_array(($trace[$i++])['function'], ['pre', 'pred', 'prej', 'predj', 'hered', __FUNCTION__])) { /**/\n    }\n    $p = $trace[$i - 2];\n    // $p     = in_array($trace[1]['function'], ['pred', 'prej', 'predj',]) ? $trace[1] : $trace[0];\n//            print(\"\\n{$p['file']}@{$p['line']}<br />\\n\" . (is_string($die) ? 'Die message: ' . $die . \"<br />\\n\" : ''));\n    print(\"\\n{$p['file']}@{$p['line']}<br />\\n\");\n  }\n\n  function hered() {\n    print 'Stopped at ' . TIME_NOW_SQL_MICRO . ' ';\n    here();\n    die();\n  }\n\n  function pred()\n  {\n    call_user_func_array('pre', func_get_args());\n    die();\n  }\n\n  /**\n   * @param mixed $value     <p>The variable you want to export.</p>\n   * @param mixed ...$values [optional]\n   *\n   * @return void\n   */\n  function prej()\n  {\n    $args = [];\n    foreach (func_get_args() as $arg) {\n      $args[] = json_encode($arg, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);\n    }\n    if (func_num_args() <= 0) {\n      return;\n    }\n\n    foreach ($args as $var) {\n      print \"<pre>\";\n      print($var);\n      print \"</pre>\";\n    }\n    here();\n  }\n\n  function predj()\n  {\n    call_user_func_array('prej', func_get_args());\n    die();\n  }\n\n  function gLog($data, $force = true, $filename = '_log.txt')\n  {\n    if ($force) {\n      if (is_array($data)) {\n        $data = implode('', $data);\n      }\n\n      @file_put_contents(\n        __DIR__ . '/../../application/logs/' . $filename,\n        ($data ? debug . tools . phpdate('Y-m-d H:i:s - ') . $data : '') . \"\\n\", FILE_APPEND);\n    }\n  }\n\n  function dT($msg = '', $die = null)\n  {\n    var_dump((! $msg ?: $msg . ' ') . (microtime(true) - START));\n    $die === null ?: die($die);\n  }\n\n  /**\n   * Nicer dumper for servers w/o xDebug installed\n   *\n   * @param mixed  $var Var to dump\n   * @param string $msg Message to preface dump\n   * @param mixed  $die Should we die() after dump and if yes - how we should die()\n   *\n   * @return void\n   */\n  function pdump($var, $msg = '', $die = null)\n  {\n    echo '<pre>' . ($msg ? \"$msg = \" : '');\n    var_dump($var);\n    echo '</pre>';\n    if ($die !== null) {\n      die($die);\n    }\n  }\n\n  function ptrace($die = false)\n  {\n    $trace = debug_backtrace();\n    array_shift($trace);\n    foreach ($trace as &$item) {\n      if ( ! empty($item['object'])) {\n        $item['object'] = class_name($item['object']);\n      }\n    }\n    pdump($trace);\n\n    if ($die) {\n      die();\n    }\n  }\n\n// Gorlum's debug tools [END] ------------------------------\n\n  /**\n   * @param null|string $msg Message to print. If NULL - nothing printed, just time logged\n   * @param null|mixed  $die If not NULL - die, using $die as message/exit code/etc ( @see die() )\n   *\n   * @return void\n   */\n  function dT2($msg = '', $die = null)\n  {\n    static $events = [];\n\n    $current = (object)['ts' => microtime(true), 'msg' => $msg,];\n\n    $prev = end($events);\n    if ( ! is_object($prev)) {\n      $prev = (object)['ts' => START, 'msg' => 'COUNTER START',];\n    }\n    $events[] = $current;\n\n    //$fromStart = $current->ts - START;\n    $fromLast = $current->ts - $prev->ts;\n    if ($msg !== null) {\n      print(number_format($fromLast, 8) . \"s\" . (! $msg ?: ' - ' . $msg) . \"<br />\\n\\r\");\n    }\n    $die === null ?: die($die);\n  }\n}\n"
  },
  {
    "path": "includes/functions/_deprecated.php",
    "content": "<?php\n/**\n * Created by Gorlum 15.06.2017 4:32\n */\n\n// ------------------------------------------------------------------\nuse DBAL\\db_mysql;\nuse DBAL\\OldDbChangeSet;\nuse Fleet\\DbFleetStatic;\nuse Planet\\DBStaticPlanet;\n\n/**\n * @param array $fleet_row\n * @param bool  $start\n * @param bool  $only_resources\n * @param bool  $safe_fleet\n *\n * @return mixed\n * @deprecated\n */\nfunction RestoreFleetToPlanet(&$fleet_row, $start = true, $only_resources = false, $safe_fleet = false) {\n  /** @see sn_RestoreFleetToPlanet() Default function */\n  return sn_function_call('RestoreFleetToPlanet', array(&$fleet_row, $start, $only_resources, $safe_fleet, &$result));\n}\n\n/**\n * @param array $fleet_row\n * @param bool  $start\n * @param bool  $only_resources\n * @param bool  $safe_fleet\n * @param mixed $result\n *\n * @return int\n * @deprecated\n */\nfunction sn_RestoreFleetToPlanet(&$fleet_row, $start = true, $only_resources = false, $safe_fleet = false, &$result) {\n  db_mysql::db_transaction_check(true);\n\n  $result = CACHE_NOTHING;\n  if (!is_array($fleet_row)) {\n    return $result;\n  }\n\n  $prefix = $start ? 'start' : 'end';\n\n  // Поскольку эта функция может быть вызвана не из обработчика флотов - нам надо всё заблокировать вроде бы НЕ МОЖЕТ!!!\n  // TODO Проеверить от многократного срабатывания !!!\n  // Тут не блокируем пока - сначала надо заблокировать пользователя, что бы не было дедлока\n//  $fleet_row = doquery(\"SELECT * FROM {{fleets}} WHERE `fleet_id`='{$fleet_row['fleet_id']}' LIMIT 1\", true);\n  // Узнаем ИД владельца планеты - без блокировки\n  // TODO поменять на владельца планеты - когда его будут возвращать всегда !!!\n  $user_id = DBStaticPlanet::db_planet_by_vector($fleet_row, \"fleet_{$prefix}_\");\n  $user_id = $user_id['id_owner'];\n  // Блокируем пользователя\n  $user = db_user_by_id($user_id, true);\n  // Блокируем планету\n  $planet_arrival = DBStaticPlanet::db_planet_by_vector($fleet_row, \"fleet_{$prefix}_\");\n  // Блокируем флот\n//  $fleet_row = doquery(\"SELECT * FROM {{fleets}} WHERE `fleet_id`='{$fleet_row['fleet_id']}' LIMIT 1 FOR UPDATE;\", true);\n\n  // Если флот уже обработан - не существует или возращается - тогда ничего не делаем\n  if (!$fleet_row || !is_array($fleet_row) || ($fleet_row['fleet_mess'] == 1 && $only_resources)) {\n    return $result;\n  }\n\n  // Флот, который возвращается на захваченную планету, пропадает\n  if ($start && $fleet_row['fleet_mess'] == 1 && $planet_arrival['id_owner'] != $fleet_row['fleet_owner']) {\n    DbFleetStatic::db_fleet_delete($fleet_row['fleet_id']);\n\n    return $result;\n  }\n\n  $db_changeset = array();\n  if (!$only_resources) {\n    DbFleetStatic::db_fleet_delete($fleet_row['fleet_id']);\n\n    if ($fleet_row['fleet_owner'] == $planet_arrival['id_owner']) {\n      $fleet_array = sys_unit_str2arr($fleet_row['fleet_array']);\n      foreach ($fleet_array as $ship_id => $ship_count) {\n        if ($ship_count) {\n          $db_changeset['unit'][] = OldDbChangeSet::db_changeset_prepare_unit($ship_id, $ship_count, $user, $planet_arrival['id']);\n        }\n      }\n    } else {\n      return CACHE_NOTHING;\n    }\n  } else {\n    $fleet_set = array(\n      'fleet_resource_metal'     => 0,\n      'fleet_resource_crystal'   => 0,\n      'fleet_resource_deuterium' => 0,\n      'fleet_mess'               => 1,\n    );\n    DbFleetStatic::fleet_update_set($fleet_row['fleet_id'], $fleet_set);\n  }\n\n  if (!empty($db_changeset)) {\n    OldDbChangeSet::db_changeset_apply($db_changeset);\n  }\n\n  DBStaticPlanet::db_planet_set_by_id($planet_arrival['id'],\n    \"`metal` = `metal` + '{$fleet_row['fleet_resource_metal']}', `crystal` = `crystal` + '{$fleet_row['fleet_resource_crystal']}', `deuterium` = `deuterium` + '{$fleet_row['fleet_resource_deuterium']}'\");\n  $result = CACHE_FLEET | ($start ? CACHE_PLANET_SRC : CACHE_PLANET_DST);\n\n  return $result;\n}\n"
  },
  {
    "path": "includes/functions/_news.php",
    "content": "<?php\n\nuse DBAL\\db_mysql;\nuse DBAL\\DbSqlPaging;\nuse General\\Helpers\\PagingRenderer;\n\n/**\n * @param template $template\n * @param string   $query_where\n * @param int      $query_limit\n */\nfunction nws_render(&$user, &$template, $query_where = '', $query_limit = 20) {\n  if($user['authlevel'] < AUTH_LEVEL_ADMINISTRATOR) {\n    $query_where .= (!empty($query_where) ? ' AND ' : '')\n      . '`tsTimeStamp` <= (NOW())';\n  }\n\n  $mmModuleIsActive = !empty(SN::$gc->modules->getModulesInGroup('payment'));\n\n  $sqlText = \"SELECT a.*, UNIX_TIMESTAMP(`tsTimeStamp`) AS unix_time, u.authlevel, s.*\n    FROM\n      `{{announce}}` AS a\n      LEFT JOIN `{{survey}}` AS s ON s.survey_announce_id = a.idAnnounce\n      LEFT JOIN `{{users}}` AS u ON u.id = a.user_id\n    WHERE 1 {$query_where}\n    ORDER BY `tsTimeStamp` DESC, idAnnounce\";\n\n  $announce_list = new DbSqlPaging($sqlText, $query_limit, sys_get_param_int(PagingRenderer::KEYWORD));\n  $pager = new PagingRenderer($announce_list, 'announce.php');\n\n  $users = array();\n  foreach ($announce_list as $announce) {\n    if ($announce['user_id'] && !isset($users[$announce['user_id']])) {\n      $users[$announce['user_id']] = db_user_by_id($announce['user_id']);\n    }\n\n    $survey_vote = array('survey_vote_id' => 1);\n    $survey_complete = strtotime($announce['survey_until']) < SN_TIME_NOW;\n\n    if ($announce['survey_id'] && !empty($user['id'])) {\n      $survey_vote = !$survey_complete ? $survey_vote = doquery(\"SELECT `survey_vote_id` FROM `{{survey_votes}}` WHERE survey_parent_id = {$announce['survey_id']} AND survey_vote_user_id = {$user['id']} LIMIT 1;\", true) : array();\n    }\n\n    $announce_exploded = explode(\"<br /><br />\", SN::$gc->bbCodeParser->expandBbCode($announce['strAnnounce'], intval($announce['authlevel'])));\n\n    $template->assign_block_vars('announces', array(\n      'ID'              => $announce['idAnnounce'],\n      'TIME'            => date(FMT_DATE_TIME, $announce['unix_time'] + SN_CLIENT_TIME_DIFF),\n      'ANNOUNCE'        => SN::$gc->bbCodeParser->expandBbCode($announce['strAnnounce'], intval($announce['authlevel'])),\n      'DETAIL_URL'      => $announce['detail_url'],\n      'USER_NAME'       => !empty($users[$announce['user_id']])\n        ? player_nick_render_to_html($users[$announce['user_id']], array('color' => true))\n        : js_safe_string($announce['user_name']),\n      'NEW'             => $announce['unix_time'] + SN::$config->game_news_actual >= SN_TIME_NOW,\n      'FUTURE'          => $announce['unix_time'] > SN_TIME_NOW,\n      'SURVEY_ID'       => $announce['survey_id'],\n      'SURVEY_TEXT'     => $announce['survey_question'],\n      'SURVEY_CAN_VOTE' => empty($survey_vote) && !$survey_complete,\n      'SURVEY_COMPLETE' => $survey_complete,\n      'SURVEY_UNTIL'    => $announce['survey_until'],\n    ));\n\n    foreach ($announce_exploded as $announce_paragraph) {\n      $template->assign_block_vars('announces.paragraph', array(\n        'TEXT' => $announce_paragraph,\n      ));\n    }\n\n    if ($announce['survey_id']) {\n      $survey_query = doquery(\n        \"SELECT survey_answer_id AS `ID`, survey_answer_text AS `TEXT`, count(DISTINCT survey_vote_id) AS `VOTES`\n          FROM `{{survey_answers}}` AS sa\n            LEFT JOIN `{{survey_votes}}` AS sv ON sv.survey_parent_answer_id = sa.survey_answer_id\n          WHERE sa.survey_parent_id = {$announce['survey_id']}\n          GROUP BY survey_answer_id\n          ORDER BY survey_answer_id;\"\n      );\n      $survey_vote_result = array();\n      $total_votes = 0;\n      $total_mm = 0;\n      $total_money = 0;\n      while ($row = db_fetch($survey_query)) {\n        $survey_vote_result[$row['ID']] = $row;\n        $total_votes += $row['VOTES'];\n      }\n\n      if ($mmModuleIsActive && $user['authlevel'] >= AUTH_LEVEL_ADMINISTRATOR) {\n        $mQuery = doquery(\n          \"SELECT\n            sa.survey_answer_id,\n            sum(acc.account_metamatter_total) AS `mm`,\n            (\n              SELECT sum(payment_amount)\n              FROM `{{payment}}` AS pay\n              WHERE payment_currency = 'USD' AND pay.payment_user_id = sv.survey_vote_user_id\n              GROUP BY payment_user_id, payment_currency\n            )                                 AS `money`\n          FROM `{{survey_votes}}` AS sv\n            LEFT JOIN `{{survey_answers}}` AS sa ON sa.survey_answer_id = sv.survey_parent_answer_id\n            LEFT JOIN `{{account_translate}}` AS act ON act.user_id = sv.survey_vote_user_id\n            LEFT JOIN `{{account}}` AS acc ON acc.account_id = act.provider_account_id\n          WHERE sv.survey_parent_id = {$announce['survey_id']}\n          GROUP BY sv.survey_parent_id, sv.survey_parent_answer_id;\"\n        );\n        while ($row = db_fetch($mQuery)) {\n          $survey_vote_result[$row['survey_answer_id']] += [\n            'MM'    => $row['mm'],\n            'MONEY' => $row['money'],\n          ];\n          $total_mm += $row['mm'];\n          $total_money += $row['money'];\n        }\n      }\n\n      // Show result\n      foreach ($survey_vote_result as &$vote_result) {\n        $vote_percent = $total_votes ? $vote_result['VOTES'] / $total_votes * 100 : 0;\n        $vote_result['PERCENT'] = $vote_percent;\n        $vote_result['PERCENT_TEXT'] = round($vote_percent, 1);\n        $vote_result['VOTES'] = HelperString::numberFloorAndFormat($vote_result['VOTES']);\n\n        if ($mmModuleIsActive && $user['authlevel'] >= AUTH_LEVEL_ADMINISTRATOR) {\n          $vote_result['PERCENT_MM'] = $total_mm ? $vote_result['MM'] / $total_mm * 100 : 0;\n          $vote_result['PERCENT_MONEY'] = $total_money ? $vote_result['MONEY'] / $total_money * 100 : 0;\n        }\n\n        $template->assign_block_vars('announces.survey_answers', $vote_result);\n      }\n      // Dirty hack\n      $template->assign_block_vars('announces.total_votes', array(\n        'TOTAL_VOTES' => $total_votes,\n        'TOTAL_MM'    => $total_mm,\n        'TOTAL_MONEY' => $total_money,\n      ));\n    }\n  }\n\n  $template->assign_vars([\n    'PAGER_MESSAGES' => $pager ? $pager->render() : '',\n    'NEWS_COUNT'     => HelperString::numberFloorAndFormat($announce_list->getTotalRecords()),\n\n    'MM_MODULE_ACTIVE' => $mmModuleIsActive,\n  ]);\n}\n\nfunction nws_mark_read(&$user) {\n  if (!empty($user['id'])) {\n    db_user_set_by_id($user['id'], '`news_lastread` = ' . SN_TIME_NOW);\n    $user['news_lastread'] = SN_TIME_NOW;\n  }\n\n  return true;\n}\n\nfunction survey_vote(&$user) {\n  if (empty($user['id'])) {\n    return true;\n  }\n\n  db_mysql::db_transaction_start();\n  $survey_id = sys_get_param_id('survey_id');\n  $is_voted = doquery(\"SELECT `survey_vote_id` FROM `{{survey_votes}}` WHERE survey_parent_id = {$survey_id} AND survey_vote_user_id = {$user['id']} FOR UPDATE;\", true);\n  if (empty($is_voted)) {\n    $survey_vote_id = sys_get_param_id('survey_vote');\n    $is_answer_exists = doquery(\"SELECT `survey_answer_id` FROM `{{survey_answers}}` WHERE survey_parent_id = {$survey_id} AND survey_answer_id = {$survey_vote_id};\", true);\n    if (!empty($is_answer_exists)) {\n      $user_name_safe = SN::$db->db_escape($user['username']);\n      doquery(\"INSERT INTO `{{survey_votes}}` SET `survey_parent_id` = {$survey_id}, `survey_parent_answer_id` = {$survey_vote_id}, `survey_vote_user_id` = {$user['id']}, `survey_vote_user_name` = '{$user_name_safe}';\");\n    }\n  }\n  db_mysql::db_transaction_commit();\n\n  return true;\n}\n"
  },
  {
    "path": "includes/functions/ali_alliances.php",
    "content": "<?php\n\nfunction ali_rank_list_save($ranks) {\n  global $user;\n\n  if (!empty($ranks)) {\n    foreach ($ranks as $rank => $rights) {\n      $rights = implode(',', $rights);\n      $ranklist .= $rights . ';';\n    }\n  }\n\n  doquery(\"UPDATE {{alliance}} SET `ranklist` = '{$ranklist}' WHERE `id` ='{$user['ally_id']}';\");\n\n  return $ranklist;\n}\n\nfunction ali_relations($ally_from, $ally_to = 0) {\n  $ally_to = intval($ally_to);\n  $ally_to = $ally_to ? \" AND alliance_diplomacy_contr_ally_id = {$ally_to}\" : '';\n\n  $temp_array = array();\n  $query = doquery(\n    \"SELECT b.*\n      FROM\n        {{alliance_diplomacy}} AS b,\n        (SELECT alliance_diplomacy_contr_ally_id, MAX(alliance_diplomacy_time) AS alliance_diplomacy_time\n          FROM {{alliance_diplomacy}}\n          WHERE alliance_diplomacy_ally_id = {$ally_from}  {$ally_to}\n          GROUP BY alliance_diplomacy_ally_id, alliance_diplomacy_contr_ally_id\n        ) AS m\n      WHERE b.alliance_diplomacy_contr_ally_id = m.alliance_diplomacy_contr_ally_id\n        AND b.alliance_diplomacy_time = m.alliance_diplomacy_time AND b.alliance_diplomacy_ally_id = {$ally_from}\n      ORDER BY alliance_diplomacy_time, alliance_diplomacy_id;\"\n  );\n\n  while ($record = db_fetch($query)) {\n    $temp_array[$record['alliance_diplomacy_contr_ally_id']] = $record;\n  }\n\n  return $temp_array;\n}\n\nfunction ali_relation($ally_from, $ally_to) {\n  $relation = ali_relations($ally_from, $ally_to);\n\n  return empty($relation) ? ALLY_DIPLOMACY_NEUTRAL : $relation[$ally_to]['alliance_diplomacy_relation'];\n}\n"
  },
  {
    "path": "includes/functions/cht_message_parse.php",
    "content": "<?php\n\n"
  },
  {
    "path": "includes/functions/eco_get_build_data.php",
    "content": "<?php\n\nuse Meta\\Economic\\BuildDataStatic;\nuse Unit\\DBStaticUnit;\n\nfunction eco_lab_sort_effectivness($a, $b) {\n  return $a['laboratory_effective_level'] > $b['laboratory_effective_level'] ? -1 : ($a['laboratory_effective_level'] < $b['laboratory_effective_level'] ? 1 : 0);\n}\n\n/**\n * eco_get_build_data.php\n *\n * 1.0 - copyright (c) 2010 by Gorlum for http://supernova.ws\n * @version 1.0\n */\nfunction eco_get_lab_max_effective_level(&$user, $lab_require) {\n  if (!$user['user_as_ally'] && !isset($user['laboratories_active'])) {\n    $user['laboratories_active'] = array();\n    $query                       = DBStaticUnit::db_unit_list_laboratories($user['id']);\n    while ($row = db_fetch($query)) {\n      if (!eco_unit_busy($user, $row, UNIT_TECHNOLOGIES)) {\n        $row                                     += array(\n          STRUC_LABORATORY             => $level_lab = mrc_get_level($user, $row, STRUC_LABORATORY),\n          STRUC_LABORATORY_NANO        => $level_lab_nano = mrc_get_level($user, $row, STRUC_LABORATORY_NANO),\n          'laboratory_effective_level' => $level_lab * pow(2, $level_lab_nano),\n        );\n        $user['laboratories_active'][$row['id']] = $row;\n      }\n    }\n\n    uasort($user['laboratories_active'], 'eco_lab_sort_effectivness');\n  }\n\n  if (!isset($user['research_effective_level'][$lab_require])) {\n    if ($user['user_as_ally']) {\n      $lab_level = doquery(\"SELECT ally_members AS effective_level FROM {{alliance}} WHERE id = {$user['user_as_ally']} LIMIT 1\", true);\n    } else {\n      $tech_intergalactic           = mrc_get_level($user, false, TECH_RESEARCH) + 1;\n      $lab_level['effective_level'] = 0;\n\n      foreach ($user['laboratories_active'] as $data) {\n        if ($tech_intergalactic <= 0) {\n          break;\n        }\n        if ($data[STRUC_LABORATORY] >= $lab_require) {\n          $lab_level['effective_level'] += $data['laboratory_effective_level'];\n          $tech_intergalactic--;\n        }\n      }\n    }\n    $user['research_effective_level'][$lab_require] = $lab_level['effective_level'] ? $lab_level['effective_level'] : 1;\n  }\n\n  return $user['research_effective_level'][$lab_require];\n}\n\n/**\n * @param array      $user\n * @param array      $planet\n * @param int        $unit_id\n * @param int|string $unit_level\n * @param bool       $only_cost\n * @param array|null $info\n *\n * @return mixed\n */\nfunction eco_get_build_data(&$user, $planet, $unit_id, $unit_level = 0, $only_cost = false, $info = null) {\n  $unit_data = get_unit_param($unit_id);\n\n  // Filling basic build data - unit cost\n  $cost = BuildDataStatic::getBasicData($user, $planet, $unit_data, $unit_level);\n\n  // Getting autoconvert unit amount\n  $cost[BUILD_AUTOCONVERT] = !$cost[P_OPTIONS][P_ONLY_DARK_MATTER] ? BuildDataStatic::getAutoconvertCount($user, $planet, $cost) : 0;\n  // Limiting autoconvert amount to unit max stack - if is set\n  !empty($unit_data[P_MAX_STACK]) ? $cost[BUILD_AUTOCONVERT] = min($unit_data[P_MAX_STACK], $cost[BUILD_AUTOCONVERT]) : false;\n\n  $cost[P_OPTIONS][P_TIME_RAW] = $cost[P_OPTIONS][P_TIME_RAW] * 60 * 60 / get_game_speed() / 2500;\n\n  // TODO - Вынести в отдельную процедуру расчёт стоимости\n  if ($only_cost) {\n    return $cost;\n  }\n\n  // Check if unit can be built\n  $cost['RESULT'][BUILD_CREATE] = BuildDataStatic::getBuildStatus($user, $planet, $unit_id, $cost['CAN'][BUILD_CREATE]);\n  // Setting destroy status\n  $cost['RESULT'][BUILD_DESTROY] = BuildDataStatic::getDestroyStatus($user, $planet, $unit_id, $cost);\n\n\n  // Time calculations\n  if (in_array($unit_id, sn_get_groups('governors')) || $cost[P_OPTIONS][P_ONLY_DARK_MATTER]) {\n    // Zero build time for Governors and other units with DM in price\n    $cost[RES_TIME][BUILD_CREATE] = $cost[RES_TIME][BUILD_DESTROY] = 0;\n  } else {\n    $cost[RES_TIME][BUILD_CREATE] = $cost[P_OPTIONS][P_TIME_RAW];\n\n    // Applying time modifiers\n    $cost[RES_TIME][BUILD_CREATE] /= BuildDataStatic::getCapitalTimeDivisor($user, $planet, $unit_id);\n    $cost[RES_TIME][BUILD_CREATE] /= BuildDataStatic::getMercenaryTimeDivisor($user, $planet, $unit_id);\n    $cost[RES_TIME][BUILD_CREATE] /= SN::$gc->pimp->getStructuresTimeDivisor($user, $planet, $unit_id, $unit_data);\n\n    // Final calculations\n    $cost[RES_TIME][BUILD_CREATE]  = $cost[RES_TIME][BUILD_CREATE] > 1 ? ceil($cost[RES_TIME][BUILD_CREATE]) : 1;\n    $cost[RES_TIME][BUILD_DESTROY] = $cost[RES_TIME][BUILD_CREATE] / 2 > 1 ? ceil($cost[RES_TIME][BUILD_CREATE] / 2) : 1;\n  }\n\n  return $cost;\n}\n\n\nfunction eco_can_build_unit($user, $planet, $unit_id) {\n  $result = null;\n\n  return sn_function_call('eco_can_build_unit', [$user, $planet, $unit_id, &$result]);\n}\n\nfunction sn_eco_can_build_unit($user, $planet, $unit_id, &$result) {\n  $result = isset($result) ? $result : BUILD_ALLOWED;\n  $result = $result == BUILD_ALLOWED && eco_unit_busy($user, $planet, $unit_id) ? BUILD_UNIT_BUSY : $result;\n\n  $unit_param = get_unit_param($unit_id);\n  if ($unit_param[P_UNIT_TYPE] != UNIT_MERCENARIES || !SN::$config->empire_mercenary_temporary) {\n    $requirement = &$unit_param[P_REQUIRE];\n    if ($result == BUILD_ALLOWED && $requirement) {\n      foreach ($requirement as $require_id => $require_level) {\n        if (mrc_get_level($user, $planet, $require_id) < $require_level) {\n          $result = BUILD_REQUIRE_NOT_MEET;\n          break;\n        }\n      }\n    }\n  }\n\n  return $result;\n}\n\nfunction eco_is_builds_in_que($planet_que, $unit_list) {\n  $eco_is_builds_in_que = false;\n\n  $unit_list  = is_array($unit_list) ? $unit_list : array($unit_list => $unit_list);\n  $planet_que = explode(';', $planet_que);\n  foreach ($planet_que as $planet_que_item) {\n    if ($planet_que_item) {\n      list($planet_que_item) = explode(',', $planet_que_item);\n      if (in_array($planet_que_item, $unit_list)) {\n        $eco_is_builds_in_que = true;\n        break;\n      }\n    }\n  }\n\n  return $eco_is_builds_in_que;\n}\n\nfunction eco_unit_busy(&$user, &$planet, $unit_id) {\n  $result = null;\n\n  return sn_function_call('eco_unit_busy', [&$user, &$planet, $unit_id, &$result]);\n}\n\nfunction sn_eco_unit_busy(&$user, &$planet, $unit_id, &$result) {\n  global $config;\n\n  $result = isset($result) ? $result : false;\n  if (!$result) {\n    if (($unit_id == STRUC_LABORATORY || $unit_id == STRUC_LABORATORY_NANO) && !$config->BuildLabWhileRun) {\n      $global_que = que_get($user['id'], $planet['id'], QUE_RESEARCH, false);\n      if (is_array($global_que['ques'][QUE_RESEARCH][$user['id']])) {\n        $first_element = reset($global_que['ques'][QUE_RESEARCH][$user['id']]);\n        if (is_array($first_element)) {\n          $result = true;\n        }\n      }\n    } elseif (($unit_id == UNIT_TECHNOLOGIES || in_array($unit_id, sn_get_groups('tech'))) && !$config->BuildLabWhileRun) {\n      $userId        = floatval($user['id']);\n      $isLabBuilding = doquery(\n        \"SELECT 1 FROM `{{que}}`\n        WHERE\n            `que_player_id` = {$userId}\n            AND `que_unit_id` IN (\" . STRUC_LABORATORY . \", \" . STRUC_LABORATORY_NANO . \")\", true);\n\n      $result = !empty($isLabBuilding);\n    }\n  }\n\n//  switch($unit_id)\n//  {\n//    case STRUC_FACTORY_HANGAR:\n//      $hangar_busy = $planet['b_hangar'] && $planet['b_hangar_id'];\n//      $return = $hangar_busy;\n//    break;\n//  }\n\n  return $result;\n}\n"
  },
  {
    "path": "includes/functions/eco_planet_update.php",
    "content": "<?php\n/** @noinspection PhpDeprecationInspection */\n/** @noinspection PhpUnnecessaryCurlyVarSyntaxInspection */\n/** @noinspection SqlResolve */\n\nuse DBAL\\db_mysql;\nuse Meta\\Economic\\ResourceCalculations;\nuse Planet\\DBStaticPlanet;\n\n/**\n * @param ?int       $userId\n * @param ?int|array $planet\n * @param int        $UpdateTime\n * @param bool       $simulation\n * @param bool       $no_user_update\n *\n * @return array[]|false[]|void\n */\nfunction sys_o_get_updated($userId, $planet, $UpdateTime, $simulation = false, $no_user_update = false) {\n  db_mysql::db_transaction_check(true);\n\n  $no_data = ['user' => false, 'planet' => false, 'que' => false];\n\n  if (!$planet) {\n    return $no_data;\n  }\n\n  if (!$no_user_update) {\n    if (!$userId) {\n      SN::$debug->error('sys_o_get_updated() - USER пустой!');\n      die();\n    }\n  }\n  $user = db_user_by_id($userId, !$simulation, true);\n\n  if (empty($user['id'])) {\n    return $no_data;\n  }\n\n  if (is_array($planet) && isset($planet['galaxy']) && $planet['galaxy']) {\n    $planet = DBStaticPlanet::db_planet_by_vector($planet, '');\n  } else {\n    $planet = intval(is_array($planet) && isset($planet['id']) ? $planet['id'] : $planet);\n    $planet = DBStaticPlanet::db_planet_by_id($planet, !$simulation);\n  }\n  if (!is_array($planet) || !isset($planet['id'])) {\n    return $no_data;\n  }\n\n  $que = que_process($user, $planet, $UpdateTime);\n\n  $ProductionTime        = max(0, $UpdateTime - $planet['last_update']);\n  $planet['prev_update'] = $planet['last_update'];\n  $planet['last_update'] += $ProductionTime;\n\n  // TODO ЭТО НАДО ДЕЛАТЬ ТОЛЬКО ПРИ СПЕЦУСЛОВИЯХ\n\n//  if ($ProductionTime > 0)\n  {\n    $capsObj = new ResourceCalculations();\n    $capsObj->eco_get_planet_caps($user, $planet, 3600);\n    $resources_increase = array(\n      RES_METAL     => 0,\n      RES_CRYSTAL   => 0,\n      RES_DEUTERIUM => 0,\n    );\n\n    switch ($planet['planet_type']) {\n      case PT_PLANET:\n        foreach ($resources_increase as $resource_id => &$increment) {\n          $resource_name = pname_resource_name($resource_id);\n\n          /** @noinspection SpellCheckingInspection */\n          $increment  = $planet[$resource_name . '_perhour'] * $ProductionTime / 3600;\n          $store_free = $planet[$resource_name . '_max'] - $planet[$resource_name];\n          $increment  = min($increment, max(0, $store_free));\n\n          if ($planet[$resource_name] + $increment < 0 && !$simulation) {\n            global $debug;\n            $debug->warning(\"Player ID {$user['id']} have negative resources on ID {$planet['id']}.{$planet['planet_type']} [{$planet['galaxy']}:{$planet['system']}:{$planet['planet']}]. Difference {$planet[$resource_name]} of {$resource_name}\", 'Negative Resources', 501);\n          }\n          $planet[$resource_name] += $increment;\n        }\n      break;\n\n      case PT_MOON:\n      default:\n        /** @noinspection SpellCheckingInspection */\n        $planet['metal_perhour']     = 0;\n        $planet['crystal_perhour']   = 0;\n        $planet['deuterium_perhour'] = 0;\n        $planet['energy_used']       = 0;\n        $planet['energy_max']        = 0;\n      break;\n    }\n\n    // TODO пересчитывать размер планеты только при постройке чего-нибудь и при покупке сектора\n    $planet['field_current'] = 0;\n    $sn_group_build_allow    = sn_get_groups('build_allow');\n    if (is_array($sn_group_build_allow[$planet['planet_type']])) {\n      foreach ($sn_group_build_allow[$planet['planet_type']] as $building_id) {\n        $planet['field_current'] += mrc_get_level($user, $planet, $building_id, !$simulation, true);\n      }\n    }\n\n    // Saving data if not a simulation\n    if (!$simulation) {\n      /** @noinspection SpellCheckingInspection */\n      DBStaticPlanet::db_planet_set_by_id($planet['id'],\n        \"`last_update` = '{$planet['last_update']}', `field_current` = {$planet['field_current']},\n    `metal` = `metal` + '{$resources_increase[RES_METAL]}', `crystal` = `crystal` + '{$resources_increase[RES_CRYSTAL]}', `deuterium` = `deuterium` + '{$resources_increase[RES_DEUTERIUM]}',\n    `metal_perhour` = '{$planet['metal_perhour']}', `crystal_perhour` = '{$planet['crystal_perhour']}', `deuterium_perhour` = '{$planet['deuterium_perhour']}',\n    `energy_used` = '{$planet['energy_used']}', `energy_max` = '{$planet['energy_max']}'\"\n      );\n    }\n  }\n\n  return array('user' => $user, 'planet' => $planet, 'que' => $que);\n}\n"
  },
  {
    "path": "includes/functions/eco_queue.php",
    "content": "<?php\n\nuse DBAL\\db_mysql;\nuse DBAL\\OldDbChangeSet;\nuse Planet\\DBStaticPlanet;\nuse Que\\DBStaticQue;\nuse Que\\QueUnitStatic;\n\nfunction que_get_unit_que($unit_id) {\n  $que_type = false;\n  foreach (sn_get_groups('ques') as $que_id => $que_data) {\n    if (in_array($unit_id, $que_data['unit_list'])) {\n      $que_type = $que_id;\n      break;\n    }\n  }\n\n  return $que_type;\n}\n\n\nfunction que_get_max_que_length($user, $planet, $que_id, $que_data = null) {\n  global $config;\n\n  if (empty($que_data)) {\n    $que_data = sn_get_groups('ques');\n    $que_data = $que_data[$que_id];\n  }\n\n\n  $que_length = 1;\n  switch ($que_id) {\n    case QUE_RESEARCH:\n      $que_length = $config->server_que_length_research + mrc_get_level($user, '', UNIT_PREMIUM); // TODO - вынести в модуль\n    break;\n\n    default:\n      $que_length = isset($que_data['length']) ? $que_data['length'] + mrc_get_level($user, $planet, $que_data['mercenary']) : 1;\n  }\n\n  return $que_length;\n}\n\nfunction eco_que_str2arr($que_str) {\n  $que_arr = explode(';', $que_str);\n  foreach ($que_arr as $que_index => &$que_item) {\n    if ($que_item) {\n      $que_item = explode(',', $que_item);\n    } else {\n      unset($que_arr[$que_index]);\n    }\n  }\n\n  return $que_arr;\n}\n\nfunction eco_que_arr2str($que_arr) {\n  foreach ($que_arr as &$que_item) {\n    $que_item = implode(',', $que_item);\n  }\n\n  return implode(';', $que_arr);\n}\n\n\nfunction que_build($user, $planet, $build_mode = BUILD_CREATE, $redirect = true) {\n  global $lang, $config;\n\n  $is_autoconvert = false;\n  if ($build_mode == BUILD_AUTOCONVERT || sys_get_param_int('auto_convert')) {\n    $build_mode     = BUILD_CREATE;\n    $is_autoconvert = true;\n  }\n\n  $unit_amount_qued = 0;\n  try {\n    if (!$user['id']) {\n      throw new exception('{Нет идентификатора пользователя - сообщите Администрации}', ERR_ERROR); // TODO EXCEPTION\n    }\n\n    $unit_id = sys_get_param_int('unit_id');\n    /*\n    if(!$unit_id && is_array($unit_list = sys_get_param('fmenge')))\n    {\n      foreach($unit_list as $unit_id => $unit_amount) if($unit_amount) break;\n    }\n    */\n    if (!$unit_id) {\n      throw new exception('{Нет идентификатора юнита - сообщите Администрации}', ERR_ERROR); // TODO EXCEPTION\n    }\n\n    $que_id = que_get_unit_que($unit_id);\n    if (!$que_id) {\n      throw new exception('{Неправильный тип очереди - сообщите Администрации}', ERR_ERROR); // TODO EXCEPTION\n    }\n\n    if ($build_mode == BUILD_DESTROY && $que_id != QUE_STRUCTURES) {\n      throw new exception('{Уничтожать можно только здания на планете}', ERR_ERROR); // TODO EXCEPTION\n    }\n\n    $que_data = sn_get_groups('ques');\n    $que_data = $que_data[$que_id];\n\n    // TODO Переделать под подочереди\n    if ($que_id == QUE_STRUCTURES) {\n      $sn_groups_build_allow = sn_get_groups('build_allow');\n      $que_data['unit_list'] = $sn_groups_build_allow[$planet['planet_type']];\n\n      if (!isset($que_data['unit_list'][$unit_id])) {\n        throw new exception('{Это здание нельзя строить на ' . ($planet['planet_type'] == PT_PLANET ? 'планете' : 'луне'), ERR_ERROR); // TODO EXCEPTION\n      }\n    }\n    /*\n    // TODO Разделить очереди для Верфи и Обороны\n    elseif($que_id == QUE_HANGAR)\n    {\n      $que_data['mercenary'] = in_array($unit_id, sn_get_groups('defense')) ? MRC_FORTIFIER : MRC_ENGINEER;\n    }\n    elseif($que_id == QUE_HANGAR)\n    {\n      $que_data['mercenary'] = in_array($unit_id, sn_get_groups('defense')) ? MRC_FORTIFIER : MRC_ENGINEER;\n    }\n    */\n\n\n    db_mysql::db_transaction_start();\n    // Это нужно, что бы заблокировать пользователя и работу с очередями\n    $user = db_user_by_id($user['id']);\n    // Это нужно, что бы заблокировать планету от списания ресурсов\n    if (isset($planet['id']) && $planet['id']) {\n      $planet = DBStaticPlanet::db_planet_by_id($planet['id'], true);\n    } else {\n      $planet['id'] = 0;\n    }\n\n    $planet_id = $que_id == QUE_RESEARCH ? 0 : intval($planet['id']);\n\n    $que            = que_get($user['id'], $planet['id'], $que_id, true);\n    $in_que         = &$que['in_que'][$que_id][$user['id']][$planet_id];\n    $que_max_length = que_get_max_que_length($user, $planet, $que_id, $que_data);\n    // TODO Добавить вызовы функций проверок текущей и максимальной длин очередей\n    if ((empty($in_que) ? 0 : count($in_que)) >= $que_max_length) {\n      throw new exception('{Все слоты очереди заняты}', ERR_ERROR); // TODO EXCEPTION\n    }\n\n    // TODO Отдельно посмотреть на уничтожение зданий - что бы можно было уничтожать их без планов\n    switch (eco_can_build_unit($user, $planet, $unit_id)) {\n      case BUILD_ALLOWED:\n      break;\n      case BUILD_UNIT_BUSY:\n        throw new exception('{Строение занято}', ERR_ERROR);\n      break; // TODO EXCEPTION eco_bld_msg_err_laboratory_upgrading\n      // case BUILD_REQUIRE_NOT_MEET:\n      default:\n        if ($build_mode == BUILD_CREATE) {\n          throw new exception('{Требования не удовлетворены}', ERR_ERROR);\n        }\n      break; // TODO EXCEPTION eco_bld_msg_err_requirements_not_meet\n    }\n\n    $unit_amount      = floor(sys_get_param_float('unit_amount', 1));\n    $unit_amount_qued = $unit_amount;\n    $units_qued       = isset($in_que[$unit_id]) ? $in_que[$unit_id] : 0;\n    $unit_level       = mrc_get_level($user, $planet, $unit_id, true, true) + $units_qued;\n    if ($unit_max = get_unit_param($unit_id, P_MAX_STACK)) {\n      if ($unit_level >= $unit_max) {\n        throw new exception('{Максимальное количество юнитов данного типа уже достигнуто или будет достигнуто по окончанию очереди}', ERR_ERROR); // TODO EXCEPTION\n      }\n      $unit_amount = max(0, min($unit_amount, $unit_max - $unit_level));\n    }\n\n    if ($unit_amount < 1) {\n      throw new exception('{Неправильное количество юнитов - сообщите Администрации}', ERR_ERROR); // TODO EXCEPTION\n    }\n\n    /*\n    if($unit_max && $unit_level + $unit_amount > $unit_max)\n    {\n      throw new exception(\"Постройка {$unit_amount} {$lang['tech'][$unit_id]} приведет к привышению максимально возможного количества юнитов данного типа\", ERR_ERROR); // TODO EXCEPTION\n    }\n    */\n\n    // TODO Переделать eco_unit_busy для всех типов зданий\n    //  if(eco_unit_busy($user, $planet, $que, $unit_id))\n    //  {\n    //    die('Unit busy'); // TODO EXCEPTION\n    //  }\n    if (get_unit_param($unit_id, P_STACKABLE)) {\n      // TODO Поле 'max_Lot_size' для ограничения размера стэка в очереди - то ли в юниты, то ли в очередь\n      if (in_array($unit_id, $group_missile = sn_get_groups('missile'))) {\n        // TODO Поле 'container' - указывает на родительску структуру, в которой хранится данный юнит и по вместительности которой нужно применять размер юнита\n        $used_silo = 0;\n        foreach ($group_missile as $missile_id) {\n          $missile_qued = isset($in_que[$missile_id]) ? $in_que[$missile_id] : 0;\n          $used_silo    += (mrc_get_level($user, $planet, $missile_id, true, true) + $missile_qued) * get_unit_param($missile_id, P_UNIT_SIZE);\n        }\n        $free_silo = mrc_get_level($user, $planet, STRUC_SILO) * get_unit_param(STRUC_SILO, P_CAPACITY) - $used_silo;\n        if ($free_silo <= 0) {\n          throw new exception('{Ракетная шахта уже заполнена или будет заполнена по окончанию очереди}', ERR_ERROR); // TODO EXCEPTION\n        }\n        $unit_size = get_unit_param($unit_id, P_UNIT_SIZE);\n        if ($free_silo < $unit_size) {\n          throw new exception(\"{В ракетной шахте нет места для {$lang['tech'][$unit_id]}}\", ERR_ERROR); // TODO EXCEPTION\n        }\n        $unit_amount = max(0, min($unit_amount, floor($free_silo / $unit_size)));\n      }\n      $unit_level = $new_unit_level = 0;\n    } else {\n      $unit_amount = 1;\n      if ($que_id == QUE_STRUCTURES) {\n        // if($build_mode == BUILD_CREATE && eco_planet_fields_max($planet) - $planet['field_current'] - $que['sectors'][$planet['id']] <= 0)\n        $sectors_qued = is_array($in_que) ? array_sum($in_que) : 0;\n        if ($build_mode == BUILD_CREATE && eco_planet_fields_max($planet) - $planet['field_current'] - $sectors_qued <= 0) {\n          throw new exception('{Не хватает секторов на планете}', ERR_ERROR); // TODO EXCEPTION\n        }\n        // И что это я такое написал? Зачем?\n        //if($build_mode == BUILD_DESTROY && $planet['field_current'] <= $que['amounts'][$que_id])\n        //{\n        //  die('Too much buildings'); // TODO EXCEPTION\n        //}\n      }\n      $build_multiplier = $build_mode == BUILD_CREATE ? 1 : -1;\n      $new_unit_level   = $unit_level + $unit_amount * $build_multiplier;\n    }\n\n    $build_data = eco_get_build_data($user, $planet, $unit_id, $unit_level);\n\n    $exchange                    = array();\n    $market_get_autoconvert_cost = market_get_autoconvert_cost();\n    if ($is_autoconvert && $build_data[BUILD_AUTOCONVERT]) {\n      $dark_matter = mrc_get_level($user, null, RES_DARK_MATTER);\n      if (mrc_get_level($user, null, RES_DARK_MATTER) < $market_get_autoconvert_cost) {\n        throw new exception(\"{Нет хватает \" . ($market_get_autoconvert_cost - $dark_matter) . \"ТМ на постройки с автоконвертацией ресурсов}\", ERR_ERROR); // TODO EXCEPTION\n      }\n\n      !get_unit_param($unit_id, P_STACKABLE) ? $unit_amount = 1 : false;\n      $resources_loot          = sn_get_groups('resources_loot');\n      $resource_got            = array();\n      $resource_exchange_rates = array();\n      $resource_diff           = array();\n      $all_positive            = true;\n      foreach ($resources_loot as $resource_id) {\n        $resource_db_name                      = pname_resource_name($resource_id);\n        $resource_got[$resource_id]            = floor(mrc_get_level($user, $planet, $resource_id));\n        $resource_exchange_rates[$resource_id] = $config->__get(\"rpg_exchange_{$resource_db_name}\");\n        $resource_diff[$resource_id]           = $resource_got[$resource_id] - $build_data[BUILD_CREATE][$resource_id] * $unit_amount;\n        $all_positive                          = $all_positive && ($resource_diff[$resource_id] > 0);\n      }\n      // Нужна автоконвертация\n      if ($all_positive) {\n        $is_autoconvert = false;\n      } else {\n        foreach ($resource_diff as $resource_diff_id => &$resource_diff_amount) {\n          if ($resource_diff_amount >= 0) {\n            continue;\n          }\n          foreach ($resource_diff as $resource_got_id => &$resource_got_amount) {\n            if ($resource_got_amount <= 0) {\n              continue;\n            }\n            $current_exchange = $resource_exchange_rates[$resource_got_id] / $resource_exchange_rates[$resource_diff_id];\n\n            $will_exchage_to   = min(-$resource_diff_amount, floor($resource_got_amount * $current_exchange));\n            $will_exchage_from = $will_exchage_to / $current_exchange;\n\n            $resource_diff_amount        += $will_exchage_to;\n            $resource_got_amount         -= $will_exchage_from;\n            $exchange[$resource_diff_id] += $will_exchage_to;\n            $exchange[$resource_got_id]  -= $will_exchage_from;\n          }\n        }\n\n        $is_autoconvert_ok = true;\n        foreach ($resource_diff as $resource_diff_amount2) {\n          if ($resource_diff_amount2 < 0) {\n            $is_autoconvert_ok = false;\n            break;\n          }\n        }\n\n        if ($is_autoconvert_ok) {\n          $build_data['RESULT'][$build_mode] = BUILD_ALLOWED;\n          $build_data['CAN'][$build_mode]    = $unit_amount;\n        } else {\n          $unit_amount = 0;\n        }\n      }\n    }\n    $unit_amount = min($build_data['CAN'][$build_mode], $unit_amount);\n    if ($unit_amount <= 0) {\n      throw new exception('{Не хватает ресурсов}', ERR_ERROR); // TODO EXCEPTION\n    }\n\n    if ($new_unit_level < 0) {\n      throw new exception('{Нельзя уничтожить больше юнитов, чем есть}', ERR_ERROR); // TODO EXCEPTION\n    }\n\n    if ($build_data['RESULT'][$build_mode] != BUILD_ALLOWED) {\n      throw new exception('{Строительство блокировано}', ERR_ERROR); // TODO EXCEPTION\n    }\n\n    if ($is_autoconvert) {\n      ksort($exchange);\n      ksort($resource_got);\n      db_change_units($user, $planet, array(\n        RES_METAL     => !empty($exchange[RES_METAL]) ? $exchange[RES_METAL] : 0,\n        RES_CRYSTAL   => !empty($exchange[RES_CRYSTAL]) ? $exchange[RES_CRYSTAL] : 0,\n        RES_DEUTERIUM => !empty($exchange[RES_DEUTERIUM]) ? $exchange[RES_DEUTERIUM] : 0,\n      ));\n      rpg_points_change($user['id'], RPG_BUILD_AUTOCONVERT, -$market_get_autoconvert_cost, sprintf(\n        $lang['bld_autoconvert'], $unit_id, $unit_amount, uni_render_planet_full($planet, '', false, true), $lang['tech'][$unit_id],\n        sys_unit_arr2str($build_data[BUILD_CREATE]), sys_unit_arr2str($resource_got), sys_unit_arr2str($exchange)\n      ));\n    }\n\n    $unit_amount_qued = 0;\n    while (\n      $unit_amount > 0\n      &&\n      (empty($que['ques'][$que_id][$user['id']][$planet_id]) ? 0 : count($que['ques'][$que_id][$user['id']][$planet_id])) < $que_max_length\n    ) {\n      $place = min($unit_amount, MAX_FLEET_OR_DEFS_PER_ROW);\n//      $sqlBlock = QueUnitStatic::que_unit_make_sql($unit_id, $user, $planet, $build_data, $new_unit_level, $place, $build_mode);\n      $sqlBlock = SN::$gc->pimp->que_unit_make_sql($unit_id, $user, $planet, $build_data, $new_unit_level, $place, $build_mode);\n\n      array_walk($sqlBlock, function (&$value, $field) {\n        if ($value === null) {\n          $value = 'NULL';\n        } elseif (is_string($value)) {\n          $value = \"'{$value}'\";\n        }\n\n        $value = \"`{$field}` = {$value}\";\n      });\n\n      DBStaticQue::db_que_set_insert(implode(',', $sqlBlock)\n//        \"`que_player_id` = {$user['id']},\n//      `que_planet_id` = {$planet_id},\n//      `que_planet_id_origin` = {$planet_id_origin},\n//      `que_type` = {$que_type},\n//      `que_time_left` = {$build_data[RES_TIME][$build_mode]},\n//      `que_unit_id` = {$unit_id},\n//      `que_unit_amount` = {$unit_amount},\n//      `que_unit_mode` = {$build_mode},\n//      `que_unit_level` = {$unit_level},\n//      `que_unit_time` = {$build_data[RES_TIME][$build_mode]},\n//      `que_unit_price` = '{$resource_list}'\"\n      );\n\n\n      $unit_amount      -= $place;\n      $que              = que_get($user['id'], $planet['id'], $que_id, true);\n      $unit_amount_qued += $place;\n    }\n\n    db_mysql::db_transaction_commit();\n\n    if ($redirect) {\n      sys_redirect(\"{$_SERVER['PHP_SELF']}?mode=\" . sys_get_param_str('mode') . \"&ally_id=\" . sys_get_param_id('ally_id'));\n    }\n\n    $operation_result = array(\n      'STATUS'  => ERR_NONE,\n      'MESSAGE' => '{Строительство начато}',\n    );\n  } catch (exception $e) {\n    db_mysql::db_transaction_rollback();\n    $operation_result = array(\n      'STATUS'  => in_array($e->getCode(), array(ERR_NONE, ERR_WARNING, ERR_ERROR)) ? $e->getCode() : ERR_ERROR,\n      'MESSAGE' => $e->getMessage()\n    );\n  }\n\n  if (!empty($operation_result['MESSAGE'])) {\n    $operation_result['MESSAGE'] .= ' ' . ($unit_amount_qued ? $unit_amount_qued : $unit_amount) . 'x[' . $lang['tech'][$unit_id] . ']';\n  }\n\n  return $operation_result;\n}\n\n\nfunction que_recalculate($old_que) {\n  $new_que = array();\n\n  if (!is_array($old_que['items'])) {\n    return $new_que;\n  }\n  foreach ($old_que['items'] as $row) {\n    if (!isset($row) || !$row || $row['que_unit_amount'] <= 0) {\n      continue;\n    }\n\n    $new_que['items'][] = $row;\n\n    $new_que['in_que'][$row['que_type']][$row['que_player_id']][intval($row['que_planet_id'])][$row['que_unit_id']]     += $row['que_unit_amount'] * ($row['que_unit_mode'] == BUILD_CREATE ? 1 : -1);\n    $new_que['in_que_abs'][$row['que_type']][$row['que_player_id']][intval($row['que_planet_id'])][$row['que_unit_id']] += $row['que_unit_amount'];\n\n    $last_id = count($new_que['items']) - 1;\n\n    if ($row['que_planet_id']) {\n      $new_que['planets'][$row['que_planet_id']][$row['que_type']][] = &$new_que['items'][$last_id];\n    } elseif ($row['que_type'] == QUE_RESEARCH) {\n      $new_que['players'][$row['que_player_id']][$row['que_type']][] = &$new_que['items'][$last_id];\n    }\n    $new_que['ques'][$row['que_type']][$row['que_player_id']][intval($row['que_planet_id'])][] = &$new_que['items'][$last_id];\n\n    // Это мы можем посчитать по длине очереди в players и planets\n    //$ques['used_slots'][$row['que_type']][$row['que_player_id']][intval($row['que_planet_id'])][$row['que_unit_id']]++;\n  }\n\n  return $new_que;\n}\n\nfunction que_get($user_id, $planet_id = null, $que_type = false, $for_update = false) {\n  return SN::db_que_list_by_type_location($user_id, $planet_id, $que_type, $for_update);\n}\n\nfunction que_delete($que_type, $user = array(), $planet = array(), $clear = false) {\n  $planets_locked = array();\n\n  // TODO: Some checks\n  db_mysql::db_transaction_start();\n  $user         = db_user_by_id($user['id'], true);\n  $planet['id'] = $planet['id'] && $que_type !== QUE_RESEARCH ? $planet['id'] : 0;\n  $global_que   = que_get($user['id'], $planet['id'], $que_type, true);\n\n  if (!empty($global_que['ques'][$que_type][$user['id']][$planet['id']])) {\n    $que = array_reverse($global_que['ques'][$que_type][$user['id']][$planet['id']]);\n\n    foreach ($que as $que_item) {\n      DBStaticQue::db_que_delete_by_id($que_item['que_id']);\n\n      if ($que_item['que_planet_id_origin']) {\n        $planet['id'] = $que_item['que_planet_id_origin'];\n      }\n\n      if (!isset($planets_locked[$planet['id']])) {\n        $planets_locked[$planet['id']] = $planet['id'] ? DBStaticPlanet::db_planet_by_id($planet['id'], true) : $planet;\n      }\n\n      $build_data = sys_unit_str2arr($que_item['que_unit_price']);\n\n      db_change_units($user, $planets_locked[$planet['id']], array(\n        RES_METAL     => $build_data[RES_METAL] * $que_item['que_unit_amount'],\n        RES_CRYSTAL   => $build_data[RES_CRYSTAL] * $que_item['que_unit_amount'],\n        RES_DEUTERIUM => $build_data[RES_DEUTERIUM] * $que_item['que_unit_amount'],\n      ));\n\n      if (!$clear) {\n        break;\n      }\n    }\n\n    if (is_numeric($planet['id'])) {\n      DBStaticPlanet::db_planet_set_by_id($planet['id'], \"`que_processed` = UNIX_TIMESTAMP(NOW())\");\n    } elseif (is_numeric($user['id'])) {\n      db_user_set_by_id($user['id'], '`que_processed` = UNIX_TIMESTAMP(NOW())');\n    }\n\n    db_mysql::db_transaction_commit();\n  } else {\n    db_mysql::db_transaction_rollback();\n  }\n\n  sys_redirect(\"{$_SERVER['PHP_SELF']}?mode={$que_type}\" . \"&ally_id=\" . sys_get_param_id('ally_id'));\n}\n\n\nfunction que_tpl_parse_element($que_element, $short_names = false) {\n  global $lang;\n\n  return\n    array(\n      'ID'        => $que_element['que_unit_id'],\n      'QUE'       => $que_element['que_type'],\n      'NAME'      => $short_names && !empty($lang['tech_short'][$que_element['que_unit_id']])\n        ? $lang['tech_short'][$que_element['que_unit_id']]\n        : $lang['tech'][$que_element['que_unit_id']],\n      'TIME'      => $que_element['que_time_left'],\n      'TIME_FULL' => $que_element['que_unit_time'],\n      'AMOUNT'    => $que_element['que_unit_amount'],\n      'LEVEL'     => $que_element['que_unit_level'],\n    );\n}\n\n/*\n *\n * Процедура парсит очереди текущего игрока в темплейт\n *\n * TODO: Переместить в хелперы темплейтов\n * TODO: Сделать поддержку нескольких очередей\n *\n * $que_type - тип очереди ОБЯЗАТЕЛЬНО\n * $que - либо результат $que_get(), либо конкретная очередь\n *\n */\nfunction que_tpl_parse(&$template, $que_type, $user, $planet = array(), $que = null, $short_names = false) {\n  // TODO: Переделать для $que_type === false\n  $planet['id'] = $planet['id'] ? $planet['id'] : 0;\n\n  if (!is_array($que)) {\n    $que = que_get($user['id'], $planet['id'], $que_type);\n  }\n\n  if (is_array($que) && isset($que['items'])) {\n    $que = $que['ques'][$que_type][$user['id']][$planet['id']];\n  }\n\n  if ($que) {\n    foreach ($que as $que_element) {\n      $template->assign_block_vars('que', que_tpl_parse_element($que_element, $short_names));\n    }\n  }\n\n  if ($que_type == QUE_RESEARCH) {\n    // TODO Исправить\n//    $template->assign_var('RESEARCH_ONGOING', count($global_que[QUE_RESEARCH][0]) >= $config->server_que_length_research);\n  }\n}\n\n\n/*\n *\n * Эта процедура должна вызываться исключительно в транзакции!!!\n *\n * $user_id\n *   (integer) - обрабатываются глообальные очереди пользователя\n * $planet_id\n *   null - обработка очередей планет не производится\n * // TODO    false/0 - обрабатываются очереди всех планет по $user_id\n *   (integer) - обрабатываются локальные очереди для планеты. Нужно, например, в обработчике флотов\n *\n */\nfunction que_process(&$user, $planet = null, $on_time = SN_TIME_NOW) {\n  db_mysql::db_transaction_check(true);\n\n  $que = array();\n\n  // Блокируем пользователя. Собственно, запись о нём нам не нужна - будем использовать старую\n  $user = db_user_by_id($user['id'], true);\n\n  $time_left[$user['id']][0] = max(0, $on_time - $user['que_processed']);\n  if ($planet === null && !$time_left[$user['id']][0]) {\n    // TODO\n    return $que;\n  }\n\n  // Определяем, какие очереди нам нужны и получаем их\n  $que_type_id = $planet === null ? QUE_RESEARCH : false;\n  $planet      = intval(is_array($planet) ? $planet['id'] : $planet); // В $planet у нас теперь только её ID или шаблон null/0/false\n  $que         = que_get($user['id'], $planet, $que_type_id, true);\n  if (empty($que['items'])) {\n    return $que;\n  }\n\n  $planet_list = array();\n  if ($planet !== null) {\n    // Если нужно изменять данные на планетах - блокируем планеты и получаем данные о них\n    // TODO - от них не надо ничего, кроме ID и que_processed\n    $planet_row                                            = DBStaticPlanet::db_planet_list_by_user_or_planet($user['id'], $planet);\n    $planet_list[$planet_row['id']]                        = $planet_row;\n    $time_left[$planet_row['id_owner']][$planet_row['id']] = max(0, $on_time - $planet_row['que_processed']);\n  }\n\n  // Теперь в $time_left лежит время обсчета всех очередей по каждой из планеты\n  if (array_sum($time_left[$user['id']]) == 0) {\n    return $que;\n  }\n\n  $db_changeset = array();\n  $unit_changes = array();\n  foreach ($que['items'] as &$que_item) {\n    $que_player_id = &$que_item['que_player_id'];\n    $que_planet_id = intval($que_item['que_planet_id']);\n\n    $que_time_left = &$que['time_left'][$que_player_id][$que_planet_id][$que_item['que_type']];\n    if (!isset($que_time_left)) {\n      $que_time_left = $time_left[$que_player_id][$que_planet_id];\n    }\n    if ($que_time_left <= 0 || $que_item['que_unit_amount'] <= 0) {\n      continue;\n    }\n    // Дальше мы идем, если только осталось время в очереди И юниты к постройке\n\n    // Вычисляем, сколько целых юнитов будет построено - от 0 до количества юнитов в очереди\n    $unit_processed = min($que_item['que_unit_amount'] - 1, floor($que_time_left / $que_item['que_unit_time']));\n    // Вычитаем это время из остатков\n    $que_time_left -= $unit_processed * $que_item['que_unit_time'];\n\n    // Теперь работаем с остатком времени на юните. Оно не может быть равно или меньше нуля\n\n    // Если времени в очереди осталось не меньше, чем время текущего юнита - значит мы достроили юнит\n    if ($que_time_left >= $que_item['que_time_left']) {\n      // Увеличиваем количество отстроенных юнитов\n      $unit_processed++;\n      // Вычитаем из времени очереди потраченное на постройку время\n      $que_time_left -= $que_item['que_time_left'];\n      // Полное время юнита равно времени нового юнита\n      $que_item['que_time_left'] = $que_item['que_unit_time'];\n      // Тут у нас может остатся время очереди - если постройка была не последняя\n    }\n    // Изменяем количество оставшихся юнитов\n    $que_item['que_unit_amount'] -= $unit_processed;\n\n    // Если еще остались юниты - значит ВСЁ оставшееся время приходится на достройку следующего юнита\n    if ($que_item['que_unit_amount'] > 0) {\n      $que_item['que_time_left'] = $que_item['que_time_left'] - $que_time_left;\n      $que_time_left             = 0;\n    }\n\n    if ($que_item['que_unit_amount'] <= 0) {\n      $db_changeset['que'][] = array(\n        'action'  => SQL_OP_DELETE,\n        P_VERSION => 1,\n        'where'   => array(\n          \"que_id\" => $que_item['que_id'],\n        ),\n      );\n    } else {\n      $db_changeset['que'][] = array(\n        'action'  => SQL_OP_UPDATE,\n        P_VERSION => 1,\n        'where'   => array(\n          \"que_id\" => $que_item['que_id'],\n        ),\n        'fields'  => array(\n          'que_unit_amount' => array(\n            'delta' => -$unit_processed\n          ),\n          'que_time_left'   => array(\n            'set' => $que_item['que_time_left']\n          ),\n        ),\n      );\n    }\n\n    if ($unit_processed) {\n      $unit_processed_delta                                                   = $unit_processed * ($que_item['que_unit_mode'] == BUILD_CREATE ? 1 : -1);\n      $unit_changes[$que_player_id][$que_planet_id][$que_item['que_unit_id']] += $unit_processed_delta;\n    }\n  }\n\n  foreach ($time_left as $player_id => $planet_data) {\n    foreach ($planet_data as $planet_id => $time_on_planet) {\n      $table                  = $planet_id ? 'planets' : 'users';\n      $id                     = $planet_id ? $planet_id : $player_id;\n      $db_changeset[$table][] = array(\n        'action'  => SQL_OP_UPDATE,\n        P_VERSION => 1,\n        'where'   => array(\n          \"id\" => $id,\n        ),\n        'fields'  => array(\n          'que_processed' => array(\n            'set' => $on_time,\n          ),\n        ),\n      );\n\n      if (is_array($unit_changes[$player_id][$planet_id])) {\n        foreach ($unit_changes[$player_id][$planet_id] as $unit_id => $unit_amount) {\n          $db_changeset['unit'][] = OldDbChangeSet::db_changeset_prepare_unit($unit_id, $unit_amount, $user, $planet_id ? $planet_id : null);\n        }\n      }\n    }\n  }\n\n  $que = que_recalculate($que);\n\n  // TODO: Re-enable quests for Alliances\n  if (!empty($unit_changes) && !$user['user_as_ally']) {\n    $quest_list     = qst_get_quests($user['id'], QUEST_STATUS_ALL);\n    $quest_triggers = qst_active_triggers($quest_list);\n    $quest_rewards  = array();\n    $quest_statuses = array();\n\n\n    $xp_incoming = array();\n    foreach ($unit_changes as $user_id => $planet_changes) {\n      foreach ($planet_changes as $planet_id => $changes) {\n        $planet_this = $planet_id ? SN::db_get_record_by_id(LOC_PLANET, $planet_id) : array();\n        foreach ($changes as $unit_id => $unit_value) {\n          $que_id         = que_get_unit_que($unit_id);\n          $unit_level_new = mrc_get_level($user, $planet_this, $unit_id, false, true) + $unit_value;\n          if ($que_id == QUE_STRUCTURES || $que_id == QUE_RESEARCH) {\n            $build_data = eco_get_build_data($user, $planet_this, $unit_id, $unit_level_new - 1);\n            $build_data = $build_data[BUILD_CREATE];\n            foreach (sn_get_groups('resources_loot') as $resource_id) {\n              $xp_incoming[$que_id] += $build_data[$resource_id]; // TODO - добавить конверсию рейтов обмена\n            }\n          }\n\n          if (is_array($quest_triggers)) {\n            // TODO: Check mutiply condition quests\n            $quest_trigger_list = array_keys($quest_triggers, $unit_id);\n\n            if (is_array($quest_trigger_list)) {\n              foreach ($quest_trigger_list as $quest_id) {\n                if ($quest_list[$quest_id]['quest_status_status'] != QUEST_STATUS_COMPLETE) {\n                  if ($quest_list[$quest_id]['quest_unit_amount'] <= $unit_level_new) {\n                    $quest_rewards[$quest_id][$user_id][$planet_id] = $quest_list[$quest_id]['quest_rewards_list'];\n                    $quest_statuses[$quest_id]                      = QUEST_STATUS_COMPLETE;\n                  } else {\n                    $quest_statuses[$quest_id] = QUEST_STATUS_STARTED;\n                  }\n                }\n              }\n            }\n          }\n        }\n      }\n    }\n    // TODO: Изменить начисление награды за квесты на ту планету, на которой происходил ресеч\n    qst_reward($user, $quest_rewards, $quest_list, $quest_statuses);\n\n    foreach ($xp_incoming as $que_id => $xp) {\n      rpg_level_up($user, $que_id == QUE_RESEARCH ? RPG_TECH : RPG_STRUCTURE, $xp / 1000);\n    }\n  }\n\n  OldDbChangeSet::db_changeset_apply($db_changeset);\n\n  // TODO Сообщения о постройке\n\n  return $que;\n}\n"
  },
  {
    "path": "includes/functions/flt_mission_missile.php",
    "content": "<?php\n// Copyright (c) 2009-2012 by Gorlum for http://supernova.ws\n// Date 2009-08-08\n// Open Source\n// V1\n//\nuse DBAL\\db_mysql;\nuse DBAL\\OldDbChangeSet;\nuse Planet\\DBStaticPlanet;\n\nfunction COE_missileAttack($defenceTech, $attackerTech, $MIPs, $structures, $targetedStructure = '0') {\n  // Here we select which part of defense should take damage: structure or shield\n  // $damageTo = P_SHIELD;\n  // $damageTo = P_STRUCTURE;\n  $damageTo = P_DEFENSE;\n\n  $mip_data  = get_unit_param(UNIT_DEF_MISSILE_INTERPLANET);\n  $MIPDamage = floor(mrc_modify_value($attackerTech, false, TECH_WEAPON, $MIPs * $mip_data[P_ATTACK] * mt_rand(80, 120) / 100));\n  foreach ($structures as $key => $structure) {\n    $unit_info                     = get_unit_param($key);\n    $amplify                       = isset($mip_data[P_AMPLIFY][$key]) ? $mip_data[P_AMPLIFY][$key] : 1;\n    $structures[$key][P_SHIELD]    = floor(mrc_modify_value($defenceTech, false, TECH_SHIELD, $unit_info[P_SHIELD]) / $amplify);\n    $structures[$key][P_STRUCTURE] = floor(mrc_modify_value($defenceTech, false, TECH_ARMOR, $unit_info[P_ARMOR]) / $amplify);\n    $structures[$key][P_DEFENSE]   = floor((\n        mrc_modify_value($defenceTech, false, TECH_ARMOR, $unit_info[P_ARMOR]) +\n        mrc_modify_value($defenceTech, false, TECH_SHIELD, $unit_info[P_SHIELD])\n      ) / $amplify * mt_rand(80, 120) / 100);\n  }\n\n  $startStructs = $structures;\n\n  if ($targetedStructure) {\n    //attacking only selected structure\n    $damageDone                        = $structures[$targetedStructure][$damageTo];\n    $structsDestroyed                  = min(floor($MIPDamage / $damageDone), $structures[$targetedStructure][0]);\n    $structures[$targetedStructure][0] -= $structsDestroyed;\n    $MIPDamage                         -= $structsDestroyed * $damageDone;\n  } else {\n    // REALLY random attack\n    $can_be_damaged = sn_get_groups('defense_active');\n//debug($structures);\n//debug($can_be_damaged);\n    do {\n      // finding is there any structure that can be damaged with leftovers of $MIPDamage\n      foreach ($can_be_damaged as $key => $unit_id) {\n//debug($structures[$unit_id][0]);\n//debug($structures[$unit_id][$damageTo], $MIPDamage);\n        if ($structures[$unit_id][0] <= 0 || $structures[$unit_id][$damageTo] > $MIPDamage) {\n          unset($can_be_damaged[$key]);\n        }\n      }\n      if (empty($can_be_damaged)) {\n        break;\n      }\n      sort($can_be_damaged);\n//debug($can_be_damaged, 'can be damaged');\n      $random_defense = mt_rand(0, count($can_be_damaged) - 1);\n//debug($can_be_damaged[$random_defense], 'Target');\n      $current_target = &$structures[$can_be_damaged[$random_defense]];\n//debug($current_target[0], 'Amount was');\n      $can_be_destroyed = min($current_target[0], floor($MIPDamage / $current_target[$damageTo]));\n//debug($MIPDamage, 'MIPDamage');\n//debug($can_be_destroyed, 'Can be destroyed');\n      $destroyed         = mt_rand(1, $can_be_destroyed);\n      $MIPDamage         -= $current_target[$damageTo] * $destroyed;\n      $current_target[0] -= $destroyed;\n//debug($destroyed, 'Actually destroyed');\n\n//print('<hr>');\n    } while ($MIPDamage > 0 && !empty($can_be_damaged));\n//debug($MIPDamage, 'MIPDamage left');\n  }\n//debug($structures);//die();\n  // 1/2 of metal and 1/4 of crystal of destroyed structures returns to planet\n  $metal   = 0;\n  $crystal = 0;\n  foreach ($structures as $key => $structure) {\n    $unit_info = get_unit_param($key);\n    $destroyed = $startStructs[$key][0] - $structure[0];\n    $metal     += $destroyed * $unit_info[P_COST][RES_METAL] / 2;\n    $crystal   += $destroyed * $unit_info[P_COST][RES_CRYSTAL] / 4;\n  }\n\n  $return['structures'] = $structures;     // Structures left after attack\n  $return['metal']      = floor($metal);   // Metal scraps\n  $return['crystal']    = floor($crystal); // Crystal scraps\n\n  return $return;\n}\n\n// Copyright (c) 2009-2010 by Gorlum for http://supernova.ws\n// Date 2009-08-08\n// Open Source\n\n/**\n * Copyright (c) 2009-2010 by Gorlum for http://supernova.ws\n *       OpenSource as long as you don't remove this Copyright\n * V3 2009-11-13\n * V2 2009-10-10\n */\n\n/**\n * @return int IPR fleets processed\n */\nfunction coe_o_missile_calculate() {\n//  db_mysql::db_transaction_check(true);\n\n  global $lang;\n\n  $processedIPR = 0;\n\n  $iraks = doquery(\"SELECT * FROM {{iraks}} WHERE `fleet_end_time` <= \" . SN_TIME_NOW . \" FOR UPDATE;\");\n\n  while ($fleetRow = db_fetch($iraks)) {\n    $sourcePlanet = DBStaticPlanet::db_planet_by_vector($fleetRow, 'fleet_start_');\n    $target_planet_row = DBStaticPlanet::db_planet_by_gspt(\n      $fleetRow['fleet_end_galaxy'],\n      $fleetRow['fleet_end_system'],\n      $fleetRow['fleet_end_planet'],\n      PT_PLANET\n    );\n    $targetUser = db_user_by_id($target_planet_row['id_owner'], true);\n    $rowAttacker = db_user_by_id($fleetRow['fleet_owner'], true);\n\n    db_mysql::db_transaction_start();\n    set_time_limit(15);\n    SN::$gc->db->lockRecords([\n      'users'   => [$targetUser['id'], $rowAttacker['id'],],\n      'planets' => [$target_planet_row['id'], $sourcePlanet['id'],],\n      'iraks'   => [$fleetRow['id'],],\n    ]);\n    $fleetRow = doquery(\"SELECT * FROM `{{iraks}}` WHERE `id` = {$fleetRow['id']} FOR UPDATE;\", '', true);\n    if (empty($fleetRow)) {\n      db_mysql::db_transaction_rollback();\n      continue;\n    }\n\n    $db_changeset = array();\n\n    $target_planet_row = sys_o_get_updated($targetUser['id'], $target_planet_row['id'], SN_TIME_NOW);\n    $target_planet_row = $target_planet_row['planet'];\n\n\n    if ($target_planet_row['id']) {\n      $planetDefense = array();\n      foreach (sn_get_groups('defense_active') as $unit_id) {\n        $planetDefense[$unit_id] = array(mrc_get_level($targetUser, $target_planet_row, $unit_id, true, true));\n      }\n\n      $message      = '';\n      $interceptors = mrc_get_level($targetUser, $target_planet_row, UNIT_DEF_MISSILE_INTERCEPTOR, true, true); //$target_planet_row[$interceptor_db_name]; // Number of interceptors\n      $missiles     = $fleetRow['fleet_amount']; // Number of MIP\n      if ($interceptors >= $missiles) {\n        $message                = $lang['mip_all_destroyed'];\n        $db_changeset['unit'][] = OldDbChangeSet::db_changeset_prepare_unit(UNIT_DEF_MISSILE_INTERCEPTOR, -$missiles, $targetUser, $target_planet_row['id']);\n      } else {\n        if ($interceptors) {\n          $message                = sprintf($lang['mip_destroyed'], $interceptors);\n          $db_changeset['unit'][] = OldDbChangeSet::db_changeset_prepare_unit(UNIT_DEF_MISSILE_INTERCEPTOR, -$interceptors, $targetUser, $target_planet_row['id']);\n        }\n\n        $attackResult = COE_missileAttack($targetUser, $rowAttacker, $missiles - $interceptors, $planetDefense, $fleetRow['primaer']);\n\n        foreach ($attackResult['structures'] as $key => $structure) {\n          $destroyed = $planetDefense[$key][0] - $structure[0];\n          if ($destroyed) {\n            $db_changeset['unit'][] = OldDbChangeSet::db_changeset_prepare_unit($key, -$destroyed, $targetUser, $target_planet_row['id']);\n\n            $message .= \"&nbsp;&nbsp;{$lang['tech'][$key]} - {$destroyed} {$lang['quantity']}<br>\";\n          }\n        }\n\n        if (!empty($message)) {\n          $message = $lang['mip_defense_destroyed'] . $message . \"{$lang['mip_recycled']}{$lang['Metal']}: {$attackResult['metal']}, {$lang['Crystal']}: {$attackResult['crystal']}<br>\";\n\n          DBStaticPlanet::db_planet_set_by_id($target_planet_row['id'], \"`metal` = `metal` + {$attackResult['metal']}, `crystal` = `crystal` + {$attackResult['crystal']}\");\n        }\n      }\n      OldDbChangeSet::db_changeset_apply($db_changeset);\n\n      $fleetRow['fleet_start_type'] = PT_PLANET;\n\n      $message_vorlage = sprintf($lang['mip_body_attack'], $fleetRow['fleet_amount'],\n        addslashes($sourcePlanet['name']), $fleetRow['fleet_start_galaxy'], $fleetRow['fleet_start_system'], $fleetRow['fleet_start_planet'],\n        addslashes($target_planet_row['name']), $fleetRow['fleet_end_galaxy'], $fleetRow['fleet_end_system'], $fleetRow['fleet_end_planet']);\n\n      empty($message) ? $message = $lang['mip_no_defense'] : false;\n\n      msg_send_simple_message($fleetRow['fleet_owner'], '', SN_TIME_NOW, MSG_TYPE_SPY, $lang['mip_sender_amd'], $lang['mip_subject_amd'], $message_vorlage . $message);\n      msg_send_simple_message($fleetRow['fleet_target_owner'], '', SN_TIME_NOW, MSG_TYPE_SPY, $lang['mip_sender_amd'], $lang['mip_subject_amd'], $message_vorlage . $message);\n\n    }\n\n    doquery(\"DELETE FROM {{iraks}} WHERE id = '{$fleetRow['id']}';\");\n\n    $processedIPR++;\n    db_mysql::db_transaction_commit();\n  }\n\n  return $processedIPR;\n}\n"
  },
  {
    "path": "includes/functions/geoip.php",
    "content": "<?php\n\nfunction geoip_status(){return sn_function_call('geoip_status', array(&$result));}\nfunction sn_geoip_status(&$result) {\n  return $result = false;\n}\n\nfunction geoip_ip_info($ip){return sn_function_call('geoip_ip_info', array($ip, &$result));}\nfunction sn_geoip_ip_info($ip, &$result) {\n  return $result = false;\n}\n"
  },
  {
    "path": "includes/functions/index.html",
    "content": ""
  },
  {
    "path": "includes/functions/int_banner_create.php",
    "content": "<?php\n\nuse Planet\\DBStaticPlanet;\n\n/**\n * int_banner_create.php\n * @version 2.0\n * @copyright 2010 Gorlum for http://supernova.ws\n *\n * heavily based on\n *   CreateBanner.php\n * @version 1.2 by Ihor\n * @version 1.0 copyright 2008 By e-Zobar for XNova\n */\n\nfunction int_banner_create($id, $type = 'userbar', $format = 'png') {\n// banner.php?id=<userid>&type=<banner|userbar>&format=<png>\n  global $config, $lang;\n\n  $id = intval($id);\n\n  switch ($type) {\n    case 'banner':\n      $img_name = $config->int_banner_background;\n    break;\n\n    default:\n      $img_name = $config->int_userbar_background;\n    break;\n  }\n  $size = getimagesize(SN_ROOT_PHYSICAL . $img_name);\n  $im = imagecreatefrompng(SN_ROOT_PHYSICAL . $img_name);\n  $image = imagecreatetruecolor($size[0], $size[1]);\n  imagecopy($image, $im, 0, 0, 0, 0, $size[0], $size[1]);\n  imagedestroy($im);\n\n  // Colors\n//  $color = \"FFFFFF\";\n//  $red = hexdec(substr($color, 0, 2));\n//  $green = hexdec(substr($color, 2, 4));\n//  $blue = hexdec(substr($color, 4, 6));\n//  $select = imagecolorallocate($image, $red, $green, $blue);\n  $txt_shadow = imagecolorallocatealpha($image, 255, 255, 255, 0); // was 255\n  $txt_color = imagecolorallocatealpha($image, 255, 255, 255, 2);\n  $txt_shadow2 = imagecolorallocatealpha($image, 255, 255, 255, 0); // was 255\n  $txt_color2 = imagecolorallocatealpha($image, 255, 255, 255, 40);\n\n  $fonts = array(\n    'userbar'  => SN_ROOT_PHYSICAL . \"design/fonts/\" . $config->int_userbar_font,\n    'universe' => SN_ROOT_PHYSICAL . \"design/fonts/\" . $config->int_banner_fontUniverse,\n    'raids'    => SN_ROOT_PHYSICAL . \"design/fonts/\" . $config->int_banner_fontRaids,\n    'info'     => SN_ROOT_PHYSICAL . \"design/fonts/\" . $config->int_banner_fontInfo,\n  );\n\n  if ($id) {\n    // Querys\n    $user = db_user_by_id($id);\n    $planet_row = DBStaticPlanet::db_planet_by_id($user['id_planet']);\n\n    // Variables\n    $b_user = $user['username'];\n    $b_ally = $user['ally_name'];\n    $b_planet = $planet_row['name'];\n    $b_xyz = \"[\" . $planet_row['galaxy'] . \":\" . $planet_row['system'] . \":\" . $planet_row['planet'] . \"]\";\n    $b_lvl = ($user['total_rank'] ? $user['total_rank'] : $config->users_amount) . \"/{$config->users_amount}\";\n  } else {\n    $b_user = $lang['ov_banner_empty_id'];\n  }\n\n  $b_univ = $config->game_name;\n  switch ($type) {\n    case 'banner':\n      // Banner size 416 x 58\n      $fsize = 15;\n\n      $is = imagettfbbox($fsize, 0, $fonts['universe'], $b_univ);\n      imagettftext($image, $fsize, 0, $size[0] - 4 - $is[2], $size[1] - 2, $txt_shadow, $fonts['universe'], $b_univ);\n      imagettftext($image, $fsize, 0, $size[0] - 6 - $is[2], $size[1] - 4, $txt_color, $fonts['universe'], $b_univ);\n\n      // Player name\n      imagettftext($image, 11, 0, 8, 26, $txt_shadow, $fonts['info'], $b_user);\n      imagettftext($image, 11, 0, 6, 23, $txt_color, $fonts['info'], $b_user);\n\n      if ($id) {\n        // Player level - right-alligned\n        $is = imagettfbbox(11, 0, $fonts['info'], $b_lvl);\n        imagettftext($image, 11, 0, $size[0] - 4 - $is[2], 25, $txt_shadow, $fonts['info'], $b_lvl);\n        imagettftext($image, 11, 0, $size[0] - 6 - $is[2], 23, $txt_color, $fonts['info'], $b_lvl);\n\n        // Ally name\n        $is = imagettfbbox(9, 0, $fonts['info'], $b_ally);\n        imagettftext($image, 9, 0, 412 - $is[2], 37, $txt_shadow, $fonts['info'], $b_ally);\n        imagettftext($image, 9, 0, 410 - $is[2], 35, $txt_color, $fonts['info'], $b_ally);\n\n        // Player b_planet\n        imagettftext($image, 6, 0, 8, 10, $txt_shadow2, $fonts['raids'], $b_planet . \" \" . $b_xyz);\n        imagettftext($image, 6, 0, 6, 9, $txt_color2, $fonts['raids'], $b_planet . \" \" . $b_xyz);\n\n        //StatPoint\n        $b_points = $lang['ov_points'] . \": \" . HelperString::numberFloorAndFormat($user['total_points']);\n        $is = imagettfbbox(8, 0, $fonts['info'], $b_points);\n        imagettftext($image, 8, 0, 412 - $is[2], 11, $txt_shadow, $fonts['info'], $b_points);\n        imagettftext($image, 8, 0, 410 - $is[2], 9, $txt_color, $fonts['info'], $b_points);\n\n        //Raids Total\n        imagettftext($image, 6, 0, 8, 37, $txt_shadow2, $fonts['raids'], $lang['NumberOfRaids']);\n        imagettftext($image, 6, 0, 6, 35, $txt_color2, $fonts['raids'], $lang['NumberOfRaids']);\n        $b_points = \": \" . HelperString::numberFloorAndFormat($user['raids']);\n        imagettftext($image, 6, 0, 61, 37, $txt_shadow2, $fonts['raids'], $b_points);\n        imagettftext($image, 6, 0, 59, 35, $txt_color2, $fonts['raids'], $b_points);\n\n        //Raids Won\n        imagettftext($image, 6, 0, 8, 47, $txt_shadow2, $fonts['raids'], $lang['RaidsWin']);\n        imagettftext($image, 6, 0, 6, 45, $txt_color2, $fonts['raids'], $lang['RaidsWin']);\n        $b_points = \": \" . HelperString::numberFloorAndFormat($user['raidswin']);\n        imagettftext($image, 6, 0, 61, 47, $txt_shadow2, $fonts['raids'], $b_points);\n        imagettftext($image, 6, 0, 59, 45, $txt_color2, $fonts['raids'], $b_points);\n\n        //Raids Lost\n        imagettftext($image, 6, 0, 8, 57, $txt_shadow2, $fonts['raids'], $lang['RaidsLoose']);\n        imagettftext($image, 6, 0, 6, 55, $txt_color2, $fonts['raids'], $lang['RaidsLoose']);\n        $b_points = \": \" . HelperString::numberFloorAndFormat($user['raidsloose']);\n        imagettftext($image, 6, 0, 61, 57, $txt_shadow2, $fonts['raids'], $b_points);\n        imagettftext($image, 6, 0, 59, 55, $txt_color2, $fonts['raids'], $b_points);\n      }\n\n    break;\n\n    default:\n      // Userbar 350 x 19\n      $b_univ = strtoupper($b_univ);\n      $is = imagettfbbox(9, 0, $fonts['userbar'], $b_univ);\n      $is = $size[0] - $is[2] - 2;\n      imagettftext($image, 9, 0, $is, $size[1] - 3, $txt_shadow, $fonts['userbar'], $b_univ);\n      imagettftext($image, 9, 0, $is - 1, $size[1] - 5, $txt_color, $fonts['userbar'], $b_univ);\n      imagettftext($image, 22, 0, $is - 8, $size[1], $txt_color, $fonts['userbar'], '/');\n\n      // Player name\n      imagettftext($image, 9, 0, 4, $size[1] - 4, $txt_shadow, $fonts['userbar'], $b_user);\n      imagettftext($image, 9, 0, 2, $size[1] - 6, $txt_color, $fonts['userbar'], $b_user);\n\n      if ($id) {\n        // Player level - right-alligned\n        $isp = imagettfbbox(9, 0, $fonts['userbar'], $b_lvl);\n        imagettftext($image, 9, 0, $is - $isp[2] - 10, $size[1] - 4, $txt_shadow, $fonts['userbar'], $b_lvl);\n        imagettftext($image, 9, 0, $is - $isp[2] - 10 - 1, $size[1] - 4 - 1, $txt_color, $fonts['userbar'], $b_lvl);\n      }\n  }\n\n  //And now convert it back to PNG-8\n  $im_result = imagecreate($size[0], $size[1]);\n  imagecopy($im_result, $image, 0, 0, 0, 0, $size[0], $size[1]);\n  imagedestroy($image);\n  //And save it\n  $imagetypes = imagetypes();\n  // TODO: Add support to different image types\n  header(\"Content-type: image/png\");\n  imagepng($im_result);\n  imagedestroy($im_result);\n}\n"
  },
  {
    "path": "includes/functions/int_fleet_events.php",
    "content": "<?php\n\n\nuse Planet\\DBStaticPlanet;\n\nfunction flt_parse_fleets_to_events($fleet_list, $planet_scanned = false)\n{\n  global $config, $user, $fleet_number, $lang;\n\n  $fleet_events = array();\n  $fleet_number = 0;\n\n  if(empty($fleet_list))\n  {\n    return;\n  }\n\n  foreach($fleet_list as $fleet)\n  {\n    $planet_start_type = $fleet['fleet_start_type'] == PT_MOON ? PT_MOON : PT_PLANET;\n    $planet_start = DBStaticPlanet::db_planet_by_gspt($fleet['fleet_start_galaxy'], $fleet['fleet_start_system'], $fleet['fleet_start_planet'], $planet_start_type);\n    $fleet['fleet_start_name'] = $planet_start['name'];\n\n    $planet_end_type = $fleet['fleet_end_type'] == PT_MOON ? PT_MOON : PT_PLANET;\n    if($fleet['fleet_end_planet'] > $config->game_maxPlanet)\n    {\n      $fleet['fleet_end_name'] = $lang['ov_fleet_exploration'];\n    }\n    elseif($fleet['fleet_mission'] == MT_COLONIZE)\n    {\n      $fleet['fleet_end_name'] = $lang['ov_fleet_colonization'];\n    }\n    else\n    {\n      $planet_end = DBStaticPlanet::db_planet_by_gspt($fleet['fleet_end_galaxy'], $fleet['fleet_end_system'], $fleet['fleet_end_planet'], $planet_end_type);\n      $fleet['fleet_end_name'] = $planet_end['name'];\n    }\n\n    if($fleet['fleet_start_time'] > SN_TIME_NOW && $fleet['fleet_mess'] == 0 && $fleet['fleet_mission'] != MT_MISSILE &&\n      ($planet_scanned === false\n        ||\n        (\n          $planet_scanned !== false\n          && $planet_scanned['galaxy'] == $fleet['fleet_end_galaxy'] && $planet_scanned['system'] == $fleet['fleet_end_system'] && $planet_scanned['planet'] == $fleet['fleet_end_planet'] && $planet_scanned['planet_type'] == $planet_end_type\n          && $planet_start_type != PT_MOON\n          && $fleet['fleet_mission'] != MT_HOLD\n        )\n      )\n    )\n    {\n      $fleet_events[] = flt_register_fleet_event($fleet, 0, $planet_end_type);\n    }\n\n    if($fleet['fleet_end_stay'] > SN_TIME_NOW && $fleet['fleet_mess'] == 0 && $planet_scanned === false && $fleet['fleet_mission'] != MT_MISSILE)\n    {\n      $fleet_events[] = flt_register_fleet_event($fleet, 1, $planet_end_type);\n    }\n\n    if(\n      $fleet['fleet_end_time'] > SN_TIME_NOW && $fleet['fleet_mission'] != MT_MISSILE && ($fleet['fleet_mess'] == 1 || ($fleet['fleet_mission'] != MT_RELOCATE && $fleet['fleet_mission'] != MT_COLONIZE)) &&\n      (\n        ($planet_scanned === false && $fleet['fleet_owner'] == $user['id'])\n        ||\n        (\n          $planet_scanned !== false\n          && $fleet['fleet_mission'] != MT_RELOCATE\n          && $planet_start_type != PT_MOON\n          && $planet_scanned['galaxy'] == $fleet['fleet_start_galaxy'] && $planet_scanned['system'] == $fleet['fleet_start_system'] && $planet_scanned['planet'] == $fleet['fleet_start_planet'] && $planet_scanned['planet_type'] == $planet_start_type\n        )\n      )\n    )\n    {\n      $fleet_events[] = flt_register_fleet_event($fleet, 2, $planet_end_type);\n    }\n\n    if($fleet['fleet_mission'] == MT_MISSILE)\n    {\n      $fleet_events[] = flt_register_fleet_event($fleet, 3, $planet_end_type);\n    }\n  }\n\n  return $fleet_events;\n}\n\nfunction flt_register_fleet_event($fleet, $ov_label, $planet_end_type)\n{\n  global $user, $planetrow, $fleet_number;\n\n  switch($fleet['ov_label'] = $ov_label)\n  {\n    case 0:\n      $fleet['event_time'] = $fleet['fleet_start_time'];\n      $is_this_planet = (\n        ($planetrow['galaxy'] == $fleet['fleet_end_galaxy']) AND\n        ($planetrow['system'] == $fleet['fleet_end_system']) AND\n        ($planetrow['planet'] == $fleet['fleet_end_planet']) AND\n        ($planetrow['planet_type'] == $planet_end_type));\n    break;\n\n    case 1:\n      $fleet['event_time'] = $fleet['fleet_end_stay'];\n      $is_this_planet = (\n        ($planetrow['galaxy'] == $fleet['fleet_end_galaxy']) AND\n        ($planetrow['system'] == $fleet['fleet_end_system']) AND\n        ($planetrow['planet'] == $fleet['fleet_end_planet']) AND\n        ($planetrow['planet_type'] == $planet_end_type));\n    break;\n\n    case 2:\n    case 3:\n      $fleet['event_time'] = $fleet['fleet_end_time'];\n      $is_this_planet = (\n        ($planetrow['galaxy'] == $fleet['fleet_start_galaxy']) AND\n        ($planetrow['system'] == $fleet['fleet_start_system']) AND\n        ($planetrow['planet'] == $fleet['fleet_start_planet']) AND\n        ($planetrow['planet_type'] == $fleet['fleet_start_type']));\n    break;\n\n  }\n\n  $fleet['ov_this_planet'] = $is_this_planet;// || $planet_scanned != false;\n\n  if($fleet['fleet_owner'] == $user['id'])\n  {\n    $user_data = $user;\n  }\n  else\n  {\n    $user_data = db_user_by_id($fleet['fleet_owner']);\n  }\n\n  return tpl_parse_fleet_db($fleet, ++$fleet_number, $user_data);\n}\n\n/**\n * @param array    $planetrow\n * @param template $template\n */\nfunction int_planet_pretemplate($planetrow, &$template)\n{\n  global $lang, $user;\n\n  $governor_id = $planetrow['PLANET_GOVERNOR_ID'];\n  $governor_level_plain = mrc_get_level($user, $planetrow, $governor_id, false, true);\n\n  $template->assign_vars(array(\n    'PLANET_ID'          => $planetrow['id'],\n    'PLANET_NAME'        => htmlentities($planetrow['name'], ENT_QUOTES, 'UTF-8'),\n    'PLANET_NAME_JS'     => htmlentities(js_safe_string($planetrow['name']), ENT_QUOTES, 'UTF-8'),\n    'PLANET_GALAXY'      => $planetrow['galaxy'],\n    'PLANET_SYSTEM'      => $planetrow['system'],\n    'PLANET_PLANET'      => $planetrow['planet'],\n    'PLANET_TYPE'        => $planetrow['planet_type'],\n    'PLANET_TYPE_TEXT'   => $lang['sys_planet_type'][$planetrow['planet_type']],\n    'PLANET_DEBRIS'      => $planetrow['debris_metal'] + $planetrow['debris_crystal'],\n\n    'PLANET_GOVERNOR_ID'         => $governor_id,\n    'PLANET_GOVERNOR_NAME'       => $lang['tech'][$governor_id],\n    'PLANET_GOVERNOR_LEVEL'      => $governor_level_plain,\n    'PLANET_GOVERNOR_LEVEL_PLUS' => mrc_get_level($user, $planetrow, $governor_id, false, false) - $governor_level_plain,\n    'PLANET_GOVERNOR_LEVEL_MAX'  => get_unit_param($governor_id, P_MAX_STACK),\n  ));\n}\n"
  },
  {
    "path": "includes/functions/lng_language.php",
    "content": "<?php\n\n// ----------------------------------------------------------------------------------------------------------------\nfunction lng_try_filepath($path, $file_path_relative)\n{\n  $file_path = SN_ROOT_PHYSICAL . ($path && file_exists(SN_ROOT_PHYSICAL . $path . $file_path_relative) ? $path : '') . $file_path_relative;\n  return file_exists($file_path) ? $file_path : false;\n}\n\nfunction lng_die_not_an_object()\n{\n  print('Ошибка - $lang не объект! Сообщите Администратору сервера и приложите содержимое страницы');\n  $trace = debug_backtrace();\n  unset($trace[0]);\n  pdump($trace);\n  return die();\n}\n\n// ----------------------------------------------------------------------------------------------------------------\nfunction lng_include($filename, $path = '', $ext = '.mo.php')\n{\n  global $lang;\n  return is_object($lang) ? $lang->lng_include($filename, $path, $ext) : lng_die_not_an_object();\n}\n\nfunction lng_get_list()\n{\n  global $lang;\n  return is_object($lang) ? $lang->lng_get_list() : lng_die_not_an_object();\n}\n\nfunction lng_get_info($entry)\n{\n  global $lang;\n  return is_object($lang) ? $lang->lng_get_info($entry) : lng_die_not_an_object();\n}\n\nfunction lng_switch($language_new)\n{\n  global $lang;\n  return is_object($lang) ? $lang->lng_switch($language_new) : lng_die_not_an_object();\n}\n\nfunction lng_load_i18n($i18n)\n{\n  global $lang;\n  return is_object($lang) ? $lang->lng_load_i18n($i18n) : lng_die_not_an_object();\n}\n"
  },
  {
    "path": "includes/functions/msg_send_simple_message.php",
    "content": "<?php\n\n/**\n * SendSimpleMessage.php\n *\n * @version 1.3\n * @copyright 2008 by Chlorel for XNova\n   Revision history :\n   1.0 - Initial release (mise en fonction generique)\n   1.1 - Ajout gestion des messages par type pour le module de messages\n   1.2 - Correction bug (addslashes pour les zone texte pouvant contenir une apostrophe)\n   1.3 - Correction bug (integration de la variable $Time pour afficher l'heure exacte de l'evenement pour les flottes)\n   1.4 - copyright (c) 2010 by Gorlum for http://supernova.ws\n         [+] Ability to mass-send emails. Mass-sending done via two queries - one for messages table, one for users table\n   1.5 - copyright (c) 2010-2011 by Gorlum for http://supernova.ws\n         [+] SuperMassMailing - authlevel=3 player can send messages to whole server ('*' as $owners)\n */\nfunction msg_ali_send($message, $subject, $ally_rank_id = 0, $ally_id = 0)\n{\n  global $user;\n\n  $ally_id = $ally_id ? $ally_id : $user['ally_id'];\n\n  $list = '';\n  $query = db_user_list(\n    \"ally_id = '{$ally_id}'\" . ($ally_rank_id >= 0 ? \" AND ally_rank_id = {$ally_rank_id}\" : ''),\n    false, 'id, username');\n  // while ($u = db_fetch($query))\n  foreach($query as $u)\n  {\n    $sendList[] = $u['id'];\n    $list .= \"<br>{$u['username']} \";\n  }\n\n  msg_send_simple_message($sendList, $user['id'], SN_TIME_NOW, MSG_TYPE_ALLIANCE, $user['username'], $subject, $message, true);\n\n  return $list;\n}\n\n/**\n * @param      $owners\n * @param      $sender\n * @param      $timestamp\n * @param      $message_type\n * @param      $from\n * @param      $subject\n * @param      $text\n * @param bool $escaped - is $from, $subject and $text escaped?\n * @param bool $force\n * @param bool $json\n */\nfunction msg_send_simple_message($owners, $sender, $timestamp, $message_type, $from, $subject, $text, $escaped = STRING_NEED_ESCAPING, $force = false, $json = false)\n{\n  global $config, $user, $sn_message_class_list;\n\n  if(!$owners)\n  {\n    return;\n  }\n\n  $timestamp = $timestamp ? $timestamp : SN_TIME_NOW;\n  $sender = intval($sender);\n  if(!is_array($owners))\n  {\n    $owners = array($owners);\n  }\n\n  if($escaped == STRING_NEED_ESCAPING)\n  {\n    $from = SN::$db->db_escape($from);\n    $subject = SN::$db->db_escape($subject);\n    $text = SN::$db->db_escape($text);\n  }\n\n  $text_unescaped = stripslashes(str_replace(array('\\r\\n', \"\\r\\n\"), \"<br />\", $text));\n\n  $message_class = $sn_message_class_list[$message_type];\n  $message_class_email = $message_class['email'];\n  $message_class_switchable = $message_class['switchable'];\n  $message_class_name = $message_class['name'];\n\n  $message_class_name_total = $sn_message_class_list[MSG_TYPE_NEW]['name'];\n\n  if($owners[0] == '*')\n  {\n    if($user['authlevel'] < 3)\n    {\n      return false;\n    }\n    // TODO Добавить $timestamp - рассылка может быть и отсроченной\n    // TODO Добавить $sender - рассылка может быть и от кого-то\n    db_message_insert_all($message_type, $from, $subject, $text);\n    $owners = [];\n  }\n  else\n  {\n    $insert_values = [];\n    $insert_template = \"('%u',\" . str_replace('%', '%%', \" '{$sender}', '{$timestamp}', '{$message_type}', '{$from}', '{$subject}', '{$text}', '\" . intval($json) . \"')\");\n\n    foreach ($owners as $owner)\n    {\n      if($user['id'] != $owner)\n      {\n        $owner_row = db_user_by_id($owner);\n      }\n      else\n      {\n        $owner_row = $user;\n      }\n      sys_user_options_unpack($owner_row);\n\n      if($force || !$message_class_switchable || $owner_row[\"opt_{$message_class_name}\"])\n      {\n        $insert_values[] = sprintf($insert_template, $owner);\n      }\n\n      if($message_class_email && $config->game_email_pm && $owner_row[\"opt_email_{$message_class_name}\"])\n      {\n        if($message_type == MSG_TYPE_SPY) {\n          @$result = mymail($owner_row['email'], $subject, SN::$lang['sys_spy_activity'], '', true);\n        } else {\n          @$result = mymail($owner_row['email'], $subject, $text_unescaped, '', true);\n        }\n      }\n    }\n\n    if(empty($insert_values))\n    {\n      return;\n    }\n\n    doquery($QryInsertMessage = 'INSERT INTO {{messages}} (`message_owner`, `message_sender`, `message_time`, `message_type`, `message_from`, `message_subject`, `message_text`, `message_json`) ' .\n      'VALUES ' . implode(',', $insert_values));\n  }\n  db_user_list_set_mass_mail($owners, \"`{$message_class_name}` = `{$message_class_name}` + 1, `{$message_class_name_total}` = `{$message_class_name_total}` + 1\");\n\n  if(in_array($user['id'], $owners) || $owners[0] == '*')\n  {\n    $user[$message_class_name]++;\n    $user[$message_class_name_total]++;\n  }\n}\n"
  },
  {
    "path": "includes/functions/qst_quest.php",
    "content": "<?php\n\nuse \\DBAL\\DbQuery;\nuse DBAL\\OldDbChangeSet;\n\nfunction roughQuestRenderWrapper() {\n  global $lang, $user;\n  lng_include('quest');\n\n  $template = SnTemplate::gettemplate('quest', true);\n  qst_render_page($lang, $user, $template);\n\n  SnTemplate::display($template, $lang['qst_quests']);\n}\n\n/**\n * @param classLocale $lang\n * @param array       $user\n * @param template    $template\n */\nfunction qst_render_page(classLocale $lang, array $user, template $template) {\n  $questCurrentManaged = null;\n  $in_admin = defined('IN_ADMIN') && IN_ADMIN === true;\n\n  $user_id = sys_get_param_id('user_id', false);\n  $currentMode = sys_get_param_str('mode');\n\n  if ($in_admin) {\n    $questCurrentManaged = questPageModelManage($lang, $template, $currentMode);\n  } elseif (!$user_id) {\n    $user_id = $user['id'];\n  }\n\n  $quest_list = qst_get_quests($user_id, SN::$user_options[PLAYER_OPTION_QUEST_LIST_FILTER]);\n  $template->assign_vars(array(\n    'AUTHLEVEL' => $user['authlevel'],\n    'TOTAL'     => count($quest_list),\n    'mode'      => $currentMode,\n    'USER_ID'   => $user_id,\n    'IN_ADMIN'  => $in_admin,\n\n    'QUEST_STATUS_NOT_STARTED' => QUEST_STATUS_NOT_STARTED,\n    'QUEST_STATUS_STARTED'     => QUEST_STATUS_STARTED,\n    'QUEST_STATUS_COMPLETE'    => QUEST_STATUS_COMPLETE,\n\n    'PLAYER_OPTION_QUEST_LIST_FILTER' => SN::$user_options[PLAYER_OPTION_QUEST_LIST_FILTER],\n  ));\n\n  foreach ($lang['qst_status_list'] as $statusId => $statusName) {\n    $template->assign_block_vars('status', array(\n      'ID'   => $statusId,\n      'NAME' => $statusName,\n    ));\n  }\n\n  if (!empty($questCurrentManaged)) {\n    $quest_templatized = qst_templatize(qst_quest_parse($questCurrentManaged));\n  } else {\n    $quest_templatized['quest_rewards_list'] = [];\n  }\n\n  questTemplatizeReward($quest_templatized, $lang);\n\n  qst_assign_to_template($template, $quest_templatized);\n\n  foreach ($quest_list as $quest_data) {\n    qst_assign_to_template($template, qst_templatize($quest_data, true), 'quest');\n  }\n\n  foreach (questUnitsAllowed() as $unit_id) {\n    $template->assign_block_vars('allowed_unit', array(\n      'ID'   => $unit_id,\n      'NAME' => $lang['tech'][$unit_id],\n    ));\n  }\n}\n\n/**\n * @param classLocale $lang\n * @param template    $template\n * @param string      $mode\n */\nfunction questPageModelManage(classLocale $lang, template $template, &$mode) {\n  $questManaged = null;\n\n  $quest_id = sys_get_param_id('id');\n  $quest_name = sys_get_param_str_unsafe('QUEST_NAME');\n  if (!empty($quest_name)) {\n    try {\n      // TODO: Change quest type\n      $quest_type = 0;\n      $theQuery = DbQuery::build()\n        ->setTable('quest')\n        ->setValues([\n          'quest_type'        => $quest_type,\n          'quest_name'        => $quest_name,\n          'quest_description' => sys_get_param_str_unsafe('QUEST_DESCRIPTION'),\n          'quest_conditions'  => questGetConditionFromParams($lang),\n          'quest_rewards'     => questGetRewardsStringFromParams($lang),\n        ]);\n\n      if ($mode == 'edit') {\n        $theQuery\n          ->setWhereArray(['quest_id' => $quest_id])\n          ->setOneRow()\n          ->doUpdate();\n      } else {\n        $theQuery->doInsert();\n      }\n\n      // TODO: Add mass mail for new quests\n      /*\n      if(sys_get_param_int('news_mass_mail')) {\n        msg_send_simple_message('*', 0, 0, MSG_TYPE_PLAYER, $lang['sys_administration'], $lang['news_title'], $text);\n      }\n      */\n    } catch (Exception $e) {\n      SnTemplate::messageBox($e->getMessage(), $lang['sys_error']);\n    }\n\n    $mode = '';\n  }\n\n  switch ($mode) {\n    case 'del':\n      doquery(\"DELETE FROM `{{quest}}` WHERE `quest_id` = {$quest_id} LIMIT 1;\");\n      $mode = '';\n    break;\n\n    /** @noinspection PhpMissingBreakStatementInspection */\n    case 'edit':\n      $template->assign_var('QUEST_ID', $quest_id);\n\n    case 'copy':\n      $questManaged = doquery(\"SELECT * FROM `{{quest}}` WHERE `quest_id` = {$quest_id} LIMIT 1;\", '', true);\n    break;\n  }\n\n  $query = doquery(\"SELECT count(*) AS count FROM `{{quest}}`;\", '', true);\n  SN::$config->pass()->quest_total = $query['count'];\n\n  return $questManaged;\n}\n\n/**\n * @param array       $quest_templatized\n * @param classLocale $lang\n */\nfunction questTemplatizeReward(&$quest_templatized, $lang) {\n  foreach (questAllowedRewardsList() as $unit_id) {\n    $found = false;\n    foreach ($quest_templatized['quest_rewards_list'] as $quest_templatized_reward) {\n      if ($quest_templatized_reward['ID'] == $unit_id) {\n        $found = true;\n        break;\n      }\n    }\n\n    if (!$found) {\n      $quest_templatized['quest_rewards_list'][$unit_id] = array(\n        'ID'     => $unit_id,\n        'NAME'   => $lang['tech'][$unit_id],\n        'AMOUNT' => 0,\n      );\n    }\n  }\n}\n\n/**\n * @param $lang\n *\n * @return string\n * @throws Exception\n */\nfunction questGetRewardsStringFromParams($lang) {\n  $quest_reward_allowed = questAllowedRewardsList();\n  $quest_rewards = [];\n  foreach (sys_get_param('QUEST_REWARDS_LIST') as $quest_rewards_id => $quest_rewards_amount) {\n    if (!in_array($quest_rewards_id, $quest_reward_allowed)) {\n      throw new Exception($lang['qst_adm_err_reward_type']);\n    }\n\n    $quest_rewards_amount = round($quest_rewards_amount);\n    if ($quest_rewards_amount < 0) {\n      throw new Exception($lang['qst_adm_err_reward_amount']);\n    } elseif ($quest_rewards_amount > 0) {\n      $quest_rewards[intval($quest_rewards_id)] = $quest_rewards_amount;\n    }\n  }\n  if (empty($quest_rewards)) {\n    throw new Exception($lang['qst_adm_err_reward_empty']);\n  }\n  $quest_rewards_string = sys_unit_arr2str($quest_rewards);\n\n  return $quest_rewards_string;\n}\n\n/**\n * @param $lang\n *\n * @return string\n * @throws Exception\n */\nfunction questGetConditionFromParams($lang) {\n  $quest_unit_id = sys_get_param_int('QUEST_UNIT_ID');\n  if (!in_array($quest_unit_id, questUnitsAllowed())) {\n    throw new Exception($lang['qst_adm_err_unit_id']);\n  }\n  $quest_unit_amount = sys_get_param_float('QUEST_UNIT_AMOUNT');\n  if ($quest_unit_amount <= 0) {\n    throw new Exception($lang['qst_adm_err_unit_amount']);\n  }\n  $quest_conditions = \"{$quest_unit_id},{$quest_unit_amount}\";\n\n  return $quest_conditions;\n}\n\n/**\n * @return array\n */\nfunction questAllowedRewardsList() {\n  return sn_get_groups('quest_rewards');\n}\n\n/**\n * @return array\n */\nfunction questUnitsAllowed() {\n  return sn_get_groups(['structures', 'tech', 'fleet', 'defense']);\n}\n\nfunction qst_get_quests($user_id = false, $status = QUEST_STATUS_ALL) {\n  $quest_list = array();\n\n  if ($user_id) {\n    if ($status !== QUEST_STATUS_ALL) {\n      $query_add_where = \"\";\n      if ($status == null || $status == QUEST_STATUS_NOT_STARTED) {\n        $query_add_where .= \"AND qs.quest_status_status IS NULL\";\n      } elseif ($status == QUEST_STATUS_EXCEPT_COMPLETE) {\n        $query_add_where .= \"AND (qs.quest_status_status IS NULL OR qs.quest_status_status = \" . QUEST_STATUS_STARTED . \")\";\n      } else {\n        $query_add_where .= \"AND qs.quest_status_status = {$status}\";\n      }\n    }\n    $query_add_select = \", qs.quest_status_progress, qs.quest_status_status\";\n    $query_add_from = \"LEFT JOIN {{quest_status}} AS qs ON qs.quest_status_quest_id = q.quest_id AND qs.quest_status_user_id = {$user_id}\";\n  }\n\n  $query = doquery(\n    \"SELECT q.* {$query_add_select}\n      FROM `{{quest}}` AS q {$query_add_from}\n      WHERE 1 {$query_add_where}\n    ;\"\n  );\n\n  while ($quest = db_fetch($query)) {\n    $quest_list[$quest['quest_id']] = qst_quest_parse($quest);\n  }\n\n  return $quest_list;\n}\n\n/**\n * @param template    $template\n * @param array       $quest_templatized\n * @param string|bool $block_name\n */\nfunction qst_assign_to_template(&$template, $quest_templatized, $block_name = false) {\n  if ($block_name) {\n    $template->assign_block_vars($block_name, $quest_templatized);\n  } else {\n    $template->assign_vars($quest_templatized);\n    if (!empty($quest_templatized['quest_rewards_list'])) {\n      foreach ($quest_templatized['quest_rewards_list'] as $quest_reward) {\n        $template->assign_block_vars(($block_name ? $block_name . '.' : '') . 'quest_rewards_list', $quest_reward);\n      }\n    }\n  }\n}\n\nfunction qst_quest_parse($quest) {\n  list($quest['quest_unit_id'], $quest['quest_unit_amount']) = explode(',', $quest['quest_conditions']);\n\n  $quest['quest_rewards_list'] = sys_unit_str2arr($quest['quest_rewards']);\n\n  return $quest;\n}\n\nfunction qst_templatize($quest, $for_display = true) {\n  global $lang;\n\n  $tmp = array();\n  foreach ($quest['quest_rewards_list'] as $quest_reward_id => $quest_reward_amount) {\n    $tmp[] = array(\n      'ID'     => $quest_reward_id,\n      'NAME'   => $for_display ? str_replace(' ', '&nbsp;', $lang['tech'][$quest_reward_id]) : $lang['tech'][$quest_reward_id],\n      'AMOUNT' => $quest_reward_amount,\n    );\n  }\n\n  return array(\n    'QUEST_ID'           => $quest['quest_id'],\n    'QUEST_NAME'         => $quest['quest_name'],\n    'QUEST_TYPE'         => $quest['quest_type'],\n    'QUEST_DESCRIPTION'  => $for_display ? HelperString::nl2br($quest['quest_description']) : $quest['quest_description'],\n    'QUEST_CONDITIONS'   => $quest['quest_condition'],\n    'QUEST_UNIT_ID'      => $quest['quest_unit_id'],\n    'QUEST_UNIT_NAME'    => $lang['tech'][$quest['quest_unit_id']],\n    'QUEST_UNIT_AMOUNT'  => $quest['quest_unit_amount'],\n    'QUEST_STATUS'       => intval($quest['quest_status_status']),\n    'QUEST_STATUS_NAME'  => $lang['qst_status_list'][intval($quest['quest_status_status'])],\n    'quest_rewards_list' => $tmp,\n  );\n}\n\nfunction qst_active_triggers($quest_list) {\n  $quest_triggers = array();\n  foreach ($quest_list as $quest_id => $quest) {\n    if ($quest['quest_status_status'] != QUEST_STATUS_COMPLETE) {\n      list($quest_unit_id, $quest_unit_amount) = explode(',', $quest['quest_conditions']);\n      $quest_triggers[$quest_id] = $quest_unit_id;\n    }\n  }\n\n  return $quest_triggers;\n}\n\n/**\n * @param           $user\n * @param           $rewards\n * @param           $quest_list\n * @param integer[] $quest_statuses\n */\nfunction qst_reward(&$user, &$rewards, &$quest_list, &$quest_statuses) {\n  if (empty($quest_statuses)) {\n    return;\n  }\n\n  global $lang;\n\n  foreach ($quest_statuses as $quest_id => $quest_status) {\n    $quest_list[$quest_id]['quest_status_status'] = $quest_status;\n\n    $questStatus = DbQuery::build()\n      ->setTable('quest_status')\n      ->setWhereArray(array(\n        'quest_status_quest_id' => $quest_id,\n        'quest_status_user_id'  => $user['id'],\n      ))\n      ->doSelectFetch();\n\n    if (empty($questStatus)) {\n      DbQuery::build()\n        ->setTable('quest_status')\n        ->setValues(array(\n          'quest_status_quest_id' => $quest_id,\n          'quest_status_user_id'  => $user['id'],\n          'quest_status_status'   => $quest_status\n        ))\n        ->doInsert();\n    } elseif ($questStatus['quest_status_status'] != $quest_status) {\n      DbQuery::build()\n        ->setTable('quest_status')\n        ->setWhereArray(array(\n          'quest_status_quest_id' => $quest_id,\n          'quest_status_user_id'  => $user['id'],\n        ))\n        ->setValues(array(\n          'quest_status_status' => $quest_status\n        ))\n        ->doUpdate();\n    }\n  }\n\n  if (empty($rewards)) {\n    return;\n  }\n\n  $db_changeset = array();\n  $total_rewards = array();\n  $comment_dm = '';\n\n  foreach ($rewards as $quest_id => $user_data) {\n    foreach ($user_data as $user_id => $planet_data) {\n      foreach ($planet_data as $planet_id => $reward_list) {\n        $comment = sprintf($lang['qst_msg_complete_body'], $quest_list[$quest_id]['quest_name']);\n        $comment_dm .= isset($reward_list[RES_DARK_MATTER]) ? $comment : '';\n\n        $comment_reward = array();\n        foreach ($reward_list as $unit_id => $unit_amount) {\n          $comment_reward[] = $unit_amount . ' ' . $lang['tech'][$unit_id];\n          $total_rewards[$user_id][$planet_id][$unit_id] += $unit_amount;\n        }\n        $comment .= \" {$lang['qst_msg_your_reward']} \" . implode(',', $comment_reward);\n\n        msg_send_simple_message($user['id'], 0, SN_TIME_NOW, MSG_TYPE_ADMIN, $lang['msg_from_admin'], $lang['qst_msg_complete_subject'], $comment);\n\n        DbQuery::build()\n          ->setTable('quest_status')\n          ->setValues(array(\n            'quest_status_quest_id' => $quest_id,\n            'quest_status_user_id'  => $user_id,\n            'quest_status_status'   => QUEST_STATUS_COMPLETE\n          ))\n          ->doInsert();\n      }\n    }\n  }\n\n  $group_resources = sn_get_groups('resources_loot');\n  $quest_rewards_allowed = questAllowedRewardsList();\n  if (!empty($total_rewards)) {\n    foreach ($total_rewards as $user_id => $planet_data) {\n      $user_row = db_user_by_id($user_id);\n      foreach ($planet_data as $planet_id => $unit_data) {\n        $local_changeset = array();\n        foreach ($unit_data as $unit_id => $unit_amount) {\n          if (!isset($quest_rewards_allowed[$unit_id])) {\n            continue;\n          }\n\n          if ($unit_id == RES_DARK_MATTER) {\n            rpg_points_change($user['id'], RPG_QUEST, $unit_amount, $comment_dm);\n          } elseif (isset($group_resources[$unit_id])) {\n            $local_changeset[pname_resource_name($unit_id)] = array('delta' => $unit_amount);\n          } else // Проверим на юниты\n          {\n            $db_changeset['unit'][] = OldDbChangeSet::db_changeset_prepare_unit($unit_id, $unit_amount, $user_row, $planet_id);\n          }\n        }\n\n        if (!empty($local_changeset)) {\n          $planet_id = $planet_id == 0 && isset($user_row['id_planet']) ? $user_row['id_planet'] : $planet_id;\n          $db_changeset[$planet_id ? 'planets' : 'users'][] = array(\n            'action'  => SQL_OP_UPDATE,\n            P_VERSION => 1,\n            'where'   => array(\n              \"id\" => $planet_id ? $planet_id : $user_id,\n            ),\n            'fields'  => $local_changeset,\n          );\n        }\n      }\n    }\n\n    OldDbChangeSet::db_changeset_apply($db_changeset);\n  }\n}\n\nfunction get_quest_amount_complete($user_id) {\n  return count(qst_get_quests($user_id, QUEST_STATUS_COMPLETE));\n}\n\nfunction get_quest_amount_in_progress($user_id) {\n  return count(qst_get_quests($user_id, QUEST_STATUS_STARTED));\n}\n"
  },
  {
    "path": "includes/functions/rpg_points.php",
    "content": "<?php\n/**\n*\n* @package rpg\n* @version $Id$\n* @copyright (c) 2009-2010 Gorlum for http://supernova.ws\n* @license http://opensource.org/licenses/gpl-license.php GNU Public License\n*\n*/\n\n\n/**\n*\n* This function changes rpg_points for user\n* You should ALWAYS use this function and NEVER directly change rpg_points by yourself\n* Otherwise refferal system wouldn't work and no logs would be made\n* \"No logs\" means you can never check if the user cheating with DM\n*\n* @package rpg\n*\n*/\n\n/**\n * @param int   $user_id\n * @param int   $change_type\n * @param float $dark_matter\n * @param bool  $comment\n * @param bool  $already_changed\n *\n * @return bool|int\n */\nfunction rpg_points_change($user_id, $change_type, $dark_matter, $comment = false, $already_changed = false) {\n  global $debug, $config, $dm_change_legit, $user;\n\n  if(!$user_id) {\n    return false;\n  }\n\n  $dm_change_legit = true;\n  $sn_data_dark_matter_db_name = pname_resource_name(RES_DARK_MATTER);\n\n  if($already_changed) {\n    $rows_affected = 1;\n  } else {\n    $changeset = array();\n    $a_user = db_user_by_id($user_id, true);\n    if($dark_matter < 0) {\n      $dark_matter_exists = mrc_get_level($a_user, null, RES_DARK_MATTER, false, true);\n      $dark_matter_exists < 0 ? $dark_matter_exists = 0 : false;\n      $metamatter_to_reduce = -$dark_matter - $dark_matter_exists;\n      if($metamatter_to_reduce > 0) {\n        $metamatter_exists = mrc_get_level($a_user, null, RES_METAMATTER);\n        if($metamatter_exists < $metamatter_to_reduce) {\n          $debug->error('Ошибка снятия ТМ - ММ+ТМ меньше, чем сумма для снятия!', 'Ошибка снятия ТМ', LOG_ERR_INT_NOT_ENOUGH_DARK_MATTER);\n        }\n        if(is_array($comment)) {\n          $comment = call_user_func_array('sprintf', $comment);\n        }\n//        mm_points_change($user_id, $change_type, -$metamatter_to_reduce, 'ММ в ТМ: ' . (-$dark_matter) . ' ТМ = ' . $dark_matter_exists . ' ТМ + ' . $metamatter_to_reduce . ' ММ. ' . $comment);\n        SN::$auth->account->metamatter_change($change_type, -$metamatter_to_reduce, 'ММ в ТМ: ' . (-$dark_matter) . ' ТМ = ' . $dark_matter_exists . ' ТМ + ' . $metamatter_to_reduce . ' ММ. ' . $comment);\n        $dark_matter = -$dark_matter_exists;\n      }\n    } else {\n      $changeset[] = \"`dark_matter_total` = `dark_matter_total` + '{$dark_matter}'\";\n    }\n    $dark_matter ? $changeset[] = \"`{$sn_data_dark_matter_db_name}` = `{$sn_data_dark_matter_db_name}` + '{$dark_matter}'\" : false;\n    !empty($changeset) ? db_user_set_by_id($user_id, implode(',', $changeset)) : false;\n    $rows_affected = SN::$db->db_affected_rows();\n  }\n\n  if($rows_affected || !$dark_matter) {\n    $page_url = SN::$db->db_escape($_SERVER['SCRIPT_NAME']);\n    if(is_array($comment)) {\n      $comment = call_user_func_array('sprintf', $comment);\n    }\n    $comment = SN::$db->db_escape($comment);\n    $row = db_user_by_id($user_id, false);\n    $row['username'] = SN::$db->db_escape($row['username']);\n    doquery(\n      \"INSERT INTO {{log_dark_matter}} (`log_dark_matter_username`, `log_dark_matter_reason`,\n        `log_dark_matter_amount`, `log_dark_matter_comment`, `log_dark_matter_page`, `log_dark_matter_sender`)\n      VALUES (\n        '{$row['username']}', {$change_type},\n        {$dark_matter}, '{$comment}', '{$page_url}', {$user_id}\n      );\");\n\n    if($user['id'] == $user_id) {\n      $user['dark_matter'] += $dark_matter;\n    }\n\n    if($dark_matter > 0) {\n      $old_referral = doquery(\"SELECT * FROM {{referrals}} WHERE `id` = {$user_id} LIMIT 1 FOR UPDATE;\", '', true);\n      if($old_referral['id']) {\n        doquery(\"UPDATE {{referrals}} SET dark_matter = dark_matter + '{$dark_matter}' WHERE `id` = {$user_id} LIMIT 1;\");\n        $new_referral = doquery(\"SELECT * FROM {{referrals}} WHERE `id` = {$user_id} LIMIT 1;\", '', true);\n\n        $partner_bonus = floor($new_referral['dark_matter'] / $config->rpg_bonus_divisor) - ($old_referral['dark_matter'] >= $config->rpg_bonus_minimum ? floor($old_referral['dark_matter'] / $config->rpg_bonus_divisor) : 0);\n        if($partner_bonus > 0 && $new_referral['dark_matter'] >= $config->rpg_bonus_minimum) {\n          rpg_points_change($new_referral['id_partner'], RPG_REFERRAL, $partner_bonus, \"Incoming From Referral ID {$user_id}\");\n        }\n      }\n    }\n  } else {\n    $debug->warning(\"Error adjusting Dark Matter for player ID {$user_id} (Player Not Found?) with {$dark_matter}. Reason: {$comment}\", 'Dark Matter Change', 402);\n  }\n\n  $dm_change_legit = false;\n  return $rows_affected;\n}\n\nfunction rpg_level_up(&$user, $type, $xp_to_add = 0)\n{\n  $q = 1.03;\n\n  switch($type)\n  {\n    case RPG_STRUCTURE:\n      $field_level = 'lvl_minier';\n      $field_xp = 'xpminier';\n      $b1 = 50;\n      $comment = 'Level Up For Structure Building';\n    break;\n\n    case RPG_RAID:\n      $field_level = 'lvl_raid';\n      $field_xp = 'xpraid';\n      $b1 = 10;\n      $comment = 'Level Up For Raiding';\n    break;\n\n    case RPG_TECH:\n      $field_level = 'player_rpg_tech_level';\n      $field_xp = 'player_rpg_tech_xp';\n      $b1 = 50;\n      $comment = 'Level Up For Research';\n    break;\n\n    case RPG_EXPLORE:\n      $field_level = 'player_rpg_explore_level';\n      $field_xp = 'player_rpg_explore_xp';\n      $b1 = 10;\n      $comment = 'Level Up For Exploration';\n      $q = 1.05;\n    break;\n\n    default:\n      break;\n\n  }\n\n  $xp = &$user[$field_xp];\n\n  if($xp_to_add)\n  {\n    $xp += $xp_to_add;\n    db_user_set_by_id($user['id'], \"`{$field_xp}` = `{$field_xp}` + '{$xp_to_add}'\");\n  }\n\n  $level = $user[$field_level];\n  while($xp > rpg_xp_for_level($level + 1, $b1, $q))\n  {\n    $level++;\n  }\n  $level -= $user[$field_level];\n  if($level > 0)\n  {\n    db_user_set_by_id($user['id'], \"`{$field_level}` = `{$field_level}` + '{$level}'\");\n    rpg_points_change($user['id'], $type, $level * 1000, $comment);\n    $user[$field_level] += $level;\n  }\n}\n\nfunction rpg_xp_for_level($level, $b1, $q)\n{\n  return floor($b1 * (pow($q, $level) - 1)/($q - 1));\n}\n\nfunction rpg_get_miner_xp($level)\n{\n  return rpg_xp_for_level($level, 50, 1.03);\n}\n\nfunction RPG_get_raider_xp($level)\n{\n  return rpg_xp_for_level($level, 10, 1.03);\n}\n\nfunction rpg_get_tech_xp($level)\n{\n  return rpg_xp_for_level($level, 50, 1.03);\n}\n\nfunction rpg_get_explore_xp($level)\n{\n  return rpg_xp_for_level($level, 10, 1.05);\n}\n"
  },
  {
    "path": "includes/functions/sys_maintenance.php",
    "content": "<?php\n\nfunction sys_maintenance()\n{\n  global $config;\n\n  $bashing_time_limit = SN_TIME_NOW - $config->fleet_bashing_scope;\n\n  // TODO: Move here some cleaning procedures from admin/maintenance.php\n  // TODO: Add description of operation to log it\n  $queries = array(\n    // Cleaning outdated records from bashing table\n    array('query' => \"DELETE FROM `{{bashing}}` WHERE bashing_time < {$bashing_time_limit};\", 'result' => false, 'error' => '', 'affected_rows' => 0),\n    // Cleaning ACS table from empty records\n    array('query' => 'DELETE FROM `{{aks}}` WHERE `id` NOT IN (SELECT DISTINCT `fleet_group` FROM `{{fleets}}`);', 'result' => false, 'error' => '', 'affected_rows' => 0),\n    // Cleaning destroyed planets & moons which outlives it's time\n    array('query' => \"DELETE FROM `{{planets}}` WHERE `id_owner` = 0 AND `destruyed` < UNIX_TIMESTAMP();\", 'result' => false, 'error' => '', 'affected_rows' => 0),\n  );\n\n  foreach($queries as &$query)\n  {\n    $query['result'] = doquery($query['query']);\n    $query['error']  = SN::$db->db_error();\n    $query['affected_rows']  = SN::$db->db_affected_rows();\n  }\n\n  return $queries;\n}\n\n// define('SCHEDULER_PREG2', '/^(?:(\\w\\@))?(?:(?:(?:(?:(?:(\\d*)-)?(\\d*)-)?(?:(\\d*)\\ ))?(?:(\\d*):))?(?:(\\d*):))?(\\d*)?$/i');\n\n// format: [<m|w|d|h|m|s>@]<time>\n// first param: m - monthly, w - weekly, d - daily, h - hourly, i - minutly, s - secondly\n// second param: [<months>-[<days|weeks> [<hours>:[<minutes>:]]]<seconds>\n//        valid: '10' - runtime every 10 s\n//        valid: '05:' or '05:00' - runtime every 5 m\n//        valid: '02::' or '02:00:' or '02:00:00' - runtime every 2 h\n//        etc\n\n/*\n * Формат\n *\n * 1. Y-M-D H:I:S  - указывает раз в сколько времени должна запускаться задача и где Y, M, D, H, I, S - соответственно количество лет, месяцев, дней, часов, минут, секунд, определяющих интервал\n * TODO: 2. [<m|w|d|h|m|s>@]<time>\n */\n\nfunction sys_schedule_get_prev_run($scheduleList, $recorded_run = SN_TIME_NOW, $return_next_run = false)\n{\n  static $date_part_names_reverse = array('seconds', 'minutes', 'hours', 'days', 'months', 'years',);\n\n  $possible_schedules = array();\n\n  $recorded_run = strtotime($recorded_run);\n\n  $prev_run_array = getdate($recorded_run);\n  $prev_run_array = array($prev_run_array['seconds'],$prev_run_array['minutes'],$prev_run_array['hours'],$prev_run_array['mday'],$prev_run_array['mon'],$prev_run_array['year']);\n  $today_array = getdate(SN_TIME_NOW);\n  $today_array = array($today_array['seconds'],$today_array['minutes'],$today_array['hours'],$today_array['mday'],$today_array['mon'],$today_array['year']);\n  $scheduleList = explode(',', $scheduleList);\n  array_walk($scheduleList, function(&$schedule) use ($prev_run_array, $today_array, $date_part_names_reverse, &$possible_schedules) {\n    $schedule = array('schedule_array' => array_reverse(explode(':', trim($schedule))));\n\n    $interval = $date_part_names_reverse[count($schedule['schedule_array'])];\n\n    foreach($prev_run_array as $index => $date_part) {\n      $schedule['array']['recorded'][$index] = isset($schedule['schedule_array'][$index]) ? intval($schedule['schedule_array'][$index]) : $date_part;\n      $schedule['array']['now'][$index] = isset($schedule['schedule_array'][$index]) ? intval($schedule['schedule_array'][$index]) : $today_array[$index];\n    }\n    if($schedule['array']['recorded'] == $schedule['array']['now']) {\n      unset($schedule['array']['now']);\n    }\n\n    foreach($schedule['array'] as $name => $array) {\n      $schedule['string'][$name] = \"{$array[5]}-{$array[4]}-{$array[3]} {$array[2]}:{$array[1]}:{$array[0]}\";\n      $schedule['string'][$name . '_next'] = $schedule['string'][$name] . ' +1 ' . $interval;\n      $schedule['string'][$name . '_prev'] = $schedule['string'][$name] . ' -1 ' . $interval;\n    }\n\n    foreach($schedule['string'] as $string) {\n      $timestamp = strtotime($string);\n      $schedule['timestamp'][$timestamp] = $possible_schedules[$timestamp] = date(FMT_DATE_TIME_SQL, strtotime($string));\n    }\n  });\n\n  ksort($possible_schedules);\n\n  $prev_run = 0;\n  $next_run = 0;\n  foreach($possible_schedules as $timestamp => $string_date) {\n    $prev_run = SN_TIME_NOW >= $timestamp ? $timestamp : $prev_run;\n    $next_run = SN_TIME_NOW < $timestamp && !$next_run ? $timestamp : $next_run;\n  }\n\n  return $return_next_run ? $next_run : $prev_run;\n}\n"
  },
  {
    "path": "includes/functions/sys_user.php",
    "content": "<?php\n\nuse DBAL\\db_mysql;\nuse Planet\\DBStaticPlanet;\n\n/**\n * Created by PhpStorm.\n * User: Gorlum\n * Date: 17.04.2015\n * Time: 6:37\n */\n\nfunction sys_user_vacation($user) {\n  global $config;\n\n  if (sys_get_param_str('vacation') == 'leave') {\n    if ($user['vacation'] < SN_TIME_NOW) {\n      $user['vacation']      = 0;\n      $user['vacation_next'] = SN_TIME_NOW + $config->player_vacation_timeout;\n      db_user_set_by_id($user['id'], \"`vacation` = {$user['vacation']}, `vacation_next` = {$user['vacation_next']}\");\n    }\n  }\n\n  if ($user['vacation']) {\n    // sn_sys_logout(false, true);\n    // core_auth::logout(false, true);\n\n    $template = SnTemplate::gettemplate('vacation', true);\n\n    $template->assign_vars(array(\n      'MENU'   => false,\n      'NAVBAR' => false,\n\n      'NAME'         => $user['username'],\n      'VACATION_END' => date(FMT_DATE_TIME, $user['vacation']),\n      'CAN_LEAVE'    => $user['vacation'] <= SN_TIME_NOW,\n      'RANDOM'       => mt_rand(1, 2),\n    ));\n\n    SnTemplate::display($template);\n  }\n\n  return false;\n}\n\nfunction sys_is_multiaccount($user1, $user2) {\n  global $config;\n\n  return $user1['user_lastip'] == $user2['user_lastip'] && !$config->game_multiaccount_enabled;\n}\n\n/**\n * @param        $banner\n * @param        $banned\n * @param        $term\n * @param bool   $is_vacation\n * @param string $reason\n */\nfunction sys_admin_player_ban($banner, $banned, $term, $is_vacation = true, $reason = '') {\n  $ban_current = db_user_by_id($banned['id'], false);\n  $ban_until   = ($ban_current['banaday'] ? $ban_current['banaday'] : SN_TIME_NOW) + $term;\n\n  db_user_set_by_id($banned['id'], \"`banaday` = {$ban_until} \" . ($is_vacation ? \", `vacation` = '{$ban_until}' \" : ''));\n\n  $banned['username'] = SN::$db->db_escape($banned['username']);\n  $banner['username'] = SN::$db->db_escape($banner['username']);\n  doquery(\n    \"INSERT INTO\n      `{{banned}}`\n    SET\n      `ban_user_id` = '{$banned['id']}',\n      `ban_user_name` = '{$banned['username']}',\n      `ban_reason` = '{$reason}',\n      `ban_time` = \" . SN_TIME_NOW . \",\n      `ban_until` = {$ban_until},\n      `ban_issuer_id` = '{$banner['id']}',\n      `ban_issuer_name` = '{$banner['username']}',\n      `ban_issuer_email` = '{$banner['email']}'\n  \");\n\n  DBStaticPlanet::db_planet_set_by_owner($banned['id'],\n    \"`metal_mine_porcent` = 0, `crystal_mine_porcent` = 0, `deuterium_sintetizer_porcent` = 0, `solar_plant_porcent` = 0,\n    `fusion_plant_porcent` = 0, `solar_satelit_porcent` = 0, `ship_sattelite_sloth_porcent` = 0\"\n  );\n}\n\n/**\n * @param        $banner\n * @param        $banned\n * @param string $reason\n */\nfunction sys_admin_player_ban_unset($banner, $banned, $reason = '') {\n  db_user_set_by_id($banned['id'], \"`banaday` = 0, `vacation` = \" . SN_TIME_NOW . \"\");\n\n  $banned['username'] = SN::$db->db_escape($banned['username']);\n  $banner['username'] = SN::$db->db_escape($banner['username']);\n  $reason             = SN::$db->db_escape($reason);\n  doquery(\n    \"INSERT INTO `{{banned}}`\n    SET\n      `ban_user_id` = '{$banned['id']}',\n      `ban_user_name` = '{$banned['username']}',\n      `ban_reason` = '{$reason}',\n      `ban_time` = 0,\n      `ban_until` = \" . SN_TIME_NOW . \",\n      `ban_issuer_id` = '{$banner['id']}',\n      `ban_issuer_name` = '{$banner['username']}',\n      `ban_issuer_email` = '{$banner['email']}'\n  \");\n}\n\n/**\n * @param string $username_unsafe\n * @param string $email_unsafe\n * @param array  $options\n *   [\n *   'user_bot'                 => (int),\n *   'total_points'             => (float),\n *   'language_iso'             => (string),\n *   'options'                  => (string),\n *   'options_extra'            => (string),\n *   'salt'                     => (string),\n *   'password_encoded_unsafe'  => md5(string),\n *   'partner_id'               => (id),\n *   'galaxy'                   => (int),\n *   'system'                   => (int),\n *   'planet'                   => (int),\n *   'planet_options'           => (array),\n *   ]\n *\n * @return array|false\n */\nfunction player_create($username_unsafe, $email_unsafe, $options) {\n  db_mysql::db_transaction_check(true);\n\n  static $player_options_string = 'opt_mnl_spy^1|opt_email_mnl_spy^0|opt_email_mnl_joueur^0|opt_email_mnl_alliance^0|opt_mnl_attaque^1|opt_email_mnl_attaque^0|opt_mnl_exploit^1|opt_email_mnl_exploit^0|opt_mnl_transport^1|opt_email_mnl_transport^0|opt_email_msg_admin^1|opt_mnl_expedition^1|opt_email_mnl_expedition^0|opt_mnl_buildlist^1|opt_email_mnl_buildlist^0|opt_int_navbar_resource_force^1|';\n\n  empty($options['planet_options']) ? $options['planet_options'] = [] : false;\n\n  $field_set = array(\n    'server_name'   => SN_ROOT_VIRTUAL,\n    'register_time' => SN_TIME_NOW,\n    'news_lastread' => SN_TIME_NOW,\n    'user_bot'      => $options['user_bot'] = empty($options['user_bot']) ? USER_BOT_PLAYER : $options['user_bot'],\n\n    'username' => $username_unsafe,\n    'email'    => $email_unsafe,\n    'email_2'  => $email_unsafe,\n\n    'lang' => $options['language_iso'] ? $options['language_iso'] : DEFAULT_LANG,\n    'skin' => DEFAULT_SKIN_NAME,\n\n    'total_points' => $options['total_points'] = empty($options['total_points']) ? 0 : $options['total_points'],\n\n    'options' => (empty($options['options']) ? $player_options_string : $options['options']) . (empty($options['options_extra']) ? '' : $options['options_extra']),\n\n    'galaxy' => $options['galaxy'] = intval($options['galaxy'] ? $options['galaxy'] : 0),\n    'system' => $options['system'] = intval($options['system'] ? $options['system'] : 0),\n    'planet' => $options['planet'] = intval($options['planet'] ? $options['planet'] : 0),\n  );\n\n  !empty($options['salt']) ? $field_set['salt'] = $options['salt'] : false;\n  !empty($options['password_encoded_unsafe']) ? $field_set['password'] = $options['password_encoded_unsafe'] : false;\n  \\DBAL\\DbQuery::build()->setTable('users')->setValues($field_set)->doInsert();\n  $user_new = db_user_by_id(SN::$db->db_insert_id());\n\n  if (!($options['galaxy'] && $options['system'] && $options['planet'])) {\n    $options['galaxy'] = SN::$config->LastSettedGalaxyPos;\n    $options['system'] = SN::$config->LastSettedSystemPos;\n    $segment_size      = floor(SN::$config->game_maxPlanet / 3);\n    $segment           = floor(SN::$config->LastSettedPlanetPos / $segment_size);\n    $segment++;\n    $options['planet'] = mt_rand(1 + $segment * $segment_size, ($segment + 1) * $segment_size);\n\n    while (true) {\n      if ($options['planet'] > SN::$config->game_maxPlanet) {\n        $options['planet'] = mt_rand(0, $segment_size - 1) + 1;\n        $options['system']++;\n      }\n      if ($options['system'] > SN::$config->game_maxSystem) {\n        $options['system'] = 1;\n        $options['galaxy']++;\n      }\n      $options['galaxy'] > SN::$config->game_maxGalaxy ? $options['galaxy'] = 1 : false;\n\n      $galaxy_row = DBStaticPlanet::db_planet_by_gspt($options['galaxy'], $options['system'], $options['planet'], PT_PLANET);\n      if (!$galaxy_row['id']) {\n        SN::$config->db_saveItem(array(\n          'LastSettedGalaxyPos' => $options['galaxy'],\n          'LastSettedSystemPos' => $options['system'],\n          'LastSettedPlanetPos' => $options['planet'],\n        ));\n        break;\n      }\n      $options['planet'] += 3;\n    }\n  }\n  $new_planet_id = uni_create_planet($options['galaxy'], $options['system'], $options['planet'], $user_new['id'], '', true, $options['planet_options']);\n\n  db_user_set_by_id($user_new['id'],\n    \"`id_planet` = '{$new_planet_id}', `current_planet` = '{$new_planet_id}',\n    `galaxy` = '{$options['galaxy']}', `system` = '{$options['system']}', `planet` = '{$options['planet']}'\"\n  );\n\n  $username_safe = SN::$db->db_escape($username_unsafe);\n  doquery(\"REPLACE INTO `{{player_name_history}}` SET `player_id` = {$user_new['id']}, `player_name` = '{$username_safe}'\");\n\n  if (!empty($options['partner_id']) && ($referral_row = db_user_by_id($options['partner_id'], true))) {\n    doquery(\"INSERT INTO `{{referrals}}` SET `id` = {$user_new['id']}, `id_partner` = {$options['partner_id']}\");\n  }\n\n  sys_player_new_adjust($user_new['id'], $new_planet_id);\n\n  dbUpdateUsersCount(SN::$config->pass()->users_amount + 1);\n\n  return $result = db_user_by_id($user_new['id']);\n}\n"
  },
  {
    "path": "includes/functions/tpl_helpers.php",
    "content": "<?php\n\n// Compare function to sort fleet in time order\nuse Fleet\\DbFleetStatic;\nuse Planet\\DBStaticPlanet;\n\nfunction tpl_assign_fleet_compare($a, $b) {\n  if ($a['fleet']['OV_THIS_PLANET'] == $b['fleet']['OV_THIS_PLANET']) {\n    if ($a['fleet']['OV_LEFT'] == $b['fleet']['OV_LEFT']) {\n      return 0;\n    }\n\n    return ($a['fleet']['OV_LEFT'] < $b['fleet']['OV_LEFT']) ? -1 : 1;\n  } else {\n    return $a['fleet']['OV_THIS_PLANET'] ? -1 : 1;\n  }\n}\n\n/**\n * @param array  $fleets\n * @param string $js_name\n *\n * @return array\n */\nfunction tpl_assign_fleet_generate($fleets, $js_name = 'fleets') {\n  $result = [];\n  if (empty($fleets)) {\n    return $result;\n  }\n\n  usort($fleets, 'tpl_assign_fleet_compare');\n\n  foreach ($fleets as $fleet_data) {\n    $temp = $fleet_data['fleet'];\n\n    if ($fleet_data['ships']) {\n      $temp['.']['ships'] = $fleet_data['ships'];\n    }\n\n    $result['.'][$js_name][] = $temp;\n  }\n\n  return $result;\n}\n\n/**\n * For backward compatibility\n *\n * @param template $template\n * @param array    $fleets\n * @param string   $js_name\n *\n * @deprecated\n */\nfunction tpl_assign_fleet(&$template, $fleets, $js_name = 'fleets') {\n  if (!$fleets) {\n    return;\n  }\n\n  $template->assign_recursive(tpl_assign_fleet_generate($fleets, $js_name));\n}\n\n/**\n * function that parses internal fleet representation (as array(id => count))\n *\n * @param $fleet\n * @param $fleet_id\n *\n * @return mixed\n */\nfunction tpl_parse_fleet_sn($fleet, $fleet_id) {\n  global $lang, $user;\n\n  $user_data = &$user;\n\n  $return['fleet'] = array(\n    'ID' => $fleet_id,\n\n    'METAL'     => $fleet[RES_METAL],\n    'CRYSTAL'   => $fleet[RES_CRYSTAL],\n    'DEUTERIUM' => $fleet[RES_DEUTERIUM],\n  );\n\n  foreach ($fleet as $ship_id => $ship_amount) {\n    if (in_array($ship_id, sn_get_groups('fleet'))) {\n      $single_ship_data = get_ship_data($ship_id, $user_data);\n      $return['ships'][$ship_id] = array(\n        'ID'          => $ship_id,\n        'NAME'        => $lang['tech'][$ship_id],\n        'AMOUNT'      => $ship_amount,\n        'CONSUMPTION' => $single_ship_data['consumption'],\n        'SPEED'       => $single_ship_data['speed'],\n        'CAPACITY'    => $single_ship_data['capacity'],\n      );\n    }\n  }\n\n  return $return;\n}\n\n/**\n * @param array      $fleet\n * @param int        $index\n * @param array|bool $user_data\n *\n * @return mixed\n */\nfunction tpl_parse_fleet_db($fleet, $index, $user_data = false) {\n  $result = null;\n\n  return sn_function_call('tpl_parse_fleet_db', [$fleet, $index, $user_data, &$result]);\n}\n\n/**\n * @param array      $fleet\n * @param int        $index\n * @param array|bool $user_data\n * @param            $result\n *\n * @return mixed\n */\nfunction sn_tpl_parse_fleet_db($fleet, $index, $user_data = false, &$result) {\n  global $lang, $user;\n\n  if (!$user_data) {\n    $user_data = $user;\n  }\n\n  if ($fleet['fleet_mess'] == 0 && $fleet['fleet_mission'] == MT_AKS) {\n    $aks = DbFleetStatic::dbAcsGetById($fleet['fleet_group']);\n  }\n\n  $spy_level = $user['id'] == $fleet['fleet_owner'] ? 100 : GetSpyLevel($user);\n\n  $result['fleet'] = isset($result['fleet']) ? $result['fleet'] : array();\n\n  $result['fleet'] = array(\n    'NUMBER' => $index,\n\n    'ID'           => $fleet['fleet_id'],\n    'OWNER'        => $fleet['fleet_owner'],\n    'TARGET_OWNER' => $fleet['fleet_target_owner'],\n\n    'MESSAGE'      => $fleet['fleet_mess'],\n    'MISSION'      => $fleet['fleet_mission'],\n    'MISSION_NAME' => $lang['type_mission'][$fleet['fleet_mission']],\n    'ACS'          => $aks['name'],\n    'AMOUNT'       => $spy_level >= 4 ? (HelperString::numberFloorAndFormat($fleet['fleet_amount']) . ($fleet['fleet_resource_metal'] + $fleet['fleet_resource_crystal'] + $fleet['fleet_resource_deuterium'] ? '+' : '')) : '?',\n\n    'METAL'     => $spy_level >= 8 ? $fleet['fleet_resource_metal'] : 0,\n    'CRYSTAL'   => $spy_level >= 8 ? $fleet['fleet_resource_crystal'] : 0,\n    'DEUTERIUM' => $spy_level >= 8 ? $fleet['fleet_resource_deuterium'] : 0,\n\n    'START_TYPE_TEXT_SH' => $lang['sys_planet_type_sh'][$fleet['fleet_start_type']],\n    'START_COORDS'       => \"[{$fleet['fleet_start_galaxy']}:{$fleet['fleet_start_system']}:{$fleet['fleet_start_planet']}]\",\n    'START_TIME_TEXT'    => date(FMT_DATE_TIME, $fleet['fleet_end_time'] + SN_CLIENT_TIME_DIFF),\n    'START_LEFT'         => floor($fleet['fleet_end_time'] + 1 - SN_TIME_NOW),\n    'START_URL'          => uni_render_coordinates_href($fleet, 'fleet_start_', 3),\n    'START_NAME'         => $fleet['fleet_start_name'],\n\n    'END_TYPE_TEXT_SH' => $lang['sys_planet_type_sh'][$fleet['fleet_end_type']],\n    'END_COORDS'       => \"[{$fleet['fleet_end_galaxy']}:{$fleet['fleet_end_system']}:{$fleet['fleet_end_planet']}]\",\n    'END_TIME_TEXT'    => date(FMT_DATE_TIME, $fleet['fleet_start_time'] + SN_CLIENT_TIME_DIFF),\n    'END_LEFT'         => floor($fleet['fleet_start_time'] + 1 - SN_TIME_NOW),\n    'END_URL'          => uni_render_coordinates_href($fleet, 'fleet_end_', 3),\n    'END_NAME'         => $fleet['fleet_end_name'],\n\n    'STAY_TIME' => date(FMT_DATE_TIME, $fleet['fleet_end_stay'] + SN_CLIENT_TIME_DIFF),\n    'STAY_LEFT' => floor($fleet['fleet_end_stay'] + 1 - SN_TIME_NOW),\n\n    'OV_LABEL'        => $fleet['ov_label'],\n    'EVENT_TIME_TEXT' => date(FMT_DATE_TIME, $fleet['event_time'] + SN_CLIENT_TIME_DIFF),\n    'OV_LEFT'         => floor($fleet['event_time'] + 1 - SN_TIME_NOW),\n    'OV_THIS_PLANET'  => $fleet['ov_this_planet'],\n  );\n\n  $ship_list = explode(';', $fleet['fleet_array']);\n\n  $ship_id = 0;\n  if ($spy_level >= 6) {\n    foreach ($ship_list as $ship_record) {\n      if ($ship_record) {\n        $ship_data = explode(',', $ship_record);\n        if ($spy_level >= 10) {\n          $single_ship_data = get_ship_data($ship_data[0], $user_data);\n          $result['ships'][$ship_data[0]] = array(\n            'ID'          => $ship_data[0],\n            'NAME'        => $lang['tech'][$ship_data[0]],\n            'AMOUNT'      => $ship_data[1],\n            'AMOUNT_TEXT' => HelperString::numberFloorAndFormat($ship_data[1]),\n            'CONSUMPTION' => $single_ship_data['consumption'],\n            'SPEED'       => $single_ship_data['speed'],\n            'CAPACITY'    => $single_ship_data['capacity'],\n          );\n        } else {\n          $result['ships'][$ship_data[0]] = array(\n            'ID'               => $ship_id++,\n            'NAME'             => $lang['tech'][UNIT_SHIPS],\n            'AMOUNT'           => $ship_data[1],\n            'AMOUNT_TEXT'      => HelperString::numberFloorAndFormat($ship_data[1]),\n            'CONSUMPTION'      => 0,\n            'CONSUMPTION_TEXT' => '0',\n            'SPEED'            => 0,\n            'CAPACITY'         => 0,\n          );\n        }\n      }\n    }\n  }\n\n  return $result;\n}\n\nfunction tpl_parse_planet_que($que, $planet, $que_id) {\n  $hangar_que = array();\n  $que_hangar = $que['ques'][$que_id][$planet['id_owner']][$planet['id']];\n  if (!empty($que_hangar)) {\n    foreach ($que_hangar as $que_item) {\n      $hangar_que['que'][] = array('id' => $que_item['que_unit_id'], 'count' => $que_item['que_unit_amount']);\n      $hangar_que[$que_item['que_unit_id']] += $que_item['que_unit_amount'];\n    }\n  }\n\n  return $hangar_que;\n}\n\n/**\n * @param array $planet\n * @param array $fleet_list\n *\n * @return array\n */\nfunction tpl_parse_planet_result_fleet($planet, $fleet_list) {\n  return [\n    'FLEET_OWN'       => $fleet_list['own']['count'],\n    'FLEET_ENEMY'     => $fleet_list['enemy']['count'],\n    'FLEET_NEUTRAL'   => $fleet_list['neutral']['count'],\n    'PLANET_FLEET_ID' => !empty($fleet_list['own']['count']) ? getUniqueFleetId($planet) : 0,\n  ];\n}\n\n\n/**\n * @param int $parentPlanetId\n *\n * @return array\n */\nfunction tpl_parse_planet_moon($parentPlanetId) {\n  $moon_fill = 0;\n  $moon_fleets = [];\n\n  $moon = DBStaticPlanet::db_planet_by_parent($parentPlanetId);\n  if ($moon) {\n    $moon_fill = min(100, floor($moon['field_current'] / eco_planet_fields_max($moon) * 100));\n    $moon_fleets = flt_get_fleets_to_planet($moon);\n  }\n\n  return [\n    'MOON_ID'    => $moon['id'],\n    'MOON_NAME'  => $moon['name'],\n    'MOON_IMG'   => $moon['image'],\n    'MOON_FILL'  => min(100, $moon_fill),\n    'MOON_ENEMY' => !empty($moon_fleets['enemy']['count']) ? $moon_fleets['enemy']['count'] : 0,\n\n    'MOON_PLANET' => $moon['parent_planet'],\n  ];\n}\n\n/**\n * @param array $user\n * @param array $planet\n *\n * @return array\n */\nfunction tpl_parse_planet($user, $planet) {\n  global $lang;\n\n  $que = que_get($planet['id_owner'], $planet['id'], false);\n  $structure_que = tpl_parse_planet_que($que, $planet, QUE_STRUCTURES); // TODO Заменить на que_tpl_parse_element($que_element);\n  $structure_que_first = is_array($structure_que['que']) ? reset($structure_que['que']) : array();\n  $hangar_que = tpl_parse_planet_que($que, $planet, SUBQUE_FLEET); // TODO Заменить на que_tpl_parse_element($que_element);\n  $hangar_que_first = is_array($hangar_que['que']) ? reset($hangar_que['que']) : array();\n  $defense_que = tpl_parse_planet_que($que, $planet, SUBQUE_DEFENSE); // TODO Заменить на que_tpl_parse_element($que_element);\n  $defense_que_first = is_array($defense_que['que']) ? reset($defense_que['que']) : array();\n\n  $result = [\n    'ID'    => $planet['id'],\n    'NAME'  => $planet['name'],\n    'IMAGE' => $planet['image'],\n\n    'GALAXY'      => $planet['galaxy'],\n    'SYSTEM'      => $planet['system'],\n    'PLANET'      => $planet['planet'],\n    'TYPE'        => $planet['planet_type'],\n    'COORDINATES' => uni_render_coordinates($planet),\n    'IS_CAPITAL'  => $planet['planet_type'] == PT_PLANET && $planet['id'] == $user['id_planet'],\n    'IS_MOON'     => $planet['planet_type'] == PT_MOON,\n\n    'METAL_PERCENT'     => $planet['metal_mine_porcent'] * 10,\n    'CRYSTAL_PERCENT'   => $planet['crystal_mine_porcent'] * 10,\n    'DEUTERIUM_PERCENT' => $planet['deuterium_sintetizer_porcent'] * 10,\n\n    'STRUCTURE'       => isset($structure_que_first['id']) ? $lang['tech'][$structure_que_first['id']] : '',\n    'STRUCTURE_SLOTS' => is_array($structure_que['que']) ? count($structure_que['que']) : 0,\n    'HANGAR'          => isset($hangar_que_first['id']) ? $lang['tech'][$hangar_que_first['id']] : '',\n    'HANGAR_SLOTS'    => is_array($hangar_que['que']) ? count($hangar_que['que']) : 0,\n    'DEFENSE'         => isset($defense_que_first['id']) ? $lang['tech'][$defense_que_first['id']] : '',\n    'DEFENSE_SLOTS'   => is_array($defense_que['que']) ? count($defense_que['que']) : 0,\n\n    'FIELDS_CUR' => $planet['field_current'],\n    'FIELDS_MAX' => eco_planet_fields_max($planet),\n    'FILL'       => min(100, floor($planet['field_current'] / eco_planet_fields_max($planet) * 100)),\n\n    'PLANET_GOVERNOR_ID'        => $planet['PLANET_GOVERNOR_ID'],\n    'PLANET_GOVERNOR_NAME'      => $lang['tech'][$planet['PLANET_GOVERNOR_ID']],\n    'PLANET_GOVERNOR_LEVEL'     => $planet['PLANET_GOVERNOR_LEVEL'],\n    'PLANET_GOVERNOR_LEVEL_MAX' => get_unit_param($planet['PLANET_GOVERNOR_ID'], P_MAX_STACK),\n  ];\n\n  if (!empty($que['ques'][QUE_STRUCTURES][$planet['id_owner']][$planet['id']])) {\n    $result['building_que'] = [];\n    $building_que = &$que['ques'][QUE_STRUCTURES][$planet['id_owner']][$planet['id']];\n    foreach ($building_que as $que_element) {\n      $result['building_que'][] = que_tpl_parse_element($que_element);\n    }\n  }\n\n  return $result;\n}\n\nfunction flt_get_fleets_to_planet($planet, $fleet_db_list = 0) {\n  if (!($planet && $planet['id']) && !$fleet_db_list) {\n    return $planet;\n  }\n\n  global $user;\n\n  if ($fleet_db_list === 0) {\n    $fleet_db_list = DbFleetStatic::fleet_and_missiles_list_by_coordinates($planet);\n  }\n\n  foreach ($fleet_db_list as $fleet) {\n    if ($fleet['fleet_owner'] == $user['id']) {\n      if ($fleet['fleet_mission'] == MT_MISSILE) {\n        continue;\n      }\n      $fleet_ownage = 'own';\n    } else {\n      switch ($fleet['fleet_mission']) {\n        case MT_ATTACK:\n        case MT_AKS:\n        case MT_DESTROY:\n        case MT_MISSILE:\n          $fleet_ownage = 'enemy';\n        break;\n\n        default:\n          $fleet_ownage = 'neutral';\n        break;\n\n      }\n    }\n\n    $fleet_list[$fleet_ownage]['fleets'][$fleet['fleet_id']] = $fleet;\n\n    if ($fleet['fleet_mess'] == 1 || ($fleet['fleet_mess'] == 0 && $fleet['fleet_mission'] == MT_RELOCATE) || ($fleet['fleet_target_owner'] != $user['id'])) {\n//      $fleet_sn = flt_expand($fleet);\n      $fleet_sn = sys_unit_str2arr($fleet['fleet_array']);\n      foreach ($fleet_sn as $ship_id => $ship_amount) {\n        if (in_array($ship_id, sn_get_groups('fleet'))) {\n          $fleet_list[$fleet_ownage]['total'][$ship_id] += $ship_amount;\n        }\n      }\n    }\n\n    $fleet_list[$fleet_ownage]['count']++;\n    $fleet_list[$fleet_ownage]['amount'] += $fleet['fleet_amount'];\n    $fleet_list[$fleet_ownage]['total'][RES_METAL] += $fleet['fleet_resource_metal'];\n    $fleet_list[$fleet_ownage]['total'][RES_CRYSTAL] += $fleet['fleet_resource_crystal'];\n    $fleet_list[$fleet_ownage]['total'][RES_DEUTERIUM] += $fleet['fleet_resource_deuterium'];\n  }\n\n  return $fleet_list;\n}\n\n/**\n * @param template $template\n * @param array    $planetrow\n * @param array    $fleets_to_planet\n */\nfunction tpl_set_resource_info(&$template, $planetrow, $fleets_to_planet = array()) {\n  $template->assign_vars(array(\n    'RESOURCE_ROUNDING' => 0,\n\n    'PLANET_ENERGY'                   => $planetrow['energy_used'],\n    'ENERGY_BALANCE_NUMBER'           => $planetrow['energy_max'] - $planetrow['energy_used'],\n    'ENERGY_BALANCE'                  => prettyNumberStyledDefault($planetrow['energy_max'] - $planetrow['energy_used']),\n    'ENERGY_MAX_NUMBER'               => $planetrow['energy_max'],\n    'ENERGY_MAX_NUMBER_TEXT_NO_COLOR' => HelperString::numberFloorAndFormat($planetrow['energy_max']),\n    'ENERGY_MAX_NUMBER_TEXT'          => Tools::numberPercentSpan($planetrow['energy_max'], $planetrow['energy_used']),\n    'ENERGY_MAX'                      => prettyNumberStyledCompare($planetrow['energy_max'], -$planetrow['energy_used']),\n    'ENERGY_FILL'                     => round(($planetrow[\"energy_used\"] / ($planetrow[\"energy_max\"] + 1)) * 100, 0),\n\n    'PLANET_METAL'              => floor($planetrow[\"metal\"]),\n    'PLANET_METAL_TEXT'         => prettyNumberStyledCompare($planetrow[\"metal\"], $planetrow[\"metal_max\"]),\n    'PLANET_METAL_MAX'          => floor($planetrow[\"metal_max\"]),\n    'PLANET_METAL_MAX_TEXT'     => Tools::numberPercentSpan($planetrow[\"metal_max\"], $planetrow[\"metal\"]),\n    'PLANET_METAL_MAX_NO_COLOR' => HelperString::numberFloorAndFormat($planetrow[\"metal_max\"]),\n    'PLANET_METAL_FILL'         => round(($planetrow[\"metal\"] / ($planetrow[\"metal_max\"] + 1)) * 100, 0),\n    'PLANET_METAL_PERHOUR'      => round($planetrow[\"metal_perhour\"], 5),\n    'PLANET_METAL_FLEET_TEXT'   => prettyNumberStyledDefault($fleets_to_planet[$planetrow['id']]['fleet']['METAL']),\n\n    'PLANET_CRYSTAL'              => floor($planetrow[\"crystal\"]),\n    'PLANET_CRYSTAL_TEXT'         => prettyNumberStyledCompare($planetrow[\"crystal\"], $planetrow[\"crystal_max\"]),\n    'PLANET_CRYSTAL_MAX'          => floor($planetrow[\"crystal_max\"]),\n    'PLANET_CRYSTAL_MAX_TEXT'     => Tools::numberPercentSpan($planetrow[\"crystal_max\"], $planetrow[\"crystal\"]),\n    'PLANET_CRYSTAL_MAX_NO_COLOR' => HelperString::numberFloorAndFormat($planetrow[\"crystal_max\"]),\n    'PLANET_CRYSTAL_FILL'         => round(($planetrow[\"crystal\"] / ($planetrow[\"crystal_max\"] + 1)) * 100, 0),\n    'PLANET_CRYSTAL_PERHOUR'      => round($planetrow[\"crystal_perhour\"], 5),\n    'PLANET_CRYSTAL_FLEET_TEXT'   => prettyNumberStyledDefault($fleets_to_planet[$planetrow['id']]['fleet']['CRYSTAL']),\n\n    'PLANET_DEUTERIUM'              => floor($planetrow[\"deuterium\"]),\n    'PLANET_DEUTERIUM_TEXT'         => prettyNumberStyledCompare($planetrow[\"deuterium\"], $planetrow[\"deuterium_max\"]),\n    'PLANET_DEUTERIUM_MAX'          => floor($planetrow[\"deuterium_max\"]),\n    'PLANET_DEUTERIUM_MAX_TEXT'     => Tools::numberPercentSpan($planetrow[\"deuterium_max\"], $planetrow[\"deuterium\"]),\n    'PLANET_DEUTERIUM_MAX_NO_COLOR' => HelperString::numberFloorAndFormat($planetrow[\"deuterium_max\"]),\n    'PLANET_DEUTERIUM_FILL'         => round(($planetrow[\"deuterium\"] / ($planetrow[\"deuterium_max\"] + 1)) * 100, 0),\n    'PLANET_DEUTERIUM_PERHOUR'      => round($planetrow[\"deuterium_perhour\"], 5),\n    'PLANET_DEUTERIUM_FLEET_TEXT'   => prettyNumberStyledDefault($fleets_to_planet[$planetrow['id']]['fleet']['DEUTERIUM']),\n  ));\n}\n\n/**\n * @return int[][]\n */\nfunction templateFillPercent() {\n  $result = [];\n  for ($i = 100; $i >= 0; $i -= 10) {\n    $result[] = ['PERCENT' => $i];\n  }\n\n  return ['.' => ['percent' => $result]];\n}\n"
  },
  {
    "path": "includes/functions/uni_functions.php",
    "content": "<?php\n\nuse Fleet\\DbFleetStatic;\nuse Planet\\DBStaticPlanet;\nuse Universe\\Universe;\n\nfunction uni_create_planet_get_density($position_data, $user_row, $planet_sectors) {\n  $density_list = sn_get_groups('planet_density');\n  $density_min = reset($density_list);\n  unset($density_list[PLANET_DENSITY_NONE]);\n\n  $possible_cores = array();\n  $probability = 0;\n  foreach ($density_list as $possible_core_id => $core_data) {\n    if (!$core_data[UNIT_PLANET_DENSITY_RARITY]) {\n      continue;\n    }\n\n    if (\n      // Core type exists\n      in_array($possible_core_id, $position_data['core_types'])\n      // Limit core type with planet sector count\n      && $planet_sectors < $density_list[$possible_core_id][UNIT_PLANET_DENSITY_MAX_SECTORS]\n      // Limit core type with player AstroTech level\n      && (empty($user_row) || mrc_get_level($user_row, null, TECH_ASTROTECH) >= $density_list[$possible_core_id][UNIT_PLANET_DENSITY_MIN_ASTROTECH])\n    ) {\n      // Фильтруем типы ядер, которые не подходят по размеру планеты\n      $probability += $density_list[$possible_core_id][UNIT_PLANET_DENSITY_RARITY];\n      $possible_cores[$possible_core_id] = array(\n        UNIT_PLANET_DENSITY_INDEX  => $possible_core_id,\n        UNIT_PLANET_DENSITY_RARITY => $probability,\n        UNIT_PLANET_DENSITY        => mt_rand($density_min[UNIT_PLANET_DENSITY], $density_list[$possible_core_id][UNIT_PLANET_DENSITY] - 1),\n      );\n    }\n    $density_min = $density_list[$possible_core_id];\n  }\n\n  $random = mt_rand(1, $probability);\n  $selected_core = null;\n  foreach ($possible_cores as $core_type => $core_info) {\n    if ($random <= $core_info[UNIT_PLANET_DENSITY_RARITY]) {\n      $selected_core = $core_info;\n      break;\n    }\n  }\n\n  return $selected_core;\n}\n\n/**\n * @param int        $Galaxy\n * @param int        $System\n * @param int        $Position\n * @param int        $PlanetOwnerID\n * @param string     $planet_name_unsafe\n * @param bool|false $HomeWorld\n * @param array      $options = [\n *   'skip_check' => true,\n *   'user_row' => [],\n *   'force_name' => (string), // Force full planet name\n *   'image' => (string), // Force image\n * ]\n *\n * @return bool\n */\nfunction uni_create_planet($Galaxy, $System, $Position, $PlanetOwnerID, $planet_name_unsafe = '', $HomeWorld = false, $options = []) {\n  $Position = intval($Position);\n\n  if (!isset($options['skip_check']) && DBStaticPlanet::db_planet_by_gspt($Galaxy, $System, $Position, PT_PLANET)) {\n    return false;\n  }\n\n  $user_row = !empty($options['user_row']) && is_array($options['user_row']) ? $options['user_row'] : db_user_by_id($PlanetOwnerID);\n\n\n  $planet_generator = sn_get_groups('planet_generator');\n\n  if ($HomeWorld) {\n    $position_data = $planet_generator[0];\n  } else {\n    $position_data = $planet_generator[$Position >= UNIVERSE_RANDOM_PLANET_START || $Position < 1 ? UNIVERSE_RANDOM_PLANET_START : $Position];\n    if ($Position >= UNIVERSE_RANDOM_PLANET_START) {\n      // Корректируем температуру для планеты-странника\n      $position_data['t_max_max'] -= UNIVERSE_RANDOM_PLANET_TEMPERATURE_DECREASE * ($Position - UNIVERSE_RANDOM_PLANET_START);\n    }\n  }\n\n  if (!empty($options['image'])) {\n    $planet_image = $options['image'];\n  } else {\n    $planet_images = sn_get_groups('planet_images');\n    $planet_image = $position_data['planet_images'][mt_rand(0, count($position_data['planet_images']) - 1)];\n    $planet_image .= 'planet' . $planet_images[$planet_image][mt_rand(0, count($planet_images[$planet_image]) - 1)];\n  }\n\n  $t_max = sn_rand_gauss_range($position_data['t_max_min'], $position_data['t_max_max'], true, 1.3, true);\n  $t_min = $t_max - sn_rand_gauss_range($position_data['t_delta_min'], $position_data['t_delta_max'], true, 1.3, true);\n\n  $planet_sectors = sn_rand_gauss_range($position_data['size_min'], $position_data['size_max'], true, 1.7, true);\n//  $planet_diameter = round(pow($planet_sectors, 2) * 1000);\n  $planet_diameter = round(sqrt($planet_sectors) * 1000);\n\n  $core_info = uni_create_planet_get_density($position_data, $user_row, $planet_sectors);\n\n  $planet_name_unsafe = !empty($options['force_name']) ? $options['force_name'] :\n    ($user_row['username'] . ' ' . (\n      $HomeWorld\n        ? SN::$lang['sys_capital']\n        : ($planet_name_unsafe ?: SN::$lang['sys_colo_default_name'])\n      )\n    );\n\n  $planet['name'] = SN::$db->db_escape(strip_tags(trim($planet_name_unsafe)));\n  $planet['id_owner'] = $PlanetOwnerID;\n  $planet['last_update'] = SN_TIME_NOW;\n  $planet['image'] = $planet_image;\n\n  $planet['galaxy'] = $Galaxy;\n  $planet['system'] = $System;\n  $planet['planet'] = $planet['position_original'] = $Position;\n  $planet['planet_type'] = PT_PLANET;\n\n  $planet['diameter'] = $planet_diameter;\n  $planet['field_max'] = $planet['field_max_original'] = $planet_sectors;\n  $planet['density'] = $core_info[UNIT_PLANET_DENSITY];\n  $planet['density_index'] = $core_info[UNIT_PLANET_DENSITY_INDEX];\n  $planet['temp_min'] = $planet['temp_min_original'] = $t_min;\n  $planet['temp_max'] = $planet['temp_max_original'] = $t_max;\n\n  $planet['metal'] = SN::$config->eco_planet_starting_metal;\n  $planet['crystal'] = SN::$config->eco_planet_starting_crystal;\n  $planet['deuterium'] = SN::$config->eco_planet_starting_deuterium;\n  $planet['metal_max'] = SN::$config->eco_planet_storage_metal;\n  $planet['crystal_max'] = SN::$config->eco_planet_storage_crystal;\n  $planet['deuterium_max'] = SN::$config->eco_planet_storage_deuterium;\n\n  $density_info_resources = &$density_list[$core_info[UNIT_PLANET_DENSITY_INDEX]][UNIT_RESOURCES];\n  $planet['metal_perhour'] = SN::$config->metal_basic_income * $density_info_resources[RES_METAL];\n  $planet['crystal_perhour'] = SN::$config->crystal_basic_income * $density_info_resources[RES_CRYSTAL];\n  $planet['deuterium_perhour'] = SN::$config->deuterium_basic_income * $density_info_resources[RES_DEUTERIUM];\n\n  $RetValue = SN::db_ins_record(LOC_PLANET,\n    \"`name` = '{$planet['name']}', `id_owner` = '{$planet['id_owner']}', `last_update` = '{$planet['last_update']}', `image` = '{$planet['image']}',\n      `galaxy` = '{$planet['galaxy']}', `system` = '{$planet['system']}', `planet` = '{$planet['planet']}', `planet_type` = '{$planet['planet_type']}', `position_original` = '{$planet['position_original']}',\n      `diameter` = '{$planet['diameter']}', `field_max` = '{$planet['field_max']}', `field_max_original` = '{$planet['field_max_original']}',\n      `density` = '{$planet['density']}', `density_index` = '{$planet['density_index']}',\n      `temp_min` = '{$planet['temp_min']}', `temp_max` = '{$planet['temp_max']}', `temp_min_original` = '{$planet['temp_min_original']}', `temp_max_original` = '{$planet['temp_max_original']}',\n      `metal` = '{$planet['metal']}', `metal_perhour` = '{$planet['metal_perhour']}', `metal_max` = '{$planet['metal_max']}',\n      `crystal` = '{$planet['crystal']}', `crystal_perhour` = '{$planet['crystal_perhour']}', `crystal_max` = '{$planet['crystal_max']}',\n      `deuterium` = '{$planet['deuterium']}', `deuterium_perhour` = '{$planet['deuterium_perhour']}', `deuterium_max` = '{$planet['deuterium_max']}'\"\n  );\n\n  return is_array($RetValue) ? $RetValue['id'] : false; // OK\n}\n\n/**\n * uni_create_moon.php\n *\n * UNI: Create moon record\n *\n * V2.1  - copyright (c) 2010-2011 by Gorlum for http://supernova.ws\n *   [~] Renamed CreateOneMoonRecord to uni_create_moon\n *   [-] Removed unsed $MoonID parameter from call\n *   [~] PCG1 compliant\n * V2.0  - copyright (c) 2010 by Gorlum for http://supernova.ws\n *   [+] Deep rewrite to rid of using `galaxy` and `lunas` tables greatly reduce numbers of SQL-queries\n * @version   1.1\n * @copyright 2008\n */\n\n/**\n * @param        $pos_galaxy\n * @param        $pos_system\n * @param        $pos_planet\n * @param        $user_id\n * @param int    $size <p><b>0</b> - random moon size</p>\n * @param bool   $update_debris\n * @param array  $options ['name' => (str), 'image' => (str)]\n *\n * @return array\n */\nfunction uni_create_moon($pos_galaxy, $pos_system, $pos_planet, $user_id, $size = 0, $update_debris = true, $options = []) {\n  global $lang;\n\n  $moon_row = [];\n  $moon = DBStaticPlanet::db_planet_by_gspt($pos_galaxy, $pos_system, $pos_planet, PT_MOON);\n  if (empty($moon['id'])) {\n    $moon_planet = DBStaticPlanet::db_planet_by_gspt($pos_galaxy, $pos_system, $pos_planet, PT_PLANET);\n\n    if ($moon_planet['id']) {\n      $base_storage_size = BASE_STORAGE_SIZE;\n\n      if (empty($size)) {\n        $size = Universe::moonSizeRandom();\n      }\n\n      $temp_min = $moon_planet['temp_min'] - rand(10, 45);\n      $temp_max = $temp_min + 40;\n\n      $moon_name = !empty($options['name']) ? $options['name'] : \"{$moon_planet['name']} {$lang['sys_moon']}\";\n      $moon_name_safe = SN::$db->db_escape($moon_name);\n\n      $field_max = ceil($size / 1000);\n\n      $moon_image = !empty($options['image']) ? $options['image'] : 'mond';\n\n      $moon_row = SN::db_ins_record(LOC_PLANET,\n        \"`id_owner` = '{$user_id}', `parent_planet` = '{$moon_planet['id']}', `name` = '{$moon_name_safe}', `last_update` = \" . SN_TIME_NOW . \", `image` = '{$moon_image}',\n          `galaxy` = '{$pos_galaxy}', `system` = '{$pos_system}', `planet` = '{$pos_planet}', `planet_type` = \" . PT_MOON . \",\n          `diameter` = '{$size}', `field_max` = '{$field_max}', `density` = 2500, `density_index` = 2, `temp_min` = '{$temp_min}', `temp_max` = '{$temp_max}',\n          `metal` = '0', `metal_perhour` = '0', `metal_max` = '{$base_storage_size}',\n          `crystal` = '0', `crystal_perhour` = '0', `crystal_max` = '{$base_storage_size}',\n          `deuterium` = '0', `deuterium_perhour` = '0', `deuterium_max` = '{$base_storage_size}'\"\n      );\n\n      if ($update_debris) {\n        $debris_spent = round($size / 1000 * Universe::moonPercentCostInDebris());\n        $metal_spent = round(min($moon_planet['debris_metal'], $debris_spent * mt_rand(50 * 1000, 75 * 1000) / (100 * 1000))); // Trick for higher mt_rand resolution\n        $crystal_spent = min($moon_planet['debris_crystal'], $debris_spent - $metal_spent);\n        $metal_spent = min($moon_planet['debris_metal'], $debris_spent - $crystal_spent); // Need if crystal less then their part\n        DBStaticPlanet::db_planet_set_by_id($moon_planet['id'], \"`debris_metal` = GREATEST(0, `debris_metal` - {$metal_spent}), `debris_crystal` = GREATEST(0, `debris_crystal` - {$crystal_spent})\");\n      }\n    }\n  }\n\n  return $moon_row;\n}\n\n/*\n *\n * @function SetSelectedPlanet\n *\n * @history\n *\n * 4 - copyright (c) 2014 by Gorlum for http://supernova.ws\n *   [!] Full rewrote from scratch\n * 3 - copyright (c) 2009-2011 by Gorlum for http://supernova.ws\n *   [+] Added handling case when current_planet does not exists or didn't belong to user\n *   [+] Moved from SetSelectedPlanet.php\n *   [+] Function now return\n *   [~] Complies with PCG1\n * 2 - copyright (c) 2009-2011 by Gorlum for http://supernova.ws\n *   [~] Security checked for SQL-injection\n * 1 - copyright 2008 By Chlorel for XNova\n *\n */\nfunction SetSelectedPlanet(&$user) {\n  $planet_row['id'] = $user['current_planet'];\n\n  // Пытаемся переключить на новую планету\n  if (($selected_planet = sys_get_param_id('cp')) && $selected_planet != $user['current_planet']) {\n    $planet_row = DBStaticPlanet::db_planet_by_id_and_owner($selected_planet, $user['id'], false, 'id');\n  } else {\n    $planet_row = DBStaticPlanet::db_planet_by_id($planet_row['id']);\n  }\n\n  // Если новая планета не найдена или было переключения - проверяем текущую выбранную планету\n  if (!isset($planet_row['id'])) // || $planet_row['id'] != $user['current_planet']\n  {\n    $planet_row = DBStaticPlanet::db_planet_by_id_and_owner($user['current_planet'], $user['id'], false, 'id');\n    // Если текущей планеты не существует - выставляем Столицу\n    if (!isset($planet_row['id'])) {\n      $planet_row = DBStaticPlanet::db_planet_by_id_and_owner($user['id_planet'], $user['id'], false, 'id');\n      // Если и столицы не существует - значит что-то очень не так с записью пользователя\n      if (!isset($planet_row['id'])) {\n        global $debug;\n        $debug->error(\"User ID {$user['id']} has Capital planet {$user['id_planet']} but this planet does not exists\", 'User record error', 502);\n      }\n    }\n  }\n\n  // Если производилось переключение планеты - делаем запись в юзере\n  if ($user['current_planet'] != $planet_row['id']) {\n    db_user_set_by_id($user['id'], \"`current_planet` = '{$planet_row['id']}'\");\n    $user['current_planet'] = $planet_row['id'];\n  }\n\n  return $user['current_planet'];\n}\n\n// ----------------------------------------------------------------------------------------------------------------\nfunction uni_render_coordinates($from, $prefix = '') {\n  return \"[{$from[$prefix . 'galaxy']}:{$from[$prefix . 'system']}:{$from[$prefix . 'planet']}]\";\n}\n\nfunction uni_render_planet($from) {\n  return \"{$from['name']} [{$from['galaxy']}:{$from['system']}:{$from['planet']}]\";\n}\n\nfunction uni_render_planet_full($from, $prefix = '', $html_safe = true, $include_id = false) {\n  global $lang;\n\n  if (!$from['id']) {\n    $result = $lang['sys_planet_expedition'];\n  } else {\n    $from_planet_id = $include_id ? (\n      'ID {' . ($from['id'] ? $from['id'] : ($from[$prefix . 'planet_id'] ? $from[$prefix . 'planet_id'] : 0)) . '} '\n    ) : '';\n\n    $from_planet_type = $from['planet_type'] ? $from['planet_type'] : ($from[$prefix . 'type'] ? $from[$prefix . 'type'] : 0);\n    $from_planet_type = ($from_planet_type ? ' ' . $lang['sys_planet_type_sh'][$from_planet_type] : '');\n\n    $result = $from_planet_id . uni_render_coordinates($from, $prefix) . $from_planet_type . ($from['name'] ? ' ' . $from['name'] : '');\n    $result = $html_safe ? HelperString::htmlEncode($result, HTML_ENCODE_PREFORM | HTML_ENCODE_SPACE) : $result;\n  }\n\n  return $result;\n}\n\n/**\n * @param \\Planet\\Planet $from\n *\n * @return string\n */\nfunction uni_render_coordinates_planet_object($from) {\n  return is_object($from) ? \"[{$from->galaxy}:{$from->system}:{$from->planet}]\" : '[-:-:-]';\n}\n\n\n/**\n * @param \\Planet\\Planet $from\n * @param bool           $html_safe\n * @param bool           $include_id\n *\n * @return mixed|null|string\n */\nfunction uni_render_planet_object_full($from, $html_safe = true, $include_id = false) {\n  if (empty($from->id)) {\n    $result = SN::$lang['sys_planet_expedition'];\n  } else {\n    $from_planet_id = $include_id ? (\n      'ID {' . ($from->id ? $from->id : 0) . '} '\n    ) : '';\n\n    $from_planet_type = isset($from->planet_type) ? $from->planet_type : 0;\n    $from_planet_type = ($from_planet_type ? ' ' . SN::$lang['sys_planet_type_sh'][$from_planet_type] : '');\n\n    $result = $from_planet_id . uni_render_coordinates_planet_object($from) . $from_planet_type . (isset($from->name) ? ' ' . $from->name : '');\n    $result = $html_safe ? HelperString::htmlEncode($result, HTML_ENCODE_PREFORM | HTML_ENCODE_SPACE) : $result;\n  }\n\n  return $result;\n}\n\nfunction uni_render_coordinates_url($from, $prefix = '', $page = 'galaxy.php') {\n  return $page . (strpos($page, '?') === false ? '?' : '&') . \"galaxy={$from[$prefix . 'galaxy']}&system={$from[$prefix . 'system']}&planet={$from[$prefix . 'planet']}\";\n}\n\nfunction uni_render_coordinates_href($from, $prefix = '', $mode = 0, $fleet_type = '') {\n  return '<a href=\"' . uni_render_coordinates_url($from, $prefix, \"galaxy.php?mode={$mode}\") . '\"' . ($fleet_type ? \" {$fleet_type}\" : '') . '>' . uni_render_coordinates($from, $prefix) . '</a>';\n}\n\nfunction uni_get_time_to_jump($moon_row) {\n  $jump_gate_level = mrc_get_level($user, $moon_row, STRUC_MOON_GATE);\n\n  return $jump_gate_level ? max(0, $moon_row['last_jump_time'] + abs(60 * 60 / $jump_gate_level) - SN_TIME_NOW) : 0;\n}\n\nfunction uni_coordinates_valid($coordinates, $prefix = '') {\n  global $config;\n\n  // array_walk($coordinates, 'intval');\n  $coordinates[\"{$prefix}galaxy\"] = intval($coordinates[\"{$prefix}galaxy\"]);\n  $coordinates[\"{$prefix}system\"] = intval($coordinates[\"{$prefix}system\"]);\n  $coordinates[\"{$prefix}planet\"] = intval($coordinates[\"{$prefix}planet\"]);\n\n  return\n    isset($coordinates[\"{$prefix}galaxy\"]) && $coordinates[\"{$prefix}galaxy\"] > 0 && $coordinates[\"{$prefix}galaxy\"] <= $config->game_maxGalaxy &&\n    isset($coordinates[\"{$prefix}system\"]) && $coordinates[\"{$prefix}system\"] > 0 && $coordinates[\"{$prefix}system\"] <= $config->game_maxSystem &&\n    isset($coordinates[\"{$prefix}planet\"]) && $coordinates[\"{$prefix}planet\"] > 0 && $coordinates[\"{$prefix}planet\"] <= $config->game_maxPlanet;\n}\n\nfunction uni_planet_teleport_check($user, $planetrow, $new_coordinates = null) {\n  global $lang, $config;\n\n  try {\n    if ($planetrow['planet_teleport_next'] && $planetrow['planet_teleport_next'] > SN_TIME_NOW) {\n      throw new exception($lang['ov_teleport_err_cooldown'], ERR_ERROR);\n    }\n\n    if (mrc_get_level($user, false, RES_DARK_MATTER) < $config->planet_teleport_cost) {\n      throw new exception($lang['ov_teleport_err_no_dark_matter'], ERR_ERROR);\n    }\n\n    // TODO: Replace quick-check with using gathered flying fleet data\n//    $incoming = doquery(\"SELECT COUNT(*) AS incoming FROM {{fleets}} WHERE\n//      (fleet_start_galaxy = {$planetrow['galaxy']} and fleet_start_system = {$planetrow['system']} and fleet_start_planet = {$planetrow['planet']})\n//      or\n//      (fleet_end_galaxy = {$planetrow['galaxy']} and fleet_end_system = {$planetrow['system']} and fleet_end_planet = {$planetrow['planet']})\", true);\n//    if(!empty($incoming['incoming'])) {\n//      throw new exception($lang['ov_teleport_err_fleet'], ERR_ERROR);\n//    }\n    if (DbFleetStatic::fleet_count_incoming($planetrow['galaxy'], $planetrow['system'], $planetrow['planet'])) {\n      throw new exception($lang['ov_teleport_err_fleet'], ERR_ERROR);\n    }\n\n    //$incoming = doquery(\"SELECT COUNT(*) AS incoming FROM {{iraks}} WHERE fleet_end_galaxy = {$planetrow['galaxy']} and fleet_end_system = {$planetrow['system']} and fleet_end_planet = {$planetrow['planet']}\", true);\n    //if($incoming['incoming']) {\n    //  throw new exception($lang['ov_teleport_err_fleet'], ERR_ERROR);\n    //}\n\n    if (is_array($new_coordinates)) {\n      $new_coordinates['planet_type'] = PT_PLANET;\n      $incoming = DBStaticPlanet::db_planet_by_vector($new_coordinates, '');\n      if ($incoming['id']) {\n        throw new exception($lang['ov_teleport_err_destination_busy'], ERR_ERROR);\n      }\n    }\n\n    $response = array(\n      'result'  => ERR_NONE,\n      'message' => '',\n    );\n  } catch (exception $e) {\n    $response = array(\n      'result'  => $e->getCode(),\n      'message' => $e->getMessage(),\n    );\n  }\n\n  return $response;\n}\n"
  },
  {
    "path": "includes/general/general.php",
    "content": "<?php /** @noinspection PhpDeprecationInspection */\n/** @noinspection PhpOptionalBeforeRequiredParametersInspection */\n/** @noinspection PhpUnusedParameterInspection */\n\n\nrequire_once('general_math.php');\nrequire_once('general_compatibility.php');\nrequire_once('general_params.php');\nrequire_once('general_nickRender.php');\nrequire_once('general_formatters.php');\nrequire_once('general_validators.php');\nrequire_once('general_unitFunctions.php');\nrequire_once('general_playerFunctions.php');\nrequire_once('general_planetFunctions.php');\nrequire_once('general_urlAndHttp.php');\n\nrequire_once('general_pname.php');\n\n// HOOKS AND HANDLERS ----------------------------------------------------------------------------------------------------------------\n/**\n * Function wrapping\n *\n * Due glitch in PHP 5.3.1 SuperNova is incompatible with this version\n * Reference: https://bugs.php.net/bug.php?id=50394\n *\n * @param string $func_name\n * @param array  $func_arg\n *\n * @return mixed\n */\nfunction sn_function_call($func_name, $func_arg = array()) {\n  global $functions; // All data in $functions should be normalized to valid 'callable' state: '<function_name>'|array('<object_name>', '<method_name>')\n\n  $result = null;\n  if (is_array($functions[$func_name]) && !is_callable($functions[$func_name])) {\n    // Chain-callable functions should be made as following:\n    // 1. Never use incomplete calls with parameters \"by default\"\n    // 2. Reserve last parameter for cumulative result\n    // 3. Use same format for original value and cumulative result (if there is original value)\n    // 4. Honor cumulative result\n    // 5. Return cumulative result\n    foreach ($functions[$func_name] as $func_chain_name) {\n      // По идее - это уже тут не нужно, потому что оно все должно быть callable к этому моменту\n      // Но для старых модулей...\n      if (is_callable($func_chain_name)) {\n        $result = call_user_func_array($func_chain_name, $func_arg);\n      }\n    }\n  } else {\n    // TODO: This is left for backward compatibility. Appropriate code should be rewrote!\n    $func_name = isset($functions[$func_name]) && is_callable($functions[$func_name]) ? $functions[$func_name] : ('sn_' . $func_name);\n    if (is_callable($func_name)) {\n      $result = call_user_func_array($func_name, $func_arg);\n    }\n  }\n\n  return $result;\n}\n\n/**\n * @param        $hook_list\n * @param        $template\n * @param string $hook_type - тип хука 'model' или 'view'\n * @param string $page_name - имя страницы, для которого должен был быть выполнен хук\n *\n * @noinspection PhpParameterByRefIsNotUsedAsReferenceInspection\n */\nfunction execute_hooks(&$hook_list, &$template, $hook_type = null, $page_name = null) {\n  if (!empty($hook_list)) {\n    foreach ($hook_list as $hook) {\n      if (is_callable($hook_call = (is_string($hook) ? $hook : (is_array($hook) ? $hook['callable'] : $hook->callable)))) {\n        $template = call_user_func($hook_call, $template, $hook_type, $page_name);\n      }\n    }\n  }\n}\n\nfunction sn_sys_handler_add(&$functions, $handler_list, $class_module_name = '', $sub_type = '') {\n  if (isset($handler_list) && is_array($handler_list) && !empty($handler_list)) {\n    foreach ($handler_list as $function_name => $function_data) {\n      sys_handler_add_one($functions, $function_name, $function_data, $class_module_name, $sub_type);\n    }\n  }\n}\n\n/**\n * Adding one handler for specific function name\n *\n * @param callable[][] $functions\n * @param string       $function_name\n * @param string|array $function_data\n * @param string       $class_module_name\n * @param string       $sub_type\n */\nfunction sys_handler_add_one(&$functions, $function_name, $function_data, $class_module_name, $sub_type) {\n  if (is_string($function_data)) {\n    $override_with = &$function_data;\n  } elseif (isset($function_data['callable'])) {\n    $override_with = &$function_data['callable'];\n  }\n\n  /** @noinspection PhpUndefinedVariableInspection */\n  $overwrite = $override_with[0] == '*';\n  $prepend   = $override_with[0] == '+';\n  if ($overwrite || $prepend) {\n    $override_with = substr($override_with, 1);\n  }\n\n  if (($point_position = strpos($override_with, '.')) === false && $class_module_name) {\n    $override_with = array($class_module_name, $override_with);\n  } elseif ($point_position == 0) {\n    $override_with = substr($override_with, 1);\n  } elseif ($point_position > 0) {\n    $override_with = array(substr($override_with, 0, $point_position), substr($override_with, $point_position + 1));\n  }\n\n  if ($overwrite) {\n    $functions[$function_name] = array();\n  } elseif (!isset($functions[$function_name])) {\n    $functions[$function_name] = array();\n    $sn_function_name          = 'sn_' . $function_name . ($sub_type ? '_' . $sub_type : '');\n    //if(is_callable($sn_function_name))\n    {\n      $functions[$function_name][] = $sn_function_name;\n    }\n  }\n\n  if ($prepend) {\n    array_unshift($functions[$function_name], $function_data);\n  } else {\n    $functions[$function_name][] = $function_data;\n  }\n}\n\n\n// FLEET FUNCTIONS -----------------------------------------------------------------------------------------------------\n///**\n// * @param MissionExplore $result\n// *\n// * @return MissionExplore\n// */\n//function flt_mission_explore_addon_object($result) { return sn_function_call('flt_mission_explore_addon_object', [$result]); }\n//\n///**\n// * @param MissionExplore $result\n// *\n// * @return MissionExplore\n// */\n//function sn_flt_mission_explore_addon_object($result) {\n//  return $result;\n//}\n\n// FILE FUNCTIONS ----------------------------------------------------------------------------------------------------------------\n/** @noinspection PhpUnused */\nfunction sys_file_read($filename) {\n  return @file_get_contents($filename);\n}\n\n/** @noinspection PhpUnused */\nfunction sys_file_write($filename, $content) {\n  return @file_put_contents($filename, $content, FILE_APPEND);\n}\n\nfunction sn_sys_load_php_files($dir_name, $load_extension = 'php') {\n  if (file_exists($dir_name)) {\n    $dir = opendir($dir_name);\n    while (($file = readdir($dir)) !== false) {\n      if ($file == '..' || $file == '.') {\n        continue;\n      }\n\n      $full_filename = $dir_name . $file;\n      $extension     = substr($full_filename, -strlen($load_extension));\n      if ($extension == $load_extension) {\n        require_once($full_filename);\n      }\n    }\n  }\n}\n\n\n// GLOBAL DATA FUNCTIONS -----------------------------------------------------------------------------------------------\n/**\n * Simple wrapper to get base or calculated value for supplied unitSnId\n *\n * @param int  $unitSnId\n * @param bool $plain\n *\n * @return float|int\n */\nfunction getValueFromStorage($unitSnId, $plain = false) {\n  $valueObject = SN::$gc->valueStorage->getValueObject($unitSnId);\n\n  return $plain ? $valueObject->base : $valueObject->getValue();\n}\n\n/**\n * Get game resource multiplier aka mining speed\n *\n * @param bool $plain\n *\n * @return float|int\n */\nfunction game_resource_multiplier($plain = false) {\n  return getValueFromStorage(UNIT_SERVER_SPEED_MINING, $plain);\n}\n\n/**\n * Get game speed aka manufacturing speed\n *\n * @param bool $plain\n *\n * @return float|int\n */\nfunction get_game_speed($plain = false) {\n  return getValueFromStorage(UNIT_SERVER_SPEED_BUILDING, $plain);\n}\n\n/**\n * Получение стоимости ММ в валюте сервера\n *\n * @param bool|false $plain\n *\n * @return mixed\n */\nfunction get_mm_cost($plain = false) {\n  $result = null;\n\n  return sn_function_call('get_mm_cost', array($plain, &$result));\n}\n\nfunction sn_get_mm_cost($plain = false, &$result) {\n  return $result = SN::$config->payment_currency_exchange_mm_ ?: 20000;\n}\n\n/**\n * Получение курса обмены валюты в серверную валюту\n *\n * @param $currency_symbol\n *\n * @return float\n */\nfunction get_exchange_rate($currency_symbol) {\n  $currency_symbol = strtolower($currency_symbol);\n  $config_field    = 'payment_currency_exchange_' . $currency_symbol;\n\n  // Заворачиваем получение стоимости ММ через перекрываемую процедуру\n  return floatval($currency_symbol == 'mm_' ? get_mm_cost() : SN::$config->$config_field);\n}\n\nfunction sys_stat_get_user_skip_list() {\n  $result = array();\n\n  $user_skip_list = array();\n\n  if (SN::$config->stats_hide_admins) {\n    $user_skip_list[] = '`authlevel` > ' . AUTH_LEVEL_REGISTERED;\n  }\n\n  if (SN::$config->stats_hide_player_list) {\n    $temp = explode(',', SN::$config->stats_hide_player_list);\n    foreach ($temp as $user_id) {\n      if ($user_id = floatval($user_id)) {\n        $user_skip_list[] = '`id` = ' . $user_id;\n      }\n    }\n  }\n\n  if (!empty($user_skip_list)) {\n    $user_skip_list  = implode(' OR ', $user_skip_list);\n    $user_skip_query = db_user_list($user_skip_list);\n    if (!empty($user_skip_query)) {\n      foreach ($user_skip_query as $user_skip_row) {\n        $result[$user_skip_row['id']] = $user_skip_row['id'];\n      }\n    }\n  }\n\n  return $result;\n}\n\nfunction market_get_autoconvert_cost() {\n  return SN::$config->rpg_cost_exchange ? SN::$config->rpg_cost_exchange * 3 : 3000;\n}\n\n/**\n * @param $powerup_id\n * @param $powerup_unit\n * @param $level_max\n * @param $plain\n *\n * @return mixed|null\n */\nfunction sn_powerup_get_price_matrix($powerup_id, $powerup_unit = false, $level_max = null, $plain = false) {\n  $result = null;\n\n  /** @see sn_sn_powerup_get_price_matrix(), FestivalHighspotDiscountMatrix::sn_powerup_get_price_matrix_linear() */\n  return sn_function_call('sn_powerup_get_price_matrix', array($powerup_id, $powerup_unit, $level_max, $plain, &$result));\n}\n\n/**\n * @param $powerup_id\n * @param $powerup_unit\n * @param $level_max\n * @param $plain\n * @param $result\n *\n * @return array\n *\n * @see sn_powerup_get_price_matrix()\n */\nfunction sn_sn_powerup_get_price_matrix($powerup_id, $powerup_unit = false, $level_max = null, $plain = false, &$result) {\n  global $sn_powerup_buy_discounts;\n\n  $result = array();\n\n  $powerup_data = get_unit_param($powerup_id);\n  $is_upgrade   = !empty($powerup_unit) && $powerup_unit;\n\n  $level_current = $term_original = $time_left = 0;\n  if ($is_upgrade) {\n    $time_finish = strtotime($powerup_unit['unit_time_finish']);\n    $time_left   = max(0, $time_finish - SN_TIME_NOW);\n    if ($time_left > 0) {\n      $term_original = $time_finish - strtotime($powerup_unit['unit_time_start']);\n      $level_current = $powerup_unit['unit_level'];\n    }\n  }\n\n  $level_max     = $level_max > $powerup_data[P_MAX_STACK] ? $level_max : $powerup_data[P_MAX_STACK];\n  $original_cost = 0;\n  for ($i = 1; $i <= $level_max; $i++) {\n    $base_cost = eco_get_total_cost($powerup_id, $i);\n    $base_cost = $base_cost[BUILD_CREATE][RES_DARK_MATTER];\n    foreach ($sn_powerup_buy_discounts as $period => $discount) {\n      $upgrade_price       = floor($base_cost * $discount * $period / PERIOD_MONTH);\n      $result[$i][$period] = $upgrade_price;\n      $original_cost       = $is_upgrade && $i == $level_current && $period <= $term_original ? $upgrade_price : $original_cost;\n    }\n  }\n\n  if ($is_upgrade && $time_left) {\n    $term_original = round($term_original / PERIOD_DAY);\n    $time_left     = min(floor($time_left / PERIOD_DAY), $term_original);\n    $cost_left     = $term_original > 0 ? ceil($time_left / $term_original * $original_cost) : 0;\n\n    array_walk_recursive($result, function (&$value) use ($cost_left) {\n      $value -= $cost_left;\n    });\n  }\n\n  return $result;\n}\n\n/**\n * @param $price_matrix_plain\n * @param $price_matrix_original\n * @param $price_matrix_upgrade\n * @param $user_dark_matter\n *\n * @return array\n *\n * Used in player_premium and interface_batch_operation modules\n */\nfunction price_matrix_templatize(&$price_matrix_plain, &$price_matrix_original, &$price_matrix_upgrade, $user_dark_matter) {\n  $prices = array();\n  foreach ($price_matrix_original as $level_num => $level_data) {\n    $price_per_period = array();\n    foreach ($level_data as $period => $price) {\n      $price_per_period[$period] = array(\n        'PERIOD'             => $period,\n        'PRICE_ORIGIN'       => $price,\n        'PRICE_ORIGIN_TEXT'  => HelperString::numberFloorAndFormat($price),\n        'PRICE_ORIGIN_CLASS' => prettyNumberGetClass($price, $user_dark_matter),\n        'PRICE_UPGRADE'      => $price_matrix_upgrade[$level_num][$period],\n        'PRICE_UPGRADE_TEXT' => HelperString::numberFloorAndFormat($price_matrix_upgrade[$level_num][$period]),\n      );\n      if (isset($price_matrix_plain[$level_num][$period])) {\n        $price_per_period[$period] += array(\n          'PRICE_PLAIN_PERCENT' => ceil(100 - ($price / $price_matrix_plain[$level_num][$period]) * 100),\n          'PRICE_PLAIN'         => $price_matrix_plain[$level_num][$period],\n          'PRICE_PLAIN_TEXT'    => HelperString::numberFloorAndFormat($price_matrix_plain[$level_num][$period]),\n        );\n      }\n    }\n\n    $prices[$level_num] = array(\n      '.'     => array('period' => $price_per_period),\n      'LEVEL' => $level_num,\n    );\n  }\n\n  return $prices;\n}\n\n\n// TOOLS & UTILITIES ----------------------------------------------------------------------------------------------------------------\n/**\n * Generates random string of $length symbols from $allowed_chars charset\n *\n * @param int    $length\n * @param string $allowed_chars\n *\n * @return string\n */\nfunction sys_random_string($length = 16, $allowed_chars = SN_SYS_SEC_CHARS_ALLOWED) {\n  $allowed_length = strlen($allowed_chars);\n\n  $random_string = '';\n  for ($i = 0; $i < $length; $i++) {\n    $random_string .= $allowed_chars[mt_rand(0, $allowed_length - 1)];\n  }\n\n  return $random_string;\n}\n\nfunction array_merge_recursive_numeric($array1, $array2) {\n  if (!empty($array2) && is_array($array2)) {\n    foreach ($array2 as $key => $value) {\n      $array1[$key] = !isset($array1[$key]) || !is_array($array1[$key]) ? $value : array_merge_recursive_numeric($array1[$key], $value);\n    }\n  }\n\n  return $array1;\n}\n\nfunction sn_sys_array_cumulative_sum(&$array) {\n  $accum = 0;\n  foreach ($array as &$value) {\n    $accum += $value;\n    $value = $accum;\n  }\n}\n\nfunction print_rr($var, $capture = false) {\n  $print = '<pre>' . htmlspecialchars(print_r($var, true)) . '</pre>';\n  if ($capture) {\n    return $print;\n  } else {\n    print($print);\n  }\n\n  return $print;\n}\n\n/**\n * Returns unique string ID for total fleets on planet\n *\n * @param array $planetTemplatized\n *\n * @return int|string\n */\nfunction getUniqueFleetId($planetTemplatized) {\n  return empty($planetTemplatized['id']) ? 0 : sprintf(FLEET_ID_TEMPLATE, $planetTemplatized['id']);\n}\n\n/**\n * @param array $context\n *\n * @return array\n */\nfunction getLocationFromContext($context = []) {\n  if (!empty($context[LOC_FLEET])) {\n    return [LOC_FLEET, $context[LOC_FLEET]['fleet_id']];\n  } elseif (!empty($context[LOC_PLANET])) {\n    return [LOC_PLANET, $context[LOC_PLANET]['id']];\n  } elseif (!empty($context[LOC_USER])) {\n    return [LOC_USER, $context[LOC_USER]['id']];\n  } else {\n    return [LOC_SERVER, 0];\n  }\n\n}\n\n\n//\n\n\n// MAIL ----------------------------------------------------------------------------------------------------------------\nfunction mymail($email_unsafe, $title, $body, $from = '', $html = false) {\n  $from = trim($from ? $from : SN::$config->game_adminEmail);\n\n  $head = '';\n  $head .= \"Content-Type: text/\" . ($html ? 'html' : 'plain') . \"; charset=utf-8 \\r\\n\";\n  $head .= \"Date: \" . date('r') . \" \\r\\n\";\n  $head .= \"Return-Path: \" . SN::$config->game_adminEmail . \" \\r\\n\";\n  $head .= \"From: {$from} \\r\\n\";\n  $head .= \"Sender: {$from} \\r\\n\";\n  $head .= \"Reply-To: {$from} \\r\\n\";\n//  $head .= \"Organization: {$org} \\r\\n\";\n  $head .= \"X-Sender: {$from} \\r\\n\";\n  $head .= \"X-Priority: 3 \\r\\n\";\n\n  $body = str_replace(\"\\r\\n\", \"\\n\", $body);\n  $body = str_replace(\"\\n\", \"\\r\\n\", $body);\n\n  if ($html) {\n    /** @noinspection HtmlRequiredLangAttribute, HtmlRequiredTitleElement */\n    $body = '<html><head><base href=\"' . SN_ROOT_VIRTUAL . '\"></head><body>' . nl2br($body) . '</body></html>';\n  }\n\n  $title = '=?UTF-8?B?' . base64_encode($title) . '?=';\n\n  return @mail($email_unsafe, $title, $body, $head);\n}\n\n\n// VERSION FUNCTIONS ----------------------------------------------------------------------------------------------------------------\nfunction sn_version_compare_extra($version) {\n  static $version_regexp = '#(\\d+)([a-f])(\\d+)(?:\\.(\\d+))*#';\n  preg_match($version_regexp, $version, $version);\n  unset($version[0]);\n  $version[2] = ord($version[2]) - ord('a');\n\n  return implode('.', $version);\n}\n\nfunction sn_version_compare($ver1, $ver2) {\n  return version_compare(sn_version_compare_extra($ver1), sn_version_compare_extra($ver2));\n}\n\n\n// MODULES FUNCTIONS ---------------------------------------------------------------------------------------------------\n/**\n * Return Award module or NULL\n *\n * For typecasting\n *\n * @return null|player_award|\\Modules\\sn_module\n */\nfunction moduleAward() {\n  return SN::$gc->modules->getModule('player_award');\n}\n\n/**\n * Return Captain module or NULL\n *\n * For typecasting\n *\n * @return null|unit_captain|\\Modules\\sn_module\n */\nfunction moduleCaptain() {\n  return SN::$gc->modules->getModule('unit_captain');\n}\n\n/**\n * Updates users online count\n *\n * We should move this to separate function due to ambiguency of pass() method\n *\n * @param $usersOnline\n */\nfunction dbUpdateUsersOnline($usersOnline) {\n  SN::$config->pass()->var_online_user_count = $usersOnline;\n}\n\n/**\n * Updates total user count\n *\n * We should move this to separate function due to ambiguency of pass() method\n *\n * @param $userCount\n */\nfunction dbUpdateUsersCount($userCount) {\n  SN::$config->pass()->users_amount = $userCount;\n}\n"
  },
  {
    "path": "includes/general/general_compatibility.php",
    "content": "<?php\n/**\n * Created by Gorlum 14.02.2017 11:21\n */\n\n/**\n * Back-compatibility functions\n */\nif (!function_exists('is_iterable')) {\n  /**\n   * Check is variable iterable\n   *\n   * Compatibility function for PHP 5.x\n   *\n   * @param mixed $var\n   *\n   * @return bool\n   */\n  function is_iterable($var) {\n    return is_array($var) || $var instanceof Traversable;\n  }\n}"
  },
  {
    "path": "includes/general/general_formatters.php",
    "content": "<?php\n/**\n * Created by Gorlum 04.12.2017 4:25\n */\n\n/**\n * Format $value to ID\n *\n * @param     $value\n * @param int $default\n *\n * @return float|int\n */\nfunction idval($value, $default = 0) {\n  $value = floatval($value);\n\n  return preg_match('#^(\\d*)#', $value, $matches) && $matches[1] ? floatval($matches[1]) : $default;\n}\n\n\n/**\n * @param int|float      $number\n * @param true|int|float $compareTo\n *    true             - compare to zero from above i.e. resources amount ($number > 0 for positive)\n *    numeric positive - compare to $compareTo from below i.e. price with resource amount ($number < $compareTo for positive)\n *    numeric negative - compare to -$compareTo from above i.e. resource amount with price ($n > -$compareTo for positive)\n *\n * @return string\n */\nfunction prettyNumberGetClass($number, $compareTo) {\n  $n = floor($number);\n\n  if ($compareTo === true) {\n    $class = $n == 0 ? 'zero' : ($n > 0 ? 'positive' : 'negative');\n  } elseif ($compareTo >= 0) {\n    $class = $n == $compareTo ? 'zero' : ($n < $compareTo ? 'positive' : 'negative');\n  } else {\n    $class = ($n == -$compareTo) ? 'zero' : ($n < -$compareTo ? 'negative' : 'positive');\n  }\n\n  return $class;\n}\n\n/**\n * Return number floored, formatted and styled with \"span\"\n *\n * @param int|float $number\n *\n * @return string\n * @see PtlVariableDecorator\n * // TODO - this should be made in templates\n */\nfunction prettyNumberStyledDefault($number) {\n  return prettyNumberStyledCompare($number, true);\n}\n\n/**\n * @param int|float $number\n * @param int|float $compareTo\n *\n * @return string\n *\n * // TODO - this should be made in templates\n */\nfunction prettyNumberStyledCompare($number, $compareTo) {\n  return\n    '<span class=\"' . prettyNumberGetClass($number, $compareTo) . '\">' .\n    HelperString::numberFloorAndFormat($number) .\n    '</span>';\n}\n\n// ----------------------------------------------------------------------------------------------------------------\nfunction pretty_time($seconds, $daysNumeric = false) {\n  $day = floor($seconds / (24 * 3600));\n\n  return sprintf(\n    '%s%02d:%02d:%02d',\n    $daysNumeric ? $day . '.' : ($day ? $day . SN::$lang['sys_day_short'] . ' ' : ''),\n    floor($seconds / 3600 % 24),\n    floor($seconds / 60 % 60),\n    floor($seconds / 1 % 60)\n  );\n}\n\nfunction pretty_time_short($seconds, $daysNumeric = true) {\n  $day = floor($seconds / (24 * 3600));\n\n  return sprintf(\n    '%s%s%s%02d',\n    $day ? $day . SN::$lang['sys_day_short'] . ' ' : '',\n    $seconds > PERIOD_HOUR ? sprintf('%02d:', floor($seconds / 3600 % 24)) : '',\n    $seconds > PERIOD_MINUTE ? sprintf('%02d:', floor($seconds / 60 % 60)) : '',\n    floor($seconds / 1 % 60)\n  );\n}\n\nfunction sys_time_human($time, $full = false) {\n  global $lang;\n\n  $seconds = $time % 60;\n  $time = floor($time / 60);\n  $minutes = $time % 60;\n  $time = floor($time / 60);\n  $hours = $time % 24;\n  $time = floor($time / 24);\n\n  return\n    ($full || $time ? \"{$time} {$lang['sys_day']}&nbsp;\" : '') .\n    ($full || $hours ? \"{$hours} {$lang['sys_hrs']}&nbsp;\" : '') .\n    ($full || $minutes ? \"{$minutes} {$lang['sys_min']}&nbsp;\" : '') .\n    ($full || !$time || $seconds ? \"{$seconds} {$lang['sys_sec']}\" : '');\n}\n\nfunction sys_time_human_system($time) {\n  return $time ? date(FMT_DATE_TIME_SQL, $time) . \" ({$time}), \" . sys_time_human(SN_TIME_NOW - $time) : '{NEVER}';\n}\n\nif (!function_exists('strptime')) {\n  function strptime($date, $format) {\n    $masks = array(\n      '%d' => '(?P<d>[0-9]{2})',\n      '%m' => '(?P<m>[0-9]{2})',\n      '%Y' => '(?P<Y>[0-9]{4})',\n      '%H' => '(?P<H>[0-9]{2})',\n      '%M' => '(?P<M>[0-9]{2})',\n      '%S' => '(?P<S>[0-9]{2})',\n      // usw..\n    );\n\n    $rexep = \"#\" . strtr(preg_quote($format), $masks) . \"#\";\n    if (preg_match($rexep, $date, $out)) {\n      $ret = array(\n        \"tm_sec\"  => (int)$out['S'],\n        \"tm_min\"  => (int)$out['M'],\n        \"tm_hour\" => (int)$out['H'],\n        \"tm_mday\" => (int)$out['d'],\n        \"tm_mon\"  => $out['m'] ? $out['m'] - 1 : 0,\n        \"tm_year\" => $out['Y'] > 1900 ? $out['Y'] - 1900 : 0,\n      );\n    } else {\n      $ret = false;\n    }\n\n    return $ret;\n  }\n}\n\n\nfunction js_safe_string($string) {\n  return str_replace(array(\"\\r\", \"\\n\"), array('\\r', '\\n'), addslashes($string));\n}\n\nfunction sys_safe_output($string) {\n  return str_replace(array(\"&\", \"\\\"\", \"<\", \">\", \"'\"), array(\"&amp;\", \"&quot;\", \"&lt;\", \"&gt;\", \"&apos;\"), $string);\n}\n\nfunction str_raw2unsafe($raw) {\n  return trim(strip_tags($raw));\n}\n\nfunction ip2longu($ip) {\n  return sprintf('%u', floatval(ip2long($ip)));\n}\n\n/**\n * @param int $fullDate\n *\n * @return int\n */\nfunction datePart($fullDate) {\n  return (int)strtotime(date('Y-m-d', $fullDate));\n}\n\n\n/**\n * Fall-back function to support old, serialize()-d data\n *\n * @param string $var\n *\n * @return mixed|string\n * @deprecated\n */\nfunction unserializeOrJsonDecode($var, $asArray = true) {\n  // Variable is not a string - returning it back\n  if (!is_string($var) || empty($var)) {\n    return $var;\n  }\n\n  set_error_handler(\n  /**\n   * @param $errno\n   * @param $errstr\n   * @param $errfile\n   * @param $errline\n   *\n   * @return bool\n   * @throws ErrorException\n   */\n    function ($errno, $errstr, $errfile, $errline) {\n      throw new ErrorException($errstr, 0, $errno, $errfile, $errline);\n    }\n  );\n\n  try {\n    $result = unserialize($var);\n  } catch (ErrorException $e) {\n    // String is not a serialized variable. May be it's a json?\n    $result = json_decode($var, $asArray);\n    if (JSON_ERROR_NONE != json_last_error()) {\n      $result = $var;\n    }\n  } finally {\n    restore_error_handler();\n  }\n\n  return $result;\n}\n"
  },
  {
    "path": "includes/general/general_math.php",
    "content": "<?php\n\nfunction geometry_progression_sum($n, $b1, $q) {\n  return $q != 1 ? ($b1 * (pow($q, $n) - 1)/($q - 1)) : ($n * $b1);\n}\n\nfunction sn_floor($value)\n{\n  return $value >= 0 ? floor($value) : ceil($value);\n}\n\n// Эта функция выдает нормально распределенное случайное число с матожиднием $mu и стандартным отклонением $sigma\n// $strict - количество $sigma, по которым идет округление функции. Т.е. $strict = 3 означает, что диапазон значений обрезается по +-3 * $sigma\n// Используется http://ru.wikipedia.org/wiki/Преобразование_Бокса_—_Мюллера\nfunction sn_rand_gauss($mu = 0, $sigma = 1, $strict = false)\n{\n  // http://ru.wikipedia.org/wiki/Среднеквадратическое_отклонение\n  // При $mu = 0 (график симметричный, цифры только для половины графика)\n  // От 0 до $sigma ~ 34.1%\n  // От $sigma до 2 * $sigma ~ 13.6%\n  // От 2 * $sigma до 3 * $sigma ~ 2.1%\n  // От 3 * $sigma до бесконечности ~ 0.15%\n  // Не менее 99.7% случайных величин лежит в пределах +-3 $sigma\n\n//  $r = sn_rand_0_1();\n//  $phi = sn_rand_0_1();\n//  $z0 = cos(2 * pi() * $phi) * sqrt(-2 * log($r));\n//  return $mu + $sigma * $z0;\n  $max_rand = mt_getrandmax();\n  $random = cos(2 * pi() * (mt_rand(1, $max_rand) / $max_rand)) * sqrt(-2 * log(mt_rand(1, $max_rand) / $max_rand));\n  $random = $strict === false ? $random : ($random > $strict ? $strict : ($random < -$strict ? -$strict : $random));\n\n  return $mu + $sigma * $random;\n}\n\n// Функция возвращает случайное нормально распределенное целое число из указанного промежутка\n/**\n * @param float      $range_start - Начало диапазона\n * @param float      $range_end - Конец диапазона\n * @param bool|int  $round - До скольки знаков округлять результат. False - не округлять, True - округлять до целого, 1 - округлять до десятков, 2 - до сотен итд\n * @param int        $strict - В сколько сигм надо уложить результат\n * @param bool|false $cut_extreme - надо ли обрезать крайние значения. Например, при $strict = 2 их слишком много\n *\n * @return float|int\n */\nfunction sn_rand_gauss_range($range_start, $range_end, $round = true, $strict = 4, $cut_extreme = false)  {\n  if($cut_extreme) {\n    $range_start--;\n    $range_end++;\n  }\n  do {\n    $random = sn_rand_gauss(($range_start + $range_end) / 2, ($range_end - $range_start) / $strict / 2, $strict);\n    $round_emul = pow(10, $round === true ? 0 : $round);\n    $result = $round ? round($random * $round_emul) / $round_emul : $random;\n  } while ($cut_extreme && ($result == $range_start || $result == $range_end));\n  return $result;\n}\n\n/**\n * Return median of array or list of arguments\n *\n * @return bool|float\n */\nfunction median() {\n  $args = func_get_args();\n\n  switch (func_num_args()) {\n    case 0:\n      // trigger_error('median() requires at least one parameter',E_USER_WARNING);\n      return false;\n    break;\n\n    /** @noinspection PhpMissingBreakStatementInspection */\n    case 1:\n      $args = array_pop($args);\n    // fallthrough\n\n    default:\n      if (!is_array($args)) {\n        // trigger_error('median() requires a list of numbers to operate on or an array of numbers', E_USER_NOTICE);\n        return false;\n      }\n\n      sort($args);\n\n      $h = intval(count($args) / 2);\n\n      $median = count($args) % 2 == 0 ? ($args[$h] + $args[$h - 1]) / 2 : $args[$h];\n\n    break;\n  }\n\n  return $median;\n}\n\n/**\n * @param array $array\n *\n * @return float\n */\nfunction avg($array)\n{\n  return is_array($array) && count($array) ? array_sum($array) / count($array) : 0;\n}\nfunction linear_calc(&$linear, $from = 0, $debug = false)\n{\n  for($i = $from; $i < count($linear); $i++)\n  {\n    $eq = &$linear[$i];\n    for($j = count($eq) - 1; $j >= $from; $j--)\n    {\n      $eq[$j] /= $eq[$from];\n    }\n  }\n  if($debug) pdump($linear, 'Нормализовано по х' . $from);\n\n  for($i = $from + 1; $i < count($linear); $i++)\n  {\n    $eq = &$linear[$i];\n    for($j = count($eq) - 1; $j >= $from; $j--)\n    {\n      $eq[$j] -= $linear[$from][$j];\n    }\n  }\n  if($debug) pdump($linear, 'Подставили х' . $from);\n\n  if($from < count($linear) - 1)\n  {\n    linear_calc($linear, $from + 1, $debug);\n  }\n\n  if($from)\n  {\n    for($i = 0; $i < $from; $i++)\n    {\n      $eq = &$linear[$i];\n      for($j = count($eq) - 1; $j >= $from; $j--)\n      {\n        $eq[$j] = $eq[$j] - $eq[$from] * $linear[$from][$j];\n      }\n    }\n    if($debug) pdump($linear, 'Подставили обратно х' . $from);\n  }\n  else\n  {\n    if($debug) pdump($linear, 'Результат' . $from);\n    foreach($linear as $index => &$eq)\n    {\n      pdump($eq[count($linear)], 'x' . $index);\n    }\n  }\n}\n\n/**\n * Get number's sign\n *\n * @param int|float $number\n *\n * @return int\n */\nfunction sign($number) {\n  return ($number > 0) - ($number < 0);\n}\n"
  },
  {
    "path": "includes/general/general_nickRender.php",
    "content": "<?php\n/**\n * Created by Gorlum 04.12.2017 4:20\n */\n\nglobal $nickSorting;\n$nickSorting = [\n  NICK_HTML,\n\n  NICK_FIRST,\n\n  NICK_RANK,\n\n  NICK_BIRTHDAY,\n  NICK_VACATION,\n  NICK_PREMIUM,\n  NICK_AUTH_LEVEL,\n\n  NICK_HIGHLIGHT,\n  NICK_CLASS,\n  NICK_NICK_CLASS,\n  NICK_NICK,\n  NICK_NICK_CLASS_END,\n  NICK_ALLY_CLASS,\n  NICK_ALLY,\n  NICK_ALLY_CLASS_END,\n  NICK_CLASS_END,\n  NICK_HIGHLIGHT_END,\n\n  NICK_GENDER,\n  NICK_RACE,\n  NICK_AWARD,\n\n  NICK_LAST,\n];\n/*\n // Old nick sorting\n $nickSorting = [\n  NICK_HTML,\n\n  NICK_FIRST,\n\n  NICK_RACE,\n  NICK_GENDER,\n  NICK_AWARD,\n  NICK_VACATION,\n  NICK_BIRTHDAY,\n  NICK_PREMIUM,\n  NICK_AUTH_LEVEL,\n\n  NICK_RANK,\n  NICK_HIGHLIGHT,\n  NICK_CLASS,\n  NICK_NICK_CLASS,\n  NICK_NICK,\n  NICK_NICK_CLASS_END,\n  NICK_ALLY_CLASS,\n  NICK_ALLY,\n  NICK_ALLY_CLASS_END,\n  NICK_CLASS_END,\n  NICK_HIGHLIGHT_END,\n\n  NICK_LAST,\n];\n*/\n\n\n/**\n * Ordering nick parts according to predefined part order\n *\n * @param array      $array\n * @param null|array $options\n *\n * @return array\n */\nfunction playerNickOrder($array, $options = null) {\n  global $nickSorting;\n\n  $currentSort = is_array($options[NICK_SORT]) ? $options[NICK_SORT] : $nickSorting;\n\n  $result = [];\n  // Rearranging nick parts according to sort array\n  foreach ($currentSort as $nickPartId) {\n    if (array_key_exists($nickPartId, $array)) {\n      $result[$nickPartId] = $array[$nickPartId];\n      unset($array[$nickPartId]);\n    }\n  }\n\n  // Adding what left of nick parts to resulting array\n  return array_merge_recursive_numeric($result, $array);\n}\n\n// Может принимать: (array)$user, $nick_render_array, $nick_render_array_html, $nick_render_string_compact\nfunction player_nick_render_to_html($result, $options = false) {\n  $result = player_nick_uncompact($result);\n\n  if (is_array($result)) {\n    if (isset($result['id'])) {\n      $result = player_nick_render_current_to_array($result, $options);\n    }\n    if (!isset($result[NICK_HTML])) {\n      $result = player_nick_render_array_to_html($result);\n    }\n    unset($result[NICK_HTML]);\n    $result = implode('', playerNickOrder($result, $options));\n  }\n\n  return $result;\n}\n\n/**\n * Pack nick parts to string\n *\n * @param array $nick_array\n *\n * @return string\n */\nfunction player_nick_compact($nick_array) {\n  return json_encode(playerNickOrder($nick_array));\n}\n\n/**\n * Unpacks nick parts from string - if necessary\n *\n * @param array|string $nick_string\n *\n * @return array|string\n */\nfunction player_nick_uncompact($nick_string) {\n  $result = $nick_string;\n  if (is_string($nick_string) && (strpos($nick_string, '{\"') === 0 || strpos($nick_string, '[\"') === 0)) {\n    $result = json_decode($nick_string, true);\n    if (json_last_error() !== JSON_ERROR_NONE) {\n      $result = $nick_string;\n    }\n  }\n\n  return $result;\n}\n\nfunction player_nick_render_array_to_html($nick_array) {\n  $result = null;\n\n  return sn_function_call('player_nick_render_array_to_html', array($nick_array, &$result));\n}\n\n/**\n * @param array $nick_array\n * @param array $result\n *\n * @return array\n */\nfunction sn_player_nick_render_array_to_html($nick_array, &$result) {\n  static $iconCache = array();\n\n  if (empty($iconCache['gender_' . $nick_array[NICK_GENDER]])) {\n    $iconCache['gender_' . $nick_array[NICK_GENDER]] = SN::$gc->skinModel->getImageCurrent(\"gender_{$nick_array[NICK_GENDER]}|html\");\n    $iconCache['icon_vacation'] = SN::$gc->skinModel->getImageCurrent('icon_vacation|html');\n    $iconCache['icon_birthday'] = SN::$gc->skinModel->getImageCurrent('icon_birthday|html');\n  }\n\n  // ALL STRING ARE UNSAFE!!!\n  if (isset($nick_array[NICK_BIRTHDAY])) {\n    $result[NICK_BIRTHDAY] = $iconCache['icon_birthday'];\n  }\n\n  if (isset($nick_array[NICK_VACATION])) {\n    $result[NICK_VACATION] = $iconCache['icon_vacation'];\n  }\n\n  if (isset($nick_array[NICK_RANK])) {\n    $result[NICK_RANK] = SN::$gc->playerLevelHelper->renderRank($nick_array[NICK_RANK], $nick_array[NICK_RANK_NO_TEXT]);\n  }\n\n  if (isset($nick_array[NICK_GENDER])) {\n    $result[NICK_GENDER] = $iconCache['gender_' . $nick_array[NICK_GENDER]];\n  }\n\n  $highlight = null;\n  if (isset($nick_array[NICK_PREMIUM])) {\n    $highlight = SN::$config->chat_highlight_premium;\n  }\n\n  if (isset($nick_array[NICK_AUTH_LEVEL])) {\n    switch ($nick_array[NICK_AUTH_LEVEL]) {\n      case 4:\n        $highlight = SN::$config->chat_highlight_developer;\n      break;\n\n      case 3:\n        $highlight = SN::$config->chat_highlight_admin;\n      break;\n\n      case 2:\n        $highlight = SN::$config->chat_highlight_operator;\n      break;\n\n      case 1:\n        $highlight = SN::$config->chat_highlight_moderator;\n      break;\n    }\n\n  }\n\n  if ($highlight) {\n    list($result[NICK_HIGHLIGHT], $result[NICK_HIGHLIGHT_END]) = explode('$1', $highlight);\n  }\n\n  if (isset($nick_array[NICK_CLASS])) {\n    $result[NICK_CLASS] = '<span ' . $nick_array[NICK_CLASS] . '>';\n    $result[NICK_CLASS_END] = '</span>';\n  }\n\n  $result[NICK_NICK] = sys_safe_output($nick_array[NICK_NICK]);\n\n  if (isset($nick_array[NICK_ALLY])) {\n    $result[NICK_ALLY] = '[' . sys_safe_output($nick_array[NICK_ALLY]) . ']';\n  }\n\n  $result[NICK_HTML] = true;\n\n  return $result;\n}\n\n/**\n * @param array      $render_user\n * @param array|bool $options - [\n *                            'color' => true,\n *                            'icons' => true,\n *                            'gender' => true,\n *                            'birthday' => true,\n *                            'ally' => true,\n *                            ]\n *\n * @return mixed\n */\nfunction player_nick_render_current_to_array($render_user, $options = false) {\n  $result = null;\n\n  return sn_function_call('player_nick_render_current_to_array', array($render_user, $options, &$result));\n}\n\n/**\n * @param array      $render_user\n * @param array|bool $options - [\n *                            'color' => true,\n *                            'icons' => true,\n *                            'gender' => true,\n *                            'birthday' => true,\n *                            'ally' => true,\n *                            ]\n * @param array      $result\n *\n * @return mixed\n */\nfunction sn_player_nick_render_current_to_array($render_user, $options = false, &$result) {\n  /*\n  $options = $options !== true ? $options :\n    array(\n      'color' => true,\n      'icons' => true,\n      'gender' => true,\n      'birthday' => true,\n      'ally' => true,\n    );\n  */\n\n\n  if (($options === true || isset($options['icons']) || isset($options['birthday'])) && (date('Y', SN_TIME_NOW) . date('-m-d', strtotime($render_user['user_birthday'])) == date('Y-m-d', SN_TIME_NOW))) {\n    if(!empty($render_user['user_birthday'])) {\n      $result[NICK_BIRTHDAY] = '';\n    }\n  }\n\n  if ($options === true || (isset($options['icons']) && $options['icons']) || (isset($options['gender']) && $options['gender'])) {\n    if (isset($render_user['gender'])) {\n      $result[NICK_GENDER] = $render_user['gender'] == GENDER_UNKNOWN ? 'unknown' : ($render_user['gender'] == GENDER_FEMALE ? 'female' : 'male');\n    }\n  }\n\n  if (($options === true || (isset($options['icons']) && $options['icons']) || (isset($options['vacancy']) && $options['vacancy'])) && $render_user['vacation']) {\n    if(isset($render_user['vacation'])) {\n      $result[NICK_VACATION] = $render_user['vacation'];\n    }\n  }\n\n  if ($options === true || !empty($options['icons']) || !empty($options['player_rank'])) {\n    if(isset($render_user['total_points'])) {\n      $result[NICK_RANK] = SN::$gc->playerLevelHelper->getPointLevel($render_user['total_points'], $render_user['authlevel']);\n    }\n  }\n\n  if (!empty($options[NICK_RANK_NO_TEXT])) {\n    $result[NICK_RANK_NO_TEXT] = $options[NICK_RANK_NO_TEXT];\n  }\n\n  if ($options === true || (isset($options['color']) && $options['color'])) {\n    if ($user_auth_level = $render_user['authlevel']) {\n      $result[NICK_AUTH_LEVEL] = $user_auth_level;\n    }\n    if ($user_premium = mrc_get_level($render_user, false, UNIT_PREMIUM)) {\n      $result[NICK_PREMIUM] = $user_premium;\n    }\n  }\n\n  if ((isset($options['class']) && $options['class'])) {\n//    $result[NICK_CLASS] = (isset($result_options[NICK_CLASS]) ? ' ' . $result_options[NICK_CLASS] : '') . $options['class'];\n    $result[NICK_CLASS] = (isset($options[NICK_CLASS]) ? ' ' . $options[NICK_CLASS] : '') . $options['class'];\n  }\n\n  if ($render_user['ally_tag'] && ($options === true || (isset($options['ally']) && $options['ally']))) {\n    if(isset($render_user['ally_tag'])) {\n      $result[NICK_ALLY] = $render_user['ally_tag'];\n    }\n  }\n\n  $result[NICK_NICK] = $render_user['username'];\n\n  return $result;\n}\n\n"
  },
  {
    "path": "includes/general/general_params.php",
    "content": "<?php\n/**\n * Created by Gorlum 04.12.2017 4:13\n */\n\nfunction isParamExists($paramName) {\n  return array_key_exists($paramName, $_GET) || array_key_exists($paramName, $_POST);\n}\n\n/**\n * @param string $param_name\n * @param string $default\n *\n * @return string|array\n */\nfunction sys_get_param($param_name, $default = '') {\n  return $_POST[$param_name] !== null ? $_POST[$param_name] : ($_GET[$param_name] !== null ? $_GET[$param_name] : $default);\n}\n\n/**\n * @param string           $param_name\n * @param int|float|string $default\n *\n * @return int|float|string\n */\nfunction sys_get_param_id($param_name, $default = 0) {\n  return is_id($value = sys_get_param($param_name, $default)) ? $value : $default;\n}\n\n/**\n * @param string $param_name\n * @param int    $default\n *\n * @return int\n */\nfunction sys_get_param_int($param_name, $default = 0) {\n  $value = sys_get_param($param_name, $default);\n\n  return $value === 'on' ? 1 : ($value === 'off' ? $default : intval($value));\n}\n\n/**\n * @param string $param_name\n * @param float  $default\n *\n * @return float\n */\nfunction sys_get_param_float($param_name, $default = 0.0) {\n  return floatval(sys_get_param($param_name, $default));\n}\n\n/**\n * @param string $param_name\n * @param string $default\n *\n * @return string\n */\nfunction sys_get_param_escaped($param_name, $default = '') {\n  return SN::$db->db_escape(sys_get_param($param_name, $default));\n}\n\n/**\n * Get list of units from environment ($_GET, $_POST etc)\n *\n * @param string $param_name\n * @param array  $default\n *\n * @return float[] - [int $unitId] => float $unitAmount\n */\nfunction sys_get_param_unit_array($param_name, $default = []) {\n  $result = $default;\n\n  if (is_array($params = sys_get_param($param_name)) && !empty($params)) {\n    $result = [];\n    foreach (sys_get_param('resources') as $unitId => $unitAmount) {\n      $result[intval($unitId)] = floatval($unitAmount);\n    }\n  }\n\n  return $result;\n}\n\n/**\n * @param string $param_name\n * @param string $default\n *\n * @return string\n */\nfunction sys_get_param_date_sql($param_name, $default = '2000-01-01') {\n  $val = sys_get_param($param_name, $default);\n\n  return preg_match(PREG_DATE_SQL_RELAXED, $val) ? $val : $default;\n}\n\n/**\n * @param string $param_name\n * @param string $default\n *\n * @return string\n */\nfunction sys_get_param_str_unsafe($param_name, $default = '') {\n  return str_raw2unsafe(sys_get_param($param_name, $default));\n}\n\n/**\n * @param string $param_name\n * @param string $default\n *\n * @return string\n */\nfunction sys_get_param_str($param_name, $default = '') {\n  return SN::$db->db_escape(sys_get_param_str_unsafe($param_name, $default));\n}\n\n/**\n * @param string $param_name\n * @param string $default\n *\n * @return array\n */\nfunction sys_get_param_str_both($param_name, $default = '') {\n  $param = sys_get_param($param_name, $default);\n  $param_unsafe = str_raw2unsafe($param);\n\n  return array(\n    'raw'    => $param,\n    'unsafe' => $param_unsafe,\n    'safe'   => SN::$db->db_escape($param_unsafe),\n  );\n}\n\n/**\n * @param string $param_name\n * @param string $default\n *\n * @return array\n */\nfunction sys_get_param_phone($param_name, $default = '') {\n  $phone_raw = sys_get_param_str_unsafe($param_name, $default = '');\n  if ($phone_raw) {\n    $phone = $phone_raw[0] == '+' ? '+' : '';\n    for ($i = 0; $i < strlen($phone_raw); $i++) {\n      $ord = ord($phone_raw[$i]);\n      if ($ord >= 48 && $ord <= 57) {\n        $phone .= $phone_raw[$i];\n      }\n    }\n    $phone = strlen($phone) < 11 ? '' : $phone;\n  } else {\n    $phone = '';\n  }\n\n  return array('raw' => $phone_raw, 'phone' => $phone);\n}\n"
  },
  {
    "path": "includes/general/general_planetFunctions.php",
    "content": "<?php\n\nuse Planet\\DBStaticPlanet;\n\n/**\n * Created by Gorlum 04.12.2017 5:04\n */\n\n// PLANET FUNCTIONS ----------------------------------------------------------------------------------------------------------------\nfunction eco_planet_fields_max($planet) {\n  return $planet['field_max'] + ($planet['planet_type'] == PT_PLANET ? mrc_get_level($user, $planet, STRUC_TERRAFORMER) * 5 : (mrc_get_level($user, $planet, STRUC_MOON_STATION) * 3));\n}\n\nfunction GetPhalanxRange($phalanx_level) {\n  return $phalanx_level > 1 ? pow($phalanx_level, 2) - 1 : 0;\n}\n\n/**\n * @param array $planet\n *\n * @return bool\n */\nfunction CheckAbandonPlanetState(&$planet) {\n  if ($planet['destruyed'] && $planet['destruyed'] <= SN_TIME_NOW) {\n    DBStaticPlanet::db_planet_delete_by_id($planet['id']);\n\n    return true;\n  }\n\n  return false;\n}\n\nfunction can_capture_planet() { $result = null; return sn_function_call('can_capture_planet', array(&$result)); }\n\nfunction sn_can_capture_planet(&$result) {\n  return $result = false;\n}\n"
  },
  {
    "path": "includes/general/general_playerFunctions.php",
    "content": "<?php\n\nuse Planet\\DBStaticPlanet;\n\n/**\n * Created by Gorlum 04.12.2017 4:34\n */\n\n// ----------------------------------------------------------------------------------------------------------------\nfunction sys_user_options_pack(&$user) {\n  global $user_option_list;\n\n  $options = '';\n  $option_list = array();\n  foreach ($user_option_list as $option_group_id => $option_group) {\n    $option_list[$option_group_id] = array();\n    foreach ($option_group as $option_name => $option_value) {\n      if (!isset($user[$option_name])) {\n        $user[$option_name] = $option_value;\n      } elseif ($user[$option_name] == '') {\n        $user[$option_name] = 0;\n      }\n      $options .= \"{$option_name}^{$user[$option_name]}\" . USER_OPTIONS_SPLIT;\n      $option_list[$option_group_id][$option_name] = $user[$option_name];\n    }\n  }\n\n  $user['options'] = $options;\n  $user['option_list'] = $option_list;\n\n  return $options;\n}\n\nfunction sys_user_options_unpack(&$user) {\n  global $user_option_list;\n\n  $option_list = array();\n  $option_string_list = explode(USER_OPTIONS_SPLIT, $user['options']);\n\n  foreach ($option_string_list as $option_string) {\n    list($option_name, $option_value) = explode('^', $option_string);\n    $option_list[$option_name] = $option_value;\n  }\n\n  $final_list = array();\n  foreach ($user_option_list as $option_group_id => $option_group) {\n    $final_list[$option_group_id] = array();\n    foreach ($option_group as $option_name => $option_value) {\n      if (!isset($option_list[$option_name])) {\n        $option_list[$option_name] = $option_value;\n      }\n      $user[$option_name] = $final_list[$option_group_id][$option_name] = $option_list[$option_name];\n    }\n  }\n\n  $user['option_list'] = $final_list;\n\n  return $final_list;\n}\n\n\n// ----------------------------------------------------------------------------------------------------------------\nfunction get_player_max_expeditons(&$user, $astrotech = -1) { $result = null; return sn_function_call('get_player_max_expeditons', array(&$user, $astrotech, &$result)); }\n\nfunction sn_get_player_max_expeditons(&$user, $astrotech = -1, &$result = 0) {\n  if ($astrotech == -1) {\n    if (!isset($user[UNIT_PLAYER_EXPEDITIONS_MAX])) {\n      $astrotech = mrc_get_level($user, false, TECH_ASTROTECH);\n      $user[UNIT_PLAYER_EXPEDITIONS_MAX] = $astrotech >= 1 ? floor(sqrt($astrotech - 1)) : 0;\n    }\n\n    return $result += $user[UNIT_PLAYER_EXPEDITIONS_MAX];\n  } else {\n    return $result += $astrotech >= 1 ? floor(sqrt($astrotech - 1)) : 0;\n  }\n}\n\nfunction get_player_max_expedition_duration(&$user, $astrotech = -1) {\n  return $astrotech == -1 ? mrc_get_level($user, false, TECH_ASTROTECH) : $astrotech;\n}\n\nfunction get_player_max_colonies(&$user, $astrotech = -1) {\n  if ($astrotech == -1) {\n    if (!isset($user[UNIT_PLAYER_COLONIES_MAX])) {\n\n      $expeditions = get_player_max_expeditons($user);\n      $astrotech = mrc_get_level($user, false, TECH_ASTROTECH);\n      $colonies = $astrotech - $expeditions;\n\n      $user[UNIT_PLAYER_COLONIES_MAX] = SN::$config->player_max_colonies < 0 ? $colonies : min(SN::$config->player_max_colonies, $colonies);\n    }\n\n    return $user[UNIT_PLAYER_COLONIES_MAX];\n  } else {\n    $expeditions = get_player_max_expeditons($user, $astrotech);\n    $colonies = $astrotech - $expeditions;\n\n    return SN::$config->player_max_colonies < 0 ? $colonies : min(SN::$config->player_max_colonies, $colonies);\n  }\n}\n\nfunction get_player_current_colonies(&$user) {\n  return $user[UNIT_PLAYER_COLONIES_CURRENT] = isset($user[UNIT_PLAYER_COLONIES_CURRENT]) ? $user[UNIT_PLAYER_COLONIES_CURRENT] : max(0, DBStaticPlanet::db_planet_count_by_type($user['id']) - 1);\n}\n\nfunction GetSpyLevel(&$user) {\n  return mrc_modify_value($user, false, array(MRC_SPY, TECH_SPY), 0);\n}\n\nfunction GetMaxFleets(&$user) {\n  return mrc_modify_value($user, false, array(MRC_COORDINATOR, TECH_COMPUTER), 1);\n}\n\n\n// ----------------------------------------------------------------------------------------------------------------\n/**\n * @param int|string $user_id\n * @param int|string $capitalPlanetId\n *\n * @return mixed\n */\nfunction sys_player_new_adjust($user_id, $capitalPlanetId) { $result = null; return sn_function_call('sys_player_new_adjust', array($user_id, $capitalPlanetId, &$result)); }\n\nfunction sn_sys_player_new_adjust($user_id, $planet_id, &$result) {\n  return $result;\n}\n\n\n// ----------------------------------------------------------------------------------------------------------------\nfunction flt_get_missile_range($user) {\n  return max(0, mrc_get_level($user, false, TECH_ENGINE_ION) * 5 - 1);\n}\n\n"
  },
  {
    "path": "includes/general/general_pname.php",
    "content": "<?php\n\n// Эти функции будут переписаны по добавлению инфы в БД\n\nfunction pname_factory_production_field_name($factory_unit_id)\n{\n  return get_unit_param($factory_unit_id, P_NAME) . '_porcent';\n}\n\nfunction pname_resource_name($resource_id)\n{\n  return get_unit_param($resource_id, P_NAME);\n}"
  },
  {
    "path": "includes/general/general_unitFunctions.php",
    "content": "<?php\n\nuse Unit\\DBStaticUnit;\n\n/**\n * Created by Gorlum 04.12.2017 4:32\n */\n\nfunction eco_get_total_cost($unit_id, $unit_level) {\n  static $rate, $sn_group_resources_all, $sn_group_resources_loot;\n  if (!$rate) {\n    $sn_group_resources_all = sn_get_groups('resources_all');\n    $sn_group_resources_loot = sn_get_groups('resources_loot');\n\n    $rate[RES_METAL] = SN::$config->rpg_exchange_metal;\n    $rate[RES_CRYSTAL] = SN::$config->rpg_exchange_crystal / SN::$config->rpg_exchange_metal;\n    $rate[RES_DEUTERIUM] = SN::$config->rpg_exchange_deuterium / SN::$config->rpg_exchange_metal;\n  }\n\n  $unit_cost_data = get_unit_param($unit_id, 'cost');\n  if (!is_array($unit_cost_data)) {\n    return array('total' => 0);\n  }\n  $factor = isset($unit_cost_data['factor']) ? $unit_cost_data['factor'] : 1;\n  $cost_array = array(BUILD_CREATE => array(), 'total' => 0);\n  $unit_level = $unit_level > 0 ? $unit_level : 0;\n  foreach ($unit_cost_data as $resource_id => $resource_amount) {\n    if (!in_array($resource_id, $sn_group_resources_all)) {\n      continue;\n    }\n    $cost_array[BUILD_CREATE][$resource_id] = round($resource_amount * ($factor == 1 ? $unit_level : ((1 - pow($factor, $unit_level)) / (1 - $factor))));\n    if (in_array($resource_id, $sn_group_resources_loot)) {\n      $cost_array['total'] += $cost_array[BUILD_CREATE][$resource_id] * $rate[$resource_id];\n    }\n  }\n\n  return $cost_array;\n}\n\nfunction sn_unit_purchase($unit_id) { }\n\nfunction sn_unit_relocate($unit_id, $from, $to) { }\n\n/**\n * @param array $user\n * @param array $planet     ([])\n * @param int   $unit_id\n * @param bool  $for_update (false)\n * @param bool  $plain      (false)\n *\n * @return int|float|bool\n *\n * @see sn_mrc_get_level()\n */\nfunction mrc_get_level(&$user, $planet = [], $unit_id, $for_update = false, $plain = false) {\n  $result = null;\n\n  return sn_function_call(__FUNCTION__, [&$user, $planet, $unit_id, $for_update, $plain, &$result]);\n}\n\n/**\n * @param $user\n * @param $planet\n * @param $unit_id\n * @param $for_update\n * @param $plain\n * @param $result\n *\n * @return int|mixed\n *\n * @see mrc_get_level()\n */\nfunction sn_mrc_get_level(&$user, $planet = [], $unit_id, $for_update = false, $plain = false, &$result) {\n  $mercenary_level = 0;\n  $unit_db_name = pname_resource_name($unit_id);\n\n  if (in_array($unit_id, sn_get_groups(array('plans', 'mercenaries', 'tech', 'artifacts')))) {\n    $unit = !empty($user['id']) ? DBStaticUnit::db_unit_by_location($user['id'], LOC_USER, $user['id'], $unit_id) : 0;\n    $mercenary_level = !empty($unit['unit_level']) ? $unit['unit_level'] : 0;\n  } elseif (in_array($unit_id, sn_get_groups(array('structures', 'fleet', 'defense')))) {\n    $unit = DBStaticUnit::db_unit_by_location(is_array($user) ? $user['id'] : $planet['id_owner'], LOC_PLANET, $planet['id'], $unit_id);\n    $mercenary_level = !empty($unit['unit_level']) ? $unit['unit_level'] : 0;\n  } elseif (in_array($unit_id, sn_get_groups('governors'))) {\n    $mercenary_level = $unit_id == $planet['PLANET_GOVERNOR_ID'] ? $planet['PLANET_GOVERNOR_LEVEL'] : 0;\n  } elseif ($unit_id == RES_DARK_MATTER) {\n    $mercenary_level = $user[$unit_db_name] + ($plain || $user['user_as_ally'] ? 0 : SN::$auth->account->account_metamatter);\n  } elseif ($unit_id == RES_METAMATTER) {\n    $mercenary_level = SN::$auth->account->account_metamatter; //$user[$unit_db_name];\n  } elseif (in_array($unit_id, sn_get_groups(array('resources_loot'))) || $unit_id == UNIT_SECTOR) {\n    $mercenary_level = !empty($planet[$unit_db_name]) ? $planet[$unit_db_name] : $user[$unit_db_name];\n  }\n\n  return $result = $mercenary_level;\n}\n\nfunction mrc_modify_value(&$user, $planet = array(), $mercenaries, $value) { return sn_function_call('mrc_modify_value', array(&$user, $planet, $mercenaries, $value)); }\n\nfunction sn_mrc_modify_value(&$user, $planet = array(), $mercenaries, $value, $base_value = null) {\n  if (!is_array($mercenaries)) {\n    $mercenaries = array($mercenaries);\n  }\n\n  $base_value = isset($base_value) ? $base_value : $value;\n\n  foreach ($mercenaries as $mercenary_id) {\n    $mercenary_level = mrc_get_level($user, $planet, $mercenary_id);\n\n    $mercenary = get_unit_param($mercenary_id);\n    $mercenary_bonus = $mercenary[P_BONUS_VALUE];\n\n    switch ($mercenary[P_BONUS_TYPE]) {\n      case BONUS_PERCENT:\n        $mercenary_level = $mercenary_bonus < 0 && $mercenary_level * $mercenary_bonus < -90 ? -90 / $mercenary_bonus : $mercenary_level;\n        $value += $base_value * $mercenary_level * $mercenary_bonus / 100;\n      break;\n\n      case BONUS_ADD:\n        $value += $mercenary_level * $mercenary_bonus;\n      break;\n\n      case BONUS_ABILITY:\n        $value = $mercenary_level ? $mercenary_level : 0;\n      break;\n\n      default:\n      break;\n    }\n  }\n\n  return $value;\n}\n\nfunction sys_unit_str2arr($fleet_string) {\n  $fleet_array = array();\n  if (!empty($fleet_string)) {\n    $arrTemp = explode(';', $fleet_string);\n    foreach ($arrTemp as $temp) {\n      if ($temp) {\n        $temp = explode(',', $temp);\n        if (!empty($temp[0]) && !empty($temp[1])) {\n          $fleet_array[$temp[0]] += $temp[1];\n        }\n      }\n    }\n  }\n\n  return $fleet_array;\n}\n\nfunction sys_unit_arr2str($unit_list) {\n  $fleet_string = array();\n  if (isset($unit_list)) {\n    if (!is_array($unit_list)) {\n      $unit_list = array($unit_list => 1);\n    }\n\n    foreach ($unit_list as $unit_id => $unit_count) {\n      if ($unit_id && $unit_count) {\n        $fleet_string[] = \"{$unit_id},{$unit_count}\";\n      }\n    }\n  }\n\n  return implode(';', $fleet_string);\n}\n\n// TODO Для полноценного функионирования апдейтера пакет функций, включая эту должен быть вынесен раньше - или грузить general.php до апдейтера\nfunction sys_get_unit_location($user, $planet, $unit_id) { return sn_function_call('sys_get_unit_location', array($user, $planet, $unit_id)); }\n\nfunction sn_sys_get_unit_location($user, $planet, $unit_id) {\n  return get_unit_param($unit_id, 'location');\n}\n\n\nfunction get_engine_data($user, $engine_info, $user_tech_level = null) {\n  $sn_data_tech_bonus = get_unit_param($engine_info['tech'], P_BONUS_VALUE);\n\n  $user_tech_level = $user_tech_level === null ? intval(mrc_get_level($user, false, $engine_info['tech'])) : $user_tech_level;\n\n  $engine_info['speed_base'] = $engine_info['speed'];\n  $tech_bonus = ($user_tech_level - $engine_info['min_level']) * $sn_data_tech_bonus / 100;\n  $tech_bonus = $tech_bonus < -0.9 ? -0.95 : $tech_bonus;\n  $engine_info['speed'] = floor(mrc_modify_value($user, false, array(MRC_NAVIGATOR), $engine_info['speed']) * (1 + $tech_bonus));\n\n  $engine_info['consumption_base'] = $engine_info['consumption'];\n  $tech_bonus = ($user_tech_level - $engine_info['min_level']) * $sn_data_tech_bonus / 1000;\n  $tech_bonus = $tech_bonus > 0.5 ? 0.5 : ($tech_bonus < 0 ? $tech_bonus * 2 : $tech_bonus);\n  $engine_info['consumption'] = ceil($engine_info['consumption'] * (1 - $tech_bonus));\n\n  return $engine_info;\n}\n\nfunction get_ship_data($ship_id, $user) {\n  $ship_data = array();\n  if (in_array($ship_id, sn_get_groups(array('fleet', 'missile')))) {\n    foreach (get_unit_param($ship_id, 'engine') as $engine_info) {\n      $tech_level = intval(mrc_get_level($user, false, $engine_info['tech']));\n      if (empty($ship_data) || $tech_level >= $engine_info['min_level']) {\n        $ship_data = $engine_info;\n        $ship_data['tech_level'] = $tech_level;\n      }\n    }\n    $ship_data = get_engine_data($user, $ship_data);\n    $ship_data['capacity'] = get_unit_param($ship_id, 'capacity');\n  }\n\n  return $ship_data;\n}\n\n/**\n * Get unit info by unit's SN ID\n *\n * @param int $unitSnId\n *\n * @return mixed\n */\nfunction getUnitInfo($unitSnId) {\n  return get_unit_param($unitSnId);\n}\n\n/**\n * @param $unit_id\n * @param $param_name\n * @param $user\n * @param $planet\n *\n * @return mixed|null\n *\n * @see sn_get_unit_param()\n */\nfunction get_unit_param($unit_id, $param_name = null, $user = null, $planet = null) {\n  $result = null;\n\n  return sn_function_call('get_unit_param', array($unit_id, $param_name, $user, $planet, &$result));\n}\n\n/**\n * @param $unit_id\n * @param $param_name\n * @param $user\n * @param $planet\n * @param $result\n *\n * @return array|mixed\n *\n * @see get_unit_param()\n */\nfunction sn_get_unit_param($unit_id, $param_name = null, $user = null, $planet = null, &$result) {\n  global $sn_data;\n\n  $result = isset($sn_data[$unit_id])\n    ? ($param_name === null\n      ? $sn_data[$unit_id]\n      : (isset($sn_data[$unit_id][$param_name]) ? $sn_data[$unit_id][$param_name] : $result)\n    )\n    : $result;\n\n  return $result;\n}\n\n/**\n * @param string|string[] $groups\n *\n * @return array|array[]\n */\nfunction sn_get_groups($groups) {\n  $result = null;\n\n  return sn_function_call('sn_get_groups', array($groups, &$result));\n}\n\nfunction sn_sn_get_groups($groups, &$result) {\n  $result = is_array($result) ? $result : array();\n  foreach ($groups = is_array($groups) ? $groups : array($groups) as $group_name) {\n    $result += is_array($a_group = get_unit_param(UNIT_GROUP, $group_name)) ? $a_group : array();\n  }\n\n  return $result;\n}\n\n\nfunction unit_requirements_render($user, $planetrow, $unit_id, $field = P_REQUIRE) {\n  $result = null;\n\n  return sn_function_call('unit_requirements_render', array($user, $planetrow, $unit_id, $field, &$result));\n}\n\nfunction sn_unit_requirements_render($user, $planetrow, $unit_id, $field = P_REQUIRE, &$result) {\n  global $lang, $config;\n\n  $sn_data_unit = get_unit_param($unit_id);\n\n  $result = is_array($result) ? $result : array();\n  if ($sn_data_unit[$field] && !($sn_data_unit[P_UNIT_TYPE] == UNIT_MERCENARIES && SN::$config->empire_mercenary_temporary)) {\n    foreach ($sn_data_unit[$field] as $require_id => $require_level) {\n      $level_got = mrc_get_level($user, $planetrow, $require_id);\n      $level_basic = mrc_get_level($user, $planetrow, $require_id, false, true);\n      $result[] = array(\n        'NAME'             => $lang['tech'][$require_id],\n        //'CLASS' => $require_level > $level_got ? 'negative' : ($require_level == $level_got ? 'zero' : 'positive'),\n        'REQUEREMENTS_MET' => intval($require_level <= $level_got ? REQUIRE_MET : REQUIRE_MET_NOT),\n        'LEVEL_REQUIRE'    => $require_level,\n        'LEVEL'            => $level_got,\n        'LEVEL_BASIC'      => $level_basic,\n        'LEVEL_BONUS'      => max(0, $level_got - $level_basic),\n        'ID'               => $require_id,\n      );\n    }\n  }\n\n  return $result;\n}\n\n/**\n * @param array $cost        - [(int)resourceId => (float)unitAmount] => [RES_CRYSTAL => 100]\n * @param int   $in_resource - RES_METAL...\n *\n * @return float|int\n */\nfunction get_unit_cost_in($cost, $in_resource = RES_METAL) {\n  static $rates;\n\n  if (!$rates) {\n    $rates = SN::$gc->economicHelper->getResourcesExchange();\n  }\n\n  unset($cost[P_FACTOR]);\n\n  $mainResourceExchange = !empty($rates[$in_resource]) ? $rates[$in_resource] : 1;\n  $metal_cost = 0;\n  foreach ($cost as $resource_id => $resource_value) {\n    if (empty($rates[$resource_id])) {\n      continue;\n    }\n\n    $metal_cost += $rates[$resource_id] / $mainResourceExchange * $resource_value;\n  }\n\n  return $metal_cost;\n}\n\n/**\n * Calculates cost of STACKABLE unit in specified resource\n *\n * @param int|float[] $units - unit ID or array [unitId => unitAmount]\n * @param int         $costResourceId\n *\n * @return float|int\n */\nfunction getStackableUnitsCost($units, $costResourceId = RES_METAL) {\n  static $costCache;\n\n  $result = 0;\n\n  if (!is_array($units)) {\n    $units = [$units => 1];\n  }\n\n  foreach ($units as $unitId => $unitAmount) {\n    if (!isset($costCache[$unitId][$costResourceId])) {\n      $unitInfo = get_unit_param($unitId);\n\n      $costCache[$unitId][$costResourceId] = !empty($unitInfo[P_COST]) ? get_unit_cost_in($unitInfo[P_COST], $costResourceId) : 0;\n    }\n\n    $result += $costCache[$unitId][$costResourceId] * $unitAmount;\n  }\n\n  return $result;\n}\n"
  },
  {
    "path": "includes/general/general_urlAndHttp.php",
    "content": "<?php\n/**\n * Created by Gorlum 04.12.2017 4:45\n */\n\n// ----------------------------------------------------------------------------------------------------------------\nfunction sys_redirect($url) {\n  header(\"Location: {$url}\");\n  ob_end_flush();\n  die();\n}\n\n/**\n * Redirects via JS-script\n *\n * @param string $url\n */\nfunction sys_redirect_js($url) {\n  ob_end_flush();\n\n  $redirectTemplate = SnTemplate::gettemplate('_redirect');\n  $redirectTemplate->assign_vars(array(\n    'URL' => js_safe_string($url),\n  ));\n\n  SnTemplate::display($redirectTemplate);\n  die();\n}\n\n\n// ----------------------------------------------------------------------------------------------------------------\n/**\n * Wrapper for header() function\n *\n * @param string $header\n */\nfunction setHeader($header) {\n  header($header);\n}\n\n\n// ----------------------------------------------------------------------------------------------------------------\nfunction sn_get_url_contents($url) {\n  if (function_exists('curl_init')) {\n    $crl     = curl_init();\n    $timeout = 5;\n    curl_setopt_array($crl, [\n      // CURLOPT_IPRESOLVE => CURL_IPRESOLVE_V4,\n      // CURLOPT_DNS_USE_GLOBAL_CACHE => false,\n      // CURLOPT_DNS_CACHE_TIMEOUT => 2,\n      CURLOPT_CONNECTTIMEOUT => $timeout,\n      CURLOPT_RETURNTRANSFER => true,\n      CURLOPT_URL            => $url,\n    ]);\n    $return = curl_exec($crl);\n    curl_close($crl);\n  } else {\n    $return = @file_get_contents($url);\n  }\n\n  return $return;\n}\n\n/**\n * @param $url\n * @param array $data\n *\n * @return bool|false|string\n */\nfunction sn_post_url_contents($url, $data) {\n  if (function_exists('curl_init')) {\n    $crl = curl_init();\n    $timeout = 5;\n    curl_setopt($crl, CURLOPT_URL, $url);\n    curl_setopt($crl, CURLOPT_RETURNTRANSFER, 1);\n    curl_setopt($crl, CURLOPT_CONNECTTIMEOUT, $timeout);\n    curl_setopt($crl, CURLOPT_POST, true);\n    curl_setopt($crl, CURLOPT_POSTFIELDS, $data);\n    $return = curl_exec($crl);\n\n//    var_dump('error: ' . curl_error($crl));\n\n    curl_close($crl);\n  } else {\n    $return = @file_get_contents($url);\n  }\n\n  return $return;\n}\n\nfunction invokeUrl($url) {\n  exec(\"curl $url > /dev/null 2>&1 &\");\n}\n\n// ----------------------------------------------------------------------------------------------------------------\nfunction sn_setcookie($name, $value = null, $expire = null, $path = SN_ROOT_RELATIVE, $domain = null, $secure = null, $httponly = null) {\n  $_COOKIE[$name] = $value;\n\n  return setcookie($name, $value, $expire, $path, $domain, $secure, $httponly);\n}\n\n\n// ----------------------------------------------------------------------------------------------------------------\n/**\n * Возвращает информацию об IPv4 адресах пользователя\n *\n * НЕ ПОДДЕРЖИВАЕТ IPv6!\n *\n * @return array\n */\nfunction sec_player_ip() {\n  // TODO - IPv6 support\n  $ip = array(\n    'ip'          => $_SERVER[\"REMOTE_ADDR\"],\n    'proxy_chain' => $_SERVER[\"HTTP_X_FORWARDED_FOR\"]\n      ? $_SERVER[\"HTTP_X_FORWARDED_FOR\"]\n      : ($_SERVER[\"HTTP_CLIENT_IP\"]\n        ? $_SERVER[\"HTTP_CLIENT_IP\"]\n        : '' // $_SERVER[\"REMOTE_ADDR\"]\n      ),\n  );\n\n  // Quick hack to support IPv6 at least on local host\n  if($ip['ip'] == '::1') {\n    $ip['ip'] = '127.0.0.1';\n  }\n\n  foreach($ip as &$val) {\n    $val = SN::$db->db_escape($val);\n  }\n\n  return $ip;\n//  return array_map('db_escape', $ip);\n}\n"
  },
  {
    "path": "includes/general/general_validators.php",
    "content": "<?php\n/**\n * Created by Gorlum 04.12.2017 4:26\n */\n\nfunction is_email($email) {\n  return (preg_match(\"/^[-_.[:alnum:]]+@((([[:alnum:]]|[[:alnum:]][[:alnum:]-]*[[:alnum:]])\\.)+(ad|ae|aero|af|ag|ai|al|am|an|ao|aq|ar|arpa|as|at|au|aw|az|ba|bb|bd|be|bf|bg|bh|bi|biz|bj|bm|bn|bo|br|bs|bt|bv|bw|by|bz|ca|cc|cd|cf|cg|ch|ci|ck|cl|cm|cn|co|com|coop|cr|cs|cu|cv|cx|cy|cz|de|dj|dk|dm|do|dz|ec|edu|ee|eg|eh|er|es|et|eu|fi|fj|fk|fm|fo|fr|ga|gb|gd|ge|gf|gh|gi|gl|gm|gn|gov|gp|gq|gr|gs|gt|gu|gw|gy|hk|hm|hn|hr|ht|hu|id|ie|il|in|info|int|io|iq|ir|is|it|jm|jo|jp|ke|kg|kh|ki|km|kn|kp|kr|kw|ky|kz|la|lb|lc|li|lk|lr|ls|lt|lu|lv|ly|ma|mc|md|mg|mh|mil|mk|ml|mm|mn|mo|mp|mq|mr|ms|mt|mu|museum|mv|mw|mx|my|mz|na|name|nc|ne|net|nf|ng|ni|nl|no|np|nr|nt|nu|nz|om|org|pa|pe|pf|pg|ph|pk|pl|pm|pn|pr|pro|ps|pt|pw|py|qa|re|ro|ru|rw|sa|sb|sc|sd|se|sg|sh|si|sj|sk|sl|sm|sn|so|sr|st|su|sv|sy|sz|tc|td|tf|tg|th|tj|tk|tm|tn|to|tp|tr|tt|tv|tw|tz|ua|ug|uk|um|us|uy|uz|va|vc|ve|vg|vi|vn|vu|wf|ws|ye|yt|yu|za|zm|zw)$|(([0-9][0-9]?|[0-1][0-9][0-9]|[2][0-4][0-9]|[2][5][0-5])\\.){3}([0-9][0-9]?|[0-1][0-9][0-9]|[2][0-4][0-9]|[2][5][0-5]))$/i\", $email));\n}\n\nfunction is_id($value) {\n  return preg_match('/^\\d+$/', $value) && ($value >= 0);\n}\n"
  },
  {
    "path": "includes/includes/art_artifact.php",
    "content": "<?php\n\nuse DBAL\\db_mysql;\nuse DBAL\\OldDbChangeSet;\nuse Planet\\DBStaticPlanet;\nuse Que\\DBStaticQue;\nuse Universe\\Universe;\n\nfunction art_use(&$user, &$planetrow, $unit_id)\n{\n  global $lang;\n\n  if(!in_array($unit_id, sn_get_groups('artifacts')))\n  {\n    return;\n  }\n\n  db_mysql::db_transaction_start();\n  $user = db_user_by_id($user['id'], true);\n\n  $unit_level = $artifact_level_old = mrc_get_level($user, array(), $unit_id, true);\n  if($unit_level > 0)\n  {\n    $db_changeset = array();\n    switch($unit_id)\n    {\n      case ART_LHC:\n      case ART_HOOK_SMALL:\n      case ART_HOOK_MEDIUM:\n      case ART_HOOK_LARGE:\n        $has_moon = DBStaticPlanet::db_planet_by_parent($planetrow['id'], true, '`id`');\n        if($planetrow['planet_type'] == PT_PLANET && !$has_moon['id'])\n        {\n          $unit_level--;\n          $updateDebris = false;\n          switch ($unit_id) {\n            case ART_HOOK_SMALL:\n              $moonSize = Universe::MOON_MIN_SIZE;\n            break;\n            case ART_HOOK_MEDIUM:\n              $moonSize = Universe::moonSizeRandom();\n            break;\n            case ART_HOOK_LARGE:\n              $moonSize = Universe::MOON_MAX_SIZE;\n            break;\n            case ART_LHC:\n            default:\n              $moonSize = Universe::moonRollSize($planetrow['debris_metal'] + $planetrow['debris_crystal']);\n              $updateDebris = true;\n            break;\n          }\n\n          if($moonSize)\n          {\n            $new_moon_row = uni_create_moon($planetrow['galaxy'], $planetrow['system'], $planetrow['planet'], $user['id'], $moonSize, $updateDebris);\n            $message = sprintf($lang['art_moon_create'][$unit_id], $new_moon_row['name'], uni_render_coordinates($planetrow), HelperString::numberFloorAndFormat($moonSize));\n          }\n          else\n          {\n            $message = $lang['art_lhc_moon_fail'];\n          }\n          msg_send_simple_message($user['id'], 0, 0, MSG_TYPE_ADMIN, $lang['art_lhc_from'], $lang['art_lhc_subj'], $message);\n        }\n        else\n        {\n          $message = $lang['art_moon_exists'];\n        }\n      break;\n\n      case ART_RCD_SMALL:\n      case ART_RCD_MEDIUM:\n      case ART_RCD_LARGE:\n        $planetrow = DBStaticPlanet::db_planet_by_id($planetrow['id'], true);\n        if($planetrow['planet_type'] != PT_PLANET)\n        {\n          $message = $lang['art_rcd_err_moon'];\n          break;\n        }\n\n        $que = que_get($user['id'], $planetrow['id'], QUE_STRUCTURES, false);\n        if(!empty($que['items']))\n        {\n          $message = $lang['art_rcd_err_que'];\n          break;\n        }\n\n        $artifact_deploy = get_unit_param($unit_id, P_DEPLOY);\n\n        $sectors_used = 0;\n        foreach($artifact_deploy as $deploy_unit_id => $deploy_unit_level)\n        {\n          if(!($levels_deployed = max(0, $deploy_unit_level - mrc_get_level($user, $planetrow, $deploy_unit_id, true, true))))\n            continue;\n          $sectors_used += $levels_deployed;\n          $db_changeset['unit'][] = OldDbChangeSet::db_changeset_prepare_unit($deploy_unit_id, $levels_deployed, $user, $planetrow['id']);\n        }\n\n        if($sectors_used == 0)\n        {\n          $message = $lang['art_rcd_err_no_sense'];\n          break;\n        }\n        $unit_level--;\n        DBStaticPlanet::db_planet_set_by_id($planetrow['id'], \"`field_current` = `field_current` + {$sectors_used}\");\n        $message = sprintf($lang['art_rcd_ok'], $lang['tech'][$unit_id], $planetrow['name'], uni_render_coordinates($planetrow));\n        msg_send_simple_message($user['id'], 0, 0, MSG_TYPE_QUE, $lang['art_rcd_subj'], $lang['art_rcd_subj'], $message);\n      break;\n\n      case ART_HEURISTIC_CHIP:\n        $que_item = null;\n        $que = que_get($user['id'], $planetrow['id'], QUE_RESEARCH, true);\n        $current_que = &$que['ques'][QUE_RESEARCH][$user['id']][0];\n        if(!empty($current_que))\n        {\n          reset($current_que);\n          $que_item = &$que['ques'][QUE_RESEARCH][$user['id']][0][key($current_que)];\n        }\n\n        if(!empty($que_item) && $que_item['que_time_left'] > 60)\n        {\n          $unit_level--;\n          $old_time = $que_item['que_time_left'];\n          $que_item['que_time_left'] = $que_item['que_time_left'] > PERIOD_HOUR ? ceil($que_item['que_time_left'] / 2) : 0;\n          DBStaticQue::db_que_set_time_left_by_id($que_item['que_id'], $que_item['que_time_left']);\n          $message = sprintf($lang['art_heurestic_chip_ok'], $lang['tech'][$que_item['que_unit_id']], $que_item['que_unit_level'], sys_time_human($old_time - $que_item['que_time_left']));\n          msg_send_simple_message($user['id'], 0, 0, MSG_TYPE_QUE, $lang['art_heurestic_chip_subj'], $lang['art_heurestic_chip_subj'], $message);\n        }\n        else\n        {\n          $message = $lang['art_heurestic_chip_no_research'];\n        }\n      break;\n\n      case ART_NANO_BUILDER:\n        $planetrow = DBStaticPlanet::db_planet_by_id($planetrow['id'], true);\n        $que_item = null;\n        $que = que_get($user['id'], $planetrow['id'], QUE_STRUCTURES, true);\n        $current_que = &$que['ques'][QUE_STRUCTURES][$user['id']][$planetrow['id']];\n        // $que_item = &$que['que'][QUE_STRUCTURES][0];\n        if(!empty($current_que))\n        {\n          reset($current_que);\n          $que_item = &$que['ques'][QUE_STRUCTURES][$user['id']][$planetrow['id']][key($current_que)];\n        }\n\n        if(isset($que_item) && $que_item['que_time_left'] > 60)\n        {\n          $unit_level--;\n          $old_time = $que_item['que_time_left'];\n          $que_item['que_time_left'] = $que_item['que_time_left'] > PERIOD_HOUR ? ceil($que_item['que_time_left'] / 2) : 0;\n          DBStaticQue::db_que_set_time_left_by_id($que_item['que_id'], $que_item['que_time_left']);\n          $message = sprintf($lang['art_nano_builder_ok'], $que_item['que_unit_mode'] == BUILD_CREATE ? $lang['art_nano_builder_build'] : $lang['art_nano_builder_destroy'],\n            $lang['tech'][$que_item['que_unit_id']], $que_item['que_unit_level'], $planetrow['name'], uni_render_coordinates($planetrow), sys_time_human($old_time - $que_item['que_time_left'])\n          );\n          msg_send_simple_message($user['id'], 0, 0, MSG_TYPE_QUE, $lang['art_nano_builder_subj'], $lang['art_nano_builder_subj'], $message);\n        }\n        else\n        {\n          $message = $lang['art_nano_builder_no_que'];\n        }\n      break;\n\n    }\n    if($unit_level != $artifact_level_old)\n    {\n      $db_changeset['unit'][] = OldDbChangeSet::db_changeset_prepare_unit($unit_id, $unit_level - $artifact_level_old, $user);\n      OldDbChangeSet::db_changeset_apply($db_changeset);\n    }\n  }\n  else\n  {\n    $message = $lang['art_err_no_artifact'];\n  }\n\n  db_mysql::db_transaction_commit();\n  SnTemplate::messageBox($message, \"{$lang['tech'][UNIT_ARTIFACTS]} - {$lang['tech'][$unit_id]}\",\n    ($request_uri = sys_get_param_str_unsafe('REQUEST_URI')) ? $request_uri : ('artifacts' . DOT_PHP_EX . '#' . $unit_id),\n    5);\n}\n"
  },
  {
    "path": "includes/includes/coe_simulator_helpers.php",
    "content": "<?php\n\nfunction sn_ube_simulator_encode_replay($combat, $type) {\n  $strPacked = \"{$type}!\";\n\n  foreach ($combat as $fleetID => $fleetCompress) {\n    foreach ($fleetCompress as $key => $value) {\n      $value = intval($value);\n      $strPacked .= \"{$key},{$value};\";\n    }\n    $strPacked .= '!';\n  }\n\n  return $strPacked;\n}\n\nfunction sn_ube_simulator_decode_replay($str_data) {\n  $fleet_id = 0;\n\n  $arr_data_unpacked = explode('!', $str_data);\n  foreach ($arr_data_unpacked as $data_piece) {\n    if (!$data_piece) {\n      continue;\n    }\n\n    if ($data_piece == 'A' || $data_piece == 'D') {\n      $fleet_type = $data_piece;\n      continue;\n    }\n\n    $arr_unit_strings = explode(';', $data_piece);\n    foreach ($arr_unit_strings as $str_unit_string) {\n      if (!$str_unit_string) {\n        continue;\n      }\n\n      $arr_unit_data = explode(',', $str_unit_string);\n      if ($arr_unit_data[1]) {\n        $unpacked[$fleet_type][$fleet_id][$arr_unit_data[0]] = intval($arr_unit_data[1]);\n      }\n    }\n\n    $fleet_id++;\n  }\n\n  return $unpacked;\n}\n"
  },
  {
    "path": "includes/includes/eco_bld_structures.php",
    "content": "<?php\n\n/**\n * eco_build.php\n *\n * @version 1.5 - Using PTE (not everywhere) by Gorlum for http://supernova.ws\n * @version 1.4 - Complying with PCG by Gorlum for http://supernova.ws\n * @version 1.3 - Security checked for SQL-injection by Gorlum for http://supernova.ws\n * // 1.0 Mise en module initiale (creation)\n * // 1.1 FIX interception cheat +1\n * // 1.2 FIX interception cheat destruction a -1\n * @version 1.1\n * @copyright 2008 by Chlorel for XNova\n */\n\nfunction eco_build($que_type, &$user, &$planet) { return sn_function_call('eco_build', array($que_type, &$user, &$planet)); }\n\nfunction sn_eco_build($que_type, &$auser, &$planet) {\n  global $lang, $config, $template_result;\n\n  if($ally_id = sys_get_param_id('ally_id')) {\n    define('SN_IN_ALLY', true);\n    $ranks = \\Alliance\\Alliance::ally_get_ranks($auser['ally']);\n    if($ranks[$auser['ally_rank_id']]['admin'] || $auser['ally']['ally_owner'] == $auser['id']) {\n      $user = &$auser['ally']['player'];\n      $planet = array(\n        'metal'     => $user['metal'],\n        'crystal'   => $user['crystal'],\n        'deuterium' => $user['deuterium'],\n      );\n    }\n  }\n\n  if(!$user) {\n    $user = &$auser;\n  }\n\n  switch($action = sys_get_param_escaped('action')) {\n    case 'create': // Add unit to que for build\n    case 'create_autoconvert': // Add unit to que for build\n    case 'destroy': // Add unit to que for remove\n      $operation_result = que_build($user, $planet, $action == 'destroy' ? BUILD_DESTROY : ($action == 'create' ? BUILD_CREATE : BUILD_AUTOCONVERT));\n    break;\n\n    case 'trim':\n      que_delete($que_type, $user, $planet, false);\n    break;\n    case 'clear':\n      que_delete($que_type, $user, $planet, true);\n    break;\n  }\n\n  $group_missile = sn_get_groups('missile');\n  $silo_capacity_free = 0;\n  if($que_type == QUE_STRUCTURES) {\n    $build_unit_list = sn_get_groups('build_allow');\n    $build_unit_list = $build_unit_list[$planet['planet_type']];\n    $artifact_id = ART_NANO_BUILDER;\n    $page_header = $lang['tech'][UNIT_STRUCTURES];\n  } elseif($que_type == QUE_RESEARCH) {\n    if(!mrc_get_level($user, $planet, STRUC_LABORATORY)) {\n      SnTemplate::messageBox($lang['no_laboratory'], $lang['tech'][UNIT_TECHNOLOGIES]);\n    }\n\n    if(eco_unit_busy($user, $planet, UNIT_TECHNOLOGIES)) {\n      SnTemplate::messageBox($lang['eco_bld_msg_err_laboratory_upgrading'], $lang['tech'][UNIT_TECHNOLOGIES]);\n    }\n    $build_unit_list = sn_get_groups('tech');\n    $artifact_id = ART_HEURISTIC_CHIP;\n    $page_header = $lang['eco_bld_research_page_name'] . ($user['user_as_ally'] ? \"&nbsp;{$lang['sys_of_ally']}&nbsp;{$user['username']}\" : '');\n  } elseif($que_type == QUE_MERCENARY) {\n//    if(!mrc_get_level($user, $planet, STRUC_LABORATORY)) {\n//      messageBox($lang['no_laboratory'], $lang['tech'][UNIT_TECHNOLOGIES]);\n//    }\n\n//    if(eco_unit_busy($user, $planet, UNIT_TECHNOLOGIES)) {\n//      messageBox($lang['eco_bld_msg_err_laboratory_upgrading'], $lang['tech'][UNIT_TECHNOLOGIES]);\n//    }\n    $build_unit_list = sn_get_groups('mercenaries');\n    $artifact_id = 0;\n    $page_header = $lang['tech'][UNIT_MERCENARIES] . ($user['user_as_ally'] ? \"&nbsp;{$lang['sys_of_ally']}&nbsp;{$user['username']}\" : '');\n  } else {\n    if(mrc_get_level($user, $planet, STRUC_FACTORY_HANGAR) == 0) {\n      SnTemplate::messageBox($lang['need_hangar'], $lang['tech'][STRUC_FACTORY_HANGAR]);\n    }\n\n    $build_unit_list = sn_get_groups($page_mode = $que_type == SUBQUE_FLEET ? 'fleet' : 'defense');\n    $page_header = $lang[$page_mode];\n    $artifact_id = 0;\n\n    $silo_capacity_free = mrc_get_level($user, $planet, STRUC_SILO) * get_unit_param(STRUC_SILO, P_CAPACITY);\n    foreach($group_missile as $unit_id) {\n      $silo_capacity_free -= (mrc_get_level($user, $planet, $unit_id, false, true) + (isset($in_que[$unit_id]) && $in_que[$unit_id] ? $in_que[$unit_id] : 0)) * get_unit_param($unit_id, P_UNIT_SIZE);\n    }\n    $silo_capacity_free = max(0, $silo_capacity_free);\n  }\n\n  // Caching values that used more then one time into local variables\n  $config_resource_multiplier = game_resource_multiplier();\n  $config_resource_multiplier_plain = game_resource_multiplier(true);\n\n  /*\n  // Code for fully working new que system\n  $hangar_busy = count($que['que'][QUE_HANGAR]);\n  $lab_busy    = count($que['que'][QUE_RESEARCH]) && !$config->BuildLabWhileRun;\n  */\n\n  $template = SnTemplate::gettemplate('buildings_builds', true);\n  if(!empty($operation_result)) {\n    $template_result['.']['result'][] = $operation_result;\n  }\n\n  $planet_id = $que_type == QUE_RESEARCH || $que_type == QUE_MERCENARY ? 0 : $planet['id'];\n\n  $ques = que_get($user['id'], $planet_id, $que_type);\n  $in_que = &$ques['in_que'][$que_type][$user['id']][$planet_id];\n  $que = &$ques['ques'][$que_type][$user['id']][$planet_id];\n  que_tpl_parse($template, $que_type, $user, $planet, $que);\n\n  $que_length = empty($que) ? 0 : count($que);\n  $can_que_element = $que_length < que_get_max_que_length($user, $planet, $que_type);\n\n  $fleet_list = flt_get_fleets_to_planet($planet);\n\n  $planet_fields_max = eco_planet_fields_max($planet);\n  $planet_fields_current = $planet['field_current'];\n  $planet_fields_que = is_array($in_que) ? -array_sum($in_que) : 0;\n  $planet_fields_free = max(0, $planet_fields_max - $planet_fields_current + $planet_fields_que);\n  $planet_fields_queable = $que_type != QUE_STRUCTURES || $planet_fields_free > 0;\n  $sn_modifiers_resource = sn_get_groups(GROUP_MODIFIERS_NAME);\n  $sn_modifiers_resource = $sn_modifiers_resource[MODIFIER_RESOURCE_PRODUCTION];\n  $sn_groups_density = sn_get_groups('planet_density');\n  $density_info = $sn_groups_density[$planet['density_index']][UNIT_RESOURCES];\n\n  $user_dark_matter = mrc_get_level($user, null, RES_DARK_MATTER);\n\n  $record_index = 0;\n\n  foreach($build_unit_list as $unit_id) {\n    $level_base = mrc_get_level($user, $planet, $unit_id, false, true);\n    $level_effective = mrc_get_level($user, $planet, $unit_id);\n    $level_in_que = $in_que[$unit_id];\n    $level_bonus = max(0, $level_effective - $level_base);\n    $level_base_and_que = $level_base + $level_in_que;\n\n    $unit_info = get_unit_param($unit_id);\n    $unit_stackable = isset($unit_info[P_STACKABLE]) && $unit_info[P_STACKABLE];\n\n    $build_data = eco_get_build_data($user, $planet, $unit_id, $level_base_and_que);\n    $temp[RES_METAL] = floor($planet['metal'] + $fleet_list['own']['total'][RES_METAL] - $build_data[BUILD_CREATE][RES_METAL]);\n    $temp[RES_CRYSTAL] = floor($planet['crystal'] + $fleet_list['own']['total'][RES_CRYSTAL] - $build_data[BUILD_CREATE][RES_CRYSTAL]);\n    $temp[RES_DEUTERIUM] = floor($planet['deuterium'] + $fleet_list['own']['total'][RES_DEUTERIUM] - $build_data[BUILD_CREATE][RES_DEUTERIUM]);\n    $temp[RES_DARK_MATTER] = floor($user_dark_matter - $build_data[BUILD_CREATE][RES_DARK_MATTER]);\n\n    $build_data['RESULT'][BUILD_CREATE] = $build_data['RESULT'][BUILD_CREATE] == BUILD_ALLOWED && !$can_que_element ? BUILD_QUE_FULL : $build_data['RESULT'][BUILD_CREATE];\n\n\n    // Restricting $can_build by resources on planet and (where applicable) with max count per unit\n    $can_build = $unit_info[P_MAX_STACK] ? max(0, $unit_info[P_MAX_STACK] - $level_in_que - $level_effective) : $build_data['CAN'][BUILD_CREATE];\n    // Restricting $can_build by free silo capacity\n    $can_build = ($unit_is_missile = in_array($unit_id, $group_missile)) ? min($can_build, floor($silo_capacity_free / $unit_info[P_UNIT_SIZE])) : $can_build;\n    if(!$can_build) {\n      if(!$build_data['CAN'][BUILD_CREATE]) {\n        $build_data['RESULT'][BUILD_CREATE] = BUILD_NO_RESOURCES;\n      } elseif($unit_is_missile && $silo_capacity_free < $unit_info[P_UNIT_SIZE]) {\n        $build_data['RESULT'][BUILD_CREATE] = BUILD_SILO_FULL;\n      } elseif($unit_info[P_MAX_STACK]) {\n        $build_data['RESULT'][BUILD_CREATE] = BUILD_MAX_REACHED;\n      }\n    }\n\n    $unit_info['type'] == UNIT_STRUCTURES && !$planet_fields_queable ? $build_data['RESULT'][BUILD_CREATE] = BUILD_SECTORS_NONE : false;\n    $unit_autoconvert_can = !SN::$user_options[PLAYER_OPTION_BUILD_AUTOCONVERT_HIDE] && $build_data['RESULT'][BUILD_CREATE] == BUILD_NO_RESOURCES && $build_data[BUILD_AUTOCONVERT];\n\n\n    $unit_autoconvert_can ? $build_data['RESULT'][BUILD_CREATE] = BUILD_AUTOCONVERT_AVAILABLE : false;\n\n    $build_result_text = $lang['sys_build_result'][$build_data['RESULT'][BUILD_CREATE]];\n    $build_result_text = !is_array($build_result_text) ? $build_result_text : (isset($build_result_text[$unit_id]) ? $build_result_text[$unit_id] : $build_result_text[0]);\n\n    $production = array(\n      '__INDEX'     => $record_index++,\n      'ID'          => $unit_id,\n      'NAME'        => $lang['tech'][$unit_id],\n      'DESCRIPTION' => $lang['info'][$unit_id]['description_short'],\n      'UNIT_TYPE'   => $unit_info[P_UNIT_TYPE],\n\n      'LEVEL_OLD'   => $level_base,\n      'LEVEL_BONUS' => $level_bonus,\n      'LEVEL_NEXT'  => $level_base + $level_in_que + 1,\n      'LEVEL_QUED'  => $level_in_que,\n      'LEVEL'       => $level_base_and_que,\n\n      'CAN_BUILD'          => $can_build,\n      'CAN_AUTOCONVERT'    => $unit_autoconvert_can,\n      'AUTOCONVERT_AMOUNT' => $build_data[BUILD_AUTOCONVERT],\n\n      'BUILD_CAN'        => $build_data['CAN'][BUILD_CREATE],\n      'TIME'             => pretty_time($build_data[RES_TIME][BUILD_CREATE]),\n      'TIME_SECONDS'     => $build_data[RES_TIME][BUILD_CREATE],\n      'METAL'            => $build_data[BUILD_CREATE][RES_METAL],\n      'METAL_TEXT'       => prettyNumberStyledCompare($build_data[BUILD_CREATE][RES_METAL], $planet['metal']),\n      'CRYSTAL'          => $build_data[BUILD_CREATE][RES_CRYSTAL],\n      'CRYSTAL_TEXT'     => prettyNumberStyledCompare($build_data[BUILD_CREATE][RES_CRYSTAL], $planet['crystal']),\n      'DEUTERIUM'        => $build_data[BUILD_CREATE][RES_DEUTERIUM],\n      'DEUTERIUM_TEXT'   => prettyNumberStyledCompare($build_data[BUILD_CREATE][RES_DEUTERIUM], $planet['deuterium']),\n      'ENERGY'           => $build_data[BUILD_CREATE][RES_ENERGY],\n      'DARK_MATTER'      => $build_data[BUILD_CREATE][RES_DARK_MATTER],\n      'DARK_MATTER_ONLY' => $build_data[P_OPTIONS][P_ONLY_DARK_MATTER],\n\n      'BUILD_RESULT'      => $build_data['RESULT'][BUILD_CREATE],\n      'BUILD_RESULT_TEXT' => $build_result_text,\n\n      'DESTROY_RESULT'    => $build_data['RESULT'][BUILD_DESTROY],\n      'DESTROY_CAN'       => $build_data['CAN'][BUILD_DESTROY],\n      'DESTROY_TIME'      => pretty_time($build_data[RES_TIME][BUILD_DESTROY]),\n      'DESTROY_METAL'     => $build_data[BUILD_DESTROY][RES_METAL],\n      'DESTROY_CRYSTAL'   => $build_data[BUILD_DESTROY][RES_CRYSTAL],\n      'DESTROY_DEUTERIUM' => $build_data[BUILD_DESTROY][RES_DEUTERIUM],\n\n//      'METAL_REST'           => prettyNumberStyledDefault($temp[RES_METAL]),\n//      'CRYSTAL_REST'         => prettyNumberStyledDefault($temp[RES_CRYSTAL]),\n//      'DEUTERIUM_REST'       => prettyNumberStyledDefault($temp[RES_DEUTERIUM]),\n//      'DARK_MATTER_REST'     => prettyNumberStyledDefault($temp[RES_DARK_MATTER]),\n      'METAL_REST_NUM'       => $temp[RES_METAL],\n      'CRYSTAL_REST_NUM'     => $temp[RES_CRYSTAL],\n      'DEUTERIUM_REST_NUM'   => $temp[RES_DEUTERIUM],\n      'DARK_MATTER_REST_NUM' => $temp[RES_DARK_MATTER],\n\n      'UNIT_BUSY' => eco_unit_busy($user, $planet, $que, $unit_id),\n\n      'MAP_IS_RESOURCE' => !empty($unit_info[P_UNIT_PRODUCTION]),\n    );\n\n    if($unit_stackable) {\n      $level_production_base = array(\n        'ACTUAL_SHIELD' => HelperString::numberFloorAndFormat(mrc_modify_value($user, false, array(MRC_ADMIRAL, TECH_SHIELD), $unit_info['shield'])),\n        'ACTUAL_ARMOR'  => HelperString::numberFloorAndFormat(mrc_modify_value($user, false, array(MRC_ADMIRAL, TECH_ARMOR), $unit_info['armor'])),\n        'ACTUAL_WEAPON' => HelperString::numberFloorAndFormat(mrc_modify_value($user, false, array(MRC_ADMIRAL, TECH_WEAPON), $unit_info['attack'])),\n      );\n\n      if($unit_info[P_UNIT_TYPE] == UNIT_SHIPS) {\n        $ship_data = get_ship_data($unit_id, $user);\n\n        $level_production_base += array(\n          'ACTUAL_SPEED'       => HelperString::numberFloorAndFormat($ship_data['speed']),\n          'ACTUAL_CONSUMPTION' => HelperString::numberFloorAndFormat($ship_data['consumption']),\n          'ACTUAL_CAPACITY'    => HelperString::numberFloorAndFormat($ship_data['capacity']),\n        );\n      }\n\n      if($unit_info[P_UNIT_PRODUCTION]) {\n        foreach($unit_info[P_UNIT_PRODUCTION] as $resource_id => $resource_calc) {\n          if($resource_income =\n            floor(mrc_modify_value($user, $planet, $sn_modifiers_resource, $resource_calc(1, 10, $user, $planet)\n              * ($resource_id == RES_ENERGY ? $config_resource_multiplier_plain : $config_resource_multiplier)\n              * (isset($density_info[$resource_id]) ? $density_info[$resource_id] : 1)))\n          ) {\n            $level_production_base['R' . $resource_id] = $resource_income;\n          }\n        }\n      }\n      $production['.']['resource'][] = $level_production_base;\n    } elseif($unit_info[P_UNIT_PRODUCTION]) {\n      $level_production_base = array();\n      $element_level_start = $level_effective + $in_que[$unit_id];\n      foreach($unit_info[P_UNIT_PRODUCTION] as $resource_id => $resource_calc) {\n        if($resource_income =\n          floor(mrc_modify_value($user, $planet, $sn_modifiers_resource, $resource_calc($element_level_start, 10, $user, $planet)\n            * ($resource_id == RES_ENERGY ? $config_resource_multiplier_plain : $config_resource_multiplier)\n            * (isset($density_info[$resource_id]) ? $density_info[$resource_id] : 1)))\n        ) {\n          $level_production_base[$resource_id] = $resource_income;\n        }\n      }\n\n      $level_start = $level_base_and_que > 1 ? $level_effective + $level_in_que - 1 : 1;\n      for($i = 0; $i < 6; $i++) {\n        $level_production = array('LEVEL' => $level_start + $i);\n        foreach($unit_info[P_UNIT_PRODUCTION] as $resource_id => $resource_calc) {\n          if(\n          $resource_income = floor(mrc_modify_value($user, $planet, $sn_modifiers_resource, $resource_calc($level_start + $i, 10, $user, $planet)\n            * ($resource_id == RES_ENERGY ? $config_resource_multiplier_plain : $config_resource_multiplier)\n            * (isset($density_info[$resource_id]) ? $density_info[$resource_id] : 1)))\n          ) {\n            $level_production['R' . $resource_id] = $resource_income;\n            $level_production['D' . $resource_id] = $resource_income - $level_production_base[$resource_id];\n            if($level_production['D' . $resource_id] == 0) {\n              $level_production['D' . $resource_id] = '-';\n            }\n          }\n        }\n        $production['.']['resource'][] = $level_production;\n      }\n    } elseif($unit_id == TECH_ASTROTECH) {\n      $element_level_start = $level_effective + $in_que[$unit_id];\n      $level_production_base = array(\n        UNIT_PLAYER_EXPEDITIONS_MAX => get_player_max_expeditons($user, $element_level_start),\n        UNIT_PLAYER_COLONIES_MAX    => get_player_max_colonies($user, $element_level_start),\n      );\n\n      $level_start = $level_base_and_que > 1 ? $level_effective + $level_in_que - 1 : 1;\n      for($i = 0; $i < 6; $i++) {\n        $level_production = array('LEVEL' => $level_start + $i);\n        $level_production['R' . UNIT_PLAYER_EXPEDITIONS_MAX] = get_player_max_expeditons($user, $level_start + $i);\n        $level_production['D' . UNIT_PLAYER_EXPEDITIONS_MAX] = $level_production['R' . UNIT_PLAYER_EXPEDITIONS_MAX] - $level_production_base[UNIT_PLAYER_EXPEDITIONS_MAX];\n        $level_production['R' . UNIT_PLAYER_COLONIES_MAX] = get_player_max_colonies($user, $level_start + $i);\n        $level_production['D' . UNIT_PLAYER_COLONIES_MAX] = $level_production['R' . UNIT_PLAYER_COLONIES_MAX] - $level_production_base[UNIT_PLAYER_COLONIES_MAX];\n        $production['.']['resource'][] = $level_production;\n\n//        $level_production_base = array(\n//          UNIT_PLAYER_EXPEDITIONS_MAX => $level_production['R' . UNIT_PLAYER_EXPEDITIONS_MAX],\n//          UNIT_PLAYER_COLONIES_MAX    => $level_production['R' . UNIT_PLAYER_COLONIES_MAX],\n//        );\n      }\n    }\n\n    $production['.'][TPL_BLOCK_REQUIRE] = unit_requirements_render($user, $planet, $unit_id);\n    $production['.']['grants'] = unit_requirements_render($user, $planet, $unit_id, P_UNIT_GRANTS);\n\n    $template_result['.']['production'][] = $production;\n  }\n\n  foreach($lang['player_option_building_sort'] as $sort_id => $sort_text) {\n    $template->assign_block_vars('sort_values', array(\n      'VALUE' => $sort_id,\n      'TEXT'  => $sort_text,\n    ));\n  }\n\n  $sort_option = SN::$user_options[array(PLAYER_OPTION_BUILDING_SORT, $que_type)];\n  $sort_option_inverse = SN::$user_options[array(PLAYER_OPTION_BUILDING_SORT_INVERSE, $que_type)];\n  if($sort_option || $sort_option_inverse != PLAYER_OPTION_SORT_ORDER_PLAIN) {\n    switch($sort_option) {\n      case PLAYER_OPTION_SORT_NAME:\n        $sort_option_field = 'NAME';\n      break;\n      case PLAYER_OPTION_SORT_ID:\n        $sort_option_field = 'ID';\n      break;\n      case PLAYER_OPTION_SORT_CREATE_TIME_LENGTH:\n        $sort_option_field = 'TIME_SECONDS';\n      break;\n      default:\n        $sort_option_field = '__INDEX';\n      break;\n    }\n    $sort_option_inverse_closure = $sort_option_inverse ? -1 : 1;\n    usort($template_result['.']['production'], function ($a, $b) use ($sort_option_field, $sort_option_inverse_closure) {\n      return $a[$sort_option_field] < $b[$sort_option_field] ? -1 * $sort_option_inverse_closure : (\n      $a[$sort_option_field] > $b[$sort_option_field] ? 1 * $sort_option_inverse_closure : 0\n      );\n    });\n  }\n\n  $sector_cost = eco_get_build_data($user, $planet, UNIT_SECTOR, mrc_get_level($user, $planet, UNIT_SECTOR), true);\n  $sector_cost = $sector_cost[BUILD_CREATE][RES_DARK_MATTER];\n  $template_result += array(\n    'ALLY_ID' => $user['user_as_ally'],\n\n    'QUE_ID'          => $que_type,\n    'SHOW_SECTORS'    => $que_type == QUE_STRUCTURES,\n    'FLEET_OWN_COUNT' => $fleet_list['own']['count'],\n\n    'ARTIFACT_ID'    => $artifact_id,\n    'ARTIFACT_LEVEL' => mrc_get_level($user, array(), $artifact_id),\n    'ARTIFACT_NAME'  => $lang['tech'][$artifact_id],\n    'REQUEST_URI'    => urlencode($_SERVER['REQUEST_URI']),\n\n    'PAGE_HEADER' => $page_header,\n\n    'PLN_ID' => $planet['id'],\n\n    'METAL'       => $planet['metal'],\n    'CRYSTAL'     => $planet['crystal'],\n    'DEUTERIUM'   => $planet['deuterium'],\n    'DARK_MATTER' => $user_dark_matter,\n\n    'METAL_INCOMING'     => $fleet_list['own']['total'][RES_METAL],\n    'CRYSTAL_INCOMING'   => $fleet_list['own']['total'][RES_CRYSTAL],\n    'DEUTERIUM_INCOMING' => $fleet_list['own']['total'][RES_DEUTERIUM],\n\n    'FIELDS_CURRENT' => $planet_fields_current,\n    'FIELDS_MAX'     => $planet_fields_max,\n    'FIELDS_FREE'    => $planet_fields_free,\n    'FIELDS_QUE'     => $planet_fields_que == 0 ? '' : ($planet_fields_que > 0 ? \"+{$planet_fields_que}\" : $planet_fields_que),\n\n    'QUE_HAS_PLACE'  => $can_que_element,\n    'QUE_HAS_FIELDS' => $planet_fields_queable,\n\n    'PAGE_HINT'        => $lang['eco_bld_page_hint'],\n    'PLANET_TYPE'      => $planet['planet_type'],\n    'SECTOR_CAN_BUY'   => $sector_cost <= mrc_get_level($user, null, RES_DARK_MATTER),\n    'SECTOR_COST'      => $sector_cost,\n    'SECTOR_COST_TEXT' => HelperString::numberFloorAndFormat($sector_cost),\n\n    'STACKABLE' => $unit_stackable,\n\n    'TEMPORARY' => intval($config->empire_mercenary_temporary && $que_type == QUE_MERCENARY),\n\n    'STRING_CREATE'     => $que_type == QUE_MERCENARY ? $lang['bld_hire'] : ($que_type == QUE_RESEARCH ? $lang['bld_research'] : $lang['bld_create']),\n    'STRING_BUILD_TIME' => $que_type == QUE_RESEARCH ? $lang['ResearchTime'] : $lang['ConstructionTime'],\n\n    'U_opt_int_struc_vertical' => $user['option_list'][OPT_INTERFACE]['opt_int_struc_vertical'],\n\n    'MARKET_AUTOCONVERT_COST'              => market_get_autoconvert_cost(),\n    'MARKET_AUTOCONVERT_COST_TEXT'         => HelperString::numberFloorAndFormat(market_get_autoconvert_cost()),\n    'CAN_AUTOCONVERT'                      => $user_dark_matter >= market_get_autoconvert_cost(),\n    'BUILD_AUTOCONVERT_AVAILABLE'          => BUILD_AUTOCONVERT_AVAILABLE,\n    'PLAYER_OPTION_BUILD_AUTOCONVERT_HIDE' => SN::$user_options[PLAYER_OPTION_BUILD_AUTOCONVERT_HIDE],\n\n    'SORT_OPTION'         => $sort_option,\n    'SORT_OPTION_INVERSE' => $sort_option_inverse,\n\n    'QUE_RESEARCH' => QUE_RESEARCH,\n  );\n\n  $template->assign_recursive($template_result);\n\n  SnTemplate::display($template);\n}\n"
  },
  {
    "path": "includes/includes/flt_functions.php",
    "content": "<?php\n\nuse DBAL\\db_mysql;\nuse DBAL\\OldDbChangeSet;\nuse Fleet\\DbFleetStatic;\nuse Fleet\\Fleet;\nuse Fleet\\FleetStatic;\nuse Planet\\DBStaticPlanet;\n\n/**\n * @param int   $ship_id\n * @param int   $speed_percent\n * @param array $shipsData\n *\n * @return float|int\n */\nfunction flt_get_max_distance($ship_id, $speed_percent = 100, $shipsData = []) {\n  $single_ship_data = $shipsData[$ship_id];\n\n  if (!$single_ship_data['capacity'] || !$single_ship_data['consumption']) {\n    return 0;\n  }\n\n  return calcDistance($speed_percent, $single_ship_data);\n}\n\n/**\n * @param $speed_percent\n * @param $single_ship_data\n *\n * @return float\n */\nfunction calcDistance($speed_percent, $single_ship_data) {\n  return floor(($single_ship_data['capacity'] - 1) / $single_ship_data['consumption'] / pow($speed_percent / 100 + 1, 2) * 35000);\n}\n\n\n/**\n * @param         $user_row\n * @param         $from\n * @param         $to\n * @param         $fleet_array\n * @param int     $speed_percent\n * @param array[] $shipsData - prepared ships data to use in calculations\n *\n * @return array\n */\nfunction flt_travel_data($user_row, $from, $to, $fleet_array, $speed_percent = 10, $shipsData = [], $distance = null) {\n  $distance = $distance === null ? Universe::distance($from, $to) : $distance;\n\n  $consumption = 0;\n  $capacity    = 0;\n  $duration    = 0;\n\n  $game_fleet_speed = Universe::flt_server_flight_speed_multiplier();\n  $fleet_speed      = FleetStatic::flt_fleet_speed($user_row, $fleet_array, $shipsData);\n  if (!empty($fleet_array) && $fleet_speed && $game_fleet_speed) {\n    $speed_percent = $speed_percent ? max(min($speed_percent, 10), 1) : 10;\n    $real_speed    = $speed_percent * sqrt($fleet_speed);\n\n    $duration = max(1, round(\n      (35000 / $speed_percent * sqrt($distance * 10 / $fleet_speed) + 10) / $game_fleet_speed\n    ));\n\n    foreach ($fleet_array as $ship_id => $ship_count) {\n      if (!$ship_id || !$ship_count) {\n        continue;\n      }\n\n      $single_ship_data          = !empty($shipsData[$ship_id]) ? $shipsData[$ship_id] : get_ship_data($ship_id, $user_row);\n      $single_ship_data['speed'] = $single_ship_data['speed'] < 1 ? 1 : $single_ship_data['speed'];\n\n      $consumption += $single_ship_data['consumption'] * $ship_count * pow($real_speed / sqrt($single_ship_data['speed']) / 10 + 1, 2);\n      $capacity    += $single_ship_data['capacity'] * $ship_count;\n    }\n\n    $consumption = ceil($distance * $consumption / 35000) + 1;\n  }\n\n  return array(\n    'fleet_speed'            => $fleet_speed,\n    'distance'               => $distance,\n    'duration'               => $duration,\n    'consumption'            => $consumption,\n    'capacity'               => $capacity,\n    'hold'                   => $capacity - $consumption,\n    'transport_effectivness' => $consumption ? $capacity / $consumption : 0,\n  );\n}\n\nfunction flt_bashing_check($user, $enemy, $planet_dst, $mission, $flight_duration, $fleet_group = 0) {\n  global $config;\n\n  $config_bashing_attacks  = $config->fleet_bashing_attacks;\n  $config_bashing_interval = $config->fleet_bashing_interval;\n  if (!$config_bashing_attacks) {\n    // Bashing allowed - protection disabled\n    return ATTACK_ALLOWED;\n  }\n\n  $bashing_result = ATTACK_BASHING;\n  if ($user['ally_id'] && $enemy['ally_id']) {\n    $relations = ali_relations($user['ally_id'], $enemy['ally_id']);\n    if (!empty($relations)) {\n      $relations = $relations[$enemy['ally_id']];\n      switch ($relations['alliance_diplomacy_relation']) {\n        case ALLY_DIPLOMACY_WAR:\n          if (SN_TIME_NOW - $relations['alliance_diplomacy_time'] <= $config->fleet_bashing_war_delay) {\n            $bashing_result = ATTACK_BASHING_WAR_DELAY;\n          } else {\n            return ATTACK_ALLOWED;\n          }\n        break;\n        // Here goes other relations\n\n        /*\n                default:\n                  return ATTACK_ALLOWED;\n                break;\n        */\n      }\n    }\n  }\n\n  $time_limit   = SN_TIME_NOW + $flight_duration - $config->fleet_bashing_scope;\n  $bashing_list = array(SN_TIME_NOW);\n\n  // Retrieving flying fleets\n  $bashing_fleet_list = DbFleetStatic::fleet_list_bashing($user['id'], $planet_dst);\n  foreach ($bashing_fleet_list as $fleet_row) {\n    // Checking for ACS - each ACS count only once\n    if ($fleet_row['fleet_group']) {\n      $bashing_list[\"{$user['id']}_{$fleet_row['fleet_group']}\"] = $fleet_row['fleet_start_time'];\n    } else {\n      $bashing_list[] = $fleet_row['fleet_start_time'];\n    }\n  }\n\n  // Check for joining to ACS - if there are already fleets in ACS no checks should be done\n  if ($mission == MT_AKS && $bashing_list[\"{$user['id']}_{$fleet_group}\"]) {\n    return ATTACK_ALLOWED;\n  }\n\n  $query = doquery(\"SELECT bashing_time FROM `{{bashing}}` WHERE bashing_user_id = {$user['id']} AND bashing_planet_id = {$planet_dst['id']} AND bashing_time >= {$time_limit};\");\n  while ($bashing_row = db_fetch($query)) {\n    $bashing_list[] = $bashing_row['bashing_time'];\n  }\n\n  sort($bashing_list);\n\n  $last_attack = 0;\n  $wave        = 0;\n  $attack      = 1;\n  foreach ($bashing_list as &$bash_time) {\n    $attack++;\n    if ($bash_time - $last_attack > $config_bashing_interval || $attack > $config_bashing_attacks) {\n      $attack = 1;\n      $wave++;\n    }\n\n    $last_attack = $bash_time;\n  }\n\n  return ($wave > $config->fleet_bashing_waves ? $bashing_result : ATTACK_ALLOWED);\n}\n\n/**\n * @param array $planet_src - source planet record/vector\n * @param array $planet_dst - destination planet record/vector\n * @param array $fleet      - array of ship amount [(int)shipId => (float)shipAmount]\n * @param int   $mission    - Mission ID\n * @param array $options    - [\n *                          P_FLEET_ATTACK_RESOURCES_SUM       => (float),\n *                          P_FLEET_ATTACK_RES_LIST            => [(int)resId => (float)amount]\n *                          P_FLEET_ATTACK_SPEED_PERCENT_TENTH => (int)1..10\n *                          P_FLEET_ATTACK_FLEET_GROUP         => (int|string)\n *                          P_FLEET_ATTACK_FLYING_COUNT        => (int)\n *                          P_FLEET_ATTACK_TARGET_STRUCTURE    => (int) - targeted defense structure snID for MISSILE missions\n *                          P_FLEET_ATTACK_STAY_TIME           => (int) - stay HOURS\n *                          ]\n *\n * @return int\n */\nfunction flt_can_attack($planet_src, $planet_dst, $fleet = [], $mission, $options = []) {\n  $result = null;\n\n  return sn_function_call('flt_can_attack', [$planet_src, $planet_dst, $fleet, $mission, $options, &$result]);\n}\n\n/**\n * @param array $planet_src\n * @param array $planet_dst\n * @param array $fleet\n * @param int   $mission\n * @param array $options\n * @param int   $result\n *\n * @return int\n * @see flt_can_attack()\n */\nfunction sn_flt_can_attack($planet_src, $planet_dst, $fleet = [], $mission, $options = [], &$result) {\n  //TODO: try..catch\n  global $config, $user;\n\n  !is_array($options) ? $options = [] : false;\n\n  if ($user['vacation']) {\n    return $result = ATTACK_OWN_VACATION;\n  }\n\n  $sn_groups_mission = sn_get_groups('missions');\n  if (!isset($sn_groups_mission[$mission])) {\n    return $result = ATTACK_MISSION_ABSENT;\n  }\n  $sn_data_mission = $sn_groups_mission[$mission];\n\n//TODO: Проверка на отстуствие ресурсов в нетранспортных миссиях (Транспорт, Передислокация, Колонизация)\n\n  //TODO: Проверка на наличие ресурсов при Транспорте\n  // TODO: Проверка на отрицательные ресурсы при транспорте\n  // TODO: Проверка на перегрузку при транспорте\n\n  // TODO: В ракетных миссиях могут лететь только ракеты\n  // TODO: В неракетных миссиях ракеты должны отсутствовать\n\n  if (empty($fleet) || !is_array($fleet)) {\n    return $result = ATTACK_NO_FLEET;\n  }\n\n  $ships        = 0;\n  $recyclers    = 0;\n  $spies        = 0;\n  $resources    = 0;\n  $ship_ids     = sn_get_groups('fleet');\n  $resource_ids = sn_get_groups('resources_loot');\n  foreach ($fleet as $ship_id => $ship_count) {\n    $is_ship     = in_array($ship_id, $ship_ids);\n    $is_resource = in_array($ship_id, $resource_ids);\n//    if (!$is_ship && !$is_resource) {\n//      // TODO Спецобработчик для Капитана и модулей\n//      return ATTACK_WRONG_UNIT;\n//    }\n\n    if ($ship_count < 0) {\n      return $result = $is_ship ? ATTACK_SHIP_COUNT_WRONG : ATTACK_RESOURCE_COUNT_WRONG;\n    }\n\n    if ($ship_count > mrc_get_level($user, $planet_src, $ship_id)) {\n      // TODO ATTACK_NO_MISSILE\n      return $result = $is_ship ? ATTACK_NO_SHIPS : ATTACK_NO_RESOURCES;\n    }\n\n    if ($is_ship) {\n      $single_ship_data = get_ship_data($ship_id, $user);\n      if ($single_ship_data[P_SPEED] <= 0) {\n        return $result = ATTACK_ZERO_SPEED;\n      }\n      $ships     += $ship_count;\n      $recyclers += in_array($ship_id, sn_get_groups('flt_recyclers')) ? $ship_count : 0;\n      $spies     += $ship_id == SHIP_SPY ? $ship_count : 0;\n    } elseif ($is_resource) {\n      $resources += $ship_count;\n    }\n  }\n\n  if (empty($resources) && !empty($options[P_FLEET_ATTACK_RES_LIST]) && is_array($options[P_FLEET_ATTACK_RES_LIST])) {\n    $resources = array_sum($options[P_FLEET_ATTACK_RES_LIST]);\n  }\n\n  if (\n    isset($options[P_FLEET_ATTACK_RESOURCES_SUM])\n    && $options[P_FLEET_ATTACK_RESOURCES_SUM] > 0\n    && empty($sn_data_mission['transport'])\n  ) {\n    return $result = ATTACK_RESOURCE_FORBIDDEN;\n  }\n\n  /*\n    elseif($mission == MT_TRANSPORT)\n    {\n      return ATTACK_TRANSPORT_EMPTY;\n    }\n  */\n\n  $speed = $options[P_FLEET_ATTACK_SPEED_PERCENT_TENTH];\n  if ($speed && ($speed != intval($speed) || $speed < 1 || $speed > 10)) {\n    return $result = ATTACK_WRONG_SPEED;\n  }\n\n  $travel_data = flt_travel_data($user, $planet_src, $planet_dst, $fleet, $options[P_FLEET_ATTACK_SPEED_PERCENT_TENTH]);\n\n\n  if (mrc_get_level($user, $planet_src, RES_DEUTERIUM) < $fleet[RES_DEUTERIUM] + $travel_data['consumption']) {\n    return $result = ATTACK_NO_FUEL;\n  }\n\n  if ($travel_data['consumption'] > $travel_data['capacity']) {\n    return $result = ATTACK_TOO_FAR;\n  }\n\n  if ($travel_data['hold'] < $resources) {\n    return $result = ATTACK_OVERLOADED;\n  }\n\n  $fleet_start_time = SN_TIME_NOW + $travel_data['duration'];\n\n  $fleet_group = $options[P_FLEET_ATTACK_FLEET_GROUP];\n  if ($fleet_group) {\n    if ($mission != MT_AKS) {\n      return $result = ATTACK_WRONG_MISSION;\n    };\n\n    $acs = DbFleetStatic::dbAcsGetById($fleet_group);\n    if (!$acs['id']) {\n      return $result = ATTACK_NO_ACS;\n    }\n\n    if ($planet_dst['galaxy'] != $acs['galaxy'] || $planet_dst['system'] != $acs['system'] || $planet_dst['planet'] != $acs['planet'] || $planet_dst['planet_type'] != $acs['planet_type']) {\n      return $result = ATTACK_ACS_WRONG_TARGET;\n    }\n\n    if ($fleet_start_time > $acs['ankunft']) {\n      return $result = ATTACK_ACS_TOO_LATE;\n    }\n\n    if (DbFleetStatic::acsIsAcsFull($acs['id'])) {\n      return $result = ATTACK_ACS_MAX_FLEETS;\n    }\n  }\n\n  $flying_fleets = $options[P_FLEET_ATTACK_FLYING_COUNT];\n  if (!$flying_fleets) {\n    $flying_fleets = DbFleetStatic::fleet_count_flying($user['id']);\n  }\n  if (GetMaxFleets($user) <= $flying_fleets && $mission != MT_MISSILE) {\n    return $result = ATTACK_NO_SLOTS;\n  }\n\n  // В одиночку шпионские зонды могут летать только в миссии Шпионаж, Передислокация и Транспорт\n  if ($ships && $spies && $spies == $ships && !($mission == MT_SPY || $mission == MT_RELOCATE || $mission == MT_TRANSPORT)) {\n    return $result = ATTACK_SPIES_LONLY;\n  }\n\n  // Checking for no planet\n  if (!$planet_dst['id_owner']) {\n    if ($mission == MT_COLONIZE && !$fleet[SHIP_COLONIZER]) {\n      return $result = ATTACK_NO_COLONIZER;\n    }\n\n    if ($mission == MT_EXPLORE || $mission == MT_COLONIZE) {\n      return $result = ATTACK_ALLOWED;\n    }\n\n    return $result = ATTACK_NO_TARGET;\n  }\n\n  if ($mission == MT_RECYCLE) {\n    if ($planet_dst['debris_metal'] + $planet_dst['debris_crystal'] <= 0) {\n      return $result = ATTACK_NO_DEBRIS;\n    }\n    if ($recyclers <= 0) {\n      return $result = ATTACK_NO_RECYCLERS;\n    }\n\n    return $result = ATTACK_ALLOWED;\n  }\n\n  // Got planet. Checking if it is ours\n  if ($planet_dst['id_owner'] == $user['id']) {\n    if ($mission == MT_TRANSPORT || $mission == MT_RELOCATE) {\n      return $result = ATTACK_ALLOWED;\n    }\n\n    return $planet_src['id'] == $planet_dst['id'] ? ATTACK_SAME : ATTACK_OWN;\n  }\n\n  // No, planet not ours. Cutting mission that can't be send to not-ours planet\n  if ($mission == MT_RELOCATE || $mission == MT_COLONIZE || $mission == MT_EXPLORE) {\n    return $result = ATTACK_WRONG_MISSION;\n  }\n\n  $enemy = db_user_by_id($planet_dst['id_owner']);\n  // We cannot attack or send resource to users in VACATION mode\n  if ($enemy['vacation'] && $mission != MT_RECYCLE) {\n    return $result = ATTACK_VACATION;\n  }\n\n  // Multi IP protection\n  // TODO: Here we need a procedure to check proxies\n  if (sys_is_multiaccount($user, $enemy)) {\n    return $result = ATTACK_SAME_IP;\n  }\n\n  $user_points  = $user['total_points'];\n  $enemy_points = $enemy['total_points'];\n\n  // Is it transport? If yes - checking for buffing to prevent mega-alliance destroyer\n  if ($mission == MT_TRANSPORT) {\n    if ($user_points >= $enemy_points || $config->allow_buffing) {\n      return $result = ATTACK_ALLOWED;\n    } else {\n      return $result = ATTACK_BUFFING;\n    }\n  }\n\n  // Only aggresive missions passed to this point. HOLD counts as passive but aggresive\n\n  // Is it admin with planet protection?\n  if ($planet_dst['id_level'] > $user['authlevel']) {\n    return $result = ATTACK_ADMIN;\n  }\n\n  // Okay. Now skipping protection checks for inactive longer then 1 week\n  if (!$enemy['onlinetime'] || $enemy['onlinetime'] >= (SN_TIME_NOW - 60 * 60 * 24 * 7)) {\n    if (\n      (SN::$gc->general->playerIsNoobByPoints($enemy_points) && !SN::$gc->general->playerIsNoobByPoints($user_points))\n      ||\n      (SN::$gc->general->playerIs1stStrongerThen2nd($user_points, $enemy_points))\n    ) {\n      if ($mission != MT_HOLD) {\n        return $result = ATTACK_NOOB;\n      }\n      if ($mission == MT_HOLD && !($user['ally_id'] && $user['ally_id'] == $enemy['ally_id'] && $config->ally_help_weak)) {\n        return $result = ATTACK_NOOB;\n      }\n    }\n  }\n\n  // Is it HOLD mission? If yes - there should be ally deposit\n  if ($mission == MT_HOLD) {\n    if (mrc_get_level($user, $planet_dst, STRUC_ALLY_DEPOSIT)) {\n      return $result = ATTACK_ALLOWED;\n    }\n\n    return $result = ATTACK_NO_ALLY_DEPOSIT;\n  }\n\n  if ($mission == MT_SPY) {\n    return $result = $spies >= 1 ? ATTACK_ALLOWED : ATTACK_NO_SPIES;\n  }\n\n  // Is it MISSILE mission?\n  if ($mission == MT_MISSILE) {\n    $sn_data_mip = get_unit_param(UNIT_DEF_MISSILE_INTERPLANET);\n    if (mrc_get_level($user, $planet_src, STRUC_SILO) < $sn_data_mip[P_REQUIRE][STRUC_SILO]) {\n      return $result = ATTACK_NO_SILO;\n    }\n\n    if (!$fleet[UNIT_DEF_MISSILE_INTERPLANET]) {\n      return $result = ATTACK_NO_MISSILE;\n    }\n\n    $distance  = abs($planet_dst['system'] - $planet_src['system']);\n    $mip_range = flt_get_missile_range($user);\n    if ($distance > $mip_range || $planet_dst['galaxy'] != $planet_src['galaxy']) {\n      return $result = ATTACK_MISSILE_TOO_FAR;\n    }\n\n    if (!empty($options[P_FLEET_ATTACK_TARGET_STRUCTURE]) && !in_array($options[P_FLEET_ATTACK_TARGET_STRUCTURE], sn_get_groups('defense_active'))) {\n      return $result = ATTACK_WRONG_STRUCTURE;\n    }\n  }\n\n  if ($mission == MT_DESTROY && $planet_dst['planet_type'] != PT_MOON) {\n    return $result = ATTACK_WRONG_MISSION;\n  }\n\n  if ($mission == MT_ATTACK || $mission == MT_AKS || $mission == MT_DESTROY) {\n    return $result = flt_bashing_check($user, $enemy, $planet_dst, $mission, $travel_data['duration'], $fleet_group);\n  }\n\n  return $result = ATTACK_ALLOWED;\n}\n\n/**\n * @param array $user    - actual user record\n * @param array $from    - actual planet record\n * @param array $to      - actual planet record\n * @param array $fleet   - array of records $unit_id -> $amount\n * @param int   $mission - fleet mission\n * @param array $options\n *\n * @return int\n * @throws Exception\n * @see flt_can_attack()\n */\nfunction flt_t_send_fleet($user, &$from, $to, $fleet, $resources, $mission, $options = array()) {\n  $internal_transaction = !db_mysql::db_transaction_check(false) ? db_mysql::db_transaction_start() : false;\n\n  // TODO Потенциальный дедлок - если успела залочится запись пользователя - хозяина планеты\n  $user = db_user_by_id($user['id'], true);\n  $from = sys_o_get_updated($user['id'], $from['id'], SN_TIME_NOW);\n  $from = $from['planet'];\n\n//  $fleet = [\n//    202 => 1,\n//  ];\n//  $resources = [\n//    901 => 1,\n//  ];\n//  var_dump($fleet);\n//  var_dump($resources);\n//  var_dump(mrc_get_level($user, $from, 202));\n//  var_dump($from['metal']);\n//  var_dump($from['deuterium']);\n//  die();\n\n\n  !is_array($resources) ? $resources = [] : false;\n  if (empty($options[P_FLEET_ATTACK_RES_LIST])) {\n    $options[P_FLEET_ATTACK_RES_LIST] = $resources;\n  }\n  $can_attack = flt_can_attack($from, $to, $fleet, $mission, $options);\n  if ($can_attack != ATTACK_ALLOWED) {\n    $internal_transaction ? db_mysql::db_transaction_rollback() : false;\n\n    return $can_attack;\n  }\n\n  empty($options[P_FLEET_ATTACK_SPEED_PERCENT_TENTH]) ? $options[P_FLEET_ATTACK_SPEED_PERCENT_TENTH] = 10 : false;\n  $options[P_FLEET_ATTACK_STAY_TIME] = !empty($options[P_FLEET_ATTACK_STAY_TIME]) ? $options[P_FLEET_ATTACK_STAY_TIME] * PERIOD_HOUR : 0;\n\n  $fleetObj    = new Fleet();\n  $travel_data = $fleetObj\n    ->setMission($mission)\n    ->setSourceFromPlanetRecord($from)\n    ->setDestinationFromPlanetRecord($to)\n    ->setUnits($fleet)\n    ->setUnits($resources)\n    ->setSpeedPercentInTenth($options[P_FLEET_ATTACK_SPEED_PERCENT_TENTH])\n    ->calcTravelTimes(SN_TIME_NOW, $options[P_FLEET_ATTACK_STAY_TIME]);\n  $fleetObj->save();\n\n  $result = fltSendFleetAdjustPlanetResources($from['id'], $resources, $travel_data['consumption']);\n\n  $result = fltSendFleetAdjustPlanetUnits($user, $from['id'], $fleet);\n\n  $internal_transaction ? db_mysql::db_transaction_commit() : false;\n\n  $from = DBStaticPlanet::db_planet_by_id($from['id']);\n\n//  var_dump(mrc_get_level($user, $from, 202));\n//  var_dump($from['metal']);\n//  var_dump($from['deuterium']);\n//  die();\n\n  return ATTACK_ALLOWED;\n}\n\n/**\n * @param array      $user\n * @param int|string $fromId\n * @param float[]    $fleet - [(int)shipId => (float)count]\n *\n * @return bool\n */\nfunction fltSendFleetAdjustPlanetUnits($user, $fromId, $fleet) {\n  $result = [];\n\n  foreach ($fleet as $unit_id => $amount) {\n    if (floatval($amount) >= 1 && intval($unit_id) && in_array($unit_id, sn_get_groups('fleet'))) {\n      $result[] = OldDbChangeSet::db_changeset_prepare_unit($unit_id, -$amount, $user, $fromId);\n    }\n  }\n\n  return OldDbChangeSet::db_changeset_apply(['unit' => $result]);\n}\n\n/**\n * @param int|string $fromId      - Source planet ID\n * @param array      $resources   - Array of resources to transfer [(int)resourceId => (float)amount]\n * @param int|float  $consumption - Fleet consumption\n *\n * @return bool\n *\n * @throws Exception\n */\nfunction fltSendFleetAdjustPlanetResources($fromId, $resources, $consumption) {\n  $planetObj = SN::$gc->repoV2->getPlanet($fromId);\n\n  $planetObj->changeResource(RES_DEUTERIUM, -$consumption);\n\n  foreach ($resources as $resource_id => $amount) {\n    if (floatval($amount) >= 1 && intval($resource_id) && in_array($resource_id, sn_get_groups('resources_loot'))) {\n      $planetObj->changeResource($resource_id, -$amount);\n    }\n  }\n\n  return $planetObj->save();\n}\n\nfunction flt_calculate_ship_to_transport_sort($a, $b) {\n  return $a['transport_effectivness'] == $b['transport_effectivness'] ? 0 : ($a['transport_effectivness'] > $b['transport_effectivness'] ? -1 : 1);\n}\n\n// flt_calculate_ship_to_transport - calculates how many ships need to transport pointed amount of resources\n// $ship_list - list of available ships\n// $resource_amount - how much amount of resources need to be transported\n// $from - transport from\n// $to - transport to\nfunction flt_calculate_fleet_to_transport($ship_list, $resource_amount, $from, $to) {\n  global $user;\n\n  $ship_data   = array();\n  $fleet_array = array();\n  foreach ($ship_list as $transport_id => $cork) {\n    $ship_data[$transport_id] = flt_travel_data($user, $from, $to, array($transport_id => 1), 10);\n  }\n  uasort($ship_data, 'flt_calculate_ship_to_transport_sort');\n\n  $fleet_hold     = 0;\n  $fleet_capacity = 0;\n  $fuel_total     = $fuel_left = mrc_get_level($user, $from, RES_DEUTERIUM);\n  foreach ($ship_data as $transport_id => &$ship_info) {\n    $ship_loaded = min($ship_list[$transport_id], ceil($resource_amount / $ship_info['hold']), floor($fuel_left / $ship_info['consumption']));\n    if ($ship_loaded) {\n      $fleet_array[$transport_id] = $ship_loaded;\n      $resource_amount            -= min($resource_amount, $ship_info['hold'] * $ship_loaded);\n      $fuel_left                  -= $ship_info['consumption'] * $ship_loaded;\n\n      $fleet_capacity += $ship_info['capacity'] * $ship_loaded;\n    }\n  }\n\n  return array('fleet' => $fleet_array, 'ship_data' => $ship_data, 'capacity' => $fleet_capacity, 'consumption' => $fuel_total - $fuel_left);\n}\n"
  },
  {
    "path": "includes/includes/flt_mission_attack.php",
    "content": "<?php\n\nuse Fleet\\DbFleetStatic;\nuse Fleet\\FleetDispatcher;\nuse Fleet\\FleetDispatchEvent;\nuse Ube\\Ube4_1\\Ube4_1Calc;\nuse Ube\\Ube4_1\\Ube4_1Prepare;\n\n/** @noinspection PhpIncludeInspection */\nrequire_once(SN_ROOT_PHYSICAL . 'includes/includes/ube_attack_calculate.php');\n\n/*\n  copyright © 2009-2014 Gorlum for http://supernova.ws\n*/\n\n/**\n * @param $fleet_row\n * @param $combat_data\n *\n * @return mixed\n *\n * @see sn_flt_planet_capture()\n */\nfunction flt_planet_capture(&$fleet_row, &$combat_data) {\n  $result = null;\n\n  return sn_function_call('flt_planet_capture', array(&$fleet_row, &$combat_data, &$result));\n}\n\n/** @noinspection PhpUnusedParameterInspection */\nfunction sn_flt_planet_capture(&$fleet_row, &$combat_data, $result) {\n  return $result;\n}\n\n/**\n * @param FleetDispatchEvent $fleetEvent\n *\n * @return array|null\n * @noinspection PhpUnusedParameterInspection\n */\nfunction flt_mission_attack($fleetEvent) {\n  $fleet_row = $fleetEvent->fleet;\n  if (\n    // Nothing to do if fleet row is empty\n    empty($fleet_row)\n    // Also nothing to do if event is not fleet arrival to destination - i.e. actual attack\n    || $fleetEvent->event !== EVENT_FLT_ARRIVE\n  ) {\n    return null;\n  }\n\n  if (\n    // Нет данных о планете назначения\n    empty($fleetEvent->dstPlanetId)\n    // \"Уничтожение\" не на луну\n    || ($fleetEvent->missionId == MT_DESTROY && $fleetEvent->dstPlanetRow['planet_type'] != PT_MOON)\n  ) {\n    DbFleetStatic::fleet_send_back($fleet_row);\n\n    return null;\n  }\n\n  $acs_fleet_list = empty($fleet_row['fleet_group']) ? [$fleet_row] : DbFleetStatic::fleet_list_by_group($fleet_row['fleet_group']);\n\n  $fleet_list_on_hold = DbFleetStatic::fleet_list_on_hold(\n    $fleet_row['fleet_end_galaxy'],\n    $fleet_row['fleet_end_system'],\n    $fleet_row['fleet_end_planet'],\n    $fleet_row['fleet_end_type'],\n    $fleetEvent->eventTimeStamp\n  );\n\n  $ubePrepare  = new Ube4_1Prepare();\n  $combat_data = $ubePrepare->prepareFromMissionArray($fleetEvent, $fleet_list_on_hold, $acs_fleet_list);\n\n  $ubeCalc = new Ube4_1Calc();\n  $ubeCalc->sn_ube_combat($combat_data);\n\n  flt_planet_capture($fleet_row, $combat_data);\n\n  sn_ube_report_save($combat_data);\n\n  ube_combat_result_apply($combat_data);\n\n  sn_ube_message_send($combat_data);\n\n  return $combat_data;\n}\n"
  },
  {
    "path": "includes/includes/flt_mission_colonize.php",
    "content": "<?php /** @noinspection PhpUnnecessaryCurlyVarSyntaxInspection */\n\nuse Fleet\\DbFleetStatic;\nuse Fleet\\FleetDispatchEvent;\n\n/**\n * @param FleetDispatchEvent $fleetEvent\n *\n * @return int|mixed\n */\nfunction flt_mission_colonize($fleetEvent) {\n  $fleet_row    = $fleetEvent->fleet;\n  /** @noinspection PhpDeprecationInspection */\n  $fleetOwnerRow = db_user_by_id($fleetEvent->fleet['fleet_owner'], true);\n\n  global $lang;\n\n  $targetAddress = sprintf($lang['sys_address_planet'], $fleet_row['fleet_end_galaxy'], $fleet_row['fleet_end_system'], $fleet_row['fleet_end_planet']);\n\n  $fleet_array = sys_unit_str2arr($fleet_row['fleet_array']);\n\n  $TheMessage = $lang['sys_colo_no_colonizer'];\n  if ($fleet_array[SHIP_COLONIZER] >= 1) {\n    $TheMessage = $lang['sys_colo_not_free'];\n    if (empty($fleetEvent->dstPlanetRow)) {\n      $iPlanetCount = get_player_current_colonies($fleetOwnerRow);\n\n      // Can we colonize more planets?\n      $TheMessage = $lang['sys_colo_max_colo'];\n      if ($iPlanetCount < get_player_max_colonies($fleetOwnerRow)) {\n        // Yes, we can colonize\n        $TheMessage     = $lang['sys_colo_bad_pos'];\n        $NewOwnerPlanet = uni_create_planet(\n          $fleet_row['fleet_end_galaxy'], $fleet_row['fleet_end_system'], $fleet_row['fleet_end_planet'],\n          $fleet_row['fleet_owner'], \"{$lang['sys_colo_default_name']} {$iPlanetCount}\", false,\n          array('user_row' => $fleetOwnerRow));\n        if ($NewOwnerPlanet) {\n          $TheMessage = $lang['sys_colo_arrival'] . $targetAddress . $lang['sys_colo_all_is_ok'];\n          msg_send_simple_message($fleet_row['fleet_owner'], '', $fleet_row['fleet_start_time'], MSG_TYPE_SPY, $lang['sys_colo_mess_from'], $lang['sys_colo_mess_report'], $TheMessage);\n\n          $fleet_array[SHIP_COLONIZER]--;\n          $fleet_row['fleet_amount']--;\n          $fleet_row['fleet_array'] = sys_unit_arr2str($fleet_array);\n\n          /** @noinspection PhpDeprecationInspection */\n          return RestoreFleetToPlanet($fleet_row, false);\n        }\n      }\n    }\n  }\n\n  DbFleetStatic::fleet_send_back($fleet_row);\n  msg_send_simple_message($fleet_row['fleet_owner'], '', $fleet_row['fleet_start_time'], MSG_TYPE_SPY, $lang['sys_colo_mess_from'], $lang['sys_colo_mess_report'], \"{$lang['sys_colo_arrival']}{$targetAddress}{$TheMessage}\");\n\n  return CACHE_FLEET;\n}\n"
  },
  {
    "path": "includes/includes/flt_mission_hold.php",
    "content": "<?php\n\nuse Fleet\\DbFleetStatic;\nuse Fleet\\FleetDispatchEvent;\n\n/**\n * @param FleetDispatchEvent $fleetEvent\n *\n * @return int\n */\nfunction flt_mission_hold($fleetEvent) {\n  if ($fleetEvent->fleet['fleet_end_stay'] < SN_TIME_NOW) {\n    DbFleetStatic::fleet_send_back($fleetEvent->fleet);\n\n    return CACHE_FLEET;\n  }\n\n  return CACHE_NOTHING;\n}\n"
  },
  {
    "path": "includes/includes/flt_mission_recycle.php",
    "content": "<?php\n\nuse Fleet\\DbFleetStatic;\nuse Fleet\\FleetDispatchEvent;\nuse Planet\\DBStaticPlanet;\n\n/**\n * @param FleetDispatchEvent $fleetEvent\n *\n * @return int\n */\nfunction flt_mission_recycle($fleetEvent) {\n  $fleet_row          = $fleetEvent->fleet;\n  $destination_planet = $fleetEvent->dstPlanetRow;\n\n  if (!$fleet_row) {\n    return CACHE_NOTHING;\n  }\n\n  if (empty($fleetEvent->dstPlanetId)) {\n    DbFleetStatic::fleet_send_back($fleetEvent->fleet);\n\n    return CACHE_FLEET;\n  }\n\n  global $lang;\n\n  $RecyclerCapacity   = 0;\n  $OtherFleetCapacity = 0;\n  $fleet_array        = sys_unit_str2arr($fleet_row['fleet_array']);\n  foreach ($fleet_array as $unit_id => $unit_count) {\n    if (in_array($unit_id, sn_get_groups('fleet'))) {\n      $capacity = get_unit_param($unit_id, P_CAPACITY) * $unit_count;\n      if (in_array($unit_id, sn_get_groups('flt_recyclers'))) {\n        $RecyclerCapacity += $capacity;\n      } else {\n        $OtherFleetCapacity += $capacity;\n      }\n    }\n  }\n\n  $IncomingFleetGoods = $fleet_row[\"fleet_resource_metal\"] + $fleet_row[\"fleet_resource_crystal\"] + $fleet_row[\"fleet_resource_deuterium\"];\n  if ($IncomingFleetGoods > $OtherFleetCapacity) {\n    $RecyclerCapacity -= ($IncomingFleetGoods - $OtherFleetCapacity);\n  }\n\n  if (($destination_planet[\"debris_metal\"] + $destination_planet[\"debris_crystal\"]) <= $RecyclerCapacity) {\n    $RecycledGoods[\"metal\"]   = $destination_planet[\"debris_metal\"];\n    $RecycledGoods[\"crystal\"] = $destination_planet[\"debris_crystal\"];\n  } else {\n    if (($destination_planet[\"debris_metal\"] > $RecyclerCapacity / 2) and\n      ($destination_planet[\"debris_crystal\"] > $RecyclerCapacity / 2)) {\n      $RecycledGoods[\"metal\"]   = $RecyclerCapacity / 2;\n      $RecycledGoods[\"crystal\"] = $RecyclerCapacity / 2;\n    } else {\n      if ($destination_planet[\"debris_metal\"] > $destination_planet[\"debris_crystal\"]) {\n        $RecycledGoods[\"crystal\"] = $destination_planet[\"debris_crystal\"];\n        if ($destination_planet[\"debris_metal\"] > ($RecyclerCapacity - $RecycledGoods[\"crystal\"])) {\n          $RecycledGoods[\"metal\"] = $RecyclerCapacity - $RecycledGoods[\"crystal\"];\n        } else {\n          $RecycledGoods[\"metal\"] = $destination_planet[\"debris_metal\"];\n        }\n      } else {\n        $RecycledGoods[\"metal\"] = $destination_planet[\"debris_metal\"];\n        if ($destination_planet[\"debris_crystal\"] > ($RecyclerCapacity - $RecycledGoods[\"metal\"])) {\n          $RecycledGoods[\"crystal\"] = $RecyclerCapacity - $RecycledGoods[\"metal\"];\n        } else {\n          $RecycledGoods[\"crystal\"] = $destination_planet[\"debris_crystal\"];\n        }\n      }\n    }\n  }\n  $NewCargo['Metal']     = $fleet_row[\"fleet_resource_metal\"] + $RecycledGoods[\"metal\"];\n  $NewCargo['Crystal']   = $fleet_row[\"fleet_resource_crystal\"] + $RecycledGoods[\"crystal\"];\n  $NewCargo['Deuterium'] = $fleet_row[\"fleet_resource_deuterium\"];\n\n  DBStaticPlanet::db_planet_set_by_gspt($fleet_row['fleet_end_galaxy'], $fleet_row['fleet_end_system'], $fleet_row['fleet_end_planet'], \"`debris_metal` = `debris_metal` - '{$RecycledGoods['metal']}', `debris_crystal` = `debris_crystal` - '{$RecycledGoods['crystal']}'\",\n    PT_PLANET\n  );\n\n  $Message = sprintf(\n    $lang['sys_recycler_gotten'],\n    HelperString::numberFloorAndFormat($RecycledGoods[\"metal\"]), $lang['Metal'],\n    HelperString::numberFloorAndFormat($RecycledGoods[\"crystal\"]), $lang['Crystal']\n  );\n  msg_send_simple_message($fleet_row['fleet_owner'], '', $fleet_row['fleet_start_time'], MSG_TYPE_RECYCLE, $lang['sys_mess_spy_control'], $lang['sys_recycler_report'], $Message);\n\n  $fleet_set = array(\n    'fleet_mess'               => 1,\n    'fleet_resource_metal'     => $NewCargo['Metal'],\n    'fleet_resource_crystal'   => $NewCargo['Crystal'],\n    'fleet_resource_deuterium' => $NewCargo['Deuterium'],\n  );\n  DbFleetStatic::fleet_update_set($fleet_row['fleet_id'], $fleet_set);\n\n  return CACHE_FLEET | CACHE_PLANET_DST;\n}\n"
  },
  {
    "path": "includes/includes/flt_mission_relocate.php",
    "content": "<?php\n\nuse Fleet\\DbFleetStatic;\nuse Fleet\\FleetDispatchEvent;\nuse Planet\\DBStaticPlanet;\n\n/**\n * @param FleetDispatchEvent $fleetEvent\n *\n * @return int|mixed\n */\nfunction flt_mission_relocate($fleetEvent) {\n  $fleet_row          = $fleetEvent->fleet;\n  $destination_planet = $fleetEvent->dstPlanetRow;\n  // We didn't have source planet info for this mission - so retrieving it now\n  $source_planet = $fleetEvent->updateSrcPlanetRow(DBStaticPlanet::db_planet_by_id($fleetEvent->srcPlanetId));\n\n  if (!$destination_planet || !is_array($destination_planet) || $fleetEvent->fleet['fleet_owner'] != $fleetEvent->dstPlanetOwnerId) {\n    DbFleetStatic::fleet_send_back($fleetEvent->fleet);\n\n    return CACHE_FLEET;\n  }\n\n  global $lang;\n\n  /** @noinspection PhpRedundantOptionalArgumentInspection */\n  $Message = sprintf($lang['sys_tran_mess_user'],\n      $source_planet['name'],\n      uni_render_coordinates_href($fleet_row, 'fleet_start_', 3, ''),\n      $destination_planet['name'],\n      uni_render_coordinates_href($fleet_row, 'fleet_end_', 3, ''),\n      $fleet_row['fleet_resource_metal'], $lang['Metal'],\n      $fleet_row['fleet_resource_crystal'], $lang['Crystal'],\n      $fleet_row['fleet_resource_deuterium'], $lang['Deuterium']) .\n    '<br />' . $lang['sys_relocate_mess_user'];\n\n  foreach (sys_unit_str2arr($fleet_row['fleet_array']) as $ship_id => $ship_count) {\n    $Message .= $lang['tech'][$ship_id] . ' - ' . $ship_count . '<br />';\n  }\n\n  msg_send_simple_message($fleet_row['fleet_owner'], '', $fleet_row['fleet_start_time'], MSG_TYPE_TRANSPORT, $lang['sys_mess_qg'], $lang['sys_stay_mess_stay'], $Message);\n\n  /** @noinspection PhpDeprecationInspection */\n  return RestoreFleetToPlanet($fleet_row, false);\n}\n"
  },
  {
    "path": "includes/includes/flt_mission_transport.php",
    "content": "<?php\n\nuse Fleet\\DbFleetStatic;\nuse Fleet\\FleetDispatchEvent;\nuse Planet\\DBStaticPlanet;\n\n/**\n * @param FleetDispatchEvent $fleetEvent\n *\n * @return int|mixed\n */\nfunction flt_mission_transport($fleetEvent) {\n  if (empty($fleetEvent->dstPlanetId)) {\n    // If there is no info on destination planet/moon by these coordinates - just returning fleet back\n    DbFleetStatic::fleet_send_back($fleetEvent->fleet);\n\n    return CACHE_FLEET;\n  }\n\n  $fleet_row          = $fleetEvent->fleet;\n  $destination_planet = $fleetEvent->dstPlanetRow;\n  // We didn't have source planet info for this mission - so retrieving it now\n  $source_planet = $fleetEvent->updateSrcPlanetRow(DBStaticPlanet::db_planet_by_id($fleetEvent->srcPlanetId));\n\n  global $lang;\n  /** @noinspection PhpRedundantOptionalArgumentInspection */\n  $Message = sprintf($lang['sys_tran_mess_user'],\n    $source_planet['name'], uni_render_coordinates_href($fleet_row, 'fleet_start_', 3, ''),\n    $destination_planet['name'], uni_render_coordinates_href($fleet_row, 'fleet_end_', 3, ''),\n    $fleet_row['fleet_resource_metal'], $lang['Metal'],\n    $fleet_row['fleet_resource_crystal'], $lang['Crystal'],\n    $fleet_row['fleet_resource_deuterium'], $lang['Deuterium']);\n\n  $targetOwnerId = $fleetEvent->dstPlanetOwnerId;\n\n  msg_send_simple_message($targetOwnerId, '', $fleet_row['fleet_start_time'], MSG_TYPE_TRANSPORT, $lang['sys_mess_tower'], $lang['sys_mess_transport'], $Message);\n\n  if ($targetOwnerId <> $fleet_row['fleet_owner']) {\n    msg_send_simple_message($fleet_row['fleet_owner'], '', $fleet_row['fleet_start_time'], MSG_TYPE_TRANSPORT, $lang['sys_mess_tower'], $lang['sys_mess_transport'], $Message);\n  }\n\n  /** @noinspection PhpDeprecationInspection */\n  return RestoreFleetToPlanet($fleet_row, false, true);\n}\n"
  },
  {
    "path": "includes/includes/flt_page0.inc",
    "content": "<?php\n// fleet.php\n// @version 2.1 - Rebuilt to use Slider element from jUI. Also shows preliminary information about fleet: speed, consumption etc\n// @version 2.0 - Serious optimizations: remove redundant fields etc Also makes compliant with PCG\n// @version 1.0\n// @copyright 2008 by Chlorel for XNova\n\nif($fleet_ship_sort = sys_get_param_id('sort_elements')) {\n  define('IN_AJAX', true);\n  if(!empty($lang['player_option_fleet_ship_sort'][$fleet_ship_sort])) {\n    // player_save_option($user, PLAYER_OPTION_FLEET_SHIP_SORT, $fleet_ship_sort);\n    // player_save_option($user, PLAYER_OPTION_FLEET_SHIP_SORT_INVERSE, sys_get_param_id('fleet_ship_sort_inverse', 0));\n    SN::$user_options[PLAYER_OPTION_FLEET_SHIP_SORT] = $fleet_ship_sort;\n    SN::$user_options[PLAYER_OPTION_FLEET_SHIP_SORT_INVERSE] = sys_get_param_id('sort_elements_inverse', 0);\n  }\n  die();\n}\n\nif(SN_IN_FLEET !== true) {\n  $debug->error(\"Attempt to call FLEET page mode {$mode} directly - not from fleet.php\", 'Forbidden', 403);\n}\n\nlng_include('overview');\n\nif(!$planetrow) {\n  SnTemplate::messageBox($lang['fl_noplanetrow'], $lang['fl_error']);\n}\n\n$template = SnTemplate::gettemplate('fleet0', true);\n\n$record_index = 0;\n$ship_list = array();\nforeach(sn_get_groups('fleet') as $n => $unit_id) {\n  $unit_level = mrc_get_level($user, $planetrow, $unit_id, false, true);\n  if($unit_level > 0) {\n    $ship_data = get_ship_data($unit_id, $user);\n    $ship_list[$unit_id] = array(\n      '__INDEX'     => $record_index++,\n      'ID'          => $unit_id,\n      'NAME'        => $lang['tech'][$unit_id],\n      'AMOUNT'      => $unit_level,\n      'AMOUNT_TEXT' => HelperString::numberFloorAndFormat($unit_level),\n      'CONSUMPTION' => $ship_data['consumption'],\n      'CONSUMPTION_TEXT' => HelperString::numberFloorAndFormat($ship_data['consumption']),\n      'SPEED'       => $ship_data['speed'],\n      'SPEED_TEXT'  => HelperString::numberFloorAndFormat($ship_data['speed']),\n      'CAPACITY'    => $ship_data['capacity'],\n      'CAPACITY_TEXT'    => HelperString::numberFloorAndFormat($ship_data['capacity']),\n    );\n  }\n}\n\n$fleet_ship_sort = SN::$user_options[PLAYER_OPTION_FLEET_SHIP_SORT];\n$fleet_ship_sort_inverse = SN::$user_options[PLAYER_OPTION_FLEET_SHIP_SORT_INVERSE];\nif($fleet_ship_sort || $fleet_ship_sort_inverse != PLAYER_OPTION_SORT_ORDER_PLAIN) {\n  switch($fleet_ship_sort) {\n    case PLAYER_OPTION_SORT_NAME: $fleet_ship_sort_field = 'NAME'; break;\n    case PLAYER_OPTION_SORT_SPEED: $fleet_ship_sort_field = 'SPEED'; break;\n    case PLAYER_OPTION_SORT_COUNT: $fleet_ship_sort_field = 'AMOUNT'; break;\n    case PLAYER_OPTION_SORT_ID: $fleet_ship_sort_field = 'ID'; break;\n    default: $fleet_ship_sort_field = '__INDEX'; break;\n  }\n  $fleet_ship_sort_inverse_a = $fleet_ship_sort_inverse ? -1 : 1;\n  usort($ship_list, function($a, $b) use ($fleet_ship_sort_field, $fleet_ship_sort_inverse_a) {\n    return $a[$fleet_ship_sort_field] < $b[$fleet_ship_sort_field] ? -1 * $fleet_ship_sort_inverse_a : (\n      $a[$fleet_ship_sort_field] > $b[$fleet_ship_sort_field] ? 1 * $fleet_ship_sort_inverse_a : 0\n    );\n  });\n}\n\nforeach($ship_list as $ship_data) {\n  $template->assign_block_vars('ships', $ship_data);\n}\n\nforeach($lang['player_option_fleet_ship_sort'] as $sort_id => $sort_text) {\n  $template->assign_block_vars('ship_sort_list', array(\n    'VALUE' => $sort_id,\n    'TEXT' => $sort_text,\n  ));\n}\n\n$template->assign_vars(array(\n  'FLEET_SHIP_SORT' => $fleet_ship_sort,\n  'FLEET_SHIP_SORT_INVERSE' => $fleet_ship_sort_inverse,\n\n  'FLYING_FLEETS'      => $FlyingFleets,\n  'MAX_FLEETS'         => $MaxFleets,\n  'FLYING_EXPEDITIONS' => $FlyingExpeditions,\n  'MAX_EXPEDITIONS'    => $MaxExpeditions,\n  'FREE_EXPEDITIONS'   => $MaxExpeditions - $FlyingExpeditions,\n\n  'COLONIES_CURRENT' => get_player_current_colonies($user),\n  'COLONIES_MAX' => get_player_max_colonies($user),\n  'IS_COLONIZATION' => $target_mission == MT_COLONIZE,\n\n//  'target_mission'     => $target_mission,\n//  'MISSION_NAME'\t\t  => $lang['type_mission'][$target_mission],\n  'TYPE_NAME'\t\t     => $lang['fl_planettype'][$planet_type],\n\n  'ShipList'           => $ShipList,\n\n  'speed_factor'       => Universe::flt_server_flight_speed_multiplier(),\n\n  'PLANET_RESOURCES'   => HelperString::numberFloorAndFormat($planetrow['metal'] + $planetrow['crystal'] + $planetrow['deuterium']),\n  'PLANET_DEUTERIUM'   => HelperString::numberFloorAndFormat($planetrow['deuterium']),\n\n  'PLAYER_OPTION_FLEET_SHIP_SELECT_OLD' => SN::$user_options[PLAYER_OPTION_FLEET_SHIP_SELECT_OLD],\n  'PLAYER_OPTION_FLEET_SHIP_HIDE_SPEED' => SN::$user_options[PLAYER_OPTION_FLEET_SHIP_HIDE_SPEED],\n  'PLAYER_OPTION_FLEET_SHIP_HIDE_CAPACITY' => SN::$user_options[PLAYER_OPTION_FLEET_SHIP_HIDE_CAPACITY],\n  'PLAYER_OPTION_FLEET_SHIP_HIDE_CONSUMPTION' => SN::$user_options[PLAYER_OPTION_FLEET_SHIP_HIDE_CONSUMPTION],\n));\n\n$template->assign_recursive($template_result);\nSnTemplate::display($template, $lang['fl_title']);\n"
  },
  {
    "path": "includes/includes/flt_page1.inc",
    "content": "<?php\n\nuse Fleet\\DbFleetStatic;\nuse Fleet\\FleetStatic;\nuse Planet\\DBStaticPlanet;\n\nif(SN_IN_FLEET !== true) {\n  $debug->error(\"Attempt to call FLEET page mode {$mode} directly - not from fleet.php\", 'Forbidden', 403);\n}\n\n$template = SnTemplate::gettemplate('fleet1', true);\n\n$ships = sys_get_param('ships', array());\nif(!is_array($ships)) {\n  $ships = array();\n}\n\nforeach(array(PT_PLANET, PT_DEBRIS, PT_MOON) as $possible_planet_type_id) {\n  $template->assign_block_vars('possible_planet_type_id', array(\n    'ID' => $possible_planet_type_id,\n    'NAME' => $lang['sys_planet_type_sh'][$possible_planet_type_id],\n  ));\n}\n\n$template_route = array(\n  'START_TYPE_TEXT_SH' => $lang['sys_planet_type_sh'][$planetrow['planet_type']],\n  'START_COORDS'       => uni_render_coordinates($planetrow),\n  'START_NAME'         => $planetrow['name'],\n);\n\nif(!empty($TargetPlanet)) {\n  $template_route += array(\n    'END_TYPE_TEXT_SH' => $lang['sys_planet_type_sh'][$TargetPlanet['planet_type']],\n    'END_COORDS'       => uni_render_coordinates($TargetPlanet),\n    'END_NAME'         => $TargetPlanet['name'],\n  );\n}\n\n$template->assign_block_vars('fleets', $template_route);\n\n$sn_groups_fleet = sn_get_groups('fleet');\nforeach($ships as $ship_id => $ship_count) {\n  if(in_array($ship_id, $sn_groups_fleet) && $ship_count) {\n    $ship_info = get_unit_param($ship_id);\n    if($ship_count > mrc_get_level($user, $planetrow, $ship_id, false, true)) {\n      $page .= $lang['fl_noenought'];\n    } else {\n      $fleet['fleetarray'][$ship_id]  = $ship_count;\n      $fleet['amount']               += $ship_count;\n    }\n    $fleet['capacity'] += $ship_info[P_CAPACITY] * $ship_count;\n    $ship_base_data = get_ship_data($ship_id, $user);\n    $template->assign_block_vars('fleets.ships', array(\n      'ID'          => $ship_id,\n      'AMOUNT'      => $ship_count,\n      'AMOUNT_TEXT' => HelperString::numberFloorAndFormat($ship_count),\n      'CONSUMPTION' => $ship_base_data['consumption'],\n      'SPEED'       => $ship_base_data['speed'],\n      'NAME'        => $lang['tech'][$ship_id],\n    ));\n  }\n}\n\nif(empty($fleet['fleetarray'])) {\n  SnTemplate::messageBox($lang['fl_err_no_ships'], $lang['fl_error'], 'fleet' . DOT_PHP_EX, 5);\n}\n\n// Building list of shortcuts\n$query = doquery(\"SELECT * FROM {{notes}} WHERE `owner` = {$user['id']} AND `galaxy` <> 0 AND `system` <> 0 AND `planet` <> 0 ORDER BY `priority` DESC, `galaxy`, `system`, `planet`, `planet_type`;\");\nwhile($shortcut = db_fetch($query)) {\n  $template->assign_block_vars('shortcut', array(\n    'NAME'       => $shortcut['title'],\n    'GALAXY'     => $shortcut['galaxy'],\n    'SYSTEM'     => $shortcut['system'],\n    'PLANET'     => $shortcut['planet'],\n    'PRIORITY'   => $shortcut['priority'],\n    'PRIORITY_CLASS' => $note_priority_classes[$shortcut['priority']],\n    'TYPE'       => $shortcut['planet_type'],\n    'TYPE_PRINT' => $lang['fl_shrtcup'][$shortcut['planet_type']],\n  ));\n}\n\n// Building list of own planets & moons\n$colonies = DBStaticPlanet::db_planet_list_sorted ( $user );\nif(count($colonies) > 1) {\n  // while($row = db_fetch($colonies))\n  foreach($colonies as $row) {\n    $template->assign_block_vars('colonies', array(\n      'NAME'       => $row['name'],\n      'GALAXY'     => $row['galaxy'],\n      'SYSTEM'     => $row['system'],\n      'PLANET'     => $row['planet'],\n      'TYPE'       => $row['planet_type'],\n      'TYPE_PRINT' => $lang['fl_shrtcup'][$row['planet_type']],\n    ));\n  }\n}\n\n//ACS Start\n//Need to look for acs attacks.\n$aks_madnessred = DbFleetStatic::dbAcsGetAll();\nwhile($row = db_fetch($aks_madnessred)) {\n  $members = explode(',', $row['eingeladen']);\n  foreach($members as $a => $b) {\n    if ($b == $user['id']) {\n      $template->assign_block_vars('acss', array(\n        'ID'         => $row['id'],\n        'NAME'       => $row['name'],\n        'GALAXY'     => $row['galaxy'],\n        'SYSTEM'     => $row['system'],\n        'PLANET'     => $row['planet'],\n        'TYPE'       => $row['planet_type'],\n        'TYPE_PRINT' => $lang['fl_shrtcup'][$row['planet_type']],\n      ));\n    }\n  }\n}\n\n$template->assign_vars(array(\n  'usedfleet'       => str_rot13(base64_encode(json_encode($fleet['fleetarray']))),\n\n  'speed_factor'     => Universe::flt_server_flight_speed_multiplier(),\n  \"t{$planet_type}\" => 'SELECTED',\n\n//  'target_mission'  => $target_mission,\n\n//  'ships'           => str_rot13(base64_encode(serialize($ships))),\n\n  'fleet_speed'     => FleetStatic::flt_fleet_speed($user, $fleet['fleetarray']),\n  'fleet_capacity'  => $fleet['capacity'],\n\n  'PLANET_DEUTERIUM'   => HelperString::numberFloorAndFormat($planetrow['deuterium']),\n\n  'PAGE_HINT'       => $lang['fl_page1_hint'],\n));\n\n$template->assign_recursive($template_result);\nSnTemplate::display($template, $lang['fl_title']);\n"
  },
  {
    "path": "includes/includes/flt_page2.inc",
    "content": "<?php\n// floten2.php\n// @version 1.0\n// @copyright 2008 by Chlorel for XNova\n\nfunction sn_fleet_page2() {\n  global $missiontype, $target_mission, $fleetarray, $planetrow, $lang;\n  global $TargetPlanet, $consumption, $template_result;\n  global $user, $config, $is_transport_missions;\n\n  $template = SnTemplate::gettemplate('fleet2', true);\n\n  foreach ($missiontype as $key => $value) {\n    $template->assign_block_vars('missions', array(\n      'ID' => $key,\n//      'NAME' => $value . ($key == MT_COLONIZE ? ' (' . $lang['sys_colonies'] . ' ' . get_player_current_colonies($user) . '/' . get_player_max_colonies($user) . ')' : ''),\n      'NAME' => $value,\n      'CHECKED' => $target_mission == $key,\n    ));\n  };\n\n  $fleetarray = is_array($fleetarray) ? $fleetarray : array();\n\n  $template_route = array(\n    'START_TYPE_TEXT_SH' => $lang['sys_planet_type_sh'][$planetrow['planet_type']],\n    'START_COORDS'       => uni_render_coordinates($planetrow),\n    'START_NAME'         => $planetrow['name'],\n  );\n\n  if(!empty($TargetPlanet)) {\n    $template_route += array(\n      'END_TYPE_TEXT_SH' => $lang['sys_planet_type_sh'][$TargetPlanet['planet_type']],\n      'END_COORDS'       => uni_render_coordinates($TargetPlanet),\n      'END_NAME'         => $TargetPlanet['name'],\n    );\n  }\n\n  $template->assign_block_vars('fleets', $template_route);\n\n  $sn_groups_fleet = sn_get_groups('fleet');\n  foreach($fleetarray as $ship_id => $ship_count) {\n    if(in_array($ship_id, $sn_groups_fleet) && $ship_count) {\n//      $ship_base_data = get_ship_data($ship_id, $user);\n      $template->assign_block_vars('fleets.ships', array(\n        'ID'          => $ship_id,\n        'AMOUNT'      => $ship_count,\n        'AMOUNT_TEXT' => HelperString::numberFloorAndFormat($ship_count),\n//        'CONSUMPTION' => $ship_base_data['consumption'],\n//        'SPEED'       => $ship_base_data['speed'],\n        'NAME'        => $lang['tech'][$ship_id],\n      ));\n    }\n  }\n\n  $fleet_capacity = 0;\n  foreach ($fleetarray as $Ship => $Count) {\n    $fleet_capacity += get_unit_param($Ship, P_CAPACITY) * $Count;\n  }\n\n  $max_duration = $target_mission == MT_EXPLORE ? get_player_max_expedition_duration($user) :\n    (isset($missiontype[MT_HOLD]) ? 12 : 0);\n  if($max_duration) {\n    $config_game_speed_expedition = ($target_mission == MT_EXPLORE && $config->game_speed_expedition ? $config->game_speed_expedition : 1);\n    for($i = 1; $i <= $max_duration; $i++) {\n      $template->assign_block_vars('duration', array(\n        'ID' => $i,\n        'TIME' => pretty_time(ceil($i * 3600 / $config_game_speed_expedition)),\n      ));\n    }\n  }\n\n//  $temp = array(\n//    'id' => $TargetPlanet['id'],\n//    'galaxy' => $galaxy,\n//    'system' => $system,\n//    'planet' => $planet,\n//    'planet_type' => $planet_type,\n//    'name' => $TargetPlanet['name'] ? $TargetPlanet['name'] : $lang['type_mission'][$target_mission],\n//  );\n  // $TableTitle = uni_render_planet_full($planetrow) . '&nbsp;=&gt;&nbsp;' . uni_render_planet_full($temp);\n\n  $sn_group_resources = sn_get_groups('resources');\n  for($i = 0; $i<3; $i++) {\n    $amount = $planetrow[$sn_group_resources[$i]] - ($i == 2 ? $consumption : 0);\n    $template->assign_block_vars('resources', array(\n      'ID' => $i,\n      'ON_PLANET' => floor($amount),\n      'TEXT' => HelperString::numberFloorAndFormat($amount),\n      'NAME' => $lang['sys_' . $sn_group_resources[$i]],\n    ));\n  }\n\n  if(is_object($captainModule = moduleCaptain()) && ($captain = $captainModule->unit_captain_get($planetrow['id'])) && $captain['unit_location_type'] == LOC_PLANET) {\n    $template->assign_vars(array(\n      'CAPTAIN_ID' => $captain['unit_id'],\n      'CAPTAIN_LEVEL' => $captain['captain_level'],\n      'CAPTAIN_SHIELD' => $captain['captain_shield'],\n      'CAPTAIN_ARMOR' => $captain['captain_armor'],\n      'CAPTAIN_ATTACK' => $captain['captain_attack'],\n    ));\n  }\n\n  $template->assign_vars(array(\n    'planet_metal'     => floor($planetrow['metal']),\n    'planet_crystal'   => floor($planetrow['crystal']),\n    'planet_deuterium' => floor($planetrow['deuterium'] - $consumption),\n\n    'fleet_capacity'   => $fleet_capacity - $consumption,\n    'usedfleet'        => $_POST['usedfleet'],\n\n    // 'TableTitle' => $TableTitle,\n\n\n    'speedallsmin' => sys_get_param_float('speedallsmin'),\n    'speed' => sys_get_param_int('speed') ,\n\n    'fleet_group' => sys_get_param_id('fleet_group') ,\n    'acs_target_mr' => sys_get_param_str('acs_target_mr'),\n\n    'MAX_DURATION' => $max_duration,\n\n    'IS_TRANSPORT_MISSIONS' => $is_transport_missions,\n\n    'PLAYER_COLONIES_CURRENT' => get_player_current_colonies($user),\n    'PLAYER_COLONIES_MAX' => get_player_max_colonies($user),\n  ));\n\n  $template->assign_recursive($template_result);\n  SnTemplate::display($template, $lang['fl_title']);\n}\n"
  },
  {
    "path": "includes/includes/flt_page3.inc",
    "content": "<?php\n// floten3.php\n// @version 1.0\n// @copyright 2008 by Chlorel for XNova\n\nuse DBAL\\db_mysql;\nuse DBAL\\OldDbChangeSet;\nuse Fleet\\DbFleetStatic;\nuse Planet\\DBStaticPlanet;\nuse Unit\\DBStaticUnit;\n\nif(SN_IN_FLEET !== true) {\n  SN::$debug->error(\"Attempt to call FLEET page mode {$mode} directly - not from fleet.php\", 'Forbidden', 403);\n}\n\nfunction sn_fleet_page3() {\n  global $missiontype, $target_mission, $fleetarray, $planetrow;\n  global $galaxy, $system, $planet, $TargetPlanet, $consumption, $template_result;\n  global $errorlist, $config, $planet_type, $MaxExpeditions, $FlyingExpeditions;\n  global $speed_percent, $distance, $fleet_speed, $user;\n  global $duration;\n\n  $lang = SN::$lang;\n\n  $errorlist .= !is_array($fleetarray) ? $lang['fl_no_fleetarray'] : '';\n\n  $errorlist = '';\n  if (!$galaxy || $galaxy > $config->game_maxGalaxy || $galaxy < 1)\n    $errorlist .= $lang['fl_limit_galaxy'];\n  if (!$system || $system > $config->game_maxSystem || $system < 1)\n    $errorlist .= $lang['fl_limit_system'];\n  if (!$planet || $planet < 1 || ($planet > $config->game_maxPlanet && $target_mission != MT_EXPLORE ))\n    $errorlist .= $lang['fl_limit_planet'];\n  if ($planetrow['galaxy'] == $galaxy && $planetrow['system'] == $system && $planetrow['planet'] == $planet && $planetrow['planet_type'] == $planet_type)\n    $errorlist .= $lang['fl_ownpl_err'];\n  if (!$planet_type)\n    $errorlist .= $lang['fl_no_planettype'];\n  if ($planet_type != PT_PLANET && $planet_type != PT_DEBRIS && $planet_type != PT_MOON)\n    $errorlist .= $lang['fl_fleet_err_pl'];\n  if (empty($missiontype[$target_mission])) {\n    $errorlist .= $lang['fl_bad_mission'];\n  }\n\n  $TransMetal      = max(0, sys_get_param_float('resource0'));\n  $TransCrystal    = max(0, sys_get_param_float('resource1'));\n  $TransDeuterium  = max(0, sys_get_param_float('resource2'));\n  $StorageNeeded   = $TransMetal + $TransCrystal + $TransDeuterium;\n\n  if (!$StorageNeeded && $target_mission == MT_TRANSPORT) {\n    $errorlist .= $lang['fl_noenoughtgoods'];\n  }\n\n  // Preparing to lock both source and target users and planets\n  $recordsToLock = [\n    'users'   => [$user['id'],] + (!empty($TargetPlanet['id_owner']) ? [1 => $TargetPlanet['id_owner'],] : []),\n    'planets' => [$planetrow['id'],] + (!empty($TargetPlanet['id']) ? [1 => $TargetPlanet['id'],] : []),\n  ];\n  // Adding incoming aggressive fleets from current user to this planet - to lock those fleets. They will be needed later in bashing check\n  if (!empty($TargetPlanet) && !empty($fleetsBashing = DbFleetStatic::fleet_list_bashing($user['id'], $TargetPlanet))) {\n    foreach($fleetsBashing as $bashingFleet) {\n      $recordsToLock['fleets'][] = $bashingFleet['fleet_id'];\n    }\n  }\n\n  db_mysql::db_transaction_start();\n  // Locking user and planet records\n  SN::$gc->db->lockRecords($recordsToLock);\n\n  // Refreshing current user and planet data\n  $user = db_user_by_id($user['id'], true);\n  $planetrow = DBStaticPlanet::db_planet_by_id($planetrow['id'], true);\n\n  if ($target_mission == MT_EXPLORE) {\n    if ($MaxExpeditions == 0 ) {\n      $errorlist .= $lang['fl_expe_notech'];\n    } elseif ($FlyingExpeditions >= $MaxExpeditions ) {\n      $errorlist .= $lang['fl_expe_max'];\n    }\n  } else {\n    if ($TargetPlanet['id_owner']){\n      if ($target_mission == MT_COLONIZE)\n        $errorlist .= $lang['fl_colonized'];\n\n      if ($TargetPlanet['id_owner'] == $planetrow['id_owner']){\n        if ($target_mission == MT_ATTACK)\n          $errorlist .= $lang['fl_no_self_attack'];\n\n        if ($target_mission == MT_SPY)\n          $errorlist .= $lang['fl_no_self_spy'];\n      }else{\n        if ($target_mission == MT_RELOCATE)\n          $errorlist .= $lang['fl_only_stay_at_home'];\n      }\n    }else{\n      if ($target_mission < MT_COLONIZE){\n        $errorlist .= $lang['fl_unknow_target'];\n      }else{\n        if ($target_mission == MT_DESTROY)\n          $errorlist .= $lang['fl_nomoon'];\n\n        if ($target_mission == MT_RECYCLE){\n          if($TargetPlanet['debris_metal'] + $TargetPlanet['debris_crystal'] == 0)\n            $errorlist .= $lang['fl_nodebris'];\n        }\n      }\n    }\n  }\n\n\n  if(is_object($captainModule = moduleCaptain()) && $captain_id = sys_get_param_id('captain_id')) {\n    $captain = $captainModule->unit_captain_get($planetrow['id']);\n//      mrc_get_level($user, $planetrow, UNIT_CAPTAIN, true);\n    if(!$captain) {\n      $errorlist .= $lang['module_unit_captain_error_no_captain'];\n    } elseif($captain['unit_location_type'] == LOC_PLANET) {\n      if($target_mission == MT_RELOCATE && ($arriving_captain = mrc_get_level($user, $TargetPlanet, UNIT_CAPTAIN, true))) {\n        $errorlist .= $lang['module_unit_captain_error_captain_already_bound'];\n      }\n    } else {\n      $errorlist .= $lang['module_unit_captain_error_captain_flying'];\n    }\n  }\n\n  if ($errorlist) {\n    db_mysql::db_transaction_rollback();\n    SnTemplate::messageBox(\"<span class='error'><ul>{$errorlist}</ul></span>\", $lang['fl_error'], 'fleet' . DOT_PHP_EX, false);\n  }\n\n  // On verifie s'il y a assez de vaisseaux sur la planete !\n  foreach ($fleetarray as $Ship => $Count) {\n    if ($Count > mrc_get_level($user, $planetrow, $Ship)) {\n      SnTemplate::messageBox(\"<span class='error'><b>{$lang['fl_fleet_err']}</b></span>\", $lang['fl_error'], 'fleet' . DOT_PHP_EX, 2);\n    }\n  }\n\n  //Normally... unless its acs...\n  $fleet_group = max(0, intval($_POST['fleet_group']));\n  //But is it acs??\n  //Well all acs fleets must have a fleet code.\n  if($fleet_group) {\n    //Also it must be mission type 2\n    $target_mission = MT_AKS;\n\n    //The co-ords must be the same as where the acs fleet is going.\n    $target = \"g{$galaxy}s{$system}p{$planet}t{$planet_type}\";\n    if($_POST['acs_target_mr'] == $target) {\n      //ACS attack must exist (if acs fleet has arrived this will also return false (2 checks in 1!!!)\n      $aks = DbFleetStatic::dbAcsGetById($fleet_group);\n      if (!$aks) {\n        $fleet_group = 0;\n      }\n      else\n      {\n        $galaxy = $aks['galaxy'];\n        $system = $aks['system'];\n        $planet = $aks['planet'];\n        $planet_type = $aks['planet_type'];\n      }\n    }\n  }\n  //Check that a failed acs attack isn't being sent, if it is, make it an attack fleet.\n  if(!$fleet_group && $target_mission == MT_AKS) {\n    $target_mission = MT_ATTACK;\n  }\n\n  if ($target_mission == MT_COLONIZE || $target_mission == MT_EXPLORE) {\n    $TargetPlanet = ['galaxy' => $galaxy, 'system' => $system, 'planet' => $planet, 'id_owner' => 0];\n  }\n  $options = [P_FLEET_ATTACK_SPEED_PERCENT_TENTH => $speed_percent, P_FLEET_ATTACK_FLEET_GROUP => $fleet_group, P_FLEET_ATTACK_RESOURCES_SUM => $StorageNeeded];\n  $cant_attack = flt_can_attack($planetrow, $TargetPlanet, $fleetarray, $target_mission, $options);\n\n  if($cant_attack !== ATTACK_ALLOWED) {\n    SnTemplate::messageBox(\"<span class='error'><b>{$lang['fl_attack_error'][$cant_attack]}</b></span>\", $lang['fl_error'], 'fleet' . DOT_PHP_EX, 99);\n  }\n\n  $speed_possible = array(10, 9, 8, 7, 6, 5, 4, 3, 2, 1);\n  if (!in_array($speed_percent, $speed_possible)) {\n    SnTemplate::messageBox(\"<span class='error'><b>\" . $lang['fl_cheat_speed'] . \"</b></span>\", $lang['fl_error'], 'fleet' . DOT_PHP_EX, 2);\n  }\n\n  $fleet['start_time'] = $duration + SN_TIME_NOW;\n\n  $max_duration = $target_mission == MT_EXPLORE ? get_player_max_expedition_duration($user) :\n    ($target_mission == MT_HOLD ? 12 : 0);\n  if($max_duration) {\n    $StayDuration = sys_get_param_id('missiontime');\n    if($StayDuration > $max_duration || $StayDuration < 1) {\n      SN::$debug->warning('Supplying wrong mission time', 'Hack attempt', 302, array('base_dump' => true));\n      die();\n    }\n    $StayDuration = ceil($StayDuration * 3600 / ($target_mission == MT_EXPLORE && $config->game_speed_expedition ? $config->game_speed_expedition : 1));\n    $StayTime     = $fleet['start_time'] + $StayDuration;\n  } else {\n    $StayDuration = 0;\n    $StayTime     = 0;\n  }\n  $fleet['end_time']   = $StayDuration + (2 * $duration) + SN_TIME_NOW;\n\n  if($aks && $target_mission == MT_AKS) {\n    if ($fleet['start_time']>$aks['ankunft']) {\n      SnTemplate::messageBox($lang['fl_aks_too_slow'] . 'Fleet arrival: ' . date(FMT_DATE_TIME, $fleet['start_time']) . \" AKS arrival: \" . date(FMT_DATE_TIME, $aks['ankunft']), $lang['fl_error']);\n    }\n    $fleet['start_time'] = $aks['ankunft'];\n    $fleet['end_time'] = $aks['ankunft'] + $duration;\n  }\n\n  $FleetStorage        = 0;\n  $FleetShipCount      = 0;\n  $db_changeset = array();\n  foreach ($fleetarray as $Ship => $Count) {\n    $FleetStorage    += get_unit_param($Ship, P_CAPACITY) * $Count;\n    $db_changeset['unit'][] = OldDbChangeSet::db_changeset_prepare_unit($Ship, -$Count, $user, $planetrow['id']);\n  }\n  $fleet_array = sys_unit_arr2str($fleetarray);\n  $FleetShipCount  += array_sum($fleetarray);\n  $FleetStorage        -= $consumption;\n\n  if ( $StorageNeeded > $FleetStorage) {\n    SnTemplate::messageBox(\"<span class='error'><b>\" . $lang['fl_nostoragespa'] . HelperString::numberFloorAndFormat($StorageNeeded - $FleetStorage) . \"</b></span>\", $lang['fl_error'], 'fleet' . DOT_PHP_EX, 2);\n  }\n  if ($planetrow['deuterium'] < $TransDeuterium + $consumption) {\n    SnTemplate::messageBox(\"<font color=\\\"red\\\"><b>\" . $lang['fl_no_deuterium'] . HelperString::numberFloorAndFormat($TransDeuterium + $consumption - $planetrow['deuterium']) . \"</b></font>\", $lang['fl_error'], 'fleet' . DOT_PHP_EX, 2);\n  }\n  if (($planetrow['metal'] < $TransMetal) || ($planetrow['crystal'] < $TransCrystal)) {\n    SnTemplate::messageBox(\"<font color=\\\"red\\\"><b>\" . $lang['fl_no_resources'] . \"</b></font>\", $lang['fl_error'], 'fleet' . DOT_PHP_EX, 2);\n  }\n\n  $fleet_set = array(\n    'fleet_owner' => $user['id'],\n    'fleet_mission' => $target_mission,\n    'fleet_amount' => $FleetShipCount,\n    'fleet_array' => $fleet_array,\n    'fleet_start_time' => $fleet['start_time'],\n    'fleet_start_planet_id' => !empty($planetrow['id']) ? $planetrow['id'] : null,\n    'fleet_start_galaxy' => $planetrow['galaxy'],\n    'fleet_start_system' => $planetrow['system'],\n    'fleet_start_planet' => $planetrow['planet'],\n    'fleet_start_type'   => $planetrow['planet_type'],\n    'fleet_end_time' => $fleet['end_time'],\n    'fleet_end_stay' => $StayTime,\n    'fleet_end_planet_id' => !empty($TargetPlanet['id']) ? $TargetPlanet['id'] : null,\n    'fleet_end_galaxy' => !empty($galaxy) ? $galaxy : 0,\n    'fleet_end_system' => !empty($system) ? $system : 0,\n    'fleet_end_planet' => !empty($planet) ? $planet : 0,\n    'fleet_end_type' => !empty($planet_type) ? $planet_type : 0,\n    'fleet_target_owner' => $TargetPlanet['id_owner'],\n    'fleet_group' => $fleet_group,\n    'fleet_resource_metal' => $TransMetal,\n    'fleet_resource_crystal' => $TransCrystal,\n    'fleet_resource_deuterium' => $TransDeuterium,\n    'start_time' => SN_TIME_NOW,\n  );\n  $fleet_id = DbFleetStatic::fleet_insert_set_dbq($fleet_set);\n\n  DBStaticPlanet::db_planet_set_by_id($planetrow['id'], \"`metal` = `metal` - {$TransMetal}, `crystal` = `crystal` - {$TransCrystal}, `deuterium` = `deuterium` - {$TransDeuterium} - {$consumption}\");\n  OldDbChangeSet::db_changeset_apply($db_changeset);\n\n  $template = SnTemplate::gettemplate('fleet3', true);\n\n//  foreach($fleetarray as $Ship => $Count) {\n//    $template->assign_block_vars('ships', array(\n//      'NAME'  => $lang['tech'][$Ship],\n//      'COUNT' => pretty_number($Count),\n//    ));\n//  }\n\n  if(is_array($captain)) {\n    DBStaticUnit::db_unit_set_by_id($captain['unit_id'], \"`unit_location_type` = \" . LOC_FLEET . \", `unit_location_id` = {$fleet_id}\");\n  }\n\n  $template_route = array(\n    'ID'                 => 1,\n    'START_TYPE_TEXT_SH' => $lang['sys_planet_type_sh'][$planetrow['planet_type']],\n    'START_COORDS'       => uni_render_coordinates($planetrow),\n    'START_NAME'         => $planetrow['name'],\n    'START_TIME_TEXT'    => date(FMT_DATE_TIME, $fleet['end_time'] + SN_CLIENT_TIME_DIFF),\n    'START_LEFT'         => floor($fleet['end_time'] + 1 - SN_TIME_NOW),\n  );\n\n  if(!empty($TargetPlanet)) {\n    $template_route += array(\n      'END_TYPE_TEXT_SH' => $lang['sys_planet_type_sh'][$TargetPlanet['planet_type']],\n      'END_COORDS'       => uni_render_coordinates($TargetPlanet),\n      'END_NAME'         => $TargetPlanet['name'],\n      'END_TIME_TEXT'    => date(FMT_DATE_TIME, $fleet['start_time'] + SN_CLIENT_TIME_DIFF),\n      'END_LEFT'         => floor($fleet['start_time'] + 1 - SN_TIME_NOW),\n    );\n  }\n\n  $template->assign_block_vars('fleets', $template_route);\n\n  $sn_groups_fleet = sn_get_groups('fleet');\n  foreach($fleetarray as $ship_id => $ship_count) {\n    if(in_array($ship_id, $sn_groups_fleet) && $ship_count) {\n//      $ship_base_data = get_ship_data($ship_id, $user);\n      $template->assign_block_vars('fleets.ships', array(\n        'ID'          => $ship_id,\n        'AMOUNT'      => $ship_count,\n        'AMOUNT_TEXT' => HelperString::numberFloorAndFormat($ship_count),\n//        'CONSUMPTION' => $ship_base_data['consumption'],\n//        'SPEED'       => $ship_base_data['speed'],\n        'NAME'        => $lang['tech'][$ship_id],\n      ));\n    }\n  }\n\n  $template->assign_vars(array(\n    'mission' => $lang['type_mission'][$target_mission] . ($target_mission == MT_EXPLORE || $target_mission == MT_HOLD ? ' ' . pretty_time($StayDuration) : ''),\n    'dist' => HelperString::numberFloorAndFormat($distance),\n    'speed' => HelperString::numberFloorAndFormat($fleet_speed),\n    'deute_need' => HelperString::numberFloorAndFormat($consumption),\n    'from' => \"{$planetrow['galaxy']}:{$planetrow['system']}:{$planetrow['planet']}\",\n    'time_go' => date(FMT_DATE_TIME, $fleet['start_time']),\n    'time_go_local' => date(FMT_DATE_TIME, $fleet['start_time'] + SN_CLIENT_TIME_DIFF),\n    'time_back' => date(FMT_DATE_TIME, $fleet['end_time']),\n    'time_back_local' => date(FMT_DATE_TIME, $fleet['end_time'] + SN_CLIENT_TIME_DIFF),\n  ));\n\n  db_mysql::db_transaction_commit();\n  $planetrow = DBStaticPlanet::db_planet_by_id($planetrow['id']);\n\n  $template->assign_recursive($template_result);\n  SnTemplate::display($template, $lang['fl_title']);\n}\n"
  },
  {
    "path": "includes/includes/flt_page4.inc",
    "content": "<?php\n\nuse Fleet\\DbFleetStatic;\nuse Planet\\DBStaticPlanet;\n\nif(SN_IN_FLEET !== true) {\n  $debug->error(\"Attempt to call FLEET page mode {$mode} directly - not from fleet.php\", 'Forbidden', 403);\n}\n\n$fleetid = sys_get_param_id('fleetid');\n\nif(!is_numeric($fleetid) || empty($fleetid)) {\n  sys_redirect(\"fleet.php\");\n}\n\n$fleet = DbFleetStatic::db_fleet_get($fleetid);\nif(!$fleet) {\n  SnTemplate::messageBox($lang['fl_fleet_not_exists'], $lang['fl_error']);\n}\n\nif ($fleet['fleet_start_time'] <= SN_TIME_NOW || $fleet['fleet_end_time'] < SN_TIME_NOW || $fleet['fleet_mess'] == 1) {\n  SnTemplate::messageBox($lang['fl_isback'], $lang['fl_error']);\n}\n\nif($fleet['fleet_owner'] != $user['id']) {\n  $debug->warning($lang['fl_aks_hack_wrong_fleet'],'Wrong Fleet Owner',301);\n  SnTemplate::messageBox($lang['fl_aks_hack_wrong_fleet'], $lang['fl_error']);\n}\n\n// If we got a message to add some1 to attack (MadnessRed code)\n$userNameToAdd_unsafe = sys_get_param_str_unsafe('addtogroup');\n\nif (!empty($userNameToAdd_unsafe)) {\n  try {\n    $userToAddRecord = db_user_by_username($userNameToAdd_unsafe, true);\n\n    $aks = DbFleetStatic::acsAddUser($user, $fleet, $userToAddRecord);\n    $fleet['fleet_group']   = $aks['id'];\n    $fleet['fleet_mission'] = MT_AKS;\n\n    msg_send_simple_message($userToAddRecord['id'], $user['id'], SN_TIME_NOW, MSG_TYPE_COMBAT, $user['username'],\n      SN::$lang['fl_aks_invite_message_header'], sprintf(SN::$lang['fl_aks_invite_message'], $user['username']));\n\n    SnTemplate::tplAddResult(sprintf($lang['fl_aks_player_invited'], $userNameToAdd_unsafe), ERR_NONE);\n  } catch (Exception $e) {\n    SnTemplate::tplAddResult(sprintf($e->getMessage(), $userNameToAdd_unsafe), ERR_ERROR);\n  }\n}\n\n$fleet = DbFleetStatic::db_fleet_get($fleetid);\n$aks = DbFleetStatic::dbAcsGetByFleet($fleetid);\n\n$template = SnTemplate::gettemplate('fleet_aks_invite', true);\n$template->assign_vars(array(\n  'FLEET_ID' => $fleetid,\n  'PAGE_HEADER' => \"{$lang['fl_associate']} '{$aks['name']}'\",\n  'MISSION_NAME' => $lang['type_mission'][MT_AKS],\n));\n\nif($aks['eingeladen'] && is_array($members = db_user_list(\"`id` in ({$aks['eingeladen']})\")) && !empty($members)) {\n  foreach($members as $row) {\n    $template->assign_block_vars('invited', array(\n      'NAME' => $row['username'],\n    ));\n  }\n}\n\n$i++;\n$fleet_row = DbFleetStatic::db_fleet_get($fleetid);\n\nif(is_array($fleet_row) && !empty($fleet_row)) {\n$planet_start = DBStaticPlanet::db_planet_by_id($fleet_row['fleet_start_planet_id']);\n$fleet_row['fleet_start_name'] = !empty($planet_start['name']) ? $planet_start['name'] : '';\n$planet_end = DBStaticPlanet::db_planet_by_id($fleet_row['fleet_end_planet_id']);\n$fleet_row['fleet_end_name'] = !empty($planet_end['name']) ? $planet_end['name'] : '';\n\n$fleet_data = tpl_parse_fleet_db($fleet_row, $i, $user);\n$template->assign_block_vars('fleets', $fleet_data['fleet']);\nforeach($fleet_data['ships'] as $ship_data) {\n  $template->assign_block_vars('fleets.ships', $ship_data);\n}\n}\n\nSnTemplate::display($template, $lang['fl_title']);\n"
  },
  {
    "path": "includes/includes/market_fleeter.inc",
    "content": "<?php\n\nuse DBAL\\db_mysql;\nuse DBAL\\OldDbChangeSet;\nuse Planet\\DBStaticPlanet;\n\nif(!defined('SN_IN_MARKET') || SN_IN_MARKET !== true) {\n  $debug->error(\"Attempt to call market page mode {$mode} directly - not from market.php\", 'Forbidden', 403);\n}\n\n$page_title .= ' - ' . $lang[\"eco_mrk_{$submode}\"];\n\n$template = SnTemplate::gettemplate('market_fleet', true);\n$template->assign_vars(array(\n  'PAGE_HEADER' => $page_title,\n  'rpg_cost' => $rpg_cost,\n));\n\nif(is_array($shipList)) {\n  if(mrc_get_level($user, null, RES_DARK_MATTER) < $rpg_cost) {\n    $intError = MARKET_NO_DM;\n  }\n\n  db_mysql::db_transaction_start();\n  // Здесь не надо апдейтить планету - очередь мы посчитали глобально\n  $planetrow = DBStaticPlanet::db_planet_by_id($planetrow['id'], true);\n//  $global_data = sys_o_get_updated($user, $planetrow, SN_TIME_NOW);\n//  $planetrow = $global_data['planet'];\n\n  // Блокируем запись о запасах кораблей у трейдера\n  doquery(\"SELECT * FROM {{config}} WHERE `config_name` = 'eco_stockman_fleet' LIMIT 1 FOR UPDATE;\");\n\n  $message .= $lang[\"eco_mrk_{$submode}_ships\"] . '<ul>';\n  $qry = array();\n  $total = array();\n  $db_changeset = array();\n  foreach($shipList as $shipID => &$shipCount) {\n    $shipCount = ceil(floatval($shipCount));\n    if(!$shipCount) {\n      continue;\n    }\n\n    if($shipCount < 0) {\n      $debug->warning('User supplied negative ship count on Black Market page', 'Hack Attempt', 307);\n      $intError = MARKET_NEGATIVE_SHIPS;\n      break;\n    }\n\n    if($mode == MARKET_SCRAPPER) {\n      $amount = mrc_get_level($user, $planetrow, $shipID, true, true); // $planetrow[get_unit_param($shipID, P_NAME)];\n    } elseif($mode == MARKET_STOCKMAN) {\n      $amount = $stock[$shipID];\n    }\n\n    if($amount < $shipCount) {\n      $intError = $error_no_stock;\n      break;\n    }\n\n    if(!in_array($shipID, sn_get_groups('fleet'))) {\n      $debug->warning('Hack Attempt', 'User supplied non-ship unit ID on Black Market page', 306);\n      $intError = MARKET_NOT_A_SHIP;\n      break;\n    }\n\n    $multiplier = $mode == MARKET_SCRAPPER ? -1 : 1;\n\n    $db_changeset['unit'][] = OldDbChangeSet::db_changeset_prepare_unit($shipID, $shipCount * $multiplier, $user, $planetrow['id']);\n    $newstock[$shipID] += $shipCount * (-$multiplier);\n\n    $build_data = eco_get_build_data($user, $planetrow, $shipID);\n    $resTemp[RES_METAL] = floor($build_data[BUILD_CREATE][RES_METAL] * $shipCount * $config_rpg_scrape_metal * (-$multiplier));\n    $resTemp[RES_CRYSTAL] = floor($build_data[BUILD_CREATE][RES_CRYSTAL] * $shipCount * $config_rpg_scrape_crystal * (-$multiplier));\n    $resTemp[RES_DEUTERIUM] = floor($build_data[BUILD_CREATE][RES_DEUTERIUM] * $shipCount * $config_rpg_scrape_deuterium * (-$multiplier));\n\n    foreach($resTemp as $resID => $resCount) {\n      $total[$resID] += $resCount;\n    }\n\n    $message .= \"<li>{$lang['tech'][$shipID]}: \" . HelperString::numberFloorAndFormat($shipCount);\n  }\n\n  if($mode == MARKET_STOCKMAN && $intError == MARKET_DEAL) {\n    foreach($total as $resID => $resCount) {\n      if(mrc_get_level($user, $planetrow, $resID, true, true) < -$resCount) {\n        $intError = MARKET_NO_RESOURCES;\n        $debug->warning('Trying to use bug in s/h market', 'S/H Ship Market', 301);\n        break;\n      }\n    }\n  }\n\n  $intError = ($intError == MARKET_DEAL) && (array_sum($total) == 0) ? $error_zero_res : $intError;\n\n  if($intError == MARKET_DEAL) {\n    $message .= '</ul>' . $lang[\"eco_mrk_{$submode}_res\"] . '<ul>';\n    foreach($total as $resID => $resCount) {\n      if(!$resCount) {\n        continue;\n      }\n\n      $resource_name = pname_resource_name($resID);\n      // $newrow[$resID] = $planetrow[$resID] + $resCount * (-$multiplier);\n      $qry[] = \"`{$resource_name}` = `{$resource_name}` + ({$resCount})\";\n\n      $message .= \"<li>\" . $lang['sys_' . $resource_name] . \": \" . HelperString::numberFloorAndFormat(abs($resCount));\n    }\n    $message .= \"</ul>\";\n\n    DBStaticPlanet::db_planet_set_by_id($planetrow['id'], implode(',', $qry));\n    OldDbChangeSet::db_changeset_apply($db_changeset);\n\n    rpg_points_change($user['id'], RPG_MARKET_FLEET, -($rpg_cost), \"Using Black Market page {$mode}\");\n    $config->db_saveItem('eco_stockman_fleet', sys_unit_arr2str($newstock));\n\n    $stock = $newstock;\n    $planetrow = DBStaticPlanet::db_planet_by_id($planetrow['id']);\n\n    db_mysql::db_transaction_commit();\n\n    $template->assign_block_vars('result', array(\n      'STATUS' => ERR_NONE,\n      'MESSAGE' => $message,\n    ));\n  } else {\n    db_mysql::db_transaction_rollback();\n    $template->assign_block_vars('result', array(\n      'STATUS' => ERR_ERROR,\n      'MESSAGE' => $lang['eco_mrk_errors'][$intError],\n    ));\n\n    foreach($shipList as $shipID => $shipCount) {\n      $data['ships'][$shipID] = max(0, intval($shipCount));\n    }\n  }\n}\n$message = '';\n\nif(!$config->eco_stockman_fleet && $config->eco_stockman_fleet_populate) {\n  $config->db_saveItem('eco_stockman_fleet', sys_unit_arr2str(array_map(function($item){return mt_rand(1, 1000);}, sn_get_groups('fleet'))));\n}\n\ntpl_set_resource_info($template, $planetrow, array());\n\nif(!$array) {\n  $array = array();\n}\n\n$group_fleet = sn_get_groups('fleet');\nforeach($array as $key => $value) {\n  if($mode == MARKET_SCRAPPER) {\n    $shipID = $value;\n    $amount = mrc_get_level($user, $planetrow, $shipID, false, true); // $planetrow[get_unit_param($shipID, P_NAME)];\n  } elseif($mode == MARKET_STOCKMAN) {\n    $shipID = $key;\n    $amount = $value;\n  }\n\n  if(!in_array($shipID, $group_fleet)) {\n    continue;\n  }\n\n  if($amount > 0) {\n    $build_data = eco_get_build_data($user, $planetrow, $shipID);\n    $template->assign_block_vars('ships', array(\n      'ID'        => $shipID,\n      'COUNT'     => $amount,\n      'NAME'      => $lang['tech'][$shipID],\n      'METAL'     => floor($build_data[BUILD_CREATE][RES_METAL] * $config_rpg_scrape_metal),\n      'CRYSTAL'   => floor($build_data[BUILD_CREATE][RES_CRYSTAL] * $config_rpg_scrape_crystal),\n      'DEUTERIUM' => floor($build_data[BUILD_CREATE][RES_DEUTERIUM] * $config_rpg_scrape_deuterium),\n      'AMOUNT'    => intval($data['ships'][$shipID]),\n    ));\n  }\n}\n"
  },
  {
    "path": "includes/includes/market_info.inc",
    "content": "<?php\n\nuse DBAL\\db_mysql;\n\nif(!defined('SN_IN_MARKET') || SN_IN_MARKET !== true)\n{\n  $debug->error(\"Attempt to call market page mode {$mode} directly - not from market.php\", 'Forbidden', 403);\n}\n\n$page_title .= \" - {$lang['eco_mrk_info']}\";\n\n$template = SnTemplate::gettemplate('market_info', true);\n\n$template->assign_vars(array(\n  'RES_DARK_MATTER' => mrc_get_level($user, null, RES_DARK_MATTER),\n  'PAGE_HEADER' => $page_title,\n));\n\n$info_action = sys_get_param_int('action');\nif($info_action)\n{\n  try\n  {\n    db_mysql::db_transaction_start();\n\n    $user = db_user_by_id($user['id'], true);\n    if(mrc_get_level($user, null, RES_DARK_MATTER) < $config->rpg_cost_info)\n    {\n      throw new Exception(MARKET_NO_DM, ERR_ERROR);\n    }\n\n    switch($info_action)\n    {\n      case MARKET_INFO_PLAYER:\n        $user_info_name_or_id_unsafe = sys_get_param_str_unsafe('user_info_name');\n        if(!$user_info_name_or_id_unsafe)\n        {\n          throw new Exception(MARKET_INFO_PLAYER_WRONG, ERR_ERROR);\n        }\n\n        if(is_id($user_info_name_or_id_unsafe))\n        {\n          $user_info = db_user_by_id($user_info_name_or_id_unsafe, true, true);\n        }\n        if(!is_array($user_info))\n        {\n          $user_info = db_user_by_username($user_info_name_or_id_unsafe, true);\n        }\n        if(!is_array($user_info))\n        {\n          throw new Exception(MARKET_INFO_PLAYER_NOT_FOUND, ERR_ERROR);\n        }\n        if($user_info['id'] == $user['id'])\n        {\n          throw new Exception(MARKET_INFO_PLAYER_SAME, ERR_ERROR);\n        }\n\n        $msg_text = array();\n        foreach(sn_get_groups('mercenaries') as $mercenary_id)\n        {\n          $msg_text[] = \"{$lang['tech'][$mercenary_id]} - \" . (($mercenary_level = mrc_get_level($user_info, false, $mercenary_id)) ? \"{$lang['sys_level']} {$mercenary_level}\" : $lang['eco_mrk_info_not_hired']);\n        }\n        if($mercenary_level = mrc_get_level($user_info, false, UNIT_PREMIUM))\n        {\n          $msg_text[] = \"{$lang['tech'][UNIT_PREMIUM]} - {$mercenary_level} {$lang['sys_level']}\";\n        }\n        $msg_text = sprintf($lang['eco_mrk_info_player_message'], $user_info['id'], $user_info['username']) . \"\\r\\n\" . implode(\"\\r\\n\", $msg_text);\n\n        msg_send_simple_message($user['id'], 0, SN_TIME_NOW, MSG_TYPE_SPY, $lang['eco_mrk_info_msg_from'], \"{$lang['eco_mrk_info_player']} ID {$user_info['id']} [{$user_info['username']}]\", $msg_text, false, true);\n      break;\n\n      default:\n        throw new Exception(MARKET_INFO_WRONG, ERR_ERROR);\n      break;\n    }\n\n    if(!rpg_points_change($user['id'], RPG_MARKET_INFO_MERCENARY, -$config->rpg_cost_info, \"Using Black Market page {$lang['eco_mrk_info']} - getting info about user ID {$user_info['id']}\"))\n    {\n      // TODO: throw new Exception(MARKET_INFO_PLAYER_SAME, ERR_ERROR);\n    }\n\n    db_mysql::db_transaction_commit();\n    throw new Exception($info_action, ERR_NONE);\n  }\n  catch (Exception $e)\n  {\n    db_mysql::db_transaction_rollback();\n\n    $template->assign_block_vars('result', array(\n      'STATUS' => $e->getCode(),\n      'MESSAGE' => $lang['eco_mrk_errors'][$e->getMessage()],\n    ));\n    /*\n    return array(\n      'STATUS'  => in_array($e->getCode(), array(ERR_NONE, ERR_WARNING, ERR_ERROR)) ? $e->getCode() : ERR_ERROR,\n      'MESSAGE' => $e->getMessage()\n    );\n    */\n  }\n\n}\n"
  },
  {
    "path": "includes/includes/market_trader.inc",
    "content": "<?php\n\nuse DBAL\\db_mysql;\n\nif((!defined('SN_IN_MARKET') || SN_IN_MARKET !== true) && (!defined('SN_IN_ALLY') || SN_IN_ALLY !== true)) {\n  $debug->error(\"Attempt to call market page mode {$mode} directly - not from market.php\", 'Forbidden', 403);\n}\n\nfunction eco_mrk_trader($user, $planetrow = null) {\n  global $debug, $mode;\n  // global $lang;\n\n  // $page_title = \" - {$lang['eco_mrk_trader']}\";\n  $template = SnTemplate::gettemplate('market_trader', true);\n\n  $intError = MARKET_DEAL;\n  $planetrow = isset($planetrow) ? $planetrow : $user;\n\n  global $page_title, $config, $lang;\n\n  lng_include('market');\n\n  $rates = array(\n    RES_METAL => $config->rpg_exchange_metal,\n    RES_CRYSTAL => $config->rpg_exchange_crystal,\n    RES_DEUTERIUM => $config->rpg_exchange_deuterium,\n    RES_DARK_MATTER => $config->rpg_exchange_darkMatter\n  );\n\n  // $dm_db_name = pname_resource_name(RES_DARK_MATTER);\n  $exchangeTo = in_array($exchangeTo = sys_get_param_int('exchangeTo'), sn_get_groups('resources_trader')) ? $exchangeTo : 0;\n  if($exchangeTo && is_array($tradeList = $_POST['spend'])) {\n    $value = 0;\n    $qry = array();\n\n    db_mysql::db_transaction_start();\n    if($planetrow['id_owner']) {\n      $global_data = sys_o_get_updated($planetrow['id_owner'], $planetrow['id'], SN_TIME_NOW);\n      $planetrow = $global_data['planet'];\n    }\n    else {\n      // Locking user record\n      $user = db_user_by_id($user['id'], true);\n    }\n\n    foreach(sn_get_groups('resources_trader') as $resource_id) {\n      $amount = floatval($tradeList[$resource_id]);\n      if($amount < 0) {\n        $debug->error('Trying to supply negative resource amount on Black Market Page', 'Hack Attempt', 305);\n      }\n\n      if($resource_id == RES_DARK_MATTER && $exchangeTo == RES_DARK_MATTER) {\n        continue;\n      }\n\n      $resource_db_name = pname_resource_name($resource_id);\n      if($exchangeTo == RES_DARK_MATTER) {\n        $sign = '+';\n        $amount = floor($tradeList[RES_DARK_MATTER] / 3 * $rates[RES_DARK_MATTER] / $rates[$resource_id]);\n        $value += $amount;\n      } else {\n        $value += floor($amount * $rates[$resource_id] / $rates[$exchangeTo]);\n        if($resource_id == RES_DARK_MATTER) {\n          $amount = 0;\n        } else {\n          if(mrc_get_level($user, $planetrow, $resource_id, true) < $amount) {\n            $intError = MARKET_NO_RESOURCES;\n            break;\n          }\n\n          $sign = '-';\n        }\n      }\n\n      if($amount) {\n        $qry[] = \"`{$resource_db_name}` = `{$resource_db_name}` {$sign} {$amount}\";\n      }\n    }\n\n    if($exchangeTo != RES_DARK_MATTER) {\n      $amount = floor($value);\n      $exchange_to_db_name = pname_resource_name($exchangeTo);\n      $qry[] = \"`{$exchange_to_db_name}` = `{$exchange_to_db_name}` + {$amount}\";\n    }\n\n    $operation_cost = $config->rpg_cost_trader * ($exchangeTo == RES_DARK_MATTER ? 3 : 1) + $tradeList[RES_DARK_MATTER];\n\n    $intError = $value <= 0 ? MARKET_ZERO_DEAL : $intError;\n    $intError = mrc_get_level($user, null, RES_DARK_MATTER) < $operation_cost ? MARKET_NO_DM : $intError;\n\n    if($intError == MARKET_DEAL) {\n      $qry = implode(', ', $qry);\n      $table = $planetrow['id_owner'] ? 'planets' : 'users';\n\n      doquery(\"UPDATE {{{$table}}} SET {$qry} WHERE `id` = {$planetrow['id']} LIMIT 1;\");\n      rpg_points_change($user['id'], RPG_MARKET_EXCHANGE, -$operation_cost, \"Using Black Market page {$lang['eco_mrk_trader']}\");\n      db_mysql::db_transaction_commit();\n\n      $intError = MARKET_DEAL_TRADE;\n      $_SERVER['REQUEST_URI'] = ($has_message = strpos($_SERVER['REQUEST_URI'], '&message=')) ? substr($_SERVER['REQUEST_URI'], 0, $has_message) : $_SERVER['REQUEST_URI'];\n      sys_redirect(\"{$_SERVER['REQUEST_URI']}&message={$intError}\");\n    }\n    db_mysql::db_transaction_rollback();\n    $template->assign_block_vars('result', array(\n      'STATUS' => $intError == MARKET_DEAL ? ERR_NONE : ERR_ERROR,\n      'MESSAGE' => $lang['eco_mrk_errors'][$intError],\n    ));\n  }\n\n  $template->assign_vars(array(\n    'EXCHANGE_TO_RESOURCE_ID' => $exchangeTo,\n  ));\n\n  foreach(sn_get_groups('resources_trader') as $resource_id) {\n    if($resource_id == RES_DARK_MATTER) {\n      $amount = floor(mrc_get_level($user, null, RES_DARK_MATTER) - $config->rpg_cost_trader);\n    } else {\n      $amount = floor(mrc_get_level($user, $planetrow, $resource_id));\n    }\n\n    $template->assign_block_vars('resources', array(\n      'ID'         => $resource_id,\n      'NAME'       => $lang['tech'][$resource_id],\n      'AVAIL'      => $amount,\n      'AVAIL_TEXT' => HelperString::numberFloorAndFormat($amount),\n      'SPENT'      => ($intError == MARKET_DEAL) ? 0 : max(0, $tradeList[$resource_id]),\n      'RATE'       => $rates[$resource_id],\n    ));\n  }\n\n  $template->assign_vars(array(\n    'rpg_cost_trader'   => $config->rpg_cost_trader,\n    // 'message' => $message,\n    'MODE' => $mode,\n  ));\n\n  SnTemplate::display($template, $page_title);\n}\n"
  },
  {
    "path": "includes/includes/sys_avatar.php",
    "content": "<?php\n\nfunction sys_avatar_upload($subject_id, &$avatar_field, $prefix = 'avatar') {\n  global $config, $lang, $user;\n\n  try {\n    $avatar_filename = $fullsize_filename = SN_PATH_AVATAR . $prefix . '_' . $subject_id;\n    $avatar_filename .= '.png';\n    $fullsize_filename .= '_full.png';\n    if (sys_get_param_int('avatar_remove')) {\n      if (file_exists($avatar_filename) && !unlink($avatar_filename)) {\n        throw new Exception($lang['opt_msg_avatar_error_delete'], ERR_ERROR);\n      }\n      $avatar_field = 0;\n      throw new Exception($lang['opt_msg_avatar_removed'], ERR_NONE);\n    } elseif ($_FILES['avatar']['size']) {\n      if (!in_array($_FILES['avatar']['type'], array('image/gif', 'image/jpeg', 'image/jpg', 'image/pjpeg', 'image/png')) || $_FILES['avatar']['size'] > 204800) {\n        throw new Exception($lang['opt_msg_avatar_error_unsupported'], ERR_WARNING);\n      }\n\n      if ($_FILES['avatar']['error']) {\n        throw new Exception(sprintf($lang['opt_msg_avatar_error_upload'], $_FILES['avatar']['error']), ERR_ERROR);\n      }\n\n      if (!($avatar_image = imagecreatefromstring(file_get_contents($_FILES['avatar']['tmp_name'])))) {\n        throw new Exception($lang['opt_msg_avatar_error_unsupported'], ERR_WARNING);\n      }\n\n      $avatar_size = getimagesize($_FILES['avatar']['tmp_name']);\n      $avatar_max_width = $config->avatar_max_width;\n      $avatar_max_height = $config->avatar_max_height;\n      if ($avatar_size[0] > $avatar_max_width || $avatar_size[1] > $avatar_max_height) {\n        $aspect_ratio = min($avatar_max_width / $avatar_size[0], $avatar_max_height / $avatar_size[1]);\n        $avatar_image_new = imagecreatetruecolor($avatar_size[0] * $aspect_ratio, $avatar_size[0] * $aspect_ratio);\n        $result = imagecopyresized($avatar_image_new, $avatar_image, 0, 0, 0, 0, $avatar_size[0] * $aspect_ratio, $avatar_size[0] * $aspect_ratio, $avatar_size[0], $avatar_size[1]);\n        imagedestroy($avatar_image);\n        $avatar_image = $avatar_image_new;\n      }\n\n      if (file_exists($avatar_filename) && !unlink($avatar_filename)) {\n        throw new Exception($lang['opt_msg_avatar_error_delete'], ERR_ERROR);\n      }\n\n      if (!imagepng($avatar_image, $avatar_filename, 9)) {\n        throw new Exception($lang['opt_msg_avatar_error_writing'], ERR_ERROR);\n      }\n\n      $avatar_field = 1;\n      imagedestroy($avatar_image);\n      throw new Exception($lang['opt_msg_avatar_uploaded'], ERR_NONE);\n    }\n  } catch (Exception $e) {\n    return array(\n      'STATUS'  => in_array($e->getCode(), array(ERR_NONE, ERR_WARNING, ERR_ERROR)) ? $e->getCode() : ERR_ERROR,\n      'MESSAGE' => $e->getMessage()\n    );\n  }\n}\n"
  },
  {
    "path": "includes/includes/ube_attack_calculate.php",
    "content": "<?php\n\nuse DBAL\\OldDbChangeSet;\nuse Fleet\\DbFleetStatic;\nuse Planet\\DBStaticPlanet;\nuse Unit\\DBStaticUnit;\n\nrequire_once('ube_report.php');\n\n/*\n\n// TODO: Для админского боя обнулять обломки до луны - что бы не создавалась луна и не писалось количество обломков\n\n// TODO: В симуляторе все равно печтатать поле обломков и изменить надпись \"Средний диаметр луны с указанным шансом %%\" UBE_MOON_SIMULATED\n\n// TODO: Отсылать каждому игроку сообщение на его языке!\n\n// TODO: Перевод на английский\n\n*/\n\n/*\nПланы\n\n[*] UBEv4: Уменьшать каждый раунд значение общей брони кораблей на % от дамаджа, прошедшего за щиты\n    Так будет эмулироваться распределение повреждений по всем кораблям. На этот же процент уменьшать атаку. Процент брать от брони кораблей\n[*] UBEv4: Рандомизировать броню в самом начале боя\n[*] UBEv4: Можно прописать разным кораблям разную стойкость ко взрыву\n[*] UBEv4: Уровень регенерации щитов\n[*] UBEv4: При атаке и проигрыше выбрасывать в космос излишки ресурсов со складов - часть ресурсов, которые за пределами складов. А часть - терять\n\n[*] UBEv4: Боевые порядки юнитов\n[*] UBEv4: Collateral damage: Взрыв корабля наносит урон соседям. Взрыв всех или только одиночек ?\n[*] UBEv4: Регенерация брони - спецмодуль корабля. И вообще подумать о спецмодулях - скорее всего через прошивки\n[*] UBEv4: Распределить планетарный щит в пропорции между всеми защитными сооружениями - эмуляция первого залпа. Или просто добавить такое количество щитов всем оборонам\n[*] UBEv4: Динамическое цветовое кодирование результатов боя - для защитников отдельно, для атакующих - отдельно\n[*] UBEv4: При отлете СН и ЗС могут уничтожить новосозданную луну (?)\n[*] UBEv4: Броня влияет на количество брони, соотношение брони и структуры - т.е. как быстро рванет корабль, а так же на ходовые качества\n    Типы брони: дюралюминий (легкая, но непрочная - минус к весу, минус к броне), броневая сталь (стандартная броня),\n    легированная сталь (более прочная и более дорогая версия стальной - плюс к броне, плюс к цене), комозитная броня (по прочности, как сталь, но легче - минус к весу, плюс к цене),\n    урановая броня (более прочная и заметно тяжелее, чем стальная - плюс к весу, плюс к броне, плюс к цене),\n    титановая броня (хай-энд: прочная, как уран, легкая, как дюралюминия, дорогая, как пиздец)\n    Модуль активной брони против кинетического оружия\n[*] UBEv4: Щиты\n    Модуль для щитов \"Перегрузка\": дает 200% щитов на первый залп\n\n[*] ЧР: Инфа об удержании\n\n[*] Симулятор: Поддержка мультифлотов. Переписать интерфейс симулятора под работу с любым количеством флотов\n\n[*] Артефакты: Гиперсборщик - позволяет собрать обломки сразу после боя\n\n[*] Наемники: Мародер. Капитан или офицер. +1% к вывозимым ресурсам за каждый уровень. Скажем, до +25%. Считаем от общей доли вывоза или от всех ресов на планете?\n\n[*] Боты: Захватывать ничьи планеты\n\n*/\n\nif (BE_DEBUG === true) {\n  require_once('ube_zi_helpers.php');\n}\n\n// ------------------------------------------------------------------------------------------------\n// Рассылает письма всем участникам боя\nfunction sn_ube_message_send(&$combat_data) {\n  global $lang;\n\n  // TODO: Отсылать каждому игроку сообщение на его языке!\n\n  $outcome = &$combat_data[UBE_OUTCOME];\n  $planet_info = &$outcome[UBE_PLANET];\n\n  // Генерируем текст письма\n  $text_common = sprintf($lang['ube_report_msg_body_common'],\n    date(FMT_DATE_TIME, $combat_data['UBE_TIME']),\n    $lang['sys_planet_type_sh'][$planet_info[PLANET_TYPE]],\n    $planet_info[PLANET_GALAXY],\n    $planet_info[PLANET_SYSTEM],\n    $planet_info[PLANET_PLANET],\n    htmlentities($planet_info[PLANET_NAME], ENT_COMPAT, 'UTF-8'),\n    $lang[$outcome['UBE_COMBAT_RESULT'] == UBE_COMBAT_RESULT_WIN ? 'ube_report_info_outcome_win' :\n      ($outcome['UBE_COMBAT_RESULT'] == UBE_COMBAT_RESULT_DRAW ? 'ube_report_info_outcome_draw' : 'ube_report_info_outcome_loss')]\n  );\n\n  $text_defender = '';\n  foreach ($outcome[UBE_DEBRIS] as $resource_id => $resource_amount) {\n    if ($resource_id == RES_DEUTERIUM) {\n      continue;\n    }\n\n    $text_defender .= \"{$lang['tech'][$resource_id]}: \" . HelperString::numberFloorAndFormat($resource_amount) . '<br />';\n  }\n  if ($text_defender) {\n    $text_defender = \"{$lang['ube_report_msg_body_debris']}{$text_defender}<br />\";\n  }\n\n  if ($outcome[UBE_MOON] == UBE_MOON_CREATE_SUCCESS) {\n    $text_defender .= \"{$lang['ube_report_moon_created']} {$outcome[UBE_MOON_SIZE]} {$lang['sys_kilometers_short']}<br /><br />\";\n  } elseif ($outcome[UBE_MOON] == UBE_MOON_CREATE_FAILED) {\n    $text_defender .= \"{$lang['ube_report_moon_chance']} {$outcome[UBE_MOON_CHANCE]}%<br /><br />\";\n  }\n\n  if ($combat_data[UBE_OPTIONS][UBE_MISSION_TYPE] == MT_DESTROY) {\n    if ($outcome[UBE_MOON_REAPERS] == UBE_MOON_REAPERS_NONE) {\n      $text_defender .= $lang['ube_report_moon_reapers_none'];\n    } else {\n      $text_defender .= \"{$lang['ube_report_moon_reapers_wave']}. {$lang['ube_report_moon_reapers_chance']} {$outcome[UBE_MOON_DESTROY_CHANCE]}%. \";\n      $text_defender .= $lang[$outcome[UBE_MOON] == UBE_MOON_DESTROY_SUCCESS ? 'ube_report_moon_reapers_success' : 'ube_report_moon_reapers_failure'] . \"<br />\";\n\n      $text_defender .= \"{$lang['ube_report_moon_reapers_outcome']} {$outcome[UBE_MOON_REAPERS_DIE_CHANCE]}%. \";\n      $text_defender .= $lang[$outcome[UBE_MOON_REAPERS] == UBE_MOON_REAPERS_RETURNED ? 'ube_report_moon_reapers_survive' : 'ube_report_moon_reapers_died'];\n    }\n    $text_defender .= '<br /><br />';\n  }\n\n  $text_defender .= \"{$lang['ube_report_info_link']}: <a href=\\\"index.php?page=battle_report&cypher={$combat_data[UBE_REPORT_CYPHER]}\\\">{$combat_data[UBE_REPORT_CYPHER]}</a>\";\n\n  // TODO: Оптимизировать отсылку сообщений - отсылать пакетами\n  foreach ($combat_data[UBE_PLAYERS] as $player_id => $player_info) {\n    $message = $text_common . ($outcome[UBE_SFR] && $player_info[UBE_ATTACKER] ? $lang['ube_report_msg_body_sfr'] : $text_defender);\n    msg_send_simple_message($player_id, '', $combat_data[UBE_TIME], MSG_TYPE_COMBAT, $lang['sys_mess_tower'], $lang['sys_mess_attack_report'], $message);\n  }\n\n}\n\n\n// ------------------------------------------------------------------------------------------------\n// Записывает результат боя в БД\nfunction ube_combat_result_apply(&$combat_data) { return sn_function_call('ube_combat_result_apply', array(&$combat_data)); }\n\nfunction sn_ube_combat_result_apply(&$combat_data) {\n// TODO: Поменять все отладки на запросы\n  $destination_user_id = $combat_data[UBE_FLEETS][0][UBE_OWNER];\n\n  $outcome = &$combat_data[UBE_OUTCOME];\n  $planet_info = &$outcome[UBE_PLANET];\n  $planet_id = $planet_info[PLANET_ID];\n  // Обновляем поле обломков на планете\n  if (!$combat_data[UBE_OPTIONS][UBE_COMBAT_ADMIN] && !empty($outcome[UBE_DEBRIS])) {\n    DBStaticPlanet::db_planet_set_by_gspt($planet_info[PLANET_GALAXY], $planet_info[PLANET_SYSTEM], $planet_info[PLANET_PLANET], \"`debris_metal` = `debris_metal` + \" . floor($outcome[UBE_DEBRIS][RES_METAL]) . \", `debris_crystal` = `debris_crystal` + \" . floor($outcome[UBE_DEBRIS][RES_CRYSTAL]),\n      PT_PLANET\n    );\n  }\n\n  $db_save = array(\n    UBE_FLEET_GROUP => array(), // Для САБов\n  );\n\n  $fleets_outcome = &$outcome[UBE_FLEETS];\n  foreach ($combat_data[UBE_FLEETS] as $fleet_id => &$fleet_info) {\n    if ($fleet_info[UBE_FLEET_GROUP]) {\n      $db_save[UBE_FLEET_GROUP][$fleet_info[UBE_FLEET_GROUP]] = $fleet_info[UBE_FLEET_GROUP];\n    }\n\n    $fleet_info[UBE_COUNT] = $fleet_info[UBE_COUNT] ? $fleet_info[UBE_COUNT] : array();\n    $fleets_outcome[$fleet_id][UBE_UNITS_LOST] = $fleets_outcome[$fleet_id][UBE_UNITS_LOST] ? $fleets_outcome[$fleet_id][UBE_UNITS_LOST] : array();\n\n    $fleet_query = array();\n    $db_changeset = array();\n    $old_fleet_count = array_sum($fleet_info[UBE_COUNT]);\n    $new_fleet_count = $old_fleet_count - array_sum($fleets_outcome[$fleet_id][UBE_UNITS_LOST]);\n    // Перебираем юниты если во время боя количество юнитов изменилось и при этом во флоту остались юниты или это планета\n    if ($new_fleet_count != $old_fleet_count && (!$fleet_id || $new_fleet_count)) {\n      // Просматриваем результаты изменения флотов\n      foreach ($fleet_info[UBE_COUNT] as $unit_id => $unit_count) {\n        // Перебираем аутком на случай восстановления юнитов\n        $units_lost = (float)$fleets_outcome[$fleet_id][UBE_UNITS_LOST][$unit_id];\n\n        $units_left = $unit_count - $units_lost;\n        if ($fleet_id) {\n          // Не планета - всегда сразу записываем строку итогов флота\n          $fleet_query[$unit_id] = \"{$unit_id},{$units_left}\";\n        } elseif ($units_lost) {\n          // Планета - записываем в ИД юнита его потери только если есть потери\n          $db_changeset['unit'][] = OldDbChangeSet::db_changeset_prepare_unit($unit_id, -$units_lost, $combat_data[UBE_PLAYERS][$destination_user_id][UBE_PLAYER_DATA], $planet_id);\n        }\n      }\n\n      if ($fleet_id) {\n        // Для флотов перегенерируем массив как одно вхождение в SET SQL-запроса\n        $fleet_query = array(\n          'fleet_array' => implode(';', $fleet_query),\n        );\n      }\n    }\n\n    $fleet_delta = array();\n    // Если во флоте остались юниты или это планета - генерируем изменение ресурсов\n    if ($new_fleet_count || !$fleet_id) {\n      foreach (sn_get_groups('resources_loot') as $resource_id) {\n        $resource_change = (float)$fleets_outcome[$fleet_id][UBE_RESOURCES_LOOTED][$resource_id] + (float)$fleets_outcome[$fleet_id][UBE_CARGO_DROPPED][$resource_id];\n        if ($resource_change) {\n          $resource_db_name = ($fleet_id ? 'fleet_resource_' : '') . pname_resource_name($resource_id);\n          $fleet_delta[$resource_db_name] = -($resource_change);\n        }\n      }\n    }\n\n    if ($fleet_id && $new_fleet_count) {\n      // Если защитник и не РМФ - отправляем флот назад\n      if (($fleet_info[UBE_FLEET_TYPE] == UBE_DEFENDERS && !$outcome[UBE_SFR]) || $fleet_info[UBE_FLEET_TYPE] == UBE_ATTACKERS) {\n        $fleet_query['fleet_mess'] = 1;\n      }\n    }\n\n    if ($fleet_id) // Не планета\n    {\n      if ($fleet_info[UBE_FLEET_TYPE] == UBE_ATTACKERS && $outcome[UBE_MOON_REAPERS] == UBE_MOON_REAPERS_DIED) {\n        $new_fleet_count = 0;\n      }\n\n      if ($new_fleet_count) {\n        if (!empty($fleet_query) || !empty($fleet_delta)) {\n          $fleet_query['fleet_amount'] = $new_fleet_count;\n          DbFleetStatic::fleet_update_set($fleet_id, $fleet_query, $fleet_delta);\n        }\n      } else {\n        // Удаляем пустые флоты\n        DbFleetStatic::db_fleet_delete($fleet_id);\n        DBStaticUnit::db_unit_list_delete(0, LOC_FLEET, $fleet_id, 0);\n      }\n    } else // Планета\n    {\n      // Сохраняем изменения ресурсов - если они есть\n      if (!empty($fleet_delta)) {\n        $temp = array();\n        foreach ($fleet_delta as $resource_db_name => $resource_amount) {\n          $temp[] = \"`{$resource_db_name}` = `{$resource_db_name}` + ({$resource_amount})\";\n        }\n        DBStaticPlanet::db_planet_set_by_id($planet_id, implode(',', $temp));\n      }\n      if (!empty($db_changeset)) // Сохраняем изменения юнитов на планете - если они есть\n      {\n        OldDbChangeSet::db_changeset_apply($db_changeset);\n      }\n    }\n  }\n\n  // TODO: Связать сабы с флотами констраинтами ON DELETE SET NULL\n  if (!empty($db_save[UBE_FLEET_GROUP])) {\n    DbFleetStatic::dbAcsDelete($db_save[UBE_FLEET_GROUP]);\n\n    $db_save[UBE_FLEET_GROUP] = implode(',', $db_save[UBE_FLEET_GROUP]);\n  }\n\n  if ($outcome[UBE_MOON] == UBE_MOON_CREATE_SUCCESS) {\n    $moon_row = uni_create_moon($planet_info[PLANET_GALAXY], $planet_info[PLANET_SYSTEM], $planet_info[PLANET_PLANET], $destination_user_id, $outcome[UBE_MOON_SIZE], false);\n    $outcome[UBE_MOON_NAME] = $moon_row['name'];\n    unset($moon_row);\n  } elseif ($outcome[UBE_MOON] == UBE_MOON_DESTROY_SUCCESS) {\n    DBStaticPlanet::db_planet_delete_by_id($planet_id);\n  }\n\n  $bashing_list = array();\n  foreach ($combat_data[UBE_PLAYERS] as $player_id => $player_info) {\n    if ($player_info[UBE_ATTACKER]) {\n      if ($outcome[UBE_MOON] != UBE_MOON_DESTROY_SUCCESS) {\n        $bashing_list[] = \"({$player_id}, {$planet_id}, {$combat_data[UBE_TIME]})\";\n      }\n      if ($combat_data[UBE_OPTIONS][UBE_MISSION_TYPE] == MT_ATTACK && $combat_data[UBE_OPTIONS][UBE_DEFENDER_ACTIVE]) {\n        $str_loose_or_win = $outcome[UBE_COMBAT_RESULT] == UBE_COMBAT_RESULT_WIN ? 'raidswin' : 'raidsloose';\n        db_user_set_by_id($player_id, \"`xpraid` = `xpraid` + 1, `raids` = `raids` + 1, `{$str_loose_or_win}` = `{$str_loose_or_win}` + 1\");\n      }\n    }\n  }\n  $bashing_list = implode(',', $bashing_list);\n  if ($bashing_list) {\n    doquery(\"INSERT INTO {{bashing}} (bashing_user_id, bashing_planet_id, bashing_time) VALUES {$bashing_list};\");\n  }\n}\n"
  },
  {
    "path": "includes/includes/ube_report.php",
    "content": "<?php\n\n// ------------------------------------------------------------------------------------------------\n// Записывает боевой отчет в БД\nfunction sn_ube_report_save(&$combat_data) {\n  global $config;\n\n  // Если уже есть ИД репорта - значит репорт был взят из таблицы. С таким мы не работаем\n  if ($combat_data[UBE_REPORT_CYPHER]) {\n    return false;\n  }\n\n  // Генерируем уникальный секретный ключ и проверяем наличие в базе\n  do {\n    $combat_data[UBE_REPORT_CYPHER] = sys_random_string(32);\n  } while (doquery(\"SELECT ube_report_cypher FROM {{ube_report}} WHERE ube_report_cypher = '{$combat_data[UBE_REPORT_CYPHER]}' LIMIT 1 FOR UPDATE\", true));\n\n  // Инициализация таблицы для пакетной вставки информации\n  $sql_perform = array(\n    'ube_report_player' => array(\n      array(\n        '`ube_report_id`',\n        '`ube_report_player_player_id`',\n        '`ube_report_player_name`',\n        '`ube_report_player_attacker`',\n        '`ube_report_player_bonus_attack`',\n        '`ube_report_player_bonus_shield`',\n        '`ube_report_player_bonus_armor`',\n      ),\n    ),\n\n    'ube_report_fleet' => array(\n      array(\n        '`ube_report_id`',\n        '`ube_report_fleet_player_id`',\n        '`ube_report_fleet_fleet_id`',\n        '`ube_report_fleet_planet_id`',\n        '`ube_report_fleet_planet_name`',\n        '`ube_report_fleet_planet_galaxy`',\n        '`ube_report_fleet_planet_system`',\n        '`ube_report_fleet_planet_planet`',\n        '`ube_report_fleet_planet_planet_type`',\n        '`ube_report_fleet_resource_metal`',\n        '`ube_report_fleet_resource_crystal`',\n        '`ube_report_fleet_resource_deuterium`',\n        '`ube_report_fleet_bonus_attack`',\n        '`ube_report_fleet_bonus_shield`',\n        '`ube_report_fleet_bonus_armor`',\n      ),\n    ),\n\n    'ube_report_outcome_fleet' => array(\n      array(\n        '`ube_report_id`',\n        '`ube_report_outcome_fleet_fleet_id`',\n        '`ube_report_outcome_fleet_resource_lost_metal`',\n        '`ube_report_outcome_fleet_resource_lost_crystal`',\n        '`ube_report_outcome_fleet_resource_lost_deuterium`',\n        '`ube_report_outcome_fleet_resource_dropped_metal`',\n        '`ube_report_outcome_fleet_resource_dropped_crystal`',\n        '`ube_report_outcome_fleet_resource_dropped_deuterium`',\n        '`ube_report_outcome_fleet_resource_loot_metal`',\n        '`ube_report_outcome_fleet_resource_loot_crystal`',\n        '`ube_report_outcome_fleet_resource_loot_deuterium`',\n        '`ube_report_outcome_fleet_resource_lost_in_metal`',\n      ),\n    ),\n\n    'ube_report_outcome_unit' => array(\n      array(\n        '`ube_report_id`',\n        '`ube_report_outcome_unit_fleet_id`',\n        '`ube_report_outcome_unit_unit_id`',\n        '`ube_report_outcome_unit_restored`',\n        '`ube_report_outcome_unit_lost`',\n        '`ube_report_outcome_unit_sort_order`',\n      ),\n    ),\n\n    'ube_report_unit' => array(\n      array(\n        '`ube_report_id`',\n        '`ube_report_unit_player_id`',\n        '`ube_report_unit_fleet_id`',\n        '`ube_report_unit_round`',\n        '`ube_report_unit_unit_id`',\n        '`ube_report_unit_count`',\n        '`ube_report_unit_boom`',\n        '`ube_report_unit_attack`',\n        '`ube_report_unit_shield`',\n        '`ube_report_unit_armor`',\n        '`ube_report_unit_attack_base`',\n        '`ube_report_unit_shield_base`',\n        '`ube_report_unit_armor_base`',\n        '`ube_report_unit_sort_order`',\n      ),\n    ),\n  );\n\n  // Сохраняем общую информацию о бое\n  $outcome = &$combat_data[UBE_OUTCOME];\n  $ube_report_debris_total_in_metal = (\n      floatval($outcome[UBE_DEBRIS][RES_METAL])\n      + floatval($outcome[UBE_DEBRIS][RES_CRYSTAL]) * floatval($config->rpg_exchange_crystal)\n    ) / (floatval($config->rpg_exchange_metal) ? floatval($config->rpg_exchange_metal) : 1);\n  doquery(\"INSERT INTO `{{ube_report}}`\n    SET\n      `ube_report_cypher` = '{$combat_data[UBE_REPORT_CYPHER]}',\n      `ube_report_time_combat` = '\" . date(FMT_DATE_TIME_SQL, $combat_data[UBE_TIME]) . \"',\n      `ube_report_time_spent` = {$combat_data[UBE_TIME_SPENT]},\n\n      `ube_report_combat_admin` = \" . (int)$combat_data[UBE_OPTIONS][UBE_COMBAT_ADMIN] . \",\n      `ube_report_mission_type` = {$combat_data[UBE_OPTIONS][UBE_MISSION_TYPE]},\n\n      `ube_report_combat_result` = {$outcome[UBE_COMBAT_RESULT]},\n      `ube_report_combat_sfr` = \" . (int)$outcome[UBE_SFR] . \",\n\n      `ube_report_debris_metal` = \" . (float)$outcome[UBE_DEBRIS][RES_METAL] . \",\n      `ube_report_debris_crystal` = \" . (float)$outcome[UBE_DEBRIS][RES_CRYSTAL] . \",\n      `ube_report_debris_total_in_metal` = \" . $ube_report_debris_total_in_metal . \",\n\n      `ube_report_planet_id`          = \" . (int)$outcome[UBE_PLANET][PLANET_ID] . \",\n      `ube_report_planet_name`        = '\" . SN::$db->db_escape($outcome[UBE_PLANET][PLANET_NAME]) . \"',\n      `ube_report_planet_size`        = \" . (int)$outcome[UBE_PLANET][PLANET_SIZE] . \",\n      `ube_report_planet_galaxy`      = \" . (int)$outcome[UBE_PLANET][PLANET_GALAXY] . \",\n      `ube_report_planet_system`      = \" . (int)$outcome[UBE_PLANET][PLANET_SYSTEM] . \",\n      `ube_report_planet_planet`      = \" . (int)$outcome[UBE_PLANET][PLANET_PLANET] . \",\n      `ube_report_planet_planet_type` = \" . (int)$outcome[UBE_PLANET][PLANET_TYPE] . \",\n\n      `ube_report_moon` = \" . (int)$outcome[UBE_MOON] . \",\n      `ube_report_moon_chance` = \" . (int)$outcome[UBE_MOON_CHANCE] . \",\n      `ube_report_moon_size` = \" . (float)$outcome[UBE_MOON_SIZE] . \",\n\n      `ube_report_moon_reapers` = \" . (int)$outcome[UBE_MOON_REAPERS] . \",\n      `ube_report_moon_destroy_chance` = \" . (int)$outcome[UBE_MOON_DESTROY_CHANCE] . \",\n      `ube_report_moon_reapers_die_chance` = \" . (int)$outcome[UBE_MOON_REAPERS_DIE_CHANCE] . \",\n\n      `ube_report_capture_result` = \" . (int)$outcome[UBE_CAPTURE_RESULT] . \"\n  \");\n  $ube_report_id = $combat_data[UBE_REPORT_ID] = SN::$db->db_insert_id();\n\n  // Сохраняем общую информацию по игрокам\n  foreach ($combat_data[UBE_PLAYERS] as $player_id => &$player_info) {\n    $sql_perform['ube_report_player'][] = array(\n      $ube_report_id,\n      $player_id,\n\n      \"'\" . SN::$db->db_escape($player_info[UBE_NAME]) . \"'\",\n      (int)$player_info[UBE_ATTACKER],\n\n      (float)$player_info[UBE_BONUSES][UBE_ATTACK],\n      (float)$player_info[UBE_BONUSES][UBE_SHIELD],\n      (float)$player_info[UBE_BONUSES][UBE_ARMOR],\n    );\n  }\n\n  // Всякая информация по флотам\n  $unit_sort_order = 0;\n  foreach ($combat_data[UBE_FLEETS] as $fleet_id => &$fleet_info) {\n    // Сохраняем общую информацию по флотам\n    $sql_perform['ube_report_fleet'][] = array(\n      $ube_report_id,\n      $fleet_info[UBE_OWNER],\n      $fleet_id,\n\n      (float)$fleet_info[UBE_PLANET][PLANET_ID],\n      \"'\" . SN::$db->db_escape($fleet_info[UBE_PLANET][PLANET_NAME]) . \"'\",\n      (int)$fleet_info[UBE_PLANET][PLANET_GALAXY],\n      (int)$fleet_info[UBE_PLANET][PLANET_SYSTEM],\n      (int)$fleet_info[UBE_PLANET][PLANET_PLANET],\n      (int)$fleet_info[UBE_PLANET][PLANET_TYPE],\n\n      (float)$fleet_info[UBE_RESOURCES][RES_METAL],\n      (float)$fleet_info[UBE_RESOURCES][RES_CRYSTAL],\n      (float)$fleet_info[UBE_RESOURCES][RES_DEUTERIUM],\n\n      (float)$fleet_info[UBE_BONUSES][UBE_ATTACK],\n      (float)$fleet_info[UBE_BONUSES][UBE_SHIELD],\n      (float)$fleet_info[UBE_BONUSES][UBE_ARMOR],\n    );\n\n    // Сохраняем итоговую информацию по ресурсам флота - потеряно, выброшено, увезено\n    $fleet_outcome_data = &$outcome[UBE_FLEETS][$fleet_id];\n    $sql_perform['ube_report_outcome_fleet'][] = array(\n      $ube_report_id,\n      $fleet_id,\n\n      (float)$fleet_outcome_data[UBE_RESOURCES_LOST][RES_METAL],\n      (float)$fleet_outcome_data[UBE_RESOURCES_LOST][RES_CRYSTAL],\n      (float)$fleet_outcome_data[UBE_RESOURCES_LOST][RES_DEUTERIUM],\n\n      (float)$fleet_outcome_data[UBE_CARGO_DROPPED][RES_METAL],\n      (float)$fleet_outcome_data[UBE_CARGO_DROPPED][RES_CRYSTAL],\n      (float)$fleet_outcome_data[UBE_CARGO_DROPPED][RES_DEUTERIUM],\n\n      (float)$fleet_outcome_data[UBE_RESOURCES_LOOTED][RES_METAL],\n      (float)$fleet_outcome_data[UBE_RESOURCES_LOOTED][RES_CRYSTAL],\n      (float)$fleet_outcome_data[UBE_RESOURCES_LOOTED][RES_DEUTERIUM],\n\n      (float)$fleet_outcome_data[UBE_RESOURCES_LOST_IN_METAL][RES_METAL],\n    );\n\n    // Сохраняем результаты по юнитам - потеряно и восстановлено\n    foreach ($fleet_info[UBE_COUNT] as $unit_id => $unit_count) {\n      if ($fleet_outcome_data[UBE_UNITS_LOST][$unit_id] || $fleet_outcome_data[UBE_DEFENCE_RESTORE][$unit_id]) {\n        $unit_sort_order++;\n        $sql_perform['ube_report_outcome_unit'][] = array(\n          $ube_report_id,\n          $fleet_id,\n\n          $unit_id,\n          (float)$fleet_outcome_data[UBE_DEFENCE_RESTORE][$unit_id],\n          (float)$fleet_outcome_data[UBE_UNITS_LOST][$unit_id],\n\n          $unit_sort_order,\n        );\n      }\n    }\n  }\n\n  // Сохраняем информацию о раундах\n  $unit_sort_order = 0;\n  foreach ($combat_data[UBE_ROUNDS] as $round => &$round_data) {\n    foreach ($round_data[UBE_FLEETS] as $fleet_id => &$fleet_data) {\n      foreach ($fleet_data[UBE_COUNT] as $unit_id => $unit_count) {\n        $unit_sort_order++;\n\n        $sql_perform['ube_report_unit'][] = array(\n          $ube_report_id,\n          $fleet_data[UBE_FLEET_INFO][UBE_OWNER],\n          $fleet_id,\n          $round,\n\n          $unit_id,\n          $unit_count,\n          (int)$fleet_data[UBE_UNITS_BOOM][$unit_id],\n\n          $fleet_data[UBE_ATTACK][$unit_id],\n          $fleet_data[UBE_SHIELD][$unit_id],\n          $fleet_data[UBE_ARMOR][$unit_id],\n\n          $fleet_data[UBE_ATTACK_BASE][$unit_id],\n          $fleet_data[UBE_SHIELD_BASE][$unit_id],\n          $fleet_data[UBE_ARMOR_BASE][$unit_id],\n\n          $unit_sort_order,\n        );\n      }\n    }\n  }\n\n  // Пакетная вставка данных\n  foreach ($sql_perform as $table_name => $table_data) {\n    if (count($table_data) < 2) {\n      continue;\n    }\n    foreach ($table_data as &$record_data) {\n      $record_data = '(' . implode(',', $record_data) . ')';\n    }\n    $fields = $table_data[0];\n    unset($table_data[0]);\n    doquery(\"INSERT INTO {{{$table_name}}} {$fields} VALUES \" . implode(',', $table_data));\n  }\n\n  return $combat_data[UBE_REPORT_CYPHER];\n}\n\n// ------------------------------------------------------------------------------------------------\n// Читает боевой отчет из БД\nfunction sn_ube_report_load($report_cypher) {\n  $report_cypher = SN::$db->db_escape($report_cypher);\n\n  $report_row = doquery(\"SELECT * FROM {{ube_report}} WHERE ube_report_cypher = '{$report_cypher}' LIMIT 1\", true);\n  if (!$report_row) {\n    return UBE_REPORT_NOT_FOUND;\n  }\n\n  $combat_data = array(\n    UBE_OPTIONS => array(\n      UBE_LOADED       => true,\n      UBE_COMBAT_ADMIN => $report_row['ube_report_combat_admin'],\n      UBE_MISSION_TYPE => $report_row['ube_report_mission_type'],\n    ),\n\n    UBE_TIME          => strtotime($report_row['ube_report_time_combat']),\n    UBE_TIME_SPENT    => $report_row['ube_report_time_spent'],\n    UBE_REPORT_CYPHER => $report_cypher,\n    UBE_REPORT_ID     => $report_row['ube_report_id'],\n\n    UBE_OUTCOME => array(\n      UBE_COMBAT_RESULT => $report_row['ube_report_combat_result'],\n      UBE_SFR           => $report_row['ube_report_combat_sfr'],\n\n      UBE_PLANET => array(\n        PLANET_ID     => $report_row['ube_report_planet_id'],\n        PLANET_NAME   => $report_row['ube_report_planet_name'],\n        PLANET_SIZE   => $report_row['ube_report_planet_size'],\n        PLANET_GALAXY => $report_row['ube_report_planet_galaxy'],\n        PLANET_SYSTEM => $report_row['ube_report_planet_system'],\n        PLANET_PLANET => $report_row['ube_report_planet_planet'],\n        PLANET_TYPE   => $report_row['ube_report_planet_planet_type'],\n      ),\n\n      UBE_DEBRIS => array(\n        RES_METAL   => $report_row['ube_report_debris_metal'],\n        RES_CRYSTAL => $report_row['ube_report_debris_crystal'],\n      ),\n\n      UBE_MOON        => $report_row['ube_report_moon'],\n      UBE_MOON_CHANCE => $report_row['ube_report_moon_chance'],\n      UBE_MOON_SIZE   => $report_row['ube_report_moon_size'],\n\n      UBE_MOON_REAPERS            => $report_row['ube_report_moon_reapers'],\n      UBE_MOON_DESTROY_CHANCE     => $report_row['ube_report_moon_destroy_chance'],\n      UBE_MOON_REAPERS_DIE_CHANCE => $report_row['ube_report_moon_reapers_die_chance'],\n\n      UBE_CAPTURE_RESULT => $report_row['ube_report_capture_result'],\n\n      UBE_ATTACKERS => array(),\n      UBE_DEFENDERS => array(),\n    ),\n  );\n\n  $outcome = &$combat_data[UBE_OUTCOME];\n\n  $query = doquery(\"SELECT * FROM {{ube_report_player}} WHERE `ube_report_id` = {$report_row['ube_report_id']}\");\n  while ($player_row = db_fetch($query)) {\n    $combat_data[UBE_PLAYERS][$player_row['ube_report_player_player_id']] = array(\n      UBE_NAME     => $player_row['ube_report_player_name'],\n      UBE_ATTACKER => $player_row['ube_report_player_attacker'],\n\n      UBE_BONUSES => array(\n        UBE_ATTACK => $player_row['ube_report_player_bonus_attack'],\n        UBE_SHIELD => $player_row['ube_report_player_bonus_shield'],\n        UBE_ARMOR  => $player_row['ube_report_player_bonus_armor'],\n      ),\n    );\n  }\n\n  $query = doquery(\"SELECT * FROM {{ube_report_fleet}} WHERE `ube_report_id` = {$report_row['ube_report_id']}\");\n  while ($fleet_row = db_fetch($query)) {\n    $combat_data[UBE_FLEETS][$fleet_row['ube_report_fleet_fleet_id']] = array(\n      UBE_OWNER => $fleet_row['ube_report_fleet_player_id'],\n\n      UBE_FLEET_TYPE => $combat_data[UBE_PLAYERS][$fleet_row['ube_report_fleet_player_id']][UBE_ATTACKER] ? UBE_ATTACKERS : UBE_DEFENDERS,\n\n      UBE_PLANET => array(\n        PLANET_ID     => $fleet_row['ube_report_fleet_planet_id'],\n        PLANET_NAME   => $fleet_row['ube_report_fleet_planet_name'],\n        PLANET_GALAXY => $fleet_row['ube_report_fleet_planet_galaxy'],\n        PLANET_SYSTEM => $fleet_row['ube_report_fleet_planet_system'],\n        PLANET_PLANET => $fleet_row['ube_report_fleet_planet_planet'],\n        PLANET_TYPE   => $fleet_row['ube_report_fleet_planet_planet_type'],\n      ),\n\n      UBE_BONUSES => array(\n        UBE_ATTACK => $player_row['ube_report_fleet_bonus_attack'],\n        UBE_SHIELD => $player_row['ube_report_fleet_bonus_shield'],\n        UBE_ARMOR  => $player_row['ube_report_fleet_bonus_armor'],\n      ),\n\n      UBE_RESOURCES => array(\n        RES_METAL     => $player_row['ube_report_fleet_resource_metal'],\n        RES_CRYSTAL   => $player_row['ube_report_fleet_resource_crystal'],\n        RES_DEUTERIUM => $player_row['ube_report_fleet_resource_deuterium'],\n      ),\n    );\n  }\n\n  $combat_data[UBE_ROUNDS] = array();\n  $rounds_data = &$combat_data[UBE_ROUNDS];\n\n  $query = doquery(\"SELECT * FROM {{ube_report_unit}} WHERE `ube_report_id` = {$report_row['ube_report_id']} ORDER BY `ube_report_unit_sort_order`\");\n  while ($round_row = db_fetch($query)) {\n    $round = $round_row['ube_report_unit_round'];\n    $fleet_id = $round_row['ube_report_unit_fleet_id'];\n\n    $side = $combat_data[UBE_FLEETS][$fleet_id][UBE_FLEET_TYPE];\n    $rounds_data[$round][$side][UBE_ATTACK][$fleet_id] = 0;\n\n    if (!isset($rounds_data[$round][UBE_FLEETS][$fleet_id])) {\n      $rounds_data[$round][UBE_FLEETS][$fleet_id] = array();\n    }\n\n    $round_fleet_data = &$rounds_data[$round][UBE_FLEETS][$fleet_id];\n    $unit_id = $round_row['ube_report_unit_unit_id'];\n    $round_fleet_data[UBE_COUNT][$unit_id] = $round_row['ube_report_unit_count'];\n    $round_fleet_data[UBE_UNITS_BOOM][$unit_id] = $round_row['ube_report_unit_boom'];\n\n    $round_fleet_data[UBE_ATTACK][$unit_id] = $round_row['ube_report_unit_attack'];\n    $round_fleet_data[UBE_SHIELD][$unit_id] = $round_row['ube_report_unit_shield'];\n    $round_fleet_data[UBE_ARMOR][$unit_id] = $round_row['ube_report_unit_armor'];\n\n    $round_fleet_data[UBE_ATTACK_BASE][$unit_id] = $round_row['ube_report_unit_attack_base'];\n    $round_fleet_data[UBE_SHIELD_BASE][$unit_id] = $round_row['ube_report_unit_shield_base'];\n    $round_fleet_data[UBE_ARMOR_BASE][$unit_id] = $round_row['ube_report_unit_armor_base'];\n  }\n\n\n  $query = doquery(\"SELECT * FROM {{ube_report_outcome_fleet}} WHERE `ube_report_id` = {$report_row['ube_report_id']}\");\n  while ($row = db_fetch($query)) {\n    $fleet_id = $row['ube_report_outcome_fleet_fleet_id'];\n\n    $outcome[UBE_FLEETS][$fleet_id] = array(\n      UBE_RESOURCES_LOST => array(\n        RES_METAL     => $row['ube_report_outcome_fleet_resource_lost_metal'],\n        RES_CRYSTAL   => $row['ube_report_outcome_fleet_resource_lost_crystal'],\n        RES_DEUTERIUM => $row['ube_report_outcome_fleet_resource_lost_deuterium'],\n      ),\n\n      UBE_CARGO_DROPPED => array(\n        RES_METAL     => $row['ube_report_outcome_fleet_resource_dropped_metal'],\n        RES_CRYSTAL   => $row['ube_report_outcome_fleet_resource_dropped_crystal'],\n        RES_DEUTERIUM => $row['ube_report_outcome_fleet_resource_dropped_deuterium'],\n      ),\n\n      UBE_RESOURCES_LOOTED => array(\n        RES_METAL     => $row['ube_report_outcome_fleet_resource_loot_metal'],\n        RES_CRYSTAL   => $row['ube_report_outcome_fleet_resource_loot_crystal'],\n        RES_DEUTERIUM => $row['ube_report_outcome_fleet_resource_loot_deuterium'],\n      ),\n\n      UBE_RESOURCES_LOST_IN_METAL => array(\n        RES_METAL => $row['ube_report_outcome_fleet_resource_lost_in_metal'],\n      ),\n    );\n\n    $side = $combat_data[UBE_FLEETS][$fleet_id][UBE_FLEET_TYPE];\n\n    $outcome[$side][UBE_FLEETS][$fleet_id] = &$outcome[UBE_FLEETS][$fleet_id];\n  }\n\n  $query = doquery(\"SELECT * FROM {{ube_report_outcome_unit}} WHERE `ube_report_id` = {$report_row['ube_report_id']} ORDER BY `ube_report_outcome_unit_sort_order`\");\n  while ($row = db_fetch($query)) {\n    $fleet_id = $row['ube_report_outcome_unit_fleet_id'];\n    $side = $combat_data[UBE_FLEETS][$fleet_id][UBE_FLEET_TYPE];\n    $outcome[$side][UBE_FLEETS][$fleet_id][UBE_UNITS_LOST][$row['ube_report_outcome_unit_unit_id']] = $row['ube_report_outcome_unit_lost'];\n    $outcome[$side][UBE_FLEETS][$fleet_id][UBE_DEFENCE_RESTORE][$row['ube_report_outcome_unit_unit_id']] = $row['ube_report_outcome_unit_restored'];\n  }\n\n  return $combat_data;\n}\n\n\n// ------------------------------------------------------------------------------------------------\n// Парсит инфу о раунде для темплейта\nfunction sn_ube_report_round_fleet(&$combat_data, $round) {\n  global $lang;\n\n  $fleets_info = &$combat_data[UBE_FLEETS];\n  $round_template = array();\n  $round_data = &$combat_data[UBE_ROUNDS][$round];\n  foreach (array(UBE_ATTACKERS, UBE_DEFENDERS) as $side) {\n    $round_data[$side][UBE_ATTACK] = $round_data[$side][UBE_ATTACK] ? $round_data[$side][UBE_ATTACK] : array();\n    foreach ($round_data[$side][UBE_ATTACK] as $fleet_id => $temp) {\n      $fleet_data = &$round_data[UBE_FLEETS][$fleet_id];\n      $fleet_data_prev = &$combat_data[UBE_ROUNDS][$round - 1][UBE_FLEETS][$fleet_id];\n      $fleet_template = array(\n        'ID'          => $fleet_id,\n        'IS_ATTACKER' => $side == UBE_ATTACKERS,\n        'PLAYER_NAME' => htmlentities($combat_data[UBE_PLAYERS][$fleets_info[$fleet_id][UBE_OWNER]][UBE_NAME], ENT_COMPAT, 'UTF-8'),\n      );\n\n      if (is_array($combat_data[UBE_FLEETS][$fleet_id][UBE_PLANET])) {\n        $fleet_template += $combat_data[UBE_FLEETS][$fleet_id][UBE_PLANET];\n        $fleet_template[PLANET_NAME] = $fleet_template[PLANET_NAME] ? htmlentities($fleet_template[PLANET_NAME], ENT_COMPAT, 'UTF-8') : '';\n        $fleet_template['PLANET_TYPE_TEXT'] = $lang['sys_planet_type_sh'][$fleet_template['PLANET_TYPE']];\n      }\n\n      foreach ($fleet_data[UBE_COUNT] as $unit_id => $unit_count) {\n        $shields_original = $fleet_data[UBE_SHIELD_BASE][$unit_id] * $fleet_data_prev[UBE_COUNT][$unit_id];\n        $ship_template = array(\n          'ID'          => $unit_id,\n          'NAME'        => $lang['tech'][$unit_id],\n          'ATTACK'      => HelperString::numberFloorAndFormat($fleet_data[UBE_ATTACK][$unit_id]),\n          'SHIELD'      => HelperString::numberFloorAndFormat($shields_original),\n          'SHIELD_LOST' => HelperString::numberFloorAndFormat($shields_original - $fleet_data[UBE_SHIELD][$unit_id]),\n          'ARMOR'       => HelperString::numberFloorAndFormat($fleet_data_prev[UBE_ARMOR][$unit_id]),\n          'ARMOR_LOST'  => HelperString::numberFloorAndFormat($fleet_data_prev[UBE_ARMOR][$unit_id] - $fleet_data[UBE_ARMOR][$unit_id]),\n          'UNITS'       => HelperString::numberFloorAndFormat($fleet_data_prev[UBE_COUNT][$unit_id]),\n          'UNITS_LOST'  => HelperString::numberFloorAndFormat($fleet_data_prev[UBE_COUNT][$unit_id] - $fleet_data[UBE_COUNT][$unit_id]),\n          'UNITS_BOOM'  => HelperString::numberFloorAndFormat($fleet_data[UBE_UNITS_BOOM][$unit_id]),\n        );\n\n        $fleet_template['.']['ship'][] = $ship_template;\n      }\n\n      $round_template[] = $fleet_template;\n    }\n  }\n\n  return $round_template;\n}\n\n// ------------------------------------------------------------------------------------------------\n// Рендерит таблицу общего результата боя\nfunction sn_ube_report_table_render(&$array, $header) {\n  global $lang;\n\n  $result = array();\n  if (!empty($array)) {\n    foreach ($array as $unit_id => $unit_count) {\n      if ($unit_count) {\n        $result[] = array(\n          'NAME' => $lang['tech'][$unit_id],\n          'LOSS' => HelperString::numberFloorAndFormat($unit_count),\n        );\n      }\n    }\n    if ($header && count($result)) {\n      array_unshift($result, array('NAME' => $header));\n    }\n  }\n\n  return $result;\n}\n\n// ------------------------------------------------------------------------------------------------\n// Генерирует данные для отчета из разобранных данных боя\nfunction sn_ube_report_generate(&$combat_data, &$template_result) {\n  if (!is_array($combat_data)) {\n    return;\n  }\n\n  global $lang;\n\n  // Обсчитываем результаты боя из начальных данных\n  $players_info = &$combat_data[UBE_PLAYERS];\n  $fleets_info = &$combat_data[UBE_FLEETS];\n  $outcome = &$combat_data[UBE_OUTCOME];\n  // Генерируем отчет по флотам\n  for ($round = 1; $round <= count($combat_data[UBE_ROUNDS]) - 1; $round++) {\n    $round_template = array(\n      'NUMBER' => $round,\n      '.'      => array(\n        'fleet' => sn_ube_report_round_fleet($combat_data, $round),\n      ),\n    );\n    $template_result['.']['round'][] = $round_template;\n  }\n\n  // Боевые потери флотов\n  foreach (array(UBE_ATTACKERS, UBE_DEFENDERS) as $side) {\n    if (!is_array($outcome[$side][UBE_FLEETS])) {\n      continue;\n    }\n    foreach ($outcome[$side][UBE_FLEETS] as $fleet_id => $temp) {\n      $player_info = &$players_info[$fleets_info[$fleet_id][UBE_OWNER]];\n      $fleet_outcome = &$outcome[UBE_FLEETS][$fleet_id];\n\n      $template_result['.']['loss'][] = array(\n        'ID'          => $fleet_id,\n        'NAME'        => $player_info[UBE_NAME],\n        'IS_ATTACKER' => $player_info[UBE_ATTACKER],\n        '.'           => array(\n          'param' => array_merge(\n            sn_ube_report_table_render($fleet_outcome[UBE_DEFENCE_RESTORE], $lang['ube_report_info_restored']),\n            sn_ube_report_table_render($fleet_outcome[UBE_UNITS_LOST], $lang['ube_report_info_loss_final']),\n            sn_ube_report_table_render($fleet_outcome[UBE_RESOURCES_LOST], $lang['ube_report_info_loss_resources']),\n            sn_ube_report_table_render($fleet_outcome[UBE_CARGO_DROPPED], $lang['ube_report_info_loss_dropped']),\n            sn_ube_report_table_render($fleet_outcome[UBE_RESOURCES_LOOTED], $lang[$player_info[UBE_ATTACKER] ? 'ube_report_info_loot_lost' : 'ube_report_info_loss_gained']),\n            sn_ube_report_table_render($fleet_outcome[UBE_RESOURCES_LOST_IN_METAL], $lang['ube_report_info_loss_in_metal'])\n          ),\n        ),\n      );\n    }\n  }\n\n  // Обломки\n  $debrisOutcome = $combat_data[UBE_OPTIONS][UBE_SIMULATOR] ? $outcome[UBE_DEBRIS_ORIGINAL] : $outcome[UBE_DEBRIS];\n  $debris = [];\n  foreach ([RES_METAL, RES_CRYSTAL] as $resource_id) {\n    if ($resource_amount = $debrisOutcome[$resource_id]) {\n      $debris[] = array(\n        'NAME'   => $lang['tech'][$resource_id],\n        'AMOUNT' => HelperString::numberFloorAndFormat($resource_amount),\n      );\n    }\n  }\n\n// TODO: $combat_data[UBE_OPTIONS][UBE_COMBAT_ADMIN] - если админский бой не генерировать осколки и не делать луну. Сделать серверную опцию\n\n  // Координаты, тип и название планеты - если есть\n//R  $planet_owner_id = $combat_data[UBE_FLEETS][0][UBE_OWNER];\n  if (isset($combat_data[UBE_OUTCOME][UBE_PLANET])) {\n    $template_result += $combat_data[UBE_OUTCOME][UBE_PLANET];\n    $template_result[PLANET_NAME] = str_replace(' ', '&nbsp;', htmlentities($template_result[PLANET_NAME], ENT_COMPAT, 'UTF-8'));\n  }\n\n  /** @noinspection SpellCheckingInspection */\n  $template_result += array(\n    'MICROTIME'         => $combat_data[UBE_TIME_SPENT],\n    'COMBAT_TIME'       => $combat_data[UBE_TIME] ? $combat_data[UBE_TIME] + SN_CLIENT_TIME_DIFF : 0,\n    'COMBAT_TIME_TEXT'  => date(FMT_DATE_TIME, $combat_data[UBE_TIME] + SN_CLIENT_TIME_DIFF),\n    'COMBAT_ROUNDS'     => count($combat_data[UBE_ROUNDS]) - 1,\n    'UBE_MISSION_TYPE'  => $combat_data[UBE_OPTIONS][UBE_MISSION_TYPE],\n    'MT_DESTROY'        => MT_DESTROY,\n    'UBE_REPORT_CYPHER' => $combat_data[UBE_REPORT_CYPHER],\n    'UBE_IS_SIMULATOR'  => $combat_data[UBE_OPTIONS][UBE_SIMULATOR],\n\n    'PLANET_TYPE_TEXT' => $lang['sys_planet_type_sh'][$template_result['PLANET_TYPE']],\n\n    'UBE_MOON'                    => $outcome[UBE_MOON],\n    'UBE_MOON_CHANCE'             => round($outcome[UBE_MOON_CHANCE], 2),\n    'UBE_MOON_SIZE'               => $outcome[UBE_MOON_SIZE],\n    'UBE_MOON_REAPERS'            => $outcome[UBE_MOON_REAPERS],\n    'UBE_MOON_DESTROY_CHANCE'     => $outcome[UBE_MOON_DESTROY_CHANCE],\n    'UBE_MOON_REAPERS_DIE_CHANCE' => $outcome[UBE_MOON_REAPERS_DIE_CHANCE],\n\n    'UBE_MOON_WAS'              => UBE_MOON_WAS,\n    'UBE_MOON_NONE'             => UBE_MOON_NONE,\n    'UBE_MOON_CREATE_SUCCESS'   => UBE_MOON_CREATE_SUCCESS,\n    'UBE_MOON_CREATE_FAILED'    => UBE_MOON_CREATE_FAILED,\n    'UBE_MOON_REAPERS_NONE'     => UBE_MOON_REAPERS_NONE,\n    'UBE_MOON_DESTROY_SUCCESS'  => UBE_MOON_DESTROY_SUCCESS,\n    'UBE_MOON_REAPERS_RETURNED' => UBE_MOON_REAPERS_RETURNED,\n\n    'UBE_CAPTURE_RESULT'      => $combat_data[UBE_OUTCOME][UBE_CAPTURE_RESULT],\n    'UBE_CAPTURE_RESULT_TEXT' => $lang['ube_report_capture_result'][$combat_data[UBE_OUTCOME][UBE_CAPTURE_RESULT]],\n\n    'UBE_SFR'                => $outcome[UBE_SFR],\n    'UBE_COMBAT_RESULT'      => $outcome[UBE_COMBAT_RESULT],\n    'UBE_COMBAT_RESULT_WIN'  => UBE_COMBAT_RESULT_WIN,\n    'UBE_COMBAT_RESULT_LOSS' => UBE_COMBAT_RESULT_LOSS,\n  );\n  $template_result['.']['debris'] = $debris;\n}\n"
  },
  {
    "path": "includes/includes/ube_zi_helpers.php",
    "content": "<?php\n\nfunction sn_ube_combat_helper_round_header($round) {\n  $header = array(\n    'ИД1',\n    'ИД2',\n    'Ат',\n    '%',\n    'Ат%',\n    'Залп',\n    'АтЗа',\n    'Щит',\n    'Щит-',\n    'ЩитО',\n    'АтО',\n    'Бр',\n    'Бр-',\n    'Пос',\n    'Пос%',\n    'Rand',\n    'Boom',\n    'БрО',\n    'Юнит',\n  );\n\n  print($round);\n  print('<table border=1>');\n  print('<tr align=\"left\">');\n  foreach ($header as $key => $value) {\n    if (is_array($value)) {\n      continue;\n    }\n    print('<th>');\n    print($value);\n    print('</th>');\n  }\n  print('</tr>');\n}\n\nfunction sn_ube_combat_helper_round_footer($round = 0) {\n  print('</table>');\n}\n\nfunction sn_ube_combat_helper_round_row(&$debug_unit_crossfire_result) {\n  $SN = array(\n    SHIP_CARGO_SMALL     => 'МаТр', SHIP_CARGO_BIG => 'БоТр', SHIP_CARGO_SUPER => 'СуТр', SHIP_CARGO_HYPER => 'ГпТр',\n    SHIP_SATTELITE_SOLAR => 'СоСп',\n    SHIP_COLONIZER       => 'Коло', SHIP_RECYCLER => 'Пере', SHIP_SPY => 'Шпио', SHIP_SATTELITE_SLOTH => 'Лень',\n\n    SHIP_SMALL_FIGHTER_LIGHT => 'ЛгИс', SHIP_SMALL_FIGHTER_WRATH => 'Гнев', SHIP_SMALL_FIGHTER_HEAVY => 'ТяИс', SHIP_SMALL_FIGHTER_ASSAULT => 'Штур',\n\n    SHIP_MEDIUM_DESTROYER   => 'Эсми', SHIP_MEDIUM_BOMBER_ENVY => 'Зави', SHIP_LARGE_BOMBER => 'Бомб',\n    SHIP_CARGO_GREED        => 'Жадн', SHIP_LARGE_CRUISER => 'Крей', SHIP_LARGE_BATTLESHIP => 'Линк', SHIP_LARGE_BATTLESHIP_PRIDE => 'Горд',\n    SHIP_LARGE_DESTRUCTOR   => 'Уник', SHIP_HUGE_DEATH_STAR => 'ЗвСм', SHIP_HUGE_SUPERNOVA => 'Нова',\n    UNIT_DEF_TURRET_MISSILE => 'Раке', UNIT_DEF_TURRET_LASER_SMALL => 'ЛеЛа', UNIT_DEF_TURRET_LASER_BIG => 'ТяЛа', UNIT_DEF_TURRET_GAUSS => 'Гаус', UNIT_DEF_TURRET_ION => 'Ионн', UNIT_DEF_TURRET_PLASMA => 'Плаз', UNIT_DEF_SHIELD_SMALL => 'МалЩ', UNIT_DEF_SHIELD_BIG => 'БолЩ', UNIT_DEF_SHIELD_PLANET => 'ПлаЩ'\n  );\n\n  print('<tr align=\"right\">');\n  foreach ($debug_unit_crossfire_result as $key => $value) {\n    if (is_array($value)) {\n      continue;\n    }\n    print('<td>');\n    print(($key === 'attack_unit_id' || $key === 'defend_unit_id') ? $SN[$value] : $value);\n    print('</td>');\n  }\n  print('</tr>');\n}\n"
  },
  {
    "path": "includes/includes/uni_rename.php",
    "content": "<?php\n\nuse DBAL\\db_mysql;\n\ntry\n  {\n    $template = SnTemplate::gettemplate('universe_rename', true);\n\n    $uni_galaxy = sys_get_param_int('galaxy', $planetrow['galaxy']);\n    $uni_system = sys_get_param_int('system');\n\n    if($uni_galaxy < 1 || $uni_galaxy > $config->game_maxGalaxy)\n    {\n      throw new exception($lang['uni_msg_error_wrong_galaxy'], ERR_ERROR);\n    }\n\n    if($uni_system < 0 || $uni_system > $config->game_maxSystem)\n    {\n      throw new exception($lang['uni_msg_error_wrong_system'], ERR_ERROR);\n    }\n\n    $uni_row = doquery(\"select * from `{{universe}}` where `universe_galaxy` = {$uni_galaxy} and `universe_system` = {$uni_system} limit 1;\", '', true);\n    $uni_row['universe_price'] += $uni_system ? $config->uni_price_system : $config->uni_price_galaxy;\n    $uni_row['universe_name'] = strip_tags($uni_row['universe_name'] ? $uni_row['universe_name'] : ($uni_system ? \"{$lang['sys_system']} [{$uni_galaxy}:{$uni_system}]\" : \"{$lang['sys_galaxy']} {$uni_galaxy}\"));\n\n    if(sys_get_param_str('uni_name_submit'))\n    {\n      $uni_row['universe_name'] = strip_tags(sys_get_param_str('uni_name'));\n\n      $uni_price = sys_get_param_float('uni_price');\n      if($uni_price < $uni_row['universe_price'])\n      {\n        throw new exception($lang['uni_msg_error_low_price'], ERR_ERROR);\n      }\n      $uni_row['universe_price'] = $uni_price;\n\n      db_mysql::db_transaction_start();\n      $user = db_user_by_id($user['id'], true);\n      // if($user[get_unit_param(RES_DARK_MATTER, P_NAME)] < $uni_price)\n      if(mrc_get_level($user, null, RES_DARK_MATTER) < $uni_price)\n      {\n        throw new exception($lang['uni_msg_error_no_dm'], ERR_ERROR);\n      }\n\n      if(!rpg_points_change($user['id'], RPG_RENAME, -$uni_price, \"Renaming [{$uni_galaxy}:{$uni_system}] to \" . sys_get_param_str_unsafe('uni_name')))\n      {\n        throw new exception($lang['sys_msg_err_update_dm'], ERR_ERROR);\n      }\n\n      doquery(\"replace {{universe}} set `universe_galaxy` = {$uni_galaxy}, `universe_system` = {$uni_system}, `universe_name` = '{$uni_row['universe_name']}', `universe_price` = {$uni_row['universe_price']};\");\n      $debug->warning(sprintf($lang['uni_msg_admin_rename'], $user['id'], $user['username'], $uni_price, $uni_system ? $lang['uni_system_of'] : $lang['uni_galaxy_of'], $uni_galaxy, $uni_system ? \":{$uni_system}\" : '', strip_tags(sys_get_param_str_unsafe('uni_name'))), $lang['uni_naming'], LOG_INFO_UNI_RENAME);\n      db_mysql::db_transaction_commit();\n      sys_redirect(\"galaxy.php?mode=name&galaxy={$uni_galaxy}&system={$uni_system}\");\n    }\n  }\n  catch (exception $e)\n  {\n    db_mysql::db_transaction_rollback();\n    $template->assign_block_vars('result', array(\n      'STATUS'  => in_array($e->getCode(), array(ERR_NONE, ERR_WARNING, ERR_ERROR)) ? $e->getCode() : ERR_ERROR,\n      'MESSAGE' => $e->getMessage()\n    ));\n  }\n\n  $template->assign_vars(array(\n    'GALAXY' => $uni_galaxy,\n    'SYSTEM' => $uni_system,\n\n    'NAME'   => sys_safe_output($uni_row['universe_name']),\n    'PRICE'  => $uni_row['universe_price'],\n\n    'PAGE_HINT'   => $lang['uni_name_page_hint'],\n  ));\n\n$pageTitle = $lang['sys_universe'] . ' - ' . $lang['uni_naming'];\nSnTemplate::display($template, $pageTitle);\n"
  },
  {
    "path": "includes/includes/user_birthday_celebrate.php",
    "content": "<?php\n\nuse DBAL\\db_mysql;\n\nfunction sn_user_birthday_celebrate()\n{\n  global $config, $lang;\n\n  db_mysql::db_transaction_start();\n\n  $query = db_user_list_to_celebrate($config->user_birthday_range);\n\n  while($row = db_fetch($query))\n  {\n    $row['username'] = SN::$db->db_escape($row['username']);\n    rpg_points_change($row['id'], RPG_BIRTHDAY, $config->user_birthday_gift, \"Birthday gift for user {$row['username']} ID {$row['id']} on his birthday on {$row['user_birthday']}. Gift last gaved at {$row['user_birthday_celebrated']}\");\n    db_user_set_by_id($row['id'], \"`user_birthday_celebrated` = '{$row['current_birthday']}'\");\n    msg_send_simple_message($row['id'], 0, SN_TIME_NOW, MSG_TYPE_ADMIN, $lang['sys_administration'], $lang['sys_birthday'], sprintf($lang['sys_birthday_message'], $row['username'], $row['current_birthday'], $config->user_birthday_gift, $lang['sys_dark_matter_sh']), true, true);\n  }\n\n  $config->db_saveItem('user_birthday_celebrate', SN_TIME_NOW);\n  db_mysql::db_transaction_commit();\n}\n"
  },
  {
    "path": "includes/index.html",
    "content": ""
  },
  {
    "path": "includes/init.php",
    "content": "<?php\n\nversion_compare(PHP_VERSION, '5.6') < 0 ? die('FATAL ERROR: SuperNova REQUIRE PHP version >= 5.6') : false;\n\nuse Common\\Tools\\VersionCheckerDeprecated;\nuse Core\\Autoloader;\nuse Core\\SnBootstrap;\nuse Fleet\\TaskDispatchFleets;\nuse Player\\playerTimeDiff;\n\n// Защита от двойного инита\nif(defined('INIT')) {\n  return;\n}\n\nconst INIT = true;\n\n// Initial time and mem params\ndefine('SN_TIME_MICRO', microtime(true));\ndefine('SN_MEM_START', memory_get_usage());\n\n// Basic constants\n!defined('INSIDE') && define('INSIDE', true);\n!defined('INSTALL') && define('INSTALL', false);\n!defined('IN_PHPBB') && define('IN_PHPBB', true);\n\nrequire_once __DIR__ . '/debug.tools.php';\n\ncall_user_func(function () {\n  if (file_exists($fileName = realpath(__DIR__ . '/../.env.ini'))) {\n    define('SN_ENV_PROD', 'production');\n    define('SN_ENV_DEV', 'development');\n\n    if (($ini = @parse_ini_file($fileName)) !== false) {\n      if (!empty($ini['SN_ENV']) && in_array($envName = $ini['SN_ENV'], [SN_ENV_DEV, SN_ENV_PROD,])) {\n        define('SN_ENV', $envName);\n      } else {\n        define('SN_ENV', SN_ENV_PROD);\n      }\n    }\n  }\n});\n\n// Config file name\nconst SN_CONFIG_NAME = 'config.php';\n\n// Calculating physical root path to SN\ndefine('SN_ROOT_PHYSICAL', str_replace('\\\\', '/', realpath(dirname(__DIR__))) . '/');\ndefine('SN_ROOT_PHYSICAL_STR_LEN', strlen(SN_ROOT_PHYSICAL));\n\nconst SN_ROOT_FILES = SN_ROOT_PHYSICAL . 'files' . '/';\n\n// Getting relative HTTP root to game resources and executable pages (.php)\n// I.e. in https://server.com/supernova/index.php SN_ROOT_RELATIVE will become '/supernova/'\n// It needed to make game work on sub-folders and do not mess with cookies\n// Not very accurate - heavily relies on filesystem paths and may fail on complicate web server setups\n$sn_root_relative = str_replace(['\\\\', '//'], '/', getcwd() . '/');\n$sn_root_relative = str_replace(SN_ROOT_PHYSICAL, '', $sn_root_relative);\n$sn_root_relative = $sn_root_relative . basename($_SERVER['SCRIPT_NAME']);\n// Removing script name to obtain HTTP root\ndefine('SN_ROOT_RELATIVE', str_replace($sn_root_relative, '', $_SERVER['SCRIPT_NAME']));\n\n// Path to modules\nconst SN_ROOT_MODULES = SN_ROOT_PHYSICAL . 'modules/';\nconst SN_MODULE_CONFIG_NAME = SN_CONFIG_NAME;\n\n// Detecting if we are under Google's eye - domain is prefixed with `google.`\n$_server_server_name =\n  isset($_SERVER['SERVER_NAME']) ? $_SERVER['SERVER_NAME'] :\n    (isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : '');\nif (substr(strtolower($_server_server_name), 0, 7) === 'google.') {\n  define('SN_GOOGLE', true);\n} else {\n  define('SN_GOOGLE', false);\n}\n\n// Instancing\n// If server name contains port - replacing : with _ - servers on different ports are different instances\n$instanceName = str_replace(':', '_', $_server_server_name);\nif(SN_GOOGLE) {\n  $instanceName = substr($instanceName, 7);\n}\n$instancePath = 'servers/' . $instanceName . '/';\n// This would be our relative path to instance-specific files\n// Instance is propagated only if config file `/servers/(server name with port - if any)/config.php` present\ndefine('SN_INSTANCE_PATH_RELATIVE', file_exists(SN_ROOT_PHYSICAL . $instancePath . SN_CONFIG_NAME) ? $instancePath : '');\n// Absolute path to instance files\nconst SN_INSTANCE_PATH_ABSOLUTE = SN_ROOT_PHYSICAL . SN_INSTANCE_PATH_RELATIVE;\n// Config for instance\nconst SN_CONFIG_PATH = SN_INSTANCE_PATH_ABSOLUTE . SN_CONFIG_NAME;\n\n// Now finding what folder we will use for player's avatars\n$avatarDirs = [\n  SN_INSTANCE_PATH_RELATIVE . 'avatars',\n  'avatars',\n  'images/avatar',\n];\nforeach ($avatarDirs as $possibleDir) {\n  $avatarInstancePath = SN_ROOT_PHYSICAL . $possibleDir . '/';\n  if (is_dir($avatarInstancePath) && is_writable($avatarInstancePath)) {\n    define('SN_PATH_AVATAR_RELATIVE', $possibleDir . '/');\n    define('SN_PATH_AVATAR', $avatarInstancePath);\n    break;\n  }\n}\nif (!defined('SN_PATH_AVATAR_RELATIVE')) {\n  die('Can not finding writable folder for player avatars. Check that `/avatars` or `/image/avatar` folder exists and it writable by web-server.');\n}\n\n// Detecting root URL aka Virtual Root\n$_server_http_host = isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : '';\ndefine('SN_ROOT_VIRTUAL', 'http' . (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on' ? 's' : '') . '://' . $_server_http_host . SN_ROOT_RELATIVE);\n// Extra config in case of Google\ndefine('SN_ROOT_VIRTUAL_PARENT', str_replace('//google.', '//', SN_ROOT_VIRTUAL));\n// HTTP path to user avatars\ndefine('SN_HTTP_AVATAR', SN_ROOT_VIRTUAL . SN_PATH_AVATAR_RELATIVE);\n\n\nini_set('error_reporting', E_ALL ^ E_NOTICE);\n\nheader('Content-type: text/html; charset=utf-8');\nob_start();\n\n// Installing autoloader\nrequire_once SN_ROOT_PHYSICAL . 'classes/Core/Autoloader.php';\nAutoloader::register('classes/');\nAutoloader::register('classes/UBE/');\n\n// Propagating other constants\nrequire_once SN_ROOT_PHYSICAL . 'includes/constants/constants.php';\n\n// Initiating benchmark\nSnBootstrap::install_benchmark();\n// Loading functions - can't be inserted into function\nrequire_once SN_ROOT_PHYSICAL . 'includes/db.php';\nrequire_once(SN_ROOT_PHYSICAL . 'includes/general/general.php');\nsn_sys_load_php_files(SN_ROOT_PHYSICAL . 'includes/functions/', PHP_EX);\n\nSN::loadFileSettings();\nSN::init_global_objects();\n\n// AFTER init global objects 'cause vars.php uses some from config\nrequire_once(SN_ROOT_PHYSICAL . 'includes/vars.php');\n\n// Some time required to start from cold cache\nset_time_limit(60); // TODO - Optimize\n\n// Отладка\n// define('BE_DEBUG', true); // Отладка боевого движка\nSnBootstrap::init_debug_state();\n\nSnBootstrap::performUpdate(SN::$config);\n\nif (BanHammer::checkIpV4($_SERVER[\"REMOTE_ADDR\"])) {\n  die(\"Server error. Try to connect later...<br />\\nСерверная ошибка. Попробуйте подключиться позже...\");\n}\n\n// init constants from db\n// Moved from SnBootstrap for phpStorm autocomplete\n// TODO - Should be removed someday - there should NOT be constants that depends on configuration!\ndefine('SN_COOKIE', (SN::$config->COOKIE_NAME ?: 'SuperNova') . (SN_GOOGLE ? '_G' : ''));\ndefine('SN_COOKIE_I', SN_COOKIE . AUTH_COOKIE_IMPERSONATE_SUFFIX);\ndefine('SN_COOKIE_D', SN_COOKIE . '_D');\ndefine('SN_COOKIE_T', SN_COOKIE . '_T'); // Time measure cookie\ndefine('SN_COOKIE_F', SN_COOKIE . '_F'); // Font size cookie\ndefine('SN_COOKIE_U', SN_COOKIE . '_U'); // Current user cookie aka user ID\ndefine('SN_COOKIE_U_I', SN_COOKIE_U . AUTH_COOKIE_IMPERSONATE_SUFFIX); // Current impersonator user cookie aka impersonator user ID\ndefine('SN_COOKIE_WEBP', SN_COOKIE . '_WEBP'); // WebP support cookie\n\ndefine('DEFAULT_SKIN_NAME', 'EpicBlue');\ndefine('DEFAULT_SKINPATH', SN::$config->game_default_skin ?: 'skins/' . DEFAULT_SKIN_NAME . '/');\n\ndefine('DEFAULT_LANG', SN::$config->game_default_language ?: 'ru');\n\ndefine('FMT_DATE', SN::$config->int_format_date ?: 'd.m.Y');\ndefine('FMT_TIME', SN::$config->int_format_time ?: 'H:i:s');\ndefine('FMT_DATE_TIME', FMT_DATE . ' ' . FMT_TIME);\n\n/**\n * @var classCache  $sn_cache\n * @var classConfig $config\n * @var debug       $debug\n */\nglobal $sn_cache, $config, $auth, $debug, $lang;\n\n\nglobal $sn_page_name;\nempty($sn_page_name) ? $sn_page_name = INITIAL_PAGE : false;\nglobal $template_result;\n$template_result = ['.' => ['result' => []]];\n\nSN::$lang = $lang = new classLocale(SN::$config->server_locale_log_usage);\n\nglobal $sn_data, $sn_mvc;\n\n// Including here to allow `AwardConstants` use in MM race festival module\n@include_once(SN_ROOT_MODULES . 'player_award/classes/AwardConstants.php');\n\n// Подключаем все модули\n// По нормальным делам тут надо подключать манифесты\n// И читать конфиги - вдруг модуль отключен?\n// Конфиг - часть манифеста?\n\n// TODO\n// Здесь - потому что core_auth модуль лежит в другом каталоге и его нужно инициализировать отдельно\n// И надо инициализировать после загрузки других модулей. Когда-то это казалось отличной идеей, бля...\nSN::$auth = new \\core_auth();\nSN::$gc->modules->registerModule(SN::$auth->manifest['name'], SN::$auth);\n//SN::$gc->modules->registerModule(core_auth::$main_provider->manifest['name'], core_auth::$main_provider);\n\nSN::$gc->modules->loadModules(SN_ROOT_MODULES);\nSN::$gc->modules->initModules();\n\n\n// Подключаем дефолтную страницу\n// По нормальным делам её надо подключать в порядке загрузки обработчиков\n// Сейчас мы делаем это здесь только для того, что бы содержание дефолтной страницы оказалось вверху. Что не факт, что нужно всегда\n// Но нужно, пока у нас есть не MVC-страницы\n$sn_page_data      = $sn_mvc['pages'][$sn_page_name];\n$sn_page_name_file = 'includes/pages/' . $sn_page_data['filename'] . DOT_PHP_EX;\nif($sn_page_name) {\n  // Merging page options to global option pull\n  if(is_array($sn_page_data['options'])) {\n    SN::$options = array_merge(SN::$options, $sn_page_data['options']);\n  }\n\n  if(isset($sn_page_data) && file_exists($sn_page_name_file)) {\n    require_once($sn_page_name_file);\n  }\n}\n\nif((defined('IN_AJAX') && IN_AJAX === true) || (defined('IN_ADMIN') && IN_ADMIN === true) || (!empty(SN::$options[PAGE_OPTION_ADMIN]))) {\n  SN::$options[PAGE_OPTION_FLEET_UPDATE_SKIP] = true;\n}\n\n\n// А теперь проверяем - поддерживают ли у нас загруженный код такую страницу\n// TODO - костыль, что бы работали старые модули. Убрать!\nif(is_array($sn_data['pages'])) {\n  $sn_mvc['pages'] = array_merge($sn_mvc['pages'], $sn_data['pages']);\n}\nif(!isset($sn_mvc['pages'][$sn_page_name])) {\n  $sn_page_name = '';\n}\n\n$lang->lng_switch(sys_get_param_str('lang'));\n\n\nif(SN::$config->server_updater_check_auto && SN::$config->server_updater_check_last + SN::$config->server_updater_check_period <= SN_TIME_NOW) {\n  VersionCheckerDeprecated::performCheckVersion();\n}\n\nSN::$gc->watchdog->register(new TaskDispatchFleets(), TaskDispatchFleets::class);\nSN::$gc->worker->registerWorker('dispatchFleets', function () {\n  \\Core\\Worker::detachIncomingRequest();\n\n  $result = SN::$gc->fleetDispatcher->flt_flying_fleet_handler();\n\n  return $result + ['message' => 'Fleets dispatched', ];\n});\n\n// TODO Check URL timestamp when checking signature\nif (INITIAL_PAGE === 'worker' && SN::$gc->request->url->isSigned()) {\n  if (!defined('IN_AJAX')) {\n    define('IN_AJAX', true);\n  }\n\n  $result = [];\n\n  if (!empty($mode = sys_get_param_str('mode'))) {\n    $result = SN::$gc->worker->$mode();\n  }\n\n  die(json_encode($result));\n}\n\nif(SN::$config->user_birthday_gift && SN_TIME_NOW - SN::$config->user_birthday_celebrate > PERIOD_DAY) {\n  require_once(SN_ROOT_PHYSICAL . 'includes/includes/user_birthday_celebrate.php');\n  sn_user_birthday_celebrate();\n}\n\nif(!SN::$config->var_online_user_count || SN::$config->var_online_user_time + SN::$config->game_users_update_online < SN_TIME_NOW) {\n  dbUpdateUsersCount(db_user_count());\n  dbUpdateUsersOnline(db_user_count(true));\n  SN::$config->pass()->var_online_user_time = SN_TIME_NOW;\n  if(SN::$config->server_log_online) {\n    /** @noinspection SqlResolve */\n    doquery(\"INSERT IGNORE INTO `{{log_users_online}}` SET online_count = \" . SN::$config->var_online_user_count . \";\");\n  }\n}\n\n\n\n\nglobal $user;\n$result = SN::$auth->login();\n\nglobal $account_logged_in;\n$account_logged_in = !empty(SN::$auth->account) && $result[F_LOGIN_STATUS] == LOGIN_SUCCESS;\n\n$user = !empty($result[F_USER]) ? $result[F_USER] : false;\n\nunset($result[F_USER]);\n$template_result += $result;\nunset($result);\n// В этой точке пользователь либо авторизирован - и есть его запись - либо пользователя гарантированно нет в базе\n\n$template_result[F_ACCOUNT_IS_AUTHORIZED] = SN::$sys_user_logged_in = !empty($user) && isset($user['id']) && $user['id'];\n\nif(!empty($user['id'])) {\n  SN::$user_options->user_change($user['id']);\n}\n\n// Если сообщение пустое - заполняем его по коду\n$template_result[F_LOGIN_MESSAGE] =\n  !empty($template_result[F_LOGIN_MESSAGE])\n    ? $template_result[F_LOGIN_MESSAGE]\n    : ($template_result[F_LOGIN_STATUS] != LOGIN_UNDEFINED\n        ? $lang['sys_login_messages'][$template_result[F_LOGIN_STATUS]]\n        : false\n      );\n\nif($template_result[F_LOGIN_STATUS] == LOGIN_ERROR_USERNAME_RESTRICTED_CHARACTERS) {\n  $prohibited_characters = array_map(function($value) {\n    return \"'\" . htmlentities($value, ENT_QUOTES, 'UTF-8') . \"'\";\n  }, str_split(LOGIN_REGISTER_CHARACTERS_PROHIBITED));\n  $template_result[F_LOGIN_MESSAGE] .= implode(', ', $prohibited_characters);\n}\n\n\nif(defined('DEBUG_AUTH') && DEBUG_AUTH && !defined('IN_AJAX')) {\n  pdump('Отключи отладку перед продакшном!');\n}\n\n\n// Это уже переключаемся на пользовательский язык с откатом до языка в параметрах запроса\n$lang->lng_switch(sys_get_param_str('lang'));\n\nSN::$config->db_loadItem('game_disable') == GAME_DISABLE_INSTALL\n  ? define('INSTALL_MODE', GAME_DISABLE_INSTALL)\n  : false;\n\n// TODO - to scheduler\nStatUpdateLauncher::unlock();\n\nif(!empty($_GET['admin_http_key']) && $_GET['admin_http_key'] == $config->admin_http_key) {\n  define('IN_API', true);\n}\n\nif($template_result[F_GAME_DISABLE] = SN::$config->game_disable) {\n  $template_result[F_GAME_DISABLE_REASON] = HelperString::nl2br(\n    SN::$config->game_disable == GAME_DISABLE_REASON\n      ? SN::$config->game_disable_reason\n      : $lang['sys_game_disable_reason'][SN::$config->game_disable]\n  );\n\n  // For API - just skipping all checks\n  // TODO: That is ideologically wrong and should be redone\n  if(defined('IN_API')) {\n    return;\n  }\n\n  // Actions for install mode\n  if(defined('INSTALL_MODE') && INSTALL_MODE) {\n    // Handling log out - should work even in install mode\n    if(strtolower(INITIAL_PAGE) === 'logout') {\n      SN::$auth->logout(true);\n      die();\n    }\n\n    // If user not logged in AND we are not on login page - redirect user there\n    if(!SN::$sys_user_logged_in && !defined('LOGIN_LOGOUT')) {\n      header('Location: login.php');\n      die();\n    }\n\n    // If user is type of admin AND in user pages - redirecting him to admin interface\n    // You really shouldn't mess in user interface until game not configured!\n    if($user['authlevel'] >= 1 && !defined('IN_ADMIN')) {\n      header('Location: ' . SN_ROOT_VIRTUAL_PARENT . 'admin/overview.php');\n      die();\n    }\n  }\n\n  if(\n    ($user['authlevel'] < 1 || !(defined('IN_ADMIN') && IN_ADMIN))\n    &&\n    !(defined('INSTALL_MODE') && defined('LOGIN_LOGOUT'))\n    &&\n    empty(SN::$options[PAGE_OPTION_ADMIN])\n  ) {\n    SnTemplate::messageBox($template_result[F_GAME_DISABLE_REASON], SN::$config->game_name, '', 5, false);\n    ob_end_flush();\n    die();\n  }\n}\n\n// TODO ban\n// TODO $skip_ban_check\nglobal $skip_ban_check;\nif($template_result[F_BANNED_STATUS] && !$skip_ban_check) {\n  if(defined('IN_API')) {\n    return;\n  }\n\n  $bantime = date(FMT_DATE_TIME, $template_result[F_BANNED_STATUS]);\n  // TODO: Add ban reason. Add vacation time. Add message window\n  SnTemplate::messageBox(\"{$lang['sys_banned_msg']} {$bantime}\", $lang['ban_title']);\n  die(\"{$lang['sys_banned_msg']} {$bantime}\");\n}\n\n// TODO !!! Просто $allow_anonymous используется в платежных модулях !!!\nglobal $allow_anonymous;\n$allow_anonymous = $allow_anonymous || (isset($sn_page_data['allow_anonymous']) && $sn_page_data['allow_anonymous']);\n\n\nif(SN::$sys_user_logged_in && INITIAL_PAGE == 'login') {\n  sys_redirect(SN_ROOT_VIRTUAL . 'overview.php');\n} elseif($account_logged_in && !SN::$sys_user_logged_in) { // empty(core_auth::$user['id'])\n} elseif(!$allow_anonymous && !SN::$sys_user_logged_in && !defined('IN_API')) {\n  sys_redirect(SN_ROOT_VIRTUAL . 'login.php');\n}\n\nplayerTimeDiff::defineTimeDiff();\n\n// TODO: ...to controller\n!empty($user) && sys_get_param_id('only_hide_news') ? die(nws_mark_read($user)) : false;\n!empty($user) && sys_get_param_id('survey_vote') ? die(survey_vote($user)) : false;\n\n!empty($sn_mvc['i18n']['']) ? lng_load_i18n($sn_mvc['i18n']['']) : false;\n$sn_page_name && !empty($sn_mvc['i18n'][$sn_page_name]) ? lng_load_i18n($sn_mvc['i18n'][$sn_page_name]) : false;\n\nexecute_hooks($sn_mvc['model'][''], $template, 'model', '');\n\nSN::$gc->watchdog->execute();\n\n//ini_set('error_reporting', E_ALL);\n\n//SN::$gc->watchdog->checkConfigTimeDiff(\n//  'fleet_update_last',\n//  SN::$config->fleet_update_interval,\n//  // Promise\n//  function () {SN::$gc->fleetDispatcher->dispatch();},\n//  classConfig::DATE_TYPE_SQL_STRING,\n//  false\n//);\n\nStatUpdateLauncher::scheduler_process();\n"
  },
  {
    "path": "includes/pages/admin/admin_ally.php",
    "content": "<?php\n\nuse Alliance\\Alliance;\n\n/**\n * Created by Gorlum 15.06.2017 10:08\n */\n\nfunction sn_admin_ally_model($template = null) {\n  define('IN_ADMIN', true);\n  lng_include('admin');\n  SnTemplate::messageBoxAdminAccessDenied(AUTH_LEVEL_ADMINISTRATOR);\n\n  global $template_result;\n\n  if (\n    ($allyId = sys_get_param_id('ally_id'))\n    &&\n    (sys_get_param_str('action') == 'pass')\n    &&\n    ($newOwnerId = sys_get_param_id('new_owner_id'))\n  ) {\n    try {\n      if (empty($alliance = Alliance::findById($allyId))) {\n        throw new \\Exception('{ Альянс с указанным ID не найден }', ERR_ERROR);\n      }\n      if (empty($newOwnerMember = $alliance->getMemberList()->getById($newOwnerId))) {\n        throw new \\Exception('{ Новый владелец Альянса не найден }', ERR_ERROR);\n      }\n\n      $alliance->pass($newOwnerMember);\n\n      $template_result['.']['result'][] = [\n        'MESSAGE' => '{ Альянс успешно передан другому игроку }',\n        'STATUS'  => ERR_NONE,\n      ];\n    } catch (Exception $e) {\n      $template_result['.']['result'][] = [\n        'MESSAGE' => $e->getMessage(),\n        'STATUS'  => $e->getCode(),\n      ];\n    }\n  }\n\n  return $template;\n}\n\n/**\n * @param template|null $template\n *\n * @param Alliance      $alliance\n *\n * @return null|template\n */\nfunction sn_admin_ally_view_one($template, $alliance) {\n  global $template_result;\n\n  SnTemplate::messageBoxAdminAccessDenied(AUTH_LEVEL_ADMINISTRATOR);\n\n  $template = SnTemplate::gettemplate('admin/admin_ally_one', $template);\n\n  $template_result['.']['members'] = $alliance->getMemberList()->asPtl();\n\n  $template->assign_recursive($alliance->asPtl());\n  $template->assign_vars([\n    'PAGE_HEADER'                    => '{ Альянс }' . ' [' . $alliance->id . '] ' . ' [' . $alliance->tag . '] ' . $alliance->name,\n    'ALLIANCE_HEAD_INACTIVE_TIMEOUT' => ALLIANCE_HEAD_INACTIVE_TIMEOUT,\n    'SN_TIME_NOW'                    => SN_TIME_NOW,\n  ]);\n\n  return $template;\n}\n\nfunction sn_admin_ally_view_all($template = null) {\n  SnTemplate::messageBoxAdminAccessDenied(AUTH_LEVEL_ADMINISTRATOR);\n\n  $template = SnTemplate::gettemplate('admin/admin_ally_all', $template);\n\n  foreach (Alliance::findAll([]) as $alliance) {\n    $template->assign_block_vars('ally', $alliance->asPtl());\n  };\n\n  $template->assign_vars([\n    'PAGE_HEADER' => SN::$lang['admin_ally_list'],\n  ]);\n\n  return $template;\n}\n\nfunction sn_admin_ally_view($template = null) {\n  define('IN_ADMIN', true);\n  lng_include('admin');\n  SnTemplate::messageBoxAdminAccessDenied(AUTH_LEVEL_ADMINISTRATOR);\n\n  $allyId = sys_get_param_id('ally_id');\n  $alliance = Alliance::findById($allyId);\n\n  return !empty($alliance) ? sn_admin_ally_view_one($template, $alliance) : sn_admin_ally_view_all($template);\n}\n"
  },
  {
    "path": "includes/pages/battle_report.php",
    "content": "<?php\n\n/**\n * @param null|template $template\n *\n * @return null|template\n */\nfunction sn_battle_report_view($template = null) {\n  global $template_result, $lang;\n\n  require_once('includes/includes/ube_report.php');\n\n  $combat_data = sn_ube_report_load(sys_get_param_str('cypher'));\n\n  if($combat_data != UBE_REPORT_NOT_FOUND) {\n    sn_ube_report_generate($combat_data, $template_result);\n    $template = SnTemplate::gettemplate('ube_combat_report', $template);\n    $template->assign_vars(array(\n      'PAGE_HEADER' => $lang['ube_report_info_page_header'],\n    ));\n  } else {\n    SnTemplate::messageBox($lang['sys_msg_ube_report_err_not_found'], $lang['sys_error']);\n  }\n\n  return $template;\n}\n"
  },
  {
    "path": "includes/pages/contact.php",
    "content": "<?php\n\n/**\n * List of authorities: admin, ops, moders\n */\n\nfunction sn_contact_view($template = null) {\n  global $template_result, $lang;\n\n  $template = SnTemplate::gettemplate('contact', $template);\n\n  $query = db_user_list(\"`authlevel` > 0 ORDER BY `authlevel` ASC\");\n\n  foreach ($query as $row) {\n    $template_result['.']['contact'][] = array(\n      'ID'  => $row['id'],\n      'NAME'  => $row['username'],\n      'LEVEL' => $lang['user_level'][$row['authlevel']],\n      'EMAIL' => $row['email'],\n    );\n  }\n\n  $template_result['PAGE_HEADER'] = $lang['ctc_title'];\n\n  return $template;\n}\n"
  },
  {
    "path": "includes/pages/imperator.php",
    "content": "<?php\n\n/**\n * imperator.php\n *\n * Player's information\n *\n * @copyright (c) 2010-2017 by Gorlum for http://supernova.ws\n */\n\nfunction sn_imperator_view($template = null) {\n  global $template_result, $config, $lang, $user;\n\n  $stat_fields = array(\n    'stat_date' => 'STAT_DATE',\n\n    // 'stat_code' => 'STAT_CODE',\n    'total_rank' => 'TOTAL_RANK',\n    'total_points' => 'TOTAL_POINTS',\n    //'total_count' => 'TOTAL_COUNT',\n    'tech_rank' => 'TECH_RANK',\n    'tech_points' => 'TECH_POINTS',\n    //'tech_count' => 'TECH_COUNT',\n    'build_rank' => 'BUILD_RANK',\n    'build_points' => 'BUILD_POINTS',\n    //'build_count' => 'BUILD_COUNT',\n    'defs_rank' => 'DEFS_RANK',\n    'defs_points' => 'DEFS_POINTS',\n    //'defs_count' => 'DEFS_COUNT',\n    'fleet_rank' => 'FLEET_RANK',\n    'fleet_points' => 'FLEET_POINTS',\n    //'fleet_count' => 'FLEET_COUNT',\n    'res_rank' => 'RES_RANK',\n    'res_points' => 'RES_POINTS',\n    // 'res_count' => 'RES_COUNT',\n  );\n\n  $user_id = sys_get_param_id('int_user_id', $user['id']);\n\n  $user_data = ($same_user = $user_id == $user['id']) ? $user : db_user_by_id($user_id);\n\n//  if($user_id == $user['id']) {\n//    $user_data = &$user;\n//    $same_user = true;\n//  } else {\n//    $user_data = db_user_by_id($user_id);\n//    $same_user = false;\n//  }\n\n  if(!$user_data) {\n    SnTemplate::messageBox($lang['imp_imperator_none'], $lang['sys_error'], 'index.php', 10);\n    die();\n  }\n\n  $template = SnTemplate::gettemplate('imperator', $template);\n  $StatRecord = doquery(\"SELECT * FROM {{statpoints}} WHERE `stat_type` = 1 AND `stat_code` = 1 AND `id_owner` = {$user_id};\", true);\n\n  $stat_array = array();\n  $query = doquery(\"SELECT * FROM {{statpoints}} WHERE `stat_type` = 1 AND `id_owner` = {$user_id} ORDER BY `stat_code` DESC;\");\n  $stat_count = SN::$db->db_affected_rows();\n  while($row = db_fetch($query)) {\n    foreach($stat_fields as $field_db_name => $field_template_name) {\n      // $stat_count - $row['stat_code'] - для реверсирования ID статы в JS\n      $stat_array[$field_template_name]['DATA'][$stat_count - $row['stat_code']] = $row[$field_db_name];\n    }\n  }\n\n  $stat_array_date = $stat_array['STAT_DATE'];\n  empty($stat_array_date['DATA']) ? $stat_array_date['DATA'] = array() : false;\n  foreach($stat_array_date['DATA'] as $key => $value) {\n    $template->assign_block_vars('stat_date', array(\n      'ID' => $key,\n      'VALUE' => $value,\n      'TEXT' => date(FMT_DATE_TIME, $value),\n    ));\n  }\n\n  unset($stat_array['STAT_DATE']);\n  $template_data = array();\n  foreach($stat_array as $stat_type => &$stat_type_data) {\n    $reverse_min_max = strpos($stat_type, '_RANK') !== false;\n    $stat_type_data['MIN'] = $reverse_min_max ? max($stat_type_data['DATA']) : min($stat_type_data['DATA']);\n    $stat_type_data['MAX'] = $reverse_min_max ? min($stat_type_data['DATA']) : max($stat_type_data['DATA']);\n    $stat_type_data['AVG'] = avg($stat_type_data['DATA']);\n    foreach($stat_type_data['DATA'] as $key => $value) {\n      $stat_type_data['PERCENT'][$key] = ($stat_type_data['MAX'] - $value ? ($value - $stat_type_data['MIN']) / ($stat_type_data['MAX'] - $stat_type_data['MIN']) : 1) * 100;\n      $template_data[$stat_type][$key]['ID'] = $key;\n      $template_data[$stat_type][$key]['VALUE'] = $value;\n      $template_data[$stat_type][$key]['DELTA'] = ($reverse_min_max ? $stat_type_data['MIN']  - $value : $value - $stat_type_data['MAX']);\n      $template_data[$stat_type][$key]['PERCENT'] = $stat_type_data['PERCENT'][$key];\n    }\n  }\n\n  foreach($template_data as $stat_type => $stat_type_data) {\n    $template->assign_block_vars('stat', array(\n      'TYPE' => $stat_type,\n      'TEXT' => $lang['imp_stat_types'][$stat_type],\n      'MIN' => $stat_array[$stat_type]['MIN'],\n      'MAX' => $stat_array[$stat_type]['MAX'],\n      'AVG' => $stat_array[$stat_type]['AVG'],\n    ));\n    foreach($stat_type_data as $stat_entry) {\n      $template->assign_block_vars('stat.entry', $stat_entry);\n    }\n  }\n\n\n  if($same_user) {\n    rpg_level_up($user, RPG_STRUCTURE);\n    rpg_level_up($user, RPG_RAID);\n    rpg_level_up($user, RPG_TECH);\n    rpg_level_up($user, RPG_EXPLORE);\n\n\n    /*\n    // -----------------------------------------------------------------------------------------------\n    // News Frame ...\n    if ($config->game_news_overview)\n    {\n      nws_render($template, \"WHERE UNIX_TIMESTAMP(`tsTimeStamp`)<=\" . SN_TIME_NOW . \"\", $config->game_news_overview);\n    }\n    */\n  }\n\n\n  $template->assign_vars(array(\n    'USERS_TOTAL'          => $config->users_amount,\n\n    'USER_ID'              => $user_id,\n    'user_username'        => player_nick_render_to_html($user_data, true),\n    // 'user_gender'             => $user_data['gender'] == 'F' ? 'female' : 'male',\n    'USER_AVATAR'          => $user_data['avatar'],\n    'VACATION'             => $user_data['vacation'],\n    'GENDER_TEXT'          => $lang['sys_gender_list'][$user_data['gender']],\n\n    'PLAYER_RANK_NUMBER'   => $playerRank = SN::$gc->playerLevelHelper->getPointLevel($user_data['total_points'], $user_data['authlevel']),\n    'PLAYER_RANK_NAME'     => $lang['ranks'][$playerRank],\n\n    'NEW_MESSAGES'         => $user_data['new_message'],\n    'REGISTRATION_DATE'    => date(FMT_DATE_TIME, $user_data['register_time']),\n\n    'builder_xp'           => HelperString::numberFloorAndFormat($user_data['xpminier']),\n    'builder_lvl'          => HelperString::numberFloorAndFormat($user_data['lvl_minier']),\n    'builder_lvl_st'       => HelperString::numberFloorAndFormat(rpg_get_miner_xp($user_data['lvl_minier'])),\n    'builder_lvl_up'       => HelperString::numberFloorAndFormat(rpg_get_miner_xp($user_data['lvl_minier']+1)),\n    'raid_xp'              => HelperString::numberFloorAndFormat($user_data['xpraid']),\n    'raid_lvl'             => HelperString::numberFloorAndFormat($user_data['lvl_raid']),\n    'raid_lvl_up'          => HelperString::numberFloorAndFormat(rpg_get_raider_xp($user_data['lvl_raid']+1)),\n    'raids'                => HelperString::numberFloorAndFormat($user_data['raids']),\n    'raidswin'             => HelperString::numberFloorAndFormat($user_data['raidswin']),\n    'raidsloose'           => HelperString::numberFloorAndFormat($user_data['raidsloose']),\n    'tech_xp'              => HelperString::numberFloorAndFormat($user_data['player_rpg_tech_xp']),\n    'tech_lvl'             => HelperString::numberFloorAndFormat($user_data['player_rpg_tech_level']),\n    'tech_lvl_st'          => HelperString::numberFloorAndFormat(rpg_get_tech_xp($user_data['player_rpg_tech_level'])),\n    'tech_lvl_up'          => HelperString::numberFloorAndFormat(rpg_get_tech_xp($user_data['player_rpg_tech_level']+1)),\n\n    'explore_xp'           => HelperString::numberFloorAndFormat($user_data['player_rpg_explore_xp']),\n    'explore_lvl'          => HelperString::numberFloorAndFormat($user_data['player_rpg_explore_level']),\n    'explore_lvl_st'       => HelperString::numberFloorAndFormat(rpg_get_explore_xp($user_data['player_rpg_explore_level'])),\n    'explore_lvl_up'       => HelperString::numberFloorAndFormat(rpg_get_explore_xp($user_data['player_rpg_explore_level']+1)),\n\n    'build_points'         => HelperString::numberFloorAndFormat( $StatRecord['build_points'] ),\n    'tech_points'          => HelperString::numberFloorAndFormat( $StatRecord['tech_points'] ),\n    'fleet_points'         => HelperString::numberFloorAndFormat( $StatRecord['fleet_points'] ),\n    'defs_points'          => HelperString::numberFloorAndFormat( $StatRecord['defs_points'] ),\n    'res_points'           => HelperString::numberFloorAndFormat( $StatRecord['res_points'] ),\n    'total_points'         => HelperString::numberFloorAndFormat( $StatRecord['total_points'] ),\n    'user_rank'            => $StatRecord['total_rank'],\n    'RANK_DIFF'            => $StatRecord['total_old_rank'] - $StatRecord['total_rank'],\n\n    'STAT_COUNT'           => $stat_count,\n    'STAT_SPAN'            => $stat_count + 1,\n\n//    'GAME_NEWS_OVERVIEW'   => $config->game_news_overview,\n\n    'SAME_USER'            => $same_user,\n  ));\n\n  return $template;\n}\n"
  },
  {
    "path": "includes/pages/options.php",
    "content": "<?php\n\nuse DBAL\\db_mysql;\nuse DBAL\\DbQuery;\nuse Fleet\\DbFleetStatic;\nuse Old\\Avatar;\nuse Planet\\DBStaticPlanet;\nuse Player\\playerTimeDiff;\n\n/**\n * options.php\n *\n * @copyright (c) 2010-2017 by Gorlum for http://supernova.ws\n */\n\nfunction sn_options_model() {\n  global $user, $template_result;\n\n  $language_new = sys_get_param_str('langer', $user['lang']);\n  if ($language_new != $user['lang']) {\n    SN::$lang->lng_switch($language_new);\n  }\n\n  lng_include('options');\n  lng_include('messages');\n\n  sys_user_options_unpack($user);\n\n  $savedOk = false;\n//  if (sys_get_param_str('mode') == 'change') {\n  if (sys_get_param_str('save_settings')) {\n    if (!is_array($template_result['.']['result'])) {\n      $template_result['.']['result'] = [];\n    }\n\n    $user = sn_options_admin_protection($user);\n    $user = sn_options_vacation($user);\n    $user = sn_options_gender($user);\n    $user = sn_options_change_birthday($user);\n    $user = sn_options_deprecated($user);\n    sn_options_player_standard();\n\n    $template_result['.']['result'][] = sn_options_change_password();\n    list($user, $usernameResult) = sn_options_change_username($user);\n    $template_result['.']['result'] = array_merge($template_result['.']['result'], $usernameResult);\n\n    playerTimeDiff::sn_options_timediff(\n      sys_get_param_int('PLAYER_OPTION_TIME_DIFF'),\n      sys_get_param_int('PLAYER_OPTION_TIME_DIFF_FORCED'),\n      sys_get_param_int('opt_time_diff_clear')\n    );\n\n    $avatar_upload_result = Avatar::sys_avatar_upload($user['id'], $user['avatar']);\n    $template_result['.']['result'][] = $avatar_upload_result;\n\n    $user['email'] = sys_get_param_str('db_email');\n    SN::$gc->theUser->setSkinName(sys_get_param_str('skin_name'));\n    $user['lang'] = sys_get_param_str('langer', $user['lang']);\n    $user['design'] = sys_get_param_int('design');\n    $user['noipcheck'] = sys_get_param_int('noipcheck');\n    $user['deltime'] = !sys_get_param_int('deltime') ? 0 : ($user['deltime'] ? $user['deltime'] : SN_TIME_NOW + SN::$config->player_delete_time);\n\n    DbQuery::build(SN::$db)\n      ->setTable('users')\n      ->setValues([\n        'email'                    => $user['email'],\n        'lang'                     => $user['lang'],\n        'avatar'                   => $user['avatar'],\n        'design'                   => $user['design'],\n        'noipcheck'                => $user['noipcheck'],\n        'deltime'                  => $user['deltime'],\n        'vacation'                 => $user['vacation'],\n        'gender'                   => $user['gender'],\n        'skin'                     => SN::$gc->theUser->getSkinName(),\n        'user_birthday'            => $user['user_birthday'],\n        'user_birthday_celebrated' => $user['user_birthday_celebrated'],\n        'options'                  => $user['options'],\n      ])\n      ->setWhereArray(['id' => $user['id']])\n      ->doUpdate();\n\n    $savedOk = true;\n  } elseif (sys_get_param_str('result') == 'ok') {\n    $savedOk = true;\n  }\n\n  if ($savedOk) {\n    $template_result['.']['result'][] = array(\n      'STATUS'  => ERR_NONE,\n      'MESSAGE' => SN::$lang['opt_msg_saved']\n    );\n  }\n}\n\n//-------------------------------\n\nfunction sn_options_view($template = null) {\n  global $lang, $template_result, $user, $planetrow, $user_option_list, $user_option_types, $sn_message_class_list, $config;\n\n  sys_user_vacation($user);\n\n  $FMT_DATE = preg_replace(array('/d/', '/m/', '/Y/'), array('DD', 'MM', 'YYYY'), FMT_DATE);\n\n  $template = SnTemplate::gettemplate('options', $template);\n\n  $dir = dir(SN_ROOT_PHYSICAL . 'skins');\n  while (($entry = $dir->read()) !== false) {\n    if (\n      is_dir(\"skins/{$entry}\")\n      && $entry[0] != '.' && (\n        file_exists(\"skins/{$entry}/skin.ini\")\n        || file_exists(\"skins/{$entry}/skin.css\")\n      )\n    ) {\n      $template_result['.']['skin_list'][] = array(\n        'VALUE'    => $entry,\n        'NAME'     => $entry,\n        'SELECTED' => SN::$gc->theUser->getSkinName() == $entry,\n      );\n    }\n  }\n  $dir->close();\n\n  $ignores = SN::$gc->ignores->getIgnores($user['id'], true);\n  $template_result['.']['ignores'] = $ignores;\n\n  foreach ($lang['opt_planet_sort_options'] as $key => &$value) {\n    $template_result['.']['planet_sort_options'][] = array(\n      'VALUE'    => $key,\n      'NAME'     => $value,\n      'SELECTED' => SN::$user_options[PLAYER_OPTION_PLANET_SORT] == $key,\n    );\n  }\n\n  foreach ($lang['sys_gender_list'] as $key => $value) {\n    $template_result['.']['gender_list'][] = array(\n      'VALUE'    => $key,\n      'NAME'     => $value,\n      'SELECTED' => $user['gender'] == $key,\n    );\n  }\n\n  $lang_list = lng_get_list();\n  foreach ($lang_list as $lang_id => $lang_data) {\n    $template_result['.']['languages'][] = array(\n      'VALUE'    => $lang_id,\n      'NAME'     => $lang_data['LANG_NAME_NATIVE'],\n      'SELECTED' => $lang_id == $user['lang'],\n    );\n  }\n\n\n  if (isset($lang['menu_customize_show_hide_button_state'])) {\n    foreach ($lang['menu_customize_show_hide_button_state'] as $key => $value) {\n      $template->assign_block_vars('menu_customize_show_hide_button_state', array(\n        'ID'   => $key,\n        'NAME' => $value,\n      ));\n    }\n  }\n\n  $str_date_format = \"%3$02d %2$0s %1$04d {$lang['top_of_year']} %4$02d:%5$02d:%6$02d\";\n  $time_now_parsed = getdate($user['deltime']);\n\n  sn_options_add_standard($template);\n\n  $template->assign_vars([\n    'USER_ID' => $user['id'],\n\n    'ACCOUNT_NAME' => sys_safe_output(SN::$auth->account->account_name),\n\n    'USER_AUTHLEVEL' => $user['authlevel'],\n\n    'menu_customize_show_hide_button'     => SN::$user_options[PLAYER_OPTION_MENU_HIDE_SHOW_BUTTON],\n    'PLAYER_OPTION_MENU_SHOW_ON_BUTTON'   => SN::$user_options[PLAYER_OPTION_MENU_SHOW_ON_BUTTON],\n    'PLAYER_OPTION_MENU_HIDE_ON_BUTTON'   => SN::$user_options[PLAYER_OPTION_MENU_HIDE_ON_BUTTON],\n    'PLAYER_OPTION_MENU_HIDE_ON_LEAVE'    => SN::$user_options[PLAYER_OPTION_MENU_HIDE_ON_LEAVE],\n    'PLAYER_OPTION_MENU_UNPIN_ABSOLUTE'   => SN::$user_options[PLAYER_OPTION_MENU_UNPIN_ABSOLUTE],\n    'PLAYER_OPTION_MENU_ITEMS_AS_BUTTONS' => SN::$user_options[PLAYER_OPTION_MENU_ITEMS_AS_BUTTONS],\n    'PLAYER_OPTION_MENU_WHITE_TEXT'       => SN::$user_options[PLAYER_OPTION_MENU_WHITE_TEXT],\n    'PLAYER_OPTION_MENU_OLD'              => SN::$user_options[PLAYER_OPTION_MENU_OLD],\n\n    'PLAYER_OPTION_TUTORIAL_CURRENT_ID' => PLAYER_OPTION_TUTORIAL_CURRENT,\n\n    'ADM_PROTECT_PLANETS' => $user['authlevel'] >= 3,\n    'opt_usern_data'      => htmlspecialchars($user['username']),\n    'opt_mail1_data'      => $user['email'],\n    'opt_mail2_data'      => sys_safe_output(SN::$auth->account->account_email),\n\n    'PLAYER_OPTION_PLANET_SORT_INVERSE'    => SN::$user_options[PLAYER_OPTION_PLANET_SORT_INVERSE],\n    'PLAYER_OPTION_FLEET_SPY_DEFAULT'      => SN::$user_options[PLAYER_OPTION_FLEET_SPY_DEFAULT],\n    'PLAYER_OPTION_TOOLTIP_DELAY'          => SN::$user_options[PLAYER_OPTION_TOOLTIP_DELAY],\n    'PLAYER_OPTION_BUILD_AUTOCONVERT_HIDE' => SN::$user_options[PLAYER_OPTION_BUILD_AUTOCONVERT_HIDE],\n\n    'opt_sskin_data' => ($user['design'] == 1) ? \" checked='checked'\" : '',\n    'opt_noipc_data' => ($user['noipcheck'] == 1) ? \" checked='checked'\" : '',\n    'deltime'        => $user['deltime'],\n    'deltime_text'   => sprintf($str_date_format, $time_now_parsed['year'], $lang['months'][$time_now_parsed['mon']], $time_now_parsed['mday'],\n      $time_now_parsed['hours'], $time_now_parsed['minutes'], $time_now_parsed['seconds']\n    ),\n\n    'opt_avatar' => $user['avatar'],\n\n    'config_game_email_pm' => $config->game_email_pm,\n\n    'user_settings_esp'        => SN::$user_options[PLAYER_OPTION_UNIVERSE_ICON_SPYING],\n    'user_settings_mis'        => SN::$user_options[PLAYER_OPTION_UNIVERSE_ICON_MISSILE],\n    'user_settings_wri'        => SN::$user_options[PLAYER_OPTION_UNIVERSE_ICON_PM],\n    'user_settings_statistics' => SN::$user_options[PLAYER_OPTION_UNIVERSE_ICON_STATS],\n    'user_settings_info'       => SN::$user_options[PLAYER_OPTION_UNIVERSE_ICON_PROFILE],\n    'user_settings_bud'        => SN::$user_options[PLAYER_OPTION_UNIVERSE_ICON_BUDDY],\n\n    'user_time_diff_forced' => playerTimeDiff::getTimeDiffForced(),\n\n    'adm_pl_prot' => $user['admin_protection'],\n\n    'user_birthday' => $user['user_birthday'],\n    'GENDER'        => $user['gender'],\n    'GENDER_TEXT'   => $lang['sys_gender_list'][$user['gender']],\n    'FMT_DATE'      => $FMT_DATE,\n    'JS_FMT_DATE'   => js_safe_string($FMT_DATE),\n\n    'USER_VACATION_DISABLE' => $config->user_vacation_disable,\n    'VACATION_NEXT'         => $user['vacation_next'],\n    'VACATION_NEXT_TEXT'    => date(FMT_DATE_TIME, $user['vacation_next']),\n    'VACATION_TIMEOUT'      => $user['vacation_next'] - SN_TIME_NOW > 0 ? $user['vacation_next'] - SN_TIME_NOW : 0,\n    'SN_TIME_NOW'           => SN_TIME_NOW,\n\n    'SERVER_SEND_EMAIL' => $config->game_email_pm,\n\n    'SERVER_NAME_CHANGE'         => $config->game_user_changename != SERVER_PLAYER_NAME_CHANGE_NONE,\n    'SERVER_NAME_CHANGE_PAY'     => $config->game_user_changename == SERVER_PLAYER_NAME_CHANGE_PAY,\n    'SERVER_NAME_CHANGE_ENABLED' => $config->game_user_changename == SERVER_PLAYER_NAME_CHANGE_FREE || ($config->game_user_changename == SERVER_PLAYER_NAME_CHANGE_PAY && mrc_get_level($user, $planetrow, RES_DARK_MATTER) >= $config->game_user_changename_cost),\n\n    'DARK_MATTER' => prettyNumberStyledCompare($config->game_user_changename_cost, mrc_get_level($user, $planetrow, RES_DARK_MATTER)),\n\n    'GROUP_DESIGN_BLOCK_TUTORIAL'      => GROUP_DESIGN_BLOCK_TUTORIAL,\n    'GROUP_DESIGN_BLOCK_FLEET_COMPOSE' => GROUP_DESIGN_BLOCK_FLEET_COMPOSE,\n    'GROUP_DESIGN_BLOCK_UNIVERSE'      => GROUP_DESIGN_BLOCK_UNIVERSE,\n    'GROUP_DESIGN_BLOCK_NAVBAR'        => GROUP_DESIGN_BLOCK_NAVBAR,\n    'GROUP_DESIGN_BLOCK_RESOURCEBAR'   => GROUP_DESIGN_BLOCK_RESOURCEBAR,\n    'GROUP_DESIGN_BLOCK_PLANET_SORT'   => GROUP_DESIGN_BLOCK_PLANET_SORT,\n    'GROUP_DESIGN_BLOCK_COMMON_ONE'    => GROUP_DESIGN_BLOCK_COMMON_ONE,\n    'GROUP_DESIGN_BLOCK_COMMON_TWO'    => GROUP_DESIGN_BLOCK_COMMON_TWO,\n\n    'PAGE_HEADER' => $lang['opt_header'],\n  ]);\n\n  foreach ($user_option_list as $option_group_id => $option_group) {\n    if ($option_group_id == OPT_MESSAGE) {\n      foreach ($sn_message_class_list as $message_class_id => $message_class_data) {\n        if ($message_class_data['switchable'] || ($message_class_data['email'] && $config->game_email_pm)) {\n          $option_name = $message_class_data['name'];\n\n          $template->assign_block_vars(\"options_{$option_group_id}\", array(\n            'NAME'  => $message_class_data['name'],\n            'TEXT'  => $lang['msg_class'][$message_class_id], // $lang['opt_custom'][$option_name],\n            'PM'    => $message_class_data['switchable'] ? $user[\"opt_{$option_name}\"] : -1,\n            'EMAIL' => $message_class_data['email'] && $config->game_email_pm ? $user[\"opt_email_{$option_name}\"] : -1,\n          ));\n        }\n      }\n    } else {\n      foreach ($option_group as $option_name => $option_value) {\n        if (array_key_exists($option_name, $user_option_types)) {\n          $option_type = $user_option_types[$option_name];\n        } else {\n          $option_type = 'switch';\n        }\n\n        $template->assign_block_vars(\"options_{$option_group_id}\", array(\n          'NAME'  => $option_name,\n          'TYPE'  => $option_type,\n          'TEXT'  => $lang['opt_custom'][$option_name],\n          'HINT'  => $lang['opt_custom'][\"{$option_name}_hint\"],\n          'VALUE' => $user[$option_name],\n        ));\n      }\n    }\n  }\n\n//  var_dump($template_result['.']['result']);\n//  var_dump($template->_tpldata);\n//\n  return $template;\n}\n\n//-------------------------------\n\n/**\n * @param $user\n *\n * @return array\n */\nfunction sn_options_gender($user) {\n  $gender = sys_get_param_int('gender', $user['gender']);\n  !isset(SN::$lang['sys_gender_list'][$gender]) ? $gender = $user['gender'] : false;\n  $user['gender'] = $user['gender'] == GENDER_UNKNOWN ? $gender : $user['gender'];\n\n  return $user;\n}\n\n/**\n * @param array $user\n *\n * @return array\n */\nfunction sn_options_change_birthday($user) {\n  $user_birthday = sys_get_param_str_unsafe('user_birthday');\n  $FMT_DATE = preg_replace(array('/d/', '/m/', '/Y/'), array('DD', 'MM', 'YYYY'), FMT_DATE);\n\n  if ($user['birthday'] || empty($user_birthday) || $user_birthday == $FMT_DATE) {\n    return $user;\n  }\n\n  try {\n    // Some black magic to parse any valid date format - those that contains all three \"d\", \"m\" and \"Y\" and any of the delimeters \"\\\", \"/\", \".\", \"-\"\n    $pos['d'] = strpos(FMT_DATE, 'd');\n    $pos['m'] = strpos(FMT_DATE, 'm');\n    $pos['Y'] = strpos(FMT_DATE, 'Y');\n    asort($pos);\n    $i = 0;\n    foreach ($pos as &$position) {\n      $position = ++$i;\n    }\n\n    $regexp = \"/\" . preg_replace(array('/\\\\\\\\/', '/\\//', '/\\./', '/\\-/', '/d/', '/m/', '/Y/'), array('\\\\\\\\\\\\', '\\/', '\\.', '\\-', '(\\d?\\d)', '(\\d?\\d)', '(\\d{4})'), FMT_DATE) . \"/\";\n    if (!preg_match($regexp, $user_birthday, $match)) {\n      throw new Exception();\n    }\n\n    if (!checkdate($match[$pos['m']], $match[$pos['d']], $match[$pos['Y']])) {\n      throw new Exception();\n    }\n\n    $user_birthday_new_unescaped = \"{$match[$pos['Y']]}-{$match[$pos['m']]}-{$match[$pos['d']]}\";\n    $user['user_birthday'] = $user_birthday_new_unescaped;\n    // EOF black magic! Now we have valid SQL date in $user['user_birthday'] - independent of date format\n\n    $year = date('Y', SN_TIME_NOW);\n    if (mktime(0, 0, 0, $match[$pos['m']], $match[$pos['d']], $year) > SN_TIME_NOW) {\n      $year--;\n    }\n    $user['user_birthday_celebrated'] = \"{$year}-{$match[$pos['m']]}-{$match[$pos['d']]}\";\n  } catch (exception $e) {\n    $user['user_birthday'] = null;\n    $user['user_birthday_celebrated'] = null;\n  }\n\n  return $user;\n}\n\n/**\n * @return array\n */\nfunction sn_options_change_password() {\n  $result = [];\n  if (!($new_password = sys_get_param('newpass1'))) {\n    return $result;\n  }\n\n  try {\n    if ($new_password != sys_get_param('newpass2')) {\n      throw new Exception('opt_err_pass_unmatched', ERR_WARNING);\n    }\n\n    if (!SN::$auth->password_change(sys_get_param('db_password'), $new_password)) {\n      throw new Exception('opt_err_pass_wrong', ERR_WARNING);\n    }\n\n    throw new Exception('opt_msg_pass_changed', ERR_NONE);\n  } catch (Exception $e) {\n    $result = [\n      'STATUS'  => in_array($e->getCode(), [ERR_NONE, ERR_WARNING, ERR_ERROR]) ? $e->getCode() : ERR_ERROR,\n      'MESSAGE' => SN::$lang[$e->getMessage()],\n    ];\n  }\n\n  return $result;\n}\n\nfunction sn_options_player_standard() {\n  $player_options = sys_get_param('options');\n  if (empty($player_options)) {\n    return;\n  }\n\n  if ($player_options[PLAYER_OPTION_TUTORIAL_CURRENT]) {\n    $player_options[PLAYER_OPTION_TUTORIAL_CURRENT] = SN::$config->tutorial_first_item;\n    $player_options[PLAYER_OPTION_TUTORIAL_FINISHED] = 0;\n  } else {\n    unset($player_options[PLAYER_OPTION_TUTORIAL_CURRENT]);\n  }\n\n  array_walk($player_options, function (&$value) {\n    // TODO - Когда будет больше параметров - сделать больше проверок\n    $value = intval($value);\n  });\n  SN::$user_options->offsetSet($player_options);\n}\n\n/**\n * @param array $user\n *\n * @return array\n */\nfunction sn_options_change_username($user) {\n  $config = SN::$config;\n  $lang = SN::$lang;\n\n  $result = [];\n\n  $username = substr(sys_get_param_str_unsafe('username'), 0, 32);\n  if (\n    empty($username)\n    || $user['username'] == $username\n    || $config->game_user_changename == SERVER_PLAYER_NAME_CHANGE_NONE\n    || !sys_get_param_int('username_confirm')\n    || strpbrk($username, LOGIN_REGISTER_CHARACTERS_PROHIBITED)\n  ) {\n    return [$user, $result];\n  }\n\n  // проверка на корректность\n  db_mysql::db_transaction_start();\n  $username_safe = SN::$db->db_escape($username);\n  /** @noinspection SqlResolve */\n  $name_check = doquery(\"SELECT * FROM `{{player_name_history}}` WHERE `player_name` LIKE \\\"{$username_safe}\\\" LIMIT 1 FOR UPDATE;\", true);\n  if (empty($name_check['player_id']) || $name_check['player_id'] == $user['id']) {\n    $user = db_user_by_id($user['id'], true);\n    switch ($config->game_user_changename) {\n      /** @noinspection PhpMissingBreakStatementInspection */\n      case SERVER_PLAYER_NAME_CHANGE_PAY:\n        if (mrc_get_level($user, [], RES_DARK_MATTER) < $config->game_user_changename_cost) {\n          $result[] = [\n            'STATUS'  => ERR_ERROR,\n            'MESSAGE' => $lang['opt_msg_name_change_err_no_dm'],\n          ];\n          break;\n        }\n        rpg_points_change(\n          $user['id'],\n          RPG_NAME_CHANGE,\n          -$config->game_user_changename_cost,\n          vsprintf('Пользователь ID %1$d сменил имя с \"%2$s\" на \"%3$s\"', [$user['id'], $user['username'], $username,])\n        );\n\n      case SERVER_PLAYER_NAME_CHANGE_FREE:\n        db_user_set_by_id($user['id'], \"`username` = '{$username_safe}'\");\n        /** @noinspection SqlResolve */\n        doquery(\"REPLACE INTO `{{player_name_history}}` SET `player_id` = {$user['id']}, `player_name` = '{$username_safe}'\");\n        // TODO: Change cookie to not force user relogin\n        // sn_setcookie(SN_COOKIE, '', time() - PERIOD_WEEK, SN_ROOT_RELATIVE);\n        $result[] = [\n          'STATUS'  => ERR_NONE,\n          'MESSAGE' => $lang['opt_msg_name_changed']\n        ];\n        $user['username'] = $username;\n      break;\n    }\n  } else {\n    $result[] = [\n      'STATUS'  => ERR_ERROR,\n      'MESSAGE' => $lang['opt_msg_name_change_err_used_name'],\n    ];\n  }\n  db_mysql::db_transaction_commit();\n\n  return [$user, $result];\n}\n\n/**\n * Set old options\n *\n * @param array $user\n *\n * @return array\n * @deprecated\n */\nfunction sn_options_deprecated($user) {\n  global $user_option_list;\n\n  foreach ($user_option_list as $option_group_id => $option_group) {\n    foreach ($option_group as $option_name => $option_value) {\n      if ($user[$option_name] !== null) {\n        $user[$option_name] = sys_get_param_str($option_name);\n      } else {\n        $user[$option_name] = $option_value;\n      }\n    }\n  }\n\n  sys_user_options_pack($user);\n\n  return $user;\n}\n\n/**\n * @param array $user\n *\n * @return array\n */\nfunction sn_options_admin_protection($user) {\n  if ($user['authlevel'] <= AUTH_LEVEL_REGISTERED) {\n    return $user;\n  }\n\n  $planet_protection = sys_get_param_int('adm_pl_prot') ? $user['authlevel'] : 0;\n  DBStaticPlanet::db_planet_set_by_owner($user['id'], \"`id_level` = '{$planet_protection}'\");\n  db_user_set_by_id($user['id'], \"`admin_protection` = '{$planet_protection}'\");\n  $user['admin_protection'] = $planet_protection;\n\n  return $user;\n}\n\n/**\n * @param array $user\n *\n * @return array\n */\nfunction sn_options_vacation($user) {\n  $config = SN::$config;\n  $lang = SN::$lang;\n\n  if (!sys_get_param_int('vacation') || $config->user_vacation_disable) {\n    return $user;\n  }\n\n  db_mysql::db_transaction_start();\n  if ($user['authlevel'] < AUTH_LEVEL_ADMINISTRATOR) {\n    if ($user['vacation_next'] > SN_TIME_NOW) {\n      SnTemplate::messageBox($lang['opt_vacation_err_timeout'], $lang['Error'], 'index.php?page=options', 5);\n      die();\n    }\n\n    if (DbFleetStatic::fleet_count_flying($user['id'])) {\n      SnTemplate::messageBox($lang['opt_vacation_err_your_fleet'], $lang['Error'], 'index.php?page=options', 5);\n      die();\n    }\n\n    $que = que_get($user['id'], false);\n    if (!empty($que)) {\n      SnTemplate::messageBox($lang['opt_vacation_err_que'], $lang['Error'], 'index.php?page=options', 5);\n      die();\n    }\n\n    $query = SN::db_get_record_list(LOC_PLANET, \"`id_owner` = {$user['id']}\");\n    foreach ($query as $planet) {\n      DBStaticPlanet::db_planet_set_by_id($planet['id'],\n        \"last_update = \" . SN_TIME_NOW . \", energy_used = '0', energy_max = '0',\n        metal_perhour = '{$config->metal_basic_income}', crystal_perhour = '{$config->crystal_basic_income}', deuterium_perhour = '{$config->deuterium_basic_income}',\n        metal_mine_porcent = '0', crystal_mine_porcent = '0', deuterium_sintetizer_porcent = '0', solar_plant_porcent = '0',\n        fusion_plant_porcent = '0', solar_satelit_porcent = '0', ship_sattelite_sloth_porcent = 0\"\n      );\n    }\n    $user['vacation'] = SN_TIME_NOW + $config->player_vacation_time;\n  } else {\n    $user['vacation'] = SN_TIME_NOW;\n  }\n  db_mysql::db_transaction_commit();\n\n  return $user;\n}\n\n\n/**\n * @param template $template\n * @param string   $blockName\n * @param int      $blockId\n * @param int[]    $optionsNavBar\n * @param array    $options\n */\nfunction sn_options_render_block($template, $blockName, $blockId, $optionsNavBar, $options = []) {\n  $template->assign_block_vars('player_options', [\n    'ID'   => $blockId,\n    'NAME' => $blockName,\n  ]);\n\n  foreach ($optionsNavBar as $optionId) {\n    $template->assign_block_vars('player_options.option', [\n      'ID'         => $optionId,\n      'VALUE'      => SN::$user_options[$optionId],\n      'NAME'       => SN::$lang['opt_player_options'][$optionId],\n      'ALWAYS_OFF' => !empty($options[$optionId]['always_off']),\n      'CLASS'      => !empty($options[$optionId]['class']) ? $options[$optionId]['class'] : 'cell',\n    ]);\n  }\n}\n\n/**\n * @param $template\n */\nfunction sn_options_add_standard($template) {\n  sn_options_render_block($template, '', 5, [\n  ]);\n\n\n  // 8\n  sn_options_render_block($template, '', GROUP_DESIGN_BLOCK_COMMON_TWO,\n    [\n      PLAYER_OPTION_SOUND_ENABLED,\n      PLAYER_OPTION_ANIMATION_DISABLED,\n      PLAYER_OPTION_PROGRESS_BARS_DISABLED,\n    ],\n    [\n      PLAYER_OPTION_SOUND_ENABLED          => ['class' => 'header'],\n      PLAYER_OPTION_ANIMATION_DISABLED     => ['class' => 'header'],\n      PLAYER_OPTION_PROGRESS_BARS_DISABLED => ['class' => 'header'],\n    ]\n  );\n  // 7\n  sn_options_render_block($template, '', GROUP_DESIGN_BLOCK_COMMON_ONE, [\n    PLAYER_OPTION_BUILD_AUTOCONVERT_HIDE,\n    PLAYER_OPTION_DESIGN_DISABLE_BORDERS,\n    PLAYER_OPTION_TECH_TREE_TABLE,\n  ]);\n  // 6\n  sn_options_render_block($template, '', GROUP_DESIGN_BLOCK_PLANET_SORT, [\n    PLAYER_OPTION_PLANET_SORT_INVERSE,\n  ]);\n  // 4\n  sn_options_render_block($template, SN::$lang['opt_navbar_resourcebar_description'], GROUP_DESIGN_BLOCK_RESOURCEBAR, [\n    PLAYER_OPTION_NAVBAR_PLANET_VERTICAL,\n    PLAYER_OPTION_NAVBAR_PLANET_DISABLE_STORAGE,\n    PLAYER_OPTION_NAVBAR_PLANET_OLD,\n  ]);\n  // 3\n  sn_options_render_block($template, SN::$lang['opt_navbar_buttons_title'], GROUP_DESIGN_BLOCK_NAVBAR, [\n    PLAYER_OPTION_NAVBAR_RESEARCH_WIDE,\n    PLAYER_OPTION_NAVBAR_DISABLE_RESEARCH,\n    PLAYER_OPTION_NAVBAR_DISABLE_PLANET,\n    PLAYER_OPTION_NAVBAR_DISABLE_HANGAR,\n    PLAYER_OPTION_NAVBAR_DISABLE_DEFENSE,\n    PLAYER_OPTION_NAVBAR_DISABLE_EXPEDITIONS,\n    PLAYER_OPTION_NAVBAR_DISABLE_FLYING_FLEETS,\n    PLAYER_OPTION_NAVBAR_DISABLE_QUESTS,\n    PLAYER_OPTION_NAVBAR_DISABLE_META_MATTER,\n  ]);\n  // 2\n  sn_options_render_block($template, SN::$lang['galaxyvision_options'], GROUP_DESIGN_BLOCK_UNIVERSE, [\n    PLAYER_OPTION_UNIVERSE_OLD,\n    PLAYER_OPTION_UNIVERSE_DISABLE_COLONIZE,\n  ]);\n  // 1\n  sn_options_render_block($template, SN::$lang['option_fleet_send'], GROUP_DESIGN_BLOCK_FLEET_COMPOSE, [\n    PLAYER_OPTION_FLEET_SHIP_SELECT_OLD,\n    PLAYER_OPTION_FLEET_SHIP_HIDE_CONSUMPTION,\n    PLAYER_OPTION_FLEET_SHIP_HIDE_SPEED,\n    PLAYER_OPTION_FLEET_SHIP_HIDE_CAPACITY,\n  ]);\n  // 0\n  sn_options_render_block($template, SN::$lang['opt_tutorial'], GROUP_DESIGN_BLOCK_TUTORIAL, [\n    PLAYER_OPTION_TUTORIAL_DISABLED,\n    // PLAYER_OPTION_TUTORIAL_WINDOWED,\n    PLAYER_OPTION_TUTORIAL_CURRENT,\n  ], [PLAYER_OPTION_TUTORIAL_CURRENT => ['always_off' => true]]);\n}\n"
  },
  {
    "path": "includes/pages/techtree.php",
    "content": "<?php\n\n/**\n * techtree.php\n *\n * version 2.0 copyright (c) 2012 by Gorlum for http://supernova.ws\n */\n\nfunction sn_techtree_view($template = null)\n{\n  global $lang, $user, $planetrow;\n\n  $tech_tree = array();\n  foreach(get_unit_param('techtree') as $unit_group_id => $unit_list)\n  {\n    $tech_tree[] = array(\n      'NAME' => $lang['tech'][$unit_group_id],\n      'GROUP_ID' => $unit_group_id,\n    );\n\n    foreach($unit_list as $unit_id)\n    {\n      $sn_data_unit = get_unit_param($unit_id);\n      $level_basic = $sn_data_unit[P_STACKABLE] ? 0 : mrc_get_level($user, $planetrow, $unit_id, false, true);\n      $unit_level = $sn_data_unit[P_STACKABLE] ? 0 : mrc_get_level($user, $planetrow, $unit_id);\n      $rendered_info = array(\n        'ID' => $unit_id,\n        'NAME' => $lang['tech'][$unit_id],\n        'LEVEL' => $unit_level,\n        'LEVEL_BASIC' => $level_basic,\n        'LEVEL_BONUS' => max(0, $unit_level - $level_basic),\n        'LEVEL_MAX' => $sn_data_unit['max'],\n      );\n\n      $rendered_info['.'][TPL_BLOCK_REQUIRE] = unit_requirements_render($user, $planetrow, $unit_id);\n      $rendered_info['.']['grants'] = unit_requirements_render($user, $planetrow, $unit_id, P_UNIT_GRANTS);\n\n      $tech_tree[] = $rendered_info;\n    }\n  }\n\n  $template = SnTemplate::gettemplate('techtree', $template);\n  $template_result['.']['techtree'] = $tech_tree;\n  $template->assign_recursive($template_result);\n\n  $template->assign_vars(array(\n    'PAGE_HEADER' => $lang['tech'][UNIT_TECHNOLOGIES],\n    'PLAYER_OPTION_TECH_TREE_TABLE' => SN::$user_options[PLAYER_OPTION_TECH_TREE_TABLE],\n  ));\n\n  return $template;\n}\n"
  },
  {
    "path": "includes/update.php",
    "content": "<?php /** @noinspection SqlResolve */\n\n/** @noinspection PhpUnnecessaryCurlyVarSyntaxInspection */\n\nuse Core\\Updater;\n\n/**\n * update.php\n *\n * Automated DB upgrade system\n *\n * @package supernova\n */\n\nif (!defined('INIT')) {\n  die('Unauthorized access');\n}\n\nif (defined('IN_UPDATE')) {\n  die('Update already started');\n}\n\nconst IN_UPDATE = true;\n\nglobal $sn_cache, $debug, $sys_log_disabled;\n\n$updater = new Updater();\n\nswitch ($updater->new_version) {\n  /** @noinspection PhpMissingBreakStatementInspection */\n  case 40:\n    $updater->upd_log_version_update();\n    $updater->transactionStart();\n\n    if (!$updater->isTableExists('festival')) {\n      $updater->upd_create_table('festival',\n        [\n          \"`id` smallint(5) unsigned NOT NULL AUTO_INCREMENT\",\n          \"`start` datetime NOT NULL COMMENT 'Festival start datetime'\",\n          \"`finish` datetime NOT NULL COMMENT 'Festival end datetime'\",\n          \"`name` varchar(255) COLLATE utf8_unicode_ci NOT NULL DEFAULT '' COMMENT 'Название акции/ивента'\",\n          \"PRIMARY KEY (`id`)\",\n          \"KEY `I_festival_date_range` (`start`,`finish`,`id`) USING BTREE\"\n        ],\n        \"ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci\"\n      );\n\n      $updater->upd_create_table('festival_highspot',\n        [\n          \"`id` int(10) unsigned NOT NULL AUTO_INCREMENT\",\n          \"`festival_id` smallint(5) unsigned DEFAULT NULL\",\n          \"`class` tinyint(3) unsigned NOT NULL DEFAULT '0' COMMENT 'Highspot class'\",\n          \"`start` datetime NOT NULL COMMENT 'Highspot start datetime'\",\n          \"`finish` datetime NOT NULL COMMENT 'Highspot end datetime'\",\n          \"`name` varchar(255) COLLATE utf8_unicode_ci NOT NULL DEFAULT ''\",\n          \"PRIMARY KEY (`id`)\",\n          \"KEY `I_highspot_order` (`start`,`finish`,`id`)\",\n          \"KEY `I_highspot_festival_id` (`festival_id`,`start`,`finish`,`id`) USING BTREE\",\n          \"CONSTRAINT `FK_highspot_festival_id` FOREIGN KEY (`festival_id`) REFERENCES `{{festival}}` (`id`) ON DELETE CASCADE ON UPDATE CASCADE\",\n        ],\n        \"ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci\"\n      );\n\n      $updater->upd_create_table('festival_highspot_activity',\n        [\n          \"`id` int(10) unsigned NOT NULL AUTO_INCREMENT\",\n          \"`highspot_id` int(10) unsigned DEFAULT NULL\",\n          \"`class` smallint(5) unsigned NOT NULL DEFAULT '0' COMMENT 'Класс события - ID модуля события'\",\n          \"`type` tinyint(1) unsigned NOT NULL DEFAULT '0' COMMENT 'Тип активити: 1 - триггер, 2 - хук'\",\n          \"`start` datetime NOT NULL COMMENT 'Запланированное время запуска'\",\n          \"`finish` datetime DEFAULT NULL COMMENT 'Реальное время запуска'\",\n          \"`params` text COLLATE utf8_unicode_ci NOT NULL COMMENT 'Параметры активити в виде сериализированного архива'\",\n          \"PRIMARY KEY (`id`)\",\n          \"KEY `I_festival_activity_order` (`start`,`finish`,`id`) USING BTREE\",\n          \"KEY `I_festival_activity_highspot_id` (`highspot_id`,`start`,`finish`,`id`) USING BTREE\",\n          \"CONSTRAINT `FK_festival_activity_highspot_id` FOREIGN KEY (`highspot_id`) REFERENCES `{{festival_highspot}}` (`id`) ON DELETE CASCADE ON UPDATE CASCADE\",\n        ],\n        \"ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci\"\n      );\n\n      /** @noinspection SpellCheckingInspection */\n      $updater->upd_create_table('festival_unit',\n        [\n          \"`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT\",\n          \"`highspot_id` int(10) unsigned DEFAULT NULL\",\n          \"`player_id` bigint(20) unsigned DEFAULT NULL\",\n          \"`unit_id` bigint(20) NOT NULL DEFAULT '0'\",\n          \"`unit_level` bigint(20) unsigned NOT NULL DEFAULT '0'\",\n          \"PRIMARY KEY (`id`)\",\n          \"KEY `I_festival_unit_player_id` (`player_id`,`highspot_id`) USING BTREE\",\n          \"KEY `I_festival_unit_highspot_id` (`highspot_id`,`unit_id`,`player_id`) USING BTREE\",\n          \"CONSTRAINT `FK_festival_unit_hispot` FOREIGN KEY (`highspot_id`) REFERENCES `{{festival_highspot}}` (`id`) ON DELETE CASCADE ON UPDATE CASCADE\",\n          \"CONSTRAINT `FK_festival_unit_player` FOREIGN KEY (`player_id`) REFERENCES `{{users}}` (`id`) ON DELETE CASCADE ON UPDATE CASCADE\",\n        ],\n        \"ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;\"\n      );\n\n      /** @noinspection SpellCheckingInspection */\n      $updater->upd_create_table('festival_unit_log',\n        [\n          \"`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT\",\n          \"`highspot_id` int(10) unsigned DEFAULT NULL\",\n          \"`player_id` bigint(20) unsigned NOT NULL COMMENT 'User ID'\",\n          \"`player_name` varchar(32) NOT NULL DEFAULT ''\",\n          \"`unit_id` bigint(20) unsigned NOT NULL DEFAULT '0'\",\n          \"`timestamp` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP\",\n          \"`unit_level` int(11) NOT NULL DEFAULT '0'\",\n          \"`unit_image` varchar(255) NOT NULL DEFAULT ''\",\n          \"PRIMARY KEY (`id`)\",\n          \"KEY `I_festival_unit_log_player_id` (`player_id`,`highspot_id`,`id`) USING BTREE\",\n          \"KEY `I_festival_unit_log_highspot_id` (`highspot_id`,`unit_id`,`player_id`) USING BTREE\",\n          \"CONSTRAINT `FK_festival_unit_log_hispot` FOREIGN KEY (`highspot_id`) REFERENCES `{{festival_highspot}}` (`id`) ON DELETE CASCADE ON UPDATE CASCADE\",\n          \"CONSTRAINT `FK_festival_unit_log_player` FOREIGN KEY (`player_id`) REFERENCES `{{users}}` (`id`) ON DELETE CASCADE ON UPDATE CASCADE\",\n        ], \"ENGINE=InnoDB DEFAULT CHARSET=utf8;\"\n      );\n    }\n\n    // 2016-01-15 10:57:17 41a1.4\n    $updater->upd_alter_table(\n      'security_browser',\n      \"MODIFY COLUMN `browser_user_agent` VARCHAR(250) CHARSET utf8 COLLATE utf8_unicode_ci NOT NULL DEFAULT ''\",\n      $updater->getFieldDescription('security_browser', 'browser_user_agent')->Collation == 'latin1_bin'\n    );\n\n//    if ($updater->getIndexDescription('security_browser', 'I_browser_user_agent')['browser_user_agent']['Index_type'] == 'BTREE') {\n    if ($updater->getIndexDescription('security_browser', 'I_browser_user_agent')->Index_type == 'BTREE') {\n      $updater->upd_alter_table('security_browser', \"DROP KEY `I_browser_user_agent`\", true);\n      $updater->upd_alter_table('security_browser', \"ADD KEY `I_browser_user_agent` (`browser_user_agent`) USING HASH\", true);\n    }\n\n    // 2016-12-03 20:36:46 41a61.0\n    if (!$updater->isTableExists('auth_vkontakte_account')) {\n      $updater->upd_create_table('auth_vkontakte_account',\n        [\n          \"`user_id` bigint(20) unsigned NOT NULL AUTO_INCREMENT\",\n          \"`access_token` varchar(250) NOT NULL DEFAULT ''\",\n          \"`expires_in` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP\",\n          \"`email` varchar(250) NOT NULL DEFAULT ''\",\n          \"`first_name` varchar(250) NOT NULL DEFAULT ''\",\n          \"`last_name` varchar(250) NOT NULL DEFAULT ''\",\n          \"`account_id` bigint(20) unsigned NULL COMMENT 'Account ID'\",\n          \"PRIMARY KEY (`user_id`)\",\n          \"CONSTRAINT `FK_vkontakte_account_id` FOREIGN KEY (`account_id`) REFERENCES `{{account}}` (`account_id`) ON DELETE CASCADE ON UPDATE CASCADE\"\n        ],\n        \"ENGINE=InnoDB DEFAULT CHARSET=utf8\"\n      );\n    }\n\n    // 2017-02-03 16:10:49 41b1\n    $updater->new_version = 41;\n    $updater->transactionCommit();\n\n  /** @noinspection PhpMissingBreakStatementInspection */\n  case 41:\n    $updater->upd_log_version_update();\n    $updater->transactionStart();\n\n    // 2017-02-07 09:43:45 42a0\n    $updater->upd_check_key('game_news_overview_show', 2 * 7 * 24 * 60 * 60, !isset(SN::$gc->config->game_news_overview_show));\n\n    // 2017-02-13 13:44:18 42a17\n    $updater->upd_check_key('tutorial_first_item', 1, !isset(SN::$gc->config->tutorial_first_item));\n\n    // 2017-02-14 17:13:45 42a20.11\n    // TODO - REMOVE DROP TABLE AND CONDITION!\n    if (!$updater->isIndexExists('text', 'I_text_next_alt')) {\n      $updater->upd_drop_table('text');\n      $updater->upd_create_table('text',\n        [\n          \"`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT\",\n          \"`parent` bigint(20) unsigned DEFAULT NULL COMMENT 'Parent record. NULL - no parent'\",\n          \"`context` bigint(20) unsigned DEFAULT NULL COMMENT 'Tutorial context. NULL - main screen'\",\n          \"`prev` bigint(20) unsigned DEFAULT NULL COMMENT 'Previous text part. NULL - first part'\",\n          \"`next` bigint(20) unsigned DEFAULT NULL COMMENT 'Next text part. NULL - final part'\",\n          \"`next_alt` bigint(20) unsigned DEFAULT NULL COMMENT 'Alternative next text part. NULL - no alternative'\",\n          \"`title` varchar(255) COLLATE utf8_unicode_ci NOT NULL DEFAULT '' COMMENT 'Text title'\",\n          \"`content` text COLLATE utf8_unicode_ci COMMENT 'Content - 64k fits to all!'\",\n          \"PRIMARY KEY (`id`)\",\n          \"KEY `I_text_parent` (`parent`)\",\n          \"KEY `I_text_prev` (`prev`)\",\n          \"KEY `I_text_next` (`next`)\",\n          \"KEY `I_text_next_alt` (`next_alt`)\",\n          \"CONSTRAINT `FK_text_parent` FOREIGN KEY (`parent`) REFERENCES `{{text}}` (`id`) ON DELETE SET NULL ON UPDATE CASCADE\",\n          \"CONSTRAINT `FK_text_prev` FOREIGN KEY (`prev`) REFERENCES `{{text}}` (`id`) ON DELETE SET NULL ON UPDATE CASCADE\",\n          \"CONSTRAINT `FK_text_next` FOREIGN KEY (`next`) REFERENCES `{{text}}` (`id`) ON DELETE SET NULL ON UPDATE CASCADE\",\n          \"CONSTRAINT `FK_text_next_alt` FOREIGN KEY (`next_alt`) REFERENCES `{{text}}` (`id`) ON DELETE SET NULL ON UPDATE CASCADE\",\n        ],\n        'ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci'\n      );\n    }\n\n    // 2017-02-22 01:46:23 42a23.6\n    // RPG_MARKET = 6, RPG_MARKET_EXCHANGE = 35\n    $updater->upd_do_query(\"UPDATE `{{log_dark_matter}}` SET `log_dark_matter_reason` = \" . 35 . \" WHERE `log_dark_matter_reason` = \" . 6);\n    $updater->upd_do_query(\"UPDATE `{{log_metamatter}}` SET `reason` = \" . 35 . \" WHERE `reason` = \" . 6);\n\n    // 2017-03-06 00:43:16 42a26.4\n    if (!$updater->isTableExists('festival_gifts')) {\n      $updater->upd_create_table('festival_gifts',\n        [\n          \"`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT\",\n          \"`highspot_id` int(10) unsigned DEFAULT NULL\",\n          \"`from` bigint(20) unsigned DEFAULT NULL\",\n          \"`to` bigint(20) unsigned DEFAULT NULL\",\n          \"`amount` bigint(20) unsigned NOT NULL\",\n          \"PRIMARY KEY (`id`)\",\n          \"KEY `I_highspot_id` (`highspot_id`,`from`,`to`) USING BTREE\",\n          \"KEY `I_to_from` (`highspot_id`,`to`,`from`) USING BTREE\",\n        ],\n        'ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci'\n      );\n    }\n\n    // 2017-03-11 20:09:51 42a26.15\n    if (!$updater->isFieldExists('users', 'skin')) {\n      $updater->upd_alter_table(\n        'users',\n        [\n          \"ADD COLUMN `template` VARCHAR(64) CHARSET utf8 COLLATE utf8_unicode_ci NOT NULL DEFAULT 'OpenGame' AFTER `que_processed`\",\n          \"ADD COLUMN `skin` VARCHAR(64) CHARSET utf8 COLLATE utf8_unicode_ci NOT NULL DEFAULT 'EpicBlue' AFTER `template`\",\n        ],\n        !$updater->isFieldExists('users', 'skin')\n      );\n\n      $query = $updater->upd_do_query(\"SELECT `id`, `dpath` FROM `{{users}}` FOR UPDATE\");\n      while ($row = db_fetch($query)) {\n        $skinName = '';\n        /** @noinspection SpellCheckingInspection */\n        if (!$row['dpath']) {\n          $skinName = 'EpicBlue';\n        } /** @noinspection SpellCheckingInspection */\n        elseif (substr($row['dpath'], 0, 6) == 'skins/') {\n          /** @noinspection SpellCheckingInspection */\n          $skinName = substr($row['dpath'], 6, -1);\n        } else {\n          /** @noinspection SpellCheckingInspection */\n          $skinName = $row['dpath'];\n        }\n        if ($skinName) {\n          $skinName = SN::$db->db_escape($skinName);\n          $updater->upd_do_query(\"UPDATE `{{users}}` SET `skin` = '{$skinName}' WHERE `id` = {$row['id']};\");\n        }\n      }\n    }\n\n    /** @noinspection SpellCheckingInspection */\n    $updater->upd_alter_table('users', [\"DROP COLUMN `dpath`\",], $updater->isFieldExists('users', 'dpath'));\n\n    // 2017-06-12 13:47:36 42c1\n    $updater->new_version = 42;\n    $updater->transactionCommit();\n\n  /** @noinspection PhpMissingBreakStatementInspection */\n  case 42:\n    $updater->upd_log_version_update();\n    $updater->transactionStart();\n\n    // 2017-10-11 09:51:49 43a4.3\n    $updater->upd_alter_table('messages',\n      [\"ADD COLUMN `message_json` tinyint(1) unsigned NOT NULL DEFAULT 0 AFTER `message_text`\",],\n      !$updater->isFieldExists('messages', 'message_json')\n    );\n\n\n    // 2017-10-17 09:49:24 43a6.0\n    // Removing old index i_user_id\n    $updater->upd_alter_table('counter', ['DROP KEY `i_user_id`',], $updater->isIndexExists('counter', 'i_user_id'));\n    // Adding new index I_counter_user_id\n    $updater->upd_alter_table('counter',\n      [\n        'ADD KEY `I_counter_user_id` (`user_id`, `device_id`, `browser_id`, `user_ip`, `user_proxy`)'\n      ],\n      !$updater->isIndexExists('counter', 'I_counter_user_id')\n    );\n\n    // Adding new field visit_length\n    $updater->upd_alter_table('counter', [\n      \"ADD COLUMN `visit_length` int unsigned NOT NULL DEFAULT 0 AFTER `visit_time`\",\n    ], !$updater->isFieldExists('counter', 'visit_length'));\n\n    // Adding key for logger update\n    $updater->upd_alter_table('counter', [\n      'ADD KEY `I_counter_visit_time` (`visit_time`, `counter_id`)'\n    ], !$updater->isIndexExists('counter', 'I_counter_visit_time'));\n\n    // 2017-10-18 09:27:27 43a6.1\n    $updater->upd_alter_table('counter', [\n      \"ADD COLUMN `hits` int unsigned NOT NULL DEFAULT 1 AFTER `visit_length`\",\n    ], !$updater->isFieldExists('counter', 'hits'));\n\n    // 2017-11-24 05:07:29 43a7.16\n    $updater->upd_alter_table('festival_highspot', [\n      \"ADD COLUMN `params` text NOT NULL DEFAULT '' COMMENT 'Параметры хайспота в виде JSON-encoded' AFTER `name`\",\n    ], !$updater->isFieldExists('festival_highspot', 'params'));\n\n    // 2017-11-26 06:40:25 43a8.3\n    $player_metamatter_immortal = SN::$gc->config->player_metamatter_immortal;\n    $updater->upd_do_query(\n      \"INSERT INTO `{{player_award}}` (award_type_id, award_id, player_id, awarded)\n        SELECT 2300, 2301, trans.user_id, acc.account_immortal\n        FROM `{{account}}` AS acc\n          JOIN `{{account_translate}}` AS trans ON trans.provider_id = 1 AND trans.provider_account_id = acc.account_id\n          LEFT JOIN `{{player_award}}` AS award ON award.award_id = 2301 AND award.player_id = trans.user_id\n        WHERE acc.account_metamatter_total >= {$player_metamatter_immortal} AND award.id IS NULL;\"\n    );\n\n    // 2018-02-27 08:32:46 43a12.8\n    if (!$updater->isTableExists('server_patches')) {\n      $updater->upd_create_table(\n        'server_patches',\n        [\n          \"`id` int unsigned COMMENT 'Patch internal ID'\",\n          \"`applied` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP\",\n          \"PRIMARY KEY (`id`)\",\n          \"KEY `I_applied` (`applied`)\"\n        ],\n        'ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci'\n      );\n    }\n\n    $updater->updPatchApply(1, function () use ($updater) {\n      $q = $updater->upd_do_query(\"SELECT `messageid`, `user` FROM `{{chat}}`\", true);\n      while ($row = db_fetch($q)) {\n        if (strpos($row['user'], 'a:') !== 0) {\n          continue;\n        }\n\n        try {\n          /** @noinspection SpellCheckingInspection */\n          $updater->upd_do_query(\n            \"UPDATE `{{chat}}` SET `user` = '\" . SN::$db->db_escape(\n              json_encode(\n                unserialize($row['user'])\n                , JSON_FORCE_OBJECT\n              )\n            ) . \"' WHERE `messageid` = \" . floatval($row['messageid'])\n          );\n        } catch (Exception $e) {\n        }\n      }\n    });\n\n    // 2018-03-07 09:23:41 43a13.23 + 2018-03-07 12:00:47 43a13.24\n    $updater->updPatchApply(2, function () use ($updater) {\n      $updater->upd_alter_table('festival_gifts', [\n        \"ADD COLUMN `disclosure` tinyint(1) unsigned NOT NULL DEFAULT 0 AFTER `amount`\",\n        \"ADD COLUMN `message` VARCHAR(4096) CHARSET utf8 COLLATE utf8_unicode_ci NOT NULL DEFAULT '' AFTER `disclosure`\",\n      ], !$updater->isFieldExists('festival_gifts', 'disclosure'));\n    });\n\n    // 2018-03-12 13:23:10 43a13.33\n    $updater->updPatchApply(3, function () use ($updater) {\n      $updater->upd_alter_table('player_options',\n        [\n          \"MODIFY COLUMN `value` VARCHAR(16000) CHARSET utf8 COLLATE utf8_unicode_ci NOT NULL DEFAULT ''\",\n        ],\n        $updater->getFieldDescription('player_options', 'value')->Type == 'varchar(1900)'\n      );\n    });\n\n    // 2018-03-24 21:31:51 43a16.16 - OiS\n    $updater->updPatchApply(4, function () use ($updater) {\n      if (!$updater->isTableExists('festival_ois_player')) {\n        $updater->upd_create_table(\n          'festival_ois_player',\n          [\n            \"`highspot_id` int(10) unsigned COMMENT 'Highspot ID'\",\n            \"`player_id` bigint(20) unsigned COMMENT 'Player ID'\",\n            \"`ois_count` int(10) unsigned COMMENT 'OiS player controlled last tick'\",\n            \"PRIMARY KEY (`highspot_id`, `player_id`)\",\n            \"KEY `I_player_highspot` (`player_id`, `highspot_id`)\",\n            \"CONSTRAINT `FK_ois_highspot` FOREIGN KEY (`highspot_id`) REFERENCES `{{festival_highspot}}` (`id`) ON DELETE CASCADE ON UPDATE CASCADE\",\n            \"CONSTRAINT `FK_ois_player` FOREIGN KEY (`player_id`) REFERENCES `{{users}}` (`id`) ON DELETE CASCADE ON UPDATE CASCADE\",\n          ],\n          'ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci'\n        );\n      }\n    });\n\n    // 2018-03-25 08:11:39 43a16.21\n    $updater->updPatchApply(5, function () use ($updater) {\n      $updater->upd_alter_table(\n        'que',\n        \"ADD COLUMN `que_unit_one_time_raw` DECIMAL(20,5) NOT NULL DEFAULT 0\",\n        !$updater->isFieldExists('que', 'que_unit_one_time_raw')\n      );\n    });\n\n    $updater->new_version = 43;\n    $updater->transactionCommit();\n\n  /** @noinspection PhpMissingBreakStatementInspection */\n  case 43:\n    // !!!!!!!!! This one does not start transaction !!!!!!!!!!!!\n    $updater->upd_log_version_update();\n\n    // 2018-12-21 14:00:41 44a5 Module \"ad_promo_code\" support\n    $updater->updPatchApply(6, function () use ($updater) {\n      if (!$updater->isTableExists('ad_promo_codes')) {\n        $updater->upd_create_table(\n          'ad_promo_codes',\n          [\n            \"`id` int(10) unsigned NOT NULL AUTO_INCREMENT\",\n            \"`code` varchar(64) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL COMMENT 'Promo code itself. Unique'\",\n            \"`description` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL DEFAULT '' COMMENT 'Promo code description'\",\n            \"`reg_only` tinyint(1) NOT NULL DEFAULT '1'\",\n            \"`from` datetime DEFAULT NULL\",\n            \"`to` datetime DEFAULT NULL\",\n            \"`max_use` int(10) unsigned NOT NULL DEFAULT '0' COMMENT 'Max time code can be used. 0 - unlimited'\",\n            \"`used_times` int(10) unsigned NOT NULL DEFAULT '0' COMMENT 'How many time code was used'\",\n            \"`adjustments` mediumtext CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL\",\n\n            \"PRIMARY KEY (`id`)\",\n            \"UNIQUE KEY `I_promo_code` (`code`)\",\n          ],\n          'ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci'\n        );\n      }\n\n      if (!$updater->isTableExists('ad_promo_codes_uses')) {\n        $updater->upd_create_table(\n          'ad_promo_codes_uses',\n          [\n            \"`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT\",\n            \"`promo_code_id` int(10) unsigned NOT NULL\",\n            \"`user_id` bigint(20) unsigned NOT NULL\",\n            \"`use_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP\",\n\n            \"PRIMARY KEY (`id`)\",\n            \"KEY `FK_user_id` (`user_id`)\",\n            \"KEY `I_promo_code_id` (`promo_code_id`,`user_id`)\",\n          ],\n          'ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci'\n        );\n      }\n    });\n\n    // 2018-12-22 11:42:20 44a12\n    $updater->updPatchApply(7, function () use ($updater) {\n      // Creating table for HTTP query strings\n      $updater->upd_create_table(\n        'security_query_strings',\n        [\n          \"`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT\",\n          \"`query_string` varchar(250) CHARACTER SET utf8 NOT NULL DEFAULT ''\",\n          \"PRIMARY KEY (`id`)\",\n          \"UNIQUE KEY `I_query_string` (`query_string`)\",\n        ],\n        'ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci'\n      );\n\n      // Adjusting table `counter` to use HTTP query string instead of full URLs\n      $updater->upd_alter_table('counter', [\n        \"DROP FOREIGN KEY `FK_counter_plain_url_id`\",\n        \"DROP KEY `I_counter_plain_url_id`\",\n        \"DROP COLUMN `plain_url_id`\",\n\n        \"ADD COLUMN `query_string_id` bigint(20) unsigned DEFAULT NULL AFTER `page_url_id`\",\n        \"ADD KEY `I_counter_query_string_id` (`query_string_id`)\",\n\n        \"ADD COLUMN `player_entry_id` bigint(20) unsigned DEFAULT NULL AFTER `user_id`\",\n        \"ADD KEY `I_counter_player_entry_id` (`player_entry_id`, `user_id`)\",\n\n        \"DROP KEY `I_counter_device_id`\",\n        \"ADD KEY `I_counter_device_id` (device_id, browser_id, user_ip, user_proxy)\",\n      ], !$updater->isFieldExists('counter', 'query_string_id'));\n\n      // Adjusting `security_player_entry` to match new structure\n      $updater->upd_alter_table('security_player_entry', [\n        // Adding temporary key for `player_id` field - needs for FOREIGN KEY\n        \"ADD KEY `I_player_entry_player_id` (`player_id`)\",\n        // Replacing primary index with secondary one\n        \"DROP PRIMARY KEY\",\n\n        // Adding main index column\n        \"ADD COLUMN `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT FIRST\",\n        \"ADD PRIMARY KEY (`id`)\",\n\n        // Foreign keys is not needed - we want to maintain info about player entries even if dictionary info is deleted\n        \"DROP FOREIGN KEY `FK_security_player_entry_browser_id`\",\n        \"DROP FOREIGN KEY `FK_security_player_entry_device_id`\",\n        \"DROP FOREIGN KEY `FK_security_player_entry_player_id`\",\n      ], !$updater->isFieldExists('security_player_entry', 'id'));\n\n      if ($updater->isFieldExists('counter', 'device_id')) {\n        $oldLockTime                   = SN::$gc->config->upd_lock_time;\n        SN::$gc->config->upd_lock_time = 300;\n\n        $updater->transactionStart();\n        $updater->upd_drop_table('spe_temp');\n        $updater->upd_create_table(\n          'spe_temp',\n          [\n            \"`device_id` bigint(20) unsigned NOT NULL DEFAULT '0'\",\n            \"`browser_id` bigint(20) unsigned NOT NULL DEFAULT '0'\",\n            \"`user_ip` int(10) unsigned NOT NULL DEFAULT '0'\",\n            \"`user_proxy` varchar(255) COLLATE latin1_bin NOT NULL DEFAULT ''\",\n            \"`first_visit` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP\",\n\n            \"UNIQUE KEY `I_temp_key` (`device_id`,`browser_id`,`user_ip`,`user_proxy`)\",\n          ],\n          'ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci'\n        );\n        // Repopulating temp table with records with `user_id` == NULL\n        $updater->upd_do_query(\n          \"INSERT IGNORE INTO `{{spe_temp}}` (`device_id`, `browser_id`, `user_ip`, `user_proxy`, `first_visit`)\n          SELECT `device_id`, `browser_id`, `user_ip`, `user_proxy`, min(`first_visit`) \n          FROM `{{security_player_entry}}`\n          GROUP BY `device_id`, `browser_id`, `user_ip`, `user_proxy`\"\n        );\n        // Populating temp table with data from `counter`\n        $updater->upd_do_query(\n          \"INSERT IGNORE INTO `{{spe_temp}}` (`device_id`, `browser_id`, `user_ip`, `user_proxy`, `first_visit`)\n          SELECT `device_id`, `browser_id`, `user_ip`, `user_proxy`, min(`visit_time`)\n          FROM `{{counter}}`\n          GROUP BY `device_id`, `browser_id`, `user_ip`, `user_proxy`\"\n        );\n\n        // Deleting all records from `security_player_entry`\n        $updater->upd_do_query(\"TRUNCATE TABLE `{{security_player_entry}}`;\");\n        // Adding unique index for all significant fields\n        $updater->upd_alter_table('security_player_entry', [\n          \"ADD UNIQUE KEY `I_player_entry_unique` (`device_id`, `browser_id`, `user_ip`, `user_proxy`)\",\n        ], !$updater->isIndexExists('security_player_entry', 'I_player_entry_unique'));\n        // Filling `security_player_entry` from temp table\n        $updater->upd_do_query(\n          \"INSERT IGNORE INTO `{{security_player_entry}}` (`device_id`, `browser_id`, `user_ip`, `user_proxy`, `first_visit`)\n          SELECT `device_id`, `browser_id`, `user_ip`, `user_proxy`, `first_visit`\n          FROM `{{spe_temp}}`\"\n        );\n        // Dropping temp table - it has no use anymore\n        $updater->upd_drop_table('spe_temp');\n\n        // Updating counter to match player entries\n        /** @noinspection SqlWithoutWhere */\n        $updater->upd_do_query(\n          \"UPDATE `{{counter}}` AS c\n          LEFT JOIN `{{security_player_entry}}` AS spe\n            ON spe.device_id = c.device_id AND spe.browser_id = c.browser_id\n                AND spe.user_ip = c.user_ip AND spe.user_proxy = c.user_proxy\n        SET c.player_entry_id = spe.id\"\n        );\n\n        $updater->upd_alter_table('security_player_entry', [\n          \"DROP KEY `I_player_entry_device_id`\",\n          \"DROP KEY `I_player_entry_player_id`\",\n          // Removing unused field `security_player_entry`.`player_id`\n          \"DROP COLUMN `player_id`\",\n        ], $updater->isFieldExists('security_player_entry', 'player_id'));\n// todo - вынести вниз в отдельный патч (?) Сверить с живыми\n        // Remove unused fields from `counter` table\n        $updater->upd_alter_table('counter', [\n          \"DROP KEY `I_counter_user_id`\",\n          \"ADD KEY `I_counter_user_id` (`user_id`, `player_entry_id`)\",\n\n          \"DROP FOREIGN KEY `FK_counter_device_id`\",\n          \"DROP KEY `I_counter_device_id`\",\n          \"DROP COLUMN `device_id`\",\n\n          \"DROP FOREIGN KEY `FK_counter_browser_id`\",\n          \"DROP KEY `I_counter_browser_id`\",\n          \"DROP COLUMN `browser_id`\",\n\n          \"DROP COLUMN `user_ip`\",\n          \"DROP COLUMN `user_proxy`\",\n        ], $updater->isFieldExists('counter', 'device_id'));\n\n        SN::$gc->config->upd_lock_time = $oldLockTime;\n        $updater->transactionCommit();\n      }\n    });\n\n    $updater->new_version = 44;\n    $updater->transactionCommit();\n\n  /** @noinspection PhpMissingBreakStatementInspection */\n  case 44:\n    // !!!!!!!!! This one does not start transaction !!!!!!!!!!!!\n    $updater->upd_log_version_update();\n\n    // 2019-08-15 00:10:48 45a8\n    $updater->updPatchApply(8, function () use ($updater) {\n      if (!$updater->isTableExists('player_ignore')) {\n        $updater->upd_create_table(\n          'player_ignore',\n          [\n            \"`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT\",\n            \"`player_id` bigint(20) unsigned NOT NULL\",\n            \"`ignored_id` bigint(20) unsigned NOT NULL\",\n            \"`subsystem` tinyint(4) NOT NULL DEFAULT '0'\",\n            \"PRIMARY KEY (`id`)\",\n            \"UNIQUE KEY `I_player_ignore_all` (`player_id`,`ignored_id`,`subsystem`) USING BTREE\",\n            \"KEY `I_player_ignore_ignored` (`ignored_id`)\",\n            \"CONSTRAINT `FK_player_ignore_ignored` FOREIGN KEY (`ignored_id`) REFERENCES `{{users}}` (`id`) ON DELETE CASCADE ON UPDATE CASCADE\",\n            \"CONSTRAINT `FK_player_ignore_player` FOREIGN KEY (`player_id`) REFERENCES `{{users}}` (`id`) ON DELETE CASCADE ON UPDATE CASCADE\",\n          ],\n          'ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci'\n        );\n      }\n    }, PATCH_REGISTER);\n\n    // 2019-08-21 20:14:18 45a19\n    $updater->updPatchApply(9, function () use ($updater) {\n      $updater->upd_alter_table('payment', [\n        'ADD COLUMN `payment_method_id` smallint DEFAULT NULL AFTER `payment_module_name`',\n        'ADD KEY `I_payment_method_id` (`payment_method_id`)',\n      ], !$updater->isFieldExists('payment', 'payment_method_id'));\n    }, PATCH_REGISTER);\n\n    // 2020-02-18 21:00:19 45a71\n    $updater->updPatchApply(10, function () use ($updater) {\n      $name = classConfig::FLEET_UPDATE_MAX_RUN_TIME;\n      if (!SN::$gc->config->pass()->$name) {\n        SN::$gc->config->pass()->$name = 30;\n      }\n    }, PATCH_REGISTER);\n\n    $updater->new_version = 45;\n    $updater->transactionCommit();\n\n  /** @noinspection PhpMissingBreakStatementInspection */\n  case 45:\n    // !!!!!!!!! This one does not start transaction !!!!!!!!!!!!\n    $updater->upd_log_version_update();\n\n    // 2021-03-03 13:41:05 46a13\n    $updater->updPatchApply(11, function () use ($updater) {\n      $updater->upd_alter_table('festival_gifts', [\n        'ADD COLUMN `gift_unit_id` bigint(20) NOT NULL DEFAULT 0 AFTER `amount`',\n      ], !$updater->isFieldExists('festival_gifts', 'gift_unit_id'));\n    }, PATCH_REGISTER);\n\n    // 2024-04-13 13:04:16 46a127\n    $updater->updPatchApply(12, function () use ($updater) {\n      $updater->upd_alter_table('config', [\n        \"ADD COLUMN `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP\",\n        \"ADD COLUMN `updated_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP\",\n      ], !$updater->isFieldExists('config', 'created_at'));\n\n      if (!$updater->isTableExists('festival_config')) {\n        $updater->upd_create_table(\n          'festival_config',\n          [\n            \"`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT\",\n            \"`festival_id` smallint(5) unsigned NULL DEFAULT NULL\",\n            \"`highspot_id` int(10) unsigned NULL DEFAULT NULL\",\n\n            \"`config_name` varchar(64) NOT NULL\",\n            \"`config_value` mediumtext NOT NULL\",\n\n            \"`created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP\",\n            \"`updated_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP\",\n\n            \"PRIMARY KEY (`id`)\",\n\n            \"KEY `I_festival_config_festival` (`festival_id`,`config_name`) USING BTREE\",\n            \"UNIQUE KEY `I_festival_config_highspot` (`highspot_id`,`festival_id`,`config_name`) USING BTREE\",\n\n            \"CONSTRAINT `FK_festival_config_festival_id` FOREIGN KEY (`festival_id`) REFERENCES `{{festival}}` (`id`) ON DELETE CASCADE ON UPDATE CASCADE\",\n            \"CONSTRAINT `FK_festival_config_highspot_id` FOREIGN KEY (`highspot_id`) REFERENCES `{{festival_highspot}}` (`id`) ON DELETE CASCADE ON UPDATE CASCADE\",\n          ],\n          'ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci'\n        );\n\n        // module_festival_69_highspot_1396_code\n        $query = $updater->upd_do_query(\"SELECT * FROM {{config}} WHERE `config_name` LIKE 'module_festival_%_highspot_%';\");\n        $total = $patched = 0;\n        while ($row = db_fetch($query)) {\n          $total++;\n          if (preg_match('/module_festival_(\\d+)_highspot_(\\d+)_(.+)/', $row['config_name'], $matches)) {\n            /*\n             74|array(4)\n                0 => string(38) module_festival_13_highspot_275_status\n                1 => string(2) 13\n                2 => string(3) 275\n                3 => string(6) status\n             * */\n            $festival = $updater->upd_do_query(\"SELECT `id` FROM {{festival}} WHERE `id` = {$matches[1]};\", true);\n            $highspot = $updater->upd_do_query(\"SELECT `id` FROM {{festival_highspot}} WHERE `id` = {$matches[2]};\", true);\n            if (!empty($festival->num_rows) && !empty($highspot->num_rows)) {\n              $matches[3] = \"'\" . SN::$db->db_escape($matches[3]) . \"'\";\n              $matches[4] = \"'\" . SN::$db->db_escape($row['config_value']) . \"'\";\n              $updater->upd_do_query(\"\n                REPLACE INTO {{festival_config}}\n                SET\n                  `festival_id` = {$matches[1]},\n                  `highspot_id` = {$matches[2]},\n                  `config_name` = {$matches[3]},\n                  `config_value` = {$matches[4]}\n                ;\");\n              $patched++;\n            } elseif (empty($festival->num_rows)) {\n              $updater->upd_log_message(\"Warning! Festival ID {$matches[1]} not found\");\n            } elseif (empty($highspot->num_rows)) {\n              $updater->upd_log_message(\"Warning! Highspot ID {$matches[2]} not found\");\n            }\n          }\n        }\n\n        $updater->upd_log_message(\"Migrated {$patched}/{$total} festival configuration records\");\n      }\n\n      $updater->upd_alter_table('que', ['DROP KEY `que_id`',], $updater->isIndexExists('que', 'que_id'));\n      $updater->upd_alter_table('counter', ['DROP KEY `counter_id`',], $updater->isIndexExists('counter', 'counter_id'));\n      $updater->upd_alter_table('captain', ['DROP KEY `captain_id`',], $updater->isIndexExists('captain', 'captain_id'));\n    }, PATCH_REGISTER);\n\n    // 2024-10-21 21:08:03 46a147\n    $updater->updPatchApply(13, function () use ($updater) {\n      $updater->indexDropIfExists('planets', 'id');\n      $updater->indexDropIfExists('users', 'I_user_id_name');\n\n      $updater->indexReplace(\n        'que',\n        'I_que_planet_id',\n        ['que_planet_id', 'que_player_id',],\n        function () use ($updater) {\n          $updater->constraintDropIfExists('que', 'FK_que_planet_id');\n        },\n        function () use ($updater) {\n          //    CONSTRAINT `FK_que_player_id` FOREIGN KEY (`que_player_id`) REFERENCES `sn_users` (`id`) ON DELETE CASCADE ON UPDATE CASCADE\n          $updater->upd_alter_table(\n            'que',\n            ['ADD CONSTRAINT `FK_que_planet_id` FOREIGN KEY (`que_planet_id`) REFERENCES `{{planets}}` (`id`) ON DELETE CASCADE ON UPDATE CASCADE',],\n            true\n          );\n        }\n      );\n    }, PATCH_REGISTER);\n\n    // 2025-02-25 12:29:49 46a154\n    $updater->updPatchApply(14, function () use ($updater) {\n      if (!$updater->isTableExists('ban_ip')) {\n        $updater->upd_create_table(\n          'ban_ip',\n          [\n            \"`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT\",\n            \"`ipv4_from` int unsigned COMMENT 'IP v4 range start'\",\n            \"`ipv4_to` int unsigned COMMENT 'IP v4 range end'\",\n\n            \"`created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT 'When ban was issued'\",\n            \"`expired_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT 'When ban will expire'\",\n\n            \"PRIMARY KEY (`id`)\",\n\n            \"KEY `I_ban_ip_v4` (`ipv4_from`,`ipv4_to`, `expired_at`) USING BTREE\",\n          ],\n          'ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci'\n        );\n      }\n    }, PATCH_REGISTER);\n\n    // 2025-12-15 12:30:09 46a225\n    $updater->updPatchApply(15, function () use ($updater) {\n      if (!$updater->isTableExists('stories')) {\n        $updater->upd_create_table(\n          'stories',\n          [\n            \"`id` int(10) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 'Story ID'\",\n            \"`name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT 'Story name'\",\n            \"`path` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT 'Path to story files from SN root'\",\n\n            \"PRIMARY KEY (`id`) USING BTREE\",\n\n            \"INDEX `I_stories_name`(`name`) USING BTREE\"\n          ],\n          'ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci'\n        );\n        $updater->upd_do_query(\"\n                INSERT IGNORE INTO {{stories}} \n                SET\n                  `id` = 1,\n                  `name` = 'simple_story',\n                  `path` = 'modules/core_stories/stories/simple_story/'\n                ;\"\n        );\n      }\n      if (!$updater->isTableExists('story_rewards')) {\n        $updater->upd_create_table(\n          'story_rewards',\n          [\n            \"`id` int(10) UNSIGNED NOT NULL AUTO_INCREMENT\",\n            \"`story_id` int(10) UNSIGNED NULL DEFAULT NULL\",\n            \"`player_id` bigint(20) UNSIGNED NULL DEFAULT NULL\",\n            \"`reward_id` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT 'Story\\'s internal reward ID'\",\n            \"`received` text CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL\",\n\n            \"PRIMARY KEY (`id`) USING BTREE\",\n            \"INDEX `I_story_rewards_story`(`story_id`, `player_id`, `reward_id`) USING BTREE\",\n            \"INDEX `I_story_rewards_player`(`player_id`) USING BTREE\",\n            \"CONSTRAINT `FK_story_rewards_player` FOREIGN KEY (`player_id`) REFERENCES `{{users}}` (`id`) ON DELETE CASCADE ON UPDATE CASCADE\",\n            \"CONSTRAINT `FK_story_rewards_story` FOREIGN KEY (`story_id`) REFERENCES `{{stories}}` (`id`) ON DELETE CASCADE ON UPDATE CASCADE\",\n          ],\n          'ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci'\n        );\n      }\n    }, PATCH_REGISTER);\n\n    $updater->new_version = 46;\n    $updater->transactionCommit();\n\n  case 46:\n//    // #ctv\n//    $updater->updPatchApply(16, function() use ($updater) {\n//    }, PATCH_PRE_CHECK);\n\n//   TODO - UNCOMMENT ON RELEASE!\n//    $updater->new_version = 47;\n//    $updater->transactionCommit();\n\n}\n\n$updater->successTermination = true;\n// DO NOT DELETE ! This will invoke destructor !\nunset($updater);\n"
  },
  {
    "path": "includes/vars.php",
    "content": "<?php\n\n/**\n * vars.php\n *\n * @version   1.0\n * @copyright 2008 by Chlorel for XNova\n */\n\nuse Alliance\\Alliance;\nuse Chat\\Chat;\nuse Pages\\Deprecated\\PageAdminMining;\nuse Pages\\Deprecated\\PageAdminModules;\nuse Pages\\Deprecated\\PageAdminPayment;\nuse Pages\\Deprecated\\PageAdminUserView;\nuse Pages\\Deprecated\\PageImperium;\n\nif (!defined('INSIDE')) {\n  die('Hack attempt!');\n}\n\nglobal $sn_menu_extra, $sn_menu_admin_extra, $config, $sn_version_check_class;\n\n$sn_menu_extra = array();\n$sn_menu_admin_extra = array();\n\nglobal $sn_mvc;\n$sn_mvc = [\n  FIELD_MODEL => [\n    /** @see AjaxController::controller() */\n    'ajax' => [AjaxController::class . '::controller'],\n\n    'options'  => ['sn_options_model'],\n\n//    'chat'     => ['sn_chat_model'],\n//    'chat_add' => ['sn_chat_add_model'],\n    /** @see Chat::chatModel() */\n    'chat'     => [Chat::class  . '::' . 'chatModel'],\n    /** @see Chat::chatAddModel() */\n    'chat_add' => [Chat::class  . '::' . 'chatAddModel'],\n\n    /** @see PageImperium::modelStatic() */\n    'imperium' => [PageImperium::class . '::' . 'modelStatic'],\n\n    /** @see PageAdminUserView::modelStatic() */\n    'admin/user_view'  => [PageAdminUserView::class . '::' . 'modelStatic'],\n    'admin/admin_ally' => ['sn_admin_ally_model'],\n  ],\n  FIELD_VIEW  => [\n    /** @see AjaxController::view() */\n    'ajax' => [AjaxController::class . '::view'],\n\n    'options'       => ['sn_options_view'],\n\n//    'chat'          => ['sn_chat_view'],\n//    'chat_msg'      => ['sn_chat_msg_view'],\n    /** @see Chat::chatView() */\n    'chat'          => [Chat::class  . '::' . 'chatView'],\n    /** @see Chat::chatMsgView() */\n    'chat_msg'      => [Chat::class  . '::' . 'chatMsgView'],\n    /** @see Chat::chatFrameView() */\n    'chat_frame'      => [Chat::class  . '::' . 'chatFrameView'],\n\n    'battle_report' => ['sn_battle_report_view'],\n    'contact'       => ['sn_contact_view'],\n    'imperator'     => ['sn_imperator_view'],\n    /** @see PageImperium::viewStatic() */\n    'imperium'      => [PageImperium::class . '::' . 'viewStatic'],\n    'techtree'      => ['sn_techtree_view'],\n\n    /** @see PageAdminUserView::viewStatic() */\n    'admin/user_view'     => [PageAdminUserView::class . '::viewStatic'],\n    'admin/admin_ally'    => ['sn_admin_ally_view'],\n    /** @see PageAdminMining::viewStatic() */\n    'admin/admin_mining'  => [PageAdminMining::class . '::' . 'viewStatic'],\n    /** @see PageAdminModules::viewStatic() */\n    'admin/admin_modules' => [PageAdminModules::class . '::' . 'viewStatic'],\n    /** @see PageAdminPayment::viewStatic() */\n    'admin/admin_payment' => [PageAdminPayment::class . '::' . 'viewStatic'],\n  ],\n\n  // For now - common for same VIEW and MODEL\n  MVC_OPTIONS => [],\n\n  'controller' => [],\n\n  'i18n' => [\n    'options'             => [\n      'options'  => 'options',\n      'messages' => 'messages',\n    ],\n    'imperator'           => [\n      'overview' => 'overview',\n    ],\n    'admin/admin_payment' => [\n      'admin' => 'admin',\n    ],\n    'admin/user_view'     => [\n      'admin' => 'admin',\n    ],\n\n    'chat' => [\n      'chat_advanced' => [\n        'file' => 'chat_advanced',\n        'path' => '',\n      ],\n    ],\n    'chat_add' => [\n      'chat_advanced' => 'chat_advanced',\n//      'chat_advanced' => [\n//        'file' => 'chat_advanced',\n//        'path' => '',\n//      ],\n    ],\n    'chat_msg' => [\n      'chat_advanced' => 'chat_advanced',\n//      'chat_advanced' => [\n//        'file' => 'chat_advanced',\n//        'path' => '',\n//      ],\n    ],\n    'chat_frame' => [\n      'chat_advanced' => 'chat_advanced',\n//      'chat_advanced' => [\n//        'file' => 'chat_advanced',\n//        'path' => '',\n//      ],\n    ],\n\n  ],\n\n  'pages' => [\n    'admin/user_view' => [\n      'filename' => 'admin/user_view',\n      'options'  => [\n        PAGE_OPTION_FLEET_UPDATE_SKIP => true,\n        PAGE_OPTION_ADMIN             => true,\n      ],\n    ],\n\n    'admin/admin_ally' => [\n      'filename' => 'admin/admin_ally',\n      'options'  => [\n        PAGE_OPTION_FLEET_UPDATE_SKIP => true,\n        PAGE_OPTION_ADMIN             => true,\n      ],\n    ],\n\n    'admin/admin_mining'  => [\n      'options' => [\n        PAGE_OPTION_ADMIN => true,\n      ],\n    ],\n    'admin/admin_modules' => [\n      'options' => [\n        PAGE_OPTION_ADMIN => true,\n      ],\n    ],\n    'admin/admin_payment' => [\n      'options' => [\n        PAGE_OPTION_ADMIN => true,\n      ],\n    ],\n\n    'chat'     => [\n      'filename' => 'chat',\n      'options'  => [\n        PAGE_OPTION_FLEET_UPDATE_SKIP => true,\n      ],\n    ],\n    'chat_add' => [\n      'filename' => 'chat',\n      'options'  => [\n        PAGE_OPTION_FLEET_UPDATE_SKIP => true,\n      ],\n    ],\n    'chat_msg' => [\n      'filename' => 'chat',\n      'options'  => [\n        PAGE_OPTION_FLEET_UPDATE_SKIP => true,\n      ],\n    ],\n    'chat_frame' => [\n      'filename' => 'chat',\n      'options'  => [\n        PAGE_OPTION_FLEET_UPDATE_SKIP => true,\n      ],\n    ],\n\n    'contact'       => [\n      'allow_anonymous' => true,\n      'filename'        => 'contact',\n    ],\n    'imperator'     => [\n      'filename' => 'imperator',\n    ],\n    'imperium'      => [],\n    'options'       => [\n      'filename' => 'options',\n    ],\n    'techtree'      => [\n      'filename' => 'techtree',\n    ],\n    'battle_report' => [\n      'filename' => 'battle_report',\n    ],\n    'ajax'          => [],\n    'time_probe'    => [\n      'options' => [\n        PAGE_OPTION_FLEET_UPDATE_SKIP => true,\n      ],\n    ],\n\n    'buildings' => [\n      // PAGE_OPTION_EARLY_HEADER => true,\n      // PAGE_OPTION_TITLE => 'buildings',\n    ],\n\n    'overview' => [\n      // PAGE_OPTION_EARLY_HEADER => true,\n      // PAGE_OPTION_TITLE => 'buildings',\n    ],\n  ],\n];\n\n$note_priority_classes = array(\n  4 => 'error',\n  3 => 'warning',\n  2 => 'notice',\n  1 => 'ok',\n  0 => 'white',\n);\n\n$sn_ali_admin_internal = array(\n  'rights'    => array(\n    'include' => 'alliance/ali_internal_admin_rights.inc',\n    'title'   => 'ali_adm_rights_title'\n  ),\n  'members'   => array(\n    'include' => 'alliance/ali_internal_members.inc',\n    'title'   => 'Members_list'\n  ),\n  'requests'  => array(\n    'include' => 'alliance/ali_internal_admin_request.inc',\n    'title'   => 'ali_req_check'\n  ),\n  'diplomacy' => array(\n    'include' => 'alliance/ali_internal_admin_diplomacy.inc',\n    'title'   => 'ali_dip_title'\n  ),\n  'mail'      => array(\n    'include' => 'alliance/ali_internal_admin_mail.inc',\n    'title'   => 'Send_circular_mail'\n  ),\n  'default'   => array(\n    'include' => 'alliance/ali_internal_admin.inc',\n  ),\n);\n\n$sn_version_check_class = array(\n  SNC_VER_NEVER => 'warning',\n\n  SNC_VER_ERROR_CONNECT => 'error',\n  SNC_VER_ERROR_SERVER  => 'error',\n\n  SNC_VER_EXACT  => 'ok',\n  SNC_VER_LESS   => 'notice',\n  SNC_VER_FUTURE => 'error',\n\n  SNC_VER_RELEASE_EXACT => 'ok',\n  SNC_VER_RELEASE_MINOR => 'notice',\n  SNC_VER_RELEASE_MAJOR => 'warning',\n  SNC_VER_RELEASE_ALPHA => 'ok',\n\n  SNC_VER_MAINTENANCE      => 'notice',\n  SNC_VER_UNKNOWN_RESPONSE => 'warning',\n  SNC_VER_INVALID          => 'error',\n  SNC_VER_STRANGE          => 'error',\n\n  SNC_VER_REGISTER_UNREGISTERED      => 'warning',\n  SNC_VER_REGISTER_ERROR_MULTISERVER => 'error',\n  SNC_VER_REGISTER_ERROR_REGISTERED  => 'error',\n  SNC_VER_REGISTER_ERROR_NO_NAME     => 'error',\n  SNC_VER_REGISTER_ERROR_WRONG_URL   => 'error',\n  SNC_VER_REGISTER_REGISTERED        => 'ok',\n\n  SNC_VER_ERROR_INCOMPLETE_REQUEST => 'error',\n  SNC_VER_ERROR_UNKNOWN_KEY        => 'error',\n  SNC_VER_ERROR_MISSMATCH_KEY_ID   => 'error',\n);\n\n$tableList = array('aks', 'alliance', 'alliance_requests', 'announce', 'annonce', 'banned', 'buddy', 'chat', 'config', 'counter',\n  'errors', 'fleets', 'fleet_log', 'galaxy', 'iraks', 'logs', 'log_dark_matter', 'messages', 'notes', 'planets', 'quest',\n  'quest_status', 'referrals', 'rw', 'statpoints', 'users'\n);\n\n$sn_image_allowed_extensions = array('png', 'jpg', 'jpeg', 'gif');\n\nglobal $ally_rights;\n$ally_rights = Alliance::RIGHTS_ALL;\n\n$functions = array(//    'test' => 'sn_test',\n);\n\n$sn_message_class_list = array(\n  MSG_TYPE_NEW       => array(\n    'name'       => 'new_message',\n    'switchable' => false,\n    'email'      => false,\n  ),\n  MSG_TYPE_ADMIN     => array(\n    'name'       => 'msg_admin',\n    'switchable' => false,\n    'email'      => true,\n  ),\n  MSG_TYPE_PLAYER    => array(\n    'name'       => 'mnl_joueur',\n    'switchable' => false,\n    'email'      => true,\n  ),\n  MSG_TYPE_ALLIANCE  => array(\n    'name'       => 'mnl_alliance',\n    'switchable' => false,\n    'email'      => true,\n  ),\n  MSG_TYPE_SPY       => array(\n    'name'       => 'mnl_spy',\n    'switchable' => true,\n    'email'      => true,\n  ),\n  MSG_TYPE_COMBAT    => array(\n    'name'       => 'mnl_attaque',\n    'switchable' => true,\n    'email'      => true,\n  ),\n  MSG_TYPE_TRANSPORT => array(\n    'name'       => 'mnl_transport',\n    'switchable' => true,\n    'email'      => true,\n  ),\n  MSG_TYPE_RECYCLE   => array(\n    'name'       => 'mnl_exploit',\n    'switchable' => true,\n    'email'      => true,\n  ),\n  MSG_TYPE_EXPLORE   => array(\n    'name'       => 'mnl_expedition',\n    'switchable' => true,\n    'email'      => true,\n  ),\n  //     97 => 'mnl_general',\n  MSG_TYPE_QUE       => array(\n    'name'       => 'mnl_buildlist',\n    'switchable' => true,\n    'email'      => true,\n  ),\n  MSG_TYPE_OUTBOX    => array(\n    'name'       => 'mnl_outbox',\n    'switchable' => false,\n    'email'      => false,\n  ),\n);\n\n$sn_message_groups = array(\n  'switchable' => array(MSG_TYPE_SPY, MSG_TYPE_COMBAT, MSG_TYPE_RECYCLE, MSG_TYPE_TRANSPORT, MSG_TYPE_EXPLORE, MSG_TYPE_QUE),\n  'email'      => array(MSG_TYPE_SPY, MSG_TYPE_PLAYER, MSG_TYPE_ALLIANCE, MSG_TYPE_COMBAT, MSG_TYPE_RECYCLE, MSG_TYPE_TRANSPORT,\n    MSG_TYPE_ADMIN, MSG_TYPE_EXPLORE, MSG_TYPE_QUE),\n);\n\n// Default user option list as 'option_name' => 'option_list'\n$user_option_list = array();\n\n$user_option_list[OPT_MESSAGE] = array();\nforeach ($sn_message_class_list as $message_class_id => $message_class_data) {\n  if ($message_class_data['switchable']) {\n    $user_option_list[OPT_MESSAGE][\"opt_{$message_class_data['name']}\"] = 1;\n  }\n\n  if ($message_class_data['email']) {\n    $user_option_list[OPT_MESSAGE][\"opt_email_{$message_class_data['name']}\"] = 0;\n  }\n}\n\n$user_option_list[OPT_UNIVERSE] = array(\n  'opt_uni_avatar_user' => 1,\n  'opt_uni_avatar_ally' => 1,\n);\n\n$user_option_list[OPT_INTERFACE] = array(\n  'opt_int_navbar_resource_force'   => 0,\n  'opt_int_overview_planet_columns' => 0,\n  'opt_int_overview_planet_rows'    => 5,\n  'opt_int_struc_vertical'          => 0,\n);\n\n$user_option_types = array(\n  'opt_int_overview_planet_columns' => 'integer',\n  'opt_int_overview_planet_rows'    => 'integer',\n);\n\n$sn_diplomacy_relation_list = array(\n  ALLY_DIPLOMACY_NEUTRAL => array(\n    'relation_id' => ALLY_DIPLOMACY_NEUTRAL,\n    'enter_delay' => 0,\n    'exit_delay'  => 0,\n  ),\n  ALLY_DIPLOMACY_WAR     => array(\n    'relation_id' => ALLY_DIPLOMACY_WAR,\n    'enter_delay' => $config->fleet_bashing_war_delay,\n    'exit_delay'  => -1,\n  ),\n  ALLY_DIPLOMACY_PEACE   => array(\n    'relation_id' => ALLY_DIPLOMACY_PEACE,\n    'enter_delay' => -1,\n    'exit_delay'  => 0,\n  ),\n  /*\n  ALLY_DIPLOMACY_CONFEDERATION => array(\n    'relation_id' => ALLY_DIPLOMACY_CONFEDERATION,\n    'enter_delay' => -1,\n    'exit_delay'  => $config->fleet_bashing_war_delay,\n  ),\n  ALLY_DIPLOMACY_FEDERATION    => array(\n    'relation_id' => ALLY_DIPLOMACY_FEDERATION,\n    'enter_delay' => -1,\n    'exit_delay'  => $config->fleet_bashing_war_delay,\n  ),\n  ALLY_DIPLOMACY_UNION         => array(\n    'relation_id' => ALLY_DIPLOMACY_UNION,\n    'enter_delay' => -1,\n    'exit_delay'  => $config->fleet_bashing_war_delay,\n  ),\n  ALLY_DIPLOMACY_MASTER        => array(\n    'relation_id' => ALLY_DIPLOMACY_MASTER,\n    'enter_delay' => -1,\n    'exit_delay'  => 0,\n  ),\n  ALLY_DIPLOMACY_SLAVE         => array(\n    'relation_id' => ALLY_DIPLOMACY_SLAVE,\n    'enter_delay' => -1,\n    'exit_delay'  => $config->fleet_bashing_war_delay,\n  )\n  */\n);\n\n// factor -> price_factor, perhour_factor\n$sn_data = array();\n\nrequire_once('vars_structures.php');\nrequire_once('vars_combats.php');\nrequire_once('vars_powerups.php');\n\n$sn_data += [\n  UNIT_PLAYER_EMPIRE_SPY => [\n    'type'     => UNIT_INTERNAL,\n    'location' => LOC_USER,\n//    P_BONUS_TYPE => BONUS_ADD,\n  ],\n  UNIT_FLEET_PLANET_SPY  => [\n    'type'     => UNIT_INTERNAL,\n    'location' => LOC_USER,\n//    P_BONUS_TYPE => BONUS_ADD,\n  ],\n\n\n  RES_METAL       => [\n    'name'       => 'metal',\n    'type'       => UNIT_RESOURCES,\n    'location'   => LOC_PLANET,\n    P_BONUS_TYPE => BONUS_ABILITY,\n    P_STACKABLE  => true,\n  ],\n  RES_CRYSTAL     => [\n    'name'       => 'crystal',\n    'type'       => UNIT_RESOURCES,\n    'location'   => LOC_PLANET,\n    P_BONUS_TYPE => BONUS_ABILITY,\n    P_STACKABLE  => true,\n  ],\n  RES_DEUTERIUM   => [\n    'name'       => 'deuterium',\n    'type'       => UNIT_RESOURCES,\n    'location'   => LOC_PLANET,\n    P_BONUS_TYPE => BONUS_ABILITY,\n    P_STACKABLE  => true,\n  ],\n  RES_ENERGY      => [\n    'name'       => 'energy',\n    'type'       => UNIT_RESOURCES,\n    'location'   => LOC_PLANET,\n    P_BONUS_TYPE => BONUS_ABILITY,\n    P_STACKABLE  => true,\n  ],\n  RES_DARK_MATTER => [\n    'name'       => 'dark_matter',\n    'type'       => UNIT_RESOURCES,\n    'location'   => LOC_USER,\n    P_BONUS_TYPE => BONUS_ABILITY,\n    P_STACKABLE  => true,\n  ],\n  RES_METAMATTER  => [\n    'name'       => 'metamatter',\n    'type'       => UNIT_RESOURCES,\n    'location'   => LOC_USER,\n    P_BONUS_TYPE => BONUS_ABILITY,\n    P_STACKABLE  => true,\n  ],\n\n  UNIT_SECTOR => [\n    'name'       => 'field_max',\n    'type'       => UNIT_SECTOR,\n    'location'   => LOC_PLANET,\n    'cost'       => [\n      RES_DARK_MATTER => 1000,\n      'factor'        => 1.01,\n    ],\n    P_BONUS_TYPE => BONUS_ABILITY,\n  ],\n\n  UNIT_PLANET_DENSITY => [\n    'name'       => 'density',\n    'type'       => UNIT_PLANET_DENSITY,\n    'location'   => LOC_PLANET,\n    'cost'       => [\n      RES_DARK_MATTER => 2000,\n      'factor'        => 1,\n    ],\n    P_BONUS_TYPE => BONUS_ABILITY,\n  ],\n\n  UNIT_GROUP => [\n    // Missions\n    /*\n    mission = array(\n    'DESTINATION' => EMPTY/SAME/PLAYER/ALLY\n    'ONE_WAY' => true/false, // Is it mission one-way like Relocate/Colonize?\n    'DURATION' => array(duration list  in  second)/false,  //  List  of  possible durations\n    'AGGRESIVE' => true/false, // Should aggresive trigger rise?\n    'AJAX' => true/false, // Is mission can be launch via ajax?\n    'REQUIRE' => array( // requirements for mission. Empty = any unit from sn_get_groups('fleet')\n      <any unit_id> => 0 // require any number\n      <any unit_id> => <number> // require at least <number>\n    ),\n    );\n    */\n    'missions' => [\n      // For almost all missions lock statuses is for EVENT_FLT_ARRIVE\n      MT_ATTACK => [\n        'transport'  => false,\n        'isAttack'   => true,\n      ],\n\n      MT_AKS => [\n        'transport'  => false,\n        'isAttack'   => true,\n      ],\n\n      MT_DESTROY => [\n        'transport'  => false,\n        'isAttack'   => true,\n      ],\n\n      MT_SPY => [\n        'transport'  => false,\n        'AJAX'       => true,\n      ],\n\n      MT_HOLD => [\n        'transport'  => false,\n      ],\n\n\n      MT_TRANSPORT => [\n        'transport'  => true,\n      ],\n\n      MT_RELOCATE => [\n        'transport'  => true,\n      ],\n\n      MT_RECYCLE => [\n        'transport'  => false,\n        'AJAX'       => true,\n      ],\n\n      MT_EXPLORE => [\n        'transport'  => false,\n      ],\n\n      MT_COLONIZE => [\n        'transport'  => true,\n      ],\n\n      MT_MISSILE => [\n        'transport'  => false,\n        'AJAX'       => true,\n      ],\n    ],\n\n    GROUP_DESIGN_OPTION_BLOCKS => [\n      GROUP_DESIGN_BLOCK_TUTORIAL      => [],\n      GROUP_DESIGN_BLOCK_FLEET_COMPOSE => [],\n      GROUP_DESIGN_BLOCK_UNIVERSE      => [],\n      GROUP_DESIGN_BLOCK_NAVBAR        => [],\n      GROUP_DESIGN_BLOCK_RESOURCEBAR   => [],\n      GROUP_DESIGN_BLOCK_PLANET_SORT   => [],\n      GROUP_DESIGN_BLOCK_COMMON_ONE    => [],\n      GROUP_DESIGN_BLOCK_COMMON_TWO    => [],\n    ],\n\n    'planet_images' => [\n      'trocken'    => ['01', '02', '03', '04', '05', '06', '07', '08', '09', '10'],\n      'dschjungel' => ['01', '02', '03', '04', '05', '06', '07', '08', '09', '10'],\n      'normaltemp' => ['01', '02', '03', '04', '05', '06', '07'],\n      'wasser'     => ['01', '02', '03', '04', '05', '06', '07', '08', '09'],\n      'eis'        => ['01', '02', '03', '04', '05', '06', '07', '08', '09', '10'],\n    ],\n\n    'planet_generator' => [\n      0  => [ // HomeWorld\n        't_max_min'     => 40, // Tmax 40\n        't_max_max'     => 40,\n        't_delta_min'   => 40, // Tmin 0\n        't_delta_max'   => 40,\n        'size_min'      => $config->initial_fields,\n        'size_max'      => $config->initial_fields,\n        'core_types'    => [PLANET_DENSITY_STANDARD,],\n        'planet_images' => ['normaltemp'],\n      ],\n      1  => [\n        't_max_min'     => 100,\n        't_max_max'     => 150,\n        't_delta_min'   => 5,\n        't_delta_max'   => 20,\n        'size_min'      => 50,\n        'size_max'      => 150,\n        'core_types'    => [\n          PLANET_DENSITY_STANDARD,\n\n          //PLANET_DENSITY_ICE_WATER, PLANET_DENSITY_ICE_METHANE, PLANET_DENSITY_ICE_HYDROGEN,\n          PLANET_DENSITY_CRYSTAL_STONE, // PLANET_DENSITY_CRYSTAL_SILICATE, PLANET_DENSITY_CRYSTAL_RAW,\n          PLANET_DENSITY_METAL_ORE, PLANET_DENSITY_METAL_PERIDOT, PLANET_DENSITY_METAL_RAW,\n        ],\n        'planet_images' => ['trocken'],\n      ],\n      2  => [\n        't_max_min'     => 90,\n        't_max_max'     => 145,\n        't_delta_min'   => 5,\n        't_delta_max'   => 25,\n        'size_min'      => 80,\n        'size_max'      => 180,\n        'core_types'    => [\n          PLANET_DENSITY_STANDARD,\n\n          // PLANET_DENSITY_ICE_WATER, PLANET_DENSITY_ICE_METHANE, PLANET_DENSITY_ICE_HYDROGEN,\n          PLANET_DENSITY_CRYSTAL_STONE, PLANET_DENSITY_CRYSTAL_SILICATE, // PLANET_DENSITY_CRYSTAL_RAW,\n          PLANET_DENSITY_METAL_ORE, PLANET_DENSITY_METAL_PERIDOT, PLANET_DENSITY_METAL_RAW,\n        ],\n        'planet_images' => ['trocken'],\n      ],\n      3  => [\n        't_max_min'     => 70,\n        't_max_max'     => 135,\n        't_delta_min'   => 5,\n        't_delta_max'   => 30,\n        'size_min'      => 100,\n        'size_max'      => 210,\n        'core_types'    => [\n          PLANET_DENSITY_STANDARD,\n          PLANET_DENSITY_ICE_WATER, // PLANET_DENSITY_ICE_METHANE, PLANET_DENSITY_ICE_HYDROGEN,\n          PLANET_DENSITY_CRYSTAL_STONE, PLANET_DENSITY_CRYSTAL_SILICATE, // PLANET_DENSITY_CRYSTAL_RAW,\n          PLANET_DENSITY_METAL_ORE, PLANET_DENSITY_METAL_PERIDOT, // PLANET_DENSITY_METAL_RAW,\n        ],\n        'planet_images' => ['trocken'],\n      ],\n      4  => [\n        't_max_min'     => 40,\n        't_max_max'     => 110,\n        't_delta_min'   => 10,\n        't_delta_max'   => 35,\n        'size_min'      => 130,\n        'size_max'      => 240,\n        'core_types'    => [\n          PLANET_DENSITY_STANDARD,\n          PLANET_DENSITY_ICE_WATER, // PLANET_DENSITY_ICE_METHANE, PLANET_DENSITY_ICE_HYDROGEN,\n          PLANET_DENSITY_CRYSTAL_STONE, // PLANET_DENSITY_CRYSTAL_SILICATE, // PLANET_DENSITY_CRYSTAL_RAW,\n          PLANET_DENSITY_METAL_ORE, PLANET_DENSITY_METAL_PERIDOT, // PLANET_DENSITY_METAL_RAW,\n        ],\n        'planet_images' => ['dschjungel'],\n      ],\n      5  => [\n        't_max_min'     => 25,\n        't_max_max'     => 100,\n        't_delta_min'   => 10,\n        't_delta_max'   => 40,\n        'size_min'      => 170,\n        'size_max'      => 270,\n        'core_types'    => [\n          PLANET_DENSITY_STANDARD,\n          PLANET_DENSITY_ICE_WATER, // PLANET_DENSITY_ICE_METHANE, // PLANET_DENSITY_ICE_HYDROGEN,\n          PLANET_DENSITY_CRYSTAL_STONE, PLANET_DENSITY_CRYSTAL_SILICATE, // PLANET_DENSITY_CRYSTAL_RAW,\n          PLANET_DENSITY_METAL_ORE, // PLANET_DENSITY_METAL_PERIDOT, // PLANET_DENSITY_METAL_RAW,\n        ],\n        'planet_images' => ['dschjungel'],\n      ],\n      6  => [\n        't_max_min'     => 15,\n        't_max_max'     => 95,\n        't_delta_min'   => 10,\n        't_delta_max'   => 45,\n        'size_min'      => 220,\n        'size_max'      => 300,\n        'core_types'    => [\n          PLANET_DENSITY_STANDARD,\n          PLANET_DENSITY_ICE_WATER, // PLANET_DENSITY_ICE_METHANE, // PLANET_DENSITY_ICE_HYDROGEN,\n          PLANET_DENSITY_CRYSTAL_STONE, // PLANET_DENSITY_CRYSTAL_SILICATE, // PLANET_DENSITY_CRYSTAL_RAW,\n          PLANET_DENSITY_METAL_ORE, PLANET_DENSITY_METAL_PERIDOT, // PLANET_DENSITY_METAL_RAW,\n        ],\n        'planet_images' => ['dschjungel'],\n      ],\n      7  => [\n        't_max_min'     => 5,\n        't_max_max'     => 90,\n        't_delta_min'   => 20,\n        't_delta_max'   => 50,\n        'size_min'      => 200,\n        'size_max'      => 280,\n        'core_types'    => [\n          PLANET_DENSITY_STANDARD,\n          PLANET_DENSITY_ICE_WATER, PLANET_DENSITY_ICE_METHANE, // PLANET_DENSITY_ICE_HYDROGEN,\n          PLANET_DENSITY_CRYSTAL_STONE, // PLANET_DENSITY_CRYSTAL_SILICATE, // PLANET_DENSITY_CRYSTAL_RAW,\n          PLANET_DENSITY_METAL_ORE, // PLANET_DENSITY_METAL_PERIDOT, // PLANET_DENSITY_METAL_RAW,\n        ],\n        'planet_images' => ['normaltemp'],\n      ],\n      8  => [\n        't_max_min'     => 0,\n        't_max_max'     => 80,\n        't_delta_min'   => 20,\n        't_delta_max'   => 45,\n        'size_min'      => 160,\n        'size_max'      => 250,\n        'core_types'    => [\n          PLANET_DENSITY_STANDARD,\n          PLANET_DENSITY_ICE_WATER, // PLANET_DENSITY_ICE_METHANE, // PLANET_DENSITY_ICE_HYDROGEN,\n          PLANET_DENSITY_CRYSTAL_STONE, // PLANET_DENSITY_CRYSTAL_SILICATE, // PLANET_DENSITY_CRYSTAL_RAW,\n          PLANET_DENSITY_METAL_ORE, PLANET_DENSITY_METAL_PERIDOT, // PLANET_DENSITY_METAL_RAW,\n        ],\n        'planet_images' => ['normaltemp'],\n      ],\n      9  => [\n        't_max_min'     => -10,\n        't_max_max'     => 65,\n        't_delta_min'   => 18,\n        't_delta_max'   => 40,\n        'size_min'      => 120,\n        'size_max'      => 210,\n        'core_types'    => [\n          PLANET_DENSITY_STANDARD,\n          PLANET_DENSITY_ICE_WATER, PLANET_DENSITY_ICE_METHANE, // PLANET_DENSITY_ICE_HYDROGEN,\n          PLANET_DENSITY_CRYSTAL_STONE, // PLANET_DENSITY_CRYSTAL_SILICATE, // PLANET_DENSITY_CRYSTAL_RAW,\n          PLANET_DENSITY_METAL_ORE, PLANET_DENSITY_METAL_PERIDOT, // PLANET_DENSITY_METAL_RAW,\n        ],\n        'planet_images' => ['normaltemp'],\n      ],\n      10 => [\n        't_max_min'     => -20,\n        't_max_max'     => 50,\n        't_delta_min'   => 15,\n        't_delta_max'   => 35,\n        'size_min'      => 170,\n        'size_max'      => 240,\n        'core_types'    => [\n          PLANET_DENSITY_STANDARD,\n          PLANET_DENSITY_ICE_WATER, PLANET_DENSITY_ICE_METHANE, // PLANET_DENSITY_ICE_HYDROGEN,\n          PLANET_DENSITY_CRYSTAL_STONE, // PLANET_DENSITY_CRYSTAL_SILICATE, // PLANET_DENSITY_CRYSTAL_RAW,\n          PLANET_DENSITY_METAL_ORE, // PLANET_DENSITY_METAL_PERIDOT, // PLANET_DENSITY_METAL_RAW,\n        ],\n        'planet_images' => ['wasser'],\n      ],\n      11 => [\n        't_max_min'     => -32,\n        't_max_max'     => 36,\n        't_delta_min'   => 12,\n        't_delta_max'   => 30,\n        'size_min'      => 110,\n        'size_max'      => 230,\n        'core_types'    => [\n          PLANET_DENSITY_STANDARD,\n          PLANET_DENSITY_ICE_WATER, PLANET_DENSITY_ICE_METHANE, // PLANET_DENSITY_ICE_HYDROGEN,\n          PLANET_DENSITY_CRYSTAL_STONE, PLANET_DENSITY_CRYSTAL_SILICATE, // PLANET_DENSITY_CRYSTAL_RAW,\n          PLANET_DENSITY_METAL_ORE, // PLANET_DENSITY_METAL_PERIDOT, // PLANET_DENSITY_METAL_RAW,\n        ],\n        'planet_images' => ['wasser'],\n      ],\n      12 => [\n        't_max_min'     => -45,\n        't_max_max'     => 20,\n        't_delta_min'   => 10,\n        't_delta_max'   => 25,\n        'size_min'      => 90,\n        'size_max'      => 190,\n        'core_types'    => [\n          PLANET_DENSITY_STANDARD,\n          PLANET_DENSITY_ICE_WATER, // PLANET_DENSITY_ICE_METHANE, // PLANET_DENSITY_ICE_HYDROGEN,\n          PLANET_DENSITY_CRYSTAL_STONE, PLANET_DENSITY_CRYSTAL_SILICATE, PLANET_DENSITY_CRYSTAL_RAW,\n          // PLANET_DENSITY_METAL_ORE, // PLANET_DENSITY_METAL_PERIDOT, // PLANET_DENSITY_METAL_RAW,\n        ],\n        'planet_images' => ['wasser'],\n      ],\n      13 => [\n        't_max_min'     => -55,\n        't_max_max'     => 5,\n        't_delta_min'   => 8,\n        't_delta_max'   => 20,\n        'size_min'      => 80,\n        'size_max'      => 170,\n        'core_types'    => [\n          PLANET_DENSITY_STANDARD,\n          PLANET_DENSITY_ICE_WATER, PLANET_DENSITY_ICE_METHANE, // PLANET_DENSITY_ICE_HYDROGEN,\n          PLANET_DENSITY_CRYSTAL_STONE, PLANET_DENSITY_CRYSTAL_SILICATE, PLANET_DENSITY_CRYSTAL_RAW,\n          // PLANET_DENSITY_METAL_ORE, // PLANET_DENSITY_METAL_PERIDOT, // PLANET_DENSITY_METAL_RAW,\n        ],\n        'planet_images' => ['eis'],\n      ],\n      14 => [\n        't_max_min'     => -60,\n        't_max_max'     => 0,\n        't_delta_min'   => 5,\n        't_delta_max'   => 15,\n        'size_min'      => 70,\n        'size_max'      => 150,\n        'core_types'    => [\n          PLANET_DENSITY_STANDARD,\n          PLANET_DENSITY_ICE_WATER, PLANET_DENSITY_ICE_METHANE, PLANET_DENSITY_ICE_HYDROGEN,\n          PLANET_DENSITY_CRYSTAL_STONE, PLANET_DENSITY_CRYSTAL_SILICATE, // PLANET_DENSITY_CRYSTAL_RAW,\n          // PLANET_DENSITY_METAL_ORE, // PLANET_DENSITY_METAL_PERIDOT, // PLANET_DENSITY_METAL_RAW,\n        ],\n        'planet_images' => ['eis'],\n      ],\n      15 => [\n        't_max_min'     => -65,\n        't_max_max'     => -5,\n        't_delta_min'   => 2,\n        't_delta_max'   => 10,\n        'size_min'      => 60,\n        'size_max'      => 130,\n        'core_types'    => [\n          PLANET_DENSITY_STANDARD,\n          PLANET_DENSITY_ICE_WATER, PLANET_DENSITY_ICE_METHANE, PLANET_DENSITY_ICE_HYDROGEN,\n          PLANET_DENSITY_CRYSTAL_STONE, // PLANET_DENSITY_CRYSTAL_SILICATE, // PLANET_DENSITY_CRYSTAL_RAW,\n          // PLANET_DENSITY_METAL_ORE, // PLANET_DENSITY_METAL_PERIDOT, // PLANET_DENSITY_METAL_RAW,\n        ],\n        'planet_images' => ['eis'],\n      ],\n      16 => [ // Random planet - stranger; -35 avg\n        't_max_min'     => -90,\n        't_max_max'     => +40,\n        't_delta_min'   => 2,\n        't_delta_max'   => 50,\n        'size_min'      => 30,\n        'size_max'      => 330,\n        'core_types'    => [\n          PLANET_DENSITY_STANDARD,\n\n          PLANET_DENSITY_ICE_HYDROGEN,\n          PLANET_DENSITY_ICE_METHANE,\n          PLANET_DENSITY_ICE_WATER,\n          PLANET_DENSITY_CRYSTAL_RAW,\n          PLANET_DENSITY_CRYSTAL_SILICATE,\n          PLANET_DENSITY_CRYSTAL_STONE,\n          PLANET_DENSITY_METAL_ORE,\n          PLANET_DENSITY_METAL_PERIDOT,\n          PLANET_DENSITY_METAL_RAW,\n        ],\n        'planet_images' => ['trocken', 'dschjungel', 'normaltemp', 'wasser', 'eis',],\n      ],\n    ],\n\n    'planet_density' => [\n      PLANET_DENSITY_NONE => [\n        UNIT_PLANET_DENSITY               => 250,\n        UNIT_PLANET_DENSITY_INDEX         => PLANET_DENSITY_NONE,\n        UNIT_PLANET_DENSITY_RARITY        => 0,\n        UNIT_RESOURCES                    => [\n          RES_METAL     => 0.10,\n          RES_CRYSTAL   => 0.10,\n          RES_DEUTERIUM => 1.30\n        ],\n        UNIT_PLANET_DENSITY_MAX_SECTORS   => 999,\n        UNIT_PLANET_DENSITY_MIN_ASTROTECH => 0,\n      ],\n\n      PLANET_DENSITY_ICE_HYDROGEN => [\n        UNIT_PLANET_DENSITY               => 750,\n        UNIT_PLANET_DENSITY_INDEX         => PLANET_DENSITY_ICE_HYDROGEN,\n        UNIT_PLANET_DENSITY_RARITY        => 30, // 1, // 40.00, // * 1/121 0,82645\n        UNIT_PLANET_DENSITY_RICHNESS      => PLANET_DENSITY_RICHNESS_PERFECT,\n        UNIT_RESOURCES                    => [RES_METAL => 0.20, RES_CRYSTAL => 0.60, RES_DEUTERIUM => 7.10,],\n        UNIT_PLANET_DENSITY_MAX_SECTORS   => 150,\n        UNIT_PLANET_DENSITY_MIN_ASTROTECH => 11,\n      ],\n      PLANET_DENSITY_ICE_METHANE  => [\n        UNIT_PLANET_DENSITY               => 1250,\n        UNIT_PLANET_DENSITY_INDEX         => PLANET_DENSITY_ICE_METHANE,\n        UNIT_PLANET_DENSITY_RARITY        => 130, // 6, // 6.67, // * 6,0\t4,95868\n        UNIT_PLANET_DENSITY_RICHNESS      => PLANET_DENSITY_RICHNESS_GOOD,\n        UNIT_RESOURCES                    => [RES_METAL => 0.55, RES_CRYSTAL => 0.85, RES_DEUTERIUM => 4.60,],\n        UNIT_PLANET_DENSITY_MAX_SECTORS   => 200,\n        UNIT_PLANET_DENSITY_MIN_ASTROTECH => 6,\n      ],\n      PLANET_DENSITY_ICE_WATER    => [\n        UNIT_PLANET_DENSITY               => 2000,\n        UNIT_PLANET_DENSITY_INDEX         => PLANET_DENSITY_ICE_WATER,\n        UNIT_PLANET_DENSITY_RARITY        => 450, //20, // 2.00, // * 20,0\t16,52893\n        UNIT_PLANET_DENSITY_RICHNESS      => PLANET_DENSITY_RICHNESS_AVERAGE,\n        UNIT_RESOURCES                    => [RES_METAL => 0.86, RES_CRYSTAL => 0.95, RES_DEUTERIUM => 2.20,],\n        UNIT_PLANET_DENSITY_MAX_SECTORS   => 999,\n        UNIT_PLANET_DENSITY_MIN_ASTROTECH => 0,\n      ],\n\n      PLANET_DENSITY_CRYSTAL_RAW      => [\n        UNIT_PLANET_DENSITY               => 2500,\n        UNIT_PLANET_DENSITY_INDEX         => PLANET_DENSITY_CRYSTAL_RAW,\n        UNIT_PLANET_DENSITY_RARITY        => 20, // 1, // 40.00, // *1,0\t0,82645\n        UNIT_PLANET_DENSITY_RICHNESS      => PLANET_DENSITY_RICHNESS_PERFECT,\n        UNIT_RESOURCES                    => [RES_METAL => 0.40, RES_CRYSTAL => 12.37, RES_DEUTERIUM => 0.50,],\n        UNIT_PLANET_DENSITY_MAX_SECTORS   => 150,\n        UNIT_PLANET_DENSITY_MIN_ASTROTECH => 11,\n      ],\n      PLANET_DENSITY_CRYSTAL_SILICATE => [\n        UNIT_PLANET_DENSITY               => 3500,\n        UNIT_PLANET_DENSITY_INDEX         => PLANET_DENSITY_CRYSTAL_SILICATE,\n        UNIT_PLANET_DENSITY_RARITY        => 140, // 5.71, // * 7,0\t5,78512\n        UNIT_PLANET_DENSITY_RICHNESS      => PLANET_DENSITY_RICHNESS_GOOD,\n        UNIT_RESOURCES                    => [RES_METAL => 0.67, RES_CRYSTAL => 4.50, RES_DEUTERIUM => 0.85,],\n        UNIT_PLANET_DENSITY_MAX_SECTORS   => 200,\n        UNIT_PLANET_DENSITY_MIN_ASTROTECH => 6,\n      ],\n      PLANET_DENSITY_CRYSTAL_STONE    => [\n        UNIT_PLANET_DENSITY               => 4750,\n        UNIT_PLANET_DENSITY_INDEX         => PLANET_DENSITY_CRYSTAL_STONE,\n        UNIT_PLANET_DENSITY_RARITY        => 500, // 1.90, // * 21,0\t17,35537\n        UNIT_PLANET_DENSITY_RICHNESS      => PLANET_DENSITY_RICHNESS_AVERAGE,\n        UNIT_RESOURCES                    => [RES_METAL => 0.80, RES_CRYSTAL => 2.00, RES_DEUTERIUM => 0.95,],\n        UNIT_PLANET_DENSITY_MAX_SECTORS   => 999,\n        UNIT_PLANET_DENSITY_MIN_ASTROTECH => 0,\n      ],\n\n      PLANET_DENSITY_STANDARD => [\n        UNIT_PLANET_DENSITY               => 5750,\n        UNIT_PLANET_DENSITY_INDEX         => PLANET_DENSITY_STANDARD,\n        UNIT_PLANET_DENSITY_RARITY        => 1000, // 1.0, // * 40,0\t33,05785\n        UNIT_PLANET_DENSITY_RICHNESS      => PLANET_DENSITY_RICHNESS_NORMAL,\n        UNIT_RESOURCES                    => [RES_METAL => 1.00, RES_CRYSTAL => 1.00, RES_DEUTERIUM => 1.00,],\n        UNIT_PLANET_DENSITY_MAX_SECTORS   => 999,\n        UNIT_PLANET_DENSITY_MIN_ASTROTECH => 0,\n      ],\n\n      PLANET_DENSITY_METAL_ORE     => [\n        UNIT_PLANET_DENSITY               => 7000,\n        UNIT_PLANET_DENSITY_INDEX         => PLANET_DENSITY_METAL_ORE,\n        UNIT_PLANET_DENSITY_RARITY        => 550, // 2.11, // * 19,0\t15,70248\n        UNIT_PLANET_DENSITY_RICHNESS      => PLANET_DENSITY_RICHNESS_AVERAGE,\n        UNIT_RESOURCES                    => [RES_METAL => 1.60, RES_CRYSTAL => 0.90, RES_DEUTERIUM => 0.80,],\n        UNIT_PLANET_DENSITY_MAX_SECTORS   => 999,\n        UNIT_PLANET_DENSITY_MIN_ASTROTECH => 0,\n      ],\n      PLANET_DENSITY_METAL_PERIDOT => [\n        UNIT_PLANET_DENSITY               => 8250,\n        UNIT_PLANET_DENSITY_INDEX         => PLANET_DENSITY_METAL_PERIDOT,\n        UNIT_PLANET_DENSITY_RARITY        => 120, // 8.00, // * 5,0\t4,13223\n        UNIT_PLANET_DENSITY_RICHNESS      => PLANET_DENSITY_RICHNESS_GOOD,\n        UNIT_RESOURCES                    => [RES_METAL => 4.71, RES_CRYSTAL => 0.80, RES_DEUTERIUM => 0.55,],\n        UNIT_PLANET_DENSITY_MAX_SECTORS   => 200,\n        UNIT_PLANET_DENSITY_MIN_ASTROTECH => 6,\n      ],\n      PLANET_DENSITY_METAL_RAW     => [\n        UNIT_PLANET_DENSITY               => 9500,\n        UNIT_PLANET_DENSITY_INDEX         => PLANET_DENSITY_METAL_RAW,\n        UNIT_PLANET_DENSITY_RARITY        => 25, // 40.00, // * 1,0\t0,82645\n        UNIT_PLANET_DENSITY_RICHNESS      => PLANET_DENSITY_RICHNESS_PERFECT,\n        UNIT_RESOURCES                    => [RES_METAL => 8.00, RES_CRYSTAL => 0.40, RES_DEUTERIUM => 0.25,],\n        UNIT_PLANET_DENSITY_MAX_SECTORS   => 150,\n        UNIT_PLANET_DENSITY_MIN_ASTROTECH => 11,\n      ],\n    ],\n\n    'planet_density_old' => [\n      PLANET_DENSITY_NONE             => [\n        UNIT_PLANET_DENSITY        => 1000,\n        UNIT_PLANET_DENSITY_INDEX  => PLANET_DENSITY_NONE,\n        UNIT_PLANET_DENSITY_RARITY => 0,\n        UNIT_RESOURCES             => [\n          RES_METAL     => 0.10,\n          RES_CRYSTAL   => 0.10,\n          RES_DEUTERIUM => 1.30\n        ],\n      ],\n      PLANET_DENSITY_ICE_WATER        => [\n        UNIT_PLANET_DENSITY        => 2000,\n        UNIT_PLANET_DENSITY_INDEX  => PLANET_DENSITY_ICE_WATER,\n        UNIT_PLANET_DENSITY_RARITY => 23.4,\n        UNIT_RESOURCES             => [\n          RES_METAL     => 0.30,\n          RES_CRYSTAL   => 0.20,\n          RES_DEUTERIUM => 1.20\n        ],\n      ],\n      PLANET_DENSITY_CRYSTAL_SILICATE => [\n        UNIT_PLANET_DENSITY        => 3250,\n        UNIT_PLANET_DENSITY_INDEX  => PLANET_DENSITY_CRYSTAL_SILICATE,\n        UNIT_PLANET_DENSITY_RARITY => 4.1,\n        UNIT_RESOURCES             => [\n          RES_METAL     => 0.40,\n          RES_CRYSTAL   => 1.40,\n          RES_DEUTERIUM => 0.90\n        ],\n      ],\n      PLANET_DENSITY_CRYSTAL_STONE    => [\n        UNIT_PLANET_DENSITY        => 4500,\n        UNIT_PLANET_DENSITY_INDEX  => PLANET_DENSITY_CRYSTAL_STONE,\n        UNIT_PLANET_DENSITY_RARITY => 1.4,\n        UNIT_RESOURCES             => [\n          RES_METAL     => 0.80,\n          RES_CRYSTAL   => 1.25,\n          RES_DEUTERIUM => 0.80\n        ],\n      ],\n      PLANET_DENSITY_STANDARD         => [\n        UNIT_PLANET_DENSITY        => 5750,\n        UNIT_PLANET_DENSITY_INDEX  => PLANET_DENSITY_STANDARD,\n        UNIT_PLANET_DENSITY_RARITY => 1,\n        UNIT_RESOURCES             => [\n          RES_METAL     => 1.00,\n          RES_CRYSTAL   => 1.00,\n          RES_DEUTERIUM => 1.00\n        ],\n      ],\n      PLANET_DENSITY_METAL_ORE        => [\n        UNIT_PLANET_DENSITY        => 7000,\n        UNIT_PLANET_DENSITY_INDEX  => PLANET_DENSITY_METAL_ORE,\n        UNIT_PLANET_DENSITY_RARITY => 1.5,\n        UNIT_RESOURCES             => [\n          RES_METAL     => 2.00,\n          RES_CRYSTAL   => 0.75,\n          RES_DEUTERIUM => 0.75\n        ],\n      ],\n      PLANET_DENSITY_METAL_PERIDOT    => [\n        UNIT_PLANET_DENSITY        => 8250,\n        UNIT_PLANET_DENSITY_INDEX  => PLANET_DENSITY_METAL_PERIDOT,\n        UNIT_PLANET_DENSITY_RARITY => 4.9,\n        UNIT_RESOURCES             => [\n          RES_METAL     => 3.00,\n          RES_CRYSTAL   => 0.50,\n          RES_DEUTERIUM => 0.50\n        ],\n      ],\n      PLANET_DENSITY_METAL_RAW        => [\n        UNIT_PLANET_DENSITY        => 9250,\n        UNIT_PLANET_DENSITY_INDEX  => PLANET_DENSITY_METAL_RAW,\n        UNIT_PLANET_DENSITY_RARITY => 31.4,\n        UNIT_RESOURCES             => [\n          RES_METAL     => 4.00,\n          RES_CRYSTAL   => 0.25,\n          RES_DEUTERIUM => 0.25\n        ],\n      ],\n    ],\n\n    // Planet structures list\n    UNIT_STRUCTURES_STR  => [\n      STRUC_MINE_METAL    => STRUC_MINE_METAL, STRUC_MINE_CRYSTAL => STRUC_MINE_CRYSTAL, STRUC_MINE_DEUTERIUM => STRUC_MINE_DEUTERIUM,\n      STRUC_MINE_SOLAR    => STRUC_MINE_SOLAR, STRUC_MINE_FUSION => STRUC_MINE_FUSION,\n      STRUC_FACTORY_ROBOT => STRUC_FACTORY_ROBOT, STRUC_FACTORY_HANGAR => STRUC_FACTORY_HANGAR, STRUC_FACTORY_NANO => STRUC_FACTORY_NANO,\n      STRUC_LABORATORY    => STRUC_LABORATORY, STRUC_LABORATORY_NANO => STRUC_LABORATORY_NANO,\n      STRUC_SILO          => STRUC_SILO,\n      STRUC_STORE_METAL   => STRUC_STORE_METAL, STRUC_STORE_CRYSTAL => STRUC_STORE_CRYSTAL, STRUC_STORE_DEUTERIUM => STRUC_STORE_DEUTERIUM,\n      STRUC_ALLY_DEPOSIT  => STRUC_ALLY_DEPOSIT,\n      STRUC_TERRAFORMER   => STRUC_TERRAFORMER,\n      STRUC_MOON_STATION  => STRUC_MOON_STATION, STRUC_MOON_PHALANX => STRUC_MOON_PHALANX, STRUC_MOON_GATE => STRUC_MOON_GATE,\n    ],\n    'build_allow'        => [\n      PT_PLANET => [\n        STRUC_MINE_METAL    => STRUC_MINE_METAL, STRUC_MINE_CRYSTAL => STRUC_MINE_CRYSTAL, STRUC_MINE_DEUTERIUM => STRUC_MINE_DEUTERIUM,\n        STRUC_MINE_SOLAR    => STRUC_MINE_SOLAR, STRUC_MINE_FUSION => STRUC_MINE_FUSION,\n        STRUC_FACTORY_ROBOT => STRUC_FACTORY_ROBOT, STRUC_FACTORY_HANGAR => STRUC_FACTORY_HANGAR, STRUC_FACTORY_NANO => STRUC_FACTORY_NANO,\n        STRUC_LABORATORY    => STRUC_LABORATORY, STRUC_LABORATORY_NANO => STRUC_LABORATORY_NANO,\n        STRUC_SILO          => STRUC_SILO,\n        STRUC_STORE_METAL   => STRUC_STORE_METAL, STRUC_STORE_CRYSTAL => STRUC_STORE_CRYSTAL, STRUC_STORE_DEUTERIUM => STRUC_STORE_DEUTERIUM,\n        STRUC_ALLY_DEPOSIT  => STRUC_ALLY_DEPOSIT,\n        STRUC_TERRAFORMER   => STRUC_TERRAFORMER,\n      ],\n      PT_MOON   => [\n        STRUC_FACTORY_ROBOT => STRUC_FACTORY_ROBOT, STRUC_FACTORY_HANGAR => STRUC_FACTORY_HANGAR, STRUC_FACTORY_NANO => STRUC_FACTORY_NANO,\n//        STRUC_STORE_METAL => STRUC_STORE_METAL, STRUC_STORE_CRYSTAL => STRUC_STORE_CRYSTAL, STRUC_STORE_DEUTERIUM => STRUC_STORE_DEUTERIUM,\n        STRUC_ALLY_DEPOSIT  => STRUC_ALLY_DEPOSIT,\n        STRUC_MOON_STATION  => STRUC_MOON_STATION, STRUC_MOON_PHALANX => STRUC_MOON_PHALANX, STRUC_MOON_GATE => STRUC_MOON_GATE,\n      ],\n    ],\n    // List of units that can produce resources\n    'factories'          => [\n      STRUC_MINE_METAL => STRUC_MINE_METAL, STRUC_MINE_CRYSTAL => STRUC_MINE_CRYSTAL, STRUC_MINE_DEUTERIUM => STRUC_MINE_DEUTERIUM,\n      STRUC_MINE_SOLAR => STRUC_MINE_SOLAR, STRUC_MINE_FUSION => STRUC_MINE_FUSION, SHIP_SATTELITE_SOLAR => SHIP_SATTELITE_SOLAR,\n    ],\n    // List of units that can hold resources\n    'storages'           => [\n      STRUC_STORE_METAL => STRUC_STORE_METAL, STRUC_STORE_CRYSTAL => STRUC_STORE_CRYSTAL, STRUC_STORE_DEUTERIUM => STRUC_STORE_DEUTERIUM,\n    ],\n\n    // Tech list\n    'tech'               => [\n      TECH_ARMOR           => TECH_ARMOR, TECH_WEAPON => TECH_WEAPON, TECH_SHIELD => TECH_SHIELD,\n      TECH_SPY             => TECH_SPY, TECH_COMPUTER => TECH_COMPUTER,\n      TECH_ENERGY          => TECH_ENERGY, TECH_LASER => TECH_LASER, TECH_ION => TECH_ION, TECH_PLASMA => TECH_PLASMA, TECH_HYPERSPACE => TECH_HYPERSPACE,\n      TECH_ENGINE_CHEMICAL => TECH_ENGINE_CHEMICAL, TECH_ENGINE_ION => TECH_ENGINE_ION, TECH_ENGINE_HYPER => TECH_ENGINE_HYPER,\n      // TECH_EXPEDITION => TECH_EXPEDITION, TECH_COLONIZATION => TECH_COLONIZATION,\n      TECH_ASTROTECH       => TECH_ASTROTECH,\n      TECH_GRAVITON        => TECH_GRAVITON, TECH_RESEARCH => TECH_RESEARCH,\n    ],\n\n    // Mercenaries\n    'mercenaries'        => [\n      MRC_STOCKMAN => MRC_STOCKMAN, MRC_SPY => MRC_SPY, MRC_ACADEMIC => MRC_ACADEMIC,\n      MRC_ADMIRAL  => MRC_ADMIRAL, MRC_COORDINATOR => MRC_COORDINATOR, MRC_NAVIGATOR => MRC_NAVIGATOR,\n    ],\n    // Governors\n    'governors'          => [\n      MRC_TECHNOLOGIST => MRC_TECHNOLOGIST, MRC_ENGINEER => MRC_ENGINEER, MRC_FORTIFIER => MRC_FORTIFIER\n    ],\n    // Plans\n    'plans'              => [\n      UNIT_PLAN_STRUC_MINE_FUSION => UNIT_PLAN_STRUC_MINE_FUSION,\n      UNIT_PLAN_SHIP_CARGO_SUPER  => UNIT_PLAN_SHIP_CARGO_SUPER, UNIT_PLAN_SHIP_CARGO_HYPER => UNIT_PLAN_SHIP_CARGO_HYPER,\n      UNIT_PLAN_SHIP_DEATH_STAR   => UNIT_PLAN_SHIP_DEATH_STAR, UNIT_PLAN_SHIP_SUPERNOVA => UNIT_PLAN_SHIP_SUPERNOVA,\n      UNIT_PLAN_DEF_SHIELD_PLANET => UNIT_PLAN_DEF_SHIELD_PLANET,\n    ],\n\n    // Spaceships list\n    UNIT_SHIPS_STR   => [\n      SHIP_SMALL_FIGHTER_LIGHT => SHIP_SMALL_FIGHTER_LIGHT, SHIP_SMALL_FIGHTER_HEAVY => SHIP_SMALL_FIGHTER_HEAVY,\n      SHIP_MEDIUM_DESTROYER    => SHIP_MEDIUM_DESTROYER, SHIP_LARGE_CRUISER => SHIP_LARGE_CRUISER,\n      SHIP_LARGE_BOMBER        => SHIP_LARGE_BOMBER, SHIP_LARGE_BATTLESHIP => SHIP_LARGE_BATTLESHIP, SHIP_LARGE_DESTRUCTOR => SHIP_LARGE_DESTRUCTOR,\n      SHIP_HUGE_DEATH_STAR     => SHIP_HUGE_DEATH_STAR, SHIP_HUGE_SUPERNOVA => SHIP_HUGE_SUPERNOVA,\n      SHIP_CARGO_SMALL         => SHIP_CARGO_SMALL, SHIP_CARGO_BIG => SHIP_CARGO_BIG, SHIP_CARGO_SUPER => SHIP_CARGO_SUPER, SHIP_CARGO_HYPER => SHIP_CARGO_HYPER,\n      SHIP_RECYCLER            => SHIP_RECYCLER, SHIP_COLONIZER => SHIP_COLONIZER, SHIP_SPY => SHIP_SPY, SHIP_SATTELITE_SOLAR => SHIP_SATTELITE_SOLAR\n    ],\n    // Defensive building list\n    UNIT_DEFENCE_STR => [UNIT_DEF_TURRET_MISSILE   => UNIT_DEF_TURRET_MISSILE, UNIT_DEF_TURRET_LASER_SMALL => UNIT_DEF_TURRET_LASER_SMALL,\n                         UNIT_DEF_TURRET_LASER_BIG => UNIT_DEF_TURRET_LASER_BIG, UNIT_DEF_TURRET_GAUSS => UNIT_DEF_TURRET_GAUSS,\n                         UNIT_DEF_TURRET_ION       => UNIT_DEF_TURRET_ION, UNIT_DEF_TURRET_PLASMA => UNIT_DEF_TURRET_PLASMA,\n\n                         UNIT_DEF_SHIELD_SMALL => UNIT_DEF_SHIELD_SMALL, UNIT_DEF_SHIELD_BIG => UNIT_DEF_SHIELD_BIG, UNIT_DEF_SHIELD_PLANET => UNIT_DEF_SHIELD_PLANET,\n\n                         UNIT_DEF_MISSILE_INTERCEPTOR => UNIT_DEF_MISSILE_INTERCEPTOR, UNIT_DEF_MISSILE_INTERPLANET => UNIT_DEF_MISSILE_INTERPLANET,\n    ],\n\n    // Missiles list\n    UNIT_DEF_MISSILES_STR => [UNIT_DEF_MISSILE_INTERCEPTOR => UNIT_DEF_MISSILE_INTERCEPTOR, UNIT_DEF_MISSILE_INTERPLANET => UNIT_DEF_MISSILE_INTERPLANET,],\n\n    // Combat units list\n    'combat'             => [\n      SHIP_CARGO_SMALL         => SHIP_CARGO_SMALL, SHIP_CARGO_BIG => SHIP_CARGO_BIG, SHIP_CARGO_SUPER => SHIP_CARGO_SUPER, SHIP_CARGO_HYPER => SHIP_CARGO_HYPER,\n      SHIP_SMALL_FIGHTER_LIGHT => SHIP_SMALL_FIGHTER_LIGHT, SHIP_SMALL_FIGHTER_HEAVY => SHIP_SMALL_FIGHTER_HEAVY,\n      SHIP_MEDIUM_DESTROYER    => SHIP_MEDIUM_DESTROYER, SHIP_LARGE_CRUISER => SHIP_LARGE_CRUISER, SHIP_COLONIZER => SHIP_COLONIZER, SHIP_RECYCLER => SHIP_RECYCLER,\n      SHIP_SPY                 => SHIP_SPY,\n      SHIP_LARGE_BOMBER        => SHIP_LARGE_BOMBER, SHIP_SATTELITE_SOLAR => SHIP_SATTELITE_SOLAR, SHIP_LARGE_DESTRUCTOR => SHIP_LARGE_DESTRUCTOR, SHIP_HUGE_DEATH_STAR => SHIP_HUGE_DEATH_STAR,\n      SHIP_LARGE_BATTLESHIP    => SHIP_LARGE_BATTLESHIP, SHIP_HUGE_SUPERNOVA => SHIP_HUGE_SUPERNOVA,\n      UNIT_DEF_TURRET_MISSILE  => UNIT_DEF_TURRET_MISSILE, UNIT_DEF_TURRET_LASER_SMALL => UNIT_DEF_TURRET_LASER_SMALL, UNIT_DEF_TURRET_LASER_BIG => UNIT_DEF_TURRET_LASER_BIG, UNIT_DEF_TURRET_GAUSS => UNIT_DEF_TURRET_GAUSS, UNIT_DEF_TURRET_ION => UNIT_DEF_TURRET_ION, UNIT_DEF_TURRET_PLASMA => UNIT_DEF_TURRET_PLASMA, UNIT_DEF_SHIELD_SMALL => UNIT_DEF_SHIELD_SMALL, UNIT_DEF_SHIELD_BIG => UNIT_DEF_SHIELD_BIG, UNIT_DEF_SHIELD_PLANET => UNIT_DEF_SHIELD_PLANET,\n    ],\n    // Planet active defense list\n    'defense_active'     => [\n      UNIT_DEF_TURRET_MISSILE => UNIT_DEF_TURRET_MISSILE, UNIT_DEF_TURRET_LASER_SMALL => UNIT_DEF_TURRET_LASER_SMALL, UNIT_DEF_TURRET_LASER_BIG => UNIT_DEF_TURRET_LASER_BIG, UNIT_DEF_TURRET_GAUSS => UNIT_DEF_TURRET_GAUSS, UNIT_DEF_TURRET_ION => UNIT_DEF_TURRET_ION, UNIT_DEF_TURRET_PLASMA => UNIT_DEF_TURRET_PLASMA, UNIT_DEF_SHIELD_SMALL => UNIT_DEF_SHIELD_SMALL, UNIT_DEF_SHIELD_BIG => UNIT_DEF_SHIELD_BIG, UNIT_DEF_SHIELD_PLANET => UNIT_DEF_SHIELD_PLANET,\n    ],\n    // Transports\n    'flt_transports'     => [\n      SHIP_CARGO_SMALL => SHIP_CARGO_SMALL, SHIP_CARGO_BIG => SHIP_CARGO_BIG, SHIP_CARGO_SUPER => SHIP_CARGO_SUPER, SHIP_CARGO_HYPER => SHIP_CARGO_HYPER,\n    ],\n    // Recyclers\n    'flt_recyclers'      => [\n      SHIP_RECYCLER => SHIP_RECYCLER,\n    ],\n    // Spies\n    'flt_spies'          => [\n      SHIP_SPY => SHIP_SPY,\n    ],\n    // Anti-Spies\n    'flt_spies_anti'     => [\n    ],\n    // Bombers\n    'flt_bombers'        => [\n      SHIP_LARGE_BOMBER => SHIP_LARGE_BOMBER,\n    ],\n    // Colonizers\n    'flt_colonizers'     => [\n      SHIP_COLONIZER => SHIP_COLONIZER,\n    ],\n\n    UNIT_ARTIFACTS_STR => [\n      ART_LHC            => ART_LHC, ART_HOOK_SMALL => ART_HOOK_SMALL, ART_HOOK_MEDIUM => ART_HOOK_MEDIUM, ART_HOOK_LARGE => ART_HOOK_LARGE,\n      ART_RCD_SMALL      => ART_RCD_SMALL, ART_RCD_MEDIUM => ART_RCD_MEDIUM, ART_RCD_LARGE => ART_RCD_LARGE,\n      ART_HEURISTIC_CHIP => ART_HEURISTIC_CHIP, ART_NANO_BUILDER => ART_NANO_BUILDER, // ART_DENSITY_CHANGER => ART_DENSITY_CHANGER,\n    ],\n\n    // Resource list\n    UNIT_RESOURCES_STR      => [0 => 'metal', 1 => 'crystal', 2 => 'deuterium', 3 => 'dark_matter'],\n    // Resources all\n    'resources_all'         => [RES_METAL => RES_METAL, RES_CRYSTAL => RES_CRYSTAL, RES_DEUTERIUM => RES_DEUTERIUM, RES_ENERGY => RES_ENERGY, RES_DARK_MATTER => RES_DARK_MATTER, RES_METAMATTER => RES_METAMATTER,],\n    // Resources can be produced on planet\n    'resources_planet'      => [RES_METAL => RES_METAL, RES_CRYSTAL => RES_CRYSTAL, RES_DEUTERIUM => RES_DEUTERIUM, RES_ENERGY => RES_ENERGY],\n    // Resources can be looted from planet\n    UNIT_RESOURCES_STR_LOOT => [RES_METAL => RES_METAL, RES_CRYSTAL => RES_CRYSTAL, RES_DEUTERIUM => RES_DEUTERIUM],\n    // Resources that can be tradeable in market trader\n    UNIT_RESOURCES_STR_TRADER => [RES_METAL => RES_METAL, RES_CRYSTAL => RES_CRYSTAL, RES_DEUTERIUM => RES_DEUTERIUM, RES_DARK_MATTER => RES_DARK_MATTER],\n\n    // List of data modifiers\n    GROUP_MODIFIERS_NAME    => [\n      MODIFIER_RESOURCE_CAPACITY   => [\n        MRC_STOCKMAN => MRC_STOCKMAN,\n      ],\n      MODIFIER_RESOURCE_PRODUCTION => [\n        MRC_TECHNOLOGIST => MRC_TECHNOLOGIST,\n      ],\n    ],\n\n    // Resources that can be tradeable in market trader AND be a quest_rewards\n    'quest_rewards'      => [RES_METAL => RES_METAL, RES_CRYSTAL => RES_CRYSTAL, RES_DEUTERIUM => RES_DEUTERIUM, RES_DARK_MATTER => RES_DARK_MATTER,],\n\n//      // Ques list\n//      'ques' => array(QUE_STRUCTURES, QUE_HANGAR, QUE_RESEARCH),\n\n    'STAT_COMMON' => [STAT_TOTAL => STAT_TOTAL, STAT_FLEET => STAT_FLEET, STAT_TECH => STAT_TECH, STAT_BUILDING => STAT_BUILDING, STAT_DEFENSE => STAT_DEFENSE, STAT_RESOURCE => STAT_RESOURCE,],\n    'STAT_PLAYER' => [STAT_RAID_TOTAL => STAT_RAID_TOTAL, STAT_RAID_WON => STAT_RAID_WON, STAT_RAID_LOST => STAT_RAID_LOST, STAT_LVL_BUILDING => STAT_LVL_BUILDING, STAT_LVL_TECH => STAT_LVL_TECH, STAT_LVL_RAID => STAT_LVL_RAID,],\n\n    GROUP_GROUP_ID_TO_NAMES => [\n      UNIT_STRUCTURES   => 'structures',\n      UNIT_TECHNOLOGIES => 'tech',\n      UNIT_SHIPS        => 'fleet',\n      UNIT_DEFENCE      => 'defense',\n      UNIT_MERCENARIES  => 'mercenaries',\n      UNIT_GOVERNORS    => 'governors',\n      UNIT_RESOURCES    => 'resources_all',\n      UNIT_ARTIFACTS    => 'artifacts',\n      UNIT_PLANS        => 'plans',\n    ],\n\n    GROUP_CAPITAL_BUILDING_BONUS_GROUPS => ['structures', 'defense', 'fleet',],\n\n    GROUP_UNIT_COMBAT_SORT_ORDER => [\n      SHIP_SPY,\n      SHIP_SATELLITE_SPUTNIK,\n\n      SHIP_SATTELITE_SOLAR,\n      SHIP_SATTELITE_SLOTH,\n\n      SHIP_CARGO_SMALL,\n      SHIP_CARGO_BIG,\n      SHIP_CARGO_SUPER,\n      SHIP_CARGO_GREED,\n      SHIP_CARGO_FIREFLY,\n      SHIP_CARGO_HYPER,\n\n      SHIP_COLONIZER,\n      SHIP_RECYCLER,\n      SHIP_RECYCLER_GLUTTONY,\n      SHIP_RECYCLER_BURAN,\n\n      SHIP_SMALL_FIGHTER_SOYUZ,\n      SHIP_SMALL_FIGHTER_LIGHT,\n      SHIP_SMALL_FIGHTER_WRATH,\n      SHIP_SMALL_FIGHTER_HEAVY,\n      SHIP_SMALL_FIGHTER_ASSAULT,\n      SHIP_MEDIUM_TORPEDO_SPIRAL,\n      SHIP_MEDIUM_DESTROYER,\n      SHIP_MEDIUM_FRIGATE,\n      SHIP_MEDIUM_BOMBER_ENVY,\n      SHIP_LARGE_CRUISER,\n      SHIP_LARGE_BOMBER,\n      SHIP_LARGE_BATTLESHIP,\n      SHIP_LARGE_BATTLESHIP_PRIDE,\n      SHIP_LARGE_DESTRUCTOR,\n      SHIP_HUGE_DEATH_STAR,\n      SHIP_HUGE_SUPERNOVA,\n\n      SHIP_LARGE_ORBITAL_HEAVY,\n\n      UNIT_DEF_TURRET_MISSILE,\n      UNIT_DEF_TURRET_LASER_SMALL,\n      UNIT_DEF_TURRET_LASER_BIG,\n      UNIT_DEF_TURRET_GAUSS,\n      UNIT_DEF_TURRET_ION,\n      UNIT_DEF_TURRET_PLASMA,\n\n      UNIT_DEF_SHIELD_SMALL,\n      UNIT_DEF_SHIELD_BIG,\n      UNIT_DEF_SHIELD_PLANET,\n    ],\n\n  ],\n];\n\n$sn_data['techtree'] = array(\n  UNIT_STRUCTURES         => &$sn_data[UNIT_GROUP]['build_allow'][PT_PLANET],\n  UNIT_STRUCTURES_SPECIAL => array_diff($sn_data[UNIT_GROUP]['build_allow'][PT_MOON], $sn_data[UNIT_GROUP]['build_allow'][PT_PLANET]),\n  UNIT_TECHNOLOGIES       => &$sn_data[UNIT_GROUP]['tech'],\n  UNIT_SHIPS              => &$sn_data[UNIT_GROUP]['fleet'],\n  UNIT_DEFENCE            => &$sn_data[UNIT_GROUP]['defense'],\n  UNIT_MERCENARIES        => &$sn_data[UNIT_GROUP]['mercenaries'],\n  UNIT_GOVERNORS          => &$sn_data[UNIT_GROUP]['governors'],\n  UNIT_RESOURCES          => &$sn_data[UNIT_GROUP]['resources_all'],\n  UNIT_ARTIFACTS          => &$sn_data[UNIT_GROUP]['artifacts'],\n  UNIT_PLANS              => &$sn_data[UNIT_GROUP]['plans'],\n);\n\n//All resources\n$sn_data[UNIT_GROUP]['all'] = array_merge(\n  $sn_data[UNIT_GROUP]['structures'],\n  $sn_data[UNIT_GROUP]['tech'],\n  $sn_data[UNIT_GROUP]['fleet'],\n  $sn_data[UNIT_GROUP]['defense'],\n  $sn_data[UNIT_GROUP]['mercenaries']\n);\n\n$sn_data[UNIT_GROUP]['ques'] = array(\n  QUE_STRUCTURES => array(\n    'unit_list' => $sn_data[UNIT_GROUP]['structures'],\n    'length'    => 5,\n    'mercenary' => MRC_ENGINEER,\n    'que'       => QUE_STRUCTURES,\n  ),\n\n  QUE_HANGAR => array(\n    'unit_list' => $sn_data[UNIT_GROUP]['fleet'],\n    'length'    => 5,\n    'mercenary' => MRC_ENGINEER,\n    'que'       => QUE_HANGAR,\n  ),\n\n  SUBQUE_DEFENSE => array(\n    'unit_list' => $sn_data[UNIT_GROUP]['defense'],\n    'length'    => 5,\n    'mercenary' => MRC_FORTIFIER,\n    'que'       => QUE_HANGAR,\n  ),\n\n  QUE_RESEARCH => array(\n    'unit_list' => $sn_data[UNIT_GROUP]['tech'],\n    'length'    => 1,\n    'mercenary' => MRC_ACADEMIC,\n    'que'       => QUE_RESEARCH,\n  )\n);\n\n$sn_data[UNIT_GROUP]['subques'] = array(\n  SUBQUE_PLANET => array(\n    'que'       => QUE_STRUCTURES,\n    'mercenary' => MRC_ENGINEER,\n    'unit_list' => $sn_data[UNIT_GROUP]['build_allow'][PT_PLANET],\n  ),\n\n  SUBQUE_MOON => array(\n    'que'       => QUE_STRUCTURES,\n    'mercenary' => MRC_ENGINEER,\n    'unit_list' => $sn_data[UNIT_GROUP]['build_allow'][PT_MOON],\n  ),\n\n  SUBQUE_FLEET => array(\n    'que'       => QUE_HANGAR,\n    'mercenary' => MRC_ENGINEER,\n    'unit_list' => $sn_data[UNIT_GROUP]['fleet'],\n  ),\n\n  SUBQUE_DEFENSE => array(\n    'que'       => QUE_HANGAR,\n    'mercenary' => MRC_FORTIFIER,\n    'unit_list' => $sn_data[UNIT_GROUP]['defense'],\n  ),\n\n  SUBQUE_RESEARCH => array(\n    'que'       => QUE_RESEARCH,\n    'mercenary' => MRC_ACADEMIC,\n    'unit_list' => $sn_data[UNIT_GROUP]['tech'],\n  ),\n);\n\n$sn_powerup_buy_discounts = array(\n//  PERIOD_MINUTE    => 1,\n//  PERIOD_MINUTE_3  => 1,\n//  PERIOD_MINUTE_5  => 1,\n//  PERIOD_MINUTE_10 => 1,\n//  PERIOD_DAY       => 3,\n//  PERIOD_DAY_3     => 2,\n  PERIOD_WEEK    => 1.5,\n  PERIOD_WEEK_2  => 1.2,\n  PERIOD_MONTH   => 1,\n  PERIOD_MONTH_2 => 0.9,\n  PERIOD_MONTH_3 => 0.8,\n//  PERIOD_DAY     => 3,\n//  PERIOD_DAY_3   => 2.5,\n//  PERIOD_WEEK    => 2.0, // 1.5,\n//  PERIOD_WEEK_2  => 1.5, // 1.2,\n//  PERIOD_MONTH   => 1,\n//  PERIOD_MONTH_2 => 0.9,\n//  PERIOD_MONTH_3 => 0.8,\n);\n\nglobal $sn_data_bbCodes;\n$sn_data_bbCodes = array(\n  AUTH_LEVEL_REGISTERED => array(\n    // Prefix sn:// resolves to current server URL\n    /** @lang RegExp */\n    '#sn://#isU'                                                                        => SN_ROOT_VIRTUAL,\n    // news://ID resolves to news BBCode\n    /** @lang RegExp */\n    '#news:\\/\\/(\\d+)#is'                                                                => \"[news=$1]\",\n    // [news=ID] resolves to link to news\n    /** @lang RegExp */\n    '#\\[news\\=(\\d+)\\]#is'                                                               => \"<a href=\\\"announce.php?id=$1\\\" class=\\\"link zero\\\">news://$1</a>\",\n    // [ube=ID] resolves to link to battle report\n    /** @lang RegExp */\n    '#\\[ube\\=([0-9a-zA-Z]{32})\\]#isU'                                                   => \"<a href=\\\"index.php?page=battle_report&cypher=$1\\\"><span class=\\\"battle_report_link link\\\">($1)</span></a>\",\n    // Battle report's URL from current server also resolves to special link\n    /** @lang RegExp */\n    \"#\" . SN_ROOT_VIRTUAL . \"index.php?page=battle_report&cypher=([0-9a-zA-Z]{32})#isU\" => \"<a href=\\\"index.php?page=battle_report&cypher=$1\\\"><span class=\\\"battle_report_link link\\\">($1)</span></a>\",\n\n    /** @lang RegExp */\n    '#\\[(c|color)=(white|cyan|yellow|green|pink|red|lime|maroon|orange)\\](.+)\\[/\\1\\]#isU' => \"<span style=\\\"color: $2\\\">$3</span>\",\n    /** @lang RegExp */\n    '#\\[b\\](.+)\\[/b\\]#isU'                                                                => \"<b>$1</b>\",\n    /** @lang RegExp */\n    '#\\[i\\](.+)\\[/i\\]#isU'                                                                => \"<i>$1</i>\",\n    /** @lang RegExp */\n    '#\\[u\\](.+)\\[/u\\]#isU'                                                                => '<span class=\"underline\">$1</span>',\n    /** @lang RegExp */\n    '#\\[s\\](.+)\\[/s\\]#isU'                                                                => '<span class=\"strike\">$1</span>',\n  ),\n\n  AUTH_LEVEL_ADMINISTRATOR => array(\n    // Plain URL on string start\n    /** @lang RegExp */\n    \"#^((?:ftps?|https?|sn|faq):\\/\\/[^\\s\\[]+)#i\"                => \"<a href=\\\"$1$2\\\" target=\\\"_blank\\\" class=\\\"link_external\\\">$1$2</a>\",\n    // Plain URL in the string\n    /** @lang RegExp */\n    '#([\\s\\)\\]\\}\\>])((?:ftps?|https?|sn|faq):\\/\\/[^\\s\\[\\<]+)#i' => \"$1<a href=\\\"$2$3\\\" target=\\\"_blank\\\" class=\\\"link_external\\\">$2$3</a>\",\n\n    // [urlw=URL]DESCRIPTION[urlw] - opens link in current window\n    /** @lang RegExp */\n    \"#\\[urlw=(ftps?|https?://)(.+)\\](.+)\\[/urlw\\]#isU\"        => \"<a href=\\\"$1$2\\\" class=\\\"link\\\">$3</a>\",\n    // [url=URL]DESCRIPTION[url] - opens link in new window\n    /** @lang RegExp */\n    '#\\[url=(ftps?|https?://)(.+)\\](.+)\\[/url\\]#isU'          => \"<a href=\\\"$1$2\\\" target=\\\"_blank\\\" class=\\\"link_external\\\">$3</a>\",\n\n    // Admins can use color codes and special PURPLE color\n    /** @lang RegExp */\n    '#\\[(c|color)=(\\#[0-9A-Fa-f]+|purple)\\](.+)\\[/\\1\\]#isU' => \"<span style=\\\"color: $2\\\">$3</span>\",\n  ),\n);\n\nglobal $sn_data_smiles;\n$sn_data_smiles = array(\n  AUTH_LEVEL_REGISTERED => array(\n    ':)'          => 'smile',\n    ':p:'         => 'tongue',\n//        ':D'          => 'lol',\n    'rofl'        => 'rofl',\n    ':wink:'      => 'wink',\n    ':clap:'      => 'clapping',\n    ':good:'      => 'good',\n    ':yu:'        => 'yu',\n    ':yahoo:'     => 'yahoo',\n    ':diablo:'    => 'diablo',\n    ':angel:'     => 'angel',\n    ':rose:'      => 'give_rose',\n    ':blush:'     => 'blush',\n    ':sorry:'     => 'sorry',\n    ':cool:'      => 'cool',\n    ':cool2:'     => 'dirol',\n    ':quote:'     => 'pleasantry',\n    ':shout:'     => 'shout',\n    ':unknw:'     => 'unknw',\n    ':ups:'       => 'pardon',\n    ':nea:'       => 'nea',\n    ':sarcasm:'   => 'sarcasm',\n    ':shok:'      => 'shok',\n    ':blink:'     => 'blink',\n    ':huh:'       => 'huh',\n    ':('          => 'mellow',\n    ':sad:'       => 'sad',\n    ':c:'         => 'cray',\n    ':bad:'       => 'bad',\n    ':eye:'       => 'blackeye',\n    ':bomb:'      => 'bomb',\n    ':crz:'       => 'crazy',\n    ':fool:'      => 'fool',\n    ':tease:'     => 'tease',\n    ':spiteful:'  => 'spiteful',\n    ':agr:'       => 'aggressive',\n//        ':tratata:'   => 'mill',\n    ':wall:'      => 'wall',\n    ':suicide:'   => 'suicide',\n    ':plushit:'   => 'plushit',\n    ':fr:'        => 'friends',\n    ':dr:'        => 'drinks',\n    ':popcorn:'   => 'popcorn',\n    ':coctail:'   => 'coctail',\n    ':coffee:'    => 'coffee',\n    ':accordion:' => 'accordion',\n    ':hmm:'       => 'hmm',\n    ':facepalm:'  => 'facepalm',\n    ':ban:'       => 'ban',\n//        ':bayan:'     => 'bayan',\n    ':censored:'  => 'censored',\n    ':contract:'  => 'contract',\n    ':help:'      => 'help',\n//        ':maniac:'    => 'maniac',\n    ':panic:'     => 'panic',\n    ':poke:'      => 'poke',\n    ':pray:'      => 'pray',\n    ':whistle:'   => 'whistle',\n  ),\n);\n"
  },
  {
    "path": "includes/vars_combats.php",
    "content": "<?php\n\nif (!defined('INSIDE')) {\n  die('Hack attempt!');\n}\n\nglobal $sn_data;\n$sn_data += array(\n  SHIP_CARGO_SMALL => array(\n    'name'      => 'small_ship_cargo',\n    'type'      => UNIT_SHIPS,\n    P_STACKABLE => true,\n    'location'  => LOC_PLANET,\n    P_REQUIRE   => array(STRUC_FACTORY_HANGAR => 2, TECH_ENGINE_CHEMICAL => 2),\n    'cost'      => array(\n      RES_METAL     => 2000,\n      RES_CRYSTAL   => 2000,\n      RES_DEUTERIUM => 0,\n      RES_ENERGY    => 0,\n      'factor'      => 1,\n    ),\n    P_HULL_SIZE => HULL_SIZE_SMALL,\n    'capacity'  => 5000,\n    'engine'    => array(\n      array(\n        'tech'        => TECH_ENGINE_CHEMICAL,\n        'speed'       => 5000,\n        'consumption' => 20,\n        'min_level'   => 2,\n      ),\n      array(\n        'tech'        => TECH_ENGINE_ION,\n        'speed'       => 10000,\n        'consumption' => 40,\n        'min_level'   => 5,\n      ),\n    ),\n    'shield'    => 10,\n    'armor'     => 400,\n    'attack'    => 5,\n    'amplify'   => array(SHIP_SPY => 100, SHIP_SATTELITE_SOLAR => 250,),\n  ),\n  SHIP_CARGO_BIG   => array(\n    'name'      => 'big_ship_cargo',\n    'type'      => UNIT_SHIPS,\n    P_STACKABLE => true,\n    'location'  => LOC_PLANET,\n    P_REQUIRE   => array(STRUC_FACTORY_HANGAR => 4, TECH_ENGINE_CHEMICAL => 6),\n    'cost'      => array(\n      RES_METAL     => 6000,\n      RES_CRYSTAL   => 6000,\n      RES_DEUTERIUM => 0,\n      RES_ENERGY    => 0,\n      'factor'      => 1,\n    ),\n    P_HULL_SIZE => HULL_SIZE_MEDIUM,\n    'capacity'  => 25000,\n    'shield'    => 25,\n    'armor'     => 1200,\n    'attack'    => 5,\n    'amplify'   => array(SHIP_SPY => 100, SHIP_SATTELITE_SOLAR => 250,),\n    'engine'    => array(\n      array(\n        'tech'        => TECH_ENGINE_CHEMICAL,\n        'speed'       => 7500,\n        'consumption' => 50,\n        'min_level'   => 6,\n      ),\n    ),\n  ),\n  SHIP_CARGO_SUPER => array(\n    'name'      => 'supercargo',\n    'type'      => UNIT_SHIPS,\n    P_STACKABLE => true,\n    'location'  => LOC_PLANET,\n    P_REQUIRE   => array(STRUC_FACTORY_HANGAR => 8, TECH_ENGINE_ION => 5, UNIT_PLAN_SHIP_CARGO_SUPER => 1),\n    'cost'      => array(\n      RES_METAL     => 25000,\n      RES_CRYSTAL   => 15000,\n      RES_DEUTERIUM => 5000,\n      RES_ENERGY    => 0,\n      'factor'      => 1,\n    ),\n    P_HULL_SIZE => HULL_SIZE_LARGE,\n    'capacity'  => 100000,\n    'shield'    => 50,\n    'armor'     => 3000,\n    'attack'    => 10,\n    'amplify'   => array(SHIP_SPY => 100, SHIP_SATTELITE_SOLAR => 250,),\n    'engine'    => array(\n      array(\n        'tech'        => TECH_ENGINE_ION,\n        'speed'       => 5000,\n        'consumption' => 100,\n        'min_level'   => 5,\n      ),\n    ),\n  ),\n  SHIP_CARGO_HYPER => array(\n    'name'      => 'planet_cargo_hyper',\n    'type'      => UNIT_SHIPS,\n    'location'  => LOC_PLANET,\n    P_STACKABLE => true,\n    P_REQUIRE   => array(STRUC_FACTORY_HANGAR => 10, TECH_ENGINE_HYPER => 5, UNIT_PLAN_SHIP_CARGO_HYPER => 1),\n    'cost'      => array(\n      RES_METAL     => 500000,\n      RES_CRYSTAL   => 200000,\n      RES_DEUTERIUM => 100000,\n      RES_ENERGY    => 0,\n      'factor'      => 1,\n    ),\n    P_HULL_SIZE => HULL_SIZE_HUGE,\n    'capacity'  => 1000000,\n    'shield'    => 200,\n    'armor'     => 70000,\n    'attack'    => 50,\n    'amplify'   => array(SHIP_SPY => 100, SHIP_SATTELITE_SOLAR => 250,),\n    'engine'    => array(\n      array(\n        'tech'        => TECH_ENGINE_HYPER,\n        'speed'       => 2000,\n        'consumption' => 1000,\n        'min_level'   => 5,\n      ),\n    ),\n  ),\n\n  SHIP_COLONIZER       => array(\n    'name'      => 'colonizer',\n    'type'      => UNIT_SHIPS,\n    P_STACKABLE => true,\n    'location'  => LOC_PLANET,\n    P_REQUIRE   => array(STRUC_FACTORY_HANGAR => 4, TECH_ENGINE_ION => 3, TECH_ASTROTECH => 2),\n    'cost'      => array(\n      RES_METAL     => 10000,\n      RES_CRYSTAL   => 20000,\n      RES_DEUTERIUM => 10000,\n      RES_ENERGY    => 0,\n      'factor'      => 1,\n    ),\n    P_HULL_SIZE => HULL_SIZE_LARGE,\n    'capacity'  => 7500,\n    'shield'    => 100,\n    'armor'     => 3000,\n    'attack'    => 50,\n    'amplify'   => array(SHIP_SPY => 10.001, SHIP_SATTELITE_SOLAR => 21,),\n    'engine'    => array(\n      array(\n        'tech'        => TECH_ENGINE_ION,\n        'speed'       => 2500,\n        'consumption' => 1000,\n        'min_level'   => 3,\n      ),\n    ),\n  ),\n  SHIP_RECYCLER        => array(\n    'name'      => 'recycler',\n    'type'      => UNIT_SHIPS,\n    P_STACKABLE => true,\n    'location'  => LOC_PLANET,\n    P_REQUIRE   => array(STRUC_FACTORY_HANGAR => 4, TECH_ENGINE_CHEMICAL => 6, TECH_SHIELD => 2),\n    'cost'      => array(\n      RES_METAL     => 10000,\n      RES_CRYSTAL   => 6000,\n      RES_DEUTERIUM => 2000,\n      RES_ENERGY    => 0,\n      'factor'      => 1,\n    ),\n    P_HULL_SIZE => HULL_SIZE_MEDIUM,\n    'capacity'  => 20000,\n    'shield'    => 10,\n    'armor'     => 1600,\n    'attack'    => 1,\n    'amplify'   => array(SHIP_SPY => 500.05, SHIP_SATTELITE_SOLAR => 1050,),\n    'engine'    => array(\n      array(\n        'tech'        => TECH_ENGINE_CHEMICAL,\n        'speed'       => 2000,\n        'consumption' => 300,\n        'min_level'   => 6,\n      ),\n    ),\n  ),\n  SHIP_SPY             => array(\n    'name'      => 'spy_sonde',\n    'type'      => UNIT_SHIPS,\n    P_STACKABLE => true,\n    'location'  => LOC_PLANET,\n    P_REQUIRE   => array(STRUC_FACTORY_HANGAR => 3, TECH_ENGINE_CHEMICAL => 3, TECH_SPY => 2),\n    'cost'      => array(\n      RES_METAL     => 0,\n      RES_CRYSTAL   => 1000,\n      RES_DEUTERIUM => 0,\n      RES_ENERGY    => 0,\n      'factor'      => 1,\n    ),\n    P_HULL_SIZE => HULL_SIZE_TINY,\n    'capacity'  => 5,\n    'shield'    => 0.01,\n    'armor'     => 100,\n    'attack'    => 0.01,\n    'amplify'   => array(SHIP_SPY => 1,),\n    'engine'    => array(\n      array(\n        'tech'        => TECH_ENGINE_CHEMICAL,\n        'speed'       => 100000000,\n        'consumption' => 1,\n        'min_level'   => 3,\n      ),\n    ),\n  ),\n  SHIP_SATTELITE_SOLAR => array(\n    'name'              => 'solar_satelit',\n    'type'              => UNIT_SHIPS,\n    P_STACKABLE         => true,\n    'location'          => LOC_PLANET,\n    P_REQUIRE           => array(STRUC_FACTORY_HANGAR => 1),\n    'cost'              => array(\n      RES_METAL     => 1500,\n      RES_CRYSTAL   => 2000,\n      RES_DEUTERIUM => 100,\n      RES_ENERGY    => 0,\n      'factor'      => 1,\n    ),\n    P_UNIT_PRODUCTION   => array(\n      RES_ENERGY => function ($level, $production_factor, $user, $planet_row) { return ($planet_row[\"temp_max\"] / 4 + 20) * $level * (0.1 * $production_factor); },\n    ),\n    P_MINING_IS_MANAGED => true,\n\n    P_HULL_SIZE => HULL_SIZE_TINY,\n    'capacity'  => 1,\n    'shield'    => 10,\n    'armor'     => 200,\n    'attack'    => 1,\n    'amplify'   => array(SHIP_SPY => 1,),\n    'engine'    => array(\n      array(\n        'tech'        => TECH_ENGINE_CHEMICAL,\n        'speed'       => 0,\n        'consumption' => 0,\n        'min_level'   => 0,\n      ),\n    ),\n    'comment'   => 'Пулемет',\n  ),\n\n\n  SHIP_SMALL_FIGHTER_LIGHT   => array(\n    'name'      => 'light_hunter',\n    'type'      => UNIT_SHIPS,\n    P_STACKABLE => true,\n    'location'  => LOC_PLANET,\n    P_REQUIRE   => array(STRUC_FACTORY_HANGAR => 1, TECH_ENGINE_CHEMICAL => 1),\n    'cost'      => array(\n      RES_METAL     => 3000,\n      RES_CRYSTAL   => 1000,\n      RES_DEUTERIUM => 0,\n      RES_ENERGY    => 0,\n      'factor'      => 1,\n    ),\n    P_HULL_SIZE => HULL_SIZE_SMALL,\n    'capacity'  => 50,\n    'shield'    => 10,\n    'armor'     => 400,\n    'attack'    => 50,\n    'amplify'   => array(SHIP_CARGO_SMALL => 16.4, SHIP_SPY => 10.001, SHIP_SATTELITE_SOLAR => 21,),\n    'engine'    => array(\n      array(\n        'tech'        => TECH_ENGINE_CHEMICAL,\n        'speed'       => 12500,\n        'consumption' => 20,\n        'min_level'   => 1,\n      ),\n    ),\n    'comment'   => 'Автопушка + ракеты, мелкие щиты',\n  ),\n  SHIP_SMALL_FIGHTER_HEAVY   => array(\n    'name'      => 'heavy_hunter',\n    'type'      => UNIT_SHIPS,\n    P_STACKABLE => true,\n    'location'  => LOC_PLANET,\n    P_REQUIRE   => array(STRUC_FACTORY_HANGAR => 3, TECH_ARMOR => 2, TECH_ENGINE_ION => 2),\n    'cost'      => array(\n      RES_METAL     => 6000,\n      RES_CRYSTAL   => 4000,\n      RES_DEUTERIUM => 0,\n      RES_ENERGY    => 0,\n      'factor'      => 1,\n    ),\n    P_HULL_SIZE => HULL_SIZE_SMALL,\n    'capacity'  => 100,\n    'shield'    => 25,\n    'armor'     => 1000,\n    'attack'    => 150,\n    'amplify'   => array(SHIP_CARGO_SMALL => 8.2, SHIP_SPY => 3.33367, SHIP_SATTELITE_SOLAR => 7,),\n    'engine'    => array(\n      array(\n        'tech'        => TECH_ENGINE_ION,\n        'speed'       => 10000,\n        'consumption' => 75,\n        'min_level'   => 2,\n      ),\n    ),\n    'comment'   => 'ЛеЛа+Ракеты',\n  ),\n  SHIP_SMALL_FIGHTER_ASSAULT => array(\n    'name'      => 'assault_ship',\n    'type'      => UNIT_SHIPS,\n    P_STACKABLE => true,\n    'location'  => LOC_PLANET,\n    P_REQUIRE   => array(STRUC_FACTORY_HANGAR => 3, TECH_ARMOR => 2, TECH_ENGINE_ION => 2),\n    'cost'      => array(\n      RES_METAL     => 10000,\n      RES_CRYSTAL   => 5000,\n      RES_DEUTERIUM => 1000,\n      RES_ENERGY    => 0,\n      'factor'      => 1,\n    ),\n    P_HULL_SIZE => HULL_SIZE_SMALL,\n    'capacity'  => 150,\n    'shield'    => 50,\n    'armor'     => 1500,\n    'attack'    => 281,\n    'amplify'   => array(),\n    'engine'    => array(\n      array(\n        'tech'        => TECH_ENGINE_ION,\n        'speed'       => 8000,\n        'consumption' => 100,\n        'min_level'   => 2,\n      ),\n    ),\n    'comment'   => 'ТяЛа + Ракеты х2 + чутку бомб',\n  ),\n\n  SHIP_MEDIUM_DESTROYER => array(\n    'name'      => 'crusher',\n    'type'      => UNIT_SHIPS,\n    P_STACKABLE => true,\n    'location'  => LOC_PLANET,\n    P_REQUIRE   => array(STRUC_FACTORY_HANGAR => 5, TECH_ENGINE_ION => 4, TECH_ION => 2),\n    'cost'      => array(\n      RES_METAL     => 20000,\n      RES_CRYSTAL   => 7000,\n      RES_DEUTERIUM => 2000,\n      RES_ENERGY    => 0,\n      'factor'      => 1,\n    ),\n    P_HULL_SIZE => HULL_SIZE_MEDIUM,\n    'capacity'  => 800,\n    'shield'    => 50,\n    'armor'     => 2700,\n    'attack'    => 400,\n    'amplify'   => array(SHIP_SMALL_FIGHTER_LIGHT => 6.15, SHIP_SPY => 1.25013, SHIP_SATTELITE_SOLAR => 2.625, UNIT_DEF_TURRET_MISSILE => 5.5,),\n    'engine'    => array(\n      array(\n        'tech'        => TECH_ENGINE_ION,\n        'speed'       => 15000,\n        'consumption' => 300,\n        'min_level'   => 4,\n      ),\n    ),\n    'comment'   => '1 торпеда, ракеты, лазеры',\n  ),\n  SHIP_MEDIUM_FRIGATE        => array(\n    'name'      => 'destoyer2',\n    'type'      => UNIT_SHIPS,\n    'location'  => LOC_PLANET,\n    P_STACKABLE => true,\n    P_REQUIRE   => array(STRUC_FACTORY_HANGAR => 5, TECH_ENGINE_ION => 4, TECH_ION => 2),\n    'cost'      => array(\n      RES_METAL     => 32000,\n      RES_CRYSTAL   => 11000,\n      RES_DEUTERIUM => 3000,\n      RES_ENERGY    => 0,\n      'factor'      => 1,\n    ),\n    P_HULL_SIZE => HULL_SIZE_MEDIUM,\n    'capacity'  => 1000,\n    'shield'    => 120,\n    'armor'     => 4300,\n    'attack'    => 750,\n    'amplify'   => array(),\n    'engine'    => array(\n      array(\n        'tech'        => TECH_ENGINE_ION,\n        'speed'       => 12500,\n        'consumption' => 400,\n        'min_level'   => 4,\n      ),\n    ),\n    'comment'   => 'торпеды',\n  ),\n\n  SHIP_LARGE_CRUISER    => array(\n    'name'      => 'battle_ship',\n    'type'      => UNIT_SHIPS,\n    P_STACKABLE => true,\n    'location'  => LOC_PLANET,\n    P_REQUIRE   => array(STRUC_FACTORY_HANGAR => 7, TECH_ENGINE_HYPER => 4),\n    'cost'      => array(\n      RES_METAL     => 45000,\n      RES_CRYSTAL   => 15000,\n      RES_DEUTERIUM => 0,\n      RES_ENERGY    => 0,\n      'factor'      => 1,\n    ),\n    P_HULL_SIZE => HULL_SIZE_LARGE,\n    'capacity'  => 1500,\n    'shield'    => 200,\n    'armor'     => 6000,\n    'attack'    => 1000,\n    'amplify'   => array(SHIP_SPY => 0.50005, SHIP_SATTELITE_SOLAR => 1.05, UNIT_DEF_TURRET_MISSILE => 1.76,),\n    'engine'    => array(\n      array(\n        'tech'        => TECH_ENGINE_HYPER,\n        'speed'       => 10000,\n        'consumption' => 500,\n        'min_level'   => 4,\n      ),\n    ),\n  ),\n  SHIP_LARGE_BOMBER     => array(\n    'name'      => 'bomber_ship',\n    'type'      => UNIT_SHIPS,\n    P_STACKABLE => true,\n    'location'  => LOC_PLANET,\n    P_REQUIRE   => array(TECH_ENGINE_ION => 6, STRUC_FACTORY_HANGAR => 8, TECH_PLASMA => 5),\n    'cost'      => array(\n      RES_METAL     => 50000,\n      RES_CRYSTAL   => 25000,\n      RES_DEUTERIUM => 15000,\n      RES_ENERGY    => 0,\n      'factor'      => 1,\n    ),\n    P_HULL_SIZE => HULL_SIZE_LARGE,\n    'capacity'  => 500,\n    'shield'    => 500,\n    'armor'     => 7500,\n    'attack'    => 1000,\n    'amplify'   => array(\n      SHIP_SPY                => 0.50005, SHIP_SATTELITE_SOLAR => 1.05,\n      UNIT_DEF_TURRET_MISSILE => 4.4, UNIT_DEF_TURRET_LASER_SMALL => 4.5, UNIT_DEF_TURRET_LASER_BIG => 9, UNIT_DEF_TURRET_ION => 8, UNIT_DEF_TURRET_PLASMA => 1.3,\n    ),\n    'engine'    => array(\n      array(\n        'tech'        => TECH_ENGINE_ION,\n        'speed'       => 4000,\n        'consumption' => 1000,\n        'min_level'   => 6,\n      ),\n      array(\n        'tech'        => TECH_ENGINE_HYPER,\n        'speed'       => 5000,\n        'consumption' => 1250,\n        'min_level'   => 8,\n      ),\n    ),\n  ),\n  SHIP_LARGE_BATTLESHIP => array(\n    'name'      => 'battleship',\n    'type'      => UNIT_SHIPS,\n    P_STACKABLE => true,\n    'location'  => LOC_PLANET,\n    P_REQUIRE   => array(STRUC_FACTORY_HANGAR => 8, TECH_HYPERSPACE => 5, TECH_ENGINE_HYPER => 5, TECH_LASER => 12),\n    'cost'      => array(\n      RES_METAL     => 30000,\n      RES_CRYSTAL   => 40000,\n      RES_DEUTERIUM => 15000,\n      RES_ENERGY    => 0,\n      'factor'      => 1,\n    ),\n    P_HULL_SIZE => HULL_SIZE_LARGE,\n    'capacity'  => 750,\n    'shield'    => 400,\n    'armor'     => 7000,\n    'attack'    => 700,\n    'amplify'   => array(\n      SHIP_CARGO_SUPER         => 11.57143, SHIP_CARGO_SMALL => 2.92857, SHIP_CARGO_BIG => 5.25,\n      SHIP_SMALL_FIGHTER_HEAVY => 5.85714, SHIP_MEDIUM_DESTROYER => 15.71429, SHIP_LARGE_CRUISER => 62, SHIP_SPY => 0.71436, SHIP_SATTELITE_SOLAR => 1.5,\n    ),\n    'engine'    => array(\n      array(\n        'tech'        => TECH_ENGINE_HYPER,\n        'speed'       => 10000,\n        'consumption' => 250,\n        'min_level'   => 5,\n      ),\n    ),\n  ),\n  SHIP_LARGE_DESTRUCTOR => array(\n    'name'      => 'destructor',\n    'type'      => UNIT_SHIPS,\n    P_STACKABLE => true,\n    'location'  => LOC_PLANET,\n    P_REQUIRE   => array(STRUC_FACTORY_HANGAR => 9, TECH_HYPERSPACE => 5, TECH_ENGINE_HYPER => 6),\n    'cost'      => array(\n      RES_METAL     => 60000,\n      RES_CRYSTAL   => 50000,\n      RES_DEUTERIUM => 15000,\n      RES_ENERGY    => 0,\n      'factor'      => 1,\n    ),\n    P_HULL_SIZE => HULL_SIZE_LARGE,\n    'capacity'  => 2000,\n    'shield'    => 500,\n    'armor'     => 11000,\n    'attack'    => 2000,\n    'amplify'   => array(SHIP_SPY => 0.25003, SHIP_SATTELITE_SOLAR => 0.525, SHIP_LARGE_BATTLESHIP => 7.4, UNIT_DEF_TURRET_LASER_SMALL => 1.125,),\n    'engine'    => array(\n      array(\n        'tech'        => TECH_ENGINE_HYPER,\n        'speed'       => 5000,\n        'consumption' => 1000,\n        'min_level'   => 6,\n      ),\n    ),\n  ),\n\n  SHIP_HUGE_DEATH_STAR => array(\n    'name'      => 'dearth_star',\n    'type'      => UNIT_SHIPS,\n    P_STACKABLE => true,\n    'location'  => LOC_PLANET,\n    P_REQUIRE   => array(STRUC_FACTORY_HANGAR => 12, TECH_ENGINE_HYPER => 7, TECH_GRAVITON => 1, UNIT_PLAN_SHIP_DEATH_STAR => 1),\n    'cost'      => array(\n      RES_METAL     => 5000000,\n      RES_CRYSTAL   => 4000000,\n      RES_DEUTERIUM => 1000000,\n      RES_ENERGY    => 0,\n      'factor'      => 1,\n    ),\n    P_HULL_SIZE => HULL_SIZE_HUGE,\n    'capacity'  => 1000000,\n    'shield'    => 50000,\n    'armor'     => 900000,\n    'attack'    => 200000,\n    'amplify'   => array(\n      SHIP_CARGO_SUPER => 2.025, SHIP_CARGO_SMALL => 0.41, SHIP_CARGO_BIG => 0.91875,\n      SHIP_COLONIZER   => 3.875, SHIP_RECYCLER => 2.0125, SHIP_SPY => 0.62506, SHIP_SATTELITE_SOLAR => 1.3125,\n\n      SHIP_SMALL_FIGHTER_LIGHT => 0.41, SHIP_SMALL_FIGHTER_HEAVY => 0.5125, SHIP_MEDIUM_DESTROYER => 0.45375,\n      SHIP_LARGE_CRUISER       => 0.93, SHIP_LARGE_DESTRUCTOR => 0.2875, SHIP_LARGE_BATTLESHIP => 0.03,\n\n      UNIT_DEF_TURRET_MISSILE => 0.02, UNIT_DEF_TURRET_LASER_SMALL => 0.025, UNIT_DEF_TURRET_LASER_BIG => 0.05, UNIT_DEF_TURRET_GAUSS => 0.05, UNIT_DEF_TURRET_ION => 0.25,\n    ),\n    'engine'    => array(\n      array(\n        'tech'        => TECH_ENGINE_HYPER,\n        'speed'       => 100,\n        'consumption' => 1,\n        'min_level'   => 7,\n      ),\n    ),\n  ),\n  SHIP_HUGE_SUPERNOVA  => array(\n    'name'      => 'supernova',\n    'type'      => UNIT_SHIPS,\n    P_STACKABLE => true,\n    'location'  => LOC_PLANET,\n    P_REQUIRE   => array(STRUC_FACTORY_HANGAR => 15, TECH_ENGINE_HYPER => 9, TECH_GRAVITON => 1, UNIT_PLAN_SHIP_SUPERNOVA => 1),\n    'cost'      => array(\n      RES_METAL     => 20000000,\n      RES_CRYSTAL   => 15000000,\n      RES_DEUTERIUM => 5000000,\n      RES_ENERGY    => 0,\n      'factor'      => 1,\n    ),\n    P_HULL_SIZE => HULL_SIZE_HUGE,\n    'capacity'  => 2000000,\n    'shield'    => 1000000,\n    'armor'     => 3500000,\n    'attack'    => 100000,\n    'amplify'   => array(\n      SHIP_CARGO_SMALL => 1, SHIP_CARGO_BIG => 3, SHIP_CARGO_SUPER => 6, SHIP_CARGO_HYPER => 10,\n      SHIP_RECYCLER    => 5, SHIP_SPY => 2, SHIP_SATTELITE_SOLAR => 3, SHIP_COLONIZER => 8,\n\n      SHIP_SMALL_FIGHTER_LIGHT => 0.861, SHIP_SMALL_FIGHTER_HEAVY => 0.871, SHIP_MEDIUM_DESTROYER => 5,\n      SHIP_LARGE_BOMBER        => 5, SHIP_LARGE_CRUISER => 1.86, SHIP_LARGE_BATTLESHIP => 1.11, SHIP_LARGE_DESTRUCTOR => 0.575,\n      SHIP_HUGE_DEATH_STAR     => 10, SHIP_HUGE_SUPERNOVA => 10,\n\n      UNIT_DEF_TURRET_MISSILE => 0.04, UNIT_DEF_TURRET_LASER_SMALL => 0.05, UNIT_DEF_TURRET_LASER_BIG => 0.1, UNIT_DEF_TURRET_GAUSS => 0.1, UNIT_DEF_TURRET_ION => 0.5, UNIT_DEF_TURRET_PLASMA => 1,\n      UNIT_DEF_SHIELD_SMALL   => 1, UNIT_DEF_SHIELD_BIG => 1, UNIT_DEF_SHIELD_PLANET => 1,\n    ),\n    'engine'    => array(\n      array(\n        'tech'        => TECH_ENGINE_HYPER,\n        'speed'       => 150,\n        'consumption' => 250,\n        'min_level'   => 9,\n      ),\n    ),\n  ),\n\n\n  UNIT_DEF_TURRET_MISSILE     => array(\n    'name'      => 'misil_launcher',\n    'type'      => UNIT_DEFENCE,\n    P_STACKABLE => true,\n    'location'  => LOC_PLANET,\n    P_REQUIRE   => array(STRUC_FACTORY_HANGAR => 1),\n    'cost'      => array(\n      RES_METAL     => 2000,\n      RES_CRYSTAL   => 0,\n      RES_DEUTERIUM => 0,\n      RES_ENERGY    => 0,\n      'factor'      => 1,\n    ),\n    'shield'    => 20,\n    'armor'     => 200,\n    'attack'    => 80,\n    'amplify'   => array(SHIP_SPY => 7,),\n  ),\n  UNIT_DEF_TURRET_LASER_SMALL => array(\n    'name'      => 'small_laser',\n    'type'      => UNIT_DEFENCE,\n    P_STACKABLE => true,\n    'location'  => LOC_PLANET,\n    P_REQUIRE   => array(TECH_ENERGY => 1, STRUC_FACTORY_HANGAR => 2, TECH_LASER => 3),\n    'cost'      => array(\n      RES_METAL     => 1500,\n      RES_CRYSTAL   => 500,\n      RES_DEUTERIUM => 0,\n      RES_ENERGY    => 0,\n      'factor'      => 1,\n    ),\n    'shield'    => 25,\n    'armor'     => 200,\n    'attack'    => 100,\n    'amplify'   => array(SHIP_SPY => 5,),\n  ),\n  UNIT_DEF_TURRET_LASER_BIG   => array(\n    'name'      => 'big_laser',\n    'type'      => UNIT_DEFENCE,\n    P_STACKABLE => true,\n    'location'  => LOC_PLANET,\n    P_REQUIRE   => array(TECH_ENERGY => 3, STRUC_FACTORY_HANGAR => 4, TECH_LASER => 6),\n    'cost'      => array(\n      RES_METAL     => 6000,\n      RES_CRYSTAL   => 2000,\n      RES_DEUTERIUM => 0,\n      RES_ENERGY    => 0,\n      'factor'      => 1,\n    ),\n    'shield'    => 100,\n    'armor'     => 800,\n    'attack'    => 250,\n    'amplify'   => array(SHIP_SPY => 2,),\n  ),\n  UNIT_DEF_TURRET_GAUSS       => array(\n    'name'      => 'gauss_canyon',\n    'type'      => UNIT_DEFENCE,\n    P_STACKABLE => true,\n    'location'  => LOC_PLANET,\n    P_REQUIRE   => array(STRUC_FACTORY_HANGAR => 6, TECH_ENERGY => 6, TECH_WEAPON => 3, TECH_SHIELD => 1),\n    'cost'      => array(\n      RES_METAL     => 20000,\n      RES_CRYSTAL   => 15000,\n      RES_DEUTERIUM => 2000,\n      RES_ENERGY    => 0,\n      'factor'      => 1,\n    ),\n    'armor'     => 3500,\n    'shield'    => 200,\n    'attack'    => 1100,\n    'amplify'   => array(SHIP_SPY => 0.5,),\n  ),\n  UNIT_DEF_TURRET_ION         => array(\n    'name'      => 'ionic_canyon',\n    'type'      => UNIT_DEFENCE,\n    P_STACKABLE => true,\n    'location'  => LOC_PLANET,\n    P_REQUIRE   => array(STRUC_FACTORY_HANGAR => 4, TECH_ION => 4),\n    'cost'      => array(\n      RES_METAL     => 2000,\n      RES_CRYSTAL   => 6000,\n      RES_DEUTERIUM => 0,\n      RES_ENERGY    => 0,\n      'factor'      => 1,\n    ),\n    'shield'    => 500,\n    'armor'     => 800,\n    'attack'    => 150,\n    'amplify'   => array(SHIP_SPY => 3.3,),\n  ),\n  UNIT_DEF_TURRET_PLASMA      => array(\n    'name'      => 'buster_canyon',\n    'type'      => UNIT_DEFENCE,\n    P_STACKABLE => true,\n    'location'  => LOC_PLANET,\n    P_REQUIRE   => array(STRUC_FACTORY_HANGAR => 8, TECH_PLASMA => 7),\n    'cost'      => array(\n      RES_METAL     => 50000,\n      RES_CRYSTAL   => 50000,\n      RES_DEUTERIUM => 30000,\n      RES_ENERGY    => 0,\n      'factor'      => 1,\n    ),\n    'shield'    => 300,\n    'armor'     => 10000,\n    'attack'    => 3000,\n    'amplify'   => array(SHIP_SPY => 0.17,),\n  ),\n\n  UNIT_DEF_SHIELD_SMALL  => array(\n    'name'      => 'small_protection_shield',\n    'type'      => UNIT_DEFENCE,\n    P_STACKABLE => true,\n    'max'       => 1,\n    'location'  => LOC_PLANET,\n    P_REQUIRE   => array(TECH_SHIELD => 2, STRUC_FACTORY_HANGAR => 1),\n    'cost'      => array(\n      RES_METAL     => 10000,\n      RES_CRYSTAL   => 10000,\n      RES_DEUTERIUM => 0,\n      RES_ENERGY    => 0,\n      'factor'      => 1,\n    ),\n    'shield'    => 2000,\n    'armor'     => 2000,\n    'attack'    => 1,\n    'amplify'   => array(SHIP_SPY => 500,),\n  ),\n  UNIT_DEF_SHIELD_BIG    => array(\n    'name'      => 'big_protection_shield',\n    'type'      => UNIT_DEFENCE,\n    P_STACKABLE => true,\n    'max'       => 1,\n    'location'  => LOC_PLANET,\n    P_REQUIRE   => array(TECH_SHIELD => 6, STRUC_FACTORY_HANGAR => 6),\n    'cost'      => array(\n      RES_METAL     => 50000,\n      RES_CRYSTAL   => 50000,\n      RES_DEUTERIUM => 0,\n      RES_ENERGY    => 0,\n      'factor'      => 1,\n    ),\n    'shield'    => 2000,\n    'armor'     => 10000,\n    'attack'    => 1,\n    'amplify'   => array(SHIP_SPY => 500,),\n  ),\n  UNIT_DEF_SHIELD_PLANET => array(\n    'name'      => 'planet_protector',\n    'type'      => UNIT_DEFENCE,\n    P_STACKABLE => true,\n    'max'       => 1,\n    'location'  => LOC_PLANET,\n    P_REQUIRE   => array(UNIT_PLAN_DEF_SHIELD_PLANET => 1),\n    'cost'      => array(\n      RES_METAL     => 10000000,\n      RES_CRYSTAL   => 5000000,\n      RES_DEUTERIUM => 2500000,\n      RES_ENERGY    => 0,\n      'factor'      => 1,\n    ),\n    'shield'    => 1000000,\n    'armor'     => 1500000,\n    'attack'    => 1000000,\n    'amplify'   => array(\n      SHIP_CARGO_SUPER => 0.2025, SHIP_CARGO_SMALL => 0.041, SHIP_CARGO_BIG => 0.098,\n      SHIP_COLONIZER   => 0.31, SHIP_RECYCLER => 0.161, SHIP_SPY => 0.05001, SHIP_SATTELITE_SOLAR => 0.105,\n\n      SHIP_SMALL_FIGHTER_LIGHT => 0.03075, SHIP_SMALL_FIGHTER_HEAVY => 0.0615, SHIP_MEDIUM_DESTROYER => 0.055,\n      SHIP_LARGE_CRUISER       => 0.124, SHIP_LARGE_BOMBER => 0.08, SHIP_LARGE_DESTRUCTOR => 0.023, SHIP_LARGE_BATTLESHIP => 0.037,\n    ),\n  ),\n\n\n  UNIT_DEF_MISSILE_INTERCEPTOR => array(\n    'name'      => 'interceptor_misil',\n    'type'      => UNIT_DEFENCE,\n    P_STACKABLE => true,\n    'location'  => LOC_PLANET,\n    P_REQUIRE   => array(STRUC_SILO => 2),\n    'size'      => 1,\n    'cost'      => array(\n      RES_METAL     => 8000,\n      RES_CRYSTAL   => 2000,\n      RES_DEUTERIUM => 0,\n      RES_ENERGY    => 0,\n      'factor'      => 1,\n    ),\n\n    'shield' => 1,\n    'armor'  => 1000,\n    'attack' => 1,\n  ),\n  UNIT_DEF_MISSILE_INTERPLANET => array(\n    'name'      => 'interplanetary_misil',\n    'type'      => UNIT_DEFENCE,\n    P_STACKABLE => true,\n    'location'  => LOC_PLANET,\n    P_REQUIRE   => array(STRUC_SILO => 4, TECH_ENGINE_ION => 2),\n    'size'      => 3,\n    'cost'      => array(\n      RES_METAL     => 12500,\n      RES_CRYSTAL   => 2500,\n      RES_DEUTERIUM => 10000,\n      RES_ENERGY    => 0,\n      'factor'      => 1,\n    ),\n    'capacity'  => 10,\n    'engine'    => array(\n      array(\n        'tech'        => TECH_ENGINE_ION,\n        'speed'       => 10000000,\n        'consumption' => 10,\n        'min_level'   => 2,\n      ),\n    ),\n\n    'shield' => 1,\n    'armor'  => 1500,\n    'attack' => 120000,\n  ),\n);\n"
  },
  {
    "path": "includes/vars_menu.php",
    "content": "<?php\n\nif (!defined('INSIDE')) {\n  die('Hack attempt!');\n}\n\nlng_include('menu');\n\n$active_payment_modules = SN::$gc->modules->countModulesInGroup('payment') > 0;\n\n/** @var classConfig $config */\nglobal $sn_version_check_class, $template_result, $user, $config, $lang;\nglobal $sn_menu, $sn_menu_admin;\n\n\n/*\n  'menu_triolan' => array(                     // This should be used as ID for both internal submenu insert AND as \"id\" attribute of Tx HTML-tag (see below)\n    'LEVEL'    => 'submenu',                   // Which Tx HTML tag to use. 'header' - would be used TH; 'submenu' - TD\n    'TYPE'     => 'image',                     // Menu item type: 'image' (wrapped by IMG tag), 'text' (puts \"as-is\") or 'lang' for late biding with $lang[ITEM] values. Default is 'text'\n    'CLASS'    => 'c_c',                       // Class for TD/TH element. Can be c_c, c_l, c_r or any other custom. 'c_c' default for 'header', 'c_l' default for 'text'\n    'TITLE'    => 'Triolan.COM',               // TITLE tag for Tx HTML-element\n    'ICON'     => 'menu_icon.png',             // Icon filename - would be searched in skinPath/icons/. If 'true' - icon name would be generated from menu item ID plus \".png\"\n    'ITEM'     => 'images/triolan.gif',        // Item: text, relative image URL or lang reference. Lang reference supports constants and multilevel arrays i.e. 'info[STRUC_MINE_METAL][description]'\n    'LINK'     => 'http://www.triolan.com/',   // URL\n    'BLANK'    => true,                        // Should link open in new window/tab?\n    'SPAN'     => 'lm_overview',               // Class for internal SPAN - to override <A> style. NOT COMPATIBLE WITH STYLE!\n    'STYLE'    => 'color: white',              // CSS-class for internal SPAN - to override <A> style. NOT COMPATIBLE WITH SPAN!\n    'ALT'      => 'Triolan.COM',               // ALT-tag for image\n\n    'HIDE'     => {0|1},                       // Should be this item hide?\n\n    'WRAP_START' => 'html',                    // HTML-code to put after Tx element - before menu render\n    'ITEM_FINISH => 'html',                    // HTML-code to put as last element before potential </a> tag close\n    'WRAP_END' => 'html',                      // HTML-code to put before /Tx element - after menu render\n\n    'AUTH_LEVEL' => (int),                     // Меню будет видно только пользователям с уровнем доступа выше указанного\n    'DISABLED'  => {0|1},                      // DISABLED == 1 - пункт не будет показан\n\n    'LOCATION' => '+menu_supernova_logo',      // Special atrtribute for modules' $extra_menu. SHOULD BE USE EXCLUSIVE IN MODULES!\n                                               // Format\n                                               // [-|+][<menu_item_id>]\n                                               // <menu_item_id> identifies menu item aginst new menu item would be placed. When ommited new item placed against whole menu\n                                               // -/+ indicates that new item should be placed before/after identified menu item (or whole menu). If ommited and menu item exists - new item will replace previous one\n                                               // Empty or non-existent LOCATION equivalent to '+' - place item at end of menu\n                                               // Non-existent menu_item_id treated as empty\n  ),\n*/\n$sn_menu = [\n  'menu_server_name'   => [\n    'LEVEL'    => 'text',\n    'CLASS'    => 'menu_text_t',\n    'ITEM'     => $config->game_name,\n    'MOVEABLE' => 2,\n    'HIDEABLE' => 3,\n    'DISABLED' => !empty($config->game_name) && !empty($config->menu_server_name_disabled),\n  ],\n  'menu_server_launch' => [\n    'LEVEL'    => 'text',\n    'CLASS'    => 'menu_text_t',\n    'ITEM'     => \"{$lang['sys_from']} {$config->server_start_date}\",\n    'MOVEABLE' => 2,\n    'HIDEABLE' => 3,\n    'DISABLED' => !empty($config->menu_launch_date_disabled),\n  ],\n  'menu_server_logo'   => [\n    'LEVEL'    => 'text',\n    'CLASS'    => 'menu_text_b',\n    'TYPE'     => 'image',\n    'ITEM'     => empty($config->menu_server_logo) ? 'design/images/supernova.png' : $config->menu_server_logo,\n    'LINK'     => '.',\n    'ALT'      => $config->game_name,\n    'MOVEABLE' => 2,\n    'HIDEABLE' => 3,\n    'DISABLED' => !empty($config->menu_server_logo_disabled),\n  ],\n  'menu_admin'         => [\n    'LEVEL'    => 'header',\n    'ITEM'     => $lang['user_level'][$user['authlevel']],\n    'LINK'     => 'admin/overview.php',\n    'MOVEABLE' => 2,\n    'HIDEABLE' => 3,\n    'DISABLED' => $user['authlevel'] < 1,\n  ],\n  'menu_impersonator'  => [\n    'LEVEL'    => 'header',\n    'TYPE'     => 'lang',\n    'ITEM'     => 'sys_impersonate_done',\n    'LINK'     => 'logout.php',\n    'MOVEABLE' => 2,\n    'HIDEABLE' => 3,\n    'DISABLED' => $template_result[F_IMPERSONATE_STATUS] == LOGIN_UNDEFINED,\n  ],\n\n\n  'menu_faq'               => [\n    'LEVEL'    => 'header',\n//    'LEVEL' => 'submenu',\n    'TYPE'     => 'lang',\n    'ITEM'     => 'm_faq',\n    'LINK'     => $config->url_faq,\n    'BLANK'    => true,\n    'ICON'     => true,\n    'MOVEABLE' => 2,\n    'HIDEABLE' => 3,\n    'DISABLED' => empty($config->url_faq),\n  ],\n  'menu_planet_overview'   => [\n    'LEVEL' => 'header',\n    'TYPE'  => 'lang',\n    'ITEM'  => 'sys_planet',\n    'LINK'  => 'overview.php',\n    'ICON'  => true,\n  ],\n  'menu_planet_structures' => [\n    'LEVEL' => 'submenu',\n    'TYPE'  => 'lang',\n    'ITEM'  => 'tech[UNIT_STRUCTURES]',\n    'LINK'  => 'buildings.php?mode=' . QUE_STRUCTURES,\n    'ICON'  => true,\n  ],\n  'menu_planet_shipyard'   => [\n    'LEVEL' => 'submenu',\n    'TYPE'  => 'lang',\n    'ITEM'  => 'Shipyard',\n    'LINK'  => 'buildings.php?mode=' . SUBQUE_FLEET,\n    'ICON'  => true,\n  ],\n  'menu_planet_defense'    => [\n    'LEVEL' => 'submenu',\n    'TYPE'  => 'lang',\n    'ITEM'  => 'Defense',\n    'LINK'  => 'buildings.php?mode=' . SUBQUE_DEFENSE,\n    'ICON'  => true,\n  ],\n  'menu_planet_resources'  => [\n    'LEVEL' => 'submenu',\n    'TYPE'  => 'lang',\n    'ITEM'  => 'Resources',\n    'LINK'  => 'resources.php',\n    'ICON'  => true,\n  ],\n  'menu_planet_fleets'     => [\n    'LEVEL' => 'submenu',\n    'TYPE'  => 'lang',\n    'ITEM'  => 'lm_fleet_orbiting',\n    'LINK'  => 'fleet.php',\n    'ICON'  => true,\n  ],\n\n  'menu_empire_overview' => [\n    'LEVEL' => 'header',\n    'TYPE'  => 'lang',\n    'ITEM'  => 'sys_empire',\n    'LINK'  => 'index.php?page=imperium',\n    'ICON'  => true,\n  ],\n  'menu_info_research'   => [\n    'LEVEL' => 'submenu',\n    'TYPE'  => 'lang',\n    'ITEM'  => 'Research',\n    'LINK'  => 'buildings.php?mode=' . QUE_RESEARCH,\n    'ICON'  => true,\n  ],\n  'menu_empire_techtree' => [\n    'LEVEL' => 'submenu',\n    'TYPE'  => 'lang',\n    'ITEM'  => 'Technology',\n    'LINK'  => 'index.php?page=techtree',\n    'ICON'  => true,\n  ],\n  'menu_empire_fleets'   => [\n    'LEVEL' => 'submenu',\n    'TYPE'  => 'lang',\n    'ITEM'  => 'imp_fleets',\n    'LINK'  => 'flying_fleets.php',\n    'ICON'  => true,\n  ],\n\n  'menu_empire_universe'    => [\n    'LEVEL' => 'header',\n    'TYPE'  => 'lang',\n    'ITEM'  => 'menu_universe_overview',\n    'LINK'  => 'galaxy.php?mode=0',\n  ],\n  'menu_empire_emperor'     => [\n    'LEVEL' => 'submenu',\n    'TYPE'  => 'lang',\n    'ITEM'  => 'imp_imperator',\n    'LINK'  => 'index.php?page=imperator',\n    'ICON'  => true,\n  ],\n  'menu_ally'               => [\n//    'LEVEL' => 'header',\n    'LEVEL'    => 'submenu',\n    'TYPE'     => 'lang',\n    'ITEM'     => 'sys_alliance',\n    'LINK'     => 'alliance.php',\n    'ICON'     => true,\n    'LOCATION' => '+menu_empire_emperor',\n    'DISABLED' => $config->game_mode == GAME_BLITZ,\n  ],\n  'menu_info_stats'         => [\n    'LEVEL' => 'submenu', // header\n    'TYPE'  => 'lang',\n    'ITEM'  => 'menu_stat_players',\n    'LINK'  => 'stat.php',\n    'ICON'  => true,\n  ],\n  'menu_info_records'       => [\n    'LEVEL' => 'submenu',\n    'TYPE'  => 'lang',\n    'ITEM'  => 'menu_stat_records',\n    'LINK'  => 'records.php',\n    'ICON'  => true,\n  ],\n  'menu_empire_quests'      => [\n    'LEVEL' => 'submenu',\n    'TYPE'  => 'lang',\n    'ITEM'  => 'menu_quest_list',\n    'LINK'  => 'quest.php',\n    'ICON'  => true,\n  ],\n\n//  'menu_metamatter' => !SN_GOOGLE ? array(\n//    'LEVEL' => 'header',\n//    'TYPE'  => 'lang',\n//    'ITEM'  => 'sys_metamatter',\n//    'LINK'  => 'metamatter.php',\n//  ) : array(),\n  'menu_dark_matter'        => [\n    'LEVEL' => 'header',\n    'TYPE'  => 'lang',\n    'ITEM'  => 'sys_dark_matter',\n    'LINK'  => 'dark_matter.php',\n  ],\n  'menu_empire_market'      => [\n    'LEVEL' => 'submenu',\n    'TYPE'  => 'lang',\n    'ITEM'  => 'rinok',\n    'LINK'  => 'market.php',\n    'ICON'  => true,\n  ],\n  'menu_empire_mercenaries' => [\n    'LEVEL' => 'submenu',\n    'TYPE'  => 'lang',\n    'ITEM'  => 'tech[UNIT_MERCENARIES]',\n    'LINK'  => 'officer.php?mode=' . UNIT_MERCENARIES,\n    'ICON'  => true,\n  ],\n  'menu_empire_schematics'  => [\n    'LEVEL' => 'submenu',\n    'TYPE'  => 'lang',\n    'ITEM'  => 'tech[UNIT_PLANS]',\n    'LINK'  => 'officer.php?mode=' . UNIT_PLANS,\n    'ICON'  => true,\n  ],\n  'menu_empire_artifacts'   => [\n    'LEVEL' => 'submenu',\n    'TYPE'  => 'lang',\n    'ITEM'  => 'tech[UNIT_ARTIFACTS]',\n    'LINK'  => 'artifacts.php',\n    'ICON'  => true,\n  ],\n  'menu_affiliates'         => [\n    'LEVEL' => 'submenu',\n    'TYPE'  => 'lang',\n    'ITEM'  => 'm_affilates',\n    'LINK'  => 'affilates.php',\n    'ICON'  => true,\n  ],\n\n  'menu_comm_messages' => [\n//    'LEVEL' => 'submenu',\n    'LEVEL' => 'header',\n    'TYPE'  => 'lang',\n    'ITEM'  => 'Messages',\n    'LINK'  => 'messages.php',\n    'ICON'  => true,\n  ],\n  'menu_comm_chat'     => [\n    'LEVEL' => 'submenu',\n    'TYPE'  => 'lang',\n    'ITEM'  => 'Chat',\n    'LINK'  => 'index.php?page=chat&mode=' . CHAT_MODE_COMMON,\n    'ICON'  => true,\n  ],\n  'menu_ally_chat'     => [\n    'LEVEL'    => 'submenu',\n    'TYPE'     => 'lang',\n    'ITEM'     => 'AllyChat',\n    'LINK'     => 'index.php?page=chat&mode=' . CHAT_MODE_ALLY,\n    'ICON'     => true,\n    'DISABLED' => $config->game_mode == GAME_BLITZ,\n  ],\n\n  'menu_comm_forum'      => [\n    'LEVEL'    => 'submenu',\n    'TYPE'     => 'lang',\n    'ITEM'     => 'm_forum',\n    'LINK'     => $config->url_forum,\n    'BLANK'    => true,\n    'ICON'     => true,\n    'DISABLED' => empty($config->url_forum),\n  ],\n\n//  'menu_utils' => array(\n//    'LEVEL' => 'header',\n//    'TYPE'  => 'lang',\n//    'ITEM'  => 'm_others',\n//  ),\n  'menu_utils_search'    => [\n    'LEVEL'    => 'header',\n//    'LEVEL' => 'submenu',\n    'TYPE'     => 'lang',\n    'ITEM'     => 'Search',\n    'LINK'     => 'search.php',\n    'ICON'     => true,\n    'DISABLED' => $config->game_mode == GAME_BLITZ,\n  ],\n  'menu_utils_shortcuts' => [\n    'LEVEL' => 'submenu',\n    'TYPE'  => 'lang',\n    'ITEM'  => 'lm_shortcuts',\n    'LINK'  => 'notes.php',\n    'ICON'  => true,\n  ],\n  'menu_utils_buddies'   => [\n    'LEVEL' => 'submenu',\n    'TYPE'  => 'lang',\n    'ITEM'  => 'Buddylist',\n    'LINK'  => 'buddy.php',\n    'ICON'  => true,\n  ],\n  'menu_utils_reports'   => [\n    'LEVEL' => 'submenu',\n    'TYPE'  => 'lang',\n    'ITEM'  => 'lm_combat_reports',\n    'LINK'  => 'viewreport.php',\n    'ICON'  => true,\n  ],\n  'menu_utils_simulator' => [\n    'LEVEL' => 'submenu',\n    'TYPE'  => 'lang',\n    'ITEM'  => 'm_simulator',\n    'LINK'  => 'simulator.php',\n    'ICON'  => true,\n  ],\n  'menu_rules'           => [\n    'LEVEL'    => 'header',\n    'TYPE'     => 'lang',\n    'ITEM'     => 'sys_game_rules',\n    'LINK'     => $config->url_rules,\n    'BLANK'    => true,\n    'ICON'     => true,\n    'DISABLED' => empty($config->url_rules),\n  ],\n\n  'menu_news'          => [\n    'LEVEL'       => 'submenu',\n    'ITEM'        => $lang['news_title'],\n    'ITEM_FINISH' => ($user['news_lastread'] < SN::$config->var_news_last ? \"&nbsp;<span class=\\\"fresh\\\">{$lang['lm_announce_fresh']}</span>\" : ''),\n    'LINK'        => 'announce.php',\n    'ICON'        => true,\n  ],\n  'menu_documentation' => [\n    'TYPE'  => 'lang',\n    'ITEM'  => 'sys_game_documentation',\n    'LINK'  => 'docs/html/readme.html',\n    'BLANK' => true,\n    'ICON'  => true,\n  ],\n  'menu_info_ban'      => [\n    'LEVEL' => 'submenu',\n    'TYPE'  => 'lang',\n    'ITEM'  => 'lm_banned',\n    'LINK'  => 'banned.php',\n    'ICON'  => true,\n  ],\n  'menu_info_server'   => [\n    'LEVEL' => 'submenu',\n    'TYPE'  => 'lang',\n    'ITEM'  => 'lm_server_info',\n    'LINK'  => 'server_info.php',\n    'ICON'  => true,\n  ],\n  'menu_info_admins'   => [\n    'LEVEL' => 'submenu',\n    'TYPE'  => 'lang',\n    'ITEM'  => 'commun',\n    'LINK'  => 'index.php?page=contact',\n    'ICON'  => true,\n  ],\n\n  'menu_options' => [\n    'LEVEL'    => 'header',\n    'TYPE'     => 'lang',\n    'ITEM'     => 'Options',\n    'LINK'     => 'index.php?page=options',\n    'MOVEABLE' => 2,\n    'HIDEABLE' => 3,\n  ],\n\n  'menu_logout' => [\n    'LEVEL'    => 'header',\n    'TYPE'     => 'lang',\n    'ITEM'     => 'Logout',\n    'LINK'     => 'logout.php',\n    'MOVEABLE' => 2,\n    'HIDEABLE' => 3,\n  ],\n\n  'menu_extra' => [\n    'LEVEL'    => 'submenu',\n    'CLASS'    => 'c_c',\n    'ITEM'     => $config->advGoogleLeftMenuCode,\n    'MOVEABLE' => 2,\n    'HIDEABLE' => 3,\n    'DISABLED' =>\n      !$config->advGoogleLeftMenuIsOn\n      || empty($config->advGoogleLeftMenuCode)\n      || empty($user)\n      || SN_TIME_NOW - $user['register_time'] < PERIOD_WEEK\n      || mrc_get_level($user, [], UNIT_PREMIUM)\n    ,\n  ],\n\n  'menu_supernova_logo' => [\n    'LEVEL'    => 'submenu',\n    'TYPE'     => 'image',\n    'CLASS'    => 'c_c',\n    'ITEM'     => 'design/images/supernova.png',\n    'LINK'     => 'http://supernova.ws/index-ru.html',\n    'ALT'      => 'Powered by \\'Project \"SuperNova.WS\"\\' engine',\n    'BLANK'    => true,\n    'MOVEABLE' => 2,\n    'HIDEABLE' => 3,\n  ],\n\n  /*\n    'menu_triolan' => array(\n      'LEVEL' => 'submenu',\n      'TYPE'  => 'image',\n      'CLASS' => 'c_c',\n      'ITEM'  => 'images/triolan.gif',\n      'LINK'  => 'http://www.triolan.com/',\n      'BLANK' => true,\n      'ALT'   => 'Hosted @ Triolan.COM',\n    ),\n  */\n];\n\n\n//$sn_menu_admin = defined('IN_ADMIN') && IN_ADMIN === true ? array(\n$sn_menu_admin = [\n  'menu_admin_server_name' => [\n    'LEVEL'               => 'header',\n    'TYPE'                => 'text',\n    'ITEM'                => $config->game_name,\n    MENU_FIELD_AUTH_LEVEL => AUTH_LEVEL_MODERATOR,\n  ],\n  'menu_admin_server_time' => [\n    'TYPE'       => 'text',\n    'ITEM'       => '',\n    'AUTH_LEVEL' => AUTH_LEVEL_MODERATOR,\n  ],\n\n  'menu_admin_version_check' => [\n    'LEVEL'      => 'header',\n    'TYPE'       => 'lang',\n    'ITEM'       => 'adm_opt_ver_check',\n    'AUTH_LEVEL' => AUTH_LEVEL_MODERATOR,\n  ],\n  'menu_admin_version_info'  => [\n    'TYPE'       => 'text',\n    'ITEM'       => ($config->server_updater_check_last ? date(FMT_DATE, $config->server_updater_check_last) : '') . '<div class=\"' .\n      $sn_version_check_class[$config->server_updater_check_result] . '\">' . $lang['adm_opt_ver_response_short'][$config->server_updater_check_result] . '</div>',\n    'AUTH_LEVEL' => AUTH_LEVEL_MODERATOR,\n  ],\n\n  'USER_AUTHLEVEL_NAME' => [\n    'LEVEL'      => 'header',\n    'ITEM'       => $lang['user_level'][$user['authlevel']],\n    'LINK'       => 'index.php',\n    'AUTH_LEVEL' => AUTH_LEVEL_MODERATOR,\n  ],\n\n  'menu_admin_overview' => [\n    'LEVEL'      => 'header',\n    'TYPE'       => 'lang',\n    'ITEM'       => 'adm_over',\n    'LINK'       => 'admin/overview.php',\n    'AUTH_LEVEL' => AUTH_LEVEL_ADMINISTRATOR,\n  ],\n\n  'menu_admin_configuration' => [\n    'LEVEL'      => 'header',\n    'TYPE'       => 'lang',\n    'ITEM'       => 'adm_conf',\n    'LINK'       => 'admin/settings.php',\n    'AUTH_LEVEL' => AUTH_LEVEL_ADMINISTRATOR,\n  ],\n  'menu_admin_modules'       => [\n    'TYPE'       => 'lang',\n    'ITEM'       => 'menu_admin_modules',\n    'LINK'       => 'index.php?page=admin/admin_modules',\n    'AUTH_LEVEL' => AUTH_LEVEL_ADMINISTRATOR,\n  ],\n  'menu_admin_quests'        => [\n    'TYPE'       => 'lang',\n    'ITEM'       => 'qst_quests',\n    'LINK'       => 'admin/adm_quest.php',\n    'AUTH_LEVEL' => AUTH_LEVEL_ADMINISTRATOR,\n  ],\n\n  'menu_admin_dark_matter'        => [\n    'LEVEL'      => 'header',\n    'TYPE'       => 'lang',\n    'ITEM'       => 'dark_matter',\n    'LINK'       => 'admin/admin_darkmatter.php',\n    'AUTH_LEVEL' => AUTH_LEVEL_ADMINISTRATOR,\n  ],\n  'menu_admin_matter_analyze'     => [\n    'TYPE'       => 'lang',\n    'ITEM'       => 'matter_analyze',\n    'LINK'       => 'admin/admin_analyze_matter.php',\n    'AUTH_LEVEL' => AUTH_LEVEL_ADMINISTRATOR,\n  ],\n\n//  'menu_admin_metamatter_header'  => [\n//    'LEVEL'      => 'header',\n//    'TYPE'       => 'lang',\n//    'ITEM'       => 'adm_metametter_payment',\n//    'AUTH_LEVEL' => AUTH_LEVEL_ADMINISTRATOR,\n//    'DISABLED'   => !$active_payment_modules,\n//  ],\n  'menu_admin_metamatter'         => [\n    'LEVEL'      => 'header',\n    'TYPE'       => 'lang',\n    'ITEM'       => 'sys_metamatter',\n    'LINK'       => 'admin/adm_metamatter.php',\n    'AUTH_LEVEL' => AUTH_LEVEL_ADMINISTRATOR,\n    'DISABLED'   => !$active_payment_modules,\n  ],\n  'menu_admin_metametter_payment' => [\n    'TYPE'       => 'lang',\n    'ITEM'       => 'adm_pay',\n//    'LINK'       => 'admin/adm_payment.php',\n    'LINK'       => 'index.php?page=admin/admin_payment',\n    'AUTH_LEVEL' => AUTH_LEVEL_ADMINISTRATOR,\n    'DISABLED'   => !$active_payment_modules,\n  ],\n\n  'menu_admin_player'     => [\n    'LEVEL'      => 'header',\n    'TYPE'       => 'lang',\n    'ITEM'       => 'player',\n    'LINK'       => 'admin/userlist.php',\n    'AUTH_LEVEL' => AUTH_LEVEL_ADMINISTRATOR,\n  ],\n  'menu_admin_player_ban' => [\n    'TYPE'       => 'lang',\n    'ITEM'       => 'adm_ban_unban',\n    'LINK'       => 'admin/banned.php',\n    'AUTH_LEVEL' => AUTH_LEVEL_MODERATOR,\n  ],\n  'menu_admin_mining'     => [\n    'TYPE'       => 'lang',\n    'ITEM'       => 'menu_admin_mining',\n    'LINK'       => 'index.php?page=admin/admin_mining',\n    'AUTH_LEVEL' => AUTH_LEVEL_ADMINISTRATOR,\n  ],\n//  'menu_admin_player_list' => array(\n//    'TYPE'       => 'lang',\n//    'ITEM'       => 'adm_plrlst',\n//    'LINK'       => 'admin/userlist.php',\n//    'AUTH_LEVEL' => 3,\n//  ),\n\n  'menu_admin_ally'                => [\n    'LEVEL'      => 'header',\n    'TYPE'       => 'lang',\n    'ITEM'       => 'menu_admin_ally',\n    'LINK'       => 'index.php?page=admin/admin_ally',\n    'AUTH_LEVEL' => 3,\n  ],\n\n//  'menu_admin_universe'            => array(\n//    'LEVEL'      => 'header',\n//    'TYPE'       => 'lang',\n//    'ITEM'       => 'sys_universe',\n//    'AUTH_LEVEL' => 3,\n//  ),\n  'menu_admin_planet_list_active'  => [\n    'LEVEL'      => 'header',\n    'TYPE'       => 'lang',\n    'ITEM'       => 'adm_planet_active',\n    'LINK'       => 'admin/adm_planet_list.php?planet_active=1',\n    'AUTH_LEVEL' => 3,\n  ],\n  'menu_admin_planet_list_planets' => [\n    'TYPE'       => 'lang',\n    'ITEM'       => 'adm_pltlst',\n    'LINK'       => 'admin/adm_planet_list.php?planet_type=' . PT_PLANET,\n    'AUTH_LEVEL' => 3,\n  ],\n  'menu_admin_planet_list_moons'   => [\n    'TYPE'       => 'lang',\n    'ITEM'       => 'adm_moonlst',\n    'LINK'       => 'admin/adm_planet_list.php?planet_type=' . PT_MOON,\n    'AUTH_LEVEL' => 3,\n  ],\n  'menu_admin_planet_moon_add'     => [\n    'TYPE'       => 'lang',\n    'ITEM'       => 'adm_addmoon',\n    'LINK'       => 'admin/add_moon.php',\n    'AUTH_LEVEL' => 3,\n  ],\n  'menu_admin_planet_compensate'   => [\n    'TYPE'       => 'lang',\n    'ITEM'       => 'adm_lm_compensate',\n    'LINK'       => 'admin/planet_compensate.php',\n    'AUTH_LEVEL' => 3,\n  ],\n  'menu_admin_fleets'              => [\n    'LEVEL'      => 'header',\n    'TYPE'       => 'lang',\n    'ITEM'       => 'adm_fleet',\n    'LINK'       => 'admin/adm_flying_fleets.php',\n    'AUTH_LEVEL' => 3,\n  ],\n\n  'menu_admin_utilites'    => [\n    'LEVEL'      => 'header',\n    'TYPE'       => 'lang',\n    'ITEM'       => 'tool',\n    'CLASS'      => 'link',\n    'LINK'       => 'admin/tools.php',\n    'AUTH_LEVEL' => 3,\n  ],\n  'menu_admin_statbuilder' => [\n    'TYPE'       => 'lang',\n    'ITEM'       => 'adm_updpt',\n    'LINK'       => 'admin/statbuilder.php',\n    'AUTH_LEVEL' => 3,\n  ],\n  'menu_admin_languages'   => [\n    'TYPE'       => 'lang',\n    'ITEM'       => 'adm_lng_title',\n    'LINK'       => 'admin/admin_locale.php',\n    'AUTH_LEVEL' => 3,\n  ],\n  'menu_admin_maintenance' => [\n    'TYPE'       => 'lang',\n    'ITEM'       => 'adm_maint',\n    'LINK'       => 'admin/maintenance.php',\n    'AUTH_LEVEL' => 3,\n  ],\n  'menu_admin_backup'      => [\n    'TYPE'       => 'lang',\n    'ITEM'       => 'adm_backup',\n    'LINK'       => 'admin/sxd/index.php',\n    'AUTH_LEVEL' => 3,\n  ],\n  'menu_admin_messages'    => [\n    'TYPE'       => 'lang',\n    'ITEM'       => 'adm_msg',\n    'LINK'       => 'admin/adm_message_list.php',\n    'AUTH_LEVEL' => 3,\n  ],\n  'menu_admin_chat'        => [\n    'TYPE'       => 'lang',\n    'ITEM'       => 'adm_chat',\n    'LINK'       => 'admin/admin_chat.php',\n    'AUTH_LEVEL' => 3,\n  ],\n  'menu_admin_logs'        => [\n    'TYPE'       => 'lang',\n    'ITEM'       => 'adm_log_main',\n    'LINK'       => 'admin/adm_log_main.php',\n    'AUTH_LEVEL' => 3,\n  ],\n\n  'menu_admin_exit' => [\n    'LEVEL'      => 'header',\n    'CLASS'      => 'link',\n    'TYPE'       => 'lang',\n    'ITEM'       => 'adm_back',\n    'LINK'       => 'index.php',\n    'AUTH_LEVEL' => AUTH_LEVEL_MODERATOR,\n  ],\n];\n"
  },
  {
    "path": "includes/vars_powerups.php",
    "content": "<?php\n\nif (!defined('INSIDE')) {\n  die('Hack attempt!');\n}\n\nglobal $sn_data;\n$sn_data += array(\n  TECH_COMPUTER => array(\n    'name'     => 'computer_tech',\n    'type'     => UNIT_TECHNOLOGIES,\n    'location' => LOC_USER,\n    P_REQUIRE  => array(STRUC_LABORATORY => 1),\n    'cost'     => array(\n      RES_METAL     => 0,\n      RES_CRYSTAL   => 400,\n      RES_DEUTERIUM => 600,\n      RES_ENERGY    => 0,\n      'factor'      => 2,\n    ),\n\n    P_BONUS_VALUE => 1,\n    P_BONUS_TYPE  => BONUS_ADD,\n  ),\n\n  TECH_SPY => array(\n    'name'     => 'spy_tech',\n    'type'     => UNIT_TECHNOLOGIES,\n    'location' => LOC_USER,\n    P_REQUIRE  => array(STRUC_LABORATORY => 3),\n    'cost'     => array(\n      RES_METAL     => 200,\n      RES_CRYSTAL   => 1000,\n      RES_DEUTERIUM => 200,\n      RES_ENERGY    => 0,\n      'factor'      => 2,\n    ),\n\n    P_BONUS_VALUE => 1,\n    P_BONUS_TYPE  => BONUS_ADD,\n  ),\n\n  TECH_WEAPON => array(\n    'name'     => 'military_tech',\n    'type'     => UNIT_TECHNOLOGIES,\n    'location' => LOC_USER,\n    P_REQUIRE  => array(STRUC_LABORATORY => 4),\n    'cost'     => array(\n      RES_METAL     => 800,\n      RES_CRYSTAL   => 200,\n      RES_DEUTERIUM => 0,\n      RES_ENERGY    => 0,\n      'factor'      => 2,\n    ),\n\n    P_BONUS_VALUE => 10,\n    P_BONUS_TYPE  => BONUS_PERCENT,\n  ),\n\n  TECH_SHIELD => array(\n    'name'     => 'shield_tech',\n    'type'     => UNIT_TECHNOLOGIES,\n    'location' => LOC_USER,\n    P_REQUIRE  => array(STRUC_LABORATORY => 6, TECH_ENERGY => 3),\n    'cost'     => array(\n      RES_METAL     => 200,\n      RES_CRYSTAL   => 600,\n      RES_DEUTERIUM => 0,\n      RES_ENERGY    => 0,\n      'factor'      => 2,\n    ),\n\n    P_BONUS_VALUE => 10,\n    P_BONUS_TYPE  => BONUS_PERCENT,\n  ),\n\n  TECH_ARMOR => array(\n    'name'     => 'defence_tech',\n    'type'     => UNIT_TECHNOLOGIES,\n    'location' => LOC_USER,\n    P_REQUIRE  => array(STRUC_LABORATORY => 2),\n    'cost'     => array(\n      RES_METAL     => 1000,\n      RES_CRYSTAL   => 0,\n      RES_DEUTERIUM => 0,\n      RES_ENERGY    => 0,\n      'factor'      => 2,\n    ),\n\n    P_BONUS_VALUE => 10,\n    P_BONUS_TYPE  => BONUS_PERCENT,\n  ),\n\n  TECH_ENERGY => array(\n    'name'     => 'energy_tech',\n    'type'     => UNIT_TECHNOLOGIES,\n    'location' => LOC_USER,\n    P_REQUIRE  => array(STRUC_LABORATORY => 1),\n    'cost'     => array(\n      RES_METAL     => 0,\n      RES_CRYSTAL   => 800,\n      RES_DEUTERIUM => 400,\n      RES_ENERGY    => 0,\n      'factor'      => 2,\n    ),\n  ),\n\n  TECH_HYPERSPACE => array(\n    'name'     => 'hyperspace_tech',\n    'type'     => UNIT_TECHNOLOGIES,\n    'location' => LOC_USER,\n    P_REQUIRE  => array(STRUC_LABORATORY => 7, TECH_ENERGY => 10, TECH_SHIELD => 5),\n    'cost'     => array(\n      RES_METAL     => 0,\n      RES_CRYSTAL   => 4000,\n      RES_DEUTERIUM => 2000,\n      RES_ENERGY    => 0,\n      'factor'      => 2,\n    ),\n  ),\n\n  TECH_ENGINE_CHEMICAL => array(\n    'name'           => 'combustion_tech',\n    'type'           => UNIT_TECHNOLOGIES,\n    'location'       => LOC_USER,\n    P_REQUIRE        => array(STRUC_LABORATORY => 1, TECH_ENERGY => 1),\n    'cost'           => array(\n      RES_METAL     => 400,\n      RES_CRYSTAL   => 0,\n      RES_DEUTERIUM => 600,\n      RES_ENERGY    => 0,\n      'factor'      => 2,\n    ),\n    P_BONUS_VALUE    => 10,\n    P_BONUS_TYPE     => BONUS_PERCENT,\n    'speed_increase' => 0.1,\n  ),\n\n  TECH_ENGINE_ION => array(\n    'name'           => 'impulse_motor_tech',\n    'type'           => UNIT_TECHNOLOGIES,\n    'location'       => LOC_USER,\n    P_REQUIRE        => array(STRUC_LABORATORY => 4, TECH_ION => 1),\n    'cost'           => array(\n      RES_METAL     => 2000,\n      RES_CRYSTAL   => 4000,\n      RES_DEUTERIUM => 600,\n      RES_ENERGY    => 0,\n      'factor'      => 2,\n    ),\n    P_BONUS_VALUE    => 20,\n    P_BONUS_TYPE     => BONUS_PERCENT,\n    'speed_increase' => 0.2,\n  ),\n\n  TECH_ENGINE_HYPER => array(\n    'name'           => 'hyperspace_motor_tech',\n    'type'           => UNIT_TECHNOLOGIES,\n    'location'       => LOC_USER,\n    P_REQUIRE        => array(STRUC_LABORATORY => 8, TECH_HYPERSPACE => 3),\n    'cost'           => array(\n      RES_METAL     => 10000,\n      RES_CRYSTAL   => 20000,\n      RES_DEUTERIUM => 6000,\n      RES_ENERGY    => 0,\n      'factor'      => 2,\n    ),\n    P_BONUS_VALUE    => 30,\n    P_BONUS_TYPE     => BONUS_PERCENT,\n    'speed_increase' => 0.3,\n  ),\n\n  TECH_LASER => array(\n    'name'     => 'laser_tech',\n    'type'     => UNIT_TECHNOLOGIES,\n    'location' => LOC_USER,\n    P_REQUIRE  => array(STRUC_LABORATORY => 1, TECH_ENERGY => 2),\n    'cost'     => array(\n      RES_METAL     => 200,\n      RES_CRYSTAL   => 100,\n      RES_DEUTERIUM => 0,\n      RES_ENERGY    => 0,\n      'factor'      => 2,\n    ),\n  ),\n\n  TECH_ION => array(\n    'name'     => 'ionic_tech',\n    'type'     => UNIT_TECHNOLOGIES,\n    'location' => LOC_USER,\n    P_REQUIRE  => array(STRUC_LABORATORY => 3, TECH_ENERGY => 4, TECH_LASER => 5),\n    'cost'     => array(\n      RES_METAL     => 1000,\n      RES_CRYSTAL   => 300,\n      RES_DEUTERIUM => 100,\n      RES_ENERGY    => 0,\n      'factor'      => 2,\n    ),\n  ),\n\n  TECH_PLASMA => array(\n    'name'     => 'buster_tech',\n    'type'     => UNIT_TECHNOLOGIES,\n    'location' => LOC_USER,\n    P_REQUIRE  => array(STRUC_LABORATORY => 5, TECH_ENERGY => 8, TECH_LASER => 10, TECH_ION => 5),\n    'cost'     => array(\n      RES_METAL     => 2000,\n      RES_CRYSTAL   => 4000,\n      RES_DEUTERIUM => 1000,\n      RES_ENERGY    => 0,\n      'factor'      => 2,\n    ),\n  ),\n\n  TECH_RESEARCH => array(\n    'name'     => 'intergalactic_tech',\n    'type'     => UNIT_TECHNOLOGIES,\n    'location' => LOC_USER,\n    P_REQUIRE  => array(STRUC_LABORATORY => 10, TECH_COMPUTER => 8, TECH_HYPERSPACE => 8),\n    'cost'     => array(\n      RES_METAL     => 240000,\n      RES_CRYSTAL   => 400000,\n      RES_DEUTERIUM => 160000,\n      RES_ENERGY    => 0,\n      'factor'      => 2,\n    ),\n  ),\n\n  TECH_EXPEDITION => array(\n    'name'     => 'expedition_tech',\n    'type'     => UNIT_TECHNOLOGIES,\n    'location' => LOC_USER,\n    P_REQUIRE  => array(STRUC_LABORATORY => 3, TECH_COMPUTER => 4, TECH_ENGINE_ION => 3),\n    'cost'     => array(\n      RES_METAL     => 4000,\n      RES_CRYSTAL   => 8000,\n      RES_DEUTERIUM => 4000,\n      RES_ENERGY    => 0,\n      'factor'      => 2,\n    ),\n  ),\n\n  TECH_COLONIZATION => array(\n    'name'     => 'colonisation_tech',\n    'type'     => UNIT_TECHNOLOGIES,\n    'location' => LOC_USER,\n    P_REQUIRE  => array(STRUC_LABORATORY => 3, TECH_ENERGY => 5, TECH_ARMOR => 2),\n    'cost'     => array(\n      RES_METAL     => 1000,\n      RES_CRYSTAL   => 4000,\n      RES_DEUTERIUM => 1000,\n      RES_ENERGY    => 0,\n      'factor'      => 2,\n    ),\n  ),\n\n  TECH_ASTROTECH => array(\n    'name'     => 'tech_astro',\n    'type'     => UNIT_TECHNOLOGIES,\n    'location' => LOC_USER,\n    P_REQUIRE  => array(STRUC_LABORATORY => 3, TECH_ENERGY => 5, TECH_ARMOR => 2),\n    'cost'     => array(\n      RES_METAL     => 1000,\n      RES_CRYSTAL   => 3000,\n      RES_DEUTERIUM => 900,\n      RES_ENERGY    => 0,\n      'factor'      => 2.5,\n    ),\n  ),\n\n  TECH_GRAVITON => array(\n    'name'     => 'graviton_tech',\n    'type'     => UNIT_TECHNOLOGIES,\n    'location' => LOC_USER,\n    P_REQUIRE  => array(STRUC_LABORATORY => 12, TECH_ENERGY => 12, TECH_HYPERSPACE => 6),\n    'cost'     => array(\n      RES_METAL     => 100000000,\n      RES_CRYSTAL   => 100000000,\n      RES_DEUTERIUM => 50000000,\n      //RES_ENERGY    => 300000,   // 100000 satellites\n      'factor'      => 3,\n    ),\n  ),\n\n\n  MRC_TECHNOLOGIST => array(\n    'name'        => 'rpg_geologue',\n    'type'        => UNIT_GOVERNORS,\n    'location'    => LOC_PLANET,\n    'cost'        => array(\n      RES_DARK_MATTER => 800,\n      'factor'        => 1.06,\n    ),\n    P_BONUS_VALUE => 5,\n    P_BONUS_TYPE  => BONUS_PERCENT,\n  ),\n  MRC_ENGINEER     => array(\n    'name'        => 'rpg_constructeur',\n    'type'        => UNIT_GOVERNORS,\n    'location'    => LOC_PLANET,\n    'cost'        => array(\n      RES_DARK_MATTER => 500,\n      'factor'        => 1.65,\n    ),\n    P_BONUS_VALUE => 10,\n    P_BONUS_TYPE  => BONUS_PERCENT,\n  ),\n  MRC_FORTIFIER    => array(\n    'name'        => 'rpg_defenseur',\n    'type'        => UNIT_GOVERNORS,\n    'location'    => LOC_PLANET,\n    'cost'        => array(\n      RES_DARK_MATTER => 2000,\n      'factor'        => 1.25,\n    ),\n    P_BONUS_VALUE => 10,\n    P_BONUS_TYPE  => BONUS_PERCENT,\n  ),\n\n\n  MRC_STOCKMAN => array(\n    'name'        => 'rpg_stockeur',\n    'type'        => UNIT_MERCENARIES,\n    'location'    => LOC_USER,\n    'cost'        => array(\n      RES_DARK_MATTER => 3000,\n      'factor'        => 1,\n    ),\n    'max'         => 20,\n    P_BONUS_VALUE => 20,\n    P_BONUS_TYPE  => BONUS_PERCENT,\n  ),\n\n  MRC_SPY => array(\n    'name'        => 'rpg_espion',\n    'type'        => UNIT_MERCENARIES,\n    'location'    => LOC_USER,\n    P_REQUIRE     => array(MRC_STOCKMAN => 5),\n    'cost'        => array(\n      RES_DARK_MATTER => 3000,\n      'factor'        => 1,\n    ),\n    'max'         => 5,\n    P_BONUS_VALUE => 1,\n    P_BONUS_TYPE  => BONUS_ADD,\n  ),\n\n  MRC_ACADEMIC => array(\n    'name'        => 'mrc_academic',\n    'type'        => UNIT_MERCENARIES,\n    'location'    => LOC_USER,\n    P_REQUIRE     => array(MRC_STOCKMAN => 10, MRC_SPY => 5),\n    'cost'        => array(\n      RES_DARK_MATTER => 3000,\n      'factor'        => 1,\n    ),\n    'max'         => 30,\n    P_BONUS_VALUE => 10,\n    P_BONUS_TYPE  => BONUS_PERCENT,\n  ),\n\n  MRC_ADMIRAL => array(\n    'name'        => 'rpg_amiral',\n    'type'        => UNIT_MERCENARIES,\n    'location'    => LOC_USER,\n    'cost'        => array(\n      RES_DARK_MATTER => 3000,\n      'factor'        => 1,\n    ),\n    'max'         => 20,\n    P_BONUS_VALUE => 5,\n    P_BONUS_TYPE  => BONUS_PERCENT,\n  ),\n\n  MRC_COORDINATOR => array(\n    'name'        => 'rpg_commandant',\n    'type'        => UNIT_MERCENARIES,\n    'location'    => LOC_USER,\n    P_REQUIRE     => array(MRC_ADMIRAL => 5),\n    'cost'        => array(\n      RES_DARK_MATTER => 3000,\n      'factor'        => 1,\n    ),\n    'max'         => 5,\n    P_BONUS_VALUE => 1,\n    P_BONUS_TYPE  => BONUS_ADD,\n  ),\n\n  MRC_NAVIGATOR => array(\n    'name'        => 'rpg_general',\n    'type'        => UNIT_MERCENARIES,\n    'location'    => LOC_USER,\n    P_REQUIRE     => array(MRC_ADMIRAL => 10, MRC_COORDINATOR => 5),\n    'cost'        => array(\n      RES_DARK_MATTER => 3000,\n      'factor'        => 1,\n    ),\n    'max'         => 10,\n    P_BONUS_VALUE => 5,\n    P_BONUS_TYPE  => BONUS_PERCENT,\n  ),\n\n\n  ART_LHC             => array(\n    'name'       => 'art_lhc',\n    'type'       => UNIT_ARTIFACTS,\n    'location'   => LOC_USER,\n    'cost'       => array(\n      RES_DARK_MATTER => 25000,\n      'factor'        => 1,\n    ),\n    P_BONUS_TYPE => BONUS_ABILITY,\n  ),\n  ART_HOOK_SMALL      => array(\n    'name'       => 'art_hook_small',\n    'type'       => UNIT_ARTIFACTS,\n    'location'   => LOC_USER,\n    'cost'       => array(\n      RES_DARK_MATTER => 100000,\n      'factor'        => 1,\n    ),\n    P_BONUS_TYPE => BONUS_ABILITY,\n  ),\n  ART_HOOK_MEDIUM     => array(\n    'name'       => 'art_hook_medium',\n    'type'       => UNIT_ARTIFACTS,\n    'location'   => LOC_USER,\n    'cost'       => array(\n      RES_DARK_MATTER => 200000,\n      'factor'        => 1,\n    ),\n    P_BONUS_TYPE => BONUS_ABILITY,\n  ),\n  ART_HOOK_LARGE      => array(\n    'name'       => 'art_hook_large',\n    'type'       => UNIT_ARTIFACTS,\n    'location'   => LOC_USER,\n    'cost'       => array(\n      RES_DARK_MATTER => 400000,\n      'factor'        => 1,\n    ),\n    P_BONUS_TYPE => BONUS_ABILITY,\n  ),\n  ART_RCD_SMALL       => array(\n    'name'       => 'art_rcd_small',\n    'type'       => UNIT_ARTIFACTS,\n    'location'   => LOC_USER,\n    'cost'       => array(\n      RES_DARK_MATTER => 5000,\n      'factor'        => 1,\n    ),\n    P_BONUS_TYPE => BONUS_ABILITY,\n    'deploy'     => array(\n      STRUC_MINE_METAL     => 10,\n      STRUC_MINE_CRYSTAL   => 10,\n      STRUC_MINE_DEUTERIUM => 10,\n      STRUC_MINE_SOLAR     => 14,\n      STRUC_FACTORY_ROBOT  => 4,\n    ),\n  ),\n  ART_RCD_MEDIUM      => array(\n    'name'       => 'art_rcd_medium',\n    'type'       => UNIT_ARTIFACTS,\n    'location'   => LOC_USER,\n    'cost'       => array(\n      RES_DARK_MATTER => 25000,\n      'factor'        => 1,\n    ),\n    P_BONUS_TYPE => BONUS_ABILITY,\n    'deploy'     => array(\n      STRUC_MINE_METAL     => 15,\n      STRUC_MINE_CRYSTAL   => 15,\n      STRUC_MINE_DEUTERIUM => 15,\n      STRUC_MINE_SOLAR     => 20,\n      STRUC_FACTORY_ROBOT  => 8,\n    ),\n  ),\n  ART_RCD_LARGE       => array(\n    'name'       => 'art_rcd_large',\n    'type'       => UNIT_ARTIFACTS,\n    'location'   => LOC_USER,\n    'cost'       => array(\n      RES_DARK_MATTER => 60000,\n      'factor'        => 1,\n    ),\n    P_BONUS_TYPE => BONUS_ABILITY,\n    'deploy'     => array(\n      STRUC_MINE_METAL     => 20,\n      STRUC_MINE_CRYSTAL   => 20,\n      STRUC_MINE_DEUTERIUM => 20,\n      STRUC_MINE_SOLAR     => 25,\n      STRUC_FACTORY_ROBOT  => 10,\n      STRUC_FACTORY_NANO   => 1,\n    ),\n  ),\n  ART_HEURISTIC_CHIP  => array(\n    'name'       => 'art_heuristic_chip',\n    'type'       => UNIT_ARTIFACTS,\n    'location'   => LOC_USER,\n    'cost'       => array(\n      RES_DARK_MATTER => 20000,\n      'factor'        => 1,\n    ),\n    P_BONUS_TYPE => BONUS_ABILITY,\n  ),\n  ART_NANO_BUILDER    => array(\n    'name'       => 'art_nano_builder',\n    'type'       => UNIT_ARTIFACTS,\n    'location'   => LOC_USER,\n    'cost'       => array(\n      RES_DARK_MATTER => 5000,\n      'factor'        => 1,\n    ),\n    P_BONUS_TYPE => BONUS_ABILITY,\n  ),\n  ART_DENSITY_CHANGER => array(\n    'name'       => 'art_density_changer',\n    'type'       => UNIT_ARTIFACTS,\n    'location'   => LOC_USER,\n    'cost'       => array(\n      RES_DARK_MATTER => 50000,\n      'factor'        => 1,\n    ),\n    P_BONUS_TYPE => BONUS_ABILITY,\n  ),\n\n  UNIT_PLAN_STRUC_MINE_FUSION => array(\n    'name'       => 'UNIT_PLAN_STRUC_MINE_FUSION',\n    'type'       => UNIT_PLANS,\n    'location'   => LOC_USER,\n    'cost'       => array(\n      RES_DARK_MATTER => 10000,\n      'factor'        => 1,\n    ),\n    'max'        => 1,\n    P_BONUS_TYPE => BONUS_ABILITY,\n  ),\n\n  UNIT_PLAN_SHIP_CARGO_SUPER  => array(\n    'name'       => 'UNIT_PLAN_SHIP_CARGO_SUPER',\n    'type'       => UNIT_PLANS,\n    'location'   => LOC_USER,\n    'cost'       => array(\n      RES_DARK_MATTER => 10000,\n      'factor'        => 1,\n    ),\n    'max'        => 1,\n    P_BONUS_TYPE => BONUS_ABILITY,\n  ),\n  UNIT_PLAN_SHIP_CARGO_HYPER  => array(\n    'name'       => 'UNIT_PLAN_SHIP_CARGO_HYPER',\n    'type'       => UNIT_PLANS,\n    'location'   => LOC_USER,\n    'cost'       => array(\n      RES_DARK_MATTER => 25000,\n      'factor'        => 1,\n    ),\n    'max'        => 1,\n    P_BONUS_TYPE => BONUS_ABILITY,\n  ),\n  UNIT_PLAN_SHIP_DEATH_STAR   => array(\n    'name'       => 'UNIT_PLAN_SHIP_DEATH_STAR',\n    'type'       => UNIT_PLANS,\n    'location'   => LOC_USER,\n    'cost'       => array(\n      RES_DARK_MATTER => 10000,\n      'factor'        => 1,\n    ),\n    'max'        => 1,\n    P_BONUS_TYPE => BONUS_ABILITY,\n  ),\n  UNIT_PLAN_SHIP_SUPERNOVA    => array(\n    'name'       => 'UNIT_PLAN_SHIP_SUPERNOVA',\n    'type'       => UNIT_PLANS,\n    'location'   => LOC_USER,\n    'cost'       => array(\n      RES_DARK_MATTER => 25000,\n      'factor'        => 1,\n    ),\n    'max'        => 1,\n    P_BONUS_TYPE => BONUS_ABILITY,\n  ),\n  UNIT_PLAN_DEF_SHIELD_PLANET => array(\n    'name'       => 'UNIT_PLAN_DEF_SHIELD_PLANET',\n    'type'       => UNIT_PLANS,\n    'location'   => LOC_USER,\n    'cost'       => array(\n      RES_DARK_MATTER => 25000,\n      'factor'        => 1,\n    ),\n    'max'        => 1,\n    P_BONUS_TYPE => BONUS_ABILITY,\n  ),\n\n  /*\n    MRC_EMPEROR => array(\n      'name' => 'rpg_empereur',\n      'type' => UNIT_MERCENARIES,\n      'location' => LOC_USER,\n      P_REQUIRE => array(MRC_ASSASIN => 1, MRC_DEFENDER => 1),\n      'cost' => array(\n        RES_DARK_MATTER => 3000,\n        'factor' => 1,\n      ),\n      'max' => 1,\n      P_BONUS_TYPE => BONUS_ABILITY,\n    ),\n  */\n);\n"
  },
  {
    "path": "includes/vars_structures.php",
    "content": "<?php\n\n!defined('INSIDE') ? die('Hack attempt!') : false;\n\nglobal $sn_data;\n$sn_data += array(\n  STRUC_MINE_METAL => array(\n    'name'              => 'metal_mine',\n    'type'              => UNIT_STRUCTURES,\n    'location'          => LOC_PLANET,\n    'cost'              => array(\n      RES_METAL     => 80,\n      RES_CRYSTAL   => 20,\n      RES_DEUTERIUM => 0,\n      RES_ENERGY    => 0,\n      'factor'      => 1.5,\n    ),\n    P_UNIT_PRODUCTION   => array(\n      RES_METAL  => function ($level, $production_factor, $user, $planet_row) { return 40 * $level * pow(1.1, $level) * (0.1 * $production_factor); },\n      RES_ENERGY => function ($level, $production_factor, $user, $planet_row) { return -13 * $level * pow(1.1, $level) * (0.1 * $production_factor); },\n    ),\n    P_MINING_IS_MANAGED => true,\n  ),\n\n  STRUC_MINE_CRYSTAL => array(\n    'name'              => 'crystal_mine',\n    'type'              => UNIT_STRUCTURES,\n    'location'          => LOC_PLANET,\n    'cost'              => array(\n      RES_METAL     => 48,\n      RES_CRYSTAL   => 24,\n      RES_DEUTERIUM => 0,\n      RES_ENERGY    => 0,\n      'factor'      => 1.6,\n    ),\n    P_UNIT_PRODUCTION   => array(\n      RES_CRYSTAL => function ($level, $production_factor, $user, $planet_row) { return 32 * $level * pow(1.1, $level) * (0.1 * $production_factor); },\n      RES_ENERGY  => function ($level, $production_factor, $user, $planet_row) { return -16 * $level * pow(1.1, $level) * (0.1 * $production_factor); },\n    ),\n    P_MINING_IS_MANAGED => true,\n  ),\n\n  STRUC_MINE_DEUTERIUM => array(\n    'name'              => 'deuterium_sintetizer',\n    'type'              => UNIT_STRUCTURES,\n    'location'          => LOC_PLANET,\n    'cost'              => array(\n      RES_METAL     => 225,\n      RES_CRYSTAL   => 75,\n      RES_DEUTERIUM => 0,\n      RES_ENERGY    => 0,\n      'factor'      => 1.5,\n    ),\n    P_UNIT_PRODUCTION   => array(\n      RES_DEUTERIUM => function ($level, $production_factor, $user, $planet_row) { return 10 * $level * pow(1.1, $level) * (0.1 * $production_factor) * (-0.002 * $planet_row[\"temp_max\"] + 1.28); },\n      RES_ENERGY    => function ($level, $production_factor, $user, $planet_row) { return -20 * $level * pow(1.1, $level) * (0.1 * $production_factor); },\n    ),\n    P_MINING_IS_MANAGED => true,\n  ),\n\n  STRUC_MINE_SOLAR  => array(\n    'name'              => 'solar_plant',\n    'type'              => UNIT_STRUCTURES,\n    'location'          => LOC_PLANET,\n    'cost'              => array(\n      RES_METAL     => 75,\n      RES_CRYSTAL   => 30,\n      RES_DEUTERIUM => 0,\n      RES_ENERGY    => 0,\n      'factor'      => 1.5,\n    ),\n    P_UNIT_PRODUCTION   => array(\n      RES_ENERGY => function ($level, $production_factor, $user, $planet_row) { return ($planet_row[\"temp_max\"] / 5 + 15) * $level * pow(1.1, $level) * (0.1 * $production_factor); },\n    ),\n    P_MINING_IS_MANAGED => true,\n  ),\n// −273,15 °C\n  STRUC_MINE_FUSION => array(\n    'name'              => 'fusion_plant',\n    'type'              => UNIT_STRUCTURES,\n    'location'          => LOC_PLANET,\n    P_REQUIRE           => array(3 => 5, TECH_ENERGY => 3, UNIT_PLAN_STRUC_MINE_FUSION => 1),\n    'cost'              => array(\n      RES_METAL     => 900,\n      RES_CRYSTAL   => 360,\n      RES_DEUTERIUM => 180,\n      RES_ENERGY    => 0,\n      'factor'      => 1.8,\n    ),\n    P_UNIT_PRODUCTION   => array(\n      RES_DEUTERIUM => function ($level, $production_factor, $user, $planet_row) { return -10 * $level * pow(1.1, $level) * (0.1 * $production_factor); },\n      RES_ENERGY    => function ($level, $production_factor, $user, $planet_row) { return 30 * $level * pow(1.05 + 0.01 * mrc_get_level($user, \"\", TECH_ENERGY), $level) * (0.1 * $production_factor); },\n    ),\n    P_MINING_IS_MANAGED => true,\n  ),\n\n  STRUC_STORE_METAL => array(\n    'name'     => 'metal_store',\n    'type'     => UNIT_STRUCTURES,\n    'location' => LOC_PLANET,\n    'cost'     => array(\n      RES_METAL     => 2000,\n      RES_CRYSTAL   => 0,\n      RES_DEUTERIUM => 0,\n      RES_ENERGY    => 0,\n      'factor'      => 2,\n    ),\n    'storage'  => array(\n      RES_METAL => function ($level) { return BASE_STORAGE_SIZE * pow(1.5, $level); },\n    ),\n  ),\n\n  STRUC_STORE_CRYSTAL => array(\n    'name'     => 'crystal_store',\n    'type'     => UNIT_STRUCTURES,\n    'location' => LOC_PLANET,\n    'cost'     => array(\n      RES_METAL     => 2000,\n      RES_CRYSTAL   => 1000,\n      RES_DEUTERIUM => 0,\n      RES_ENERGY    => 0,\n      'factor'      => 2,\n    ),\n    'storage'  => array(\n      RES_CRYSTAL => function ($level) { return BASE_STORAGE_SIZE * pow(1.5, $level); },\n    ),\n  ),\n\n  STRUC_STORE_DEUTERIUM => array(\n    'name'     => 'deuterium_store',\n    'type'     => UNIT_STRUCTURES,\n    'location' => LOC_PLANET,\n    'cost'     => array(\n      RES_METAL     => 2000,\n      RES_CRYSTAL   => 2000,\n      RES_DEUTERIUM => 0,\n      RES_ENERGY    => 0,\n      'factor'      => 2,\n    ),\n    'storage'  => array(\n      RES_DEUTERIUM => function ($level) { return BASE_STORAGE_SIZE * pow(1.5, $level); },\n    ),\n  ),\n\n  STRUC_FACTORY_ROBOT => array(\n    'name'     => 'robot_factory',\n    'type'     => UNIT_STRUCTURES,\n    'location' => LOC_PLANET,\n    'cost'     => array(\n      RES_METAL     => 400,\n      RES_CRYSTAL   => 120,\n      RES_DEUTERIUM => 200,\n      RES_ENERGY    => 0,\n      'factor'      => 2,\n    ),\n  ),\n\n  STRUC_FACTORY_NANO => array(\n    'name'     => 'nano_factory',\n    'type'     => UNIT_STRUCTURES,\n    'location' => LOC_PLANET,\n    P_REQUIRE  => array(STRUC_FACTORY_ROBOT => 10, TECH_COMPUTER => 10),\n    'cost'     => array(\n      RES_METAL     => 1000000,\n      RES_CRYSTAL   => 500000,\n      RES_DEUTERIUM => 100000,\n      RES_ENERGY    => 0,\n      'factor'      => 2,\n    ),\n  ),\n\n  STRUC_FACTORY_HANGAR => array(\n    'name'     => 'hangar',\n    'type'     => UNIT_STRUCTURES,\n    'location' => LOC_PLANET,\n    P_REQUIRE  => array(STRUC_FACTORY_ROBOT => 2),\n    'cost'     => array(\n      RES_METAL     => 400,\n      RES_CRYSTAL   => 200,\n      RES_DEUTERIUM => 100,\n      RES_ENERGY    => 0,\n      'factor'      => 2,\n    ),\n  ),\n\n  STRUC_LABORATORY => array(\n    'name'     => 'laboratory',\n    'type'     => UNIT_STRUCTURES,\n    'location' => LOC_PLANET,\n    'cost'     => array(\n      RES_METAL     => 200,\n      RES_CRYSTAL   => 400,\n      RES_DEUTERIUM => 200,\n      RES_ENERGY    => 0,\n      'factor'      => 2,\n    ),\n  ),\n\n  STRUC_TERRAFORMER => array(\n    'name'     => 'terraformer',\n    'type'     => UNIT_STRUCTURES,\n    'location' => LOC_PLANET,\n    P_REQUIRE  => array(STRUC_FACTORY_NANO => 1, TECH_ENERGY => 12),\n    'cost'     => array(\n      RES_METAL     => 0,\n      RES_CRYSTAL   => 50000,\n      RES_DEUTERIUM => 100000,\n      RES_ENERGY    => 0,\n      'factor'      => 2,\n    ),\n  ),\n\n  STRUC_ALLY_DEPOSIT => array(\n    'name'     => 'ally_deposit',\n    'type'     => UNIT_STRUCTURES,\n    'location' => LOC_PLANET,\n    'cost'     => array(\n      RES_METAL     => 20000,\n      RES_CRYSTAL   => 40000,\n      RES_DEUTERIUM => 0,\n      RES_ENERGY    => 0,\n      'factor'      => 2,\n    ),\n  ),\n\n  STRUC_LABORATORY_NANO => array(\n    'name'     => 'nano',\n    'type'     => UNIT_STRUCTURES,\n    'location' => LOC_PLANET,\n    P_REQUIRE  => array(STRUC_LABORATORY => 10, TECH_ENERGY => 10),\n    'cost'     => array(\n      RES_METAL     => 1500000,\n      RES_CRYSTAL   => 750000,\n      RES_DEUTERIUM => 150000,\n      RES_ENERGY    => 0,\n      'factor'      => 2,\n    ),\n  ),\n\n  STRUC_MOON_STATION => array(\n    'name'     => 'mondbasis',\n    'type'     => UNIT_STRUCTURES,\n    'location' => LOC_PLANET,\n    'cost'     => array(\n      RES_METAL     => 20000,\n      RES_CRYSTAL   => 40000,\n      RES_DEUTERIUM => 20000,\n      RES_ENERGY    => 0,\n      'factor'      => 2,\n    ),\n  ),\n\n  STRUC_MOON_PHALANX => array(\n    'name'     => 'phalanx',\n    'type'     => UNIT_STRUCTURES,\n    'location' => LOC_PLANET,\n    P_REQUIRE  => array(STRUC_MOON_STATION => 1),\n    'cost'     => array(\n      RES_METAL     => 20000,\n      RES_CRYSTAL   => 40000,\n      RES_DEUTERIUM => 20000,\n      RES_ENERGY    => 0,\n      'factor'      => 2,\n    ),\n  ),\n\n  STRUC_MOON_GATE => array(\n    'name'     => 'sprungtor',\n    'type'     => UNIT_STRUCTURES,\n    'location' => LOC_PLANET,\n    P_REQUIRE  => array(STRUC_MOON_STATION => 1, TECH_HYPERSPACE => 7),\n    'cost'     => array(\n      RES_METAL     => 2000000,\n      RES_CRYSTAL   => 4000000,\n      RES_DEUTERIUM => 2000000,\n      RES_ENERGY    => 0,\n      'factor'      => 2,\n    ),\n  ),\n\n  STRUC_SILO => array(\n    'name'     => 'silo',\n    'type'     => UNIT_STRUCTURES,\n    'location' => LOC_PLANET,\n    P_REQUIRE  => array(TECH_ENGINE_ION => 1),\n    'cost'     => array(\n      RES_METAL     => 20000,\n      RES_CRYSTAL   => 20000,\n      RES_DEUTERIUM => 1000,\n      RES_ENERGY    => 0,\n      'factor'      => 2,\n    ),\n    'capacity' => 12,\n  ),\n);\n"
  },
  {
    "path": "index.php",
    "content": "<?php\n\n/**\n * @var template $template\n */\n\nglobal $sn_page_name, $sn_mvc, $template, $template_result;\n\nif(isset($sn_page_name) || ($sn_page_name = isset($_GET['page']) ? trim(strip_tags($_GET['page'])) : '')) {\n  require_once('common.' . substr(strrchr(__FILE__, '.'), 1));\n  if($sn_page_name) {\n    // Loading page-specific language files\n\n    !empty($sn_mvc['model'][$sn_page_name]) and execute_hooks($sn_mvc['model'][$sn_page_name], $template, 'model', $sn_page_name);\n    !empty($sn_mvc['view'][$sn_page_name]) and execute_hooks($sn_mvc['view'][$sn_page_name], $template, 'view', $sn_page_name);\n    if(!empty($template_result) && is_object($template)) {\n      $template->assign_recursive($template_result);\n    }\n\n    SnTemplate::display($template);\n  }\n}\n\n// Добавить обработку редиректов со старых страниц\n// @ob_end_flush();\nheader('Location: overview.php');\ndie();\n"
  },
  {
    "path": "infos.php",
    "content": "<?php\n\n/**\n * @copyright Copyright (c) 2009-2017 by Gorlum for http://supernova.ws\n */\n\ninclude('common.' . substr(strrchr(__FILE__, '.'), 1));\n\n$unit_id = sys_get_param_id('gid');\nif ($unit_id == RES_DARK_MATTER) {\n  sys_redirect('dark_matter.php');\n}\n\nif ($unit_id == RES_METAMATTER) {\n  sys_redirect('metamatter.php');\n}\n\nlng_include('infos');\nif (\n  !$unit_id\n  ||\n  (!get_unit_param($unit_id) && !isset($lang['info'][$unit_id]) && !$unit_id == UNIT_CAN_NOT_BE_BUILD)\n) {\n  sys_redirect('index.php?page=techtree');\n}\n\n$template = SnTemplate::gettemplate('novapedia', true);\n\n$unit_data = get_unit_param($unit_id);\n$unit_type = $unit_data['type'];\n\nif ($unit_type == UNIT_SHIPS) {\n  $template_result['UNIT_IS_SHIP'] = true;\n\n  $ship_data = get_ship_data($unit_id, $user);\n\n  $template_result += array(\n    'BASE_SPEED'         => HelperString::numberFloorAndFormat($ship_data['speed_base']),\n    'ACTUAL_SPEED'       => HelperString::numberFloorAndFormat($ship_data['speed']),\n    'BASE_CONSUMPTION'   => HelperString::numberFloorAndFormat($ship_data['consumption_base']),\n    'ACTUAL_CONSUMPTION' => HelperString::numberFloorAndFormat($ship_data['consumption']),\n\n    'BASE_CAPACITY'   => HelperString::numberFloorAndFormat($unit_data['capacity']),\n    'ACTUAL_CAPACITY' => HelperString::numberFloorAndFormat($ship_data['capacity']),\n  );\n\n  $engine_template_info = array();\n  foreach ($unit_data['engine'] as $unit_engine_data) {\n    $unit_engine_data = get_engine_data($user, $unit_engine_data);\n\n    $engine_template_info[] = array(\n      'NAME'               => $lang['tech'][$unit_engine_data['tech']],\n      'MIN_LEVEL'          => $unit_engine_data['min_level'],\n      'USER_TECH_LEVEL'    => mrc_get_level($user, null, $unit_engine_data['tech']),\n      'BASE_SPEED'         => HelperString::numberFloorAndFormat($unit_engine_data['speed_base']),\n      'BASE_CONSUMPTION'   => HelperString::numberFloorAndFormat($unit_engine_data['consumption_base']),\n      'ACTUAL_SPEED'       => HelperString::numberFloorAndFormat($unit_engine_data['speed']),\n      'ACTUAL_CONSUMPTION' => HelperString::numberFloorAndFormat($unit_engine_data['consumption']),\n    );\n  }\n  $template_result['.']['engine'] = $engine_template_info;\n\n}\n\n\n$sn_data_group_combat = sn_get_groups('combat');\nif (in_array($unit_id, $sn_data_group_combat)) {\n  $template_result['UNIT_IS_COMBAT'] = true;\n\n  $unit_durability = $unit_data['shield'] + $unit_data['armor'];\n\n  $volley_arr = $rapid_to = $rapid_from = array();\n  $str_rapid_from = '';\n  $str_rapid_to = '';\n  foreach ($sn_data_group_combat as $enemy_id) {\n    $enemy_data = get_unit_param($enemy_id);\n    $enemy_durability = $enemy_data['shield'] + $enemy_data['armor'];\n\n    $rapid = $unit_data['attack'] * (isset($unit_data['amplify'][$enemy_id]) ? $unit_data['amplify'][$enemy_id] : 1) / $enemy_durability;\n    if ($rapid >= 1) {\n      $volley_arr[$enemy_id]['TO'] = floor($rapid);\n    }\n\n    $rapid = $enemy_data['attack'] * (isset($enemy_data['amplify'][$unit_id]) ? $enemy_data['amplify'][$unit_id] : 1) / $unit_durability;\n    if ($rapid >= 1) {\n      $volley_arr[$enemy_id]['FROM'] = floor($rapid);\n    }\n  }\n  foreach ($volley_arr as $enemy_id => &$rapid) {\n    $rapid['ENEMY_ID'] = $enemy_id;\n    $rapid['ENEMY_NAME'] = $lang['tech'][$enemy_id];\n  }\n  $template_result['.']['volley'] = $volley_arr;\n\n  $template_result += array(\n    'BASE_ARMOR'  => HelperString::numberFloorAndFormat($unit_data['armor']),\n    'BASE_SHIELD' => HelperString::numberFloorAndFormat($unit_data['shield']),\n    'BASE_WEAPON' => HelperString::numberFloorAndFormat($unit_data['attack']),\n\n    'ACTUAL_ARMOR'  => HelperString::numberFloorAndFormat(mrc_modify_value($user, false, array(MRC_ADMIRAL, TECH_ARMOR), $unit_data['armor'])),\n    'ACTUAL_SHIELD' => HelperString::numberFloorAndFormat(mrc_modify_value($user, false, array(MRC_ADMIRAL, TECH_SHIELD), $unit_data['shield'])),\n    'ACTUAL_WEAPON' => HelperString::numberFloorAndFormat(mrc_modify_value($user, false, array(MRC_ADMIRAL, TECH_WEAPON), $unit_data['attack'])),\n  );\n\n}\n\nif ($lang['info'][$unit_id]['effect']) {\n  $template_result['UNIT_EFFECT'] = $lang['info'][$unit_id]['effect'];\n}\n\nif ($unit_data[P_BONUS_VALUE]) {\n  $unit_bonus = !$unit_data[P_BONUS_VALUE] || $unit_data[P_BONUS_TYPE] == BONUS_ABILITY ? '' : (\n    ($unit_data[P_BONUS_VALUE] >= 0 ? '+' : '') . $unit_data[P_BONUS_VALUE] . ($unit_data[P_BONUS_TYPE] == BONUS_PERCENT ? '%' : '')\n  );\n  $template_result['UNIT_BONUS'] = $unit_bonus;\n}\n\n$template_result += array(\n  'PAGE_HEADER' => $lang['wiki_title'],\n\n  'UNIT_ID'          => $unit_id,\n  'UNIT_NAME'        => $lang['tech'][$unit_id],\n  'UNIT_TYPE'        => $unit_type,\n  'UNIT_TYPE_NAME'   => $lang['tech'][$unit_type],\n  'UNIT_DESCRIPTION' => $lang['info'][$unit_id]['description'],\n\n  'UNIT_IMAGE_LARGE' => SN::$gc->skinModel->isImageFileExists($unit_id . '_large'),\n);\n\n$template_result['.'][TPL_BLOCK_REQUIRE] = unit_requirements_render($user, $planetrow, $unit_id);\n\n$template->assign_recursive($template_result);\nSnTemplate::display($template);\n"
  },
  {
    "path": "js/build_unit.js",
    "content": "var unit_selected = null;\n\nfunction eco_struc_make_resource_row(unitData, resourceName) {\n  var resourceLeft, resourceLeftWithIncoming;\n  // var costDestroy = intVal(unitData[\"destroy_\" + resourceName]);\n  var costBuild = Math.ceilVal(unitData[resourceName]);\n  var resourceElement = $(\"#unit_\" + resourceName);\n\n  if (costBuild > 0) {\n    if (STACKABLE) {\n      costBuild *= Math.roundVal($(\"#unit_amount\").val());\n    }\n    resourceLeft = Math.roundVal(planet[resourceName]) - costBuild;\n    resourceLeftWithIncoming = resourceLeft + Math.floatVal(planet[resourceName + \"_incoming\"]);\n\n    resourceElement.css({visibility: \"visible\", display: \"table-row\"});\n\n    elementPrettyNumber($(\"#\" + resourceName + \"_price\"), costBuild, planet[resourceName]);\n    elementPrettyNumber($(\"#\" + resourceName + \"_left\"), resourceLeft);\n\n    if (planet[\"fleet_own\"]) {\n      elementPrettyNumber($(\"#\" + resourceName + \"_fleet\"), resourceLeftWithIncoming);\n      jQuery('#fleet_res').css(\"display\", \"block\");\n    } else {\n      $(\"#\" + resourceName + \"_fleet\").css(\"display\", \"none\");\n    }\n  } else {\n    // For more robust hide/visibility\n    // Need to mark - if any of units have cost in DM. Then it should hide with visibility. Otherwise - hide with display\n    //   (\"dark_matter\" != resourceName && unitData['dark_matter']) || (\"dark_matter\" == resourceName && !unitData[\"dark_matter\"])\n    //     ? {display: \"none\"} : {visibility: \"hidden\"}\n    resourceElement.css(\"visibility\", \"hidden\");\n  }\n}\n\nfunction eco_struc_make_resource_rows_all(unit) {\n  eco_struc_make_resource_row(unit, 'metal');\n  eco_struc_make_resource_row(unit, 'crystal');\n  eco_struc_make_resource_row(unit, 'deuterium');\n  eco_struc_make_resource_row(unit, 'dark_matter');\n}\n\nfunction buildUnitProcessResult(e, result) {\n  if (result) {\n    $('[name=action]').val(result);\n    $('#form_unit').submit();\n  } else {\n    e.preventDefault();\n  }\n  return !!result;\n}\n\njQuery(document).ready(function (e) {\n  $(\"#unit_create_button_auto\").on('click', function (e) {\n    var result = elementIsEnabled(this);\n    if (result) {\n      if (DARK_MATTER >= MARKET_AUTOCONVERT_COST) {\n        if (confirm(language['eco_bld_autoconvert_explain'] + language['eco_bld_autoconvert_confirm'])) {\n          result = \"create_autoconvert\";\n        }\n      } else {\n        alert(language['eco_bld_autoconvert_explain'] + language['eco_bld_autoconvert_dark_matter_none']);\n        result = false;\n      }\n    }\n\n    return buildUnitProcessResult(e, result);\n  });\n\n  $(\".unit_create\").on('click', function (e) {\n    var result = elementIsEnabled(this) || $(this).hasClass('icon_plus') || $(this).hasClass('icon_minus');\n    if (result) {\n      result = $(this).hasClass('unit_create') ? 'create' : 'destroy';\n    }\n\n    return buildUnitProcessResult(e, result);\n  });\n\n  $(\".unit_destroy\").on('click', function (e) {\n    return buildUnitProcessResult(e, 'destroy');\n  });\n\n  $(\"#form_unit\").on('keypress', function (e) {\n    if (13 != e.which) {\n      return true;\n    }\n\n    var ucb = $('#unit_create_button');\n    if (!ucb.is(\"[disabled]\")) {\n      ucb.click();\n    }\n    e.preventDefault();\n    return false;\n  });\n\n  $(\"#unit_amount\").on('change', function () {\n    $('#unit_create_button').button(Math.intVal($('#unit_amount').val()) ? \"enable\" : \"disable\");\n  });\n\n  jQuery('#unit_amount').on('keyup change', function (event, ui) {\n    var unit_id = Math.intVal($('#unit_id').val());\n    eco_struc_make_resource_rows_all(production[unit_id]);\n  });\n\n  jQuery(\"#unit_table\")\n    .on(\"mouseenter\", \"div[unit_id]\", function (event, ui) {\n      eco_struc_show_unit_info(jQuery(this).attr('unit_id'));\n    })\n    .on(\"mouseleave\", \"div[unit_id]\", function (event, ui) {\n      if (unit_selected != jQuery(this).attr('unit_id')) {\n        jQuery(this).removeClass('unit_border_selected');\n      }\n    })\n    .on(\"click\", \"div[unit_id]\", function (event, ui) {\n      eco_struc_select_unit(jQuery(this).attr('unit_id'));\n    });\n\n  $(\"#unit_info_extra_switch\").on('click', function () {\n    var ub = $('#unit_balance');\n    if (ub.is(':visible')) {\n      $(this).children().html(language['eco_bld_unit_info_extra_show']);\n      ub.hide();\n    } else {\n      $(this).children().html(language['eco_bld_unit_info_extra_hide']);\n      ub.show();\n    }\n  });\n\n  $(\"#auto_convert\").on('change', function () {\n    var unit_create = $('#unit_create, #unit_create *');\n    var unit_create_button = $('#unit_create_button');\n\n    unit_create.prop('disabled', true);\n    unit_create_button.button('disable');\n\n    unit_id = $('#unit_id').val();\n    var unit = production[unit_id];\n\n    $('#unit_max_number').html(sn_format_number(unit['can_build']));\n\n    if (unit['autoconvert_amount']) {\n      $('#auto_convert').prop('disabled', false).not(':checkbox').button('enable');\n    }\n\n    if (unit['build_can'] != 0 && unit['build_result'] == 0) {\n      unit_create.prop('disabled', false);\n      $('#unit_amountslide').slider({max: unit['can_build']});\n    }\n\n    if (!$('#auto_convert').is(\":disabled\") && $('#auto_convert').attr(\"aria-disabled\") != 'true' && $('#auto_convert').is(\":checked\")) {\n      //if($(this).is(\":disabled\") || $(this).attr(\"aria-disabled\") == 'true') {\n      unit_create.prop('disabled', false);\n      $('#unit_amountslide').slider({max: unit['autoconvert_amount']});\n      $('#unit_max_number').html(sn_format_number(unit['autoconvert_amount']));\n    } else {\n      if (unit['build_can'] == 0 || unit['build_result'] != 0) {\n        unit_create.prop('disabled', true);\n        $('#unit_amountslide').slider({max: 0, value: 0});\n        unit_create_button.button('disable');\n        $('#unit_amount').val(0);\n      }\n    }\n\n    if (unit['autoconvert_amount']) {\n      $('#auto_convert').prop('disabled', false).not(':checkbox').button('enable');\n    }\n\n    $('#unit_amount').change();\n  });\n\n  if (!planet['fleet_own']) {\n    jQuery(\"[hide_no_fleet]\").hide();\n  }\n\n  // eco_bld_style_probe = $('#style_probe').css('border-top-color');\n\n  production_id_first ? eco_struc_show_unit_info(production_id_first, true) : '';\n});\n\n\nvar bld_unit_info_width = 0;\n\nfunction extracted(j, balance_header) {\n  switch (j) {\n    case 'level':\n      balance_header += '<th class=\"c_l\">' + language['level_short'] + '</th>';\n      break;\n\n    case 'sys_metal':\n    case 'sys_crystal':\n    case 'sys_deuterium':\n    case 'sys_energy':\n    case 'sys_expeditions':\n    case 'sys_colonies':\n      balance_header += '<th class=\"c_c\" colspan=\"2\">' + language[j];\n      break;\n\n    case 'metal_diff':\n    case 'crystal_diff':\n    case 'deuterium_diff':\n    case 'energy_diff':\n    case 'sys_expeditions_diff':\n    case 'sys_colonies_diff':\n      balance_header += '</th>';\n      break;\n  }\n  return balance_header;\n}\nfunction eco_struc_show_unit_info(unit_id, no_color) {\n  if (!no_color) {\n    $('#unit' + unit_id).addClass('unit_border_selected');\n  }\n\n  if (unit_selected) {\n    return;\n  }\n\n  $('#unit_id').val(unit_id);\n  $('#unit_amountslide').slider({value: 0, max: 0});\n  // $('#unit_amountslide').slider({ max: 0});\n\n  var unit = production[unit_id];\n  var result = '';\n  var unit_destroy_link = '';\n\n  var unit_info_image = $('#unit_info_image');\n  unit_info_image.attr('src', unit['image']);\n  unit_info_image.attr('unit_id', unit_id);\n  $('#unit_info_wiki').attr('unit_id', unit_id);\n  $('#unit_info_description').html(unit['description']);\n\n  $('#unit_time').html(unit['time']);\n  $('#unit_time_div').css('display', unit['time_seconds'] ? \"block\" : \"none\");\n\n  $('#unit_info_name').html(unit['name']);\n  if (unit['level'] > 0 || STACKABLE) {\n    $('#unit_info_level').html(\n      (!STACKABLE ? language['level'] + ' ' : '')\n      + unit['level']\n      + (Math.intVal(unit['level_bonus']) ? '<span class=\"bonus\">+' + unit['level_bonus'] + '</span>' : '')\n    );\n  } else {\n    $('#unit_info_level').html('&nbsp;');\n  }\n\n  if (STACKABLE) {\n    $('#unit_max').show();\n    $('#unit_max_number').html(sn_format_number(unit['can_build']));\n    $('#unit_max_number_autoconvert').html(sn_format_number(unit['autoconvert_amount']));\n  }\n\n  $('#unit_create, #unit_create *').prop('disabled', true);\n  $('#unit_create_button').button('disable');\n  $('#unit_destroy').css('visibility', 'hidden');\n\n  var req;\n  var requirement_string = '';\n  if (require[unit_id]) {\n    for (i in require[unit_id]) {\n      if(!require[unit_id].hasOwnProperty(i)) {\n        continue;\n      }\n\n      req = require[unit_id][i];\n      requirement_string = requirement_string\n        + '<li class=\"' + (req['requerements_met'] ? 'positive' : 'negative  ') + '\">'\n        + '<a href=\"infos.php?gid=' + req.id + '\" class=\"link\">' + req['name'] + '</a>'\n        + (!isNaN(req['level_basic']) ? ' ' + req['level_basic'] + (req['level_bonus'] ? '<span class=\"bonus\">+' + req['level_bonus'] + '</span>' : '') + '/' + req['level_require'] : '')\n        + '</li>';\n    }\n    $('#unit_require').empty().append(requirement_string);\n    $('#unit_require_wrapper').show();\n  } else {\n    $('#unit_require_wrapper').hide();\n  }\n\n  if (grants[unit_id]) {\n    requirement_string = '';\n    for (i in grants[unit_id]) {\n      req = grants[unit_id][i];\n      requirement_string = requirement_string\n        + '<li class=\"' + (req['requerements_met'] ? 'positive' : 'negative  ') + '\">'\n        + req['name']\n        + (!isNaN(req['level_basic']) ? ' ' + req['level_basic'] + (req['level_bonus'] ? '<span class=\"bonus\">+' + req['level_bonus'] + '</span>' : '') + '/' + req['level_require'] : '')\n        + '</li>';\n    }\n    $('#unit_grants').empty().append(requirement_string);\n    $('#unit_grants_wrapper').show();\n  } else {\n    $('#unit_grants_wrapper').hide();\n  }\n\n  if (TEMPORARY) {\n    $(\"#unit_cost_table\").hide();\n  } else {\n    $(\"#unit_cost_table\").css('display', 'table');\n\n    eco_struc_make_resource_rows_all(unit);\n\n    if (planet['que_has_place'] != 0 && !unit['unit_busy']) {\n      if (STACKABLE) {\n        $(\"#auto_convert\").change();\n        if (unit['build_can'] != 0 && unit['build_result'] == 0) {\n          $('#unit_create, #unit_create *').prop('disabled', false);\n          $('#unit_amountslide').slider({max: unit['can_build']});\n        }\n      } else {\n        if (unit['level'] > 0 && unit['destroy_can'] != 0 && unit['destroy_result'] == 0) {\n          $('#unit_destroy').css('visibility', 'visible');\n          $('#unit_destroy_level').html(unit['level']);\n          $('#unit_destroy_resources').html(\n            (unit['destroy_metal'] ? language['sys_metal'][0] + ': ' + sn_format_number(unit['destroy_metal'], 0, 'positive') + ' ' : '')\n            + (unit['destroy_crystal'] ? language['sys_crystal'][0] + ': ' + sn_format_number(unit['destroy_crystal'], 0, 'positive') + ' ' : '')\n            + (unit['destroy_deuterium'] ? language['sys_deuterium'][0] + ':' + sn_format_number(unit['destroy_deuterium'], 0, 'positive') + ' ' : '')\n          );\n          $('#unit_destroy_time').html(unit['destroy_time']);\n        }\n\n        if ((planet['fields_free'] > 0 || unit['unit_type'] == UNIT_TECHNOLOGIES) && unit['build_can'] != 0 && unit['build_result'] == 0) {\n          $('#unit_create_button').button('enable');\n          $('#unit_create, #unit_create *').prop('disabled', false);\n        }\n      }\n    }\n  }\n  $('#unit_create_button_auto').button(unit['can_autoconvert'] ? 'enable' : 'disable').prop('disabled', unit['can_autoconvert'] ? false : true);\n  $('#unit_create_level, #unit_create_level_auto').html(unit['level'] + 1);\n\n  var i, j, limit;\n  result = '';\n  if (unit[\"resource_map\"]) {\n    var balance_header = '';\n    if (STACKABLE) {\n      for (i in unit[\"resource_map\"][0]) {\n        if (!unit[\"resource_map\"][0].hasOwnProperty(i)) {\n          continue;\n        }\n        if (unit[\"resource_map\"][0][i]) {\n          result += '<tr>';\n          result += '<th class=\"c_l\">' + language[i] + '</th>';\n          result += '<td class=\"c_r\">' + unit[\"resource_map\"][0][i] + '</td>';\n          result += '</tr>';\n        }\n      }\n    } else {\n      var has_header = false;\n      for (i in unit[\"resource_map\"]) {\n        if (!unit[\"resource_map\"].hasOwnProperty(i)) {\n          continue;\n        }\n\n        result += '<tr class=\"c_r\">';\n        for (j in unit[\"resource_map\"][i]) {\n          if (!unit[\"resource_map\"][i].hasOwnProperty(j) || !unit[\"resource_map\"][i][j]) {\n            continue;\n          }\n\n          if (!has_header) {\n            balance_header = extracted(j, balance_header);\n          }\n\n          limit =\n            j == 'level'\n              ? -unit['level'] - unit['level_bonus']\n              :\n              (\n                j.indexOf('diff') == -1\n                  ? (unit[\"resource_map\"][i - 1] ? -unit[\"resource_map\"][i - 1][j] : 0)\n                  : 0\n              )\n          ;\n          result += '<td>' +\n            sn_format_number(\n              Math.floatVal(unit[\"resource_map\"][i][j]), 0, 'positive',\n              limit,\n              unit[\"resource_map\"][i][j] > 0 && j.indexOf('diff') >= 0) + '</td>';\n        }\n        result += '</tr>';\n        has_header = true;\n      }\n    }\n\n    result ? result = (balance_header ? '<tr>' + balance_header + '</tr>' : '') + result : false;\n  }\n  !result ? result = '<tr><th class=\"c_c\">' + language['eco_bld_unit_info_extra_none'] + '</th></tr>' : false;\n  $('#unit_balance').html('<table class=\"border_image_small\">' + result + '</table>');\n  $('<style></style>').appendTo($(document.body)).remove();\n}\n\nfunction eco_struc_select_unit(unit_id) {\n  $('#unit_id').val(unit_id);\n  if (unit_selected == unit_id) {\n    unit_selected = null;\n  } else {\n    if (unit_selected) {\n      $('#unit' + unit_selected).removeClass('unit_border_selected');\n\n      unit_selected = null; // Need to override eco_struc_show_unit_info() behavior\n      eco_struc_show_unit_info(unit_id);\n    }\n    unit_selected = unit_id;\n    $('#unit_amountslide').slider({value: 0});\n  }\n}\n\n$(document).on('click', '#eco_que_clear', function (e) {\n  snConfirm({\n    that: $(this),\n    message: language.eco_que_clear_dialog_text,\n    title: language.eco_que_clear_dialog_title\n  });\n  return false;\n});\n\n$(document).on('click', '#eco_que_artifact', function (e) {\n  snConfirm({\n    that: $(this),\n    message: language.eco_que_artifact_dialog_text.format($(this).text()),\n    title: language.eco_que_artifact_dialog_title.format($(this).text())\n  });\n  return false;\n});\n"
  },
  {
    "path": "js/chat_advanced.js",
    "content": "/*\n * chat_advanced.js\n *\n Copyright © 2009-2020 Gorlum for http://supernova.ws\n*/\nvar chat_refreshing = false;\nvar chat_disabled = false;\nvar chat_last_message = 0;\n\njQuery(document).ready(function () {\n  if (!window.SN_GOOGLE) {\n    jQuery(document).on('mouseleave', '.chat_unmute, [chat_muted]', function () {\n      $('div#chat_muted_tooltip').css('display', 'none');\n    });\n  }\n\n  // Натягиваем скины на элементы смайлики\n  jQuery(\"#chat_smile_point, #chat_color_button, #chat_color_select div, #chat_command_menu div:not(:first-child):not(:last-child)\").button().addClass('ui-textfield');\n  $('#chat_message_smiles2 div').addClass(\"ui-button ui-widget ui-state-default ui-corner-all ui-button-text-only ui-textfield ui-button-text\").attr('role', 'button');\n\n  if (!IS_HISTORY) {\n    height = $('#shoutbox_cell').css('height');\n    $('#shoutbox').css('height', height);\n\n    $('#online_table').css('height', height);\n    $('#chat_online_div').css('height', $('#chat_online_cell').css('height'));\n\n    showMessage(true);\n\n    $('#msg').focus();\n  }\n});\n\njQuery(document).on('keypress', '#msg', function (e) {\n  if (e.ctrlKey && (e.keyCode == 13 || e.keyCode == 10)) {\n    jQuery(\"#send\").click();\n  }\n});\n\n// Make player visible/invisible\njQuery(document).on('click', '#chat_player_invisible', function () {\n  addMessage('/invisible ' + (jQuery('#chat_player_invisible').is(':checked') ? 'on' : 'off'));\n});\n\n// Click to show/hide online\njQuery(document).on('click', '#chat_online_dragger', function () {\n  if ($(\"#chat_online_wrapper\").is(\":visible\")) {\n    $(\".js_chat_side_panel\").hide(\"fast\");\n    $(\"#chat_online_dragger\").text(\"<<\");\n  } else {\n    $(\".js_chat_side_panel\").show(\"fast\");\n    // $(\".js_chat_side_panel\").show(\"slide\", { direction: \"right\" }, 1000); // Need effects installed\n    $(\"#chat_online_div\").css({\"height\": \"100%\"});\n    $(\"#chat_online_dragger\").text(\">>\");\n  }\n});\n\n// On resize we need to show right panel if it was enlarged above limits and hide it if it was shrinked below limits\n$(window).resize(function () {\n  clearTimeout(window.resizedFinished);\n  window.resizedFinished = setTimeout(function () {\n    if ($(\"#chat_online_dragger\").is(\":visible\")) {\n      $(\"#chat_online_dragger\").text(\"<<\");\n      if ($(\"#chat_online_wrapper\").is(\":visible\")) {\n        $(\".js_chat_side_panel\").hide();\n      }\n    } else {\n      if ($(\"#chat_online_wrapper\").is(\":not(:visible)\")) {\n        $(\".js_chat_side_panel\").show(\"fast\");\n      }\n    }\n  }, 250);\n});\n\n// Click on simple nick in chat\njQuery(document).on('click', '.chat_nick_msg', function () {\n  addSmiley(\"(\" + jQuery(this).text() + \")\");\n});\n// Click to whisper - whisper nick in chat or nick in online list\njQuery(document).on('click', '[safe_name]', function () {\n  addSmiley(\"/w \" + (jQuery(this).attr('safe_name') ? jQuery(this).attr('safe_name') : jQuery(this).html()) + \" \", true);\n});\n\n// Tooltip for muted player - name and ban untils\njQuery(document).on('mouseenter', '[chat_muted]', function () {\n  that = jQuery(this);\n  tooltip = $('div#chat_muted_tooltip');\n\n  if (tooltip.css('display') == 'none') {\n    tooltip\n      .html(L_chat_advanced_command_mute\n        .replace('%1$s', that.parent().prev().html())\n        .replace('%2$s', that.attr('chat_muted'))\n        .replace('%3$s', that.attr('chat_mute_reason') ? L_chat_advanced_command_reason.replace('%s', '\"' + that.attr('chat_mute_reason')) + '\"' : ''))\n      .css({\n        'display': 'block',\n        'position': 'absolute',\n        'top': that.position().top + that.height(),\n        'left': that.position().left - tooltip.width() - that.width()\n      });\n  } else {\n    tooltip.css('display', 'none');\n  }\n});\n\n// -------------------\n// Chat Administration\n// Unmute player\njQuery(document).on('click', '.chat_unmute', function () {\n  addMessage('/unmute id ' + jQuery(this).parent().attr('player_id'));\n  $('div#chat_muted_tooltip').css('display', 'none');\n});\n// Popup menu for ban and mute\njQuery(document).on('click', '.chat_ban,.chat_mute', function () {\n  // addMessage('/ban id ' + jQuery(this).parent().attr('player_id') + ' 7d');\n  that = jQuery(this);\n  tooltip = $('div#chat_command_menu');\n\n  if (tooltip.css('display') == 'none') {\n    last_child = tooltip.find('div:last-child');\n    last_child\n      .find('span')\n      .css('display', that.hasClass('chat_ban') ? 'inline' : 'none')\n      .find('input:checkbox').attr(\"checked\", \"checked\");\n\n    last_child\n      .find('input:text').val(that.hasClass('chat_ban') ? L_chat_advanced_online_banned_via_chat : '');\n\n    tooltip\n      .find('#chat_command')\n      .html((that.hasClass('chat_ban') ? L_chat_advanced_online_ban : L_chat_advanced_online_mute).replace('%1$s', that.parent().next().html()));\n    tooltip\n      .attr('command', that.hasClass('chat_ban') ? 'ban' : 'mute')\n      .attr('player_id', that.parent().attr('player_id'))\n      .css({\n        'display': 'block',\n        'position': 'absolute',\n        'top': that.position().top + that.height(),\n        'left': that.position().left - that.width() - tooltip.width()\n      });\n  } else {\n    tooltip.css('display', 'none');\n  }\n});\n// Make ban or mute when command length selected\njQuery(document).on('click', '#chat_command_menu div:not(:first-child):not(:last-child)', function (e) {\n  that = jQuery(this);\n  command = that.parent().attr('command');\n  menu = $('div#chat_command_menu');\n  addMessage(\n    '/' + command\n    + ' id ' + that.parent().attr('player_id') + ' '\n    + that.attr('interval') + (menu.find('input:checkbox').is('[checked]') ? '' : '!')\n    + ' ' + menu.find('input:text').val());\n  menu.css('display', 'none');\n});\n// Tooltip for unmute button\njQuery(document).on('mouseenter', '.chat_unmute', function () {\n  that = jQuery(this);\n  tooltip = $('div#chat_muted_tooltip');\n\n  if (tooltip.css('display') == 'none') {\n    tooltip\n      .html(L_chat_advanced_online_unmute.replace('%1$s', that.parent().next().html()))\n      .css({\n        'display': 'block',\n        'position': 'absolute',\n        'top': that.position().top + that.height(),\n        'left': that.position().left - tooltip.width() - that.width()\n      });\n  } else {\n    tooltip.css('display', 'none');\n  }\n});\n\n\n// -------------------------------\n// Chat history buttons and select\njQuery(document).on('click change', '[chat_history_go]', function (e) {\n  is_select = (that = jQuery(this))[0].tagName == 'SELECT';\n  if (e.type == 'change' || !is_select) {\n    document.location.assign(\"index.php?page=chat_msg&ally=\" + ally_id + \"&history=\" + IS_HISTORY + \"&sheet=\" + (is_select ? that.val() : that.attr('chat_history_go')));\n  }\n});\n\n// ----------------\n// Chat smile popup\n$(document).on('click', '#chat_smile_point', function (event) {\n  tooltip = $('div#chat_message_smiles2');\n  offset = $(this).offset();\n  if (tooltip.css('display') == 'none') {\n    tooltip.css({\n      'display': 'block',\n      'position': 'absolute',\n      'top': offset.top - tooltip.height() - 15,\n      'left': offset.left - tooltip.width() / 3\n    });\n  } else {\n    tooltip.css('display', 'none');\n  }\n});\n// Click on smile\njQuery(document).on('click', '[smile]', function () {\n  addSmiley($(this).attr('smile'));\n  $('div#chat_message_smiles2').css('display', 'none');\n});\n// Click NOT on smile\njQuery(document).on('click', '#chat_message_smiles2', function () {\n  $(this).css('display', 'none');\n});\n\n\n// ----------------\n// Chat color popup\n$(document).on('click', '#chat_color_button', function () {\n  tooltip = $('div#chat_color_select');\n  offset = $(this).offset();\n  if (tooltip.css('display') == 'none') {\n    tooltip.css({\n      'display': 'block',\n      'position': 'absolute',\n      'top': offset.top - tooltip.height() - 15,\n      'left': offset.left - tooltip.width() / 3\n    });\n  } else {\n    tooltip.css('display', 'none');\n  }\n});\n//// Click on coloring button\njQuery(document).on('click', '#chat_color_select div', function () {\n  $('#chat_color').val($(this).attr('color'));\n  $(\"#chat_message_inputs #msg\").css('color', $(this).attr('color'));\n  $('div#chat_color_select').css('display', 'none');\n});\n// Click NOT on coloring button\njQuery(document).on('click', '#chat_color_select', function () {\n  $(this).css('display', 'none');\n});\n\n\nfunction addSmiley(smiley, onStart) {\n  jQuery('#chat_box #msg').val((onStart ? smiley : '') + jQuery('#chat_box #msg').val() + (onStart ? '' : smiley));\n  document.chat_form.msg.focus();\n}\n\nfunction addMessage(message) {\n  !message ? message = jQuery(\"#msg\").val() : false;\n  if (!message) {\n    return;\n  }\n\n  // jQuery(\"#msg\").focus().val(''); // focus\n\n  jQuery\n    .post(\n      \"index.php?page=chat_add\",\n      {'ally': ally_id, 'message': message, 'color': jQuery(\"#chat_color\").val()}\n    )\n    .always(function () {\n      showMessage();\n    });\n\n  jQuery(\"#msg\").val('').focus();\n}\n\nfunction showMessage(initial) {\n  if (chat_refreshing || chat_disabled) {\n    return;\n  }\n\n  chat_refreshing = true;\n  jQuery.post(\"index.php?page=chat_msg\", {\n      'page': 'chat_msg',\n      'ally': ally_id,\n      'last_message': chat_last_message\n    }, function (data) {\n      var focused_element = $(\"*:focus\").get(0);\n\n      // var return_focus = $(\"#msg:focus\").length; // focus\n      if (data.html) {\n        // var shoutbox = document.getElementById('shoutbox');\n        chat_last_message = data.last_message;\n        // shoutbox.innerHTML += data.html;\n        var shoutbox = jQuery('#shoutbox');\n        shoutbox.html(shoutbox.html() + data.html);\n        shoutbox.animate({scrollTop: shoutbox.prop('scrollHeight')}, 2000);\n        if (initial !== true) {\n          sn_sound_play(\"chat_message\");\n        }\n      }\n\n      if (data.users_total) {\n        jQuery('.js_global_users_total').html(data.users_total);\n      }\n      if (data.users_online) {\n        jQuery('.js_global_users_online').html(data.users_online);\n      }\n\n      if (data.online) {\n        var onlinebox = document.getElementById('onlinebox');\n        onlinebox.innerHTML = data.online;\n        // TODO !!!!\n        // jQuery('#onlinebox').animate({scrollTop: jQuery('#shoutbox').prop('scrollHeight')}, 0);\n      }\n\n      jQuery('#online_players').html(data.online_players);\n      jQuery('#online_invisibles').html(data.online_invisibles);\n      if (data.chat_player_invisible == 1) {\n        jQuery('#chat_player_invisible').attr('checked', 'checked');\n      } else {\n        jQuery('#chat_player_invisible').removeAttr('checked');\n      }\n\n      if (data.disable != undefined) {\n        jQuery('#msg,#send,#chat_color').attr('disabled', 'disabled');\n        jQuery('#chat_message_inputs, #chat_message_smiles, #chat_online_wrapper').hide();\n        jQuery('#chat_message_refresh').css('display', 'table-row');\n        chat_disabled = true;\n      } else {\n        jQuery('#msg,#send,#chat_color').removeAttr('disabled');\n\n        // if(return_focus) {\n        //   $('#msg').focus();\n        // }\n        if(focused_element) {\n          focused_element.focus();\n        }\n        chat_refreshing = false;\n        window.setTimeout(showMessage, chat_refresh_rate);\n      }\n    },\n    \"json\")\n    .always(function (data) {\n      if (!chat_disabled) {\n        window.setTimeout(showMessage, chat_refresh_rate);\n      }\n    });\n}\n"
  },
  {
    "path": "js/cnt.js",
    "content": "var pp;\n\nfunction t() {\n\tv  = new Date();\n\tvar btc = document.getElementById('btc');\n\tn  = new Date();\n\tss = pp;\n\ts  = ss - Math.round((n.getTime() - v.getTime()) / 1000.);\n\tm  = 0;\n\th  = 0;\n\tif ( s < 0 ) {\n\t\tbtc.innerHTML = \"Termin&eacute;<br>\" + \"<a href=?cp=\" + pl + \">Continuer</a>\"\n\t} else {\n\t\tif ( s > 59 ) {\n\t\tm = Math.floor( s / 60 );\n\t\ts = s - m * 60\n\t\t}\n\t\tif ( m > 59 ) {\n\t\t\th = Math.floor( m / 60 );\n\t\t\tm = m - h * 60\n\t\t}\n\t\tif ( s < 10 ) {\n\t\t\ts = \"0\" + s\n\t\t}\n\t\tif ( m < 10 ) {\n\t\t\tm = \"0\" + m\n\t\t}\n\t\tbtc.innerHTML = h + \":\" + m + \":\" + s + \"<br><a href=\" + ps + \"?cmd=\" + pk + \"&cp=\" + pl + \">Annuler</a>\"\n\t}\n\tpp = pp - 1;\n\twindow.setTimeout(\"t();\", 999);\n}"
  },
  {
    "path": "js/fleet.js",
    "content": "var C_SHIP_NAME        = 0,\n    C_SHIP_AMOUNT      = 1,\n    C_SHIP_SPEED       = 2,\n    C_SHIP_CONSUMPTION = 3,\n    C_SHIP_CAPACITY    = 4;\n\nvar C1_SHIP_NAME        = 0,\n    C1_SHIP_AMOUNT      = 0,\n    C1_SHIP_SPEED       = 1,\n    C1_SHIP_CONSUMPTION = 2,\n    C1_SHIP_CAPACITY    = 3;\n\nvar prev_mission = 0;\n\nfunction changeMission(mission) {\n  if($(mission).val() == prev_mission) {\n    return;\n  }\n\n  prev_mission = $(mission).val();\n\n  jQuery(\"img.mission_button_image.button_pseudo_pressed\").removeClass('button_pseudo_pressed');\n  var button_pressed = jQuery(\"#mission_button\" + prev_mission);\n  button_pressed.addClass('button_pseudo_pressed');\n\n  //var mini_mission = $('#fleet_mini_mission');\n  //mini_mission.length ? mini_mission.html('<img src=\"design/images/mission_pointer_' + prev_mission + '.png\" /><br />' + button_pressed.parent().text()) : false;\n  var mini_mission = $('#fleet_mini_mission');\n  mini_mission.length ? mini_mission.find('span').text(button_pressed.parent().find('#mission_name').text()) : false;\n\n  switch(prev_mission) {\n    case '1': // Attack\n    case '2': // AKS\n    case '5': // Hold\n    case '6': // Spy\n    case '8': // Recycle\n    case '9': // Destroy\n    case '10': // Missile\n    case '15':// Explore\n      jQuery('#resTable').hide();\n      jQuery('#resource0').val(0).trigger('change');\n      jQuery('#resource1').val(0).trigger('change');\n      jQuery('#resource2').val(0).trigger('change');\n    break;\n\n    default:\n      jQuery('#resTable').show();\n    break;\n  }\n\n  if(prev_mission == 15) {\n    jQuery('.fleet_expedition_warning').show();\n  } else {\n    jQuery('.fleet_expedition_warning').hide();\n  }\n}\n\nfunction speed_percent() {\n  var sp = document.getElementsByName(\"speed\")\n  return sp.length ? sp[0].value : 10;\n}\n\nfunction setTarget(galaxy, solarsystem, planet, planet_type) {\n  document.getElementsByName('galaxy')[0].value = galaxy;\n  document.getElementsByName('system')[0].value = solarsystem;\n  document.getElementsByName('planet')[0].value = planet;\n  document.getElementsByName('planet_type')[0].value = planet_type;\n  $('#end_coords').html('[' + galaxy + ':' + solarsystem + ':' + planet + ']');\n  $('#end_planet_type').html(planet_types[planet_type]);\n  planet_name = planet_names['g' + galaxy + 's' + solarsystem + 'p' + planet + 't' + planet_type];\n  $('#end_name').html(planet_name ? planet_name : '');\n  shortInfo();\n  return false;\n}\n\nfunction setMission(mission) {\n  document.getElementsByName('order')[0].selectedIndex = mission;\n  return;\n}\n\nfunction setACS(fleet_group) {\n  $('#fleet_group').val(fleet_group);\n//   document.getElementsByName('fleet_group')[0].value = fleet_group;\n//   return;\n}\n\nfunction setACS_target(acs_target_mr) {\n   document.getElementsByName('acs_target_mr')[0].value = acs_target_mr;\n   return;\n}\n\nfunction min(a, b) {\n  a = a * 1;\n  b = b * 1;\n  if (a > b) {\n    return b;\n  } else {\n    return a;\n  }\n}\n\nfunction distance() {\n  var thisGalaxy;\n  var thisSystem;\n  var thisPlanet;\n\n  var targetGalaxy;\n  var targetSystem;\n  var targetPlanet;\n\n  var dist = 0;\n\n  targetGalaxy = document.getElementsByName(\"galaxy\")[0].value;\n  targetSystem = document.getElementsByName(\"system\")[0].value;\n  targetPlanet = document.getElementsByName(\"planet\")[0].value;\n\n  thisGalaxy = document.getElementsByName(\"thisgalaxy\");\n  if(thisGalaxy.length) {\n    thisGalaxy = document.getElementsByName(\"thisgalaxy\")[0].value;\n    thisSystem = document.getElementsByName(\"thissystem\")[0].value;\n    thisPlanet = document.getElementsByName(\"thisplanet\")[0].value;\n\n    if ((targetGalaxy - thisGalaxy) != 0) {\n      dist = Math.abs(targetGalaxy - thisGalaxy) * UNIVERSE_GALAXY_DISTANCE;\n    } else if ((targetSystem - thisSystem) != 0) {\n      dist = Math.abs(targetSystem - thisSystem) * 5 * 19 + 2700;\n    } else if ((targetPlanet - thisPlanet) != 0) {\n      dist = Math.abs(targetPlanet - thisPlanet) * 5 + 1000;\n    } else {\n      dist = 5;\n    }\n  } else {\n    dist = UNIVERSE_GALAXY_DISTANCE;\n  }\n\n  return(dist);\n}\n\nfunction duration() {\n  ret = Math.round(((35000 / speed_percent() * Math.sqrt(distance() * 10 / fleet_speed) + 10) / speed_factor ));\n  return ret;\n}\n\nfunction consumption() {\n  var consumption = 0;\n  var spd = speed_percent() * Math.sqrt(fleet_speed);\n\n  for (var i in ships) {\n    shipcount = ships[i][C1_SHIP_AMOUNT];\n    shipspeed = ships[i][C1_SHIP_SPEED];\n    shipconsumption = ships[i][C1_SHIP_CONSUMPTION];\n\n    consumption += shipconsumption * shipcount  * (spd / Math.sqrt(shipspeed) / 10 + 1 ) * (spd / Math.sqrt(shipspeed) / 10 + 1 );\n  }\n\n  consumption = Math.round(distance() * consumption / 35000) + 1;\n  return(consumption);\n}\n\nfunction shortInfo() {\n  jQuery(\"#distance\").html(sn_format_number(distance()));\n\n  var seconds = duration();\n  var duration_tick = seconds * 1000;\n  if(seconds) {\n    var hours = Math.floor(seconds / 3600);\n    seconds -= hours * 3600;\n\n    var minutes = Math.floor(seconds / 60);\n    seconds -= minutes * 60;\n\n    if (hours < 10) hours = \"0\" + hours;\n    if (minutes < 10) minutes = \"0\" + minutes;\n    if (seconds < 10) seconds = \"0\" + seconds;\n\n    jQuery(\"#duration\").html(hours + \":\" + minutes + \":\" + seconds);\n\n    time_temp_local = new Date();\n    time_temp_local.setTime(time_temp_local.valueOf() + duration_tick);\n    jQuery('#time_dst_local').html(time_temp_local.toLocaleString());\n\n    time_temp = new Date(time_temp_local.valueOf() - timeDiff * 1000);\n    jQuery('#time_dst').html(time_temp.toLocaleString());\n\n    time_temp_local.setTime(time_temp_local.valueOf() + duration_tick);\n    jQuery('#time_src_local').html(time_temp_local.toLocaleString());\n\n    time_temp.setTime(time_temp.valueOf() + duration_tick);\n    jQuery('#time_src').html(time_temp.toLocaleString());\n  } else {\n    jQuery(\"#duration\").html(\"-\");\n  }\n  var cons = consumption();\n\n  jQuery(\"#consumption\").html(sn_format_number(cons, 0, 'positive'));\n  jQuery(\"#capacity\").html(sn_format_number(fleet_capacity - cons, 0, 'positive'));\n}\n\nvar fleet_consumption = 0;\nvar fleet_capacity    = 0;\nvar fleet_speed       = Infinity;\n\nfunction fl_calc_stats(event, ui) {\n  if(fleet_global_update)\n  {\n    return;\n  }\n\n  fleet_consumption = 0;\n  fleet_capacity    = 0;\n  fleet_speed       = Infinity;\n\n  var ship_number = Array();\n\n  for(i in ships)\n  {\n    ship_number[i] = jQuery('#ships' + i + 'slide').slider(\"value\");\n    if( ship_number[i] != 0)\n    {\n      fleet_speed = Math.min(fleet_speed, ships[i][C1_SHIP_SPEED]);\n      fleet_capacity += ship_number[i] * ships[i][C1_SHIP_CAPACITY];\n    }\n  }\n\n\n  var spd = speed_percent() * Math.sqrt(fleet_speed);\n  for(i in ships)\n  {\n    if( ship_number[i] != 0)\n    {\n      fleet_consumption += ships[i][C1_SHIP_CONSUMPTION] * ship_number[i]  * (spd / Math.sqrt(ships[i][C1_SHIP_SPEED]) / 10 + 1 ) * (spd / Math.sqrt(ships[i][C1_SHIP_SPEED]) / 10 + 1 );\n    }\n  }\n  fleet_consumption = Math.round(distance() * fleet_consumption / 35000) + 1;\n  if(fleet_capacity > 0)\n  {\n    fleet_capacity -= fleet_consumption;\n  }\n  else\n  {\n    fleet_consumption = 0;\n  }\n\n  document.getElementById('int_fleet_capacity').innerHTML = sn_format_number(fleet_capacity);\n  document.getElementById('int_fleet_consumption').innerHTML = sn_format_number(fleet_consumption);\n  document.getElementById('int_fleet_speed').innerHTML = fleet_speed == Infinity ? '-' : sn_format_number(fleet_speed);\n\n  shortInfo();\n}\n\nfunction calculateTransportCapacity() {\n  var transportCapacity = fleet_capacity - check_resource(0) - check_resource(1) - check_resource(2);\n\n  $(\"#remainingresources\").html(sn_format_number(transportCapacity, 0, 'positive'));\n\n  $(\"#fleet_page2_submit\").prop('disabled', transportCapacity < 0);\n  if(transportCapacity < 0) {\n    $(\".fleet_expedition_not_enough_fuel\").show();\n  } else {\n    $(\".fleet_expedition_not_enough_fuel\").hide();\n  }\n\n  // document.getElementById(\"fleet_page2_submit\").disabled = transportCapacity < 0;\n  //if(transportCapacity < 0) {\n  //  document.getElementById(\"fleet_page2_submit\").disabled = true;\n  //} else {\n  //  document.getElementById(\"fleet_page2_submit\").disabled = false;\n  //}\n  return transportCapacity;\n}\n\nfunction setNumber(name,number){\n  if (typeof document.getElementsByName('ship'+name)[0] != 'undefined'){\n    document.getElementsByName('ship'+name)[0].value=number;\n  }\n}\n\nfunction abs(a) {\n  return a < 0 ? -a : a;\n}\n\n\nvar fleet_global_update = false;\n\nfunction zero_fleet()\n{\n  fleet_global_update = true;\n/*\n  for (i in ships)\n  {\n    jQuery('#ships' + i + 'slide').slider(\"value\", 0);\n  }\n*/\n  jQuery('[id^=\"ships\"][id$=\"slide\"]').slider(\"value\", 0);\n  fleet_global_update = false;\n  fl_calc_stats();\n}\n\nfunction expe_fleet(expeditions)\n{\n  if(expeditions > 1)\n  {\n    // expeditions = 1 / expeditions;\n    fleet_global_update = true;\n  /*\n    for (i in ships)\n    {\n      jQuery('#ships' + i + 'slide').slider(\"value\", ships[i][C1_SHIP_AMOUNT]);\n    }\n  */\n    jQuery('[id^=\"ships\"][id$=\"slide\"]').each(function(){\n      jQuery(this).slider(\"value\", Math.floor(jQuery(this).slider(\"option\", \"max\") / expeditions));\n    });\n    fleet_global_update = false;\n    fl_calc_stats();\n  }\n}\n\nfunction max_fleet()\n{\n  fleet_global_update = true;\n/*\n  for (i in ships)\n  {\n    jQuery('#ships' + i + 'slide').slider(\"value\", ships[i][C1_SHIP_AMOUNT]);\n  }\n*/\n  jQuery('[id^=\"ships\"][id$=\"slide\"]').each(function(){\n    jQuery(this).slider(\"value\", jQuery(this).slider(\"option\", \"max\"));\n  });\n  fleet_global_update = false;\n  fl_calc_stats();\n}\n\nfunction check_resource(id)\n{\n//  var zi_res = parseInt(document.getElementById(\"resource\" + id).value);\n  var zi_res = parseInt($('#resource' + id).val());\n  zi_res = zi_res ? zi_res : 0;\n\n  $('#rest_res' + id).html(sn_format_number(resource_max[id] - zi_res, 0, 'zero'));\n  // document.getElementById('rest_res' + id).innerHTML = sn_format_number(resource_max[id] - zi_res, 0, 'zero');\n\n  return zi_res;\n}\n\nfunction zero_resource(id)\n{\n  element = document.getElementsByName('resource' + id)[0];\n\n  if(element)\n  {\n    element.value = 0;\n    jQuery(\"#resource\" + id).trigger('change');\n  }\n  calculateTransportCapacity();\n}\n\nfunction zero_resources()\n{\n  for (i in resource_max)\n  {\n    zero_resource(i);\n  }\n  calculateTransportCapacity();\n}\n\nfunction max_resource(id) {\n  if (document.getElementsByName(\"resource\" + id)[0])\n  {\n    var freeCapacity = Math.max(fleet_capacity - check_resource(0) - check_resource(1) - check_resource(2), 0);\n    var cargo = Math.min (freeCapacity + check_resource(id), resource_max[id]);\n\n    document.getElementsByName(\"resource\" + id)[0].value = cargo;\n    jQuery(\"#resource\" + id).trigger('change');\n    calculateTransportCapacity();\n  }\n}\n\nfunction max_resources() {\n  for (var i in resource_max) {\n    max_resource(i);\n  }\n  calculateTransportCapacity();\n}\n\nfunction fleet_dialog_show(caller, fleet_id) {\n  popup_show(fleet_table_make(fleet_id), {my: 'left top', at: 'right top', of: caller});\n}\n\nfunction fleet_table_make(fleet_id)\n{\n  if(!fleets[fleet_id]) {\n    return '';\n  }\n\n  if(!fleets[fleet_id][9]) {\n    var fleet_html = '<table class=\"no_border_image\"><tr><td class=\"c\" colspan=\"2\">' + language['sys_fleet_composition'] + '</td></tr>';\n    var fleet = fleets[fleet_id][0];\n    var resources = fleets[fleet_id][1];\n\n    var ship_id, res_id;\n    var fleet_capacity = 0;\n\n    for(ship_id in fleet) {\n      if(!fleet.hasOwnProperty(ship_id)) {\n        continue;\n      }\n\n      fleet_html += '<tr><td class=\"c_l\">';\n      fleet_html += fleet[ship_id][C_SHIP_NAME];\n      fleet_html += '</td><td class=\"c_r\">' + sn_format_number(parseInt(fleet[ship_id][C_SHIP_AMOUNT]));\n      fleet_html += '</td></tr>';\n      fleet_capacity += fleet[ship_id][C_SHIP_CAPACITY] * fleet[ship_id][C_SHIP_AMOUNT];\n    }\n\n    if(fleet_capacity) {\n      fleet_html += '<tr><td class=\"c\">' + language['sys_capacity'] + '</td><td class=\"c\" style=\"padding-right: 3px;\">' + sn_format_number(fleet_capacity, 0, 'zero') + '</td></tr>';\n    }\n\n    var resources_total = parseInt(resources[0]) + parseInt(resources[1]) + parseInt(resources[2]);\n    if(resources_total > 0) {\n      for(res_id in resources) {\n        if(!resources.hasOwnProperty(res_id) || !parseInt(resources[res_id])) {\n          continue;\n        }\n        fleet_html += '<tr><th class=c><div style=\"text-align: left\">' + res_names[res_id] + '</div></th><th><div style=\"text-align: right;\">' + sn_format_number(parseInt(resources[res_id]), 0, 'zero') + '</div></th></tr>';\n      }\n\n      fleet_html += '<tr><td class=c>' + language['sys_resources'] + '</td><td class=c style=\"text-align: right; padding-right: 3px;\">' + sn_format_number(resources_total, 0, 'zero') + '</td></tr>';\n    }\n\n    fleet_html += '</table>';\n\n    fleets[fleet_id][9] = fleet_html;\n  }\n\n  return(fleets[fleet_id][9]);\n}\n\nfunction fleet_page_2_loaded() {\n  mission_checked = 0;\n  $(\"[name='target_mission']:checked\").each(function(){\n    mission_checked = $(this);\n  });\n\n  if(!mission_checked) {\n    mission_checked = $(\"[name='target_mission']:first\");\n    mission_checked.attr('checked', 1);\n  }\n  changeMission(mission_checked);\n\n  calculateTransportCapacity();\n}\n\nfunction fleet_page_2_prepare_slider(resourceID, resourceOnPlanet, fleetCapacity) {\n  sn_ainput_make('resource' + resourceID, {max: Math.min(resourceOnPlanet, fleetCapacity), step: 1000, button_max: true, button_zero: true});\n\n  jQuery('#resource' + resourceID).on('keyup change', function(event, ui) {\n    calculateTransportCapacity();\n  });\n\n  jQuery('#resource' + resourceID + 'slide').on('slide slidechange', function(event, ui) {\n    if(fleet_slide_changing) {\n      return;\n    } else {\n      fleet_slide_changing = true;\n    }\n    var transportCapacity = fleetCapacity - check_resource(0) - check_resource(1) - check_resource(2);\n\n    for(i = 0; i < 3; i++) {\n      aSlider = jQuery('#resource' + i + 'slide');\n      aSlider.slider(\"option\", \"max\", Math.min(aSlider.slider(\"value\") + transportCapacity, resource_max[i]));\n      jQuery('#resource' + i).change();\n    }\n    fleet_slide_changing = false;\n  });\n}"
  },
  {
    "path": "js/fleet_gather.js",
    "content": "var colonies = [];\nvar reCalcGatheringStarted = false;\n\nfunction reCalcGathering() {\n  var colonyValue, maxColonyValue, freeCapacity, colony, resourceID, addedValue;\n\n  if (reCalcGatheringStarted) {\n    return;\n  }\n  reCalcGatheringStarted = true;\n\n  var resourceGrid = [0, 0, 0];\n  for (colony in colonies) {\n    colonyValue = 0;\n    maxColonyValue = 0;\n    freeCapacity = colonies[colony][3];\n\n    for (resourceID in resourceGrid) {\n      if (!resourceGrid.hasOwnProperty(resourceID)) {\n        continue;\n      }\n\n      if (jQuery('#ga_' + colony + '_' + resourceID + '').is(':checked')) {\n        addedValue = Math.min(freeCapacity, colonies[colony][resourceID]);\n        freeCapacity -= addedValue;\n\n        resourceGrid[resourceID] += addedValue;\n        colonyValue += addedValue;\n        maxColonyValue += colonies[colony][resourceID];\n      }\n    }\n\n    jQuery('#ga_' + colony + '_a')\n      .removeClass(\"negative zero positive\")\n      .addClass(colonyValue < maxColonyValue ? (colonies[colony][3] ? 'negative' : 'zero') : 'positive')\n      .text(sn_format_number(colonyValue));\n    // jQuery('#ga_' + colony + '_a').html(\n    //   '<span class=\"' + (colonyValue < maxColonyValue ? (colonies[colony][3] ? 'negative' : 'zero') : 'positive') + '\">'\n    //   + sn_format_number(colonyValue)\n    //   + '</span>'\n    // );\n  }\n\n  jQuery('#ga_a_0').html(sn_format_number(resourceGrid[0]));\n  jQuery('#ga_a_1').html(sn_format_number(resourceGrid[1]));\n  jQuery('#ga_a_2').html(sn_format_number(resourceGrid[2]));\n  jQuery('#ga_a_a').html(sn_format_number(resourceGrid[0] + resourceGrid[1] + resourceGrid[2]));\n\n  reCalcGatheringStarted = false;\n}\n\njQuery(document).ready(function(){\n  reCalcGathering();\n});\n"
  },
  {
    "path": "js/generate.js",
    "content": "var xmax = 110\nvar x = new Array(xmax)\nx[0]=\"Ae\"\nx[1]=\"Arn\"\nx[2]=\"Abi\"\nx[3]=\"Alk\"\nx[4]=\"Baa\"\nx[5]=\"Beia\"\nx[6]=\"Bea\"\nx[7]=\"Chai\"\nx[8]=\"Cen\"\nx[9]=\"Dorn\"\nx[10]=\"Dreg\"\nx[11]=\"Dov\"\nx[12]=\"Dap\"\nx[13]=\"Kyth\"\nx[14]=\"Lave\"\nx[15]=\"Tig\"\nx[16]=\"Aven\"\nx[17]=\"Kyl\"\nx[18]=\"My\"\nx[19]=\"Ang\"\nx[20]=\"Dil\"\nx[21]=\"Sar\"\nx[22]=\"Bip\"\nx[23]=\"Grai\"\nx[24]=\"Soan\"\nx[25]=\"Rugha\"\nx[26]=\"Jadea\"\nx[27]=\"Flan\"\nx[28]=\"Nys\"\nx[29]=\"Nil\"\nx[30]=\"Amil\"\nx[31]=\"Lial\"\nx[32]=\"Jem\"\nx[33]=\"Timo\"\nx[34]=\"Est\"\nx[35]=\"Esmer\"\nx[36]=\"Path\"\nx[37]=\"Marc\"\nx[38]=\"Paer\"\nx[39]=\"Aga\"\nx[40]=\"Khaz\"\nx[41]=\"Ach\"\nx[42]=\"Peri\"\nx[43]=\"Art\"\nx[44]=\"Fall\"\nx[45]=\"Kaa\"\nx[46]=\"Miy\"\nx[47]=\"Borl\"\nx[48]=\"Mik\"\nx[49]=\"Cind\"\nx[50]=\"Wol\"\nx[51]=\"Ray\"\nx[52]=\"Hal\"\nx[53]=\"Rob\"\nx[54]=\"Timo\"\nx[55]=\"Bokae\"\nx[56]=\"Asilo\"\nx[57]=\"Zort\"\nx[58]=\"Nili\"\nx[59]=\"Sedi\"\nx[60]=\"Werda\"\nx[61]=\"Dinra\"\nx[62]=\"Orna\"\nx[63]=\"Schan\"\nx[64]=\"Eaplo\"\nx[65]=\"Verilo\"\nx[66]=\"Besid\"\nx[67]=\"Hage\"\nx[68]=\"Xanpol\"\nx[69]=\"Deriter\"\nx[70]=\"Sawan\"\nx[71]=\"Pesidon\"\nx[72]=\"Phrea\"\nx[73]=\"Fiphi\"\nx[74]=\"Rhafigen\"\nx[75]=\"Kergan\"\nx[76]=\"Lyni\"\nx[77]=\"Jando\"\nx[78]=\"Quirh\"\nx[79]=\"Roina\"\nx[80]=\"Dese\"\nx[81]=\"Verti\"\nx[82]=\"Zire\"\nx[83]=\"Zanth\"\nx[84]=\"Zthra\"\nx[85]=\"Virh\"\nx[86]=\"Vea\"\nx[87]=\"Volia\"\nx[88]=\"Nydi\"\nx[89]=\"Nave\"\nx[90]=\"Ghot\"\nx[91]=\"Oanti\"\nx[92]=\"Oren\"\nx[93]=\"Shor\"\nx[94]=\"Kord\"\nx[95]=\"Miria\"\nx[96]=\"Moli\"\nx[97]=\"Medro\"\nx[98]=\"Serin\"\nx[99]=\"Astre\"\nx[100]=\"Astha\"\nx[101]=\"Pan\"\nx[102]=\"Houn\"\nx[103]=\"Qua\"\nx[104]=\"Ni\"\nx[105]=\"Ge\"\nx[106]=\"Di\"\nx[107]=\"Li\"\nx[108]=\"Ea\"\nx[109]=\"De\"\nx[110]=\"Ny\"\n\nvar ymax = 110\nvar y = new Array(ymax)\ny[0]=\"red\"\ny[1]=\"illon\"\ny[2]=\"drak\"\ny[3]=\"ilo\"\ny[4]=\"ker\"\ny[5]=\"anian\"\ny[6]=\"ath\"\ny[7]=\"ius\"\ny[8]=\"ven\"\ny[9]=\"isab\"\ny[10]=\"lay\"\ny[11]=\"vite\"\ny[12]=\"hun\"\ny[13]=\"ger\"\ny[14]=\"gons\"\ny[15]=\"ving\"\ny[16]=\"rol\"\ny[17]=\"lianth\"\ny[18]=\"kall\"\ny[19]=\"ven\"\ny[20]=\"sharr\"\ny[21]=\"cil\"\ny[22]=\"unube\"\ny[23]=\"ahm\"\ny[24]=\"lino\"\ny[25]=\"iba\"\ny[26]=\"ivo\"\ny[27]=\"freano\"\ny[28]=\"avo\"\ny[29]=\"ilo\"\ny[30]=\"donar\"\ny[31]=\"jako\"\ny[32]=\"mina\"\ny[33]=\"rinh\"\ny[34]=\"lion\"\ny[35]=\"manti\"\ny[36]=\"onga\"\ny[37]=\"tarea\"\ny[38]=\"intre\"\ny[39]=\"scire\"\ny[40]=\"philus\"\ny[41]=\"vadro\"\ny[42]=\"arok\"\ny[43]=\"olites\"\ny[44]=\"ka\"\ny[45]=\"mythe\"\ny[46]=\"pil\"\ny[47]=\"reni\"\ny[48]=\"ra\"\ny[49]=\"uan\"\ny[50]=\"dester\"\ny[51]=\"senthio\"\ny[52]=\"vertino\"\ny[53]=\"anao\"\ny[54]=\"azath\"\ny[55]=\"nifri\"\ny[56]=\"arapo\"\ny[57]=\"gavad\"\ny[58]=\"kilor\"\ny[59]=\"tlion\"\ny[60]=\"windre\"\ny[61]=\"slin\"\ny[62]=\"til\"\ny[63]=\"ustil\"\ny[64]=\"ontil\"\ny[65]=\"wesdoa\"\ny[66]=\"werona\"\ny[67]=\"kryo\"\ny[68]=\"kit\"\ny[69]=\"maol\"\ny[70]=\"wargh\"\ny[71]=\"aredo\"\ny[72]=\"kibo\"\ny[73]=\"ollia\"\ny[74]=\"yetea\"\ny[75]=\"indra\"\ny[76]=\"likaso\"\ny[77]=\"dil\"\ny[78]=\"lin\"\ny[79]=\"swern\"\ny[80]=\"eidhip\"\ny[81]=\"hasea\"\ny[82]=\"cerion\"\ny[83]=\"ksathe\"\ny[84]=\"terro\"\ny[85]=\"sanio\"\ny[86]=\"pilion\"\ny[87]=\"hia\"\ny[88]=\"mi\"\ny[89]=\"on\"\ny[90]=\"lia\"\ny[91]=\"rian\"\ny[92]=\"stor\"\ny[93]=\"elia\"\ny[94]=\"seth\"\ny[95]=\"gord\"\ny[96]=\"onard\"\ny[97]=\"deran\"\ny[98]=\"oda\"\ny[99]=\"lonth\"\ny[100]=\"pear\"\ny[101]=\"old\"\ny[102]=\"ord\"\ny[103]=\"frea\"\ny[104]=\"midre\"\ny[105]=\"dhe\"\ny[106]=\"kedr\"\ny[107]=\"toran\"\ny[108]=\"vera\"\ny[109]=\"tong\"\ny[110]=\"avinio\"\n\n\nfunction rndnumber(max)\n        {\n        var randscript = -1\n        while (randscript < 0 || randscript > max || isNaN(randscript))\n                {\n                randscript = parseInt(Math.random()*(max+1))\n                }\n        return randscript\n        }\nfunction profundity()\n        {\n        a = rndnumber( xmax )\n        b = rndnumber( ymax )\n        msg = x[a] + y[b]\n        return msg\n        }"
  },
  {
    "path": "js/index.html",
    "content": ""
  },
  {
    "path": "js/lib/fm.checkator.jquery.js",
    "content": "/*\n Checkator jQuery Plugin\n A plugin for radio and checkbox elements\n version 1.1, May 16th, 2015\n by Ingi P. Jacobsen\n\n The MIT License (MIT)\n\n Copyright (c) 2013 Ingi P. Jacobsen\n\n Permission is hereby granted, free of charge, to any person obtaining a copy of\n this software and associated documentation files (the \"Software\"), to deal in\n the Software without restriction, including without limitation the rights to\n use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of\n the Software, and to permit persons to whom the Software is furnished to do so,\n subject to the following conditions:\n\n The above copyright notice and this permission notice shall be included in all\n copies or substantial portions of the Software.\n\n THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS\n FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR\n COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER\n IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\n CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n*/\n\n(function($) {\n\t$.checkator = function (element, options) {\n\t\tvar defaults = {\n\t\t\tprefix: 'checkator_'\n\t\t};\n\t\n\t\tvar plugin = this;\n\t\tvar type = $(element).attr('type');\n\t\tvar checked = $(element)[0].checked;\n\t\tvar wrapper = null;\n\t\tvar new_element = null;\n\t\tplugin.settings = {};\n\n\t\t\n\t\t\n\t\t// INITIALIZE PLUGIN\n\t\tplugin.init = function () {\n\t\t\tplugin.settings = $.extend({}, defaults, options);\n\n\t\t\twrapper = document.createElement('div');\n\t\t\t$(wrapper).addClass(plugin.settings.prefix + 'holder ' + type);\n\t\t\t$(element).before(wrapper);\n\t\t\t$(wrapper).append(element);\n\t\t\tnew_element = document.createElement('div');\n\t\t\tif (element.id !== undefined) {\n\t\t\t\t$(new_element).attr('id', plugin.settings.prefix + element.id);\n\t\t\t}\n\t\t\t$(new_element).addClass(plugin.settings.prefix + 'element ' + type + ' ' + (checked ? 'checked ' : ''));\n\n\t\t\t$(wrapper).css({\n\t\t\t\twidth: $(element).outerWidth() + 'px',\n\t\t\t\theight: $(element).outerHeight() + 'px',\n\t\t\t\t'margin-top': $(element).css('margin-top'),\n\t\t\t\t'margin-right': $(element).css('margin-right'),\n\t\t\t\t'margin-bottom': $(element).css('margin-bottom'),\n\t\t\t\t'margin-left': $(element).css('margin-left'),\n\t\t\t\t'float': $(element).css('float'),\n\t\t\t\t'display': $(element).css('display') === 'inline' ? 'inline-clock' : $(element).css('display')\n\t\t\t});\n\t\t\t$(element).css({\n\t\t\t\topacity: 0,\n\t\t\t\tmargin: 0\n\t\t\t});\n\t\t\t\n\t\t\t$(element).addClass(plugin.settings.prefix + 'source');\n\t\t\t$(element).after(new_element);\n\n\t\t};\n\n\n\t\t// REMOVE PLUGIN AND REVERT SELECT ELEMENT TO ORIGINAL STATE\n\t\tplugin.destroy = function () {\n\t\t\t$(new_element).remove();\n\t\t\t$.removeData(element, 'checkator');\n\t\t\t$(element).css({ \n\t\t\t\topacity: '',\n\t\t\t\tmargin: '' \n\t\t\t});\n\t\t\t$(element).removeClass(plugin.settings.prefix + 'source');\n\t\t\t$(element).unwrap();\n\t\t};\n\t\t\n\n\t\t// Initialize plugin\n\t\tplugin.init();\n\t};\n\t\n\t$.fn.checkator = function(options) {\n\t\toptions = options !== undefined ? options : {};\n\t\treturn this.each(function () {\n\t\t\tif (typeof(options) === 'object') {\n\t\t\t\tif (undefined === $(this).data('checkator')) {\n\t\t\t\t\tvar plugin = new $.checkator(this, options);\n\t\t\t\t\t$(this).data('checkator', plugin);\n\t\t\t\t}\n\t\t\t} else if ($(this).data('checkator')[options]) {\n\t\t\t\t$(this).data('checkator')[options].apply(this, Array.prototype.slice.call(arguments, 1));\n\t\t\t} else {\n\t\t\t\t$.error('Method ' + options + ' does not exist in $.checkator');\n\t\t\t}\n\t\t});\n\t};\n\n}(jQuery));\n\n$(function () {\n\t$('.checkator').checkator();\n});\n"
  },
  {
    "path": "js/lib/ion.sound.js",
    "content": "﻿/**\n * Ion.Sound\n * version 3.0.7 Build 89\n * © Denis Ineshin, 2016\n *\n * Project page:    http://ionden.com/a/plugins/ion.sound/en.html\n * GitHub page:     https://github.com/IonDen/ion.sound\n *\n * Released under MIT licence:\n * http://ionden.com/a/plugins/licence-en.html\n */\n\n;(function (window, navigator, $, undefined) {\n    \"use strict\";\n\n    window.ion = window.ion || {};\n\n    if (ion.sound) {\n        return;\n    }\n\n    var warn = function (text) {\n        if (!text) text = \"undefined\";\n\n        if (window.console) {\n            if (console.warn && typeof console.warn === \"function\") {\n                console.warn(text);\n            } else if (console.log && typeof console.log === \"function\") {\n                console.log(text);\n            }\n\n            var d = $ && $(\"#debug\");\n            if (d && d.length) {\n                var a = d.html();\n                d.html(a + text + '<br/>');\n            }\n        }\n    };\n\n    var extend = function (parent, child) {\n        var prop;\n        child = child || {};\n\n        for (prop in parent) {\n            if (parent.hasOwnProperty(prop)) {\n                child[prop] = parent[prop];\n            }\n        }\n\n        return child;\n    };\n\n\n\n    /**\n     * DISABLE for unsupported browsers\n     */\n\n    if (typeof Audio !== \"function\" && typeof Audio !== \"object\") {\n        var func = function () {\n            warn(\"HTML5 Audio is not supported in this browser\");\n        };\n        ion.sound = func;\n        ion.sound.play = func;\n        ion.sound.stop = func;\n        ion.sound.pause = func;\n        ion.sound.preload = func;\n        ion.sound.destroy = func;\n        func();\n        return;\n    }\n\n\n\n    /**\n     * CORE\n     * - creating sounds collection\n     * - public methods\n     */\n\n    var is_iOS = /iPad|iPhone|iPod/.test(navigator.appVersion),\n        sounds_num = 0,\n        settings = {},\n        sounds = {},\n        i;\n\n\n\n    if (!settings.supported && is_iOS) {\n        settings.supported = [\"mp3\", \"mp4\", \"aac\"];\n    } else if (!settings.supported) {\n        settings.supported = [\"mp3\", \"ogg\", \"mp4\", \"aac\", \"wav\"];\n    }\n\n    var createSound = function (obj) {\n        var name = obj.alias || obj.name;\n\n        if (!sounds[name]) {\n            sounds[name] = new Sound(obj);\n            sounds[name].init();\n        }\n    };\n\n    ion.sound = function (options) {\n        extend(options, settings);\n\n        settings.path = settings.path || \"\";\n        settings.volume = settings.volume || 1;\n        settings.preload = settings.preload || false;\n        settings.allow_caching = settings.allow_caching || false;\n        settings.multiplay = settings.multiplay || false;\n        settings.loop = settings.loop || false;\n        settings.sprite = settings.sprite || null;\n        settings.scope = settings.scope || null;\n        settings.ready_callback = settings.ready_callback || null;\n        settings.ended_callback = settings.ended_callback || null;\n\n        sounds_num = settings.sounds.length;\n\n        if (!sounds_num) {\n            warn(\"No sound-files provided!\");\n            return;\n        }\n\n        for (i = 0; i < sounds_num; i++) {\n            createSound(settings.sounds[i]);\n        }\n    };\n\n    ion.sound.VERSION = \"3.0.7\";\n\n    ion.sound._method = function (method, name, options) {\n        if (name) {\n            sounds[name] && sounds[name][method](options);\n        } else {\n            for (i in sounds) {\n                if (!sounds.hasOwnProperty(i) || !sounds[i]) {\n                    continue;\n                }\n\n                sounds[i][method](options);\n            }\n        }\n    };\n\n    ion.sound.preload = function (name, options) {\n        options = options || {};\n        extend({preload: true}, options);\n\n        ion.sound._method(\"init\", name, options);\n    };\n\n    ion.sound.destroy = function (name) {\n        ion.sound._method(\"destroy\", name);\n\n        if (name) {\n            sounds[name] = null;\n        } else {\n            for (i in sounds) {\n                if (!sounds.hasOwnProperty(i)) {\n                    continue;\n                }\n                if (sounds[i]) {\n                    sounds[i] = null;\n                }\n            }\n        }\n    };\n\n    ion.sound.play = function (name, options) {\n        ion.sound._method(\"play\", name, options);\n    };\n\n    ion.sound.stop = function (name, options) {\n        ion.sound._method(\"stop\", name, options);\n    };\n\n    ion.sound.pause = function (name, options) {\n        ion.sound._method(\"pause\", name, options);\n    };\n\n    ion.sound.volume = function (name, options) {\n        ion.sound._method(\"volume\", name, options);\n    };\n\n    if ($) {\n        $.ionSound = ion.sound;\n    }\n\n\n\n    /**\n     * Web Audio API core\n     * - for most advanced browsers\n     */\n\n    var AudioContext = window.AudioContext || window.webkitAudioContext,\n        audio;\n\n    if (AudioContext) {\n        audio = new AudioContext();\n    }\n\n\n    var Sound = function (options) {\n        this.options = extend(settings);\n        delete this.options.sounds;\n        extend(options, this.options);\n\n        this.request = null;\n        this.streams = {};\n        this.result = {};\n        this.ext = 0;\n        this.url = \"\";\n\n        this.loaded = false;\n        this.decoded = false;\n        this.no_file = false;\n        this.autoplay = false;\n    };\n\n    Sound.prototype = {\n        init: function (options) {\n            if (options) {\n                extend(options, this.options);\n            }\n\n            if (this.options.preload) {\n                this.load();\n            }\n        },\n\n        destroy: function () {\n            var stream;\n\n            for (i in this.streams) {\n                stream = this.streams[i];\n\n                if (stream) {\n                    stream.destroy();\n                    stream = null;\n                }\n            }\n            this.streams = {};\n            this.result = null;\n            this.options.buffer = null;\n            this.options = null;\n\n            if (this.request) {\n                this.request.removeEventListener(\"load\", this.ready.bind(this), false);\n                this.request.removeEventListener(\"error\", this.error.bind(this), false);\n                this.request.abort();\n                this.request = null;\n            }\n        },\n\n        createUrl: function () {\n            this.url = this.options.path + encodeURIComponent(this.options.name) + \".\" + this.options.supported[this.ext];\n            if(!this.options.allow_caching) {\n              var no_cache = new Date().valueOf();\n              this.url += \"?\" + no_cache;\n            }\n        },\n\n        load: function () {\n            if (this.no_file) {\n                warn(\"No sources for \\\"\" + this.options.name + \"\\\" sound :(\");\n                return;\n            }\n\n            if (this.request) {\n                return;\n            }\n\n            this.createUrl();\n\n            this.request = new XMLHttpRequest();\n            this.request.open(\"GET\", this.url, true);\n            this.request.responseType = \"arraybuffer\";\n            this.request.addEventListener(\"load\", this.ready.bind(this), false);\n            this.request.addEventListener(\"error\", this.error.bind(this), false);\n\n            this.request.send();\n        },\n\n        reload: function () {\n            this.ext++;\n\n            if (this.options.supported[this.ext]) {\n                this.load();\n            } else {\n                this.no_file = true;\n                warn(\"No sources for \\\"\" + this.options.name + \"\\\" sound :(\");\n            }\n        },\n\n        ready: function (data) {\n            this.result = data.target;\n\n            if (this.result.readyState !== 4) {\n                this.reload();\n                return;\n            }\n\n            if (this.result.status !== 200 && this.result.status !== 0) {\n                warn(this.url + \" was not found on server!\");\n                this.reload();\n                return;\n            }\n\n            this.request.removeEventListener(\"load\", this.ready.bind(this), false);\n            this.request.removeEventListener(\"error\", this.error.bind(this), false);\n            this.request = null;\n            this.loaded = true;\n            //warn(\"Loaded: \" + this.options.name + \".\" + settings.supported[this.ext]);\n\n            this.decode();\n        },\n\n        decode: function () {\n            if (!audio) {\n                return;\n            }\n\n            audio.decodeAudioData(this.result.response, this.setBuffer.bind(this), this.error.bind(this));\n        },\n\n        setBuffer: function (buffer) {\n            this.options.buffer = buffer;\n            this.decoded = true;\n            //warn(\"Decoded: \" + this.options.name + \".\" + settings.supported[this.ext]);\n\n            var config = {\n                name: this.options.name,\n                alias: this.options.alias,\n                ext: this.options.supported[this.ext],\n                duration: this.options.buffer.duration\n            };\n\n            if (this.options.ready_callback && typeof this.options.ready_callback === \"function\") {\n                this.options.ready_callback.call(this.options.scope, config);\n            }\n\n            if (this.options.sprite) {\n\n                for (i in this.options.sprite) {\n                    this.options.start = this.options.sprite[i][0];\n                    this.options.end = this.options.sprite[i][1];\n                    this.streams[i] = new Stream(this.options, i);\n                }\n\n            } else {\n\n                this.streams[0] = new Stream(this.options);\n\n            }\n\n            if (this.autoplay) {\n                this.autoplay = false;\n                this.play();\n            }\n        },\n\n        error: function () {\n            this.reload();\n        },\n\n        play: function (options) {\n            delete this.options.part;\n\n            if (options) {\n                extend(options, this.options);\n            }\n\n            if (!this.loaded) {\n                this.autoplay = true;\n                this.load();\n\n                return;\n            }\n\n            if (this.no_file || !this.decoded) {\n                return;\n            }\n\n            if (this.options.sprite) {\n                if (this.options.part) {\n                    this.streams[this.options.part].play(this.options);\n                } else {\n                    for (i in this.options.sprite) {\n                        this.streams[i].play(this.options);\n                    }\n                }\n            } else {\n                this.streams[0].play(this.options);\n            }\n        },\n\n        stop: function (options) {\n            if (this.options.sprite) {\n\n                if (options) {\n                    this.streams[options.part].stop();\n                } else {\n                    for (i in this.options.sprite) {\n                        this.streams[i].stop();\n                    }\n                }\n\n            } else {\n                this.streams[0].stop();\n            }\n        },\n\n        pause: function (options) {\n            if (this.options.sprite) {\n\n                if (options) {\n                    this.streams[options.part].pause();\n                } else {\n                    for (i in this.options.sprite) {\n                        this.streams[i].pause();\n                    }\n                }\n\n            } else {\n                this.streams[0].pause();\n            }\n        },\n\n        volume: function (options) {\n            var stream;\n\n            if (options) {\n                extend(options, this.options);\n            } else {\n                return;\n            }\n\n            if (this.options.sprite) {\n                if (this.options.part) {\n                    stream = this.streams[this.options.part];\n                    stream && stream.setVolume(this.options);\n                } else {\n                    for (i in this.options.sprite) {\n                        stream = this.streams[i];\n                        stream && stream.setVolume(this.options);\n                    }\n                }\n            } else {\n                stream = this.streams[0];\n                stream && stream.setVolume(this.options);\n            }\n        }\n    };\n\n\n\n    var Stream = function (options, sprite_part) {\n        this.alias = options.alias;\n        this.name = options.name;\n        this.sprite_part = sprite_part;\n\n        this.buffer = options.buffer;\n        this.start = options.start || 0;\n        this.end = options.end || this.buffer.duration;\n        this.multiplay = options.multiplay || false;\n        this.volume = options.volume || 1;\n        this.scope = options.scope;\n        this.ended_callback = options.ended_callback;\n\n        this.setLoop(options);\n\n        this.source = null;\n        this.gain = null;\n        this.playing = false;\n        this.paused = false;\n\n        this.time_started = 0;\n        this.time_ended = 0;\n        this.time_played = 0;\n        this.time_offset = 0;\n    };\n\n    Stream.prototype = {\n        destroy: function () {\n            this.stop();\n\n            this.buffer = null;\n            this.source = null;\n\n            this.gain && this.gain.disconnect();\n            this.source && this.source.disconnect();\n            this.gain = null;\n            this.source = null;\n        },\n\n        setLoop: function (options) {\n            if (options.loop === true) {\n                this.loop = 9999999;\n            } else if (typeof options.loop === \"number\") {\n                this.loop = +options.loop - 1;\n            } else {\n                this.loop = false;\n            }\n        },\n\n        update: function (options) {\n            this.setLoop(options);\n            if (\"volume\" in options) {\n                this.volume = options.volume;\n            }\n        },\n\n        play: function (options) {\n            if (options) {\n                this.update(options);\n            }\n\n            if (!this.multiplay && this.playing) {\n                return;\n            }\n\n            this.gain = audio.createGain();\n            this.source = audio.createBufferSource();\n            this.source.buffer = this.buffer;\n            this.source.connect(this.gain);\n            this.gain.connect(audio.destination);\n            this.gain.gain.value = this.volume;\n\n            this.source.onended = this.ended.bind(this);\n\n            this._play();\n        },\n\n        _play: function () {\n            var start,\n                end;\n\n            if (this.paused) {\n                start = this.start + this.time_offset;\n                end = this.end - this.time_offset;\n            } else {\n                start = this.start;\n                end = this.end;\n            }\n\n            if (end <= 0) {\n                this.clear();\n                return;\n            }\n\n            if (typeof this.source.start === \"function\") {\n                this.source.start(0, start, end);\n            } else {\n                this.source.noteOn(0, start, end);\n            }\n\n            this.playing = true;\n            this.paused = false;\n            this.time_started = new Date().valueOf();\n        },\n\n        stop: function () {\n            if (this.playing && this.source) {\n                if (typeof this.source.stop === \"function\") {\n                    this.source.stop(0);\n                } else {\n                    this.source.noteOff(0);\n                }\n            }\n\n            this.clear();\n        },\n\n        pause: function () {\n            if (this.paused) {\n                this.play();\n                return;\n            }\n\n            if (!this.playing) {\n                return;\n            }\n\n            this.source && this.source.stop(0);\n            this.paused = true;\n        },\n\n        ended: function () {\n            this.playing = false;\n            this.time_ended = new Date().valueOf();\n            this.time_played = (this.time_ended - this.time_started) / 1000;\n            this.time_offset += this.time_played;\n\n            if (this.time_offset >= this.end || this.end - this.time_offset < 0.015) {\n                this._ended();\n                this.clear();\n\n                if (this.loop) {\n                    this.loop--;\n                    this.play();\n                }\n            }\n        },\n\n        _ended: function () {\n            var config = {\n                name: this.name,\n                alias: this.alias,\n                part: this.sprite_part,\n                start: this.start,\n                duration: this.end\n            };\n\n            if (this.ended_callback && typeof this.ended_callback === \"function\") {\n                this.ended_callback.call(this.scope, config);\n            }\n        },\n\n        clear: function () {\n            this.time_played = 0;\n            this.time_offset = 0;\n            this.paused = false;\n            this.playing = false;\n        },\n\n        setVolume: function (options) {\n            this.volume = options.volume;\n\n            if (this.gain) {\n                this.gain.gain.value = this.volume;\n            }\n        }\n    };\n\n    if (audio) {\n        return;\n    }\n\n\n\n    /**\n     * Fallback for HTML5 audio\n     * - for not so modern browsers\n     */\n\n    var checkSupport = function () {\n        var sound = new Audio(),\n            can_play_mp3 = sound.canPlayType('audio/mpeg'),\n            can_play_ogg = sound.canPlayType('audio/ogg'),\n            can_play_aac = sound.canPlayType('audio/mp4; codecs=\"mp4a.40.2\"'),\n            item, i;\n\n        for (i = 0; i < settings.supported.length; i++) {\n            item = settings.supported[i];\n\n            if (!can_play_mp3 && item === \"mp3\") {\n                settings.supported.splice(i, 1);\n            }\n\n            if (!can_play_ogg && item === \"ogg\") {\n                settings.supported.splice(i, 1);\n            }\n\n            if (!can_play_aac && item === \"aac\") {\n                settings.supported.splice(i, 1);\n            }\n\n            if (!can_play_aac && item === \"mp4\") {\n                settings.supported.splice(i, 1);\n            }\n        }\n\n        sound = null;\n    };\n    checkSupport();\n\n\n\n    Sound.prototype = {\n        init: function (options) {\n            if (options) {\n                extend(options, this.options);\n            }\n\n            this.inited = true;\n\n            if (this.options.preload) {\n                this.load();\n            }\n        },\n\n        destroy: function () {\n            var stream;\n\n            for (i in this.streams) {\n                stream = this.streams[i];\n\n                if (stream) {\n                    stream.destroy();\n                    stream = null;\n                }\n            }\n            this.streams = {};\n            this.loaded = false;\n            this.inited = false;\n        },\n\n        load: function () {\n            var part;\n\n            this.options.preload = true;\n            this.options._ready = this.ready;\n            this.options._scope = this;\n\n            if (this.options.sprite) {\n\n                for (i in this.options.sprite) {\n                    part = this.options.sprite[i];\n\n                    this.options.start = part[0];\n                    this.options.end = part[1];\n\n                    this.streams[i] = new Stream(this.options, i);\n                }\n\n            } else {\n\n                this.streams[0] = new Stream(this.options);\n\n            }\n        },\n\n        ready: function (duration) {\n            if (this.loaded) {\n                return;\n            }\n\n            this.loaded = true;\n\n            var config = {\n                name: this.options.name,\n                alias: this.options.alias,\n                ext: this.options.supported[this.ext],\n                duration: duration\n            };\n\n            if (this.options.ready_callback && typeof this.options.ready_callback === \"function\") {\n                this.options.ready_callback.call(this.options.scope, config);\n            }\n\n            if (this.autoplay) {\n                this.autoplay = false;\n                this.play();\n            }\n        },\n\n        play: function (options) {\n            if (!this.inited) {\n                return;\n            }\n\n            delete this.options.part;\n\n            if (options) {\n                extend(options, this.options);\n            }\n\n            if (!this.loaded) {\n                if (!this.options.preload) {\n                    this.autoplay = true;\n                    this.load();\n                } else {\n                    this.autoplay = true;\n                }\n\n                return;\n            }\n\n            if (this.options.sprite) {\n                if (this.options.part) {\n                    this.streams[this.options.part].play(this.options);\n                } else {\n                    for (i in this.options.sprite) {\n                        this.streams[i].play(this.options);\n                    }\n                }\n            } else {\n                this.streams[0].play(this.options);\n            }\n        },\n\n        stop: function (options) {\n            if (!this.inited) {\n                return;\n            }\n\n            if (this.options.sprite) {\n\n                if (options) {\n                    this.streams[options.part].stop();\n                } else {\n                    for (i in this.options.sprite) {\n                        this.streams[i].stop();\n                    }\n                }\n\n            } else {\n                this.streams[0].stop();\n            }\n        },\n\n        pause: function (options) {\n            if (!this.inited) {\n                return;\n            }\n\n            if (this.options.sprite) {\n\n                if (options) {\n                    this.streams[options.part].pause();\n                } else {\n                    for (i in this.options.sprite) {\n                        this.streams[i].pause();\n                    }\n                }\n\n            } else {\n                this.streams[0].pause();\n            }\n        },\n\n        volume: function (options) {\n            var stream;\n\n            if (options) {\n                extend(options, this.options);\n            } else {\n                return;\n            }\n\n            if (this.options.sprite) {\n                if (this.options.part) {\n                    stream = this.streams[this.options.part];\n                    stream && stream.setVolume(this.options);\n                } else {\n                    for (i in this.options.sprite) {\n                        stream = this.streams[i];\n                        stream && stream.setVolume(this.options);\n                    }\n                }\n            } else {\n                stream = this.streams[0];\n                stream && stream.setVolume(this.options);\n            }\n        }\n    };\n\n\n\n    Stream = function (options, sprite_part) {\n        this.name = options.name;\n        this.alias = options.alias;\n        this.sprite_part = sprite_part;\n\n        this.multiplay = options.multiplay;\n        this.volume = options.volume;\n        this.preload = options.preload;\n        this.allow_caching = options.allow_caching;\n        this.path = settings.path;\n        this.start = options.start || 0;\n        this.end = options.end || 0;\n        this.scope = options.scope;\n        this.ended_callback = options.ended_callback;\n\n        this._scope = options._scope;\n        this._ready = options._ready;\n\n        this.setLoop(options);\n\n        this.sound = null;\n        this.url = null;\n        this.loaded = false;\n\n        this.start_time = 0;\n        this.paused_time = 0;\n        this.played_time = 0;\n\n        this.init();\n    };\n\n    Stream.prototype = {\n        init: function () {\n            this.sound = new Audio();\n            this.sound.volume = this.volume;\n\n            this.createUrl();\n\n            this.sound.addEventListener(\"ended\", this.ended.bind(this), false);\n            this.sound.addEventListener(\"canplaythrough\", this.can_play_through.bind(this), false);\n            this.sound.addEventListener(\"timeupdate\", this._update.bind(this), false);\n\n            this.load();\n        },\n\n        destroy: function () {\n            this.stop();\n\n            this.sound.removeEventListener(\"ended\", this.ended.bind(this), false);\n            this.sound.removeEventListener(\"canplaythrough\", this.can_play_through.bind(this), false);\n            this.sound.removeEventListener(\"timeupdate\", this._update.bind(this), false);\n\n            this.sound = null;\n            this.loaded = false;\n        },\n\n        createUrl: function () {\n            var rand = new Date().valueOf();\n            this.url = this.path + encodeURIComponent(this.name) + \".\" + settings.supported[0] + \"?\" + rand;\n        },\n\n        can_play_through: function () {\n            if (this.preload) {\n                this.ready();\n            }\n        },\n\n        load: function () {\n            this.sound.src = this.url;\n            this.sound.preload = this.preload ? \"auto\" : \"none\";\n            if (this.preload) {\n                this.sound.load();\n            }\n        },\n\n        setLoop: function (options) {\n            if (options.loop === true) {\n                this.loop = 9999999;\n            } else if (typeof options.loop === \"number\") {\n                this.loop = +options.loop - 1;\n            } else {\n                this.loop = false;\n            }\n        },\n\n        update: function (options) {\n            this.setLoop(options);\n\n            if (\"volume\" in options) {\n                this.volume = options.volume;\n            }\n        },\n\n        ready: function () {\n            if (this.loaded || !this.sound) {\n                return;\n            }\n\n            this.loaded = true;\n            this._ready.call(this._scope, this.sound.duration);\n\n            if (!this.end) {\n                this.end = this.sound.duration;\n            }\n        },\n\n        play: function (options) {\n            if (options) {\n                this.update(options);\n            }\n\n            if (!this.multiplay && this.playing) {\n                return;\n            }\n\n            this._play();\n        },\n\n        _play: function () {\n            if (this.paused) {\n                this.paused = false;\n            } else {\n                try {\n                    this.sound.currentTime = this.start;\n                } catch (e) {}\n            }\n\n            this.playing = true;\n            this.start_time = new Date().valueOf();\n            this.sound.volume = this.volume;\n            this.sound.play();\n        },\n\n        stop: function () {\n            if (!this.playing) {\n                return;\n            }\n\n            this.playing = false;\n            this.paused = false;\n            this.sound.pause();\n            this.clear();\n\n            try {\n                this.sound.currentTime = this.start;\n            } catch (e) {}\n        },\n\n        pause: function () {\n            if (this.paused) {\n                this._play();\n            } else {\n                this.playing = false;\n                this.paused = true;\n                this.sound.pause();\n                this.paused_time = new Date().valueOf();\n                this.played_time += this.paused_time - this.start_time;\n            }\n        },\n\n        _update: function () {\n            if (!this.start_time) {\n                return;\n            }\n\n            var current_time = new Date().valueOf(),\n                played_time = current_time - this.start_time,\n                played = (this.played_time + played_time) / 1000;\n\n            if (played >= this.end) {\n                if (this.playing) {\n                    this.stop();\n                    this._ended();\n                }\n            }\n        },\n\n        ended: function () {\n            if (this.playing) {\n                this.stop();\n                this._ended();\n            }\n        },\n\n        _ended: function () {\n            this.playing = false;\n\n            var config = {\n                name: this.name,\n                alias: this.alias,\n                part: this.sprite_part,\n                start: this.start,\n                duration: this.end\n            };\n\n            if (this.ended_callback && typeof this.ended_callback === \"function\") {\n                this.ended_callback.call(this.scope, config);\n            }\n\n            if (this.loop) {\n                setTimeout(this.looper.bind(this), 15);\n            }\n        },\n\n        looper: function () {\n            this.loop--;\n            this.play();\n        },\n\n        clear: function () {\n            this.start_time = 0;\n            this.played_time = 0;\n            this.paused_time = 0;\n        },\n\n        setVolume: function (options) {\n            this.volume = options.volume;\n\n            if (this.sound) {\n                this.sound.volume = this.volume;\n            }\n        }\n    };\n\n} (window, navigator, window.jQuery || window.$));\n"
  },
  {
    "path": "js/lib/jquery-ui.js",
    "content": "/*! jQuery UI - v1.11.4 - 2017-02-16\n* http://jqueryui.com\n* Includes: core.js, widget.js, mouse.js, position.js, draggable.js, resizable.js, sortable.js, accordion.js, button.js, dialog.js, menu.js, progressbar.js, slider.js, tabs.js, tooltip.js\n* Copyright jQuery Foundation and other contributors; Licensed MIT */\n\n(function(t){\"function\"==typeof define&&define.amd?define([\"jquery\"],t):t(jQuery)})(function(t){function e(e,s){var n,o,a,r=e.nodeName.toLowerCase();return\"area\"===r?(n=e.parentNode,o=n.name,e.href&&o&&\"map\"===n.nodeName.toLowerCase()?(a=t(\"img[usemap='#\"+o+\"']\")[0],!!a&&i(a)):!1):(/^(input|select|textarea|button|object)$/.test(r)?!e.disabled:\"a\"===r?e.href||s:s)&&i(e)}function i(e){return t.expr.filters.visible(e)&&!t(e).parents().addBack().filter(function(){return\"hidden\"===t.css(this,\"visibility\")}).length}t.ui=t.ui||{},t.extend(t.ui,{version:\"1.11.4\",keyCode:{BACKSPACE:8,COMMA:188,DELETE:46,DOWN:40,END:35,ENTER:13,ESCAPE:27,HOME:36,LEFT:37,PAGE_DOWN:34,PAGE_UP:33,PERIOD:190,RIGHT:39,SPACE:32,TAB:9,UP:38}}),t.fn.extend({scrollParent:function(e){var i=this.css(\"position\"),s=\"absolute\"===i,n=e?/(auto|scroll|hidden)/:/(auto|scroll)/,o=this.parents().filter(function(){var e=t(this);return s&&\"static\"===e.css(\"position\")?!1:n.test(e.css(\"overflow\")+e.css(\"overflow-y\")+e.css(\"overflow-x\"))}).eq(0);return\"fixed\"!==i&&o.length?o:t(this[0].ownerDocument||document)},uniqueId:function(){var t=0;return function(){return this.each(function(){this.id||(this.id=\"ui-id-\"+ ++t)})}}(),removeUniqueId:function(){return this.each(function(){/^ui-id-\\d+$/.test(this.id)&&t(this).removeAttr(\"id\")})}}),t.extend(t.expr[\":\"],{data:t.expr.createPseudo?t.expr.createPseudo(function(e){return function(i){return!!t.data(i,e)}}):function(e,i,s){return!!t.data(e,s[3])},focusable:function(i){return e(i,!isNaN(t.attr(i,\"tabindex\")))},tabbable:function(i){var s=t.attr(i,\"tabindex\"),n=isNaN(s);return(n||s>=0)&&e(i,!n)}}),t(\"<a>\").outerWidth(1).jquery||t.each([\"Width\",\"Height\"],function(e,i){function s(e,i,s,o){return t.each(n,function(){i-=parseFloat(t.css(e,\"padding\"+this))||0,s&&(i-=parseFloat(t.css(e,\"border\"+this+\"Width\"))||0),o&&(i-=parseFloat(t.css(e,\"margin\"+this))||0)}),i}var n=\"Width\"===i?[\"Left\",\"Right\"]:[\"Top\",\"Bottom\"],o=i.toLowerCase(),a={innerWidth:t.fn.innerWidth,innerHeight:t.fn.innerHeight,outerWidth:t.fn.outerWidth,outerHeight:t.fn.outerHeight};t.fn[\"inner\"+i]=function(e){return void 0===e?a[\"inner\"+i].call(this):this.each(function(){t(this).css(o,s(this,e)+\"px\")})},t.fn[\"outer\"+i]=function(e,n){return\"number\"!=typeof e?a[\"outer\"+i].call(this,e):this.each(function(){t(this).css(o,s(this,e,!0,n)+\"px\")})}}),t.fn.addBack||(t.fn.addBack=function(t){return this.add(null==t?this.prevObject:this.prevObject.filter(t))}),t(\"<a>\").data(\"a-b\",\"a\").removeData(\"a-b\").data(\"a-b\")&&(t.fn.removeData=function(e){return function(i){return arguments.length?e.call(this,t.camelCase(i)):e.call(this)}}(t.fn.removeData)),t.ui.ie=!!/msie [\\w.]+/.exec(navigator.userAgent.toLowerCase()),t.fn.extend({focus:function(e){return function(i,s){return\"number\"==typeof i?this.each(function(){var e=this;setTimeout(function(){t(e).focus(),s&&s.call(e)},i)}):e.apply(this,arguments)}}(t.fn.focus),disableSelection:function(){var t=\"onselectstart\"in document.createElement(\"div\")?\"selectstart\":\"mousedown\";return function(){return this.bind(t+\".ui-disableSelection\",function(t){t.preventDefault()})}}(),enableSelection:function(){return this.unbind(\".ui-disableSelection\")},zIndex:function(e){if(void 0!==e)return this.css(\"zIndex\",e);if(this.length)for(var i,s,n=t(this[0]);n.length&&n[0]!==document;){if(i=n.css(\"position\"),(\"absolute\"===i||\"relative\"===i||\"fixed\"===i)&&(s=parseInt(n.css(\"zIndex\"),10),!isNaN(s)&&0!==s))return s;n=n.parent()}return 0}}),t.ui.plugin={add:function(e,i,s){var n,o=t.ui[e].prototype;for(n in s)o.plugins[n]=o.plugins[n]||[],o.plugins[n].push([i,s[n]])},call:function(t,e,i,s){var n,o=t.plugins[e];if(o&&(s||t.element[0].parentNode&&11!==t.element[0].parentNode.nodeType))for(n=0;o.length>n;n++)t.options[o[n][0]]&&o[n][1].apply(t.element,i)}};var s=0,n=Array.prototype.slice;t.cleanData=function(e){return function(i){var s,n,o;for(o=0;null!=(n=i[o]);o++)try{s=t._data(n,\"events\"),s&&s.remove&&t(n).triggerHandler(\"remove\")}catch(a){}e(i)}}(t.cleanData),t.widget=function(e,i,s){var n,o,a,r,h={},l=e.split(\".\")[0];return e=e.split(\".\")[1],n=l+\"-\"+e,s||(s=i,i=t.Widget),t.expr[\":\"][n.toLowerCase()]=function(e){return!!t.data(e,n)},t[l]=t[l]||{},o=t[l][e],a=t[l][e]=function(t,e){return this._createWidget?(arguments.length&&this._createWidget(t,e),void 0):new a(t,e)},t.extend(a,o,{version:s.version,_proto:t.extend({},s),_childConstructors:[]}),r=new i,r.options=t.widget.extend({},r.options),t.each(s,function(e,s){return t.isFunction(s)?(h[e]=function(){var t=function(){return i.prototype[e].apply(this,arguments)},n=function(t){return i.prototype[e].apply(this,t)};return function(){var e,i=this._super,o=this._superApply;return this._super=t,this._superApply=n,e=s.apply(this,arguments),this._super=i,this._superApply=o,e}}(),void 0):(h[e]=s,void 0)}),a.prototype=t.widget.extend(r,{widgetEventPrefix:o?r.widgetEventPrefix||e:e},h,{constructor:a,namespace:l,widgetName:e,widgetFullName:n}),o?(t.each(o._childConstructors,function(e,i){var s=i.prototype;t.widget(s.namespace+\".\"+s.widgetName,a,i._proto)}),delete o._childConstructors):i._childConstructors.push(a),t.widget.bridge(e,a),a},t.widget.extend=function(e){for(var i,s,o=n.call(arguments,1),a=0,r=o.length;r>a;a++)for(i in o[a])s=o[a][i],o[a].hasOwnProperty(i)&&void 0!==s&&(e[i]=t.isPlainObject(s)?t.isPlainObject(e[i])?t.widget.extend({},e[i],s):t.widget.extend({},s):s);return e},t.widget.bridge=function(e,i){var s=i.prototype.widgetFullName||e;t.fn[e]=function(o){var a=\"string\"==typeof o,r=n.call(arguments,1),h=this;return a?this.each(function(){var i,n=t.data(this,s);return\"instance\"===o?(h=n,!1):n?t.isFunction(n[o])&&\"_\"!==o.charAt(0)?(i=n[o].apply(n,r),i!==n&&void 0!==i?(h=i&&i.jquery?h.pushStack(i.get()):i,!1):void 0):t.error(\"no such method '\"+o+\"' for \"+e+\" widget instance\"):t.error(\"cannot call methods on \"+e+\" prior to initialization; \"+\"attempted to call method '\"+o+\"'\")}):(r.length&&(o=t.widget.extend.apply(null,[o].concat(r))),this.each(function(){var e=t.data(this,s);e?(e.option(o||{}),e._init&&e._init()):t.data(this,s,new i(o,this))})),h}},t.Widget=function(){},t.Widget._childConstructors=[],t.Widget.prototype={widgetName:\"widget\",widgetEventPrefix:\"\",defaultElement:\"<div>\",options:{disabled:!1,create:null},_createWidget:function(e,i){i=t(i||this.defaultElement||this)[0],this.element=t(i),this.uuid=s++,this.eventNamespace=\".\"+this.widgetName+this.uuid,this.bindings=t(),this.hoverable=t(),this.focusable=t(),i!==this&&(t.data(i,this.widgetFullName,this),this._on(!0,this.element,{remove:function(t){t.target===i&&this.destroy()}}),this.document=t(i.style?i.ownerDocument:i.document||i),this.window=t(this.document[0].defaultView||this.document[0].parentWindow)),this.options=t.widget.extend({},this.options,this._getCreateOptions(),e),this._create(),this._trigger(\"create\",null,this._getCreateEventData()),this._init()},_getCreateOptions:t.noop,_getCreateEventData:t.noop,_create:t.noop,_init:t.noop,destroy:function(){this._destroy(),this.element.unbind(this.eventNamespace).removeData(this.widgetFullName).removeData(t.camelCase(this.widgetFullName)),this.widget().unbind(this.eventNamespace).removeAttr(\"aria-disabled\").removeClass(this.widgetFullName+\"-disabled \"+\"ui-state-disabled\"),this.bindings.unbind(this.eventNamespace),this.hoverable.removeClass(\"ui-state-hover\"),this.focusable.removeClass(\"ui-state-focus\")},_destroy:t.noop,widget:function(){return this.element},option:function(e,i){var s,n,o,a=e;if(0===arguments.length)return t.widget.extend({},this.options);if(\"string\"==typeof e)if(a={},s=e.split(\".\"),e=s.shift(),s.length){for(n=a[e]=t.widget.extend({},this.options[e]),o=0;s.length-1>o;o++)n[s[o]]=n[s[o]]||{},n=n[s[o]];if(e=s.pop(),1===arguments.length)return void 0===n[e]?null:n[e];n[e]=i}else{if(1===arguments.length)return void 0===this.options[e]?null:this.options[e];a[e]=i}return this._setOptions(a),this},_setOptions:function(t){var e;for(e in t)this._setOption(e,t[e]);return this},_setOption:function(t,e){return this.options[t]=e,\"disabled\"===t&&(this.widget().toggleClass(this.widgetFullName+\"-disabled\",!!e),e&&(this.hoverable.removeClass(\"ui-state-hover\"),this.focusable.removeClass(\"ui-state-focus\"))),this},enable:function(){return this._setOptions({disabled:!1})},disable:function(){return this._setOptions({disabled:!0})},_on:function(e,i,s){var n,o=this;\"boolean\"!=typeof e&&(s=i,i=e,e=!1),s?(i=n=t(i),this.bindings=this.bindings.add(i)):(s=i,i=this.element,n=this.widget()),t.each(s,function(s,a){function r(){return e||o.options.disabled!==!0&&!t(this).hasClass(\"ui-state-disabled\")?(\"string\"==typeof a?o[a]:a).apply(o,arguments):void 0}\"string\"!=typeof a&&(r.guid=a.guid=a.guid||r.guid||t.guid++);var h=s.match(/^([\\w:-]*)\\s*(.*)$/),l=h[1]+o.eventNamespace,c=h[2];c?n.delegate(c,l,r):i.bind(l,r)})},_off:function(e,i){i=(i||\"\").split(\" \").join(this.eventNamespace+\" \")+this.eventNamespace,e.unbind(i).undelegate(i),this.bindings=t(this.bindings.not(e).get()),this.focusable=t(this.focusable.not(e).get()),this.hoverable=t(this.hoverable.not(e).get())},_delay:function(t,e){function i(){return(\"string\"==typeof t?s[t]:t).apply(s,arguments)}var s=this;return setTimeout(i,e||0)},_hoverable:function(e){this.hoverable=this.hoverable.add(e),this._on(e,{mouseenter:function(e){t(e.currentTarget).addClass(\"ui-state-hover\")},mouseleave:function(e){t(e.currentTarget).removeClass(\"ui-state-hover\")}})},_focusable:function(e){this.focusable=this.focusable.add(e),this._on(e,{focusin:function(e){t(e.currentTarget).addClass(\"ui-state-focus\")},focusout:function(e){t(e.currentTarget).removeClass(\"ui-state-focus\")}})},_trigger:function(e,i,s){var n,o,a=this.options[e];if(s=s||{},i=t.Event(i),i.type=(e===this.widgetEventPrefix?e:this.widgetEventPrefix+e).toLowerCase(),i.target=this.element[0],o=i.originalEvent)for(n in o)n in i||(i[n]=o[n]);return this.element.trigger(i,s),!(t.isFunction(a)&&a.apply(this.element[0],[i].concat(s))===!1||i.isDefaultPrevented())}},t.each({show:\"fadeIn\",hide:\"fadeOut\"},function(e,i){t.Widget.prototype[\"_\"+e]=function(s,n,o){\"string\"==typeof n&&(n={effect:n});var a,r=n?n===!0||\"number\"==typeof n?i:n.effect||i:e;n=n||{},\"number\"==typeof n&&(n={duration:n}),a=!t.isEmptyObject(n),n.complete=o,n.delay&&s.delay(n.delay),a&&t.effects&&t.effects.effect[r]?s[e](n):r!==e&&s[r]?s[r](n.duration,n.easing,o):s.queue(function(i){t(this)[e](),o&&o.call(s[0]),i()})}}),t.widget;var o=!1;t(document).mouseup(function(){o=!1}),t.widget(\"ui.mouse\",{version:\"1.11.4\",options:{cancel:\"input,textarea,button,select,option\",distance:1,delay:0},_mouseInit:function(){var e=this;this.element.bind(\"mousedown.\"+this.widgetName,function(t){return e._mouseDown(t)}).bind(\"click.\"+this.widgetName,function(i){return!0===t.data(i.target,e.widgetName+\".preventClickEvent\")?(t.removeData(i.target,e.widgetName+\".preventClickEvent\"),i.stopImmediatePropagation(),!1):void 0}),this.started=!1},_mouseDestroy:function(){this.element.unbind(\".\"+this.widgetName),this._mouseMoveDelegate&&this.document.unbind(\"mousemove.\"+this.widgetName,this._mouseMoveDelegate).unbind(\"mouseup.\"+this.widgetName,this._mouseUpDelegate)},_mouseDown:function(e){if(!o){this._mouseMoved=!1,this._mouseStarted&&this._mouseUp(e),this._mouseDownEvent=e;var i=this,s=1===e.which,n=\"string\"==typeof this.options.cancel&&e.target.nodeName?t(e.target).closest(this.options.cancel).length:!1;return s&&!n&&this._mouseCapture(e)?(this.mouseDelayMet=!this.options.delay,this.mouseDelayMet||(this._mouseDelayTimer=setTimeout(function(){i.mouseDelayMet=!0},this.options.delay)),this._mouseDistanceMet(e)&&this._mouseDelayMet(e)&&(this._mouseStarted=this._mouseStart(e)!==!1,!this._mouseStarted)?(e.preventDefault(),!0):(!0===t.data(e.target,this.widgetName+\".preventClickEvent\")&&t.removeData(e.target,this.widgetName+\".preventClickEvent\"),this._mouseMoveDelegate=function(t){return i._mouseMove(t)},this._mouseUpDelegate=function(t){return i._mouseUp(t)},this.document.bind(\"mousemove.\"+this.widgetName,this._mouseMoveDelegate).bind(\"mouseup.\"+this.widgetName,this._mouseUpDelegate),e.preventDefault(),o=!0,!0)):!0}},_mouseMove:function(e){if(this._mouseMoved){if(t.ui.ie&&(!document.documentMode||9>document.documentMode)&&!e.button)return this._mouseUp(e);if(!e.which)return this._mouseUp(e)}return(e.which||e.button)&&(this._mouseMoved=!0),this._mouseStarted?(this._mouseDrag(e),e.preventDefault()):(this._mouseDistanceMet(e)&&this._mouseDelayMet(e)&&(this._mouseStarted=this._mouseStart(this._mouseDownEvent,e)!==!1,this._mouseStarted?this._mouseDrag(e):this._mouseUp(e)),!this._mouseStarted)},_mouseUp:function(e){return this.document.unbind(\"mousemove.\"+this.widgetName,this._mouseMoveDelegate).unbind(\"mouseup.\"+this.widgetName,this._mouseUpDelegate),this._mouseStarted&&(this._mouseStarted=!1,e.target===this._mouseDownEvent.target&&t.data(e.target,this.widgetName+\".preventClickEvent\",!0),this._mouseStop(e)),o=!1,!1},_mouseDistanceMet:function(t){return Math.max(Math.abs(this._mouseDownEvent.pageX-t.pageX),Math.abs(this._mouseDownEvent.pageY-t.pageY))>=this.options.distance},_mouseDelayMet:function(){return this.mouseDelayMet},_mouseStart:function(){},_mouseDrag:function(){},_mouseStop:function(){},_mouseCapture:function(){return!0}}),function(){function e(t,e,i){return[parseFloat(t[0])*(p.test(t[0])?e/100:1),parseFloat(t[1])*(p.test(t[1])?i/100:1)]}function i(e,i){return parseInt(t.css(e,i),10)||0}function s(e){var i=e[0];return 9===i.nodeType?{width:e.width(),height:e.height(),offset:{top:0,left:0}}:t.isWindow(i)?{width:e.width(),height:e.height(),offset:{top:e.scrollTop(),left:e.scrollLeft()}}:i.preventDefault?{width:0,height:0,offset:{top:i.pageY,left:i.pageX}}:{width:e.outerWidth(),height:e.outerHeight(),offset:e.offset()}}t.ui=t.ui||{};var n,o,a=Math.max,r=Math.abs,h=Math.round,l=/left|center|right/,c=/top|center|bottom/,u=/[\\+\\-]\\d+(\\.[\\d]+)?%?/,d=/^\\w+/,p=/%$/,f=t.fn.position;t.position={scrollbarWidth:function(){if(void 0!==n)return n;var e,i,s=t(\"<div style='display:block;position:absolute;width:50px;height:50px;overflow:hidden;'><div style='height:100px;width:auto;'></div></div>\"),o=s.children()[0];return t(\"body\").append(s),e=o.offsetWidth,s.css(\"overflow\",\"scroll\"),i=o.offsetWidth,e===i&&(i=s[0].clientWidth),s.remove(),n=e-i},getScrollInfo:function(e){var i=e.isWindow||e.isDocument?\"\":e.element.css(\"overflow-x\"),s=e.isWindow||e.isDocument?\"\":e.element.css(\"overflow-y\"),n=\"scroll\"===i||\"auto\"===i&&e.width<e.element[0].scrollWidth,o=\"scroll\"===s||\"auto\"===s&&e.height<e.element[0].scrollHeight;return{width:o?t.position.scrollbarWidth():0,height:n?t.position.scrollbarWidth():0}},getWithinInfo:function(e){var i=t(e||window),s=t.isWindow(i[0]),n=!!i[0]&&9===i[0].nodeType;return{element:i,isWindow:s,isDocument:n,offset:i.offset()||{left:0,top:0},scrollLeft:i.scrollLeft(),scrollTop:i.scrollTop(),width:s||n?i.width():i.outerWidth(),height:s||n?i.height():i.outerHeight()}}},t.fn.position=function(n){if(!n||!n.of)return f.apply(this,arguments);n=t.extend({},n);var p,g,m,_,v,b,y=t(n.of),w=t.position.getWithinInfo(n.within),k=t.position.getScrollInfo(w),x=(n.collision||\"flip\").split(\" \"),C={};return b=s(y),y[0].preventDefault&&(n.at=\"left top\"),g=b.width,m=b.height,_=b.offset,v=t.extend({},_),t.each([\"my\",\"at\"],function(){var t,e,i=(n[this]||\"\").split(\" \");1===i.length&&(i=l.test(i[0])?i.concat([\"center\"]):c.test(i[0])?[\"center\"].concat(i):[\"center\",\"center\"]),i[0]=l.test(i[0])?i[0]:\"center\",i[1]=c.test(i[1])?i[1]:\"center\",t=u.exec(i[0]),e=u.exec(i[1]),C[this]=[t?t[0]:0,e?e[0]:0],n[this]=[d.exec(i[0])[0],d.exec(i[1])[0]]}),1===x.length&&(x[1]=x[0]),\"right\"===n.at[0]?v.left+=g:\"center\"===n.at[0]&&(v.left+=g/2),\"bottom\"===n.at[1]?v.top+=m:\"center\"===n.at[1]&&(v.top+=m/2),p=e(C.at,g,m),v.left+=p[0],v.top+=p[1],this.each(function(){var s,l,c=t(this),u=c.outerWidth(),d=c.outerHeight(),f=i(this,\"marginLeft\"),b=i(this,\"marginTop\"),D=u+f+i(this,\"marginRight\")+k.width,T=d+b+i(this,\"marginBottom\")+k.height,I=t.extend({},v),P=e(C.my,c.outerWidth(),c.outerHeight());\"right\"===n.my[0]?I.left-=u:\"center\"===n.my[0]&&(I.left-=u/2),\"bottom\"===n.my[1]?I.top-=d:\"center\"===n.my[1]&&(I.top-=d/2),I.left+=P[0],I.top+=P[1],o||(I.left=h(I.left),I.top=h(I.top)),s={marginLeft:f,marginTop:b},t.each([\"left\",\"top\"],function(e,i){t.ui.position[x[e]]&&t.ui.position[x[e]][i](I,{targetWidth:g,targetHeight:m,elemWidth:u,elemHeight:d,collisionPosition:s,collisionWidth:D,collisionHeight:T,offset:[p[0]+P[0],p[1]+P[1]],my:n.my,at:n.at,within:w,elem:c})}),n.using&&(l=function(t){var e=_.left-I.left,i=e+g-u,s=_.top-I.top,o=s+m-d,h={target:{element:y,left:_.left,top:_.top,width:g,height:m},element:{element:c,left:I.left,top:I.top,width:u,height:d},horizontal:0>i?\"left\":e>0?\"right\":\"center\",vertical:0>o?\"top\":s>0?\"bottom\":\"middle\"};u>g&&g>r(e+i)&&(h.horizontal=\"center\"),d>m&&m>r(s+o)&&(h.vertical=\"middle\"),h.important=a(r(e),r(i))>a(r(s),r(o))?\"horizontal\":\"vertical\",n.using.call(this,t,h)}),c.offset(t.extend(I,{using:l}))})},t.ui.position={fit:{left:function(t,e){var i,s=e.within,n=s.isWindow?s.scrollLeft:s.offset.left,o=s.width,r=t.left-e.collisionPosition.marginLeft,h=n-r,l=r+e.collisionWidth-o-n;e.collisionWidth>o?h>0&&0>=l?(i=t.left+h+e.collisionWidth-o-n,t.left+=h-i):t.left=l>0&&0>=h?n:h>l?n+o-e.collisionWidth:n:h>0?t.left+=h:l>0?t.left-=l:t.left=a(t.left-r,t.left)},top:function(t,e){var i,s=e.within,n=s.isWindow?s.scrollTop:s.offset.top,o=e.within.height,r=t.top-e.collisionPosition.marginTop,h=n-r,l=r+e.collisionHeight-o-n;e.collisionHeight>o?h>0&&0>=l?(i=t.top+h+e.collisionHeight-o-n,t.top+=h-i):t.top=l>0&&0>=h?n:h>l?n+o-e.collisionHeight:n:h>0?t.top+=h:l>0?t.top-=l:t.top=a(t.top-r,t.top)}},flip:{left:function(t,e){var i,s,n=e.within,o=n.offset.left+n.scrollLeft,a=n.width,h=n.isWindow?n.scrollLeft:n.offset.left,l=t.left-e.collisionPosition.marginLeft,c=l-h,u=l+e.collisionWidth-a-h,d=\"left\"===e.my[0]?-e.elemWidth:\"right\"===e.my[0]?e.elemWidth:0,p=\"left\"===e.at[0]?e.targetWidth:\"right\"===e.at[0]?-e.targetWidth:0,f=-2*e.offset[0];0>c?(i=t.left+d+p+f+e.collisionWidth-a-o,(0>i||r(c)>i)&&(t.left+=d+p+f)):u>0&&(s=t.left-e.collisionPosition.marginLeft+d+p+f-h,(s>0||u>r(s))&&(t.left+=d+p+f))},top:function(t,e){var i,s,n=e.within,o=n.offset.top+n.scrollTop,a=n.height,h=n.isWindow?n.scrollTop:n.offset.top,l=t.top-e.collisionPosition.marginTop,c=l-h,u=l+e.collisionHeight-a-h,d=\"top\"===e.my[1],p=d?-e.elemHeight:\"bottom\"===e.my[1]?e.elemHeight:0,f=\"top\"===e.at[1]?e.targetHeight:\"bottom\"===e.at[1]?-e.targetHeight:0,g=-2*e.offset[1];0>c?(s=t.top+p+f+g+e.collisionHeight-a-o,(0>s||r(c)>s)&&(t.top+=p+f+g)):u>0&&(i=t.top-e.collisionPosition.marginTop+p+f+g-h,(i>0||u>r(i))&&(t.top+=p+f+g))}},flipfit:{left:function(){t.ui.position.flip.left.apply(this,arguments),t.ui.position.fit.left.apply(this,arguments)},top:function(){t.ui.position.flip.top.apply(this,arguments),t.ui.position.fit.top.apply(this,arguments)}}},function(){var e,i,s,n,a,r=document.getElementsByTagName(\"body\")[0],h=document.createElement(\"div\");e=document.createElement(r?\"div\":\"body\"),s={visibility:\"hidden\",width:0,height:0,border:0,margin:0,background:\"none\"},r&&t.extend(s,{position:\"absolute\",left:\"-1000px\",top:\"-1000px\"});for(a in s)e.style[a]=s[a];e.appendChild(h),i=r||document.documentElement,i.insertBefore(e,i.firstChild),h.style.cssText=\"position: absolute; left: 10.7432222px;\",n=t(h).offset().left,o=n>10&&11>n,e.innerHTML=\"\",i.removeChild(e)}()}(),t.ui.position,t.widget(\"ui.draggable\",t.ui.mouse,{version:\"1.11.4\",widgetEventPrefix:\"drag\",options:{addClasses:!0,appendTo:\"parent\",axis:!1,connectToSortable:!1,containment:!1,cursor:\"auto\",cursorAt:!1,grid:!1,handle:!1,helper:\"original\",iframeFix:!1,opacity:!1,refreshPositions:!1,revert:!1,revertDuration:500,scope:\"default\",scroll:!0,scrollSensitivity:20,scrollSpeed:20,snap:!1,snapMode:\"both\",snapTolerance:20,stack:!1,zIndex:!1,drag:null,start:null,stop:null},_create:function(){\"original\"===this.options.helper&&this._setPositionRelative(),this.options.addClasses&&this.element.addClass(\"ui-draggable\"),this.options.disabled&&this.element.addClass(\"ui-draggable-disabled\"),this._setHandleClassName(),this._mouseInit()},_setOption:function(t,e){this._super(t,e),\"handle\"===t&&(this._removeHandleClassName(),this._setHandleClassName())},_destroy:function(){return(this.helper||this.element).is(\".ui-draggable-dragging\")?(this.destroyOnClear=!0,void 0):(this.element.removeClass(\"ui-draggable ui-draggable-dragging ui-draggable-disabled\"),this._removeHandleClassName(),this._mouseDestroy(),void 0)},_mouseCapture:function(e){var i=this.options;return this._blurActiveElement(e),this.helper||i.disabled||t(e.target).closest(\".ui-resizable-handle\").length>0?!1:(this.handle=this._getHandle(e),this.handle?(this._blockFrames(i.iframeFix===!0?\"iframe\":i.iframeFix),!0):!1)},_blockFrames:function(e){this.iframeBlocks=this.document.find(e).map(function(){var e=t(this);return t(\"<div>\").css(\"position\",\"absolute\").appendTo(e.parent()).outerWidth(e.outerWidth()).outerHeight(e.outerHeight()).offset(e.offset())[0]})},_unblockFrames:function(){this.iframeBlocks&&(this.iframeBlocks.remove(),delete this.iframeBlocks)},_blurActiveElement:function(e){var i=this.document[0];if(this.handleElement.is(e.target))try{i.activeElement&&\"body\"!==i.activeElement.nodeName.toLowerCase()&&t(i.activeElement).blur()}catch(s){}},_mouseStart:function(e){var i=this.options;return this.helper=this._createHelper(e),this.helper.addClass(\"ui-draggable-dragging\"),this._cacheHelperProportions(),t.ui.ddmanager&&(t.ui.ddmanager.current=this),this._cacheMargins(),this.cssPosition=this.helper.css(\"position\"),this.scrollParent=this.helper.scrollParent(!0),this.offsetParent=this.helper.offsetParent(),this.hasFixedAncestor=this.helper.parents().filter(function(){return\"fixed\"===t(this).css(\"position\")}).length>0,this.positionAbs=this.element.offset(),this._refreshOffsets(e),this.originalPosition=this.position=this._generatePosition(e,!1),this.originalPageX=e.pageX,this.originalPageY=e.pageY,i.cursorAt&&this._adjustOffsetFromHelper(i.cursorAt),this._setContainment(),this._trigger(\"start\",e)===!1?(this._clear(),!1):(this._cacheHelperProportions(),t.ui.ddmanager&&!i.dropBehaviour&&t.ui.ddmanager.prepareOffsets(this,e),this._normalizeRightBottom(),this._mouseDrag(e,!0),t.ui.ddmanager&&t.ui.ddmanager.dragStart(this,e),!0)},_refreshOffsets:function(t){this.offset={top:this.positionAbs.top-this.margins.top,left:this.positionAbs.left-this.margins.left,scroll:!1,parent:this._getParentOffset(),relative:this._getRelativeOffset()},this.offset.click={left:t.pageX-this.offset.left,top:t.pageY-this.offset.top}},_mouseDrag:function(e,i){if(this.hasFixedAncestor&&(this.offset.parent=this._getParentOffset()),this.position=this._generatePosition(e,!0),this.positionAbs=this._convertPositionTo(\"absolute\"),!i){var s=this._uiHash();if(this._trigger(\"drag\",e,s)===!1)return this._mouseUp({}),!1;this.position=s.position}return this.helper[0].style.left=this.position.left+\"px\",this.helper[0].style.top=this.position.top+\"px\",t.ui.ddmanager&&t.ui.ddmanager.drag(this,e),!1},_mouseStop:function(e){var i=this,s=!1;return t.ui.ddmanager&&!this.options.dropBehaviour&&(s=t.ui.ddmanager.drop(this,e)),this.dropped&&(s=this.dropped,this.dropped=!1),\"invalid\"===this.options.revert&&!s||\"valid\"===this.options.revert&&s||this.options.revert===!0||t.isFunction(this.options.revert)&&this.options.revert.call(this.element,s)?t(this.helper).animate(this.originalPosition,parseInt(this.options.revertDuration,10),function(){i._trigger(\"stop\",e)!==!1&&i._clear()}):this._trigger(\"stop\",e)!==!1&&this._clear(),!1},_mouseUp:function(e){return this._unblockFrames(),t.ui.ddmanager&&t.ui.ddmanager.dragStop(this,e),this.handleElement.is(e.target)&&this.element.focus(),t.ui.mouse.prototype._mouseUp.call(this,e)},cancel:function(){return this.helper.is(\".ui-draggable-dragging\")?this._mouseUp({}):this._clear(),this},_getHandle:function(e){return this.options.handle?!!t(e.target).closest(this.element.find(this.options.handle)).length:!0},_setHandleClassName:function(){this.handleElement=this.options.handle?this.element.find(this.options.handle):this.element,this.handleElement.addClass(\"ui-draggable-handle\")},_removeHandleClassName:function(){this.handleElement.removeClass(\"ui-draggable-handle\")},_createHelper:function(e){var i=this.options,s=t.isFunction(i.helper),n=s?t(i.helper.apply(this.element[0],[e])):\"clone\"===i.helper?this.element.clone().removeAttr(\"id\"):this.element;return n.parents(\"body\").length||n.appendTo(\"parent\"===i.appendTo?this.element[0].parentNode:i.appendTo),s&&n[0]===this.element[0]&&this._setPositionRelative(),n[0]===this.element[0]||/(fixed|absolute)/.test(n.css(\"position\"))||n.css(\"position\",\"absolute\"),n},_setPositionRelative:function(){/^(?:r|a|f)/.test(this.element.css(\"position\"))||(this.element[0].style.position=\"relative\")},_adjustOffsetFromHelper:function(e){\"string\"==typeof e&&(e=e.split(\" \")),t.isArray(e)&&(e={left:+e[0],top:+e[1]||0}),\"left\"in e&&(this.offset.click.left=e.left+this.margins.left),\"right\"in e&&(this.offset.click.left=this.helperProportions.width-e.right+this.margins.left),\"top\"in e&&(this.offset.click.top=e.top+this.margins.top),\"bottom\"in e&&(this.offset.click.top=this.helperProportions.height-e.bottom+this.margins.top)},_isRootNode:function(t){return/(html|body)/i.test(t.tagName)||t===this.document[0]},_getParentOffset:function(){var e=this.offsetParent.offset(),i=this.document[0];return\"absolute\"===this.cssPosition&&this.scrollParent[0]!==i&&t.contains(this.scrollParent[0],this.offsetParent[0])&&(e.left+=this.scrollParent.scrollLeft(),e.top+=this.scrollParent.scrollTop()),this._isRootNode(this.offsetParent[0])&&(e={top:0,left:0}),{top:e.top+(parseInt(this.offsetParent.css(\"borderTopWidth\"),10)||0),left:e.left+(parseInt(this.offsetParent.css(\"borderLeftWidth\"),10)||0)}},_getRelativeOffset:function(){if(\"relative\"!==this.cssPosition)return{top:0,left:0};var t=this.element.position(),e=this._isRootNode(this.scrollParent[0]);return{top:t.top-(parseInt(this.helper.css(\"top\"),10)||0)+(e?0:this.scrollParent.scrollTop()),left:t.left-(parseInt(this.helper.css(\"left\"),10)||0)+(e?0:this.scrollParent.scrollLeft())}},_cacheMargins:function(){this.margins={left:parseInt(this.element.css(\"marginLeft\"),10)||0,top:parseInt(this.element.css(\"marginTop\"),10)||0,right:parseInt(this.element.css(\"marginRight\"),10)||0,bottom:parseInt(this.element.css(\"marginBottom\"),10)||0}},_cacheHelperProportions:function(){this.helperProportions={width:this.helper.outerWidth(),height:this.helper.outerHeight()}},_setContainment:function(){var e,i,s,n=this.options,o=this.document[0];return this.relativeContainer=null,n.containment?\"window\"===n.containment?(this.containment=[t(window).scrollLeft()-this.offset.relative.left-this.offset.parent.left,t(window).scrollTop()-this.offset.relative.top-this.offset.parent.top,t(window).scrollLeft()+t(window).width()-this.helperProportions.width-this.margins.left,t(window).scrollTop()+(t(window).height()||o.body.parentNode.scrollHeight)-this.helperProportions.height-this.margins.top],void 0):\"document\"===n.containment?(this.containment=[0,0,t(o).width()-this.helperProportions.width-this.margins.left,(t(o).height()||o.body.parentNode.scrollHeight)-this.helperProportions.height-this.margins.top],void 0):n.containment.constructor===Array?(this.containment=n.containment,void 0):(\"parent\"===n.containment&&(n.containment=this.helper[0].parentNode),i=t(n.containment),s=i[0],s&&(e=/(scroll|auto)/.test(i.css(\"overflow\")),this.containment=[(parseInt(i.css(\"borderLeftWidth\"),10)||0)+(parseInt(i.css(\"paddingLeft\"),10)||0),(parseInt(i.css(\"borderTopWidth\"),10)||0)+(parseInt(i.css(\"paddingTop\"),10)||0),(e?Math.max(s.scrollWidth,s.offsetWidth):s.offsetWidth)-(parseInt(i.css(\"borderRightWidth\"),10)||0)-(parseInt(i.css(\"paddingRight\"),10)||0)-this.helperProportions.width-this.margins.left-this.margins.right,(e?Math.max(s.scrollHeight,s.offsetHeight):s.offsetHeight)-(parseInt(i.css(\"borderBottomWidth\"),10)||0)-(parseInt(i.css(\"paddingBottom\"),10)||0)-this.helperProportions.height-this.margins.top-this.margins.bottom],this.relativeContainer=i),void 0):(this.containment=null,void 0)},_convertPositionTo:function(t,e){e||(e=this.position);var i=\"absolute\"===t?1:-1,s=this._isRootNode(this.scrollParent[0]);return{top:e.top+this.offset.relative.top*i+this.offset.parent.top*i-(\"fixed\"===this.cssPosition?-this.offset.scroll.top:s?0:this.offset.scroll.top)*i,left:e.left+this.offset.relative.left*i+this.offset.parent.left*i-(\"fixed\"===this.cssPosition?-this.offset.scroll.left:s?0:this.offset.scroll.left)*i}},_generatePosition:function(t,e){var i,s,n,o,a=this.options,r=this._isRootNode(this.scrollParent[0]),h=t.pageX,l=t.pageY;return r&&this.offset.scroll||(this.offset.scroll={top:this.scrollParent.scrollTop(),left:this.scrollParent.scrollLeft()}),e&&(this.containment&&(this.relativeContainer?(s=this.relativeContainer.offset(),i=[this.containment[0]+s.left,this.containment[1]+s.top,this.containment[2]+s.left,this.containment[3]+s.top]):i=this.containment,t.pageX-this.offset.click.left<i[0]&&(h=i[0]+this.offset.click.left),t.pageY-this.offset.click.top<i[1]&&(l=i[1]+this.offset.click.top),t.pageX-this.offset.click.left>i[2]&&(h=i[2]+this.offset.click.left),t.pageY-this.offset.click.top>i[3]&&(l=i[3]+this.offset.click.top)),a.grid&&(n=a.grid[1]?this.originalPageY+Math.round((l-this.originalPageY)/a.grid[1])*a.grid[1]:this.originalPageY,l=i?n-this.offset.click.top>=i[1]||n-this.offset.click.top>i[3]?n:n-this.offset.click.top>=i[1]?n-a.grid[1]:n+a.grid[1]:n,o=a.grid[0]?this.originalPageX+Math.round((h-this.originalPageX)/a.grid[0])*a.grid[0]:this.originalPageX,h=i?o-this.offset.click.left>=i[0]||o-this.offset.click.left>i[2]?o:o-this.offset.click.left>=i[0]?o-a.grid[0]:o+a.grid[0]:o),\"y\"===a.axis&&(h=this.originalPageX),\"x\"===a.axis&&(l=this.originalPageY)),{top:l-this.offset.click.top-this.offset.relative.top-this.offset.parent.top+(\"fixed\"===this.cssPosition?-this.offset.scroll.top:r?0:this.offset.scroll.top),left:h-this.offset.click.left-this.offset.relative.left-this.offset.parent.left+(\"fixed\"===this.cssPosition?-this.offset.scroll.left:r?0:this.offset.scroll.left)}},_clear:function(){this.helper.removeClass(\"ui-draggable-dragging\"),this.helper[0]===this.element[0]||this.cancelHelperRemoval||this.helper.remove(),this.helper=null,this.cancelHelperRemoval=!1,this.destroyOnClear&&this.destroy()},_normalizeRightBottom:function(){\"y\"!==this.options.axis&&\"auto\"!==this.helper.css(\"right\")&&(this.helper.width(this.helper.width()),this.helper.css(\"right\",\"auto\")),\"x\"!==this.options.axis&&\"auto\"!==this.helper.css(\"bottom\")&&(this.helper.height(this.helper.height()),this.helper.css(\"bottom\",\"auto\"))},_trigger:function(e,i,s){return s=s||this._uiHash(),t.ui.plugin.call(this,e,[i,s,this],!0),/^(drag|start|stop)/.test(e)&&(this.positionAbs=this._convertPositionTo(\"absolute\"),s.offset=this.positionAbs),t.Widget.prototype._trigger.call(this,e,i,s)},plugins:{},_uiHash:function(){return{helper:this.helper,position:this.position,originalPosition:this.originalPosition,offset:this.positionAbs}}}),t.ui.plugin.add(\"draggable\",\"connectToSortable\",{start:function(e,i,s){var n=t.extend({},i,{item:s.element});s.sortables=[],t(s.options.connectToSortable).each(function(){var i=t(this).sortable(\"instance\");i&&!i.options.disabled&&(s.sortables.push(i),i.refreshPositions(),i._trigger(\"activate\",e,n))})},stop:function(e,i,s){var n=t.extend({},i,{item:s.element});s.cancelHelperRemoval=!1,t.each(s.sortables,function(){var t=this;t.isOver?(t.isOver=0,s.cancelHelperRemoval=!0,t.cancelHelperRemoval=!1,t._storedCSS={position:t.placeholder.css(\"position\"),top:t.placeholder.css(\"top\"),left:t.placeholder.css(\"left\")},t._mouseStop(e),t.options.helper=t.options._helper):(t.cancelHelperRemoval=!0,t._trigger(\"deactivate\",e,n))})},drag:function(e,i,s){t.each(s.sortables,function(){var n=!1,o=this;o.positionAbs=s.positionAbs,o.helperProportions=s.helperProportions,o.offset.click=s.offset.click,o._intersectsWith(o.containerCache)&&(n=!0,t.each(s.sortables,function(){return this.positionAbs=s.positionAbs,this.helperProportions=s.helperProportions,this.offset.click=s.offset.click,this!==o&&this._intersectsWith(this.containerCache)&&t.contains(o.element[0],this.element[0])&&(n=!1),n\n})),n?(o.isOver||(o.isOver=1,s._parent=i.helper.parent(),o.currentItem=i.helper.appendTo(o.element).data(\"ui-sortable-item\",!0),o.options._helper=o.options.helper,o.options.helper=function(){return i.helper[0]},e.target=o.currentItem[0],o._mouseCapture(e,!0),o._mouseStart(e,!0,!0),o.offset.click.top=s.offset.click.top,o.offset.click.left=s.offset.click.left,o.offset.parent.left-=s.offset.parent.left-o.offset.parent.left,o.offset.parent.top-=s.offset.parent.top-o.offset.parent.top,s._trigger(\"toSortable\",e),s.dropped=o.element,t.each(s.sortables,function(){this.refreshPositions()}),s.currentItem=s.element,o.fromOutside=s),o.currentItem&&(o._mouseDrag(e),i.position=o.position)):o.isOver&&(o.isOver=0,o.cancelHelperRemoval=!0,o.options._revert=o.options.revert,o.options.revert=!1,o._trigger(\"out\",e,o._uiHash(o)),o._mouseStop(e,!0),o.options.revert=o.options._revert,o.options.helper=o.options._helper,o.placeholder&&o.placeholder.remove(),i.helper.appendTo(s._parent),s._refreshOffsets(e),i.position=s._generatePosition(e,!0),s._trigger(\"fromSortable\",e),s.dropped=!1,t.each(s.sortables,function(){this.refreshPositions()}))})}}),t.ui.plugin.add(\"draggable\",\"cursor\",{start:function(e,i,s){var n=t(\"body\"),o=s.options;n.css(\"cursor\")&&(o._cursor=n.css(\"cursor\")),n.css(\"cursor\",o.cursor)},stop:function(e,i,s){var n=s.options;n._cursor&&t(\"body\").css(\"cursor\",n._cursor)}}),t.ui.plugin.add(\"draggable\",\"opacity\",{start:function(e,i,s){var n=t(i.helper),o=s.options;n.css(\"opacity\")&&(o._opacity=n.css(\"opacity\")),n.css(\"opacity\",o.opacity)},stop:function(e,i,s){var n=s.options;n._opacity&&t(i.helper).css(\"opacity\",n._opacity)}}),t.ui.plugin.add(\"draggable\",\"scroll\",{start:function(t,e,i){i.scrollParentNotHidden||(i.scrollParentNotHidden=i.helper.scrollParent(!1)),i.scrollParentNotHidden[0]!==i.document[0]&&\"HTML\"!==i.scrollParentNotHidden[0].tagName&&(i.overflowOffset=i.scrollParentNotHidden.offset())},drag:function(e,i,s){var n=s.options,o=!1,a=s.scrollParentNotHidden[0],r=s.document[0];a!==r&&\"HTML\"!==a.tagName?(n.axis&&\"x\"===n.axis||(s.overflowOffset.top+a.offsetHeight-e.pageY<n.scrollSensitivity?a.scrollTop=o=a.scrollTop+n.scrollSpeed:e.pageY-s.overflowOffset.top<n.scrollSensitivity&&(a.scrollTop=o=a.scrollTop-n.scrollSpeed)),n.axis&&\"y\"===n.axis||(s.overflowOffset.left+a.offsetWidth-e.pageX<n.scrollSensitivity?a.scrollLeft=o=a.scrollLeft+n.scrollSpeed:e.pageX-s.overflowOffset.left<n.scrollSensitivity&&(a.scrollLeft=o=a.scrollLeft-n.scrollSpeed))):(n.axis&&\"x\"===n.axis||(e.pageY-t(r).scrollTop()<n.scrollSensitivity?o=t(r).scrollTop(t(r).scrollTop()-n.scrollSpeed):t(window).height()-(e.pageY-t(r).scrollTop())<n.scrollSensitivity&&(o=t(r).scrollTop(t(r).scrollTop()+n.scrollSpeed))),n.axis&&\"y\"===n.axis||(e.pageX-t(r).scrollLeft()<n.scrollSensitivity?o=t(r).scrollLeft(t(r).scrollLeft()-n.scrollSpeed):t(window).width()-(e.pageX-t(r).scrollLeft())<n.scrollSensitivity&&(o=t(r).scrollLeft(t(r).scrollLeft()+n.scrollSpeed)))),o!==!1&&t.ui.ddmanager&&!n.dropBehaviour&&t.ui.ddmanager.prepareOffsets(s,e)}}),t.ui.plugin.add(\"draggable\",\"snap\",{start:function(e,i,s){var n=s.options;s.snapElements=[],t(n.snap.constructor!==String?n.snap.items||\":data(ui-draggable)\":n.snap).each(function(){var e=t(this),i=e.offset();this!==s.element[0]&&s.snapElements.push({item:this,width:e.outerWidth(),height:e.outerHeight(),top:i.top,left:i.left})})},drag:function(e,i,s){var n,o,a,r,h,l,c,u,d,p,f=s.options,g=f.snapTolerance,m=i.offset.left,_=m+s.helperProportions.width,v=i.offset.top,b=v+s.helperProportions.height;for(d=s.snapElements.length-1;d>=0;d--)h=s.snapElements[d].left-s.margins.left,l=h+s.snapElements[d].width,c=s.snapElements[d].top-s.margins.top,u=c+s.snapElements[d].height,h-g>_||m>l+g||c-g>b||v>u+g||!t.contains(s.snapElements[d].item.ownerDocument,s.snapElements[d].item)?(s.snapElements[d].snapping&&s.options.snap.release&&s.options.snap.release.call(s.element,e,t.extend(s._uiHash(),{snapItem:s.snapElements[d].item})),s.snapElements[d].snapping=!1):(\"inner\"!==f.snapMode&&(n=g>=Math.abs(c-b),o=g>=Math.abs(u-v),a=g>=Math.abs(h-_),r=g>=Math.abs(l-m),n&&(i.position.top=s._convertPositionTo(\"relative\",{top:c-s.helperProportions.height,left:0}).top),o&&(i.position.top=s._convertPositionTo(\"relative\",{top:u,left:0}).top),a&&(i.position.left=s._convertPositionTo(\"relative\",{top:0,left:h-s.helperProportions.width}).left),r&&(i.position.left=s._convertPositionTo(\"relative\",{top:0,left:l}).left)),p=n||o||a||r,\"outer\"!==f.snapMode&&(n=g>=Math.abs(c-v),o=g>=Math.abs(u-b),a=g>=Math.abs(h-m),r=g>=Math.abs(l-_),n&&(i.position.top=s._convertPositionTo(\"relative\",{top:c,left:0}).top),o&&(i.position.top=s._convertPositionTo(\"relative\",{top:u-s.helperProportions.height,left:0}).top),a&&(i.position.left=s._convertPositionTo(\"relative\",{top:0,left:h}).left),r&&(i.position.left=s._convertPositionTo(\"relative\",{top:0,left:l-s.helperProportions.width}).left)),!s.snapElements[d].snapping&&(n||o||a||r||p)&&s.options.snap.snap&&s.options.snap.snap.call(s.element,e,t.extend(s._uiHash(),{snapItem:s.snapElements[d].item})),s.snapElements[d].snapping=n||o||a||r||p)}}),t.ui.plugin.add(\"draggable\",\"stack\",{start:function(e,i,s){var n,o=s.options,a=t.makeArray(t(o.stack)).sort(function(e,i){return(parseInt(t(e).css(\"zIndex\"),10)||0)-(parseInt(t(i).css(\"zIndex\"),10)||0)});a.length&&(n=parseInt(t(a[0]).css(\"zIndex\"),10)||0,t(a).each(function(e){t(this).css(\"zIndex\",n+e)}),this.css(\"zIndex\",n+a.length))}}),t.ui.plugin.add(\"draggable\",\"zIndex\",{start:function(e,i,s){var n=t(i.helper),o=s.options;n.css(\"zIndex\")&&(o._zIndex=n.css(\"zIndex\")),n.css(\"zIndex\",o.zIndex)},stop:function(e,i,s){var n=s.options;n._zIndex&&t(i.helper).css(\"zIndex\",n._zIndex)}}),t.ui.draggable,t.widget(\"ui.resizable\",t.ui.mouse,{version:\"1.11.4\",widgetEventPrefix:\"resize\",options:{alsoResize:!1,animate:!1,animateDuration:\"slow\",animateEasing:\"swing\",aspectRatio:!1,autoHide:!1,containment:!1,ghost:!1,grid:!1,handles:\"e,s,se\",helper:!1,maxHeight:null,maxWidth:null,minHeight:10,minWidth:10,zIndex:90,resize:null,start:null,stop:null},_num:function(t){return parseInt(t,10)||0},_isNumber:function(t){return!isNaN(parseInt(t,10))},_hasScroll:function(e,i){if(\"hidden\"===t(e).css(\"overflow\"))return!1;var s=i&&\"left\"===i?\"scrollLeft\":\"scrollTop\",n=!1;return e[s]>0?!0:(e[s]=1,n=e[s]>0,e[s]=0,n)},_create:function(){var e,i,s,n,o,a=this,r=this.options;if(this.element.addClass(\"ui-resizable\"),t.extend(this,{_aspectRatio:!!r.aspectRatio,aspectRatio:r.aspectRatio,originalElement:this.element,_proportionallyResizeElements:[],_helper:r.helper||r.ghost||r.animate?r.helper||\"ui-resizable-helper\":null}),this.element[0].nodeName.match(/^(canvas|textarea|input|select|button|img)$/i)&&(this.element.wrap(t(\"<div class='ui-wrapper' style='overflow: hidden;'></div>\").css({position:this.element.css(\"position\"),width:this.element.outerWidth(),height:this.element.outerHeight(),top:this.element.css(\"top\"),left:this.element.css(\"left\")})),this.element=this.element.parent().data(\"ui-resizable\",this.element.resizable(\"instance\")),this.elementIsWrapper=!0,this.element.css({marginLeft:this.originalElement.css(\"marginLeft\"),marginTop:this.originalElement.css(\"marginTop\"),marginRight:this.originalElement.css(\"marginRight\"),marginBottom:this.originalElement.css(\"marginBottom\")}),this.originalElement.css({marginLeft:0,marginTop:0,marginRight:0,marginBottom:0}),this.originalResizeStyle=this.originalElement.css(\"resize\"),this.originalElement.css(\"resize\",\"none\"),this._proportionallyResizeElements.push(this.originalElement.css({position:\"static\",zoom:1,display:\"block\"})),this.originalElement.css({margin:this.originalElement.css(\"margin\")}),this._proportionallyResize()),this.handles=r.handles||(t(\".ui-resizable-handle\",this.element).length?{n:\".ui-resizable-n\",e:\".ui-resizable-e\",s:\".ui-resizable-s\",w:\".ui-resizable-w\",se:\".ui-resizable-se\",sw:\".ui-resizable-sw\",ne:\".ui-resizable-ne\",nw:\".ui-resizable-nw\"}:\"e,s,se\"),this._handles=t(),this.handles.constructor===String)for(\"all\"===this.handles&&(this.handles=\"n,e,s,w,se,sw,ne,nw\"),e=this.handles.split(\",\"),this.handles={},i=0;e.length>i;i++)s=t.trim(e[i]),o=\"ui-resizable-\"+s,n=t(\"<div class='ui-resizable-handle \"+o+\"'></div>\"),n.css({zIndex:r.zIndex}),\"se\"===s&&n.addClass(\"ui-icon ui-icon-gripsmall-diagonal-se\"),this.handles[s]=\".ui-resizable-\"+s,this.element.append(n);this._renderAxis=function(e){var i,s,n,o;e=e||this.element;for(i in this.handles)this.handles[i].constructor===String?this.handles[i]=this.element.children(this.handles[i]).first().show():(this.handles[i].jquery||this.handles[i].nodeType)&&(this.handles[i]=t(this.handles[i]),this._on(this.handles[i],{mousedown:a._mouseDown})),this.elementIsWrapper&&this.originalElement[0].nodeName.match(/^(textarea|input|select|button)$/i)&&(s=t(this.handles[i],this.element),o=/sw|ne|nw|se|n|s/.test(i)?s.outerHeight():s.outerWidth(),n=[\"padding\",/ne|nw|n/.test(i)?\"Top\":/se|sw|s/.test(i)?\"Bottom\":/^e$/.test(i)?\"Right\":\"Left\"].join(\"\"),e.css(n,o),this._proportionallyResize()),this._handles=this._handles.add(this.handles[i])},this._renderAxis(this.element),this._handles=this._handles.add(this.element.find(\".ui-resizable-handle\")),this._handles.disableSelection(),this._handles.mouseover(function(){a.resizing||(this.className&&(n=this.className.match(/ui-resizable-(se|sw|ne|nw|n|e|s|w)/i)),a.axis=n&&n[1]?n[1]:\"se\")}),r.autoHide&&(this._handles.hide(),t(this.element).addClass(\"ui-resizable-autohide\").mouseenter(function(){r.disabled||(t(this).removeClass(\"ui-resizable-autohide\"),a._handles.show())}).mouseleave(function(){r.disabled||a.resizing||(t(this).addClass(\"ui-resizable-autohide\"),a._handles.hide())})),this._mouseInit()},_destroy:function(){this._mouseDestroy();var e,i=function(e){t(e).removeClass(\"ui-resizable ui-resizable-disabled ui-resizable-resizing\").removeData(\"resizable\").removeData(\"ui-resizable\").unbind(\".resizable\").find(\".ui-resizable-handle\").remove()};return this.elementIsWrapper&&(i(this.element),e=this.element,this.originalElement.css({position:e.css(\"position\"),width:e.outerWidth(),height:e.outerHeight(),top:e.css(\"top\"),left:e.css(\"left\")}).insertAfter(e),e.remove()),this.originalElement.css(\"resize\",this.originalResizeStyle),i(this.originalElement),this},_mouseCapture:function(e){var i,s,n=!1;for(i in this.handles)s=t(this.handles[i])[0],(s===e.target||t.contains(s,e.target))&&(n=!0);return!this.options.disabled&&n},_mouseStart:function(e){var i,s,n,o=this.options,a=this.element;return this.resizing=!0,this._renderProxy(),i=this._num(this.helper.css(\"left\")),s=this._num(this.helper.css(\"top\")),o.containment&&(i+=t(o.containment).scrollLeft()||0,s+=t(o.containment).scrollTop()||0),this.offset=this.helper.offset(),this.position={left:i,top:s},this.size=this._helper?{width:this.helper.width(),height:this.helper.height()}:{width:a.width(),height:a.height()},this.originalSize=this._helper?{width:a.outerWidth(),height:a.outerHeight()}:{width:a.width(),height:a.height()},this.sizeDiff={width:a.outerWidth()-a.width(),height:a.outerHeight()-a.height()},this.originalPosition={left:i,top:s},this.originalMousePosition={left:e.pageX,top:e.pageY},this.aspectRatio=\"number\"==typeof o.aspectRatio?o.aspectRatio:this.originalSize.width/this.originalSize.height||1,n=t(\".ui-resizable-\"+this.axis).css(\"cursor\"),t(\"body\").css(\"cursor\",\"auto\"===n?this.axis+\"-resize\":n),a.addClass(\"ui-resizable-resizing\"),this._propagate(\"start\",e),!0},_mouseDrag:function(e){var i,s,n=this.originalMousePosition,o=this.axis,a=e.pageX-n.left||0,r=e.pageY-n.top||0,h=this._change[o];return this._updatePrevProperties(),h?(i=h.apply(this,[e,a,r]),this._updateVirtualBoundaries(e.shiftKey),(this._aspectRatio||e.shiftKey)&&(i=this._updateRatio(i,e)),i=this._respectSize(i,e),this._updateCache(i),this._propagate(\"resize\",e),s=this._applyChanges(),!this._helper&&this._proportionallyResizeElements.length&&this._proportionallyResize(),t.isEmptyObject(s)||(this._updatePrevProperties(),this._trigger(\"resize\",e,this.ui()),this._applyChanges()),!1):!1},_mouseStop:function(e){this.resizing=!1;var i,s,n,o,a,r,h,l=this.options,c=this;return this._helper&&(i=this._proportionallyResizeElements,s=i.length&&/textarea/i.test(i[0].nodeName),n=s&&this._hasScroll(i[0],\"left\")?0:c.sizeDiff.height,o=s?0:c.sizeDiff.width,a={width:c.helper.width()-o,height:c.helper.height()-n},r=parseInt(c.element.css(\"left\"),10)+(c.position.left-c.originalPosition.left)||null,h=parseInt(c.element.css(\"top\"),10)+(c.position.top-c.originalPosition.top)||null,l.animate||this.element.css(t.extend(a,{top:h,left:r})),c.helper.height(c.size.height),c.helper.width(c.size.width),this._helper&&!l.animate&&this._proportionallyResize()),t(\"body\").css(\"cursor\",\"auto\"),this.element.removeClass(\"ui-resizable-resizing\"),this._propagate(\"stop\",e),this._helper&&this.helper.remove(),!1},_updatePrevProperties:function(){this.prevPosition={top:this.position.top,left:this.position.left},this.prevSize={width:this.size.width,height:this.size.height}},_applyChanges:function(){var t={};return this.position.top!==this.prevPosition.top&&(t.top=this.position.top+\"px\"),this.position.left!==this.prevPosition.left&&(t.left=this.position.left+\"px\"),this.size.width!==this.prevSize.width&&(t.width=this.size.width+\"px\"),this.size.height!==this.prevSize.height&&(t.height=this.size.height+\"px\"),this.helper.css(t),t},_updateVirtualBoundaries:function(t){var e,i,s,n,o,a=this.options;o={minWidth:this._isNumber(a.minWidth)?a.minWidth:0,maxWidth:this._isNumber(a.maxWidth)?a.maxWidth:1/0,minHeight:this._isNumber(a.minHeight)?a.minHeight:0,maxHeight:this._isNumber(a.maxHeight)?a.maxHeight:1/0},(this._aspectRatio||t)&&(e=o.minHeight*this.aspectRatio,s=o.minWidth/this.aspectRatio,i=o.maxHeight*this.aspectRatio,n=o.maxWidth/this.aspectRatio,e>o.minWidth&&(o.minWidth=e),s>o.minHeight&&(o.minHeight=s),o.maxWidth>i&&(o.maxWidth=i),o.maxHeight>n&&(o.maxHeight=n)),this._vBoundaries=o},_updateCache:function(t){this.offset=this.helper.offset(),this._isNumber(t.left)&&(this.position.left=t.left),this._isNumber(t.top)&&(this.position.top=t.top),this._isNumber(t.height)&&(this.size.height=t.height),this._isNumber(t.width)&&(this.size.width=t.width)},_updateRatio:function(t){var e=this.position,i=this.size,s=this.axis;return this._isNumber(t.height)?t.width=t.height*this.aspectRatio:this._isNumber(t.width)&&(t.height=t.width/this.aspectRatio),\"sw\"===s&&(t.left=e.left+(i.width-t.width),t.top=null),\"nw\"===s&&(t.top=e.top+(i.height-t.height),t.left=e.left+(i.width-t.width)),t},_respectSize:function(t){var e=this._vBoundaries,i=this.axis,s=this._isNumber(t.width)&&e.maxWidth&&e.maxWidth<t.width,n=this._isNumber(t.height)&&e.maxHeight&&e.maxHeight<t.height,o=this._isNumber(t.width)&&e.minWidth&&e.minWidth>t.width,a=this._isNumber(t.height)&&e.minHeight&&e.minHeight>t.height,r=this.originalPosition.left+this.originalSize.width,h=this.position.top+this.size.height,l=/sw|nw|w/.test(i),c=/nw|ne|n/.test(i);return o&&(t.width=e.minWidth),a&&(t.height=e.minHeight),s&&(t.width=e.maxWidth),n&&(t.height=e.maxHeight),o&&l&&(t.left=r-e.minWidth),s&&l&&(t.left=r-e.maxWidth),a&&c&&(t.top=h-e.minHeight),n&&c&&(t.top=h-e.maxHeight),t.width||t.height||t.left||!t.top?t.width||t.height||t.top||!t.left||(t.left=null):t.top=null,t},_getPaddingPlusBorderDimensions:function(t){for(var e=0,i=[],s=[t.css(\"borderTopWidth\"),t.css(\"borderRightWidth\"),t.css(\"borderBottomWidth\"),t.css(\"borderLeftWidth\")],n=[t.css(\"paddingTop\"),t.css(\"paddingRight\"),t.css(\"paddingBottom\"),t.css(\"paddingLeft\")];4>e;e++)i[e]=parseInt(s[e],10)||0,i[e]+=parseInt(n[e],10)||0;return{height:i[0]+i[2],width:i[1]+i[3]}},_proportionallyResize:function(){if(this._proportionallyResizeElements.length)for(var t,e=0,i=this.helper||this.element;this._proportionallyResizeElements.length>e;e++)t=this._proportionallyResizeElements[e],this.outerDimensions||(this.outerDimensions=this._getPaddingPlusBorderDimensions(t)),t.css({height:i.height()-this.outerDimensions.height||0,width:i.width()-this.outerDimensions.width||0})},_renderProxy:function(){var e=this.element,i=this.options;this.elementOffset=e.offset(),this._helper?(this.helper=this.helper||t(\"<div style='overflow:hidden;'></div>\"),this.helper.addClass(this._helper).css({width:this.element.outerWidth()-1,height:this.element.outerHeight()-1,position:\"absolute\",left:this.elementOffset.left+\"px\",top:this.elementOffset.top+\"px\",zIndex:++i.zIndex}),this.helper.appendTo(\"body\").disableSelection()):this.helper=this.element},_change:{e:function(t,e){return{width:this.originalSize.width+e}},w:function(t,e){var i=this.originalSize,s=this.originalPosition;return{left:s.left+e,width:i.width-e}},n:function(t,e,i){var s=this.originalSize,n=this.originalPosition;return{top:n.top+i,height:s.height-i}},s:function(t,e,i){return{height:this.originalSize.height+i}},se:function(e,i,s){return t.extend(this._change.s.apply(this,arguments),this._change.e.apply(this,[e,i,s]))},sw:function(e,i,s){return t.extend(this._change.s.apply(this,arguments),this._change.w.apply(this,[e,i,s]))},ne:function(e,i,s){return t.extend(this._change.n.apply(this,arguments),this._change.e.apply(this,[e,i,s]))},nw:function(e,i,s){return t.extend(this._change.n.apply(this,arguments),this._change.w.apply(this,[e,i,s]))}},_propagate:function(e,i){t.ui.plugin.call(this,e,[i,this.ui()]),\"resize\"!==e&&this._trigger(e,i,this.ui())},plugins:{},ui:function(){return{originalElement:this.originalElement,element:this.element,helper:this.helper,position:this.position,size:this.size,originalSize:this.originalSize,originalPosition:this.originalPosition}}}),t.ui.plugin.add(\"resizable\",\"animate\",{stop:function(e){var i=t(this).resizable(\"instance\"),s=i.options,n=i._proportionallyResizeElements,o=n.length&&/textarea/i.test(n[0].nodeName),a=o&&i._hasScroll(n[0],\"left\")?0:i.sizeDiff.height,r=o?0:i.sizeDiff.width,h={width:i.size.width-r,height:i.size.height-a},l=parseInt(i.element.css(\"left\"),10)+(i.position.left-i.originalPosition.left)||null,c=parseInt(i.element.css(\"top\"),10)+(i.position.top-i.originalPosition.top)||null;i.element.animate(t.extend(h,c&&l?{top:c,left:l}:{}),{duration:s.animateDuration,easing:s.animateEasing,step:function(){var s={width:parseInt(i.element.css(\"width\"),10),height:parseInt(i.element.css(\"height\"),10),top:parseInt(i.element.css(\"top\"),10),left:parseInt(i.element.css(\"left\"),10)};n&&n.length&&t(n[0]).css({width:s.width,height:s.height}),i._updateCache(s),i._propagate(\"resize\",e)}})}}),t.ui.plugin.add(\"resizable\",\"containment\",{start:function(){var e,i,s,n,o,a,r,h=t(this).resizable(\"instance\"),l=h.options,c=h.element,u=l.containment,d=u instanceof t?u.get(0):/parent/.test(u)?c.parent().get(0):u;d&&(h.containerElement=t(d),/document/.test(u)||u===document?(h.containerOffset={left:0,top:0},h.containerPosition={left:0,top:0},h.parentData={element:t(document),left:0,top:0,width:t(document).width(),height:t(document).height()||document.body.parentNode.scrollHeight}):(e=t(d),i=[],t([\"Top\",\"Right\",\"Left\",\"Bottom\"]).each(function(t,s){i[t]=h._num(e.css(\"padding\"+s))}),h.containerOffset=e.offset(),h.containerPosition=e.position(),h.containerSize={height:e.innerHeight()-i[3],width:e.innerWidth()-i[1]},s=h.containerOffset,n=h.containerSize.height,o=h.containerSize.width,a=h._hasScroll(d,\"left\")?d.scrollWidth:o,r=h._hasScroll(d)?d.scrollHeight:n,h.parentData={element:d,left:s.left,top:s.top,width:a,height:r}))},resize:function(e){var i,s,n,o,a=t(this).resizable(\"instance\"),r=a.options,h=a.containerOffset,l=a.position,c=a._aspectRatio||e.shiftKey,u={top:0,left:0},d=a.containerElement,p=!0;d[0]!==document&&/static/.test(d.css(\"position\"))&&(u=h),l.left<(a._helper?h.left:0)&&(a.size.width=a.size.width+(a._helper?a.position.left-h.left:a.position.left-u.left),c&&(a.size.height=a.size.width/a.aspectRatio,p=!1),a.position.left=r.helper?h.left:0),l.top<(a._helper?h.top:0)&&(a.size.height=a.size.height+(a._helper?a.position.top-h.top:a.position.top),c&&(a.size.width=a.size.height*a.aspectRatio,p=!1),a.position.top=a._helper?h.top:0),n=a.containerElement.get(0)===a.element.parent().get(0),o=/relative|absolute/.test(a.containerElement.css(\"position\")),n&&o?(a.offset.left=a.parentData.left+a.position.left,a.offset.top=a.parentData.top+a.position.top):(a.offset.left=a.element.offset().left,a.offset.top=a.element.offset().top),i=Math.abs(a.sizeDiff.width+(a._helper?a.offset.left-u.left:a.offset.left-h.left)),s=Math.abs(a.sizeDiff.height+(a._helper?a.offset.top-u.top:a.offset.top-h.top)),i+a.size.width>=a.parentData.width&&(a.size.width=a.parentData.width-i,c&&(a.size.height=a.size.width/a.aspectRatio,p=!1)),s+a.size.height>=a.parentData.height&&(a.size.height=a.parentData.height-s,c&&(a.size.width=a.size.height*a.aspectRatio,p=!1)),p||(a.position.left=a.prevPosition.left,a.position.top=a.prevPosition.top,a.size.width=a.prevSize.width,a.size.height=a.prevSize.height)},stop:function(){var e=t(this).resizable(\"instance\"),i=e.options,s=e.containerOffset,n=e.containerPosition,o=e.containerElement,a=t(e.helper),r=a.offset(),h=a.outerWidth()-e.sizeDiff.width,l=a.outerHeight()-e.sizeDiff.height;e._helper&&!i.animate&&/relative/.test(o.css(\"position\"))&&t(this).css({left:r.left-n.left-s.left,width:h,height:l}),e._helper&&!i.animate&&/static/.test(o.css(\"position\"))&&t(this).css({left:r.left-n.left-s.left,width:h,height:l})}}),t.ui.plugin.add(\"resizable\",\"alsoResize\",{start:function(){var e=t(this).resizable(\"instance\"),i=e.options;t(i.alsoResize).each(function(){var e=t(this);e.data(\"ui-resizable-alsoresize\",{width:parseInt(e.width(),10),height:parseInt(e.height(),10),left:parseInt(e.css(\"left\"),10),top:parseInt(e.css(\"top\"),10)})})},resize:function(e,i){var s=t(this).resizable(\"instance\"),n=s.options,o=s.originalSize,a=s.originalPosition,r={height:s.size.height-o.height||0,width:s.size.width-o.width||0,top:s.position.top-a.top||0,left:s.position.left-a.left||0};t(n.alsoResize).each(function(){var e=t(this),s=t(this).data(\"ui-resizable-alsoresize\"),n={},o=e.parents(i.originalElement[0]).length?[\"width\",\"height\"]:[\"width\",\"height\",\"top\",\"left\"];t.each(o,function(t,e){var i=(s[e]||0)+(r[e]||0);i&&i>=0&&(n[e]=i||null)}),e.css(n)})},stop:function(){t(this).removeData(\"resizable-alsoresize\")}}),t.ui.plugin.add(\"resizable\",\"ghost\",{start:function(){var e=t(this).resizable(\"instance\"),i=e.options,s=e.size;e.ghost=e.originalElement.clone(),e.ghost.css({opacity:.25,display:\"block\",position:\"relative\",height:s.height,width:s.width,margin:0,left:0,top:0}).addClass(\"ui-resizable-ghost\").addClass(\"string\"==typeof i.ghost?i.ghost:\"\"),e.ghost.appendTo(e.helper)},resize:function(){var e=t(this).resizable(\"instance\");e.ghost&&e.ghost.css({position:\"relative\",height:e.size.height,width:e.size.width})},stop:function(){var e=t(this).resizable(\"instance\");e.ghost&&e.helper&&e.helper.get(0).removeChild(e.ghost.get(0))}}),t.ui.plugin.add(\"resizable\",\"grid\",{resize:function(){var e,i=t(this).resizable(\"instance\"),s=i.options,n=i.size,o=i.originalSize,a=i.originalPosition,r=i.axis,h=\"number\"==typeof s.grid?[s.grid,s.grid]:s.grid,l=h[0]||1,c=h[1]||1,u=Math.round((n.width-o.width)/l)*l,d=Math.round((n.height-o.height)/c)*c,p=o.width+u,f=o.height+d,g=s.maxWidth&&p>s.maxWidth,m=s.maxHeight&&f>s.maxHeight,_=s.minWidth&&s.minWidth>p,v=s.minHeight&&s.minHeight>f;s.grid=h,_&&(p+=l),v&&(f+=c),g&&(p-=l),m&&(f-=c),/^(se|s|e)$/.test(r)?(i.size.width=p,i.size.height=f):/^(ne)$/.test(r)?(i.size.width=p,i.size.height=f,i.position.top=a.top-d):/^(sw)$/.test(r)?(i.size.width=p,i.size.height=f,i.position.left=a.left-u):((0>=f-c||0>=p-l)&&(e=i._getPaddingPlusBorderDimensions(this)),f-c>0?(i.size.height=f,i.position.top=a.top-d):(f=c-e.height,i.size.height=f,i.position.top=a.top+o.height-f),p-l>0?(i.size.width=p,i.position.left=a.left-u):(p=l-e.width,i.size.width=p,i.position.left=a.left+o.width-p))}}),t.ui.resizable,t.widget(\"ui.sortable\",t.ui.mouse,{version:\"1.11.4\",widgetEventPrefix:\"sort\",ready:!1,options:{appendTo:\"parent\",axis:!1,connectWith:!1,containment:!1,cursor:\"auto\",cursorAt:!1,dropOnEmpty:!0,forcePlaceholderSize:!1,forceHelperSize:!1,grid:!1,handle:!1,helper:\"original\",items:\"> *\",opacity:!1,placeholder:!1,revert:!1,scroll:!0,scrollSensitivity:20,scrollSpeed:20,scope:\"default\",tolerance:\"intersect\",zIndex:1e3,activate:null,beforeStop:null,change:null,deactivate:null,out:null,over:null,receive:null,remove:null,sort:null,start:null,stop:null,update:null},_isOverAxis:function(t,e,i){return t>=e&&e+i>t},_isFloating:function(t){return/left|right/.test(t.css(\"float\"))||/inline|table-cell/.test(t.css(\"display\"))},_create:function(){this.containerCache={},this.element.addClass(\"ui-sortable\"),this.refresh(),this.offset=this.element.offset(),this._mouseInit(),this._setHandleClassName(),this.ready=!0},_setOption:function(t,e){this._super(t,e),\"handle\"===t&&this._setHandleClassName()},_setHandleClassName:function(){this.element.find(\".ui-sortable-handle\").removeClass(\"ui-sortable-handle\"),t.each(this.items,function(){(this.instance.options.handle?this.item.find(this.instance.options.handle):this.item).addClass(\"ui-sortable-handle\")})},_destroy:function(){this.element.removeClass(\"ui-sortable ui-sortable-disabled\").find(\".ui-sortable-handle\").removeClass(\"ui-sortable-handle\"),this._mouseDestroy();for(var t=this.items.length-1;t>=0;t--)this.items[t].item.removeData(this.widgetName+\"-item\");return this},_mouseCapture:function(e,i){var s=null,n=!1,o=this;return this.reverting?!1:this.options.disabled||\"static\"===this.options.type?!1:(this._refreshItems(e),t(e.target).parents().each(function(){return t.data(this,o.widgetName+\"-item\")===o?(s=t(this),!1):void 0}),t.data(e.target,o.widgetName+\"-item\")===o&&(s=t(e.target)),s?!this.options.handle||i||(t(this.options.handle,s).find(\"*\").addBack().each(function(){this===e.target&&(n=!0)}),n)?(this.currentItem=s,this._removeCurrentsFromItems(),!0):!1:!1)},_mouseStart:function(e,i,s){var n,o,a=this.options;if(this.currentContainer=this,this.refreshPositions(),this.helper=this._createHelper(e),this._cacheHelperProportions(),this._cacheMargins(),this.scrollParent=this.helper.scrollParent(),this.offset=this.currentItem.offset(),this.offset={top:this.offset.top-this.margins.top,left:this.offset.left-this.margins.left},t.extend(this.offset,{click:{left:e.pageX-this.offset.left,top:e.pageY-this.offset.top},parent:this._getParentOffset(),relative:this._getRelativeOffset()}),this.helper.css(\"position\",\"absolute\"),this.cssPosition=this.helper.css(\"position\"),this.originalPosition=this._generatePosition(e),this.originalPageX=e.pageX,this.originalPageY=e.pageY,a.cursorAt&&this._adjustOffsetFromHelper(a.cursorAt),this.domPosition={prev:this.currentItem.prev()[0],parent:this.currentItem.parent()[0]},this.helper[0]!==this.currentItem[0]&&this.currentItem.hide(),this._createPlaceholder(),a.containment&&this._setContainment(),a.cursor&&\"auto\"!==a.cursor&&(o=this.document.find(\"body\"),this.storedCursor=o.css(\"cursor\"),o.css(\"cursor\",a.cursor),this.storedStylesheet=t(\"<style>*{ cursor: \"+a.cursor+\" !important; }</style>\").appendTo(o)),a.opacity&&(this.helper.css(\"opacity\")&&(this._storedOpacity=this.helper.css(\"opacity\")),this.helper.css(\"opacity\",a.opacity)),a.zIndex&&(this.helper.css(\"zIndex\")&&(this._storedZIndex=this.helper.css(\"zIndex\")),this.helper.css(\"zIndex\",a.zIndex)),this.scrollParent[0]!==this.document[0]&&\"HTML\"!==this.scrollParent[0].tagName&&(this.overflowOffset=this.scrollParent.offset()),this._trigger(\"start\",e,this._uiHash()),this._preserveHelperProportions||this._cacheHelperProportions(),!s)for(n=this.containers.length-1;n>=0;n--)this.containers[n]._trigger(\"activate\",e,this._uiHash(this));return t.ui.ddmanager&&(t.ui.ddmanager.current=this),t.ui.ddmanager&&!a.dropBehaviour&&t.ui.ddmanager.prepareOffsets(this,e),this.dragging=!0,this.helper.addClass(\"ui-sortable-helper\"),this._mouseDrag(e),!0},_mouseDrag:function(e){var i,s,n,o,a=this.options,r=!1;for(this.position=this._generatePosition(e),this.positionAbs=this._convertPositionTo(\"absolute\"),this.lastPositionAbs||(this.lastPositionAbs=this.positionAbs),this.options.scroll&&(this.scrollParent[0]!==this.document[0]&&\"HTML\"!==this.scrollParent[0].tagName?(this.overflowOffset.top+this.scrollParent[0].offsetHeight-e.pageY<a.scrollSensitivity?this.scrollParent[0].scrollTop=r=this.scrollParent[0].scrollTop+a.scrollSpeed:e.pageY-this.overflowOffset.top<a.scrollSensitivity&&(this.scrollParent[0].scrollTop=r=this.scrollParent[0].scrollTop-a.scrollSpeed),this.overflowOffset.left+this.scrollParent[0].offsetWidth-e.pageX<a.scrollSensitivity?this.scrollParent[0].scrollLeft=r=this.scrollParent[0].scrollLeft+a.scrollSpeed:e.pageX-this.overflowOffset.left<a.scrollSensitivity&&(this.scrollParent[0].scrollLeft=r=this.scrollParent[0].scrollLeft-a.scrollSpeed)):(e.pageY-this.document.scrollTop()<a.scrollSensitivity?r=this.document.scrollTop(this.document.scrollTop()-a.scrollSpeed):this.window.height()-(e.pageY-this.document.scrollTop())<a.scrollSensitivity&&(r=this.document.scrollTop(this.document.scrollTop()+a.scrollSpeed)),e.pageX-this.document.scrollLeft()<a.scrollSensitivity?r=this.document.scrollLeft(this.document.scrollLeft()-a.scrollSpeed):this.window.width()-(e.pageX-this.document.scrollLeft())<a.scrollSensitivity&&(r=this.document.scrollLeft(this.document.scrollLeft()+a.scrollSpeed))),r!==!1&&t.ui.ddmanager&&!a.dropBehaviour&&t.ui.ddmanager.prepareOffsets(this,e)),this.positionAbs=this._convertPositionTo(\"absolute\"),this.options.axis&&\"y\"===this.options.axis||(this.helper[0].style.left=this.position.left+\"px\"),this.options.axis&&\"x\"===this.options.axis||(this.helper[0].style.top=this.position.top+\"px\"),i=this.items.length-1;i>=0;i--)if(s=this.items[i],n=s.item[0],o=this._intersectsWithPointer(s),o&&s.instance===this.currentContainer&&n!==this.currentItem[0]&&this.placeholder[1===o?\"next\":\"prev\"]()[0]!==n&&!t.contains(this.placeholder[0],n)&&(\"semi-dynamic\"===this.options.type?!t.contains(this.element[0],n):!0)){if(this.direction=1===o?\"down\":\"up\",\"pointer\"!==this.options.tolerance&&!this._intersectsWithSides(s))break;this._rearrange(e,s),this._trigger(\"change\",e,this._uiHash());break}return this._contactContainers(e),t.ui.ddmanager&&t.ui.ddmanager.drag(this,e),this._trigger(\"sort\",e,this._uiHash()),this.lastPositionAbs=this.positionAbs,!1},_mouseStop:function(e,i){if(e){if(t.ui.ddmanager&&!this.options.dropBehaviour&&t.ui.ddmanager.drop(this,e),this.options.revert){var s=this,n=this.placeholder.offset(),o=this.options.axis,a={};o&&\"x\"!==o||(a.left=n.left-this.offset.parent.left-this.margins.left+(this.offsetParent[0]===this.document[0].body?0:this.offsetParent[0].scrollLeft)),o&&\"y\"!==o||(a.top=n.top-this.offset.parent.top-this.margins.top+(this.offsetParent[0]===this.document[0].body?0:this.offsetParent[0].scrollTop)),this.reverting=!0,t(this.helper).animate(a,parseInt(this.options.revert,10)||500,function(){s._clear(e)})}else this._clear(e,i);return!1}},cancel:function(){if(this.dragging){this._mouseUp({target:null}),\"original\"===this.options.helper?this.currentItem.css(this._storedCSS).removeClass(\"ui-sortable-helper\"):this.currentItem.show();for(var e=this.containers.length-1;e>=0;e--)this.containers[e]._trigger(\"deactivate\",null,this._uiHash(this)),this.containers[e].containerCache.over&&(this.containers[e]._trigger(\"out\",null,this._uiHash(this)),this.containers[e].containerCache.over=0)}return this.placeholder&&(this.placeholder[0].parentNode&&this.placeholder[0].parentNode.removeChild(this.placeholder[0]),\"original\"!==this.options.helper&&this.helper&&this.helper[0].parentNode&&this.helper.remove(),t.extend(this,{helper:null,dragging:!1,reverting:!1,_noFinalSort:null}),this.domPosition.prev?t(this.domPosition.prev).after(this.currentItem):t(this.domPosition.parent).prepend(this.currentItem)),this},serialize:function(e){var i=this._getItemsAsjQuery(e&&e.connected),s=[];return e=e||{},t(i).each(function(){var i=(t(e.item||this).attr(e.attribute||\"id\")||\"\").match(e.expression||/(.+)[\\-=_](.+)/);i&&s.push((e.key||i[1]+\"[]\")+\"=\"+(e.key&&e.expression?i[1]:i[2]))}),!s.length&&e.key&&s.push(e.key+\"=\"),s.join(\"&\")\n},toArray:function(e){var i=this._getItemsAsjQuery(e&&e.connected),s=[];return e=e||{},i.each(function(){s.push(t(e.item||this).attr(e.attribute||\"id\")||\"\")}),s},_intersectsWith:function(t){var e=this.positionAbs.left,i=e+this.helperProportions.width,s=this.positionAbs.top,n=s+this.helperProportions.height,o=t.left,a=o+t.width,r=t.top,h=r+t.height,l=this.offset.click.top,c=this.offset.click.left,u=\"x\"===this.options.axis||s+l>r&&h>s+l,d=\"y\"===this.options.axis||e+c>o&&a>e+c,p=u&&d;return\"pointer\"===this.options.tolerance||this.options.forcePointerForContainers||\"pointer\"!==this.options.tolerance&&this.helperProportions[this.floating?\"width\":\"height\"]>t[this.floating?\"width\":\"height\"]?p:e+this.helperProportions.width/2>o&&a>i-this.helperProportions.width/2&&s+this.helperProportions.height/2>r&&h>n-this.helperProportions.height/2},_intersectsWithPointer:function(t){var e=\"x\"===this.options.axis||this._isOverAxis(this.positionAbs.top+this.offset.click.top,t.top,t.height),i=\"y\"===this.options.axis||this._isOverAxis(this.positionAbs.left+this.offset.click.left,t.left,t.width),s=e&&i,n=this._getDragVerticalDirection(),o=this._getDragHorizontalDirection();return s?this.floating?o&&\"right\"===o||\"down\"===n?2:1:n&&(\"down\"===n?2:1):!1},_intersectsWithSides:function(t){var e=this._isOverAxis(this.positionAbs.top+this.offset.click.top,t.top+t.height/2,t.height),i=this._isOverAxis(this.positionAbs.left+this.offset.click.left,t.left+t.width/2,t.width),s=this._getDragVerticalDirection(),n=this._getDragHorizontalDirection();return this.floating&&n?\"right\"===n&&i||\"left\"===n&&!i:s&&(\"down\"===s&&e||\"up\"===s&&!e)},_getDragVerticalDirection:function(){var t=this.positionAbs.top-this.lastPositionAbs.top;return 0!==t&&(t>0?\"down\":\"up\")},_getDragHorizontalDirection:function(){var t=this.positionAbs.left-this.lastPositionAbs.left;return 0!==t&&(t>0?\"right\":\"left\")},refresh:function(t){return this._refreshItems(t),this._setHandleClassName(),this.refreshPositions(),this},_connectWith:function(){var t=this.options;return t.connectWith.constructor===String?[t.connectWith]:t.connectWith},_getItemsAsjQuery:function(e){function i(){r.push(this)}var s,n,o,a,r=[],h=[],l=this._connectWith();if(l&&e)for(s=l.length-1;s>=0;s--)for(o=t(l[s],this.document[0]),n=o.length-1;n>=0;n--)a=t.data(o[n],this.widgetFullName),a&&a!==this&&!a.options.disabled&&h.push([t.isFunction(a.options.items)?a.options.items.call(a.element):t(a.options.items,a.element).not(\".ui-sortable-helper\").not(\".ui-sortable-placeholder\"),a]);for(h.push([t.isFunction(this.options.items)?this.options.items.call(this.element,null,{options:this.options,item:this.currentItem}):t(this.options.items,this.element).not(\".ui-sortable-helper\").not(\".ui-sortable-placeholder\"),this]),s=h.length-1;s>=0;s--)h[s][0].each(i);return t(r)},_removeCurrentsFromItems:function(){var e=this.currentItem.find(\":data(\"+this.widgetName+\"-item)\");this.items=t.grep(this.items,function(t){for(var i=0;e.length>i;i++)if(e[i]===t.item[0])return!1;return!0})},_refreshItems:function(e){this.items=[],this.containers=[this];var i,s,n,o,a,r,h,l,c=this.items,u=[[t.isFunction(this.options.items)?this.options.items.call(this.element[0],e,{item:this.currentItem}):t(this.options.items,this.element),this]],d=this._connectWith();if(d&&this.ready)for(i=d.length-1;i>=0;i--)for(n=t(d[i],this.document[0]),s=n.length-1;s>=0;s--)o=t.data(n[s],this.widgetFullName),o&&o!==this&&!o.options.disabled&&(u.push([t.isFunction(o.options.items)?o.options.items.call(o.element[0],e,{item:this.currentItem}):t(o.options.items,o.element),o]),this.containers.push(o));for(i=u.length-1;i>=0;i--)for(a=u[i][1],r=u[i][0],s=0,l=r.length;l>s;s++)h=t(r[s]),h.data(this.widgetName+\"-item\",a),c.push({item:h,instance:a,width:0,height:0,left:0,top:0})},refreshPositions:function(e){this.floating=this.items.length?\"x\"===this.options.axis||this._isFloating(this.items[0].item):!1,this.offsetParent&&this.helper&&(this.offset.parent=this._getParentOffset());var i,s,n,o;for(i=this.items.length-1;i>=0;i--)s=this.items[i],s.instance!==this.currentContainer&&this.currentContainer&&s.item[0]!==this.currentItem[0]||(n=this.options.toleranceElement?t(this.options.toleranceElement,s.item):s.item,e||(s.width=n.outerWidth(),s.height=n.outerHeight()),o=n.offset(),s.left=o.left,s.top=o.top);if(this.options.custom&&this.options.custom.refreshContainers)this.options.custom.refreshContainers.call(this);else for(i=this.containers.length-1;i>=0;i--)o=this.containers[i].element.offset(),this.containers[i].containerCache.left=o.left,this.containers[i].containerCache.top=o.top,this.containers[i].containerCache.width=this.containers[i].element.outerWidth(),this.containers[i].containerCache.height=this.containers[i].element.outerHeight();return this},_createPlaceholder:function(e){e=e||this;var i,s=e.options;s.placeholder&&s.placeholder.constructor!==String||(i=s.placeholder,s.placeholder={element:function(){var s=e.currentItem[0].nodeName.toLowerCase(),n=t(\"<\"+s+\">\",e.document[0]).addClass(i||e.currentItem[0].className+\" ui-sortable-placeholder\").removeClass(\"ui-sortable-helper\");return\"tbody\"===s?e._createTrPlaceholder(e.currentItem.find(\"tr\").eq(0),t(\"<tr>\",e.document[0]).appendTo(n)):\"tr\"===s?e._createTrPlaceholder(e.currentItem,n):\"img\"===s&&n.attr(\"src\",e.currentItem.attr(\"src\")),i||n.css(\"visibility\",\"hidden\"),n},update:function(t,n){(!i||s.forcePlaceholderSize)&&(n.height()||n.height(e.currentItem.innerHeight()-parseInt(e.currentItem.css(\"paddingTop\")||0,10)-parseInt(e.currentItem.css(\"paddingBottom\")||0,10)),n.width()||n.width(e.currentItem.innerWidth()-parseInt(e.currentItem.css(\"paddingLeft\")||0,10)-parseInt(e.currentItem.css(\"paddingRight\")||0,10)))}}),e.placeholder=t(s.placeholder.element.call(e.element,e.currentItem)),e.currentItem.after(e.placeholder),s.placeholder.update(e,e.placeholder)},_createTrPlaceholder:function(e,i){var s=this;e.children().each(function(){t(\"<td>&#160;</td>\",s.document[0]).attr(\"colspan\",t(this).attr(\"colspan\")||1).appendTo(i)})},_contactContainers:function(e){var i,s,n,o,a,r,h,l,c,u,d=null,p=null;for(i=this.containers.length-1;i>=0;i--)if(!t.contains(this.currentItem[0],this.containers[i].element[0]))if(this._intersectsWith(this.containers[i].containerCache)){if(d&&t.contains(this.containers[i].element[0],d.element[0]))continue;d=this.containers[i],p=i}else this.containers[i].containerCache.over&&(this.containers[i]._trigger(\"out\",e,this._uiHash(this)),this.containers[i].containerCache.over=0);if(d)if(1===this.containers.length)this.containers[p].containerCache.over||(this.containers[p]._trigger(\"over\",e,this._uiHash(this)),this.containers[p].containerCache.over=1);else{for(n=1e4,o=null,c=d.floating||this._isFloating(this.currentItem),a=c?\"left\":\"top\",r=c?\"width\":\"height\",u=c?\"clientX\":\"clientY\",s=this.items.length-1;s>=0;s--)t.contains(this.containers[p].element[0],this.items[s].item[0])&&this.items[s].item[0]!==this.currentItem[0]&&(h=this.items[s].item.offset()[a],l=!1,e[u]-h>this.items[s][r]/2&&(l=!0),n>Math.abs(e[u]-h)&&(n=Math.abs(e[u]-h),o=this.items[s],this.direction=l?\"up\":\"down\"));if(!o&&!this.options.dropOnEmpty)return;if(this.currentContainer===this.containers[p])return this.currentContainer.containerCache.over||(this.containers[p]._trigger(\"over\",e,this._uiHash()),this.currentContainer.containerCache.over=1),void 0;o?this._rearrange(e,o,null,!0):this._rearrange(e,null,this.containers[p].element,!0),this._trigger(\"change\",e,this._uiHash()),this.containers[p]._trigger(\"change\",e,this._uiHash(this)),this.currentContainer=this.containers[p],this.options.placeholder.update(this.currentContainer,this.placeholder),this.containers[p]._trigger(\"over\",e,this._uiHash(this)),this.containers[p].containerCache.over=1}},_createHelper:function(e){var i=this.options,s=t.isFunction(i.helper)?t(i.helper.apply(this.element[0],[e,this.currentItem])):\"clone\"===i.helper?this.currentItem.clone():this.currentItem;return s.parents(\"body\").length||t(\"parent\"!==i.appendTo?i.appendTo:this.currentItem[0].parentNode)[0].appendChild(s[0]),s[0]===this.currentItem[0]&&(this._storedCSS={width:this.currentItem[0].style.width,height:this.currentItem[0].style.height,position:this.currentItem.css(\"position\"),top:this.currentItem.css(\"top\"),left:this.currentItem.css(\"left\")}),(!s[0].style.width||i.forceHelperSize)&&s.width(this.currentItem.width()),(!s[0].style.height||i.forceHelperSize)&&s.height(this.currentItem.height()),s},_adjustOffsetFromHelper:function(e){\"string\"==typeof e&&(e=e.split(\" \")),t.isArray(e)&&(e={left:+e[0],top:+e[1]||0}),\"left\"in e&&(this.offset.click.left=e.left+this.margins.left),\"right\"in e&&(this.offset.click.left=this.helperProportions.width-e.right+this.margins.left),\"top\"in e&&(this.offset.click.top=e.top+this.margins.top),\"bottom\"in e&&(this.offset.click.top=this.helperProportions.height-e.bottom+this.margins.top)},_getParentOffset:function(){this.offsetParent=this.helper.offsetParent();var e=this.offsetParent.offset();return\"absolute\"===this.cssPosition&&this.scrollParent[0]!==this.document[0]&&t.contains(this.scrollParent[0],this.offsetParent[0])&&(e.left+=this.scrollParent.scrollLeft(),e.top+=this.scrollParent.scrollTop()),(this.offsetParent[0]===this.document[0].body||this.offsetParent[0].tagName&&\"html\"===this.offsetParent[0].tagName.toLowerCase()&&t.ui.ie)&&(e={top:0,left:0}),{top:e.top+(parseInt(this.offsetParent.css(\"borderTopWidth\"),10)||0),left:e.left+(parseInt(this.offsetParent.css(\"borderLeftWidth\"),10)||0)}},_getRelativeOffset:function(){if(\"relative\"===this.cssPosition){var t=this.currentItem.position();return{top:t.top-(parseInt(this.helper.css(\"top\"),10)||0)+this.scrollParent.scrollTop(),left:t.left-(parseInt(this.helper.css(\"left\"),10)||0)+this.scrollParent.scrollLeft()}}return{top:0,left:0}},_cacheMargins:function(){this.margins={left:parseInt(this.currentItem.css(\"marginLeft\"),10)||0,top:parseInt(this.currentItem.css(\"marginTop\"),10)||0}},_cacheHelperProportions:function(){this.helperProportions={width:this.helper.outerWidth(),height:this.helper.outerHeight()}},_setContainment:function(){var e,i,s,n=this.options;\"parent\"===n.containment&&(n.containment=this.helper[0].parentNode),(\"document\"===n.containment||\"window\"===n.containment)&&(this.containment=[0-this.offset.relative.left-this.offset.parent.left,0-this.offset.relative.top-this.offset.parent.top,\"document\"===n.containment?this.document.width():this.window.width()-this.helperProportions.width-this.margins.left,(\"document\"===n.containment?this.document.width():this.window.height()||this.document[0].body.parentNode.scrollHeight)-this.helperProportions.height-this.margins.top]),/^(document|window|parent)$/.test(n.containment)||(e=t(n.containment)[0],i=t(n.containment).offset(),s=\"hidden\"!==t(e).css(\"overflow\"),this.containment=[i.left+(parseInt(t(e).css(\"borderLeftWidth\"),10)||0)+(parseInt(t(e).css(\"paddingLeft\"),10)||0)-this.margins.left,i.top+(parseInt(t(e).css(\"borderTopWidth\"),10)||0)+(parseInt(t(e).css(\"paddingTop\"),10)||0)-this.margins.top,i.left+(s?Math.max(e.scrollWidth,e.offsetWidth):e.offsetWidth)-(parseInt(t(e).css(\"borderLeftWidth\"),10)||0)-(parseInt(t(e).css(\"paddingRight\"),10)||0)-this.helperProportions.width-this.margins.left,i.top+(s?Math.max(e.scrollHeight,e.offsetHeight):e.offsetHeight)-(parseInt(t(e).css(\"borderTopWidth\"),10)||0)-(parseInt(t(e).css(\"paddingBottom\"),10)||0)-this.helperProportions.height-this.margins.top])},_convertPositionTo:function(e,i){i||(i=this.position);var s=\"absolute\"===e?1:-1,n=\"absolute\"!==this.cssPosition||this.scrollParent[0]!==this.document[0]&&t.contains(this.scrollParent[0],this.offsetParent[0])?this.scrollParent:this.offsetParent,o=/(html|body)/i.test(n[0].tagName);return{top:i.top+this.offset.relative.top*s+this.offset.parent.top*s-(\"fixed\"===this.cssPosition?-this.scrollParent.scrollTop():o?0:n.scrollTop())*s,left:i.left+this.offset.relative.left*s+this.offset.parent.left*s-(\"fixed\"===this.cssPosition?-this.scrollParent.scrollLeft():o?0:n.scrollLeft())*s}},_generatePosition:function(e){var i,s,n=this.options,o=e.pageX,a=e.pageY,r=\"absolute\"!==this.cssPosition||this.scrollParent[0]!==this.document[0]&&t.contains(this.scrollParent[0],this.offsetParent[0])?this.scrollParent:this.offsetParent,h=/(html|body)/i.test(r[0].tagName);return\"relative\"!==this.cssPosition||this.scrollParent[0]!==this.document[0]&&this.scrollParent[0]!==this.offsetParent[0]||(this.offset.relative=this._getRelativeOffset()),this.originalPosition&&(this.containment&&(e.pageX-this.offset.click.left<this.containment[0]&&(o=this.containment[0]+this.offset.click.left),e.pageY-this.offset.click.top<this.containment[1]&&(a=this.containment[1]+this.offset.click.top),e.pageX-this.offset.click.left>this.containment[2]&&(o=this.containment[2]+this.offset.click.left),e.pageY-this.offset.click.top>this.containment[3]&&(a=this.containment[3]+this.offset.click.top)),n.grid&&(i=this.originalPageY+Math.round((a-this.originalPageY)/n.grid[1])*n.grid[1],a=this.containment?i-this.offset.click.top>=this.containment[1]&&i-this.offset.click.top<=this.containment[3]?i:i-this.offset.click.top>=this.containment[1]?i-n.grid[1]:i+n.grid[1]:i,s=this.originalPageX+Math.round((o-this.originalPageX)/n.grid[0])*n.grid[0],o=this.containment?s-this.offset.click.left>=this.containment[0]&&s-this.offset.click.left<=this.containment[2]?s:s-this.offset.click.left>=this.containment[0]?s-n.grid[0]:s+n.grid[0]:s)),{top:a-this.offset.click.top-this.offset.relative.top-this.offset.parent.top+(\"fixed\"===this.cssPosition?-this.scrollParent.scrollTop():h?0:r.scrollTop()),left:o-this.offset.click.left-this.offset.relative.left-this.offset.parent.left+(\"fixed\"===this.cssPosition?-this.scrollParent.scrollLeft():h?0:r.scrollLeft())}},_rearrange:function(t,e,i,s){i?i[0].appendChild(this.placeholder[0]):e.item[0].parentNode.insertBefore(this.placeholder[0],\"down\"===this.direction?e.item[0]:e.item[0].nextSibling),this.counter=this.counter?++this.counter:1;var n=this.counter;this._delay(function(){n===this.counter&&this.refreshPositions(!s)})},_clear:function(t,e){function i(t,e,i){return function(s){i._trigger(t,s,e._uiHash(e))}}this.reverting=!1;var s,n=[];if(!this._noFinalSort&&this.currentItem.parent().length&&this.placeholder.before(this.currentItem),this._noFinalSort=null,this.helper[0]===this.currentItem[0]){for(s in this._storedCSS)(\"auto\"===this._storedCSS[s]||\"static\"===this._storedCSS[s])&&(this._storedCSS[s]=\"\");this.currentItem.css(this._storedCSS).removeClass(\"ui-sortable-helper\")}else this.currentItem.show();for(this.fromOutside&&!e&&n.push(function(t){this._trigger(\"receive\",t,this._uiHash(this.fromOutside))}),!this.fromOutside&&this.domPosition.prev===this.currentItem.prev().not(\".ui-sortable-helper\")[0]&&this.domPosition.parent===this.currentItem.parent()[0]||e||n.push(function(t){this._trigger(\"update\",t,this._uiHash())}),this!==this.currentContainer&&(e||(n.push(function(t){this._trigger(\"remove\",t,this._uiHash())}),n.push(function(t){return function(e){t._trigger(\"receive\",e,this._uiHash(this))}}.call(this,this.currentContainer)),n.push(function(t){return function(e){t._trigger(\"update\",e,this._uiHash(this))}}.call(this,this.currentContainer)))),s=this.containers.length-1;s>=0;s--)e||n.push(i(\"deactivate\",this,this.containers[s])),this.containers[s].containerCache.over&&(n.push(i(\"out\",this,this.containers[s])),this.containers[s].containerCache.over=0);if(this.storedCursor&&(this.document.find(\"body\").css(\"cursor\",this.storedCursor),this.storedStylesheet.remove()),this._storedOpacity&&this.helper.css(\"opacity\",this._storedOpacity),this._storedZIndex&&this.helper.css(\"zIndex\",\"auto\"===this._storedZIndex?\"\":this._storedZIndex),this.dragging=!1,e||this._trigger(\"beforeStop\",t,this._uiHash()),this.placeholder[0].parentNode.removeChild(this.placeholder[0]),this.cancelHelperRemoval||(this.helper[0]!==this.currentItem[0]&&this.helper.remove(),this.helper=null),!e){for(s=0;n.length>s;s++)n[s].call(this,t);this._trigger(\"stop\",t,this._uiHash())}return this.fromOutside=!1,!this.cancelHelperRemoval},_trigger:function(){t.Widget.prototype._trigger.apply(this,arguments)===!1&&this.cancel()},_uiHash:function(e){var i=e||this;return{helper:i.helper,placeholder:i.placeholder||t([]),position:i.position,originalPosition:i.originalPosition,offset:i.positionAbs,item:i.currentItem,sender:e?e.element:null}}}),t.widget(\"ui.accordion\",{version:\"1.11.4\",options:{active:0,animate:{},collapsible:!1,event:\"click\",header:\"> li > :first-child,> :not(li):even\",heightStyle:\"auto\",icons:{activeHeader:\"ui-icon-triangle-1-s\",header:\"ui-icon-triangle-1-e\"},activate:null,beforeActivate:null},hideProps:{borderTopWidth:\"hide\",borderBottomWidth:\"hide\",paddingTop:\"hide\",paddingBottom:\"hide\",height:\"hide\"},showProps:{borderTopWidth:\"show\",borderBottomWidth:\"show\",paddingTop:\"show\",paddingBottom:\"show\",height:\"show\"},_create:function(){var e=this.options;this.prevShow=this.prevHide=t(),this.element.addClass(\"ui-accordion ui-widget ui-helper-reset\").attr(\"role\",\"tablist\"),e.collapsible||e.active!==!1&&null!=e.active||(e.active=0),this._processPanels(),0>e.active&&(e.active+=this.headers.length),this._refresh()},_getCreateEventData:function(){return{header:this.active,panel:this.active.length?this.active.next():t()}},_createIcons:function(){var e=this.options.icons;e&&(t(\"<span>\").addClass(\"ui-accordion-header-icon ui-icon \"+e.header).prependTo(this.headers),this.active.children(\".ui-accordion-header-icon\").removeClass(e.header).addClass(e.activeHeader),this.headers.addClass(\"ui-accordion-icons\"))},_destroyIcons:function(){this.headers.removeClass(\"ui-accordion-icons\").children(\".ui-accordion-header-icon\").remove()},_destroy:function(){var t;this.element.removeClass(\"ui-accordion ui-widget ui-helper-reset\").removeAttr(\"role\"),this.headers.removeClass(\"ui-accordion-header ui-accordion-header-active ui-state-default ui-corner-all ui-state-active ui-state-disabled ui-corner-top\").removeAttr(\"role\").removeAttr(\"aria-expanded\").removeAttr(\"aria-selected\").removeAttr(\"aria-controls\").removeAttr(\"tabIndex\").removeUniqueId(),this._destroyIcons(),t=this.headers.next().removeClass(\"ui-helper-reset ui-widget-content ui-corner-bottom ui-accordion-content ui-accordion-content-active ui-state-disabled\").css(\"display\",\"\").removeAttr(\"role\").removeAttr(\"aria-hidden\").removeAttr(\"aria-labelledby\").removeUniqueId(),\"content\"!==this.options.heightStyle&&t.css(\"height\",\"\")},_setOption:function(t,e){return\"active\"===t?(this._activate(e),void 0):(\"event\"===t&&(this.options.event&&this._off(this.headers,this.options.event),this._setupEvents(e)),this._super(t,e),\"collapsible\"!==t||e||this.options.active!==!1||this._activate(0),\"icons\"===t&&(this._destroyIcons(),e&&this._createIcons()),\"disabled\"===t&&(this.element.toggleClass(\"ui-state-disabled\",!!e).attr(\"aria-disabled\",e),this.headers.add(this.headers.next()).toggleClass(\"ui-state-disabled\",!!e)),void 0)},_keydown:function(e){if(!e.altKey&&!e.ctrlKey){var i=t.ui.keyCode,s=this.headers.length,n=this.headers.index(e.target),o=!1;switch(e.keyCode){case i.RIGHT:case i.DOWN:o=this.headers[(n+1)%s];break;case i.LEFT:case i.UP:o=this.headers[(n-1+s)%s];break;case i.SPACE:case i.ENTER:this._eventHandler(e);break;case i.HOME:o=this.headers[0];break;case i.END:o=this.headers[s-1]}o&&(t(e.target).attr(\"tabIndex\",-1),t(o).attr(\"tabIndex\",0),o.focus(),e.preventDefault())}},_panelKeyDown:function(e){e.keyCode===t.ui.keyCode.UP&&e.ctrlKey&&t(e.currentTarget).prev().focus()},refresh:function(){var e=this.options;this._processPanels(),e.active===!1&&e.collapsible===!0||!this.headers.length?(e.active=!1,this.active=t()):e.active===!1?this._activate(0):this.active.length&&!t.contains(this.element[0],this.active[0])?this.headers.length===this.headers.find(\".ui-state-disabled\").length?(e.active=!1,this.active=t()):this._activate(Math.max(0,e.active-1)):e.active=this.headers.index(this.active),this._destroyIcons(),this._refresh()},_processPanels:function(){var t=this.headers,e=this.panels;this.headers=this.element.find(this.options.header).addClass(\"ui-accordion-header ui-state-default ui-corner-all\"),this.panels=this.headers.next().addClass(\"ui-accordion-content ui-helper-reset ui-widget-content ui-corner-bottom\").filter(\":not(.ui-accordion-content-active)\").hide(),e&&(this._off(t.not(this.headers)),this._off(e.not(this.panels)))},_refresh:function(){var e,i=this.options,s=i.heightStyle,n=this.element.parent();this.active=this._findActive(i.active).addClass(\"ui-accordion-header-active ui-state-active ui-corner-top\").removeClass(\"ui-corner-all\"),this.active.next().addClass(\"ui-accordion-content-active\").show(),this.headers.attr(\"role\",\"tab\").each(function(){var e=t(this),i=e.uniqueId().attr(\"id\"),s=e.next(),n=s.uniqueId().attr(\"id\");e.attr(\"aria-controls\",n),s.attr(\"aria-labelledby\",i)}).next().attr(\"role\",\"tabpanel\"),this.headers.not(this.active).attr({\"aria-selected\":\"false\",\"aria-expanded\":\"false\",tabIndex:-1}).next().attr({\"aria-hidden\":\"true\"}).hide(),this.active.length?this.active.attr({\"aria-selected\":\"true\",\"aria-expanded\":\"true\",tabIndex:0}).next().attr({\"aria-hidden\":\"false\"}):this.headers.eq(0).attr(\"tabIndex\",0),this._createIcons(),this._setupEvents(i.event),\"fill\"===s?(e=n.height(),this.element.siblings(\":visible\").each(function(){var i=t(this),s=i.css(\"position\");\"absolute\"!==s&&\"fixed\"!==s&&(e-=i.outerHeight(!0))}),this.headers.each(function(){e-=t(this).outerHeight(!0)}),this.headers.next().each(function(){t(this).height(Math.max(0,e-t(this).innerHeight()+t(this).height()))}).css(\"overflow\",\"auto\")):\"auto\"===s&&(e=0,this.headers.next().each(function(){e=Math.max(e,t(this).css(\"height\",\"\").height())}).height(e))},_activate:function(e){var i=this._findActive(e)[0];i!==this.active[0]&&(i=i||this.active[0],this._eventHandler({target:i,currentTarget:i,preventDefault:t.noop}))},_findActive:function(e){return\"number\"==typeof e?this.headers.eq(e):t()},_setupEvents:function(e){var i={keydown:\"_keydown\"};e&&t.each(e.split(\" \"),function(t,e){i[e]=\"_eventHandler\"}),this._off(this.headers.add(this.headers.next())),this._on(this.headers,i),this._on(this.headers.next(),{keydown:\"_panelKeyDown\"}),this._hoverable(this.headers),this._focusable(this.headers)},_eventHandler:function(e){var i=this.options,s=this.active,n=t(e.currentTarget),o=n[0]===s[0],a=o&&i.collapsible,r=a?t():n.next(),h=s.next(),l={oldHeader:s,oldPanel:h,newHeader:a?t():n,newPanel:r};e.preventDefault(),o&&!i.collapsible||this._trigger(\"beforeActivate\",e,l)===!1||(i.active=a?!1:this.headers.index(n),this.active=o?t():n,this._toggle(l),s.removeClass(\"ui-accordion-header-active ui-state-active\"),i.icons&&s.children(\".ui-accordion-header-icon\").removeClass(i.icons.activeHeader).addClass(i.icons.header),o||(n.removeClass(\"ui-corner-all\").addClass(\"ui-accordion-header-active ui-state-active ui-corner-top\"),i.icons&&n.children(\".ui-accordion-header-icon\").removeClass(i.icons.header).addClass(i.icons.activeHeader),n.next().addClass(\"ui-accordion-content-active\")))},_toggle:function(e){var i=e.newPanel,s=this.prevShow.length?this.prevShow:e.oldPanel;this.prevShow.add(this.prevHide).stop(!0,!0),this.prevShow=i,this.prevHide=s,this.options.animate?this._animate(i,s,e):(s.hide(),i.show(),this._toggleComplete(e)),s.attr({\"aria-hidden\":\"true\"}),s.prev().attr({\"aria-selected\":\"false\",\"aria-expanded\":\"false\"}),i.length&&s.length?s.prev().attr({tabIndex:-1,\"aria-expanded\":\"false\"}):i.length&&this.headers.filter(function(){return 0===parseInt(t(this).attr(\"tabIndex\"),10)}).attr(\"tabIndex\",-1),i.attr(\"aria-hidden\",\"false\").prev().attr({\"aria-selected\":\"true\",\"aria-expanded\":\"true\",tabIndex:0})},_animate:function(t,e,i){var s,n,o,a=this,r=0,h=t.css(\"box-sizing\"),l=t.length&&(!e.length||t.index()<e.index()),c=this.options.animate||{},u=l&&c.down||c,d=function(){a._toggleComplete(i)};return\"number\"==typeof u&&(o=u),\"string\"==typeof u&&(n=u),n=n||u.easing||c.easing,o=o||u.duration||c.duration,e.length?t.length?(s=t.show().outerHeight(),e.animate(this.hideProps,{duration:o,easing:n,step:function(t,e){e.now=Math.round(t)}}),t.hide().animate(this.showProps,{duration:o,easing:n,complete:d,step:function(t,i){i.now=Math.round(t),\"height\"!==i.prop?\"content-box\"===h&&(r+=i.now):\"content\"!==a.options.heightStyle&&(i.now=Math.round(s-e.outerHeight()-r),r=0)}}),void 0):e.animate(this.hideProps,o,n,d):t.animate(this.showProps,o,n,d)},_toggleComplete:function(t){var e=t.oldPanel;e.removeClass(\"ui-accordion-content-active\").prev().removeClass(\"ui-corner-top\").addClass(\"ui-corner-all\"),e.length&&(e.parent()[0].className=e.parent()[0].className),this._trigger(\"activate\",null,t)}});var a,r=\"ui-button ui-widget ui-state-default ui-corner-all\",h=\"ui-button-icons-only ui-button-icon-only ui-button-text-icons ui-button-text-icon-primary ui-button-text-icon-secondary ui-button-text-only\",l=function(){var e=t(this);setTimeout(function(){e.find(\":ui-button\").button(\"refresh\")},1)},c=function(e){var i=e.name,s=e.form,n=t([]);return i&&(i=i.replace(/'/g,\"\\\\'\"),n=s?t(s).find(\"[name='\"+i+\"'][type=radio]\"):t(\"[name='\"+i+\"'][type=radio]\",e.ownerDocument).filter(function(){return!this.form})),n};t.widget(\"ui.button\",{version:\"1.11.4\",defaultElement:\"<button>\",options:{disabled:null,text:!0,label:null,icons:{primary:null,secondary:null}},_create:function(){this.element.closest(\"form\").unbind(\"reset\"+this.eventNamespace).bind(\"reset\"+this.eventNamespace,l),\"boolean\"!=typeof this.options.disabled?this.options.disabled=!!this.element.prop(\"disabled\"):this.element.prop(\"disabled\",this.options.disabled),this._determineButtonType(),this.hasTitle=!!this.buttonElement.attr(\"title\");var e=this,i=this.options,s=\"checkbox\"===this.type||\"radio\"===this.type,n=s?\"\":\"ui-state-active\";null===i.label&&(i.label=\"input\"===this.type?this.buttonElement.val():this.buttonElement.html()),this._hoverable(this.buttonElement),this.buttonElement.addClass(r).attr(\"role\",\"button\").bind(\"mouseenter\"+this.eventNamespace,function(){i.disabled||this===a&&t(this).addClass(\"ui-state-active\")}).bind(\"mouseleave\"+this.eventNamespace,function(){i.disabled||t(this).removeClass(n)}).bind(\"click\"+this.eventNamespace,function(t){i.disabled&&(t.preventDefault(),t.stopImmediatePropagation())}),this._on({focus:function(){this.buttonElement.addClass(\"ui-state-focus\")},blur:function(){this.buttonElement.removeClass(\"ui-state-focus\")}}),s&&this.element.bind(\"change\"+this.eventNamespace,function(){e.refresh()}),\"checkbox\"===this.type?this.buttonElement.bind(\"click\"+this.eventNamespace,function(){return i.disabled?!1:void 0}):\"radio\"===this.type?this.buttonElement.bind(\"click\"+this.eventNamespace,function(){if(i.disabled)return!1;t(this).addClass(\"ui-state-active\"),e.buttonElement.attr(\"aria-pressed\",\"true\");var s=e.element[0];c(s).not(s).map(function(){return t(this).button(\"widget\")[0]}).removeClass(\"ui-state-active\").attr(\"aria-pressed\",\"false\")}):(this.buttonElement.bind(\"mousedown\"+this.eventNamespace,function(){return i.disabled?!1:(t(this).addClass(\"ui-state-active\"),a=this,e.document.one(\"mouseup\",function(){a=null}),void 0)}).bind(\"mouseup\"+this.eventNamespace,function(){return i.disabled?!1:(t(this).removeClass(\"ui-state-active\"),void 0)}).bind(\"keydown\"+this.eventNamespace,function(e){return i.disabled?!1:((e.keyCode===t.ui.keyCode.SPACE||e.keyCode===t.ui.keyCode.ENTER)&&t(this).addClass(\"ui-state-active\"),void 0)}).bind(\"keyup\"+this.eventNamespace+\" blur\"+this.eventNamespace,function(){t(this).removeClass(\"ui-state-active\")}),this.buttonElement.is(\"a\")&&this.buttonElement.keyup(function(e){e.keyCode===t.ui.keyCode.SPACE&&t(this).click()})),this._setOption(\"disabled\",i.disabled),this._resetButton()},_determineButtonType:function(){var t,e,i;this.type=this.element.is(\"[type=checkbox]\")?\"checkbox\":this.element.is(\"[type=radio]\")?\"radio\":this.element.is(\"input\")?\"input\":\"button\",\"checkbox\"===this.type||\"radio\"===this.type?(t=this.element.parents().last(),e=\"label[for='\"+this.element.attr(\"id\")+\"']\",this.buttonElement=t.find(e),this.buttonElement.length||(t=t.length?t.siblings():this.element.siblings(),this.buttonElement=t.filter(e),this.buttonElement.length||(this.buttonElement=t.find(e))),this.element.addClass(\"ui-helper-hidden-accessible\"),i=this.element.is(\":checked\"),i&&this.buttonElement.addClass(\"ui-state-active\"),this.buttonElement.prop(\"aria-pressed\",i)):this.buttonElement=this.element},widget:function(){return this.buttonElement},_destroy:function(){this.element.removeClass(\"ui-helper-hidden-accessible\"),this.buttonElement.removeClass(r+\" ui-state-active \"+h).removeAttr(\"role\").removeAttr(\"aria-pressed\").html(this.buttonElement.find(\".ui-button-text\").html()),this.hasTitle||this.buttonElement.removeAttr(\"title\")},_setOption:function(t,e){return this._super(t,e),\"disabled\"===t?(this.widget().toggleClass(\"ui-state-disabled\",!!e),this.element.prop(\"disabled\",!!e),e&&(\"checkbox\"===this.type||\"radio\"===this.type?this.buttonElement.removeClass(\"ui-state-focus\"):this.buttonElement.removeClass(\"ui-state-focus ui-state-active\")),void 0):(this._resetButton(),void 0)},refresh:function(){var e=this.element.is(\"input, button\")?this.element.is(\":disabled\"):this.element.hasClass(\"ui-button-disabled\");e!==this.options.disabled&&this._setOption(\"disabled\",e),\"radio\"===this.type?c(this.element[0]).each(function(){t(this).is(\":checked\")?t(this).button(\"widget\").addClass(\"ui-state-active\").attr(\"aria-pressed\",\"true\"):t(this).button(\"widget\").removeClass(\"ui-state-active\").attr(\"aria-pressed\",\"false\")}):\"checkbox\"===this.type&&(this.element.is(\":checked\")?this.buttonElement.addClass(\"ui-state-active\").attr(\"aria-pressed\",\"true\"):this.buttonElement.removeClass(\"ui-state-active\").attr(\"aria-pressed\",\"false\"))},_resetButton:function(){if(\"input\"===this.type)return this.options.label&&this.element.val(this.options.label),void 0;var e=this.buttonElement.removeClass(h),i=t(\"<span></span>\",this.document[0]).addClass(\"ui-button-text\").html(this.options.label).appendTo(e.empty()).text(),s=this.options.icons,n=s.primary&&s.secondary,o=[];s.primary||s.secondary?(this.options.text&&o.push(\"ui-button-text-icon\"+(n?\"s\":s.primary?\"-primary\":\"-secondary\")),s.primary&&e.prepend(\"<span class='ui-button-icon-primary ui-icon \"+s.primary+\"'></span>\"),s.secondary&&e.append(\"<span class='ui-button-icon-secondary ui-icon \"+s.secondary+\"'></span>\"),this.options.text||(o.push(n?\"ui-button-icons-only\":\"ui-button-icon-only\"),this.hasTitle||e.attr(\"title\",t.trim(i)))):o.push(\"ui-button-text-only\"),e.addClass(o.join(\" \"))}}),t.widget(\"ui.buttonset\",{version:\"1.11.4\",options:{items:\"button, input[type=button], input[type=submit], input[type=reset], input[type=checkbox], input[type=radio], a, :data(ui-button)\"},_create:function(){this.element.addClass(\"ui-buttonset\")},_init:function(){this.refresh()},_setOption:function(t,e){\"disabled\"===t&&this.buttons.button(\"option\",t,e),this._super(t,e)},refresh:function(){var e=\"rtl\"===this.element.css(\"direction\"),i=this.element.find(this.options.items),s=i.filter(\":ui-button\");i.not(\":ui-button\").button(),s.button(\"refresh\"),this.buttons=i.map(function(){return t(this).button(\"widget\")[0]}).removeClass(\"ui-corner-all ui-corner-left ui-corner-right\").filter(\":first\").addClass(e?\"ui-corner-right\":\"ui-corner-left\").end().filter(\":last\").addClass(e?\"ui-corner-left\":\"ui-corner-right\").end().end()},_destroy:function(){this.element.removeClass(\"ui-buttonset\"),this.buttons.map(function(){return t(this).button(\"widget\")[0]}).removeClass(\"ui-corner-left ui-corner-right\").end().button(\"destroy\")}}),t.ui.button,t.widget(\"ui.dialog\",{version:\"1.11.4\",options:{appendTo:\"body\",autoOpen:!0,buttons:[],closeOnEscape:!0,closeText:\"Close\",dialogClass:\"\",draggable:!0,hide:null,height:\"auto\",maxHeight:null,maxWidth:null,minHeight:150,minWidth:150,modal:!1,position:{my:\"center\",at:\"center\",of:window,collision:\"fit\",using:function(e){var i=t(this).css(e).offset().top;0>i&&t(this).css(\"top\",e.top-i)}},resizable:!0,show:null,title:null,width:300,beforeClose:null,close:null,drag:null,dragStart:null,dragStop:null,focus:null,open:null,resize:null,resizeStart:null,resizeStop:null},sizeRelatedOptions:{buttons:!0,height:!0,maxHeight:!0,maxWidth:!0,minHeight:!0,minWidth:!0,width:!0},resizableRelatedOptions:{maxHeight:!0,maxWidth:!0,minHeight:!0,minWidth:!0},_create:function(){this.originalCss={display:this.element[0].style.display,width:this.element[0].style.width,minHeight:this.element[0].style.minHeight,maxHeight:this.element[0].style.maxHeight,height:this.element[0].style.height},this.originalPosition={parent:this.element.parent(),index:this.element.parent().children().index(this.element)},this.originalTitle=this.element.attr(\"title\"),this.options.title=this.options.title||this.originalTitle,this._createWrapper(),this.element.show().removeAttr(\"title\").addClass(\"ui-dialog-content ui-widget-content\").appendTo(this.uiDialog),this._createTitlebar(),this._createButtonPane(),this.options.draggable&&t.fn.draggable&&this._makeDraggable(),this.options.resizable&&t.fn.resizable&&this._makeResizable(),this._isOpen=!1,this._trackFocus()\n},_init:function(){this.options.autoOpen&&this.open()},_appendTo:function(){var e=this.options.appendTo;return e&&(e.jquery||e.nodeType)?t(e):this.document.find(e||\"body\").eq(0)},_destroy:function(){var t,e=this.originalPosition;this._untrackInstance(),this._destroyOverlay(),this.element.removeUniqueId().removeClass(\"ui-dialog-content ui-widget-content\").css(this.originalCss).detach(),this.uiDialog.stop(!0,!0).remove(),this.originalTitle&&this.element.attr(\"title\",this.originalTitle),t=e.parent.children().eq(e.index),t.length&&t[0]!==this.element[0]?t.before(this.element):e.parent.append(this.element)},widget:function(){return this.uiDialog},disable:t.noop,enable:t.noop,close:function(e){var i,s=this;if(this._isOpen&&this._trigger(\"beforeClose\",e)!==!1){if(this._isOpen=!1,this._focusedElement=null,this._destroyOverlay(),this._untrackInstance(),!this.opener.filter(\":focusable\").focus().length)try{i=this.document[0].activeElement,i&&\"body\"!==i.nodeName.toLowerCase()&&t(i).blur()}catch(n){}this._hide(this.uiDialog,this.options.hide,function(){s._trigger(\"close\",e)})}},isOpen:function(){return this._isOpen},moveToTop:function(){this._moveToTop()},_moveToTop:function(e,i){var s=!1,n=this.uiDialog.siblings(\".ui-front:visible\").map(function(){return+t(this).css(\"z-index\")}).get(),o=Math.max.apply(null,n);return o>=+this.uiDialog.css(\"z-index\")&&(this.uiDialog.css(\"z-index\",o+1),s=!0),s&&!i&&this._trigger(\"focus\",e),s},open:function(){var e=this;return this._isOpen?(this._moveToTop()&&this._focusTabbable(),void 0):(this._isOpen=!0,this.opener=t(this.document[0].activeElement),this._size(),this._position(),this._createOverlay(),this._moveToTop(null,!0),this.overlay&&this.overlay.css(\"z-index\",this.uiDialog.css(\"z-index\")-1),this._show(this.uiDialog,this.options.show,function(){e._focusTabbable(),e._trigger(\"focus\")}),this._makeFocusTarget(),this._trigger(\"open\"),void 0)},_focusTabbable:function(){var t=this._focusedElement;t||(t=this.element.find(\"[autofocus]\")),t.length||(t=this.element.find(\":tabbable\")),t.length||(t=this.uiDialogButtonPane.find(\":tabbable\")),t.length||(t=this.uiDialogTitlebarClose.filter(\":tabbable\")),t.length||(t=this.uiDialog),t.eq(0).focus()},_keepFocus:function(e){function i(){var e=this.document[0].activeElement,i=this.uiDialog[0]===e||t.contains(this.uiDialog[0],e);i||this._focusTabbable()}e.preventDefault(),i.call(this),this._delay(i)},_createWrapper:function(){this.uiDialog=t(\"<div>\").addClass(\"ui-dialog ui-widget ui-widget-content ui-corner-all ui-front \"+this.options.dialogClass).hide().attr({tabIndex:-1,role:\"dialog\"}).appendTo(this._appendTo()),this._on(this.uiDialog,{keydown:function(e){if(this.options.closeOnEscape&&!e.isDefaultPrevented()&&e.keyCode&&e.keyCode===t.ui.keyCode.ESCAPE)return e.preventDefault(),this.close(e),void 0;if(e.keyCode===t.ui.keyCode.TAB&&!e.isDefaultPrevented()){var i=this.uiDialog.find(\":tabbable\"),s=i.filter(\":first\"),n=i.filter(\":last\");e.target!==n[0]&&e.target!==this.uiDialog[0]||e.shiftKey?e.target!==s[0]&&e.target!==this.uiDialog[0]||!e.shiftKey||(this._delay(function(){n.focus()}),e.preventDefault()):(this._delay(function(){s.focus()}),e.preventDefault())}},mousedown:function(t){this._moveToTop(t)&&this._focusTabbable()}}),this.element.find(\"[aria-describedby]\").length||this.uiDialog.attr({\"aria-describedby\":this.element.uniqueId().attr(\"id\")})},_createTitlebar:function(){var e;this.uiDialogTitlebar=t(\"<div>\").addClass(\"ui-dialog-titlebar ui-widget-header ui-corner-all ui-helper-clearfix\").prependTo(this.uiDialog),this._on(this.uiDialogTitlebar,{mousedown:function(e){t(e.target).closest(\".ui-dialog-titlebar-close\")||this.uiDialog.focus()}}),this.uiDialogTitlebarClose=t(\"<button type='button'></button>\").button({label:this.options.closeText,icons:{primary:\"ui-icon-closethick\"},text:!1}).addClass(\"ui-dialog-titlebar-close\").appendTo(this.uiDialogTitlebar),this._on(this.uiDialogTitlebarClose,{click:function(t){t.preventDefault(),this.close(t)}}),e=t(\"<span>\").uniqueId().addClass(\"ui-dialog-title\").prependTo(this.uiDialogTitlebar),this._title(e),this.uiDialog.attr({\"aria-labelledby\":e.attr(\"id\")})},_title:function(t){this.options.title||t.html(\"&#160;\"),t.text(this.options.title)},_createButtonPane:function(){this.uiDialogButtonPane=t(\"<div>\").addClass(\"ui-dialog-buttonpane ui-widget-content ui-helper-clearfix\"),this.uiButtonSet=t(\"<div>\").addClass(\"ui-dialog-buttonset\").appendTo(this.uiDialogButtonPane),this._createButtons()},_createButtons:function(){var e=this,i=this.options.buttons;return this.uiDialogButtonPane.remove(),this.uiButtonSet.empty(),t.isEmptyObject(i)||t.isArray(i)&&!i.length?(this.uiDialog.removeClass(\"ui-dialog-buttons\"),void 0):(t.each(i,function(i,s){var n,o;s=t.isFunction(s)?{click:s,text:i}:s,s=t.extend({type:\"button\"},s),n=s.click,s.click=function(){n.apply(e.element[0],arguments)},o={icons:s.icons,text:s.showText},delete s.icons,delete s.showText,t(\"<button></button>\",s).button(o).appendTo(e.uiButtonSet)}),this.uiDialog.addClass(\"ui-dialog-buttons\"),this.uiDialogButtonPane.appendTo(this.uiDialog),void 0)},_makeDraggable:function(){function e(t){return{position:t.position,offset:t.offset}}var i=this,s=this.options;this.uiDialog.draggable({cancel:\".ui-dialog-content, .ui-dialog-titlebar-close\",handle:\".ui-dialog-titlebar\",containment:\"document\",start:function(s,n){t(this).addClass(\"ui-dialog-dragging\"),i._blockFrames(),i._trigger(\"dragStart\",s,e(n))},drag:function(t,s){i._trigger(\"drag\",t,e(s))},stop:function(n,o){var a=o.offset.left-i.document.scrollLeft(),r=o.offset.top-i.document.scrollTop();s.position={my:\"left top\",at:\"left\"+(a>=0?\"+\":\"\")+a+\" \"+\"top\"+(r>=0?\"+\":\"\")+r,of:i.window},t(this).removeClass(\"ui-dialog-dragging\"),i._unblockFrames(),i._trigger(\"dragStop\",n,e(o))}})},_makeResizable:function(){function e(t){return{originalPosition:t.originalPosition,originalSize:t.originalSize,position:t.position,size:t.size}}var i=this,s=this.options,n=s.resizable,o=this.uiDialog.css(\"position\"),a=\"string\"==typeof n?n:\"n,e,s,w,se,sw,ne,nw\";this.uiDialog.resizable({cancel:\".ui-dialog-content\",containment:\"document\",alsoResize:this.element,maxWidth:s.maxWidth,maxHeight:s.maxHeight,minWidth:s.minWidth,minHeight:this._minHeight(),handles:a,start:function(s,n){t(this).addClass(\"ui-dialog-resizing\"),i._blockFrames(),i._trigger(\"resizeStart\",s,e(n))},resize:function(t,s){i._trigger(\"resize\",t,e(s))},stop:function(n,o){var a=i.uiDialog.offset(),r=a.left-i.document.scrollLeft(),h=a.top-i.document.scrollTop();s.height=i.uiDialog.height(),s.width=i.uiDialog.width(),s.position={my:\"left top\",at:\"left\"+(r>=0?\"+\":\"\")+r+\" \"+\"top\"+(h>=0?\"+\":\"\")+h,of:i.window},t(this).removeClass(\"ui-dialog-resizing\"),i._unblockFrames(),i._trigger(\"resizeStop\",n,e(o))}}).css(\"position\",o)},_trackFocus:function(){this._on(this.widget(),{focusin:function(e){this._makeFocusTarget(),this._focusedElement=t(e.target)}})},_makeFocusTarget:function(){this._untrackInstance(),this._trackingInstances().unshift(this)},_untrackInstance:function(){var e=this._trackingInstances(),i=t.inArray(this,e);-1!==i&&e.splice(i,1)},_trackingInstances:function(){var t=this.document.data(\"ui-dialog-instances\");return t||(t=[],this.document.data(\"ui-dialog-instances\",t)),t},_minHeight:function(){var t=this.options;return\"auto\"===t.height?t.minHeight:Math.min(t.minHeight,t.height)},_position:function(){var t=this.uiDialog.is(\":visible\");t||this.uiDialog.show(),this.uiDialog.position(this.options.position),t||this.uiDialog.hide()},_setOptions:function(e){var i=this,s=!1,n={};t.each(e,function(t,e){i._setOption(t,e),t in i.sizeRelatedOptions&&(s=!0),t in i.resizableRelatedOptions&&(n[t]=e)}),s&&(this._size(),this._position()),this.uiDialog.is(\":data(ui-resizable)\")&&this.uiDialog.resizable(\"option\",n)},_setOption:function(t,e){var i,s,n=this.uiDialog;\"dialogClass\"===t&&n.removeClass(this.options.dialogClass).addClass(e),\"disabled\"!==t&&(this._super(t,e),\"appendTo\"===t&&this.uiDialog.appendTo(this._appendTo()),\"buttons\"===t&&this._createButtons(),\"closeText\"===t&&this.uiDialogTitlebarClose.button({label:\"\"+e}),\"draggable\"===t&&(i=n.is(\":data(ui-draggable)\"),i&&!e&&n.draggable(\"destroy\"),!i&&e&&this._makeDraggable()),\"position\"===t&&this._position(),\"resizable\"===t&&(s=n.is(\":data(ui-resizable)\"),s&&!e&&n.resizable(\"destroy\"),s&&\"string\"==typeof e&&n.resizable(\"option\",\"handles\",e),s||e===!1||this._makeResizable()),\"title\"===t&&this._title(this.uiDialogTitlebar.find(\".ui-dialog-title\")))},_size:function(){var t,e,i,s=this.options;this.element.show().css({width:\"auto\",minHeight:0,maxHeight:\"none\",height:0}),s.minWidth>s.width&&(s.width=s.minWidth),t=this.uiDialog.css({height:\"auto\",width:s.width}).outerHeight(),e=Math.max(0,s.minHeight-t),i=\"number\"==typeof s.maxHeight?Math.max(0,s.maxHeight-t):\"none\",\"auto\"===s.height?this.element.css({minHeight:e,maxHeight:i,height:\"auto\"}):this.element.height(Math.max(0,s.height-t)),this.uiDialog.is(\":data(ui-resizable)\")&&this.uiDialog.resizable(\"option\",\"minHeight\",this._minHeight())},_blockFrames:function(){this.iframeBlocks=this.document.find(\"iframe\").map(function(){var e=t(this);return t(\"<div>\").css({position:\"absolute\",width:e.outerWidth(),height:e.outerHeight()}).appendTo(e.parent()).offset(e.offset())[0]})},_unblockFrames:function(){this.iframeBlocks&&(this.iframeBlocks.remove(),delete this.iframeBlocks)},_allowInteraction:function(e){return t(e.target).closest(\".ui-dialog\").length?!0:!!t(e.target).closest(\".ui-datepicker\").length},_createOverlay:function(){if(this.options.modal){var e=!0;this._delay(function(){e=!1}),this.document.data(\"ui-dialog-overlays\")||this._on(this.document,{focusin:function(t){e||this._allowInteraction(t)||(t.preventDefault(),this._trackingInstances()[0]._focusTabbable())}}),this.overlay=t(\"<div>\").addClass(\"ui-widget-overlay ui-front\").appendTo(this._appendTo()),this._on(this.overlay,{mousedown:\"_keepFocus\"}),this.document.data(\"ui-dialog-overlays\",(this.document.data(\"ui-dialog-overlays\")||0)+1)}},_destroyOverlay:function(){if(this.options.modal&&this.overlay){var t=this.document.data(\"ui-dialog-overlays\")-1;t?this.document.data(\"ui-dialog-overlays\",t):this.document.unbind(\"focusin\").removeData(\"ui-dialog-overlays\"),this.overlay.remove(),this.overlay=null}}}),t.widget(\"ui.menu\",{version:\"1.11.4\",defaultElement:\"<ul>\",delay:300,options:{icons:{submenu:\"ui-icon-carat-1-e\"},items:\"> *\",menus:\"ul\",position:{my:\"left-1 top\",at:\"right top\"},role:\"menu\",blur:null,focus:null,select:null},_create:function(){this.activeMenu=this.element,this.mouseHandled=!1,this.element.uniqueId().addClass(\"ui-menu ui-widget ui-widget-content\").toggleClass(\"ui-menu-icons\",!!this.element.find(\".ui-icon\").length).attr({role:this.options.role,tabIndex:0}),this.options.disabled&&this.element.addClass(\"ui-state-disabled\").attr(\"aria-disabled\",\"true\"),this._on({\"mousedown .ui-menu-item\":function(t){t.preventDefault()},\"click .ui-menu-item\":function(e){var i=t(e.target);!this.mouseHandled&&i.not(\".ui-state-disabled\").length&&(this.select(e),e.isPropagationStopped()||(this.mouseHandled=!0),i.has(\".ui-menu\").length?this.expand(e):!this.element.is(\":focus\")&&t(this.document[0].activeElement).closest(\".ui-menu\").length&&(this.element.trigger(\"focus\",[!0]),this.active&&1===this.active.parents(\".ui-menu\").length&&clearTimeout(this.timer)))},\"mouseenter .ui-menu-item\":function(e){if(!this.previousFilter){var i=t(e.currentTarget);i.siblings(\".ui-state-active\").removeClass(\"ui-state-active\"),this.focus(e,i)}},mouseleave:\"collapseAll\",\"mouseleave .ui-menu\":\"collapseAll\",focus:function(t,e){var i=this.active||this.element.find(this.options.items).eq(0);e||this.focus(t,i)},blur:function(e){this._delay(function(){t.contains(this.element[0],this.document[0].activeElement)||this.collapseAll(e)})},keydown:\"_keydown\"}),this.refresh(),this._on(this.document,{click:function(t){this._closeOnDocumentClick(t)&&this.collapseAll(t),this.mouseHandled=!1}})},_destroy:function(){this.element.removeAttr(\"aria-activedescendant\").find(\".ui-menu\").addBack().removeClass(\"ui-menu ui-widget ui-widget-content ui-menu-icons ui-front\").removeAttr(\"role\").removeAttr(\"tabIndex\").removeAttr(\"aria-labelledby\").removeAttr(\"aria-expanded\").removeAttr(\"aria-hidden\").removeAttr(\"aria-disabled\").removeUniqueId().show(),this.element.find(\".ui-menu-item\").removeClass(\"ui-menu-item\").removeAttr(\"role\").removeAttr(\"aria-disabled\").removeUniqueId().removeClass(\"ui-state-hover\").removeAttr(\"tabIndex\").removeAttr(\"role\").removeAttr(\"aria-haspopup\").children().each(function(){var e=t(this);e.data(\"ui-menu-submenu-carat\")&&e.remove()}),this.element.find(\".ui-menu-divider\").removeClass(\"ui-menu-divider ui-widget-content\")},_keydown:function(e){var i,s,n,o,a=!0;switch(e.keyCode){case t.ui.keyCode.PAGE_UP:this.previousPage(e);break;case t.ui.keyCode.PAGE_DOWN:this.nextPage(e);break;case t.ui.keyCode.HOME:this._move(\"first\",\"first\",e);break;case t.ui.keyCode.END:this._move(\"last\",\"last\",e);break;case t.ui.keyCode.UP:this.previous(e);break;case t.ui.keyCode.DOWN:this.next(e);break;case t.ui.keyCode.LEFT:this.collapse(e);break;case t.ui.keyCode.RIGHT:this.active&&!this.active.is(\".ui-state-disabled\")&&this.expand(e);break;case t.ui.keyCode.ENTER:case t.ui.keyCode.SPACE:this._activate(e);break;case t.ui.keyCode.ESCAPE:this.collapse(e);break;default:a=!1,s=this.previousFilter||\"\",n=String.fromCharCode(e.keyCode),o=!1,clearTimeout(this.filterTimer),n===s?o=!0:n=s+n,i=this._filterMenuItems(n),i=o&&-1!==i.index(this.active.next())?this.active.nextAll(\".ui-menu-item\"):i,i.length||(n=String.fromCharCode(e.keyCode),i=this._filterMenuItems(n)),i.length?(this.focus(e,i),this.previousFilter=n,this.filterTimer=this._delay(function(){delete this.previousFilter},1e3)):delete this.previousFilter}a&&e.preventDefault()},_activate:function(t){this.active.is(\".ui-state-disabled\")||(this.active.is(\"[aria-haspopup='true']\")?this.expand(t):this.select(t))},refresh:function(){var e,i,s=this,n=this.options.icons.submenu,o=this.element.find(this.options.menus);this.element.toggleClass(\"ui-menu-icons\",!!this.element.find(\".ui-icon\").length),o.filter(\":not(.ui-menu)\").addClass(\"ui-menu ui-widget ui-widget-content ui-front\").hide().attr({role:this.options.role,\"aria-hidden\":\"true\",\"aria-expanded\":\"false\"}).each(function(){var e=t(this),i=e.parent(),s=t(\"<span>\").addClass(\"ui-menu-icon ui-icon \"+n).data(\"ui-menu-submenu-carat\",!0);i.attr(\"aria-haspopup\",\"true\").prepend(s),e.attr(\"aria-labelledby\",i.attr(\"id\"))}),e=o.add(this.element),i=e.find(this.options.items),i.not(\".ui-menu-item\").each(function(){var e=t(this);s._isDivider(e)&&e.addClass(\"ui-widget-content ui-menu-divider\")}),i.not(\".ui-menu-item, .ui-menu-divider\").addClass(\"ui-menu-item\").uniqueId().attr({tabIndex:-1,role:this._itemRole()}),i.filter(\".ui-state-disabled\").attr(\"aria-disabled\",\"true\"),this.active&&!t.contains(this.element[0],this.active[0])&&this.blur()},_itemRole:function(){return{menu:\"menuitem\",listbox:\"option\"}[this.options.role]},_setOption:function(t,e){\"icons\"===t&&this.element.find(\".ui-menu-icon\").removeClass(this.options.icons.submenu).addClass(e.submenu),\"disabled\"===t&&this.element.toggleClass(\"ui-state-disabled\",!!e).attr(\"aria-disabled\",e),this._super(t,e)},focus:function(t,e){var i,s;this.blur(t,t&&\"focus\"===t.type),this._scrollIntoView(e),this.active=e.first(),s=this.active.addClass(\"ui-state-focus\").removeClass(\"ui-state-active\"),this.options.role&&this.element.attr(\"aria-activedescendant\",s.attr(\"id\")),this.active.parent().closest(\".ui-menu-item\").addClass(\"ui-state-active\"),t&&\"keydown\"===t.type?this._close():this.timer=this._delay(function(){this._close()},this.delay),i=e.children(\".ui-menu\"),i.length&&t&&/^mouse/.test(t.type)&&this._startOpening(i),this.activeMenu=e.parent(),this._trigger(\"focus\",t,{item:e})},_scrollIntoView:function(e){var i,s,n,o,a,r;this._hasScroll()&&(i=parseFloat(t.css(this.activeMenu[0],\"borderTopWidth\"))||0,s=parseFloat(t.css(this.activeMenu[0],\"paddingTop\"))||0,n=e.offset().top-this.activeMenu.offset().top-i-s,o=this.activeMenu.scrollTop(),a=this.activeMenu.height(),r=e.outerHeight(),0>n?this.activeMenu.scrollTop(o+n):n+r>a&&this.activeMenu.scrollTop(o+n-a+r))},blur:function(t,e){e||clearTimeout(this.timer),this.active&&(this.active.removeClass(\"ui-state-focus\"),this.active=null,this._trigger(\"blur\",t,{item:this.active}))},_startOpening:function(t){clearTimeout(this.timer),\"true\"===t.attr(\"aria-hidden\")&&(this.timer=this._delay(function(){this._close(),this._open(t)},this.delay))},_open:function(e){var i=t.extend({of:this.active},this.options.position);clearTimeout(this.timer),this.element.find(\".ui-menu\").not(e.parents(\".ui-menu\")).hide().attr(\"aria-hidden\",\"true\"),e.show().removeAttr(\"aria-hidden\").attr(\"aria-expanded\",\"true\").position(i)},collapseAll:function(e,i){clearTimeout(this.timer),this.timer=this._delay(function(){var s=i?this.element:t(e&&e.target).closest(this.element.find(\".ui-menu\"));s.length||(s=this.element),this._close(s),this.blur(e),this.activeMenu=s},this.delay)},_close:function(t){t||(t=this.active?this.active.parent():this.element),t.find(\".ui-menu\").hide().attr(\"aria-hidden\",\"true\").attr(\"aria-expanded\",\"false\").end().find(\".ui-state-active\").not(\".ui-state-focus\").removeClass(\"ui-state-active\")},_closeOnDocumentClick:function(e){return!t(e.target).closest(\".ui-menu\").length},_isDivider:function(t){return!/[^\\-\\u2014\\u2013\\s]/.test(t.text())},collapse:function(t){var e=this.active&&this.active.parent().closest(\".ui-menu-item\",this.element);e&&e.length&&(this._close(),this.focus(t,e))},expand:function(t){var e=this.active&&this.active.children(\".ui-menu \").find(this.options.items).first();e&&e.length&&(this._open(e.parent()),this._delay(function(){this.focus(t,e)}))},next:function(t){this._move(\"next\",\"first\",t)},previous:function(t){this._move(\"prev\",\"last\",t)},isFirstItem:function(){return this.active&&!this.active.prevAll(\".ui-menu-item\").length},isLastItem:function(){return this.active&&!this.active.nextAll(\".ui-menu-item\").length},_move:function(t,e,i){var s;this.active&&(s=\"first\"===t||\"last\"===t?this.active[\"first\"===t?\"prevAll\":\"nextAll\"](\".ui-menu-item\").eq(-1):this.active[t+\"All\"](\".ui-menu-item\").eq(0)),s&&s.length&&this.active||(s=this.activeMenu.find(this.options.items)[e]()),this.focus(i,s)},nextPage:function(e){var i,s,n;return this.active?(this.isLastItem()||(this._hasScroll()?(s=this.active.offset().top,n=this.element.height(),this.active.nextAll(\".ui-menu-item\").each(function(){return i=t(this),0>i.offset().top-s-n}),this.focus(e,i)):this.focus(e,this.activeMenu.find(this.options.items)[this.active?\"last\":\"first\"]())),void 0):(this.next(e),void 0)},previousPage:function(e){var i,s,n;return this.active?(this.isFirstItem()||(this._hasScroll()?(s=this.active.offset().top,n=this.element.height(),this.active.prevAll(\".ui-menu-item\").each(function(){return i=t(this),i.offset().top-s+n>0}),this.focus(e,i)):this.focus(e,this.activeMenu.find(this.options.items).first())),void 0):(this.next(e),void 0)},_hasScroll:function(){return this.element.outerHeight()<this.element.prop(\"scrollHeight\")},select:function(e){this.active=this.active||t(e.target).closest(\".ui-menu-item\");var i={item:this.active};this.active.has(\".ui-menu\").length||this.collapseAll(e,!0),this._trigger(\"select\",e,i)},_filterMenuItems:function(e){var i=e.replace(/[\\-\\[\\]{}()*+?.,\\\\\\^$|#\\s]/g,\"\\\\$&\"),s=RegExp(\"^\"+i,\"i\");return this.activeMenu.find(this.options.items).filter(\".ui-menu-item\").filter(function(){return s.test(t.trim(t(this).text()))})}}),t.widget(\"ui.progressbar\",{version:\"1.11.4\",options:{max:100,value:0,change:null,complete:null},min:0,_create:function(){this.oldValue=this.options.value=this._constrainedValue(),this.element.addClass(\"ui-progressbar ui-widget ui-widget-content ui-corner-all\").attr({role:\"progressbar\",\"aria-valuemin\":this.min}),this.valueDiv=t(\"<div class='ui-progressbar-value ui-widget-header ui-corner-left'></div>\").appendTo(this.element),this._refreshValue()},_destroy:function(){this.element.removeClass(\"ui-progressbar ui-widget ui-widget-content ui-corner-all\").removeAttr(\"role\").removeAttr(\"aria-valuemin\").removeAttr(\"aria-valuemax\").removeAttr(\"aria-valuenow\"),this.valueDiv.remove()},value:function(t){return void 0===t?this.options.value:(this.options.value=this._constrainedValue(t),this._refreshValue(),void 0)},_constrainedValue:function(t){return void 0===t&&(t=this.options.value),this.indeterminate=t===!1,\"number\"!=typeof t&&(t=0),this.indeterminate?!1:Math.min(this.options.max,Math.max(this.min,t))},_setOptions:function(t){var e=t.value;delete t.value,this._super(t),this.options.value=this._constrainedValue(e),this._refreshValue()},_setOption:function(t,e){\"max\"===t&&(e=Math.max(this.min,e)),\"disabled\"===t&&this.element.toggleClass(\"ui-state-disabled\",!!e).attr(\"aria-disabled\",e),this._super(t,e)},_percentage:function(){return this.indeterminate?100:100*(this.options.value-this.min)/(this.options.max-this.min)},_refreshValue:function(){var e=this.options.value,i=this._percentage();this.valueDiv.toggle(this.indeterminate||e>this.min).toggleClass(\"ui-corner-right\",e===this.options.max).width(i.toFixed(0)+\"%\"),this.element.toggleClass(\"ui-progressbar-indeterminate\",this.indeterminate),this.indeterminate?(this.element.removeAttr(\"aria-valuenow\"),this.overlayDiv||(this.overlayDiv=t(\"<div class='ui-progressbar-overlay'></div>\").appendTo(this.valueDiv))):(this.element.attr({\"aria-valuemax\":this.options.max,\"aria-valuenow\":e}),this.overlayDiv&&(this.overlayDiv.remove(),this.overlayDiv=null)),this.oldValue!==e&&(this.oldValue=e,this._trigger(\"change\")),e===this.options.max&&this._trigger(\"complete\")}}),t.widget(\"ui.slider\",t.ui.mouse,{version:\"1.11.4\",widgetEventPrefix:\"slide\",options:{animate:!1,distance:0,max:100,min:0,orientation:\"horizontal\",range:!1,step:1,value:0,values:null,change:null,slide:null,start:null,stop:null},numPages:5,_create:function(){this._keySliding=!1,this._mouseSliding=!1,this._animateOff=!0,this._handleIndex=null,this._detectOrientation(),this._mouseInit(),this._calculateNewMax(),this.element.addClass(\"ui-slider ui-slider-\"+this.orientation+\" ui-widget\"+\" ui-widget-content\"+\" ui-corner-all\"),this._refresh(),this._setOption(\"disabled\",this.options.disabled),this._animateOff=!1},_refresh:function(){this._createRange(),this._createHandles(),this._setupEvents(),this._refreshValue()},_createHandles:function(){var e,i,s=this.options,n=this.element.find(\".ui-slider-handle\").addClass(\"ui-state-default ui-corner-all\"),o=\"<span class='ui-slider-handle ui-state-default ui-corner-all' tabindex='0'></span>\",a=[];for(i=s.values&&s.values.length||1,n.length>i&&(n.slice(i).remove(),n=n.slice(0,i)),e=n.length;i>e;e++)a.push(o);this.handles=n.add(t(a.join(\"\")).appendTo(this.element)),this.handle=this.handles.eq(0),this.handles.each(function(e){t(this).data(\"ui-slider-handle-index\",e)})},_createRange:function(){var e=this.options,i=\"\";e.range?(e.range===!0&&(e.values?e.values.length&&2!==e.values.length?e.values=[e.values[0],e.values[0]]:t.isArray(e.values)&&(e.values=e.values.slice(0)):e.values=[this._valueMin(),this._valueMin()]),this.range&&this.range.length?this.range.removeClass(\"ui-slider-range-min ui-slider-range-max\").css({left:\"\",bottom:\"\"}):(this.range=t(\"<div></div>\").appendTo(this.element),i=\"ui-slider-range ui-widget-header ui-corner-all\"),this.range.addClass(i+(\"min\"===e.range||\"max\"===e.range?\" ui-slider-range-\"+e.range:\"\"))):(this.range&&this.range.remove(),this.range=null)},_setupEvents:function(){this._off(this.handles),this._on(this.handles,this._handleEvents),this._hoverable(this.handles),this._focusable(this.handles)},_destroy:function(){this.handles.remove(),this.range&&this.range.remove(),this.element.removeClass(\"ui-slider ui-slider-horizontal ui-slider-vertical ui-widget ui-widget-content ui-corner-all\"),this._mouseDestroy()},_mouseCapture:function(e){var i,s,n,o,a,r,h,l,c=this,u=this.options;return u.disabled?!1:(this.elementSize={width:this.element.outerWidth(),height:this.element.outerHeight()},this.elementOffset=this.element.offset(),i={x:e.pageX,y:e.pageY},s=this._normValueFromMouse(i),n=this._valueMax()-this._valueMin()+1,this.handles.each(function(e){var i=Math.abs(s-c.values(e));(n>i||n===i&&(e===c._lastChangedValue||c.values(e)===u.min))&&(n=i,o=t(this),a=e)}),r=this._start(e,a),r===!1?!1:(this._mouseSliding=!0,this._handleIndex=a,o.addClass(\"ui-state-active\").focus(),h=o.offset(),l=!t(e.target).parents().addBack().is(\".ui-slider-handle\"),this._clickOffset=l?{left:0,top:0}:{left:e.pageX-h.left-o.width()/2,top:e.pageY-h.top-o.height()/2-(parseInt(o.css(\"borderTopWidth\"),10)||0)-(parseInt(o.css(\"borderBottomWidth\"),10)||0)+(parseInt(o.css(\"marginTop\"),10)||0)},this.handles.hasClass(\"ui-state-hover\")||this._slide(e,a,s),this._animateOff=!0,!0))},_mouseStart:function(){return!0},_mouseDrag:function(t){var e={x:t.pageX,y:t.pageY},i=this._normValueFromMouse(e);return this._slide(t,this._handleIndex,i),!1},_mouseStop:function(t){return this.handles.removeClass(\"ui-state-active\"),this._mouseSliding=!1,this._stop(t,this._handleIndex),this._change(t,this._handleIndex),this._handleIndex=null,this._clickOffset=null,this._animateOff=!1,!1},_detectOrientation:function(){this.orientation=\"vertical\"===this.options.orientation?\"vertical\":\"horizontal\"},_normValueFromMouse:function(t){var e,i,s,n,o;return\"horizontal\"===this.orientation?(e=this.elementSize.width,i=t.x-this.elementOffset.left-(this._clickOffset?this._clickOffset.left:0)):(e=this.elementSize.height,i=t.y-this.elementOffset.top-(this._clickOffset?this._clickOffset.top:0)),s=i/e,s>1&&(s=1),0>s&&(s=0),\"vertical\"===this.orientation&&(s=1-s),n=this._valueMax()-this._valueMin(),o=this._valueMin()+s*n,this._trimAlignValue(o)},_start:function(t,e){var i={handle:this.handles[e],value:this.value()};return this.options.values&&this.options.values.length&&(i.value=this.values(e),i.values=this.values()),this._trigger(\"start\",t,i)},_slide:function(t,e,i){var s,n,o;this.options.values&&this.options.values.length?(s=this.values(e?0:1),2===this.options.values.length&&this.options.range===!0&&(0===e&&i>s||1===e&&s>i)&&(i=s),i!==this.values(e)&&(n=this.values(),n[e]=i,o=this._trigger(\"slide\",t,{handle:this.handles[e],value:i,values:n}),s=this.values(e?0:1),o!==!1&&this.values(e,i))):i!==this.value()&&(o=this._trigger(\"slide\",t,{handle:this.handles[e],value:i}),o!==!1&&this.value(i))},_stop:function(t,e){var i={handle:this.handles[e],value:this.value()};this.options.values&&this.options.values.length&&(i.value=this.values(e),i.values=this.values()),this._trigger(\"stop\",t,i)},_change:function(t,e){if(!this._keySliding&&!this._mouseSliding){var i={handle:this.handles[e],value:this.value()};this.options.values&&this.options.values.length&&(i.value=this.values(e),i.values=this.values()),this._lastChangedValue=e,this._trigger(\"change\",t,i)}},value:function(t){return arguments.length?(this.options.value=this._trimAlignValue(t),this._refreshValue(),this._change(null,0),void 0):this._value()},values:function(e,i){var s,n,o;if(arguments.length>1)return this.options.values[e]=this._trimAlignValue(i),this._refreshValue(),this._change(null,e),void 0;if(!arguments.length)return this._values();if(!t.isArray(arguments[0]))return this.options.values&&this.options.values.length?this._values(e):this.value();for(s=this.options.values,n=arguments[0],o=0;s.length>o;o+=1)s[o]=this._trimAlignValue(n[o]),this._change(null,o);this._refreshValue()},_setOption:function(e,i){var s,n=0;switch(\"range\"===e&&this.options.range===!0&&(\"min\"===i?(this.options.value=this._values(0),this.options.values=null):\"max\"===i&&(this.options.value=this._values(this.options.values.length-1),this.options.values=null)),t.isArray(this.options.values)&&(n=this.options.values.length),\"disabled\"===e&&this.element.toggleClass(\"ui-state-disabled\",!!i),this._super(e,i),e){case\"orientation\":this._detectOrientation(),this.element.removeClass(\"ui-slider-horizontal ui-slider-vertical\").addClass(\"ui-slider-\"+this.orientation),this._refreshValue(),this.handles.css(\"horizontal\"===i?\"bottom\":\"left\",\"\");break;case\"value\":this._animateOff=!0,this._refreshValue(),this._change(null,0),this._animateOff=!1;break;case\"values\":for(this._animateOff=!0,this._refreshValue(),s=0;n>s;s+=1)this._change(null,s);this._animateOff=!1;break;case\"step\":case\"min\":case\"max\":this._animateOff=!0,this._calculateNewMax(),this._refreshValue(),this._animateOff=!1;break;case\"range\":this._animateOff=!0,this._refresh(),this._animateOff=!1}},_value:function(){var t=this.options.value;return t=this._trimAlignValue(t)},_values:function(t){var e,i,s;if(arguments.length)return e=this.options.values[t],e=this._trimAlignValue(e);if(this.options.values&&this.options.values.length){for(i=this.options.values.slice(),s=0;i.length>s;s+=1)i[s]=this._trimAlignValue(i[s]);return i}return[]},_trimAlignValue:function(t){if(this._valueMin()>=t)return this._valueMin();if(t>=this._valueMax())return this._valueMax();var e=this.options.step>0?this.options.step:1,i=(t-this._valueMin())%e,s=t-i;return 2*Math.abs(i)>=e&&(s+=i>0?e:-e),parseFloat(s.toFixed(5))},_calculateNewMax:function(){var t=this.options.max,e=this._valueMin(),i=this.options.step,s=Math.floor(+(t-e).toFixed(this._precision())/i)*i;t=s+e,this.max=parseFloat(t.toFixed(this._precision()))},_precision:function(){var t=this._precisionOf(this.options.step);return null!==this.options.min&&(t=Math.max(t,this._precisionOf(this.options.min))),t},_precisionOf:function(t){var e=\"\"+t,i=e.indexOf(\".\");return-1===i?0:e.length-i-1},_valueMin:function(){return this.options.min},_valueMax:function(){return this.max},_refreshValue:function(){var e,i,s,n,o,a=this.options.range,r=this.options,h=this,l=this._animateOff?!1:r.animate,c={};this.options.values&&this.options.values.length?this.handles.each(function(s){i=100*((h.values(s)-h._valueMin())/(h._valueMax()-h._valueMin())),c[\"horizontal\"===h.orientation?\"left\":\"bottom\"]=i+\"%\",t(this).stop(1,1)[l?\"animate\":\"css\"](c,r.animate),h.options.range===!0&&(\"horizontal\"===h.orientation?(0===s&&h.range.stop(1,1)[l?\"animate\":\"css\"]({left:i+\"%\"},r.animate),1===s&&h.range[l?\"animate\":\"css\"]({width:i-e+\"%\"},{queue:!1,duration:r.animate})):(0===s&&h.range.stop(1,1)[l?\"animate\":\"css\"]({bottom:i+\"%\"},r.animate),1===s&&h.range[l?\"animate\":\"css\"]({height:i-e+\"%\"},{queue:!1,duration:r.animate}))),e=i}):(s=this.value(),n=this._valueMin(),o=this._valueMax(),i=o!==n?100*((s-n)/(o-n)):0,c[\"horizontal\"===this.orientation?\"left\":\"bottom\"]=i+\"%\",this.handle.stop(1,1)[l?\"animate\":\"css\"](c,r.animate),\"min\"===a&&\"horizontal\"===this.orientation&&this.range.stop(1,1)[l?\"animate\":\"css\"]({width:i+\"%\"},r.animate),\"max\"===a&&\"horizontal\"===this.orientation&&this.range[l?\"animate\":\"css\"]({width:100-i+\"%\"},{queue:!1,duration:r.animate}),\"min\"===a&&\"vertical\"===this.orientation&&this.range.stop(1,1)[l?\"animate\":\"css\"]({height:i+\"%\"},r.animate),\"max\"===a&&\"vertical\"===this.orientation&&this.range[l?\"animate\":\"css\"]({height:100-i+\"%\"},{queue:!1,duration:r.animate}))},_handleEvents:{keydown:function(e){var i,s,n,o,a=t(e.target).data(\"ui-slider-handle-index\");switch(e.keyCode){case t.ui.keyCode.HOME:case t.ui.keyCode.END:case t.ui.keyCode.PAGE_UP:case t.ui.keyCode.PAGE_DOWN:case t.ui.keyCode.UP:case t.ui.keyCode.RIGHT:case t.ui.keyCode.DOWN:case t.ui.keyCode.LEFT:if(e.preventDefault(),!this._keySliding&&(this._keySliding=!0,t(e.target).addClass(\"ui-state-active\"),i=this._start(e,a),i===!1))return}switch(o=this.options.step,s=n=this.options.values&&this.options.values.length?this.values(a):this.value(),e.keyCode){case t.ui.keyCode.HOME:n=this._valueMin();break;case t.ui.keyCode.END:n=this._valueMax();break;case t.ui.keyCode.PAGE_UP:n=this._trimAlignValue(s+(this._valueMax()-this._valueMin())/this.numPages);break;case t.ui.keyCode.PAGE_DOWN:n=this._trimAlignValue(s-(this._valueMax()-this._valueMin())/this.numPages);break;case t.ui.keyCode.UP:case t.ui.keyCode.RIGHT:if(s===this._valueMax())return;n=this._trimAlignValue(s+o);break;case t.ui.keyCode.DOWN:case t.ui.keyCode.LEFT:if(s===this._valueMin())return;\nn=this._trimAlignValue(s-o)}this._slide(e,a,n)},keyup:function(e){var i=t(e.target).data(\"ui-slider-handle-index\");this._keySliding&&(this._keySliding=!1,this._stop(e,i),this._change(e,i),t(e.target).removeClass(\"ui-state-active\"))}}}),t.widget(\"ui.tabs\",{version:\"1.11.4\",delay:300,options:{active:null,collapsible:!1,event:\"click\",heightStyle:\"content\",hide:null,show:null,activate:null,beforeActivate:null,beforeLoad:null,load:null},_isLocal:function(){var t=/#.*$/;return function(e){var i,s;e=e.cloneNode(!1),i=e.href.replace(t,\"\"),s=location.href.replace(t,\"\");try{i=decodeURIComponent(i)}catch(n){}try{s=decodeURIComponent(s)}catch(n){}return e.hash.length>1&&i===s}}(),_create:function(){var e=this,i=this.options;this.running=!1,this.element.addClass(\"ui-tabs ui-widget ui-widget-content ui-corner-all\").toggleClass(\"ui-tabs-collapsible\",i.collapsible),this._processTabs(),i.active=this._initialActive(),t.isArray(i.disabled)&&(i.disabled=t.unique(i.disabled.concat(t.map(this.tabs.filter(\".ui-state-disabled\"),function(t){return e.tabs.index(t)}))).sort()),this.active=this.options.active!==!1&&this.anchors.length?this._findActive(i.active):t(),this._refresh(),this.active.length&&this.load(i.active)},_initialActive:function(){var e=this.options.active,i=this.options.collapsible,s=location.hash.substring(1);return null===e&&(s&&this.tabs.each(function(i,n){return t(n).attr(\"aria-controls\")===s?(e=i,!1):void 0}),null===e&&(e=this.tabs.index(this.tabs.filter(\".ui-tabs-active\"))),(null===e||-1===e)&&(e=this.tabs.length?0:!1)),e!==!1&&(e=this.tabs.index(this.tabs.eq(e)),-1===e&&(e=i?!1:0)),!i&&e===!1&&this.anchors.length&&(e=0),e},_getCreateEventData:function(){return{tab:this.active,panel:this.active.length?this._getPanelForTab(this.active):t()}},_tabKeydown:function(e){var i=t(this.document[0].activeElement).closest(\"li\"),s=this.tabs.index(i),n=!0;if(!this._handlePageNav(e)){switch(e.keyCode){case t.ui.keyCode.RIGHT:case t.ui.keyCode.DOWN:s++;break;case t.ui.keyCode.UP:case t.ui.keyCode.LEFT:n=!1,s--;break;case t.ui.keyCode.END:s=this.anchors.length-1;break;case t.ui.keyCode.HOME:s=0;break;case t.ui.keyCode.SPACE:return e.preventDefault(),clearTimeout(this.activating),this._activate(s),void 0;case t.ui.keyCode.ENTER:return e.preventDefault(),clearTimeout(this.activating),this._activate(s===this.options.active?!1:s),void 0;default:return}e.preventDefault(),clearTimeout(this.activating),s=this._focusNextTab(s,n),e.ctrlKey||e.metaKey||(i.attr(\"aria-selected\",\"false\"),this.tabs.eq(s).attr(\"aria-selected\",\"true\"),this.activating=this._delay(function(){this.option(\"active\",s)},this.delay))}},_panelKeydown:function(e){this._handlePageNav(e)||e.ctrlKey&&e.keyCode===t.ui.keyCode.UP&&(e.preventDefault(),this.active.focus())},_handlePageNav:function(e){return e.altKey&&e.keyCode===t.ui.keyCode.PAGE_UP?(this._activate(this._focusNextTab(this.options.active-1,!1)),!0):e.altKey&&e.keyCode===t.ui.keyCode.PAGE_DOWN?(this._activate(this._focusNextTab(this.options.active+1,!0)),!0):void 0},_findNextTab:function(e,i){function s(){return e>n&&(e=0),0>e&&(e=n),e}for(var n=this.tabs.length-1;-1!==t.inArray(s(),this.options.disabled);)e=i?e+1:e-1;return e},_focusNextTab:function(t,e){return t=this._findNextTab(t,e),this.tabs.eq(t).focus(),t},_setOption:function(t,e){return\"active\"===t?(this._activate(e),void 0):\"disabled\"===t?(this._setupDisabled(e),void 0):(this._super(t,e),\"collapsible\"===t&&(this.element.toggleClass(\"ui-tabs-collapsible\",e),e||this.options.active!==!1||this._activate(0)),\"event\"===t&&this._setupEvents(e),\"heightStyle\"===t&&this._setupHeightStyle(e),void 0)},_sanitizeSelector:function(t){return t?t.replace(/[!\"$%&'()*+,.\\/:;<=>?@\\[\\]\\^`{|}~]/g,\"\\\\$&\"):\"\"},refresh:function(){var e=this.options,i=this.tablist.children(\":has(a[href])\");e.disabled=t.map(i.filter(\".ui-state-disabled\"),function(t){return i.index(t)}),this._processTabs(),e.active!==!1&&this.anchors.length?this.active.length&&!t.contains(this.tablist[0],this.active[0])?this.tabs.length===e.disabled.length?(e.active=!1,this.active=t()):this._activate(this._findNextTab(Math.max(0,e.active-1),!1)):e.active=this.tabs.index(this.active):(e.active=!1,this.active=t()),this._refresh()},_refresh:function(){this._setupDisabled(this.options.disabled),this._setupEvents(this.options.event),this._setupHeightStyle(this.options.heightStyle),this.tabs.not(this.active).attr({\"aria-selected\":\"false\",\"aria-expanded\":\"false\",tabIndex:-1}),this.panels.not(this._getPanelForTab(this.active)).hide().attr({\"aria-hidden\":\"true\"}),this.active.length?(this.active.addClass(\"ui-tabs-active ui-state-active\").attr({\"aria-selected\":\"true\",\"aria-expanded\":\"true\",tabIndex:0}),this._getPanelForTab(this.active).show().attr({\"aria-hidden\":\"false\"})):this.tabs.eq(0).attr(\"tabIndex\",0)},_processTabs:function(){var e=this,i=this.tabs,s=this.anchors,n=this.panels;this.tablist=this._getList().addClass(\"ui-tabs-nav ui-helper-reset ui-helper-clearfix ui-widget-header ui-corner-all\").attr(\"role\",\"tablist\").delegate(\"> li\",\"mousedown\"+this.eventNamespace,function(e){t(this).is(\".ui-state-disabled\")&&e.preventDefault()}).delegate(\".ui-tabs-anchor\",\"focus\"+this.eventNamespace,function(){t(this).closest(\"li\").is(\".ui-state-disabled\")&&this.blur()}),this.tabs=this.tablist.find(\"> li:has(a[href])\").addClass(\"ui-state-default ui-corner-top\").attr({role:\"tab\",tabIndex:-1}),this.anchors=this.tabs.map(function(){return t(\"a\",this)[0]}).addClass(\"ui-tabs-anchor\").attr({role:\"presentation\",tabIndex:-1}),this.panels=t(),this.anchors.each(function(i,s){var n,o,a,r=t(s).uniqueId().attr(\"id\"),h=t(s).closest(\"li\"),l=h.attr(\"aria-controls\");e._isLocal(s)?(n=s.hash,a=n.substring(1),o=e.element.find(e._sanitizeSelector(n))):(a=h.attr(\"aria-controls\")||t({}).uniqueId()[0].id,n=\"#\"+a,o=e.element.find(n),o.length||(o=e._createPanel(a),o.insertAfter(e.panels[i-1]||e.tablist)),o.attr(\"aria-live\",\"polite\")),o.length&&(e.panels=e.panels.add(o)),l&&h.data(\"ui-tabs-aria-controls\",l),h.attr({\"aria-controls\":a,\"aria-labelledby\":r}),o.attr(\"aria-labelledby\",r)}),this.panels.addClass(\"ui-tabs-panel ui-widget-content ui-corner-bottom\").attr(\"role\",\"tabpanel\"),i&&(this._off(i.not(this.tabs)),this._off(s.not(this.anchors)),this._off(n.not(this.panels)))},_getList:function(){return this.tablist||this.element.find(\"ol,ul\").eq(0)},_createPanel:function(e){return t(\"<div>\").attr(\"id\",e).addClass(\"ui-tabs-panel ui-widget-content ui-corner-bottom\").data(\"ui-tabs-destroy\",!0)},_setupDisabled:function(e){t.isArray(e)&&(e.length?e.length===this.anchors.length&&(e=!0):e=!1);for(var i,s=0;i=this.tabs[s];s++)e===!0||-1!==t.inArray(s,e)?t(i).addClass(\"ui-state-disabled\").attr(\"aria-disabled\",\"true\"):t(i).removeClass(\"ui-state-disabled\").removeAttr(\"aria-disabled\");this.options.disabled=e},_setupEvents:function(e){var i={};e&&t.each(e.split(\" \"),function(t,e){i[e]=\"_eventHandler\"}),this._off(this.anchors.add(this.tabs).add(this.panels)),this._on(!0,this.anchors,{click:function(t){t.preventDefault()}}),this._on(this.anchors,i),this._on(this.tabs,{keydown:\"_tabKeydown\"}),this._on(this.panels,{keydown:\"_panelKeydown\"}),this._focusable(this.tabs),this._hoverable(this.tabs)},_setupHeightStyle:function(e){var i,s=this.element.parent();\"fill\"===e?(i=s.height(),i-=this.element.outerHeight()-this.element.height(),this.element.siblings(\":visible\").each(function(){var e=t(this),s=e.css(\"position\");\"absolute\"!==s&&\"fixed\"!==s&&(i-=e.outerHeight(!0))}),this.element.children().not(this.panels).each(function(){i-=t(this).outerHeight(!0)}),this.panels.each(function(){t(this).height(Math.max(0,i-t(this).innerHeight()+t(this).height()))}).css(\"overflow\",\"auto\")):\"auto\"===e&&(i=0,this.panels.each(function(){i=Math.max(i,t(this).height(\"\").height())}).height(i))},_eventHandler:function(e){var i=this.options,s=this.active,n=t(e.currentTarget),o=n.closest(\"li\"),a=o[0]===s[0],r=a&&i.collapsible,h=r?t():this._getPanelForTab(o),l=s.length?this._getPanelForTab(s):t(),c={oldTab:s,oldPanel:l,newTab:r?t():o,newPanel:h};e.preventDefault(),o.hasClass(\"ui-state-disabled\")||o.hasClass(\"ui-tabs-loading\")||this.running||a&&!i.collapsible||this._trigger(\"beforeActivate\",e,c)===!1||(i.active=r?!1:this.tabs.index(o),this.active=a?t():o,this.xhr&&this.xhr.abort(),l.length||h.length||t.error(\"jQuery UI Tabs: Mismatching fragment identifier.\"),h.length&&this.load(this.tabs.index(o),e),this._toggle(e,c))},_toggle:function(e,i){function s(){o.running=!1,o._trigger(\"activate\",e,i)}function n(){i.newTab.closest(\"li\").addClass(\"ui-tabs-active ui-state-active\"),a.length&&o.options.show?o._show(a,o.options.show,s):(a.show(),s())}var o=this,a=i.newPanel,r=i.oldPanel;this.running=!0,r.length&&this.options.hide?this._hide(r,this.options.hide,function(){i.oldTab.closest(\"li\").removeClass(\"ui-tabs-active ui-state-active\"),n()}):(i.oldTab.closest(\"li\").removeClass(\"ui-tabs-active ui-state-active\"),r.hide(),n()),r.attr(\"aria-hidden\",\"true\"),i.oldTab.attr({\"aria-selected\":\"false\",\"aria-expanded\":\"false\"}),a.length&&r.length?i.oldTab.attr(\"tabIndex\",-1):a.length&&this.tabs.filter(function(){return 0===t(this).attr(\"tabIndex\")}).attr(\"tabIndex\",-1),a.attr(\"aria-hidden\",\"false\"),i.newTab.attr({\"aria-selected\":\"true\",\"aria-expanded\":\"true\",tabIndex:0})},_activate:function(e){var i,s=this._findActive(e);s[0]!==this.active[0]&&(s.length||(s=this.active),i=s.find(\".ui-tabs-anchor\")[0],this._eventHandler({target:i,currentTarget:i,preventDefault:t.noop}))},_findActive:function(e){return e===!1?t():this.tabs.eq(e)},_getIndex:function(t){return\"string\"==typeof t&&(t=this.anchors.index(this.anchors.filter(\"[href$='\"+t+\"']\"))),t},_destroy:function(){this.xhr&&this.xhr.abort(),this.element.removeClass(\"ui-tabs ui-widget ui-widget-content ui-corner-all ui-tabs-collapsible\"),this.tablist.removeClass(\"ui-tabs-nav ui-helper-reset ui-helper-clearfix ui-widget-header ui-corner-all\").removeAttr(\"role\"),this.anchors.removeClass(\"ui-tabs-anchor\").removeAttr(\"role\").removeAttr(\"tabIndex\").removeUniqueId(),this.tablist.unbind(this.eventNamespace),this.tabs.add(this.panels).each(function(){t.data(this,\"ui-tabs-destroy\")?t(this).remove():t(this).removeClass(\"ui-state-default ui-state-active ui-state-disabled ui-corner-top ui-corner-bottom ui-widget-content ui-tabs-active ui-tabs-panel\").removeAttr(\"tabIndex\").removeAttr(\"aria-live\").removeAttr(\"aria-busy\").removeAttr(\"aria-selected\").removeAttr(\"aria-labelledby\").removeAttr(\"aria-hidden\").removeAttr(\"aria-expanded\").removeAttr(\"role\")}),this.tabs.each(function(){var e=t(this),i=e.data(\"ui-tabs-aria-controls\");i?e.attr(\"aria-controls\",i).removeData(\"ui-tabs-aria-controls\"):e.removeAttr(\"aria-controls\")}),this.panels.show(),\"content\"!==this.options.heightStyle&&this.panels.css(\"height\",\"\")},enable:function(e){var i=this.options.disabled;i!==!1&&(void 0===e?i=!1:(e=this._getIndex(e),i=t.isArray(i)?t.map(i,function(t){return t!==e?t:null}):t.map(this.tabs,function(t,i){return i!==e?i:null})),this._setupDisabled(i))},disable:function(e){var i=this.options.disabled;if(i!==!0){if(void 0===e)i=!0;else{if(e=this._getIndex(e),-1!==t.inArray(e,i))return;i=t.isArray(i)?t.merge([e],i).sort():[e]}this._setupDisabled(i)}},load:function(e,i){e=this._getIndex(e);var s=this,n=this.tabs.eq(e),o=n.find(\".ui-tabs-anchor\"),a=this._getPanelForTab(n),r={tab:n,panel:a},h=function(t,e){\"abort\"===e&&s.panels.stop(!1,!0),n.removeClass(\"ui-tabs-loading\"),a.removeAttr(\"aria-busy\"),t===s.xhr&&delete s.xhr};this._isLocal(o[0])||(this.xhr=t.ajax(this._ajaxSettings(o,i,r)),this.xhr&&\"canceled\"!==this.xhr.statusText&&(n.addClass(\"ui-tabs-loading\"),a.attr(\"aria-busy\",\"true\"),this.xhr.done(function(t,e,n){setTimeout(function(){a.html(t),s._trigger(\"load\",i,r),h(n,e)},1)}).fail(function(t,e){setTimeout(function(){h(t,e)},1)})))},_ajaxSettings:function(e,i,s){var n=this;return{url:e.attr(\"href\"),beforeSend:function(e,o){return n._trigger(\"beforeLoad\",i,t.extend({jqXHR:e,ajaxSettings:o},s))}}},_getPanelForTab:function(e){var i=t(e).attr(\"aria-controls\");return this.element.find(this._sanitizeSelector(\"#\"+i))}}),t.widget(\"ui.tooltip\",{version:\"1.11.4\",options:{content:function(){var e=t(this).attr(\"title\")||\"\";return t(\"<a>\").text(e).html()},hide:!0,items:\"[title]:not([disabled])\",position:{my:\"left top+15\",at:\"left bottom\",collision:\"flipfit flip\"},show:!0,tooltipClass:null,track:!1,close:null,open:null},_addDescribedBy:function(e,i){var s=(e.attr(\"aria-describedby\")||\"\").split(/\\s+/);s.push(i),e.data(\"ui-tooltip-id\",i).attr(\"aria-describedby\",t.trim(s.join(\" \")))},_removeDescribedBy:function(e){var i=e.data(\"ui-tooltip-id\"),s=(e.attr(\"aria-describedby\")||\"\").split(/\\s+/),n=t.inArray(i,s);-1!==n&&s.splice(n,1),e.removeData(\"ui-tooltip-id\"),s=t.trim(s.join(\" \")),s?e.attr(\"aria-describedby\",s):e.removeAttr(\"aria-describedby\")},_create:function(){this._on({mouseover:\"open\",focusin:\"open\"}),this.tooltips={},this.parents={},this.options.disabled&&this._disable(),this.liveRegion=t(\"<div>\").attr({role:\"log\",\"aria-live\":\"assertive\",\"aria-relevant\":\"additions\"}).addClass(\"ui-helper-hidden-accessible\").appendTo(this.document[0].body)},_setOption:function(e,i){var s=this;return\"disabled\"===e?(this[i?\"_disable\":\"_enable\"](),this.options[e]=i,void 0):(this._super(e,i),\"content\"===e&&t.each(this.tooltips,function(t,e){s._updateContent(e.element)}),void 0)},_disable:function(){var e=this;t.each(this.tooltips,function(i,s){var n=t.Event(\"blur\");n.target=n.currentTarget=s.element[0],e.close(n,!0)}),this.element.find(this.options.items).addBack().each(function(){var e=t(this);e.is(\"[title]\")&&e.data(\"ui-tooltip-title\",e.attr(\"title\")).removeAttr(\"title\")})},_enable:function(){this.element.find(this.options.items).addBack().each(function(){var e=t(this);e.data(\"ui-tooltip-title\")&&e.attr(\"title\",e.data(\"ui-tooltip-title\"))})},open:function(e){var i=this,s=t(e?e.target:this.element).closest(this.options.items);s.length&&!s.data(\"ui-tooltip-id\")&&(s.attr(\"title\")&&s.data(\"ui-tooltip-title\",s.attr(\"title\")),s.data(\"ui-tooltip-open\",!0),e&&\"mouseover\"===e.type&&s.parents().each(function(){var e,s=t(this);s.data(\"ui-tooltip-open\")&&(e=t.Event(\"blur\"),e.target=e.currentTarget=this,i.close(e,!0)),s.attr(\"title\")&&(s.uniqueId(),i.parents[this.id]={element:this,title:s.attr(\"title\")},s.attr(\"title\",\"\"))}),this._registerCloseHandlers(e,s),this._updateContent(s,e))},_updateContent:function(t,e){var i,s=this.options.content,n=this,o=e?e.type:null;return\"string\"==typeof s?this._open(e,t,s):(i=s.call(t[0],function(i){n._delay(function(){t.data(\"ui-tooltip-open\")&&(e&&(e.type=o),this._open(e,t,i))})}),i&&this._open(e,t,i),void 0)},_open:function(e,i,s){function n(t){l.of=t,a.is(\":hidden\")||a.position(l)}var o,a,r,h,l=t.extend({},this.options.position);if(s){if(o=this._find(i))return o.tooltip.find(\".ui-tooltip-content\").html(s),void 0;i.is(\"[title]\")&&(e&&\"mouseover\"===e.type?i.attr(\"title\",\"\"):i.removeAttr(\"title\")),o=this._tooltip(i),a=o.tooltip,this._addDescribedBy(i,a.attr(\"id\")),a.find(\".ui-tooltip-content\").html(s),this.liveRegion.children().hide(),s.clone?(h=s.clone(),h.removeAttr(\"id\").find(\"[id]\").removeAttr(\"id\")):h=s,t(\"<div>\").html(h).appendTo(this.liveRegion),this.options.track&&e&&/^mouse/.test(e.type)?(this._on(this.document,{mousemove:n}),n(e)):a.position(t.extend({of:i},this.options.position)),a.hide(),this._show(a,this.options.show),this.options.show&&this.options.show.delay&&(r=this.delayedShow=setInterval(function(){a.is(\":visible\")&&(n(l.of),clearInterval(r))},t.fx.interval)),this._trigger(\"open\",e,{tooltip:a})}},_registerCloseHandlers:function(e,i){var s={keyup:function(e){if(e.keyCode===t.ui.keyCode.ESCAPE){var s=t.Event(e);s.currentTarget=i[0],this.close(s,!0)}}};i[0]!==this.element[0]&&(s.remove=function(){this._removeTooltip(this._find(i).tooltip)}),e&&\"mouseover\"!==e.type||(s.mouseleave=\"close\"),e&&\"focusin\"!==e.type||(s.focusout=\"close\"),this._on(!0,i,s)},close:function(e){var i,s=this,n=t(e?e.currentTarget:this.element),o=this._find(n);return o?(i=o.tooltip,o.closing||(clearInterval(this.delayedShow),n.data(\"ui-tooltip-title\")&&!n.attr(\"title\")&&n.attr(\"title\",n.data(\"ui-tooltip-title\")),this._removeDescribedBy(n),o.hiding=!0,i.stop(!0),this._hide(i,this.options.hide,function(){s._removeTooltip(t(this))}),n.removeData(\"ui-tooltip-open\"),this._off(n,\"mouseleave focusout keyup\"),n[0]!==this.element[0]&&this._off(n,\"remove\"),this._off(this.document,\"mousemove\"),e&&\"mouseleave\"===e.type&&t.each(this.parents,function(e,i){t(i.element).attr(\"title\",i.title),delete s.parents[e]}),o.closing=!0,this._trigger(\"close\",e,{tooltip:i}),o.hiding||(o.closing=!1)),void 0):(n.removeData(\"ui-tooltip-open\"),void 0)},_tooltip:function(e){var i=t(\"<div>\").attr(\"role\",\"tooltip\").addClass(\"ui-tooltip ui-widget ui-corner-all ui-widget-content \"+(this.options.tooltipClass||\"\")),s=i.uniqueId().attr(\"id\");return t(\"<div>\").addClass(\"ui-tooltip-content\").appendTo(i),i.appendTo(this.document[0].body),this.tooltips[s]={element:e,tooltip:i}},_find:function(t){var e=t.data(\"ui-tooltip-id\");return e?this.tooltips[e]:null},_removeTooltip:function(t){t.remove(),delete this.tooltips[t.attr(\"id\")]},_destroy:function(){var e=this;t.each(this.tooltips,function(i,s){var n=t.Event(\"blur\"),o=s.element;n.target=n.currentTarget=o[0],e.close(n,!0),t(\"#\"+i).remove(),o.data(\"ui-tooltip-title\")&&(o.attr(\"title\")||o.attr(\"title\",o.data(\"ui-tooltip-title\")),o.removeData(\"ui-tooltip-title\"))}),this.liveRegion.remove()}})});"
  },
  {
    "path": "js/lib/jquery.bpopup.js",
    "content": "/*===================================================================================================================\n * @name: bPopup\n * @type: jQuery\n * @author: (c) Bjoern Klinggaard - @bklinggaard\n * @demo: http://dinbror.dk/bpopup\n * @version: 0.9.4\n * @requires jQuery 1.4.3\n *==================================================================================================================*/\n;(function($) {\n\t'use strict';\n\t\n    $.fn.bPopup = function(options, callback) {\n        \n    if ($.isFunction(options)) {\n            callback \t\t= options;\n            options \t\t= null;\n        }\n\n\t\t// OPTIONS\n        var o \t\t\t\t= $.extend({}, $.fn.bPopup.defaults, options);\n        \n\t\t// HIDE SCROLLBAR?  \n        if (!o.scrollBar)\n            $('html').css('overflow', 'hidden');\n        \n\t\t// VARIABLES\t\n        var $popup \t\t\t= this\n          , d \t\t\t\t= $(document)\n          , w \t\t\t\t= window\n\t\t  , $w\t\t\t\t= $(w)\n          , wH\t\t\t\t= windowHeight()\n\t\t  , wW\t\t\t\t= windowWidth()\n          , prefix\t\t\t= '__b-popup'\n\t\t  , isIOS6X\t\t\t= (/OS 6(_\\d)+/i).test(navigator.userAgent) // Used for a temporary fix for ios6 timer bug when using zoom/scroll \n          , buffer\t\t\t= 200\n\t\t  , popups\t\t\t= 0\n          , id\n          , inside\n          , fixedVPos\n          , fixedHPos\n          , fixedPosStyle\n\t\t  , vPos\n          , hPos\n\t\t  , height\n\t\t  , width\n\t\t  , debounce\n\t\t;\n\n\t\t////////////////////////////////////////////////////////////////////////////////////////////////////////////\n        // PUBLIC FUNCTION - call it: $(element).bPopup().close();\n\t\t////////////////////////////////////////////////////////////////////////////////////////////////////////////\n        $popup.close = function() {\n            o = this.data('bPopup');\n\t\t\tid = prefix +$w.data('bPopup') + '__';\n            close();\n        };\n\n        return $popup.each(function() {\n            if ($(this).data('bPopup')) return; //POPUP already exists?\n            init();\n        });\n\n        ////////////////////////////////////////////////////////////////////////////////////////////////////////////\n        // HELPER FUNCTIONS - PRIVATE\n        ////////////////////////////////////////////////////////////////////////////////////////////////////////////\n        function init() {\n            triggerCall(o.onOpen);\n\t\t\tpopups = ($w.data('bPopup') || 0) + 1, id = prefix + popups + '__',fixedVPos = o.position[1] !== 'auto', fixedHPos = o.position[0] !== 'auto', fixedPosStyle = o.positionStyle === 'fixed', height = $popup.outerHeight(true), width = $popup.outerWidth(true);\n            o.loadUrl ? createContent() : open();\n        };\n\t\t\n\t\tfunction createContent() {\n            o.contentContainer = $(o.contentContainer || $popup);\n            switch (o.content) {\n                case ('iframe'):\n\t\t\t\t\tvar iframe = $('<iframe class=\"b-iframe\" ' + o.iframeAttr +'></iframe>');\n\t\t\t\t\tiframe.appendTo(o.contentContainer);\n\t\t\t\t\theight = $popup.outerHeight(true);\n\t\t\t\t\twidth = $popup.outerWidth(true);\n\t\t\t\t\topen();\n\t\t\t\t\tiframe.attr('src', o.loadUrl); // setting iframe src after open due IE9 bug\n\t\t\t\t\ttriggerCall(o.loadCallback);\n                    break;\n\t\t\t\tcase ('image'):\n\t\t\t\t\topen();\n\t\t\t\t\t$('<img />')\n\t\t\t\t\t\t.load(function() {\n\t\t\t\t\t\t    triggerCall(o.loadCallback);\n\t\t\t\t\t\t\trecenter($(this));\n\t\t\t\t\t    }).attr('style','width:768px;height:432px;cursor:pointer;').attr('src', o.loadUrl).attr('onClick','var win = window.open(\"'+o.loadUrl+'\");win.focus();return false;').hide().appendTo(o.contentContainer);\n\t\t\t\t\tbreak;\n                default:\n\t\t\t\t\topen();\n\t\t\t\t\t$('<div class=\"b-ajax-wrapper\"></div>')\n                    \t.load(o.loadUrl, o.loadData, function(){\n\t\t\t\t\t\t    triggerCall(o.loadCallback);\n\t\t\t\t\t\t\trecenter($(this));\n\t\t\t\t\t\t}).hide().appendTo(o.contentContainer);\n                    break;\n            }\n        };\n\n\t\tfunction open(){\n\t\t\t// MODAL OVERLAY\n            if (o.modal) {\n                $('<div class=\"b-modal '+id+'\"></div>')\n                .css({backgroundColor: o.modalColor, position: 'fixed', top: 0, right:0, bottom:0, left: 0, opacity: 0, zIndex: o.zIndex + popups})\n                .appendTo(o.appendTo)\n                .fadeTo(o.speed, o.opacity);\n            }\n\t\t\t\n\t\t\t// POPUP\n\t\t\tcalPosition();\n            $popup\n\t\t\t\t.data('bPopup', o).data('id',id)\n\t\t\t\t.css({ \n\t\t\t\t\t  'left': o.transition == 'slideIn' || o.transition == 'slideBack' ? (o.transition == 'slideBack' ? d.scrollLeft() + wW : (hPos + width) *-1) : getLeftPos(!(!o.follow[0] && fixedHPos || fixedPosStyle))\n\t\t\t\t\t, 'position': o.positionStyle || 'absolute'\n\t\t\t\t\t, 'top': o.transition == 'slideDown' || o.transition == 'slideUp' ? (o.transition == 'slideUp' ? d.scrollTop() + wH : vPos + height * -1) : getTopPos(!(!o.follow[1] && fixedVPos || fixedPosStyle))\n\t\t\t\t\t, 'z-index': o.zIndex + popups + 1 \n\t\t\t\t}).each(function() {\n            \t\tif(o.appending) {\n                \t\t$(this).appendTo(o.appendTo);\n            \t\t}\n        \t\t});\n\t\t\tdoTransition(true);\t\n\t\t};\n\t\t\n        function close() {\n            if (o.modal) {\n                $('.b-modal.'+$popup.data('id'))\n\t                .fadeTo(o.speed, 0, function() {\n\t                    $(this).remove();\n\t                });\n            }\n\t\t\t// Clean up\n\t\t\tunbindEvents();\t\n\t\t\t// Close trasition\n            doTransition();\n            \n\t\t\treturn false; // Prevent default\n        };\n\t\t\n\t\t//Eksperimental\n\t\tfunction recenter(content){\n\t\t\tvar _width = content.width(), _height = content.height(), css = {};\n\t\t\to.contentContainer.css({height:_height,width:_width});\n\t\t\t\n\t\t\tif (_height >= $popup.height()){\n\t\t\t\tcss.height = $popup.height();\n\t\t\t}\n\t\t\tif(_width >= $popup.width()){\n\t\t\t\tcss.width = $popup.width();\n\t\t\t}\n\t\t\theight = $popup.outerHeight(true)\n\t\t\t, width = $popup.outerWidth(true);\n\t\t\t\t\n\t\t\tcalPosition();\n\t\t\to.contentContainer.css({height:'auto',width:'auto'});\t\t\n\t\t\t\n\t\t\tcss.left = getLeftPos(!(!o.follow[0] && fixedHPos || fixedPosStyle)),\n\t\t\tcss.top = getTopPos(!(!o.follow[1] && fixedVPos || fixedPosStyle));\n\t\t\t\n\t\t\t$popup\n\t\t\t\t.animate(\n\t\t\t\t\tcss\n\t\t\t\t\t, 250\n\t\t\t\t\t, function() { \n\t\t\t\t\t\tcontent.show();\n\t\t\t\t\t\tinside = insideWindow();\n\t\t\t\t\t}\n\t\t\t\t);\n\t\t};\n\t\t\n        function bindEvents() {\n            $w.data('bPopup', popups);\n\t\t\t$popup.delegate('.bClose, .' + o.closeClass, 'click.'+id, close); // legacy, still supporting the close class bClose\n            \n            if (o.modalClose) {\n                $('.b-modal.'+id).css('cursor', 'pointer').bind('click', close);\n            }\n\t\t\t\n\t\t\t// Temporary disabling scroll/resize events on devices with IOS6+\n\t\t\t// due to a bug where events are dropped after pinch to zoom\n            if (!isIOS6X && (o.follow[0] || o.follow[1])) {\n               $w.bind('scroll.'+id, function() {\n                \tif(inside){\n                    \t$popup\n                        \t.dequeue()\n                            .animate({ 'left': o.follow[0] ? getLeftPos(!fixedPosStyle) : 'auto', 'top': o.follow[1] ? getTopPos(!fixedPosStyle) : 'auto' }, o.followSpeed, o.followEasing);\n\t\t\t\t\t }  \n            \t}).bind('resize.'+id, function() {\n\t\t            wH = windowHeight();\n\t\t  \t\t    wW = windowWidth();\n\t\t\t\t\tinside = insideWindow();\n                   \tif(inside){\n\t\t\t\t\t\tclearTimeout(debounce);\n\t\t\t\t\t\tdebounce = setTimeout(function(){\n\t\t\t\t\t\t\tcalPosition();\n\t\t\t\t\t\t\t$popup\n\t                           \t.dequeue()\n\t                           \t.each(function() {\n\t                               \tif(fixedPosStyle) {\n\t                                \t$(this).css({ 'left': hPos, 'top': vPos });\n\t                               \t}\n\t                               \telse {\n\t                                   \t$(this).animate({ 'left': o.follow[0] ? getLeftPos(true) : 'auto', 'top': o.follow[1] ? getTopPos(true) : 'auto' }, o.followSpeed, o.followEasing);\n\t                               \t}\n\t                           \t});\n\t\t\t\t\t\t}, 50);\t\t\t\t\t\n                   \t}\n                });\n            }\n            if (o.escClose) {\n                d.bind('keydown.'+id, function(e) {\n                    if (e.which == 27) {  //escape\n                        close();\n                    }\n                });\n            }\n        };\n\t\t\n        function unbindEvents() {\n            if (!o.scrollBar) {\n                $('html').css('overflow', 'auto');\n            }\n            $('.b-modal.'+id).unbind('click');\n            d.unbind('keydown.'+id);\n            $w.unbind('.'+id).data('bPopup', ($w.data('bPopup')-1 > 0) ? $w.data('bPopup')-1 : null);\n            $popup.undelegate('.bClose, .' + o.closeClass, 'click.'+id, close).data('bPopup', null);\n        };\n\t\t\n\t\tfunction doTransition(open) {\n\t\t\tswitch (open ? o.transition : o.transitionClose || o.transition) {\n\t\t\t   case \"slideIn\":\n\t\t\t\t   \tanimate({\n\t\t\t\t   \t\tleft: open ? getLeftPos(!(!o.follow[0] && fixedHPos || fixedPosStyle)) : d.scrollLeft() - (width || $popup.outerWidth(true)) - buffer\n\t\t\t\t   \t});\n\t\t\t      \tbreak;\n\t\t\t   case \"slideBack\":\n\t\t\t\t   \tanimate({\n\t\t\t\t   \t\tleft: open ? getLeftPos(!(!o.follow[0] && fixedHPos || fixedPosStyle)) : d.scrollLeft() + wW + buffer\n\t\t\t\t   \t});\n\t\t\t      \tbreak;\n\t\t\t   case \"slideDown\":\n\t\t\t\t   \tanimate({\n\t\t\t\t   \t\ttop: open ? getTopPos(!(!o.follow[1] && fixedVPos || fixedPosStyle)) : d.scrollTop() - (height || $popup.outerHeight(true)) - buffer\n\t\t\t\t   \t});\n\t\t\t      \tbreak;\n\t\t   \t\tcase \"slideUp\":\n\t\t\t\t\tanimate({\n\t\t\t\t\t\ttop: open ? getTopPos(!(!o.follow[1] && fixedVPos || fixedPosStyle)) : d.scrollTop() + wH + buffer\n\t\t\t\t\t});\n\t\t      \t  \tbreak;\n\t\t\t   default:\n\t\t\t   \t  \t//Hardtyping 1 and 0 to ensure opacity 1 and not 0.9999998\n\t\t\t\t  \t$popup.stop().fadeTo(o.speed, open ? 1 : 0, function(){onCompleteCallback(open);});\n\t\t\t}\n\t\t\t\n\t\t\tfunction animate(css){\n\t\t\t  \t$popup\n\t\t\t\t\t.css({display: 'block',opacity: 1})\n\t\t\t\t\t.animate(css, o.speed, o.easing, function(){ onCompleteCallback(open); });\n\t\t\t};\n\t\t};\n\t\t\n\t\t\n\t\tfunction onCompleteCallback(open){\n\t\t\tif(open){\n\t\t\t\tbindEvents();\n\t            triggerCall(callback);\n\t\t\t\tif(o.autoClose){\n\t\t\t\t\tsetTimeout(close, o.autoClose);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t$popup.hide();\n\t\t\t\ttriggerCall(o.onClose);\n\t\t\t\tif (o.loadUrl) {\n                    o.contentContainer.empty();\n\t\t\t\t\t$popup.css({height: 'auto', width: 'auto'});\n                }\t\t\n\t\t\t}\n\t\t};\n\t\t\n\t\tfunction getLeftPos(includeScroll){\n\t\t\treturn includeScroll ? hPos + d.scrollLeft() : hPos;\n\t\t};\n\t\t\n\t\tfunction getTopPos(includeScroll){\n\t\t\treturn includeScroll ? vPos + d.scrollTop() : vPos;\n\t\t};\n\t\t\n\t\tfunction triggerCall(func) {\n\t\t\t$.isFunction(func) && func.call($popup);\n\t\t};\n\t\t\n       \tfunction calPosition(){\n\t\t\tvPos \t\t= fixedVPos ? o.position[1] : Math.max(0, ((wH- $popup.outerHeight(true)) / 2) - o.amsl)\n\t\t\t, hPos \t\t= fixedHPos ? o.position[0] : (wW - $popup.outerWidth(true)) / 2\n\t\t\t, inside \t= insideWindow();\n\t\t};\n\t\t\n        function insideWindow(){\n            return wH > $popup.outerHeight(true) && wW > $popup.outerWidth(true);\n        };\n\t\t\n\t\tfunction windowHeight(){\n\t\t\treturn w.innerHeight || $w.height();\n\t\t};\n\t\t\n\t\tfunction windowWidth(){\n\t\t\treturn w.innerWidth || $w.width();\n\t\t};\n    };\n\n\t////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\t// DEFAULT VALUES\n\t////////////////////////////////////////////////////////////////////////////////////////////////////////////\n    $.fn.bPopup.defaults = {\n          amsl: \t\t\t50\n        , appending: \t\ttrue\n        , appendTo: \t\t'body'\n\t\t, autoClose:\t\tfalse\n        , closeClass: \t\t'b-close'\n        , content: \t\t\t'ajax' // ajax, iframe or image\n        , contentContainer: false\n\t\t, easing: \t\t\t'swing'\n        , escClose: \t\ttrue\n        , follow: \t\t\t[true, true] // x, y\n\t\t, followEasing: \t'swing'\n        , followSpeed: \t\t500\n\t\t, iframeAttr: \t\t'scrolling=\"no\" frameborder=\"0\"'\n\t\t, loadCallback: \tfalse\n\t\t, loadData: \t\tfalse\n        , loadUrl: \t\t\tfalse\n        , modal: \t\t\ttrue\n        , modalClose: \t\ttrue\n        , modalColor: \t\t'#000'\n        , onClose: \t\t\tfalse\n        , onOpen: \t\t\tfalse\n        , opacity: \t\t\t0.7\n        , position: \t\t['auto', 'auto'] // x, y,\n        , positionStyle: \t'absolute'// absolute or fixed\n        , scrollBar: \t\ttrue\n\t\t, speed: \t\t\t250 // open & close speed\n\t\t, transition:\t\t'fadeIn' //transitions: fadeIn, slideDown, slideIn\n\t\t, transitionClose:\tfalse\n        , zIndex: \t\t\t9997 // popup gets z-index 9999, modal overlay 9998\n    };\n})(jQuery);\n"
  },
  {
    "path": "js/lib/jquery.grayscale.js",
    "content": "//Author Molnar Raul Alexandru\n\nfunction grayscale(src){\n\t\n\t\tvar graycanvas = document.createElement('canvas');\n\t\tvar ctx = graycanvas .getContext('2d');\n\t\tvar imgObj = new Image();\n\t\timgObj.src = src;\n\t\tgraycanvas .width = imgObj.width;\n\t\tgraycanvas .height = imgObj.height; \n\t\tctx.drawImage(imgObj, 0, 0); \n\t\tvar canvasPixels = ctx.getImageData(0, 0, graycanvas .width, graycanvas .height);\n\t\t\n\t\t\tfor(var y = 0; y < canvasPixels.height; y++){\n\t\t\tfor(var x = 0; x < canvasPixels.width; x++){\n\t\t\t\t\n\t\t\t\tvar i = (y * 4) * canvasPixels.width + x * 4;\n\t\t\t\tvar average= (canvasPixels.data[i] + canvasPixels.data[i + 1] + canvasPixels.data[i + 2]) / 3;\n\t\t\t\tcanvasPixels.data[i] = average; \n\t\t\t\tcanvasPixels.data[i + 1] = average; \n\t\t\t\tcanvasPixels.data[i + 2] = average;\n\t\t\t}\n\t\t}\n\t\tctx.putImageData(canvasPixels, 0, 0, 0, 0, canvasPixels.width, canvasPixels.height);\n\t\treturn graycanvas .toDataURL();\n    }\n\n(function($) {\n  $.fn.grayScale = function()\n  {\t\nvar elem=$(this);\n\tvar source=elem.attr('src');\n\telem.attr(\"src\",grayscale(source));\n}\n})(jQuery);"
  },
  {
    "path": "js/lib/jquery.js",
    "content": "/*!\n * jQuery JavaScript Library v1.7.1\n * http://jquery.com/\n *\n * Copyright 2011, John Resig\n * Dual licensed under the MIT or GPL Version 2 licenses.\n * http://jquery.org/license\n *\n * Includes Sizzle.js\n * http://sizzlejs.com/\n * Copyright 2011, The Dojo Foundation\n * Released under the MIT, BSD, and GPL Licenses.\n *\n * Date: Mon Nov 21 21:11:03 2011 -0500\n */\n(function( window, undefined ) {\n\n// Use the correct document accordingly with window argument (sandbox)\nvar document = window.document,\n\tnavigator = window.navigator,\n\tlocation = window.location;\nvar jQuery = (function() {\n\n// Define a local copy of jQuery\nvar jQuery = function( selector, context ) {\n\t\t// The jQuery object is actually just the init constructor 'enhanced'\n\t\treturn new jQuery.fn.init( selector, context, rootjQuery );\n\t},\n\n\t// Map over jQuery in case of overwrite\n\t_jQuery = window.jQuery,\n\n\t// Map over the $ in case of overwrite\n\t_$ = window.$,\n\n\t// A central reference to the root jQuery(document)\n\trootjQuery,\n\n\t// A simple way to check for HTML strings or ID strings\n\t// Prioritize #id over <tag> to avoid XSS via location.hash (#9521)\n\tquickExpr = /^(?:[^#<]*(<[\\w\\W]+>)[^>]*$|#([\\w\\-]*)$)/,\n\n\t// Check if a string has a non-whitespace character in it\n\trnotwhite = /\\S/,\n\n\t// Used for trimming whitespace\n\ttrimLeft = /^\\s+/,\n\ttrimRight = /\\s+$/,\n\n\t// Match a standalone tag\n\trsingleTag = /^<(\\w+)\\s*\\/?>(?:<\\/\\1>)?$/,\n\n\t// JSON RegExp\n\trvalidchars = /^[\\],:{}\\s]*$/,\n\trvalidescape = /\\\\(?:[\"\\\\\\/bfnrt]|u[0-9a-fA-F]{4})/g,\n\trvalidtokens = /\"[^\"\\\\\\n\\r]*\"|true|false|null|-?\\d+(?:\\.\\d*)?(?:[eE][+\\-]?\\d+)?/g,\n\trvalidbraces = /(?:^|:|,)(?:\\s*\\[)+/g,\n\n\t// Useragent RegExp\n\trwebkit = /(webkit)[ \\/]([\\w.]+)/,\n\tropera = /(opera)(?:.*version)?[ \\/]([\\w.]+)/,\n\trmsie = /(msie) ([\\w.]+)/,\n\trmozilla = /(mozilla)(?:.*? rv:([\\w.]+))?/,\n\n\t// Matches dashed string for camelizing\n\trdashAlpha = /-([a-z]|[0-9])/ig,\n\trmsPrefix = /^-ms-/,\n\n\t// Used by jQuery.camelCase as callback to replace()\n\tfcamelCase = function( all, letter ) {\n\t\treturn ( letter + \"\" ).toUpperCase();\n\t},\n\n\t// Keep a UserAgent string for use with jQuery.browser\n\tuserAgent = navigator.userAgent,\n\n\t// For matching the engine and version of the browser\n\tbrowserMatch,\n\n\t// The deferred used on DOM ready\n\treadyList,\n\n\t// The ready event handler\n\tDOMContentLoaded,\n\n\t// Save a reference to some core methods\n\ttoString = Object.prototype.toString,\n\thasOwn = Object.prototype.hasOwnProperty,\n\tpush = Array.prototype.push,\n\tslice = Array.prototype.slice,\n\ttrim = String.prototype.trim,\n\tindexOf = Array.prototype.indexOf,\n\n\t// [[Class]] -> type pairs\n\tclass2type = {};\n\njQuery.fn = jQuery.prototype = {\n\tconstructor: jQuery,\n\tinit: function( selector, context, rootjQuery ) {\n\t\tvar match, elem, ret, doc;\n\n\t\t// Handle $(\"\"), $(null), or $(undefined)\n\t\tif ( !selector ) {\n\t\t\treturn this;\n\t\t}\n\n\t\t// Handle $(DOMElement)\n\t\tif ( selector.nodeType ) {\n\t\t\tthis.context = this[0] = selector;\n\t\t\tthis.length = 1;\n\t\t\treturn this;\n\t\t}\n\n\t\t// The body element only exists once, optimize finding it\n\t\tif ( selector === \"body\" && !context && document.body ) {\n\t\t\tthis.context = document;\n\t\t\tthis[0] = document.body;\n\t\t\tthis.selector = selector;\n\t\t\tthis.length = 1;\n\t\t\treturn this;\n\t\t}\n\n\t\t// Handle HTML strings\n\t\tif ( typeof selector === \"string\" ) {\n\t\t\t// Are we dealing with HTML string or an ID?\n\t\t\tif ( selector.charAt(0) === \"<\" && selector.charAt( selector.length - 1 ) === \">\" && selector.length >= 3 ) {\n\t\t\t\t// Assume that strings that start and end with <> are HTML and skip the regex check\n\t\t\t\tmatch = [ null, selector, null ];\n\n\t\t\t} else {\n\t\t\t\tmatch = quickExpr.exec( selector );\n\t\t\t}\n\n\t\t\t// Verify a match, and that no context was specified for #id\n\t\t\tif ( match && (match[1] || !context) ) {\n\n\t\t\t\t// HANDLE: $(html) -> $(array)\n\t\t\t\tif ( match[1] ) {\n\t\t\t\t\tcontext = context instanceof jQuery ? context[0] : context;\n\t\t\t\t\tdoc = ( context ? context.ownerDocument || context : document );\n\n\t\t\t\t\t// If a single string is passed in and it's a single tag\n\t\t\t\t\t// just do a createElement and skip the rest\n\t\t\t\t\tret = rsingleTag.exec( selector );\n\n\t\t\t\t\tif ( ret ) {\n\t\t\t\t\t\tif ( jQuery.isPlainObject( context ) ) {\n\t\t\t\t\t\t\tselector = [ document.createElement( ret[1] ) ];\n\t\t\t\t\t\t\tjQuery.fn.attr.call( selector, context, true );\n\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tselector = [ doc.createElement( ret[1] ) ];\n\t\t\t\t\t\t}\n\n\t\t\t\t\t} else {\n\t\t\t\t\t\tret = jQuery.buildFragment( [ match[1] ], [ doc ] );\n\t\t\t\t\t\tselector = ( ret.cacheable ? jQuery.clone(ret.fragment) : ret.fragment ).childNodes;\n\t\t\t\t\t}\n\n\t\t\t\t\treturn jQuery.merge( this, selector );\n\n\t\t\t\t// HANDLE: $(\"#id\")\n\t\t\t\t} else {\n\t\t\t\t\telem = document.getElementById( match[2] );\n\n\t\t\t\t\t// Check parentNode to catch when Blackberry 4.6 returns\n\t\t\t\t\t// nodes that are no longer in the document #6963\n\t\t\t\t\tif ( elem && elem.parentNode ) {\n\t\t\t\t\t\t// Handle the case where IE and Opera return items\n\t\t\t\t\t\t// by name instead of ID\n\t\t\t\t\t\tif ( elem.id !== match[2] ) {\n\t\t\t\t\t\t\treturn rootjQuery.find( selector );\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Otherwise, we inject the element directly into the jQuery object\n\t\t\t\t\t\tthis.length = 1;\n\t\t\t\t\t\tthis[0] = elem;\n\t\t\t\t\t}\n\n\t\t\t\t\tthis.context = document;\n\t\t\t\t\tthis.selector = selector;\n\t\t\t\t\treturn this;\n\t\t\t\t}\n\n\t\t\t// HANDLE: $(expr, $(...))\n\t\t\t} else if ( !context || context.jquery ) {\n\t\t\t\treturn ( context || rootjQuery ).find( selector );\n\n\t\t\t// HANDLE: $(expr, context)\n\t\t\t// (which is just equivalent to: $(context).find(expr)\n\t\t\t} else {\n\t\t\t\treturn this.constructor( context ).find( selector );\n\t\t\t}\n\n\t\t// HANDLE: $(function)\n\t\t// Shortcut for document ready\n\t\t} else if ( jQuery.isFunction( selector ) ) {\n\t\t\treturn rootjQuery.ready( selector );\n\t\t}\n\n\t\tif ( selector.selector !== undefined ) {\n\t\t\tthis.selector = selector.selector;\n\t\t\tthis.context = selector.context;\n\t\t}\n\n\t\treturn jQuery.makeArray( selector, this );\n\t},\n\n\t// Start with an empty selector\n\tselector: \"\",\n\n\t// The current version of jQuery being used\n\tjquery: \"1.7.1\",\n\n\t// The default length of a jQuery object is 0\n\tlength: 0,\n\n\t// The number of elements contained in the matched element set\n\tsize: function() {\n\t\treturn this.length;\n\t},\n\n\ttoArray: function() {\n\t\treturn slice.call( this, 0 );\n\t},\n\n\t// Get the Nth element in the matched element set OR\n\t// Get the whole matched element set as a clean array\n\tget: function( num ) {\n\t\treturn num == null ?\n\n\t\t\t// Return a 'clean' array\n\t\t\tthis.toArray() :\n\n\t\t\t// Return just the object\n\t\t\t( num < 0 ? this[ this.length + num ] : this[ num ] );\n\t},\n\n\t// Take an array of elements and push it onto the stack\n\t// (returning the new matched element set)\n\tpushStack: function( elems, name, selector ) {\n\t\t// Build a new jQuery matched element set\n\t\tvar ret = this.constructor();\n\n\t\tif ( jQuery.isArray( elems ) ) {\n\t\t\tpush.apply( ret, elems );\n\n\t\t} else {\n\t\t\tjQuery.merge( ret, elems );\n\t\t}\n\n\t\t// Add the old object onto the stack (as a reference)\n\t\tret.prevObject = this;\n\n\t\tret.context = this.context;\n\n\t\tif ( name === \"find\" ) {\n\t\t\tret.selector = this.selector + ( this.selector ? \" \" : \"\" ) + selector;\n\t\t} else if ( name ) {\n\t\t\tret.selector = this.selector + \".\" + name + \"(\" + selector + \")\";\n\t\t}\n\n\t\t// Return the newly-formed element set\n\t\treturn ret;\n\t},\n\n\t// Execute a callback for every element in the matched set.\n\t// (You can seed the arguments with an array of args, but this is\n\t// only used internally.)\n\teach: function( callback, args ) {\n\t\treturn jQuery.each( this, callback, args );\n\t},\n\n\tready: function( fn ) {\n\t\t// Attach the listeners\n\t\tjQuery.bindReady();\n\n\t\t// Add the callback\n\t\treadyList.add( fn );\n\n\t\treturn this;\n\t},\n\n\teq: function( i ) {\n\t\ti = +i;\n\t\treturn i === -1 ?\n\t\t\tthis.slice( i ) :\n\t\t\tthis.slice( i, i + 1 );\n\t},\n\n\tfirst: function() {\n\t\treturn this.eq( 0 );\n\t},\n\n\tlast: function() {\n\t\treturn this.eq( -1 );\n\t},\n\n\tslice: function() {\n\t\treturn this.pushStack( slice.apply( this, arguments ),\n\t\t\t\"slice\", slice.call(arguments).join(\",\") );\n\t},\n\n\tmap: function( callback ) {\n\t\treturn this.pushStack( jQuery.map(this, function( elem, i ) {\n\t\t\treturn callback.call( elem, i, elem );\n\t\t}));\n\t},\n\n\tend: function() {\n\t\treturn this.prevObject || this.constructor(null);\n\t},\n\n\t// For internal use only.\n\t// Behaves like an Array's method, not like a jQuery method.\n\tpush: push,\n\tsort: [].sort,\n\tsplice: [].splice\n};\n\n// Give the init function the jQuery prototype for later instantiation\njQuery.fn.init.prototype = jQuery.fn;\n\njQuery.extend = jQuery.fn.extend = function() {\n\tvar options, name, src, copy, copyIsArray, clone,\n\t\ttarget = arguments[0] || {},\n\t\ti = 1,\n\t\tlength = arguments.length,\n\t\tdeep = false;\n\n\t// Handle a deep copy situation\n\tif ( typeof target === \"boolean\" ) {\n\t\tdeep = target;\n\t\ttarget = arguments[1] || {};\n\t\t// skip the boolean and the target\n\t\ti = 2;\n\t}\n\n\t// Handle case when target is a string or something (possible in deep copy)\n\tif ( typeof target !== \"object\" && !jQuery.isFunction(target) ) {\n\t\ttarget = {};\n\t}\n\n\t// extend jQuery itself if only one argument is passed\n\tif ( length === i ) {\n\t\ttarget = this;\n\t\t--i;\n\t}\n\n\tfor ( ; i < length; i++ ) {\n\t\t// Only deal with non-null/undefined values\n\t\tif ( (options = arguments[ i ]) != null ) {\n\t\t\t// Extend the base object\n\t\t\tfor ( name in options ) {\n\t\t\t\tsrc = target[ name ];\n\t\t\t\tcopy = options[ name ];\n\n\t\t\t\t// Prevent never-ending loop\n\t\t\t\tif ( target === copy ) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\t// Recurse if we're merging plain objects or arrays\n\t\t\t\tif ( deep && copy && ( jQuery.isPlainObject(copy) || (copyIsArray = jQuery.isArray(copy)) ) ) {\n\t\t\t\t\tif ( copyIsArray ) {\n\t\t\t\t\t\tcopyIsArray = false;\n\t\t\t\t\t\tclone = src && jQuery.isArray(src) ? src : [];\n\n\t\t\t\t\t} else {\n\t\t\t\t\t\tclone = src && jQuery.isPlainObject(src) ? src : {};\n\t\t\t\t\t}\n\n\t\t\t\t\t// Never move original objects, clone them\n\t\t\t\t\ttarget[ name ] = jQuery.extend( deep, clone, copy );\n\n\t\t\t\t// Don't bring in undefined values\n\t\t\t\t} else if ( copy !== undefined ) {\n\t\t\t\t\ttarget[ name ] = copy;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t// Return the modified object\n\treturn target;\n};\n\njQuery.extend({\n\tnoConflict: function( deep ) {\n\t\tif ( window.$ === jQuery ) {\n\t\t\twindow.$ = _$;\n\t\t}\n\n\t\tif ( deep && window.jQuery === jQuery ) {\n\t\t\twindow.jQuery = _jQuery;\n\t\t}\n\n\t\treturn jQuery;\n\t},\n\n\t// Is the DOM ready to be used? Set to true once it occurs.\n\tisReady: false,\n\n\t// A counter to track how many items to wait for before\n\t// the ready event fires. See #6781\n\treadyWait: 1,\n\n\t// Hold (or release) the ready event\n\tholdReady: function( hold ) {\n\t\tif ( hold ) {\n\t\t\tjQuery.readyWait++;\n\t\t} else {\n\t\t\tjQuery.ready( true );\n\t\t}\n\t},\n\n\t// Handle when the DOM is ready\n\tready: function( wait ) {\n\t\t// Either a released hold or an DOMready/load event and not yet ready\n\t\tif ( (wait === true && !--jQuery.readyWait) || (wait !== true && !jQuery.isReady) ) {\n\t\t\t// Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443).\n\t\t\tif ( !document.body ) {\n\t\t\t\treturn setTimeout( jQuery.ready, 1 );\n\t\t\t}\n\n\t\t\t// Remember that the DOM is ready\n\t\t\tjQuery.isReady = true;\n\n\t\t\t// If a normal DOM Ready event fired, decrement, and wait if need be\n\t\t\tif ( wait !== true && --jQuery.readyWait > 0 ) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// If there are functions bound, to execute\n\t\t\treadyList.fireWith( document, [ jQuery ] );\n\n\t\t\t// Trigger any bound ready events\n\t\t\tif ( jQuery.fn.trigger ) {\n\t\t\t\tjQuery( document ).trigger( \"ready\" ).off( \"ready\" );\n\t\t\t}\n\t\t}\n\t},\n\n\tbindReady: function() {\n\t\tif ( readyList ) {\n\t\t\treturn;\n\t\t}\n\n\t\treadyList = jQuery.Callbacks( \"once memory\" );\n\n\t\t// Catch cases where $(document).ready() is called after the\n\t\t// browser event has already occurred.\n\t\tif ( document.readyState === \"complete\" ) {\n\t\t\t// Handle it asynchronously to allow scripts the opportunity to delay ready\n\t\t\treturn setTimeout( jQuery.ready, 1 );\n\t\t}\n\n\t\t// Mozilla, Opera and webkit nightlies currently support this event\n\t\tif ( document.addEventListener ) {\n\t\t\t// Use the handy event callback\n\t\t\tdocument.addEventListener( \"DOMContentLoaded\", DOMContentLoaded, false );\n\n\t\t\t// A fallback to window.onload, that will always work\n\t\t\twindow.addEventListener( \"load\", jQuery.ready, false );\n\n\t\t// If IE event model is used\n\t\t} else if ( document.attachEvent ) {\n\t\t\t// ensure firing before onload,\n\t\t\t// maybe late but safe also for iframes\n\t\t\tdocument.attachEvent( \"onreadystatechange\", DOMContentLoaded );\n\n\t\t\t// A fallback to window.onload, that will always work\n\t\t\twindow.attachEvent( \"onload\", jQuery.ready );\n\n\t\t\t// If IE and not a frame\n\t\t\t// continually check to see if the document is ready\n\t\t\tvar toplevel = false;\n\n\t\t\ttry {\n\t\t\t\ttoplevel = window.frameElement == null;\n\t\t\t} catch(e) {}\n\n\t\t\tif ( document.documentElement.doScroll && toplevel ) {\n\t\t\t\tdoScrollCheck();\n\t\t\t}\n\t\t}\n\t},\n\n\t// See test/unit/core.js for details concerning isFunction.\n\t// Since version 1.3, DOM methods and functions like alert\n\t// aren't supported. They return false on IE (#2968).\n\tisFunction: function( obj ) {\n\t\treturn jQuery.type(obj) === \"function\";\n\t},\n\n\tisArray: Array.isArray || function( obj ) {\n\t\treturn jQuery.type(obj) === \"array\";\n\t},\n\n\t// A crude way of determining if an object is a window\n\tisWindow: function( obj ) {\n\t\treturn obj && typeof obj === \"object\" && \"setInterval\" in obj;\n\t},\n\n\tisNumeric: function( obj ) {\n\t\treturn !isNaN( parseFloat(obj) ) && isFinite( obj );\n\t},\n\n\ttype: function( obj ) {\n\t\treturn obj == null ?\n\t\t\tString( obj ) :\n\t\t\tclass2type[ toString.call(obj) ] || \"object\";\n\t},\n\n\tisPlainObject: function( obj ) {\n\t\t// Must be an Object.\n\t\t// Because of IE, we also have to check the presence of the constructor property.\n\t\t// Make sure that DOM nodes and window objects don't pass through, as well\n\t\tif ( !obj || jQuery.type(obj) !== \"object\" || obj.nodeType || jQuery.isWindow( obj ) ) {\n\t\t\treturn false;\n\t\t}\n\n\t\ttry {\n\t\t\t// Not own constructor property must be Object\n\t\t\tif ( obj.constructor &&\n\t\t\t\t!hasOwn.call(obj, \"constructor\") &&\n\t\t\t\t!hasOwn.call(obj.constructor.prototype, \"isPrototypeOf\") ) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t} catch ( e ) {\n\t\t\t// IE8,9 Will throw exceptions on certain host objects #9897\n\t\t\treturn false;\n\t\t}\n\n\t\t// Own properties are enumerated firstly, so to speed up,\n\t\t// if last one is own, then all properties are own.\n\n\t\tvar key;\n\t\tfor ( key in obj ) {}\n\n\t\treturn key === undefined || hasOwn.call( obj, key );\n\t},\n\n\tisEmptyObject: function( obj ) {\n\t\tfor ( var name in obj ) {\n\t\t\treturn false;\n\t\t}\n\t\treturn true;\n\t},\n\n\terror: function( msg ) {\n\t\tthrow new Error( msg );\n\t},\n\n\tparseJSON: function( data ) {\n\t\tif ( typeof data !== \"string\" || !data ) {\n\t\t\treturn null;\n\t\t}\n\n\t\t// Make sure leading/trailing whitespace is removed (IE can't handle it)\n\t\tdata = jQuery.trim( data );\n\n\t\t// Attempt to parse using the native JSON parser first\n\t\tif ( window.JSON && window.JSON.parse ) {\n\t\t\treturn window.JSON.parse( data );\n\t\t}\n\n\t\t// Make sure the incoming data is actual JSON\n\t\t// Logic borrowed from http://json.org/json2.js\n\t\tif ( rvalidchars.test( data.replace( rvalidescape, \"@\" )\n\t\t\t.replace( rvalidtokens, \"]\" )\n\t\t\t.replace( rvalidbraces, \"\")) ) {\n\n\t\t\treturn ( new Function( \"return \" + data ) )();\n\n\t\t}\n\t\tjQuery.error( \"Invalid JSON: \" + data );\n\t},\n\n\t// Cross-browser xml parsing\n\tparseXML: function( data ) {\n\t\tvar xml, tmp;\n\t\ttry {\n\t\t\tif ( window.DOMParser ) { // Standard\n\t\t\t\ttmp = new DOMParser();\n\t\t\t\txml = tmp.parseFromString( data , \"text/xml\" );\n\t\t\t} else { // IE\n\t\t\t\txml = new ActiveXObject( \"Microsoft.XMLDOM\" );\n\t\t\t\txml.async = \"false\";\n\t\t\t\txml.loadXML( data );\n\t\t\t}\n\t\t} catch( e ) {\n\t\t\txml = undefined;\n\t\t}\n\t\tif ( !xml || !xml.documentElement || xml.getElementsByTagName( \"parsererror\" ).length ) {\n\t\t\tjQuery.error( \"Invalid XML: \" + data );\n\t\t}\n\t\treturn xml;\n\t},\n\n\tnoop: function() {},\n\n\t// Evaluates a script in a global context\n\t// Workarounds based on findings by Jim Driscoll\n\t// http://weblogs.java.net/blog/driscoll/archive/2009/09/08/eval-javascript-global-context\n\tglobalEval: function( data ) {\n\t\tif ( data && rnotwhite.test( data ) ) {\n\t\t\t// We use execScript on Internet Explorer\n\t\t\t// We use an anonymous function so that context is window\n\t\t\t// rather than jQuery in Firefox\n\t\t\t( window.execScript || function( data ) {\n\t\t\t\twindow[ \"eval\" ].call( window, data );\n\t\t\t} )( data );\n\t\t}\n\t},\n\n\t// Convert dashed to camelCase; used by the css and data modules\n\t// Microsoft forgot to hump their vendor prefix (#9572)\n\tcamelCase: function( string ) {\n\t\treturn string.replace( rmsPrefix, \"ms-\" ).replace( rdashAlpha, fcamelCase );\n\t},\n\n\tnodeName: function( elem, name ) {\n\t\treturn elem.nodeName && elem.nodeName.toUpperCase() === name.toUpperCase();\n\t},\n\n\t// args is for internal usage only\n\teach: function( object, callback, args ) {\n\t\tvar name, i = 0,\n\t\t\tlength = object.length,\n\t\t\tisObj = length === undefined || jQuery.isFunction( object );\n\n\t\tif ( args ) {\n\t\t\tif ( isObj ) {\n\t\t\t\tfor ( name in object ) {\n\t\t\t\t\tif ( callback.apply( object[ name ], args ) === false ) {\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tfor ( ; i < length; ) {\n\t\t\t\t\tif ( callback.apply( object[ i++ ], args ) === false ) {\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t// A special, fast, case for the most common use of each\n\t\t} else {\n\t\t\tif ( isObj ) {\n\t\t\t\tfor ( name in object ) {\n\t\t\t\t\tif ( callback.call( object[ name ], name, object[ name ] ) === false ) {\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tfor ( ; i < length; ) {\n\t\t\t\t\tif ( callback.call( object[ i ], i, object[ i++ ] ) === false ) {\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn object;\n\t},\n\n\t// Use native String.trim function wherever possible\n\ttrim: trim ?\n\t\tfunction( text ) {\n\t\t\treturn text == null ?\n\t\t\t\t\"\" :\n\t\t\t\ttrim.call( text );\n\t\t} :\n\n\t\t// Otherwise use our own trimming functionality\n\t\tfunction( text ) {\n\t\t\treturn text == null ?\n\t\t\t\t\"\" :\n\t\t\t\ttext.toString().replace( trimLeft, \"\" ).replace( trimRight, \"\" );\n\t\t},\n\n\t// results is for internal usage only\n\tmakeArray: function( array, results ) {\n\t\tvar ret = results || [];\n\n\t\tif ( array != null ) {\n\t\t\t// The window, strings (and functions) also have 'length'\n\t\t\t// Tweaked logic slightly to handle Blackberry 4.7 RegExp issues #6930\n\t\t\tvar type = jQuery.type( array );\n\n\t\t\tif ( array.length == null || type === \"string\" || type === \"function\" || type === \"regexp\" || jQuery.isWindow( array ) ) {\n\t\t\t\tpush.call( ret, array );\n\t\t\t} else {\n\t\t\t\tjQuery.merge( ret, array );\n\t\t\t}\n\t\t}\n\n\t\treturn ret;\n\t},\n\n\tinArray: function( elem, array, i ) {\n\t\tvar len;\n\n\t\tif ( array ) {\n\t\t\tif ( indexOf ) {\n\t\t\t\treturn indexOf.call( array, elem, i );\n\t\t\t}\n\n\t\t\tlen = array.length;\n\t\t\ti = i ? i < 0 ? Math.max( 0, len + i ) : i : 0;\n\n\t\t\tfor ( ; i < len; i++ ) {\n\t\t\t\t// Skip accessing in sparse arrays\n\t\t\t\tif ( i in array && array[ i ] === elem ) {\n\t\t\t\t\treturn i;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn -1;\n\t},\n\n\tmerge: function( first, second ) {\n\t\tvar i = first.length,\n\t\t\tj = 0;\n\n\t\tif ( typeof second.length === \"number\" ) {\n\t\t\tfor ( var l = second.length; j < l; j++ ) {\n\t\t\t\tfirst[ i++ ] = second[ j ];\n\t\t\t}\n\n\t\t} else {\n\t\t\twhile ( second[j] !== undefined ) {\n\t\t\t\tfirst[ i++ ] = second[ j++ ];\n\t\t\t}\n\t\t}\n\n\t\tfirst.length = i;\n\n\t\treturn first;\n\t},\n\n\tgrep: function( elems, callback, inv ) {\n\t\tvar ret = [], retVal;\n\t\tinv = !!inv;\n\n\t\t// Go through the array, only saving the items\n\t\t// that pass the validator function\n\t\tfor ( var i = 0, length = elems.length; i < length; i++ ) {\n\t\t\tretVal = !!callback( elems[ i ], i );\n\t\t\tif ( inv !== retVal ) {\n\t\t\t\tret.push( elems[ i ] );\n\t\t\t}\n\t\t}\n\n\t\treturn ret;\n\t},\n\n\t// arg is for internal usage only\n\tmap: function( elems, callback, arg ) {\n\t\tvar value, key, ret = [],\n\t\t\ti = 0,\n\t\t\tlength = elems.length,\n\t\t\t// jquery objects are treated as arrays\n\t\t\tisArray = elems instanceof jQuery || length !== undefined && typeof length === \"number\" && ( ( length > 0 && elems[ 0 ] && elems[ length -1 ] ) || length === 0 || jQuery.isArray( elems ) ) ;\n\n\t\t// Go through the array, translating each of the items to their\n\t\tif ( isArray ) {\n\t\t\tfor ( ; i < length; i++ ) {\n\t\t\t\tvalue = callback( elems[ i ], i, arg );\n\n\t\t\t\tif ( value != null ) {\n\t\t\t\t\tret[ ret.length ] = value;\n\t\t\t\t}\n\t\t\t}\n\n\t\t// Go through every key on the object,\n\t\t} else {\n\t\t\tfor ( key in elems ) {\n\t\t\t\tvalue = callback( elems[ key ], key, arg );\n\n\t\t\t\tif ( value != null ) {\n\t\t\t\t\tret[ ret.length ] = value;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Flatten any nested arrays\n\t\treturn ret.concat.apply( [], ret );\n\t},\n\n\t// A global GUID counter for objects\n\tguid: 1,\n\n\t// Bind a function to a context, optionally partially applying any\n\t// arguments.\n\tproxy: function( fn, context ) {\n\t\tif ( typeof context === \"string\" ) {\n\t\t\tvar tmp = fn[ context ];\n\t\t\tcontext = fn;\n\t\t\tfn = tmp;\n\t\t}\n\n\t\t// Quick check to determine if target is callable, in the spec\n\t\t// this throws a TypeError, but we will just return undefined.\n\t\tif ( !jQuery.isFunction( fn ) ) {\n\t\t\treturn undefined;\n\t\t}\n\n\t\t// Simulated bind\n\t\tvar args = slice.call( arguments, 2 ),\n\t\t\tproxy = function() {\n\t\t\t\treturn fn.apply( context, args.concat( slice.call( arguments ) ) );\n\t\t\t};\n\n\t\t// Set the guid of unique handler to the same of original handler, so it can be removed\n\t\tproxy.guid = fn.guid = fn.guid || proxy.guid || jQuery.guid++;\n\n\t\treturn proxy;\n\t},\n\n\t// Mutifunctional method to get and set values to a collection\n\t// The value/s can optionally be executed if it's a function\n\taccess: function( elems, key, value, exec, fn, pass ) {\n\t\tvar length = elems.length;\n\n\t\t// Setting many attributes\n\t\tif ( typeof key === \"object\" ) {\n\t\t\tfor ( var k in key ) {\n\t\t\t\tjQuery.access( elems, k, key[k], exec, fn, value );\n\t\t\t}\n\t\t\treturn elems;\n\t\t}\n\n\t\t// Setting one attribute\n\t\tif ( value !== undefined ) {\n\t\t\t// Optionally, function values get executed if exec is true\n\t\t\texec = !pass && exec && jQuery.isFunction(value);\n\n\t\t\tfor ( var i = 0; i < length; i++ ) {\n\t\t\t\tfn( elems[i], key, exec ? value.call( elems[i], i, fn( elems[i], key ) ) : value, pass );\n\t\t\t}\n\n\t\t\treturn elems;\n\t\t}\n\n\t\t// Getting an attribute\n\t\treturn length ? fn( elems[0], key ) : undefined;\n\t},\n\n\tnow: function() {\n\t\treturn ( new Date() ).getTime();\n\t},\n\n\t// Use of jQuery.browser is frowned upon.\n\t// More details: http://docs.jquery.com/Utilities/jQuery.browser\n\tuaMatch: function( ua ) {\n\t\tua = ua.toLowerCase();\n\n\t\tvar match = rwebkit.exec( ua ) ||\n\t\t\tropera.exec( ua ) ||\n\t\t\trmsie.exec( ua ) ||\n\t\t\tua.indexOf(\"compatible\") < 0 && rmozilla.exec( ua ) ||\n\t\t\t[];\n\n\t\treturn { browser: match[1] || \"\", version: match[2] || \"0\" };\n\t},\n\n\tsub: function() {\n\t\tfunction jQuerySub( selector, context ) {\n\t\t\treturn new jQuerySub.fn.init( selector, context );\n\t\t}\n\t\tjQuery.extend( true, jQuerySub, this );\n\t\tjQuerySub.superclass = this;\n\t\tjQuerySub.fn = jQuerySub.prototype = this();\n\t\tjQuerySub.fn.constructor = jQuerySub;\n\t\tjQuerySub.sub = this.sub;\n\t\tjQuerySub.fn.init = function init( selector, context ) {\n\t\t\tif ( context && context instanceof jQuery && !(context instanceof jQuerySub) ) {\n\t\t\t\tcontext = jQuerySub( context );\n\t\t\t}\n\n\t\t\treturn jQuery.fn.init.call( this, selector, context, rootjQuerySub );\n\t\t};\n\t\tjQuerySub.fn.init.prototype = jQuerySub.fn;\n\t\tvar rootjQuerySub = jQuerySub(document);\n\t\treturn jQuerySub;\n\t},\n\n\tbrowser: {}\n});\n\n// Populate the class2type map\njQuery.each(\"Boolean Number String Function Array Date RegExp Object\".split(\" \"), function(i, name) {\n\tclass2type[ \"[object \" + name + \"]\" ] = name.toLowerCase();\n});\n\nbrowserMatch = jQuery.uaMatch( userAgent );\nif ( browserMatch.browser ) {\n\tjQuery.browser[ browserMatch.browser ] = true;\n\tjQuery.browser.version = browserMatch.version;\n}\n\n// Deprecated, use jQuery.browser.webkit instead\nif ( jQuery.browser.webkit ) {\n\tjQuery.browser.safari = true;\n}\n\n// IE doesn't match non-breaking spaces with \\s\nif ( rnotwhite.test( \"\\xA0\" ) ) {\n\ttrimLeft = /^[\\s\\xA0]+/;\n\ttrimRight = /[\\s\\xA0]+$/;\n}\n\n// All jQuery objects should point back to these\nrootjQuery = jQuery(document);\n\n// Cleanup functions for the document ready method\nif ( document.addEventListener ) {\n\tDOMContentLoaded = function() {\n\t\tdocument.removeEventListener( \"DOMContentLoaded\", DOMContentLoaded, false );\n\t\tjQuery.ready();\n\t};\n\n} else if ( document.attachEvent ) {\n\tDOMContentLoaded = function() {\n\t\t// Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443).\n\t\tif ( document.readyState === \"complete\" ) {\n\t\t\tdocument.detachEvent( \"onreadystatechange\", DOMContentLoaded );\n\t\t\tjQuery.ready();\n\t\t}\n\t};\n}\n\n// The DOM ready check for Internet Explorer\nfunction doScrollCheck() {\n\tif ( jQuery.isReady ) {\n\t\treturn;\n\t}\n\n\ttry {\n\t\t// If IE is used, use the trick by Diego Perini\n\t\t// http://javascript.nwbox.com/IEContentLoaded/\n\t\tdocument.documentElement.doScroll(\"left\");\n\t} catch(e) {\n\t\tsetTimeout( doScrollCheck, 1 );\n\t\treturn;\n\t}\n\n\t// and execute any waiting functions\n\tjQuery.ready();\n}\n\nreturn jQuery;\n\n})();\n\n\n// String to Object flags format cache\nvar flagsCache = {};\n\n// Convert String-formatted flags into Object-formatted ones and store in cache\nfunction createFlags( flags ) {\n\tvar object = flagsCache[ flags ] = {},\n\t\ti, length;\n\tflags = flags.split( /\\s+/ );\n\tfor ( i = 0, length = flags.length; i < length; i++ ) {\n\t\tobject[ flags[i] ] = true;\n\t}\n\treturn object;\n}\n\n/*\n * Create a callback list using the following parameters:\n *\n *\tflags:\tan optional list of space-separated flags that will change how\n *\t\t\tthe callback list behaves\n *\n * By default a callback list will act like an event callback list and can be\n * \"fired\" multiple times.\n *\n * Possible flags:\n *\n *\tonce:\t\t\twill ensure the callback list can only be fired once (like a Deferred)\n *\n *\tmemory:\t\t\twill keep track of previous values and will call any callback added\n *\t\t\t\t\tafter the list has been fired right away with the latest \"memorized\"\n *\t\t\t\t\tvalues (like a Deferred)\n *\n *\tunique:\t\t\twill ensure a callback can only be added once (no duplicate in the list)\n *\n *\tstopOnFalse:\tinterrupt callings when a callback returns false\n *\n */\njQuery.Callbacks = function( flags ) {\n\n\t// Convert flags from String-formatted to Object-formatted\n\t// (we check in cache first)\n\tflags = flags ? ( flagsCache[ flags ] || createFlags( flags ) ) : {};\n\n\tvar // Actual callback list\n\t\tlist = [],\n\t\t// Stack of fire calls for repeatable lists\n\t\tstack = [],\n\t\t// Last fire value (for non-forgettable lists)\n\t\tmemory,\n\t\t// Flag to know if list is currently firing\n\t\tfiring,\n\t\t// First callback to fire (used internally by add and fireWith)\n\t\tfiringStart,\n\t\t// End of the loop when firing\n\t\tfiringLength,\n\t\t// Index of currently firing callback (modified by remove if needed)\n\t\tfiringIndex,\n\t\t// Add one or several callbacks to the list\n\t\tadd = function( args ) {\n\t\t\tvar i,\n\t\t\t\tlength,\n\t\t\t\telem,\n\t\t\t\ttype,\n\t\t\t\tactual;\n\t\t\tfor ( i = 0, length = args.length; i < length; i++ ) {\n\t\t\t\telem = args[ i ];\n\t\t\t\ttype = jQuery.type( elem );\n\t\t\t\tif ( type === \"array\" ) {\n\t\t\t\t\t// Inspect recursively\n\t\t\t\t\tadd( elem );\n\t\t\t\t} else if ( type === \"function\" ) {\n\t\t\t\t\t// Add if not in unique mode and callback is not in\n\t\t\t\t\tif ( !flags.unique || !self.has( elem ) ) {\n\t\t\t\t\t\tlist.push( elem );\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t// Fire callbacks\n\t\tfire = function( context, args ) {\n\t\t\targs = args || [];\n\t\t\tmemory = !flags.memory || [ context, args ];\n\t\t\tfiring = true;\n\t\t\tfiringIndex = firingStart || 0;\n\t\t\tfiringStart = 0;\n\t\t\tfiringLength = list.length;\n\t\t\tfor ( ; list && firingIndex < firingLength; firingIndex++ ) {\n\t\t\t\tif ( list[ firingIndex ].apply( context, args ) === false && flags.stopOnFalse ) {\n\t\t\t\t\tmemory = true; // Mark as halted\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\tfiring = false;\n\t\t\tif ( list ) {\n\t\t\t\tif ( !flags.once ) {\n\t\t\t\t\tif ( stack && stack.length ) {\n\t\t\t\t\t\tmemory = stack.shift();\n\t\t\t\t\t\tself.fireWith( memory[ 0 ], memory[ 1 ] );\n\t\t\t\t\t}\n\t\t\t\t} else if ( memory === true ) {\n\t\t\t\t\tself.disable();\n\t\t\t\t} else {\n\t\t\t\t\tlist = [];\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t// Actual Callbacks object\n\t\tself = {\n\t\t\t// Add a callback or a collection of callbacks to the list\n\t\t\tadd: function() {\n\t\t\t\tif ( list ) {\n\t\t\t\t\tvar length = list.length;\n\t\t\t\t\tadd( arguments );\n\t\t\t\t\t// Do we need to add the callbacks to the\n\t\t\t\t\t// current firing batch?\n\t\t\t\t\tif ( firing ) {\n\t\t\t\t\t\tfiringLength = list.length;\n\t\t\t\t\t// With memory, if we're not firing then\n\t\t\t\t\t// we should call right away, unless previous\n\t\t\t\t\t// firing was halted (stopOnFalse)\n\t\t\t\t\t} else if ( memory && memory !== true ) {\n\t\t\t\t\t\tfiringStart = length;\n\t\t\t\t\t\tfire( memory[ 0 ], memory[ 1 ] );\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn this;\n\t\t\t},\n\t\t\t// Remove a callback from the list\n\t\t\tremove: function() {\n\t\t\t\tif ( list ) {\n\t\t\t\t\tvar args = arguments,\n\t\t\t\t\t\targIndex = 0,\n\t\t\t\t\t\targLength = args.length;\n\t\t\t\t\tfor ( ; argIndex < argLength ; argIndex++ ) {\n\t\t\t\t\t\tfor ( var i = 0; i < list.length; i++ ) {\n\t\t\t\t\t\t\tif ( args[ argIndex ] === list[ i ] ) {\n\t\t\t\t\t\t\t\t// Handle firingIndex and firingLength\n\t\t\t\t\t\t\t\tif ( firing ) {\n\t\t\t\t\t\t\t\t\tif ( i <= firingLength ) {\n\t\t\t\t\t\t\t\t\t\tfiringLength--;\n\t\t\t\t\t\t\t\t\t\tif ( i <= firingIndex ) {\n\t\t\t\t\t\t\t\t\t\t\tfiringIndex--;\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t// Remove the element\n\t\t\t\t\t\t\t\tlist.splice( i--, 1 );\n\t\t\t\t\t\t\t\t// If we have some unicity property then\n\t\t\t\t\t\t\t\t// we only need to do this once\n\t\t\t\t\t\t\t\tif ( flags.unique ) {\n\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn this;\n\t\t\t},\n\t\t\t// Control if a given callback is in the list\n\t\t\thas: function( fn ) {\n\t\t\t\tif ( list ) {\n\t\t\t\t\tvar i = 0,\n\t\t\t\t\t\tlength = list.length;\n\t\t\t\t\tfor ( ; i < length; i++ ) {\n\t\t\t\t\t\tif ( fn === list[ i ] ) {\n\t\t\t\t\t\t\treturn true;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn false;\n\t\t\t},\n\t\t\t// Remove all callbacks from the list\n\t\t\tempty: function() {\n\t\t\t\tlist = [];\n\t\t\t\treturn this;\n\t\t\t},\n\t\t\t// Have the list do nothing anymore\n\t\t\tdisable: function() {\n\t\t\t\tlist = stack = memory = undefined;\n\t\t\t\treturn this;\n\t\t\t},\n\t\t\t// Is it disabled?\n\t\t\tdisabled: function() {\n\t\t\t\treturn !list;\n\t\t\t},\n\t\t\t// Lock the list in its current state\n\t\t\tlock: function() {\n\t\t\t\tstack = undefined;\n\t\t\t\tif ( !memory || memory === true ) {\n\t\t\t\t\tself.disable();\n\t\t\t\t}\n\t\t\t\treturn this;\n\t\t\t},\n\t\t\t// Is it locked?\n\t\t\tlocked: function() {\n\t\t\t\treturn !stack;\n\t\t\t},\n\t\t\t// Call all callbacks with the given context and arguments\n\t\t\tfireWith: function( context, args ) {\n\t\t\t\tif ( stack ) {\n\t\t\t\t\tif ( firing ) {\n\t\t\t\t\t\tif ( !flags.once ) {\n\t\t\t\t\t\t\tstack.push( [ context, args ] );\n\t\t\t\t\t\t}\n\t\t\t\t\t} else if ( !( flags.once && memory ) ) {\n\t\t\t\t\t\tfire( context, args );\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn this;\n\t\t\t},\n\t\t\t// Call all the callbacks with the given arguments\n\t\t\tfire: function() {\n\t\t\t\tself.fireWith( this, arguments );\n\t\t\t\treturn this;\n\t\t\t},\n\t\t\t// To know if the callbacks have already been called at least once\n\t\t\tfired: function() {\n\t\t\t\treturn !!memory;\n\t\t\t}\n\t\t};\n\n\treturn self;\n};\n\n\n\n\nvar // Static reference to slice\n\tsliceDeferred = [].slice;\n\njQuery.extend({\n\n\tDeferred: function( func ) {\n\t\tvar doneList = jQuery.Callbacks( \"once memory\" ),\n\t\t\tfailList = jQuery.Callbacks( \"once memory\" ),\n\t\t\tprogressList = jQuery.Callbacks( \"memory\" ),\n\t\t\tstate = \"pending\",\n\t\t\tlists = {\n\t\t\t\tresolve: doneList,\n\t\t\t\treject: failList,\n\t\t\t\tnotify: progressList\n\t\t\t},\n\t\t\tpromise = {\n\t\t\t\tdone: doneList.add,\n\t\t\t\tfail: failList.add,\n\t\t\t\tprogress: progressList.add,\n\n\t\t\t\tstate: function() {\n\t\t\t\t\treturn state;\n\t\t\t\t},\n\n\t\t\t\t// Deprecated\n\t\t\t\tisResolved: doneList.fired,\n\t\t\t\tisRejected: failList.fired,\n\n\t\t\t\tthen: function( doneCallbacks, failCallbacks, progressCallbacks ) {\n\t\t\t\t\tdeferred.done( doneCallbacks ).fail( failCallbacks ).progress( progressCallbacks );\n\t\t\t\t\treturn this;\n\t\t\t\t},\n\t\t\t\talways: function() {\n\t\t\t\t\tdeferred.done.apply( deferred, arguments ).fail.apply( deferred, arguments );\n\t\t\t\t\treturn this;\n\t\t\t\t},\n\t\t\t\tpipe: function( fnDone, fnFail, fnProgress ) {\n\t\t\t\t\treturn jQuery.Deferred(function( newDefer ) {\n\t\t\t\t\t\tjQuery.each( {\n\t\t\t\t\t\t\tdone: [ fnDone, \"resolve\" ],\n\t\t\t\t\t\t\tfail: [ fnFail, \"reject\" ],\n\t\t\t\t\t\t\tprogress: [ fnProgress, \"notify\" ]\n\t\t\t\t\t\t}, function( handler, data ) {\n\t\t\t\t\t\t\tvar fn = data[ 0 ],\n\t\t\t\t\t\t\t\taction = data[ 1 ],\n\t\t\t\t\t\t\t\treturned;\n\t\t\t\t\t\t\tif ( jQuery.isFunction( fn ) ) {\n\t\t\t\t\t\t\t\tdeferred[ handler ](function() {\n\t\t\t\t\t\t\t\t\treturned = fn.apply( this, arguments );\n\t\t\t\t\t\t\t\t\tif ( returned && jQuery.isFunction( returned.promise ) ) {\n\t\t\t\t\t\t\t\t\t\treturned.promise().then( newDefer.resolve, newDefer.reject, newDefer.notify );\n\t\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\t\tnewDefer[ action + \"With\" ]( this === deferred ? newDefer : this, [ returned ] );\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tdeferred[ handler ]( newDefer[ action ] );\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t});\n\t\t\t\t\t}).promise();\n\t\t\t\t},\n\t\t\t\t// Get a promise for this deferred\n\t\t\t\t// If obj is provided, the promise aspect is added to the object\n\t\t\t\tpromise: function( obj ) {\n\t\t\t\t\tif ( obj == null ) {\n\t\t\t\t\t\tobj = promise;\n\t\t\t\t\t} else {\n\t\t\t\t\t\tfor ( var key in promise ) {\n\t\t\t\t\t\t\tobj[ key ] = promise[ key ];\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\treturn obj;\n\t\t\t\t}\n\t\t\t},\n\t\t\tdeferred = promise.promise({}),\n\t\t\tkey;\n\n\t\tfor ( key in lists ) {\n\t\t\tdeferred[ key ] = lists[ key ].fire;\n\t\t\tdeferred[ key + \"With\" ] = lists[ key ].fireWith;\n\t\t}\n\n\t\t// Handle state\n\t\tdeferred.done( function() {\n\t\t\tstate = \"resolved\";\n\t\t}, failList.disable, progressList.lock ).fail( function() {\n\t\t\tstate = \"rejected\";\n\t\t}, doneList.disable, progressList.lock );\n\n\t\t// Call given func if any\n\t\tif ( func ) {\n\t\t\tfunc.call( deferred, deferred );\n\t\t}\n\n\t\t// All done!\n\t\treturn deferred;\n\t},\n\n\t// Deferred helper\n\twhen: function( firstParam ) {\n\t\tvar args = sliceDeferred.call( arguments, 0 ),\n\t\t\ti = 0,\n\t\t\tlength = args.length,\n\t\t\tpValues = new Array( length ),\n\t\t\tcount = length,\n\t\t\tpCount = length,\n\t\t\tdeferred = length <= 1 && firstParam && jQuery.isFunction( firstParam.promise ) ?\n\t\t\t\tfirstParam :\n\t\t\t\tjQuery.Deferred(),\n\t\t\tpromise = deferred.promise();\n\t\tfunction resolveFunc( i ) {\n\t\t\treturn function( value ) {\n\t\t\t\targs[ i ] = arguments.length > 1 ? sliceDeferred.call( arguments, 0 ) : value;\n\t\t\t\tif ( !( --count ) ) {\n\t\t\t\t\tdeferred.resolveWith( deferred, args );\n\t\t\t\t}\n\t\t\t};\n\t\t}\n\t\tfunction progressFunc( i ) {\n\t\t\treturn function( value ) {\n\t\t\t\tpValues[ i ] = arguments.length > 1 ? sliceDeferred.call( arguments, 0 ) : value;\n\t\t\t\tdeferred.notifyWith( promise, pValues );\n\t\t\t};\n\t\t}\n\t\tif ( length > 1 ) {\n\t\t\tfor ( ; i < length; i++ ) {\n\t\t\t\tif ( args[ i ] && args[ i ].promise && jQuery.isFunction( args[ i ].promise ) ) {\n\t\t\t\t\targs[ i ].promise().then( resolveFunc(i), deferred.reject, progressFunc(i) );\n\t\t\t\t} else {\n\t\t\t\t\t--count;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif ( !count ) {\n\t\t\t\tdeferred.resolveWith( deferred, args );\n\t\t\t}\n\t\t} else if ( deferred !== firstParam ) {\n\t\t\tdeferred.resolveWith( deferred, length ? [ firstParam ] : [] );\n\t\t}\n\t\treturn promise;\n\t}\n});\n\n\n\n\njQuery.support = (function() {\n\n\tvar support,\n\t\tall,\n\t\ta,\n\t\tselect,\n\t\topt,\n\t\tinput,\n\t\tmarginDiv,\n\t\tfragment,\n\t\ttds,\n\t\tevents,\n\t\teventName,\n\t\ti,\n\t\tisSupported,\n\t\tdiv = document.createElement( \"div\" ),\n\t\tdocumentElement = document.documentElement;\n\n\t// Preliminary tests\n\tdiv.setAttribute(\"className\", \"t\");\n\tdiv.innerHTML = \"   <link/><table></table><a href='/a' style='top:1px;float:left;opacity:.55;'>a</a><input type='checkbox'/>\";\n\n\tall = div.getElementsByTagName( \"*\" );\n\ta = div.getElementsByTagName( \"a\" )[ 0 ];\n\n\t// Can't get basic test support\n\tif ( !all || !all.length || !a ) {\n\t\treturn {};\n\t}\n\n\t// First batch of supports tests\n\tselect = document.createElement( \"select\" );\n\topt = select.appendChild( document.createElement(\"option\") );\n\tinput = div.getElementsByTagName( \"input\" )[ 0 ];\n\n\tsupport = {\n\t\t// IE strips leading whitespace when .innerHTML is used\n\t\tleadingWhitespace: ( div.firstChild.nodeType === 3 ),\n\n\t\t// Make sure that tbody elements aren't automatically inserted\n\t\t// IE will insert them into empty tables\n\t\ttbody: !div.getElementsByTagName(\"tbody\").length,\n\n\t\t// Make sure that link elements get serialized correctly by innerHTML\n\t\t// This requires a wrapper element in IE\n\t\thtmlSerialize: !!div.getElementsByTagName(\"link\").length,\n\n\t\t// Get the style information from getAttribute\n\t\t// (IE uses .cssText instead)\n\t\tstyle: /top/.test( a.getAttribute(\"style\") ),\n\n\t\t// Make sure that URLs aren't manipulated\n\t\t// (IE normalizes it by default)\n\t\threfNormalized: ( a.getAttribute(\"href\") === \"/a\" ),\n\n\t\t// Make sure that element opacity exists\n\t\t// (IE uses filter instead)\n\t\t// Use a regex to work around a WebKit issue. See #5145\n\t\topacity: /^0.55/.test( a.style.opacity ),\n\n\t\t// Verify style float existence\n\t\t// (IE uses styleFloat instead of cssFloat)\n\t\tcssFloat: !!a.style.cssFloat,\n\n\t\t// Make sure that if no value is specified for a checkbox\n\t\t// that it defaults to \"on\".\n\t\t// (WebKit defaults to \"\" instead)\n\t\tcheckOn: ( input.value === \"on\" ),\n\n\t\t// Make sure that a selected-by-default option has a working selected property.\n\t\t// (WebKit defaults to false instead of true, IE too, if it's in an optgroup)\n\t\toptSelected: opt.selected,\n\n\t\t// Test setAttribute on camelCase class. If it works, we need attrFixes when doing get/setAttribute (ie6/7)\n\t\tgetSetAttribute: div.className !== \"t\",\n\n\t\t// Tests for enctype support on a form(#6743)\n\t\tenctype: !!document.createElement(\"form\").enctype,\n\n\t\t// Makes sure cloning an html5 element does not cause problems\n\t\t// Where outerHTML is undefined, this still works\n\t\thtml5Clone: document.createElement(\"nav\").cloneNode( true ).outerHTML !== \"<:nav></:nav>\",\n\n\t\t// Will be defined later\n\t\tsubmitBubbles: true,\n\t\tchangeBubbles: true,\n\t\tfocusinBubbles: false,\n\t\tdeleteExpando: true,\n\t\tnoCloneEvent: true,\n\t\tinlineBlockNeedsLayout: false,\n\t\tshrinkWrapBlocks: false,\n\t\treliableMarginRight: true\n\t};\n\n\t// Make sure checked status is properly cloned\n\tinput.checked = true;\n\tsupport.noCloneChecked = input.cloneNode( true ).checked;\n\n\t// Make sure that the options inside disabled selects aren't marked as disabled\n\t// (WebKit marks them as disabled)\n\tselect.disabled = true;\n\tsupport.optDisabled = !opt.disabled;\n\n\t// Test to see if it's possible to delete an expando from an element\n\t// Fails in Internet Explorer\n\ttry {\n\t\tdelete div.test;\n\t} catch( e ) {\n\t\tsupport.deleteExpando = false;\n\t}\n\n\tif ( !div.addEventListener && div.attachEvent && div.fireEvent ) {\n\t\tdiv.attachEvent( \"onclick\", function() {\n\t\t\t// Cloning a node shouldn't copy over any\n\t\t\t// bound event handlers (IE does this)\n\t\t\tsupport.noCloneEvent = false;\n\t\t});\n\t\tdiv.cloneNode( true ).fireEvent( \"onclick\" );\n\t}\n\n\t// Check if a radio maintains its value\n\t// after being appended to the DOM\n\tinput = document.createElement(\"input\");\n\tinput.value = \"t\";\n\tinput.setAttribute(\"type\", \"radio\");\n\tsupport.radioValue = input.value === \"t\";\n\n\tinput.setAttribute(\"checked\", \"checked\");\n\tdiv.appendChild( input );\n\tfragment = document.createDocumentFragment();\n\tfragment.appendChild( div.lastChild );\n\n\t// WebKit doesn't clone checked state correctly in fragments\n\tsupport.checkClone = fragment.cloneNode( true ).cloneNode( true ).lastChild.checked;\n\n\t// Check if a disconnected checkbox will retain its checked\n\t// value of true after appended to the DOM (IE6/7)\n\tsupport.appendChecked = input.checked;\n\n\tfragment.removeChild( input );\n\tfragment.appendChild( div );\n\n\tdiv.innerHTML = \"\";\n\n\t// Check if div with explicit width and no margin-right incorrectly\n\t// gets computed margin-right based on width of container. For more\n\t// info see bug #3333\n\t// Fails in WebKit before Feb 2011 nightlies\n\t// WebKit Bug 13343 - getComputedStyle returns wrong value for margin-right\n\tif ( window.getComputedStyle ) {\n\t\tmarginDiv = document.createElement( \"div\" );\n\t\tmarginDiv.style.width = \"0\";\n\t\tmarginDiv.style.marginRight = \"0\";\n\t\tdiv.style.width = \"2px\";\n\t\tdiv.appendChild( marginDiv );\n\t\tsupport.reliableMarginRight =\n\t\t\t( parseInt( ( window.getComputedStyle( marginDiv, null ) || { marginRight: 0 } ).marginRight, 10 ) || 0 ) === 0;\n\t}\n\n\t// Technique from Juriy Zaytsev\n\t// http://perfectionkills.com/detecting-event-support-without-browser-sniffing/\n\t// We only care about the case where non-standard event systems\n\t// are used, namely in IE. Short-circuiting here helps us to\n\t// avoid an eval call (in setAttribute) which can cause CSP\n\t// to go haywire. See: https://developer.mozilla.org/en/Security/CSP\n\tif ( div.attachEvent ) {\n\t\tfor( i in {\n\t\t\tsubmit: 1,\n\t\t\tchange: 1,\n\t\t\tfocusin: 1\n\t\t}) {\n\t\t\teventName = \"on\" + i;\n\t\t\tisSupported = ( eventName in div );\n\t\t\tif ( !isSupported ) {\n\t\t\t\tdiv.setAttribute( eventName, \"return;\" );\n\t\t\t\tisSupported = ( typeof div[ eventName ] === \"function\" );\n\t\t\t}\n\t\t\tsupport[ i + \"Bubbles\" ] = isSupported;\n\t\t}\n\t}\n\n\tfragment.removeChild( div );\n\n\t// Null elements to avoid leaks in IE\n\tfragment = select = opt = marginDiv = div = input = null;\n\n\t// Run tests that need a body at doc ready\n\tjQuery(function() {\n\t\tvar container, outer, inner, table, td, offsetSupport,\n\t\t\tconMarginTop, ptlm, vb, style, html,\n\t\t\tbody = document.getElementsByTagName(\"body\")[0];\n\n\t\tif ( !body ) {\n\t\t\t// Return for frameset docs that don't have a body\n\t\t\treturn;\n\t\t}\n\n\t\tconMarginTop = 1;\n\t\tptlm = \"position:absolute;top:0;left:0;width:1px;height:1px;margin:0;\";\n\t\tvb = \"visibility:hidden;border:0;\";\n\t\tstyle = \"style='\" + ptlm + \"border:5px solid #000;padding:0;'\";\n\t\thtml = \"<div \" + style + \"><div></div></div>\" +\n\t\t\t\"<table \" + style + \" cellpadding='0' cellspacing='0'>\" +\n\t\t\t\"<tr><td></td></tr></table>\";\n\n\t\tcontainer = document.createElement(\"div\");\n\t\tcontainer.style.cssText = vb + \"width:0;height:0;position:static;top:0;margin-top:\" + conMarginTop + \"px\";\n\t\tbody.insertBefore( container, body.firstChild );\n\n\t\t// Construct the test element\n\t\tdiv = document.createElement(\"div\");\n\t\tcontainer.appendChild( div );\n\n\t\t// Check if table cells still have offsetWidth/Height when they are set\n\t\t// to display:none and there are still other visible table cells in a\n\t\t// table row; if so, offsetWidth/Height are not reliable for use when\n\t\t// determining if an element has been hidden directly using\n\t\t// display:none (it is still safe to use offsets if a parent element is\n\t\t// hidden; don safety goggles and see bug #4512 for more information).\n\t\t// (only IE 8 fails this test)\n\t\tdiv.innerHTML = \"<table><tr><td style='padding:0;border:0;display:none'></td><td>t</td></tr></table>\";\n\t\ttds = div.getElementsByTagName( \"td\" );\n\t\tisSupported = ( tds[ 0 ].offsetHeight === 0 );\n\n\t\ttds[ 0 ].style.display = \"\";\n\t\ttds[ 1 ].style.display = \"none\";\n\n\t\t// Check if empty table cells still have offsetWidth/Height\n\t\t// (IE <= 8 fail this test)\n\t\tsupport.reliableHiddenOffsets = isSupported && ( tds[ 0 ].offsetHeight === 0 );\n\n\t\t// Figure out if the W3C box model works as expected\n\t\tdiv.innerHTML = \"\";\n\t\tdiv.style.width = div.style.paddingLeft = \"1px\";\n\t\tjQuery.boxModel = support.boxModel = div.offsetWidth === 2;\n\n\t\tif ( typeof div.style.zoom !== \"undefined\" ) {\n\t\t\t// Check if natively block-level elements act like inline-block\n\t\t\t// elements when setting their display to 'inline' and giving\n\t\t\t// them layout\n\t\t\t// (IE < 8 does this)\n\t\t\tdiv.style.display = \"inline\";\n\t\t\tdiv.style.zoom = 1;\n\t\t\tsupport.inlineBlockNeedsLayout = ( div.offsetWidth === 2 );\n\n\t\t\t// Check if elements with layout shrink-wrap their children\n\t\t\t// (IE 6 does this)\n\t\t\tdiv.style.display = \"\";\n\t\t\tdiv.innerHTML = \"<div style='width:4px;'></div>\";\n\t\t\tsupport.shrinkWrapBlocks = ( div.offsetWidth !== 2 );\n\t\t}\n\n\t\tdiv.style.cssText = ptlm + vb;\n\t\tdiv.innerHTML = html;\n\n\t\touter = div.firstChild;\n\t\tinner = outer.firstChild;\n\t\ttd = outer.nextSibling.firstChild.firstChild;\n\n\t\toffsetSupport = {\n\t\t\tdoesNotAddBorder: ( inner.offsetTop !== 5 ),\n\t\t\tdoesAddBorderForTableAndCells: ( td.offsetTop === 5 )\n\t\t};\n\n\t\tinner.style.position = \"fixed\";\n\t\tinner.style.top = \"20px\";\n\n\t\t// safari subtracts parent border width here which is 5px\n\t\toffsetSupport.fixedPosition = ( inner.offsetTop === 20 || inner.offsetTop === 15 );\n\t\tinner.style.position = inner.style.top = \"\";\n\n\t\touter.style.overflow = \"hidden\";\n\t\touter.style.position = \"relative\";\n\n\t\toffsetSupport.subtractsBorderForOverflowNotVisible = ( inner.offsetTop === -5 );\n\t\toffsetSupport.doesNotIncludeMarginInBodyOffset = ( body.offsetTop !== conMarginTop );\n\n\t\tbody.removeChild( container );\n\t\tdiv  = container = null;\n\n\t\tjQuery.extend( support, offsetSupport );\n\t});\n\n\treturn support;\n})();\n\n\n\n\nvar rbrace = /^(?:\\{.*\\}|\\[.*\\])$/,\n\trmultiDash = /([A-Z])/g;\n\njQuery.extend({\n\tcache: {},\n\n\t// Please use with caution\n\tuuid: 0,\n\n\t// Unique for each copy of jQuery on the page\n\t// Non-digits removed to match rinlinejQuery\n\texpando: \"jQuery\" + ( jQuery.fn.jquery + Math.random() ).replace( /\\D/g, \"\" ),\n\n\t// The following elements throw uncatchable exceptions if you\n\t// attempt to add expando properties to them.\n\tnoData: {\n\t\t\"embed\": true,\n\t\t// Ban all objects except for Flash (which handle expandos)\n\t\t\"object\": \"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000\",\n\t\t\"applet\": true\n\t},\n\n\thasData: function( elem ) {\n\t\telem = elem.nodeType ? jQuery.cache[ elem[jQuery.expando] ] : elem[ jQuery.expando ];\n\t\treturn !!elem && !isEmptyDataObject( elem );\n\t},\n\n\tdata: function( elem, name, data, pvt /* Internal Use Only */ ) {\n\t\tif ( !jQuery.acceptData( elem ) ) {\n\t\t\treturn;\n\t\t}\n\n\t\tvar privateCache, thisCache, ret,\n\t\t\tinternalKey = jQuery.expando,\n\t\t\tgetByName = typeof name === \"string\",\n\n\t\t\t// We have to handle DOM nodes and JS objects differently because IE6-7\n\t\t\t// can't GC object references properly across the DOM-JS boundary\n\t\t\tisNode = elem.nodeType,\n\n\t\t\t// Only DOM nodes need the global jQuery cache; JS object data is\n\t\t\t// attached directly to the object so GC can occur automatically\n\t\t\tcache = isNode ? jQuery.cache : elem,\n\n\t\t\t// Only defining an ID for JS objects if its cache already exists allows\n\t\t\t// the code to shortcut on the same path as a DOM node with no cache\n\t\t\tid = isNode ? elem[ internalKey ] : elem[ internalKey ] && internalKey,\n\t\t\tisEvents = name === \"events\";\n\n\t\t// Avoid doing any more work than we need to when trying to get data on an\n\t\t// object that has no data at all\n\t\tif ( (!id || !cache[id] || (!isEvents && !pvt && !cache[id].data)) && getByName && data === undefined ) {\n\t\t\treturn;\n\t\t}\n\n\t\tif ( !id ) {\n\t\t\t// Only DOM nodes need a new unique ID for each element since their data\n\t\t\t// ends up in the global cache\n\t\t\tif ( isNode ) {\n\t\t\t\telem[ internalKey ] = id = ++jQuery.uuid;\n\t\t\t} else {\n\t\t\t\tid = internalKey;\n\t\t\t}\n\t\t}\n\n\t\tif ( !cache[ id ] ) {\n\t\t\tcache[ id ] = {};\n\n\t\t\t// Avoids exposing jQuery metadata on plain JS objects when the object\n\t\t\t// is serialized using JSON.stringify\n\t\t\tif ( !isNode ) {\n\t\t\t\tcache[ id ].toJSON = jQuery.noop;\n\t\t\t}\n\t\t}\n\n\t\t// An object can be passed to jQuery.data instead of a key/value pair; this gets\n\t\t// shallow copied over onto the existing cache\n\t\tif ( typeof name === \"object\" || typeof name === \"function\" ) {\n\t\t\tif ( pvt ) {\n\t\t\t\tcache[ id ] = jQuery.extend( cache[ id ], name );\n\t\t\t} else {\n\t\t\t\tcache[ id ].data = jQuery.extend( cache[ id ].data, name );\n\t\t\t}\n\t\t}\n\n\t\tprivateCache = thisCache = cache[ id ];\n\n\t\t// jQuery data() is stored in a separate object inside the object's internal data\n\t\t// cache in order to avoid key collisions between internal data and user-defined\n\t\t// data.\n\t\tif ( !pvt ) {\n\t\t\tif ( !thisCache.data ) {\n\t\t\t\tthisCache.data = {};\n\t\t\t}\n\n\t\t\tthisCache = thisCache.data;\n\t\t}\n\n\t\tif ( data !== undefined ) {\n\t\t\tthisCache[ jQuery.camelCase( name ) ] = data;\n\t\t}\n\n\t\t// Users should not attempt to inspect the internal events object using jQuery.data,\n\t\t// it is undocumented and subject to change. But does anyone listen? No.\n\t\tif ( isEvents && !thisCache[ name ] ) {\n\t\t\treturn privateCache.events;\n\t\t}\n\n\t\t// Check for both converted-to-camel and non-converted data property names\n\t\t// If a data property was specified\n\t\tif ( getByName ) {\n\n\t\t\t// First Try to find as-is property data\n\t\t\tret = thisCache[ name ];\n\n\t\t\t// Test for null|undefined property data\n\t\t\tif ( ret == null ) {\n\n\t\t\t\t// Try to find the camelCased property\n\t\t\t\tret = thisCache[ jQuery.camelCase( name ) ];\n\t\t\t}\n\t\t} else {\n\t\t\tret = thisCache;\n\t\t}\n\n\t\treturn ret;\n\t},\n\n\tremoveData: function( elem, name, pvt /* Internal Use Only */ ) {\n\t\tif ( !jQuery.acceptData( elem ) ) {\n\t\t\treturn;\n\t\t}\n\n\t\tvar thisCache, i, l,\n\n\t\t\t// Reference to internal data cache key\n\t\t\tinternalKey = jQuery.expando,\n\n\t\t\tisNode = elem.nodeType,\n\n\t\t\t// See jQuery.data for more information\n\t\t\tcache = isNode ? jQuery.cache : elem,\n\n\t\t\t// See jQuery.data for more information\n\t\t\tid = isNode ? elem[ internalKey ] : internalKey;\n\n\t\t// If there is already no cache entry for this object, there is no\n\t\t// purpose in continuing\n\t\tif ( !cache[ id ] ) {\n\t\t\treturn;\n\t\t}\n\n\t\tif ( name ) {\n\n\t\t\tthisCache = pvt ? cache[ id ] : cache[ id ].data;\n\n\t\t\tif ( thisCache ) {\n\n\t\t\t\t// Support array or space separated string names for data keys\n\t\t\t\tif ( !jQuery.isArray( name ) ) {\n\n\t\t\t\t\t// try the string as a key before any manipulation\n\t\t\t\t\tif ( name in thisCache ) {\n\t\t\t\t\t\tname = [ name ];\n\t\t\t\t\t} else {\n\n\t\t\t\t\t\t// split the camel cased version by spaces unless a key with the spaces exists\n\t\t\t\t\t\tname = jQuery.camelCase( name );\n\t\t\t\t\t\tif ( name in thisCache ) {\n\t\t\t\t\t\t\tname = [ name ];\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tname = name.split( \" \" );\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tfor ( i = 0, l = name.length; i < l; i++ ) {\n\t\t\t\t\tdelete thisCache[ name[i] ];\n\t\t\t\t}\n\n\t\t\t\t// If there is no data left in the cache, we want to continue\n\t\t\t\t// and let the cache object itself get destroyed\n\t\t\t\tif ( !( pvt ? isEmptyDataObject : jQuery.isEmptyObject )( thisCache ) ) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// See jQuery.data for more information\n\t\tif ( !pvt ) {\n\t\t\tdelete cache[ id ].data;\n\n\t\t\t// Don't destroy the parent cache unless the internal data object\n\t\t\t// had been the only thing left in it\n\t\t\tif ( !isEmptyDataObject(cache[ id ]) ) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\n\t\t// Browsers that fail expando deletion also refuse to delete expandos on\n\t\t// the window, but it will allow it on all other JS objects; other browsers\n\t\t// don't care\n\t\t// Ensure that `cache` is not a window object #10080\n\t\tif ( jQuery.support.deleteExpando || !cache.setInterval ) {\n\t\t\tdelete cache[ id ];\n\t\t} else {\n\t\t\tcache[ id ] = null;\n\t\t}\n\n\t\t// We destroyed the cache and need to eliminate the expando on the node to avoid\n\t\t// false lookups in the cache for entries that no longer exist\n\t\tif ( isNode ) {\n\t\t\t// IE does not allow us to delete expando properties from nodes,\n\t\t\t// nor does it have a removeAttribute function on Document nodes;\n\t\t\t// we must handle all of these cases\n\t\t\tif ( jQuery.support.deleteExpando ) {\n\t\t\t\tdelete elem[ internalKey ];\n\t\t\t} else if ( elem.removeAttribute ) {\n\t\t\t\telem.removeAttribute( internalKey );\n\t\t\t} else {\n\t\t\t\telem[ internalKey ] = null;\n\t\t\t}\n\t\t}\n\t},\n\n\t// For internal use only.\n\t_data: function( elem, name, data ) {\n\t\treturn jQuery.data( elem, name, data, true );\n\t},\n\n\t// A method for determining if a DOM node can handle the data expando\n\tacceptData: function( elem ) {\n\t\tif ( elem.nodeName ) {\n\t\t\tvar match = jQuery.noData[ elem.nodeName.toLowerCase() ];\n\n\t\t\tif ( match ) {\n\t\t\t\treturn !(match === true || elem.getAttribute(\"classid\") !== match);\n\t\t\t}\n\t\t}\n\n\t\treturn true;\n\t}\n});\n\njQuery.fn.extend({\n\tdata: function( key, value ) {\n\t\tvar parts, attr, name,\n\t\t\tdata = null;\n\n\t\tif ( typeof key === \"undefined\" ) {\n\t\t\tif ( this.length ) {\n\t\t\t\tdata = jQuery.data( this[0] );\n\n\t\t\t\tif ( this[0].nodeType === 1 && !jQuery._data( this[0], \"parsedAttrs\" ) ) {\n\t\t\t\t\tattr = this[0].attributes;\n\t\t\t\t\tfor ( var i = 0, l = attr.length; i < l; i++ ) {\n\t\t\t\t\t\tname = attr[i].name;\n\n\t\t\t\t\t\tif ( name.indexOf( \"data-\" ) === 0 ) {\n\t\t\t\t\t\t\tname = jQuery.camelCase( name.substring(5) );\n\n\t\t\t\t\t\t\tdataAttr( this[0], name, data[ name ] );\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tjQuery._data( this[0], \"parsedAttrs\", true );\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn data;\n\n\t\t} else if ( typeof key === \"object\" ) {\n\t\t\treturn this.each(function() {\n\t\t\t\tjQuery.data( this, key );\n\t\t\t});\n\t\t}\n\n\t\tparts = key.split(\".\");\n\t\tparts[1] = parts[1] ? \".\" + parts[1] : \"\";\n\n\t\tif ( value === undefined ) {\n\t\t\tdata = this.triggerHandler(\"getData\" + parts[1] + \"!\", [parts[0]]);\n\n\t\t\t// Try to fetch any internally stored data first\n\t\t\tif ( data === undefined && this.length ) {\n\t\t\t\tdata = jQuery.data( this[0], key );\n\t\t\t\tdata = dataAttr( this[0], key, data );\n\t\t\t}\n\n\t\t\treturn data === undefined && parts[1] ?\n\t\t\t\tthis.data( parts[0] ) :\n\t\t\t\tdata;\n\n\t\t} else {\n\t\t\treturn this.each(function() {\n\t\t\t\tvar self = jQuery( this ),\n\t\t\t\t\targs = [ parts[0], value ];\n\n\t\t\t\tself.triggerHandler( \"setData\" + parts[1] + \"!\", args );\n\t\t\t\tjQuery.data( this, key, value );\n\t\t\t\tself.triggerHandler( \"changeData\" + parts[1] + \"!\", args );\n\t\t\t});\n\t\t}\n\t},\n\n\tremoveData: function( key ) {\n\t\treturn this.each(function() {\n\t\t\tjQuery.removeData( this, key );\n\t\t});\n\t}\n});\n\nfunction dataAttr( elem, key, data ) {\n\t// If nothing was found internally, try to fetch any\n\t// data from the HTML5 data-* attribute\n\tif ( data === undefined && elem.nodeType === 1 ) {\n\n\t\tvar name = \"data-\" + key.replace( rmultiDash, \"-$1\" ).toLowerCase();\n\n\t\tdata = elem.getAttribute( name );\n\n\t\tif ( typeof data === \"string\" ) {\n\t\t\ttry {\n\t\t\t\tdata = data === \"true\" ? true :\n\t\t\t\tdata === \"false\" ? false :\n\t\t\t\tdata === \"null\" ? null :\n\t\t\t\tjQuery.isNumeric( data ) ? parseFloat( data ) :\n\t\t\t\t\trbrace.test( data ) ? jQuery.parseJSON( data ) :\n\t\t\t\t\tdata;\n\t\t\t} catch( e ) {}\n\n\t\t\t// Make sure we set the data so it isn't changed later\n\t\t\tjQuery.data( elem, key, data );\n\n\t\t} else {\n\t\t\tdata = undefined;\n\t\t}\n\t}\n\n\treturn data;\n}\n\n// checks a cache object for emptiness\nfunction isEmptyDataObject( obj ) {\n\tfor ( var name in obj ) {\n\n\t\t// if the public data object is empty, the private is still empty\n\t\tif ( name === \"data\" && jQuery.isEmptyObject( obj[name] ) ) {\n\t\t\tcontinue;\n\t\t}\n\t\tif ( name !== \"toJSON\" ) {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\treturn true;\n}\n\n\n\n\nfunction handleQueueMarkDefer( elem, type, src ) {\n\tvar deferDataKey = type + \"defer\",\n\t\tqueueDataKey = type + \"queue\",\n\t\tmarkDataKey = type + \"mark\",\n\t\tdefer = jQuery._data( elem, deferDataKey );\n\tif ( defer &&\n\t\t( src === \"queue\" || !jQuery._data(elem, queueDataKey) ) &&\n\t\t( src === \"mark\" || !jQuery._data(elem, markDataKey) ) ) {\n\t\t// Give room for hard-coded callbacks to fire first\n\t\t// and eventually mark/queue something else on the element\n\t\tsetTimeout( function() {\n\t\t\tif ( !jQuery._data( elem, queueDataKey ) &&\n\t\t\t\t!jQuery._data( elem, markDataKey ) ) {\n\t\t\t\tjQuery.removeData( elem, deferDataKey, true );\n\t\t\t\tdefer.fire();\n\t\t\t}\n\t\t}, 0 );\n\t}\n}\n\njQuery.extend({\n\n\t_mark: function( elem, type ) {\n\t\tif ( elem ) {\n\t\t\ttype = ( type || \"fx\" ) + \"mark\";\n\t\t\tjQuery._data( elem, type, (jQuery._data( elem, type ) || 0) + 1 );\n\t\t}\n\t},\n\n\t_unmark: function( force, elem, type ) {\n\t\tif ( force !== true ) {\n\t\t\ttype = elem;\n\t\t\telem = force;\n\t\t\tforce = false;\n\t\t}\n\t\tif ( elem ) {\n\t\t\ttype = type || \"fx\";\n\t\t\tvar key = type + \"mark\",\n\t\t\t\tcount = force ? 0 : ( (jQuery._data( elem, key ) || 1) - 1 );\n\t\t\tif ( count ) {\n\t\t\t\tjQuery._data( elem, key, count );\n\t\t\t} else {\n\t\t\t\tjQuery.removeData( elem, key, true );\n\t\t\t\thandleQueueMarkDefer( elem, type, \"mark\" );\n\t\t\t}\n\t\t}\n\t},\n\n\tqueue: function( elem, type, data ) {\n\t\tvar q;\n\t\tif ( elem ) {\n\t\t\ttype = ( type || \"fx\" ) + \"queue\";\n\t\t\tq = jQuery._data( elem, type );\n\n\t\t\t// Speed up dequeue by getting out quickly if this is just a lookup\n\t\t\tif ( data ) {\n\t\t\t\tif ( !q || jQuery.isArray(data) ) {\n\t\t\t\t\tq = jQuery._data( elem, type, jQuery.makeArray(data) );\n\t\t\t\t} else {\n\t\t\t\t\tq.push( data );\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn q || [];\n\t\t}\n\t},\n\n\tdequeue: function( elem, type ) {\n\t\ttype = type || \"fx\";\n\n\t\tvar queue = jQuery.queue( elem, type ),\n\t\t\tfn = queue.shift(),\n\t\t\thooks = {};\n\n\t\t// If the fx queue is dequeued, always remove the progress sentinel\n\t\tif ( fn === \"inprogress\" ) {\n\t\t\tfn = queue.shift();\n\t\t}\n\n\t\tif ( fn ) {\n\t\t\t// Add a progress sentinel to prevent the fx queue from being\n\t\t\t// automatically dequeued\n\t\t\tif ( type === \"fx\" ) {\n\t\t\t\tqueue.unshift( \"inprogress\" );\n\t\t\t}\n\n\t\t\tjQuery._data( elem, type + \".run\", hooks );\n\t\t\tfn.call( elem, function() {\n\t\t\t\tjQuery.dequeue( elem, type );\n\t\t\t}, hooks );\n\t\t}\n\n\t\tif ( !queue.length ) {\n\t\t\tjQuery.removeData( elem, type + \"queue \" + type + \".run\", true );\n\t\t\thandleQueueMarkDefer( elem, type, \"queue\" );\n\t\t}\n\t}\n});\n\njQuery.fn.extend({\n\tqueue: function( type, data ) {\n\t\tif ( typeof type !== \"string\" ) {\n\t\t\tdata = type;\n\t\t\ttype = \"fx\";\n\t\t}\n\n\t\tif ( data === undefined ) {\n\t\t\treturn jQuery.queue( this[0], type );\n\t\t}\n\t\treturn this.each(function() {\n\t\t\tvar queue = jQuery.queue( this, type, data );\n\n\t\t\tif ( type === \"fx\" && queue[0] !== \"inprogress\" ) {\n\t\t\t\tjQuery.dequeue( this, type );\n\t\t\t}\n\t\t});\n\t},\n\tdequeue: function( type ) {\n\t\treturn this.each(function() {\n\t\t\tjQuery.dequeue( this, type );\n\t\t});\n\t},\n\t// Based off of the plugin by Clint Helfers, with permission.\n\t// http://blindsignals.com/index.php/2009/07/jquery-delay/\n\tdelay: function( time, type ) {\n\t\ttime = jQuery.fx ? jQuery.fx.speeds[ time ] || time : time;\n\t\ttype = type || \"fx\";\n\n\t\treturn this.queue( type, function( next, hooks ) {\n\t\t\tvar timeout = setTimeout( next, time );\n\t\t\thooks.stop = function() {\n\t\t\t\tclearTimeout( timeout );\n\t\t\t};\n\t\t});\n\t},\n\tclearQueue: function( type ) {\n\t\treturn this.queue( type || \"fx\", [] );\n\t},\n\t// Get a promise resolved when queues of a certain type\n\t// are emptied (fx is the type by default)\n\tpromise: function( type, object ) {\n\t\tif ( typeof type !== \"string\" ) {\n\t\t\tobject = type;\n\t\t\ttype = undefined;\n\t\t}\n\t\ttype = type || \"fx\";\n\t\tvar defer = jQuery.Deferred(),\n\t\t\telements = this,\n\t\t\ti = elements.length,\n\t\t\tcount = 1,\n\t\t\tdeferDataKey = type + \"defer\",\n\t\t\tqueueDataKey = type + \"queue\",\n\t\t\tmarkDataKey = type + \"mark\",\n\t\t\ttmp;\n\t\tfunction resolve() {\n\t\t\tif ( !( --count ) ) {\n\t\t\t\tdefer.resolveWith( elements, [ elements ] );\n\t\t\t}\n\t\t}\n\t\twhile( i-- ) {\n\t\t\tif (( tmp = jQuery.data( elements[ i ], deferDataKey, undefined, true ) ||\n\t\t\t\t\t( jQuery.data( elements[ i ], queueDataKey, undefined, true ) ||\n\t\t\t\t\t\tjQuery.data( elements[ i ], markDataKey, undefined, true ) ) &&\n\t\t\t\t\tjQuery.data( elements[ i ], deferDataKey, jQuery.Callbacks( \"once memory\" ), true ) )) {\n\t\t\t\tcount++;\n\t\t\t\ttmp.add( resolve );\n\t\t\t}\n\t\t}\n\t\tresolve();\n\t\treturn defer.promise();\n\t}\n});\n\n\n\n\nvar rclass = /[\\n\\t\\r]/g,\n\trspace = /\\s+/,\n\trreturn = /\\r/g,\n\trtype = /^(?:button|input)$/i,\n\trfocusable = /^(?:button|input|object|select|textarea)$/i,\n\trclickable = /^a(?:rea)?$/i,\n\trboolean = /^(?:autofocus|autoplay|async|checked|controls|defer|disabled|hidden|loop|multiple|open|readonly|required|scoped|selected)$/i,\n\tgetSetAttribute = jQuery.support.getSetAttribute,\n\tnodeHook, boolHook, fixSpecified;\n\njQuery.fn.extend({\n\tattr: function( name, value ) {\n\t\treturn jQuery.access( this, name, value, true, jQuery.attr );\n\t},\n\n\tremoveAttr: function( name ) {\n\t\treturn this.each(function() {\n\t\t\tjQuery.removeAttr( this, name );\n\t\t});\n\t},\n\n\tprop: function( name, value ) {\n\t\treturn jQuery.access( this, name, value, true, jQuery.prop );\n\t},\n\n\tremoveProp: function( name ) {\n\t\tname = jQuery.propFix[ name ] || name;\n\t\treturn this.each(function() {\n\t\t\t// try/catch handles cases where IE balks (such as removing a property on window)\n\t\t\ttry {\n\t\t\t\tthis[ name ] = undefined;\n\t\t\t\tdelete this[ name ];\n\t\t\t} catch( e ) {}\n\t\t});\n\t},\n\n\taddClass: function( value ) {\n\t\tvar classNames, i, l, elem,\n\t\t\tsetClass, c, cl;\n\n\t\tif ( jQuery.isFunction( value ) ) {\n\t\t\treturn this.each(function( j ) {\n\t\t\t\tjQuery( this ).addClass( value.call(this, j, this.className) );\n\t\t\t});\n\t\t}\n\n\t\tif ( value && typeof value === \"string\" ) {\n\t\t\tclassNames = value.split( rspace );\n\n\t\t\tfor ( i = 0, l = this.length; i < l; i++ ) {\n\t\t\t\telem = this[ i ];\n\n\t\t\t\tif ( elem.nodeType === 1 ) {\n\t\t\t\t\tif ( !elem.className && classNames.length === 1 ) {\n\t\t\t\t\t\telem.className = value;\n\n\t\t\t\t\t} else {\n\t\t\t\t\t\tsetClass = \" \" + elem.className + \" \";\n\n\t\t\t\t\t\tfor ( c = 0, cl = classNames.length; c < cl; c++ ) {\n\t\t\t\t\t\t\tif ( !~setClass.indexOf( \" \" + classNames[ c ] + \" \" ) ) {\n\t\t\t\t\t\t\t\tsetClass += classNames[ c ] + \" \";\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\telem.className = jQuery.trim( setClass );\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn this;\n\t},\n\n\tremoveClass: function( value ) {\n\t\tvar classNames, i, l, elem, className, c, cl;\n\n\t\tif ( jQuery.isFunction( value ) ) {\n\t\t\treturn this.each(function( j ) {\n\t\t\t\tjQuery( this ).removeClass( value.call(this, j, this.className) );\n\t\t\t});\n\t\t}\n\n\t\tif ( (value && typeof value === \"string\") || value === undefined ) {\n\t\t\tclassNames = ( value || \"\" ).split( rspace );\n\n\t\t\tfor ( i = 0, l = this.length; i < l; i++ ) {\n\t\t\t\telem = this[ i ];\n\n\t\t\t\tif ( elem.nodeType === 1 && elem.className ) {\n\t\t\t\t\tif ( value ) {\n\t\t\t\t\t\tclassName = (\" \" + elem.className + \" \").replace( rclass, \" \" );\n\t\t\t\t\t\tfor ( c = 0, cl = classNames.length; c < cl; c++ ) {\n\t\t\t\t\t\t\tclassName = className.replace(\" \" + classNames[ c ] + \" \", \" \");\n\t\t\t\t\t\t}\n\t\t\t\t\t\telem.className = jQuery.trim( className );\n\n\t\t\t\t\t} else {\n\t\t\t\t\t\telem.className = \"\";\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn this;\n\t},\n\n\ttoggleClass: function( value, stateVal ) {\n\t\tvar type = typeof value,\n\t\t\tisBool = typeof stateVal === \"boolean\";\n\n\t\tif ( jQuery.isFunction( value ) ) {\n\t\t\treturn this.each(function( i ) {\n\t\t\t\tjQuery( this ).toggleClass( value.call(this, i, this.className, stateVal), stateVal );\n\t\t\t});\n\t\t}\n\n\t\treturn this.each(function() {\n\t\t\tif ( type === \"string\" ) {\n\t\t\t\t// toggle individual class names\n\t\t\t\tvar className,\n\t\t\t\t\ti = 0,\n\t\t\t\t\tself = jQuery( this ),\n\t\t\t\t\tstate = stateVal,\n\t\t\t\t\tclassNames = value.split( rspace );\n\n\t\t\t\twhile ( (className = classNames[ i++ ]) ) {\n\t\t\t\t\t// check each className given, space seperated list\n\t\t\t\t\tstate = isBool ? state : !self.hasClass( className );\n\t\t\t\t\tself[ state ? \"addClass\" : \"removeClass\" ]( className );\n\t\t\t\t}\n\n\t\t\t} else if ( type === \"undefined\" || type === \"boolean\" ) {\n\t\t\t\tif ( this.className ) {\n\t\t\t\t\t// store className if set\n\t\t\t\t\tjQuery._data( this, \"__className__\", this.className );\n\t\t\t\t}\n\n\t\t\t\t// toggle whole className\n\t\t\t\tthis.className = this.className || value === false ? \"\" : jQuery._data( this, \"__className__\" ) || \"\";\n\t\t\t}\n\t\t});\n\t},\n\n\thasClass: function( selector ) {\n\t\tvar className = \" \" + selector + \" \",\n\t\t\ti = 0,\n\t\t\tl = this.length;\n\t\tfor ( ; i < l; i++ ) {\n\t\t\tif ( this[i].nodeType === 1 && (\" \" + this[i].className + \" \").replace(rclass, \" \").indexOf( className ) > -1 ) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\n\t\treturn false;\n\t},\n\n\tval: function( value ) {\n\t\tvar hooks, ret, isFunction,\n\t\t\telem = this[0];\n\n\t\tif ( !arguments.length ) {\n\t\t\tif ( elem ) {\n\t\t\t\thooks = jQuery.valHooks[ elem.nodeName.toLowerCase() ] || jQuery.valHooks[ elem.type ];\n\n\t\t\t\tif ( hooks && \"get\" in hooks && (ret = hooks.get( elem, \"value\" )) !== undefined ) {\n\t\t\t\t\treturn ret;\n\t\t\t\t}\n\n\t\t\t\tret = elem.value;\n\n\t\t\t\treturn typeof ret === \"string\" ?\n\t\t\t\t\t// handle most common string cases\n\t\t\t\t\tret.replace(rreturn, \"\") :\n\t\t\t\t\t// handle cases where value is null/undef or number\n\t\t\t\t\tret == null ? \"\" : ret;\n\t\t\t}\n\n\t\t\treturn;\n\t\t}\n\n\t\tisFunction = jQuery.isFunction( value );\n\n\t\treturn this.each(function( i ) {\n\t\t\tvar self = jQuery(this), val;\n\n\t\t\tif ( this.nodeType !== 1 ) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif ( isFunction ) {\n\t\t\t\tval = value.call( this, i, self.val() );\n\t\t\t} else {\n\t\t\t\tval = value;\n\t\t\t}\n\n\t\t\t// Treat null/undefined as \"\"; convert numbers to string\n\t\t\tif ( val == null ) {\n\t\t\t\tval = \"\";\n\t\t\t} else if ( typeof val === \"number\" ) {\n\t\t\t\tval += \"\";\n\t\t\t} else if ( jQuery.isArray( val ) ) {\n\t\t\t\tval = jQuery.map(val, function ( value ) {\n\t\t\t\t\treturn value == null ? \"\" : value + \"\";\n\t\t\t\t});\n\t\t\t}\n\n\t\t\thooks = jQuery.valHooks[ this.nodeName.toLowerCase() ] || jQuery.valHooks[ this.type ];\n\n\t\t\t// If set returns undefined, fall back to normal setting\n\t\t\tif ( !hooks || !(\"set\" in hooks) || hooks.set( this, val, \"value\" ) === undefined ) {\n\t\t\t\tthis.value = val;\n\t\t\t}\n\t\t});\n\t}\n});\n\njQuery.extend({\n\tvalHooks: {\n\t\toption: {\n\t\t\tget: function( elem ) {\n\t\t\t\t// attributes.value is undefined in Blackberry 4.7 but\n\t\t\t\t// uses .value. See #6932\n\t\t\t\tvar val = elem.attributes.value;\n\t\t\t\treturn !val || val.specified ? elem.value : elem.text;\n\t\t\t}\n\t\t},\n\t\tselect: {\n\t\t\tget: function( elem ) {\n\t\t\t\tvar value, i, max, option,\n\t\t\t\t\tindex = elem.selectedIndex,\n\t\t\t\t\tvalues = [],\n\t\t\t\t\toptions = elem.options,\n\t\t\t\t\tone = elem.type === \"select-one\";\n\n\t\t\t\t// Nothing was selected\n\t\t\t\tif ( index < 0 ) {\n\t\t\t\t\treturn null;\n\t\t\t\t}\n\n\t\t\t\t// Loop through all the selected options\n\t\t\t\ti = one ? index : 0;\n\t\t\t\tmax = one ? index + 1 : options.length;\n\t\t\t\tfor ( ; i < max; i++ ) {\n\t\t\t\t\toption = options[ i ];\n\n\t\t\t\t\t// Don't return options that are disabled or in a disabled optgroup\n\t\t\t\t\tif ( option.selected && (jQuery.support.optDisabled ? !option.disabled : option.getAttribute(\"disabled\") === null) &&\n\t\t\t\t\t\t\t(!option.parentNode.disabled || !jQuery.nodeName( option.parentNode, \"optgroup\" )) ) {\n\n\t\t\t\t\t\t// Get the specific value for the option\n\t\t\t\t\t\tvalue = jQuery( option ).val();\n\n\t\t\t\t\t\t// We don't need an array for one selects\n\t\t\t\t\t\tif ( one ) {\n\t\t\t\t\t\t\treturn value;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Multi-Selects return an array\n\t\t\t\t\t\tvalues.push( value );\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// Fixes Bug #2551 -- select.val() broken in IE after form.reset()\n\t\t\t\tif ( one && !values.length && options.length ) {\n\t\t\t\t\treturn jQuery( options[ index ] ).val();\n\t\t\t\t}\n\n\t\t\t\treturn values;\n\t\t\t},\n\n\t\t\tset: function( elem, value ) {\n\t\t\t\tvar values = jQuery.makeArray( value );\n\n\t\t\t\tjQuery(elem).find(\"option\").each(function() {\n\t\t\t\t\tthis.selected = jQuery.inArray( jQuery(this).val(), values ) >= 0;\n\t\t\t\t});\n\n\t\t\t\tif ( !values.length ) {\n\t\t\t\t\telem.selectedIndex = -1;\n\t\t\t\t}\n\t\t\t\treturn values;\n\t\t\t}\n\t\t}\n\t},\n\n\tattrFn: {\n\t\tval: true,\n\t\tcss: true,\n\t\thtml: true,\n\t\ttext: true,\n\t\tdata: true,\n\t\twidth: true,\n\t\theight: true,\n\t\toffset: true\n\t},\n\n\tattr: function( elem, name, value, pass ) {\n\t\tvar ret, hooks, notxml,\n\t\t\tnType = elem.nodeType;\n\n\t\t// don't get/set attributes on text, comment and attribute nodes\n\t\tif ( !elem || nType === 3 || nType === 8 || nType === 2 ) {\n\t\t\treturn;\n\t\t}\n\n\t\tif ( pass && name in jQuery.attrFn ) {\n\t\t\treturn jQuery( elem )[ name ]( value );\n\t\t}\n\n\t\t// Fallback to prop when attributes are not supported\n\t\tif ( typeof elem.getAttribute === \"undefined\" ) {\n\t\t\treturn jQuery.prop( elem, name, value );\n\t\t}\n\n\t\tnotxml = nType !== 1 || !jQuery.isXMLDoc( elem );\n\n\t\t// All attributes are lowercase\n\t\t// Grab necessary hook if one is defined\n\t\tif ( notxml ) {\n\t\t\tname = name.toLowerCase();\n\t\t\thooks = jQuery.attrHooks[ name ] || ( rboolean.test( name ) ? boolHook : nodeHook );\n\t\t}\n\n\t\tif ( value !== undefined ) {\n\n\t\t\tif ( value === null ) {\n\t\t\t\tjQuery.removeAttr( elem, name );\n\t\t\t\treturn;\n\n\t\t\t} else if ( hooks && \"set\" in hooks && notxml && (ret = hooks.set( elem, value, name )) !== undefined ) {\n\t\t\t\treturn ret;\n\n\t\t\t} else {\n\t\t\t\telem.setAttribute( name, \"\" + value );\n\t\t\t\treturn value;\n\t\t\t}\n\n\t\t} else if ( hooks && \"get\" in hooks && notxml && (ret = hooks.get( elem, name )) !== null ) {\n\t\t\treturn ret;\n\n\t\t} else {\n\n\t\t\tret = elem.getAttribute( name );\n\n\t\t\t// Non-existent attributes return null, we normalize to undefined\n\t\t\treturn ret === null ?\n\t\t\t\tundefined :\n\t\t\t\tret;\n\t\t}\n\t},\n\n\tremoveAttr: function( elem, value ) {\n\t\tvar propName, attrNames, name, l,\n\t\t\ti = 0;\n\n\t\tif ( value && elem.nodeType === 1 ) {\n\t\t\tattrNames = value.toLowerCase().split( rspace );\n\t\t\tl = attrNames.length;\n\n\t\t\tfor ( ; i < l; i++ ) {\n\t\t\t\tname = attrNames[ i ];\n\n\t\t\t\tif ( name ) {\n\t\t\t\t\tpropName = jQuery.propFix[ name ] || name;\n\n\t\t\t\t\t// See #9699 for explanation of this approach (setting first, then removal)\n\t\t\t\t\tjQuery.attr( elem, name, \"\" );\n\t\t\t\t\telem.removeAttribute( getSetAttribute ? name : propName );\n\n\t\t\t\t\t// Set corresponding property to false for boolean attributes\n\t\t\t\t\tif ( rboolean.test( name ) && propName in elem ) {\n\t\t\t\t\t\telem[ propName ] = false;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t},\n\n\tattrHooks: {\n\t\ttype: {\n\t\t\tset: function( elem, value ) {\n\t\t\t\t// We can't allow the type property to be changed (since it causes problems in IE)\n\t\t\t\tif ( rtype.test( elem.nodeName ) && elem.parentNode ) {\n\t\t\t\t\tjQuery.error( \"type property can't be changed\" );\n\t\t\t\t} else if ( !jQuery.support.radioValue && value === \"radio\" && jQuery.nodeName(elem, \"input\") ) {\n\t\t\t\t\t// Setting the type on a radio button after the value resets the value in IE6-9\n\t\t\t\t\t// Reset value to it's default in case type is set after value\n\t\t\t\t\t// This is for element creation\n\t\t\t\t\tvar val = elem.value;\n\t\t\t\t\telem.setAttribute( \"type\", value );\n\t\t\t\t\tif ( val ) {\n\t\t\t\t\t\telem.value = val;\n\t\t\t\t\t}\n\t\t\t\t\treturn value;\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t// Use the value property for back compat\n\t\t// Use the nodeHook for button elements in IE6/7 (#1954)\n\t\tvalue: {\n\t\t\tget: function( elem, name ) {\n\t\t\t\tif ( nodeHook && jQuery.nodeName( elem, \"button\" ) ) {\n\t\t\t\t\treturn nodeHook.get( elem, name );\n\t\t\t\t}\n\t\t\t\treturn name in elem ?\n\t\t\t\t\telem.value :\n\t\t\t\t\tnull;\n\t\t\t},\n\t\t\tset: function( elem, value, name ) {\n\t\t\t\tif ( nodeHook && jQuery.nodeName( elem, \"button\" ) ) {\n\t\t\t\t\treturn nodeHook.set( elem, value, name );\n\t\t\t\t}\n\t\t\t\t// Does not return so that setAttribute is also used\n\t\t\t\telem.value = value;\n\t\t\t}\n\t\t}\n\t},\n\n\tpropFix: {\n\t\ttabindex: \"tabIndex\",\n\t\treadonly: \"readOnly\",\n\t\t\"for\": \"htmlFor\",\n\t\t\"class\": \"className\",\n\t\tmaxlength: \"maxLength\",\n\t\tcellspacing: \"cellSpacing\",\n\t\tcellpadding: \"cellPadding\",\n\t\trowspan: \"rowSpan\",\n\t\tcolspan: \"colSpan\",\n\t\tusemap: \"useMap\",\n\t\tframeborder: \"frameBorder\",\n\t\tcontenteditable: \"contentEditable\"\n\t},\n\n\tprop: function( elem, name, value ) {\n\t\tvar ret, hooks, notxml,\n\t\t\tnType = elem.nodeType;\n\n\t\t// don't get/set properties on text, comment and attribute nodes\n\t\tif ( !elem || nType === 3 || nType === 8 || nType === 2 ) {\n\t\t\treturn;\n\t\t}\n\n\t\tnotxml = nType !== 1 || !jQuery.isXMLDoc( elem );\n\n\t\tif ( notxml ) {\n\t\t\t// Fix name and attach hooks\n\t\t\tname = jQuery.propFix[ name ] || name;\n\t\t\thooks = jQuery.propHooks[ name ];\n\t\t}\n\n\t\tif ( value !== undefined ) {\n\t\t\tif ( hooks && \"set\" in hooks && (ret = hooks.set( elem, value, name )) !== undefined ) {\n\t\t\t\treturn ret;\n\n\t\t\t} else {\n\t\t\t\treturn ( elem[ name ] = value );\n\t\t\t}\n\n\t\t} else {\n\t\t\tif ( hooks && \"get\" in hooks && (ret = hooks.get( elem, name )) !== null ) {\n\t\t\t\treturn ret;\n\n\t\t\t} else {\n\t\t\t\treturn elem[ name ];\n\t\t\t}\n\t\t}\n\t},\n\n\tpropHooks: {\n\t\ttabIndex: {\n\t\t\tget: function( elem ) {\n\t\t\t\t// elem.tabIndex doesn't always return the correct value when it hasn't been explicitly set\n\t\t\t\t// http://fluidproject.org/blog/2008/01/09/getting-setting-and-removing-tabindex-values-with-javascript/\n\t\t\t\tvar attributeNode = elem.getAttributeNode(\"tabindex\");\n\n\t\t\t\treturn attributeNode && attributeNode.specified ?\n\t\t\t\t\tparseInt( attributeNode.value, 10 ) :\n\t\t\t\t\trfocusable.test( elem.nodeName ) || rclickable.test( elem.nodeName ) && elem.href ?\n\t\t\t\t\t\t0 :\n\t\t\t\t\t\tundefined;\n\t\t\t}\n\t\t}\n\t}\n});\n\n// Add the tabIndex propHook to attrHooks for back-compat (different case is intentional)\njQuery.attrHooks.tabindex = jQuery.propHooks.tabIndex;\n\n// Hook for boolean attributes\nboolHook = {\n\tget: function( elem, name ) {\n\t\t// Align boolean attributes with corresponding properties\n\t\t// Fall back to attribute presence where some booleans are not supported\n\t\tvar attrNode,\n\t\t\tproperty = jQuery.prop( elem, name );\n\t\treturn property === true || typeof property !== \"boolean\" && ( attrNode = elem.getAttributeNode(name) ) && attrNode.nodeValue !== false ?\n\t\t\tname.toLowerCase() :\n\t\t\tundefined;\n\t},\n\tset: function( elem, value, name ) {\n\t\tvar propName;\n\t\tif ( value === false ) {\n\t\t\t// Remove boolean attributes when set to false\n\t\t\tjQuery.removeAttr( elem, name );\n\t\t} else {\n\t\t\t// value is true since we know at this point it's type boolean and not false\n\t\t\t// Set boolean attributes to the same name and set the DOM property\n\t\t\tpropName = jQuery.propFix[ name ] || name;\n\t\t\tif ( propName in elem ) {\n\t\t\t\t// Only set the IDL specifically if it already exists on the element\n\t\t\t\telem[ propName ] = true;\n\t\t\t}\n\n\t\t\telem.setAttribute( name, name.toLowerCase() );\n\t\t}\n\t\treturn name;\n\t}\n};\n\n// IE6/7 do not support getting/setting some attributes with get/setAttribute\nif ( !getSetAttribute ) {\n\n\tfixSpecified = {\n\t\tname: true,\n\t\tid: true\n\t};\n\n\t// Use this for any attribute in IE6/7\n\t// This fixes almost every IE6/7 issue\n\tnodeHook = jQuery.valHooks.button = {\n\t\tget: function( elem, name ) {\n\t\t\tvar ret;\n\t\t\tret = elem.getAttributeNode( name );\n\t\t\treturn ret && ( fixSpecified[ name ] ? ret.nodeValue !== \"\" : ret.specified ) ?\n\t\t\t\tret.nodeValue :\n\t\t\t\tundefined;\n\t\t},\n\t\tset: function( elem, value, name ) {\n\t\t\t// Set the existing or create a new attribute node\n\t\t\tvar ret = elem.getAttributeNode( name );\n\t\t\tif ( !ret ) {\n\t\t\t\tret = document.createAttribute( name );\n\t\t\t\telem.setAttributeNode( ret );\n\t\t\t}\n\t\t\treturn ( ret.nodeValue = value + \"\" );\n\t\t}\n\t};\n\n\t// Apply the nodeHook to tabindex\n\tjQuery.attrHooks.tabindex.set = nodeHook.set;\n\n\t// Set width and height to auto instead of 0 on empty string( Bug #8150 )\n\t// This is for removals\n\tjQuery.each([ \"width\", \"height\" ], function( i, name ) {\n\t\tjQuery.attrHooks[ name ] = jQuery.extend( jQuery.attrHooks[ name ], {\n\t\t\tset: function( elem, value ) {\n\t\t\t\tif ( value === \"\" ) {\n\t\t\t\t\telem.setAttribute( name, \"auto\" );\n\t\t\t\t\treturn value;\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\t});\n\n\t// Set contenteditable to false on removals(#10429)\n\t// Setting to empty string throws an error as an invalid value\n\tjQuery.attrHooks.contenteditable = {\n\t\tget: nodeHook.get,\n\t\tset: function( elem, value, name ) {\n\t\t\tif ( value === \"\" ) {\n\t\t\t\tvalue = \"false\";\n\t\t\t}\n\t\t\tnodeHook.set( elem, value, name );\n\t\t}\n\t};\n}\n\n\n// Some attributes require a special call on IE\nif ( !jQuery.support.hrefNormalized ) {\n\tjQuery.each([ \"href\", \"src\", \"width\", \"height\" ], function( i, name ) {\n\t\tjQuery.attrHooks[ name ] = jQuery.extend( jQuery.attrHooks[ name ], {\n\t\t\tget: function( elem ) {\n\t\t\t\tvar ret = elem.getAttribute( name, 2 );\n\t\t\t\treturn ret === null ? undefined : ret;\n\t\t\t}\n\t\t});\n\t});\n}\n\nif ( !jQuery.support.style ) {\n\tjQuery.attrHooks.style = {\n\t\tget: function( elem ) {\n\t\t\t// Return undefined in the case of empty string\n\t\t\t// Normalize to lowercase since IE uppercases css property names\n\t\t\treturn elem.style.cssText.toLowerCase() || undefined;\n\t\t},\n\t\tset: function( elem, value ) {\n\t\t\treturn ( elem.style.cssText = \"\" + value );\n\t\t}\n\t};\n}\n\n// Safari mis-reports the default selected property of an option\n// Accessing the parent's selectedIndex property fixes it\nif ( !jQuery.support.optSelected ) {\n\tjQuery.propHooks.selected = jQuery.extend( jQuery.propHooks.selected, {\n\t\tget: function( elem ) {\n\t\t\tvar parent = elem.parentNode;\n\n\t\t\tif ( parent ) {\n\t\t\t\tparent.selectedIndex;\n\n\t\t\t\t// Make sure that it also works with optgroups, see #5701\n\t\t\t\tif ( parent.parentNode ) {\n\t\t\t\t\tparent.parentNode.selectedIndex;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn null;\n\t\t}\n\t});\n}\n\n// IE6/7 call enctype encoding\nif ( !jQuery.support.enctype ) {\n\tjQuery.propFix.enctype = \"encoding\";\n}\n\n// Radios and checkboxes getter/setter\nif ( !jQuery.support.checkOn ) {\n\tjQuery.each([ \"radio\", \"checkbox\" ], function() {\n\t\tjQuery.valHooks[ this ] = {\n\t\t\tget: function( elem ) {\n\t\t\t\t// Handle the case where in Webkit \"\" is returned instead of \"on\" if a value isn't specified\n\t\t\t\treturn elem.getAttribute(\"value\") === null ? \"on\" : elem.value;\n\t\t\t}\n\t\t};\n\t});\n}\njQuery.each([ \"radio\", \"checkbox\" ], function() {\n\tjQuery.valHooks[ this ] = jQuery.extend( jQuery.valHooks[ this ], {\n\t\tset: function( elem, value ) {\n\t\t\tif ( jQuery.isArray( value ) ) {\n\t\t\t\treturn ( elem.checked = jQuery.inArray( jQuery(elem).val(), value ) >= 0 );\n\t\t\t}\n\t\t}\n\t});\n});\n\n\n\n\nvar rformElems = /^(?:textarea|input|select)$/i,\n\trtypenamespace = /^([^\\.]*)?(?:\\.(.+))?$/,\n\trhoverHack = /\\bhover(\\.\\S+)?\\b/,\n\trkeyEvent = /^key/,\n\trmouseEvent = /^(?:mouse|contextmenu)|click/,\n\trfocusMorph = /^(?:focusinfocus|focusoutblur)$/,\n\trquickIs = /^(\\w*)(?:#([\\w\\-]+))?(?:\\.([\\w\\-]+))?$/,\n\tquickParse = function( selector ) {\n\t\tvar quick = rquickIs.exec( selector );\n\t\tif ( quick ) {\n\t\t\t//   0  1    2   3\n\t\t\t// [ _, tag, id, class ]\n\t\t\tquick[1] = ( quick[1] || \"\" ).toLowerCase();\n\t\t\tquick[3] = quick[3] && new RegExp( \"(?:^|\\\\s)\" + quick[3] + \"(?:\\\\s|$)\" );\n\t\t}\n\t\treturn quick;\n\t},\n\tquickIs = function( elem, m ) {\n\t\tvar attrs = elem.attributes || {};\n\t\treturn (\n\t\t\t(!m[1] || elem.nodeName.toLowerCase() === m[1]) &&\n\t\t\t(!m[2] || (attrs.id || {}).value === m[2]) &&\n\t\t\t(!m[3] || m[3].test( (attrs[ \"class\" ] || {}).value ))\n\t\t);\n\t},\n\thoverHack = function( events ) {\n\t\treturn jQuery.event.special.hover ? events : events.replace( rhoverHack, \"mouseenter$1 mouseleave$1\" );\n\t};\n\n/*\n * Helper functions for managing events -- not part of the public interface.\n * Props to Dean Edwards' addEvent library for many of the ideas.\n */\njQuery.event = {\n\n\tadd: function( elem, types, handler, data, selector ) {\n\n\t\tvar elemData, eventHandle, events,\n\t\t\tt, tns, type, namespaces, handleObj,\n\t\t\thandleObjIn, quick, handlers, special;\n\n\t\t// Don't attach events to noData or text/comment nodes (allow plain objects tho)\n\t\tif ( elem.nodeType === 3 || elem.nodeType === 8 || !types || !handler || !(elemData = jQuery._data( elem )) ) {\n\t\t\treturn;\n\t\t}\n\n\t\t// Caller can pass in an object of custom data in lieu of the handler\n\t\tif ( handler.handler ) {\n\t\t\thandleObjIn = handler;\n\t\t\thandler = handleObjIn.handler;\n\t\t}\n\n\t\t// Make sure that the handler has a unique ID, used to find/remove it later\n\t\tif ( !handler.guid ) {\n\t\t\thandler.guid = jQuery.guid++;\n\t\t}\n\n\t\t// Init the element's event structure and main handler, if this is the first\n\t\tevents = elemData.events;\n\t\tif ( !events ) {\n\t\t\telemData.events = events = {};\n\t\t}\n\t\teventHandle = elemData.handle;\n\t\tif ( !eventHandle ) {\n\t\t\telemData.handle = eventHandle = function( e ) {\n\t\t\t\t// Discard the second event of a jQuery.event.trigger() and\n\t\t\t\t// when an event is called after a page has unloaded\n\t\t\t\treturn typeof jQuery !== \"undefined\" && (!e || jQuery.event.triggered !== e.type) ?\n\t\t\t\t\tjQuery.event.dispatch.apply( eventHandle.elem, arguments ) :\n\t\t\t\t\tundefined;\n\t\t\t};\n\t\t\t// Add elem as a property of the handle fn to prevent a memory leak with IE non-native events\n\t\t\teventHandle.elem = elem;\n\t\t}\n\n\t\t// Handle multiple events separated by a space\n\t\t// jQuery(...).bind(\"mouseover mouseout\", fn);\n\t\ttypes = jQuery.trim( hoverHack(types) ).split( \" \" );\n\t\tfor ( t = 0; t < types.length; t++ ) {\n\n\t\t\ttns = rtypenamespace.exec( types[t] ) || [];\n\t\t\ttype = tns[1];\n\t\t\tnamespaces = ( tns[2] || \"\" ).split( \".\" ).sort();\n\n\t\t\t// If event changes its type, use the special event handlers for the changed type\n\t\t\tspecial = jQuery.event.special[ type ] || {};\n\n\t\t\t// If selector defined, determine special event api type, otherwise given type\n\t\t\ttype = ( selector ? special.delegateType : special.bindType ) || type;\n\n\t\t\t// Update special based on newly reset type\n\t\t\tspecial = jQuery.event.special[ type ] || {};\n\n\t\t\t// handleObj is passed to all event handlers\n\t\t\thandleObj = jQuery.extend({\n\t\t\t\ttype: type,\n\t\t\t\torigType: tns[1],\n\t\t\t\tdata: data,\n\t\t\t\thandler: handler,\n\t\t\t\tguid: handler.guid,\n\t\t\t\tselector: selector,\n\t\t\t\tquick: quickParse( selector ),\n\t\t\t\tnamespace: namespaces.join(\".\")\n\t\t\t}, handleObjIn );\n\n\t\t\t// Init the event handler queue if we're the first\n\t\t\thandlers = events[ type ];\n\t\t\tif ( !handlers ) {\n\t\t\t\thandlers = events[ type ] = [];\n\t\t\t\thandlers.delegateCount = 0;\n\n\t\t\t\t// Only use addEventListener/attachEvent if the special events handler returns false\n\t\t\t\tif ( !special.setup || special.setup.call( elem, data, namespaces, eventHandle ) === false ) {\n\t\t\t\t\t// Bind the global event handler to the element\n\t\t\t\t\tif ( elem.addEventListener ) {\n\t\t\t\t\t\telem.addEventListener( type, eventHandle, false );\n\n\t\t\t\t\t} else if ( elem.attachEvent ) {\n\t\t\t\t\t\telem.attachEvent( \"on\" + type, eventHandle );\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif ( special.add ) {\n\t\t\t\tspecial.add.call( elem, handleObj );\n\n\t\t\t\tif ( !handleObj.handler.guid ) {\n\t\t\t\t\thandleObj.handler.guid = handler.guid;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Add to the element's handler list, delegates in front\n\t\t\tif ( selector ) {\n\t\t\t\thandlers.splice( handlers.delegateCount++, 0, handleObj );\n\t\t\t} else {\n\t\t\t\thandlers.push( handleObj );\n\t\t\t}\n\n\t\t\t// Keep track of which events have ever been used, for event optimization\n\t\t\tjQuery.event.global[ type ] = true;\n\t\t}\n\n\t\t// Nullify elem to prevent memory leaks in IE\n\t\telem = null;\n\t},\n\n\tglobal: {},\n\n\t// Detach an event or set of events from an element\n\tremove: function( elem, types, handler, selector, mappedTypes ) {\n\n\t\tvar elemData = jQuery.hasData( elem ) && jQuery._data( elem ),\n\t\t\tt, tns, type, origType, namespaces, origCount,\n\t\t\tj, events, special, handle, eventType, handleObj;\n\n\t\tif ( !elemData || !(events = elemData.events) ) {\n\t\t\treturn;\n\t\t}\n\n\t\t// Once for each type.namespace in types; type may be omitted\n\t\ttypes = jQuery.trim( hoverHack( types || \"\" ) ).split(\" \");\n\t\tfor ( t = 0; t < types.length; t++ ) {\n\t\t\ttns = rtypenamespace.exec( types[t] ) || [];\n\t\t\ttype = origType = tns[1];\n\t\t\tnamespaces = tns[2];\n\n\t\t\t// Unbind all events (on this namespace, if provided) for the element\n\t\t\tif ( !type ) {\n\t\t\t\tfor ( type in events ) {\n\t\t\t\t\tjQuery.event.remove( elem, type + types[ t ], handler, selector, true );\n\t\t\t\t}\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tspecial = jQuery.event.special[ type ] || {};\n\t\t\ttype = ( selector? special.delegateType : special.bindType ) || type;\n\t\t\teventType = events[ type ] || [];\n\t\t\torigCount = eventType.length;\n\t\t\tnamespaces = namespaces ? new RegExp(\"(^|\\\\.)\" + namespaces.split(\".\").sort().join(\"\\\\.(?:.*\\\\.)?\") + \"(\\\\.|$)\") : null;\n\n\t\t\t// Remove matching events\n\t\t\tfor ( j = 0; j < eventType.length; j++ ) {\n\t\t\t\thandleObj = eventType[ j ];\n\n\t\t\t\tif ( ( mappedTypes || origType === handleObj.origType ) &&\n\t\t\t\t\t ( !handler || handler.guid === handleObj.guid ) &&\n\t\t\t\t\t ( !namespaces || namespaces.test( handleObj.namespace ) ) &&\n\t\t\t\t\t ( !selector || selector === handleObj.selector || selector === \"**\" && handleObj.selector ) ) {\n\t\t\t\t\teventType.splice( j--, 1 );\n\n\t\t\t\t\tif ( handleObj.selector ) {\n\t\t\t\t\t\teventType.delegateCount--;\n\t\t\t\t\t}\n\t\t\t\t\tif ( special.remove ) {\n\t\t\t\t\t\tspecial.remove.call( elem, handleObj );\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Remove generic event handler if we removed something and no more handlers exist\n\t\t\t// (avoids potential for endless recursion during removal of special event handlers)\n\t\t\tif ( eventType.length === 0 && origCount !== eventType.length ) {\n\t\t\t\tif ( !special.teardown || special.teardown.call( elem, namespaces ) === false ) {\n\t\t\t\t\tjQuery.removeEvent( elem, type, elemData.handle );\n\t\t\t\t}\n\n\t\t\t\tdelete events[ type ];\n\t\t\t}\n\t\t}\n\n\t\t// Remove the expando if it's no longer used\n\t\tif ( jQuery.isEmptyObject( events ) ) {\n\t\t\thandle = elemData.handle;\n\t\t\tif ( handle ) {\n\t\t\t\thandle.elem = null;\n\t\t\t}\n\n\t\t\t// removeData also checks for emptiness and clears the expando if empty\n\t\t\t// so use it instead of delete\n\t\t\tjQuery.removeData( elem, [ \"events\", \"handle\" ], true );\n\t\t}\n\t},\n\n\t// Events that are safe to short-circuit if no handlers are attached.\n\t// Native DOM events should not be added, they may have inline handlers.\n\tcustomEvent: {\n\t\t\"getData\": true,\n\t\t\"setData\": true,\n\t\t\"changeData\": true\n\t},\n\n\ttrigger: function( event, data, elem, onlyHandlers ) {\n\t\t// Don't do events on text and comment nodes\n\t\tif ( elem && (elem.nodeType === 3 || elem.nodeType === 8) ) {\n\t\t\treturn;\n\t\t}\n\n\t\t// Event object or event type\n\t\tvar type = event.type || event,\n\t\t\tnamespaces = [],\n\t\t\tcache, exclusive, i, cur, old, ontype, special, handle, eventPath, bubbleType;\n\n\t\t// focus/blur morphs to focusin/out; ensure we're not firing them right now\n\t\tif ( rfocusMorph.test( type + jQuery.event.triggered ) ) {\n\t\t\treturn;\n\t\t}\n\n\t\tif ( type.indexOf( \"!\" ) >= 0 ) {\n\t\t\t// Exclusive events trigger only for the exact event (no namespaces)\n\t\t\ttype = type.slice(0, -1);\n\t\t\texclusive = true;\n\t\t}\n\n\t\tif ( type.indexOf( \".\" ) >= 0 ) {\n\t\t\t// Namespaced trigger; create a regexp to match event type in handle()\n\t\t\tnamespaces = type.split(\".\");\n\t\t\ttype = namespaces.shift();\n\t\t\tnamespaces.sort();\n\t\t}\n\n\t\tif ( (!elem || jQuery.event.customEvent[ type ]) && !jQuery.event.global[ type ] ) {\n\t\t\t// No jQuery handlers for this event type, and it can't have inline handlers\n\t\t\treturn;\n\t\t}\n\n\t\t// Caller can pass in an Event, Object, or just an event type string\n\t\tevent = typeof event === \"object\" ?\n\t\t\t// jQuery.Event object\n\t\t\tevent[ jQuery.expando ] ? event :\n\t\t\t// Object literal\n\t\t\tnew jQuery.Event( type, event ) :\n\t\t\t// Just the event type (string)\n\t\t\tnew jQuery.Event( type );\n\n\t\tevent.type = type;\n\t\tevent.isTrigger = true;\n\t\tevent.exclusive = exclusive;\n\t\tevent.namespace = namespaces.join( \".\" );\n\t\tevent.namespace_re = event.namespace? new RegExp(\"(^|\\\\.)\" + namespaces.join(\"\\\\.(?:.*\\\\.)?\") + \"(\\\\.|$)\") : null;\n\t\tontype = type.indexOf( \":\" ) < 0 ? \"on\" + type : \"\";\n\n\t\t// Handle a global trigger\n\t\tif ( !elem ) {\n\n\t\t\t// TODO: Stop taunting the data cache; remove global events and always attach to document\n\t\t\tcache = jQuery.cache;\n\t\t\tfor ( i in cache ) {\n\t\t\t\tif ( cache[ i ].events && cache[ i ].events[ type ] ) {\n\t\t\t\t\tjQuery.event.trigger( event, data, cache[ i ].handle.elem, true );\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\t// Clean up the event in case it is being reused\n\t\tevent.result = undefined;\n\t\tif ( !event.target ) {\n\t\t\tevent.target = elem;\n\t\t}\n\n\t\t// Clone any incoming data and prepend the event, creating the handler arg list\n\t\tdata = data != null ? jQuery.makeArray( data ) : [];\n\t\tdata.unshift( event );\n\n\t\t// Allow special events to draw outside the lines\n\t\tspecial = jQuery.event.special[ type ] || {};\n\t\tif ( special.trigger && special.trigger.apply( elem, data ) === false ) {\n\t\t\treturn;\n\t\t}\n\n\t\t// Determine event propagation path in advance, per W3C events spec (#9951)\n\t\t// Bubble up to document, then to window; watch for a global ownerDocument var (#9724)\n\t\teventPath = [[ elem, special.bindType || type ]];\n\t\tif ( !onlyHandlers && !special.noBubble && !jQuery.isWindow( elem ) ) {\n\n\t\t\tbubbleType = special.delegateType || type;\n\t\t\tcur = rfocusMorph.test( bubbleType + type ) ? elem : elem.parentNode;\n\t\t\told = null;\n\t\t\tfor ( ; cur; cur = cur.parentNode ) {\n\t\t\t\teventPath.push([ cur, bubbleType ]);\n\t\t\t\told = cur;\n\t\t\t}\n\n\t\t\t// Only add window if we got to document (e.g., not plain obj or detached DOM)\n\t\t\tif ( old && old === elem.ownerDocument ) {\n\t\t\t\teventPath.push([ old.defaultView || old.parentWindow || window, bubbleType ]);\n\t\t\t}\n\t\t}\n\n\t\t// Fire handlers on the event path\n\t\tfor ( i = 0; i < eventPath.length && !event.isPropagationStopped(); i++ ) {\n\n\t\t\tcur = eventPath[i][0];\n\t\t\tevent.type = eventPath[i][1];\n\n\t\t\thandle = ( jQuery._data( cur, \"events\" ) || {} )[ event.type ] && jQuery._data( cur, \"handle\" );\n\t\t\tif ( handle ) {\n\t\t\t\thandle.apply( cur, data );\n\t\t\t}\n\t\t\t// Note that this is a bare JS function and not a jQuery handler\n\t\t\thandle = ontype && cur[ ontype ];\n\t\t\tif ( handle && jQuery.acceptData( cur ) && handle.apply( cur, data ) === false ) {\n\t\t\t\tevent.preventDefault();\n\t\t\t}\n\t\t}\n\t\tevent.type = type;\n\n\t\t// If nobody prevented the default action, do it now\n\t\tif ( !onlyHandlers && !event.isDefaultPrevented() ) {\n\n\t\t\tif ( (!special._default || special._default.apply( elem.ownerDocument, data ) === false) &&\n\t\t\t\t!(type === \"click\" && jQuery.nodeName( elem, \"a\" )) && jQuery.acceptData( elem ) ) {\n\n\t\t\t\t// Call a native DOM method on the target with the same name name as the event.\n\t\t\t\t// Can't use an .isFunction() check here because IE6/7 fails that test.\n\t\t\t\t// Don't do default actions on window, that's where global variables be (#6170)\n\t\t\t\t// IE<9 dies on focus/blur to hidden element (#1486)\n\t\t\t\tif ( ontype && elem[ type ] && ((type !== \"focus\" && type !== \"blur\") || event.target.offsetWidth !== 0) && !jQuery.isWindow( elem ) ) {\n\n\t\t\t\t\t// Don't re-trigger an onFOO event when we call its FOO() method\n\t\t\t\t\told = elem[ ontype ];\n\n\t\t\t\t\tif ( old ) {\n\t\t\t\t\t\telem[ ontype ] = null;\n\t\t\t\t\t}\n\n\t\t\t\t\t// Prevent re-triggering of the same event, since we already bubbled it above\n\t\t\t\t\tjQuery.event.triggered = type;\n\t\t\t\t\telem[ type ]();\n\t\t\t\t\tjQuery.event.triggered = undefined;\n\n\t\t\t\t\tif ( old ) {\n\t\t\t\t\t\telem[ ontype ] = old;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn event.result;\n\t},\n\n\tdispatch: function( event ) {\n\n\t\t// Make a writable jQuery.Event from the native event object\n\t\tevent = jQuery.event.fix( event || window.event );\n\n\t\tvar handlers = ( (jQuery._data( this, \"events\" ) || {} )[ event.type ] || []),\n\t\t\tdelegateCount = handlers.delegateCount,\n\t\t\targs = [].slice.call( arguments, 0 ),\n\t\t\trun_all = !event.exclusive && !event.namespace,\n\t\t\thandlerQueue = [],\n\t\t\ti, j, cur, jqcur, ret, selMatch, matched, matches, handleObj, sel, related;\n\n\t\t// Use the fix-ed jQuery.Event rather than the (read-only) native event\n\t\targs[0] = event;\n\t\tevent.delegateTarget = this;\n\n\t\t// Determine handlers that should run if there are delegated events\n\t\t// Avoid disabled elements in IE (#6911) and non-left-click bubbling in Firefox (#3861)\n\t\tif ( delegateCount && !event.target.disabled && !(event.button && event.type === \"click\") ) {\n\n\t\t\t// Pregenerate a single jQuery object for reuse with .is()\n\t\t\tjqcur = jQuery(this);\n\t\t\tjqcur.context = this.ownerDocument || this;\n\n\t\t\tfor ( cur = event.target; cur != this; cur = cur.parentNode || this ) {\n\t\t\t\tselMatch = {};\n\t\t\t\tmatches = [];\n\t\t\t\tjqcur[0] = cur;\n\t\t\t\tfor ( i = 0; i < delegateCount; i++ ) {\n\t\t\t\t\thandleObj = handlers[ i ];\n\t\t\t\t\tsel = handleObj.selector;\n\n\t\t\t\t\tif ( selMatch[ sel ] === undefined ) {\n\t\t\t\t\t\tselMatch[ sel ] = (\n\t\t\t\t\t\t\thandleObj.quick ? quickIs( cur, handleObj.quick ) : jqcur.is( sel )\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t\tif ( selMatch[ sel ] ) {\n\t\t\t\t\t\tmatches.push( handleObj );\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif ( matches.length ) {\n\t\t\t\t\thandlerQueue.push({ elem: cur, matches: matches });\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Add the remaining (directly-bound) handlers\n\t\tif ( handlers.length > delegateCount ) {\n\t\t\thandlerQueue.push({ elem: this, matches: handlers.slice( delegateCount ) });\n\t\t}\n\n\t\t// Run delegates first; they may want to stop propagation beneath us\n\t\tfor ( i = 0; i < handlerQueue.length && !event.isPropagationStopped(); i++ ) {\n\t\t\tmatched = handlerQueue[ i ];\n\t\t\tevent.currentTarget = matched.elem;\n\n\t\t\tfor ( j = 0; j < matched.matches.length && !event.isImmediatePropagationStopped(); j++ ) {\n\t\t\t\thandleObj = matched.matches[ j ];\n\n\t\t\t\t// Triggered event must either 1) be non-exclusive and have no namespace, or\n\t\t\t\t// 2) have namespace(s) a subset or equal to those in the bound event (both can have no namespace).\n\t\t\t\tif ( run_all || (!event.namespace && !handleObj.namespace) || event.namespace_re && event.namespace_re.test( handleObj.namespace ) ) {\n\n\t\t\t\t\tevent.data = handleObj.data;\n\t\t\t\t\tevent.handleObj = handleObj;\n\n\t\t\t\t\tret = ( (jQuery.event.special[ handleObj.origType ] || {}).handle || handleObj.handler )\n\t\t\t\t\t\t\t.apply( matched.elem, args );\n\n\t\t\t\t\tif ( ret !== undefined ) {\n\t\t\t\t\t\tevent.result = ret;\n\t\t\t\t\t\tif ( ret === false ) {\n\t\t\t\t\t\t\tevent.preventDefault();\n\t\t\t\t\t\t\tevent.stopPropagation();\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn event.result;\n\t},\n\n\t// Includes some event props shared by KeyEvent and MouseEvent\n\t// *** attrChange attrName relatedNode srcElement  are not normalized, non-W3C, deprecated, will be removed in 1.8 ***\n\tprops: \"attrChange attrName relatedNode srcElement altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which\".split(\" \"),\n\n\tfixHooks: {},\n\n\tkeyHooks: {\n\t\tprops: \"char charCode key keyCode\".split(\" \"),\n\t\tfilter: function( event, original ) {\n\n\t\t\t// Add which for key events\n\t\t\tif ( event.which == null ) {\n\t\t\t\tevent.which = original.charCode != null ? original.charCode : original.keyCode;\n\t\t\t}\n\n\t\t\treturn event;\n\t\t}\n\t},\n\n\tmouseHooks: {\n\t\tprops: \"button buttons clientX clientY fromElement offsetX offsetY pageX pageY screenX screenY toElement\".split(\" \"),\n\t\tfilter: function( event, original ) {\n\t\t\tvar eventDoc, doc, body,\n\t\t\t\tbutton = original.button,\n\t\t\t\tfromElement = original.fromElement;\n\n\t\t\t// Calculate pageX/Y if missing and clientX/Y available\n\t\t\tif ( event.pageX == null && original.clientX != null ) {\n\t\t\t\teventDoc = event.target.ownerDocument || document;\n\t\t\t\tdoc = eventDoc.documentElement;\n\t\t\t\tbody = eventDoc.body;\n\n\t\t\t\tevent.pageX = original.clientX + ( doc && doc.scrollLeft || body && body.scrollLeft || 0 ) - ( doc && doc.clientLeft || body && body.clientLeft || 0 );\n\t\t\t\tevent.pageY = original.clientY + ( doc && doc.scrollTop  || body && body.scrollTop  || 0 ) - ( doc && doc.clientTop  || body && body.clientTop  || 0 );\n\t\t\t}\n\n\t\t\t// Add relatedTarget, if necessary\n\t\t\tif ( !event.relatedTarget && fromElement ) {\n\t\t\t\tevent.relatedTarget = fromElement === event.target ? original.toElement : fromElement;\n\t\t\t}\n\n\t\t\t// Add which for click: 1 === left; 2 === middle; 3 === right\n\t\t\t// Note: button is not normalized, so don't use it\n\t\t\tif ( !event.which && button !== undefined ) {\n\t\t\t\tevent.which = ( button & 1 ? 1 : ( button & 2 ? 3 : ( button & 4 ? 2 : 0 ) ) );\n\t\t\t}\n\n\t\t\treturn event;\n\t\t}\n\t},\n\n\tfix: function( event ) {\n\t\tif ( event[ jQuery.expando ] ) {\n\t\t\treturn event;\n\t\t}\n\n\t\t// Create a writable copy of the event object and normalize some properties\n\t\tvar i, prop,\n\t\t\toriginalEvent = event,\n\t\t\tfixHook = jQuery.event.fixHooks[ event.type ] || {},\n\t\t\tcopy = fixHook.props ? this.props.concat( fixHook.props ) : this.props;\n\n\t\tevent = jQuery.Event( originalEvent );\n\n\t\tfor ( i = copy.length; i; ) {\n\t\t\tprop = copy[ --i ];\n\t\t\tevent[ prop ] = originalEvent[ prop ];\n\t\t}\n\n\t\t// Fix target property, if necessary (#1925, IE 6/7/8 & Safari2)\n\t\tif ( !event.target ) {\n\t\t\tevent.target = originalEvent.srcElement || document;\n\t\t}\n\n\t\t// Target should not be a text node (#504, Safari)\n\t\tif ( event.target.nodeType === 3 ) {\n\t\t\tevent.target = event.target.parentNode;\n\t\t}\n\n\t\t// For mouse/key events; add metaKey if it's not there (#3368, IE6/7/8)\n\t\tif ( event.metaKey === undefined ) {\n\t\t\tevent.metaKey = event.ctrlKey;\n\t\t}\n\n\t\treturn fixHook.filter? fixHook.filter( event, originalEvent ) : event;\n\t},\n\n\tspecial: {\n\t\tready: {\n\t\t\t// Make sure the ready event is setup\n\t\t\tsetup: jQuery.bindReady\n\t\t},\n\n\t\tload: {\n\t\t\t// Prevent triggered image.load events from bubbling to window.load\n\t\t\tnoBubble: true\n\t\t},\n\n\t\tfocus: {\n\t\t\tdelegateType: \"focusin\"\n\t\t},\n\t\tblur: {\n\t\t\tdelegateType: \"focusout\"\n\t\t},\n\n\t\tbeforeunload: {\n\t\t\tsetup: function( data, namespaces, eventHandle ) {\n\t\t\t\t// We only want to do this special case on windows\n\t\t\t\tif ( jQuery.isWindow( this ) ) {\n\t\t\t\t\tthis.onbeforeunload = eventHandle;\n\t\t\t\t}\n\t\t\t},\n\n\t\t\tteardown: function( namespaces, eventHandle ) {\n\t\t\t\tif ( this.onbeforeunload === eventHandle ) {\n\t\t\t\t\tthis.onbeforeunload = null;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t},\n\n\tsimulate: function( type, elem, event, bubble ) {\n\t\t// Piggyback on a donor event to simulate a different one.\n\t\t// Fake originalEvent to avoid donor's stopPropagation, but if the\n\t\t// simulated event prevents default then we do the same on the donor.\n\t\tvar e = jQuery.extend(\n\t\t\tnew jQuery.Event(),\n\t\t\tevent,\n\t\t\t{ type: type,\n\t\t\t\tisSimulated: true,\n\t\t\t\toriginalEvent: {}\n\t\t\t}\n\t\t);\n\t\tif ( bubble ) {\n\t\t\tjQuery.event.trigger( e, null, elem );\n\t\t} else {\n\t\t\tjQuery.event.dispatch.call( elem, e );\n\t\t}\n\t\tif ( e.isDefaultPrevented() ) {\n\t\t\tevent.preventDefault();\n\t\t}\n\t}\n};\n\n// Some plugins are using, but it's undocumented/deprecated and will be removed.\n// The 1.7 special event interface should provide all the hooks needed now.\njQuery.event.handle = jQuery.event.dispatch;\n\njQuery.removeEvent = document.removeEventListener ?\n\tfunction( elem, type, handle ) {\n\t\tif ( elem.removeEventListener ) {\n\t\t\telem.removeEventListener( type, handle, false );\n\t\t}\n\t} :\n\tfunction( elem, type, handle ) {\n\t\tif ( elem.detachEvent ) {\n\t\t\telem.detachEvent( \"on\" + type, handle );\n\t\t}\n\t};\n\njQuery.Event = function( src, props ) {\n\t// Allow instantiation without the 'new' keyword\n\tif ( !(this instanceof jQuery.Event) ) {\n\t\treturn new jQuery.Event( src, props );\n\t}\n\n\t// Event object\n\tif ( src && src.type ) {\n\t\tthis.originalEvent = src;\n\t\tthis.type = src.type;\n\n\t\t// Events bubbling up the document may have been marked as prevented\n\t\t// by a handler lower down the tree; reflect the correct value.\n\t\tthis.isDefaultPrevented = ( src.defaultPrevented || src.returnValue === false ||\n\t\t\tsrc.getPreventDefault && src.getPreventDefault() ) ? returnTrue : returnFalse;\n\n\t// Event type\n\t} else {\n\t\tthis.type = src;\n\t}\n\n\t// Put explicitly provided properties onto the event object\n\tif ( props ) {\n\t\tjQuery.extend( this, props );\n\t}\n\n\t// Create a timestamp if incoming event doesn't have one\n\tthis.timeStamp = src && src.timeStamp || jQuery.now();\n\n\t// Mark it as fixed\n\tthis[ jQuery.expando ] = true;\n};\n\nfunction returnFalse() {\n\treturn false;\n}\nfunction returnTrue() {\n\treturn true;\n}\n\n// jQuery.Event is based on DOM3 Events as specified by the ECMAScript Language Binding\n// http://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html\njQuery.Event.prototype = {\n\tpreventDefault: function() {\n\t\tthis.isDefaultPrevented = returnTrue;\n\n\t\tvar e = this.originalEvent;\n\t\tif ( !e ) {\n\t\t\treturn;\n\t\t}\n\n\t\t// if preventDefault exists run it on the original event\n\t\tif ( e.preventDefault ) {\n\t\t\te.preventDefault();\n\n\t\t// otherwise set the returnValue property of the original event to false (IE)\n\t\t} else {\n\t\t\te.returnValue = false;\n\t\t}\n\t},\n\tstopPropagation: function() {\n\t\tthis.isPropagationStopped = returnTrue;\n\n\t\tvar e = this.originalEvent;\n\t\tif ( !e ) {\n\t\t\treturn;\n\t\t}\n\t\t// if stopPropagation exists run it on the original event\n\t\tif ( e.stopPropagation ) {\n\t\t\te.stopPropagation();\n\t\t}\n\t\t// otherwise set the cancelBubble property of the original event to true (IE)\n\t\te.cancelBubble = true;\n\t},\n\tstopImmediatePropagation: function() {\n\t\tthis.isImmediatePropagationStopped = returnTrue;\n\t\tthis.stopPropagation();\n\t},\n\tisDefaultPrevented: returnFalse,\n\tisPropagationStopped: returnFalse,\n\tisImmediatePropagationStopped: returnFalse\n};\n\n// Create mouseenter/leave events using mouseover/out and event-time checks\njQuery.each({\n\tmouseenter: \"mouseover\",\n\tmouseleave: \"mouseout\"\n}, function( orig, fix ) {\n\tjQuery.event.special[ orig ] = {\n\t\tdelegateType: fix,\n\t\tbindType: fix,\n\n\t\thandle: function( event ) {\n\t\t\tvar target = this,\n\t\t\t\trelated = event.relatedTarget,\n\t\t\t\thandleObj = event.handleObj,\n\t\t\t\tselector = handleObj.selector,\n\t\t\t\tret;\n\n\t\t\t// For mousenter/leave call the handler if related is outside the target.\n\t\t\t// NB: No relatedTarget if the mouse left/entered the browser window\n\t\t\tif ( !related || (related !== target && !jQuery.contains( target, related )) ) {\n\t\t\t\tevent.type = handleObj.origType;\n\t\t\t\tret = handleObj.handler.apply( this, arguments );\n\t\t\t\tevent.type = fix;\n\t\t\t}\n\t\t\treturn ret;\n\t\t}\n\t};\n});\n\n// IE submit delegation\nif ( !jQuery.support.submitBubbles ) {\n\n\tjQuery.event.special.submit = {\n\t\tsetup: function() {\n\t\t\t// Only need this for delegated form submit events\n\t\t\tif ( jQuery.nodeName( this, \"form\" ) ) {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\t// Lazy-add a submit handler when a descendant form may potentially be submitted\n\t\t\tjQuery.event.add( this, \"click._submit keypress._submit\", function( e ) {\n\t\t\t\t// Node name check avoids a VML-related crash in IE (#9807)\n\t\t\t\tvar elem = e.target,\n\t\t\t\t\tform = jQuery.nodeName( elem, \"input\" ) || jQuery.nodeName( elem, \"button\" ) ? elem.form : undefined;\n\t\t\t\tif ( form && !form._submit_attached ) {\n\t\t\t\t\tjQuery.event.add( form, \"submit._submit\", function( event ) {\n\t\t\t\t\t\t// If form was submitted by the user, bubble the event up the tree\n\t\t\t\t\t\tif ( this.parentNode && !event.isTrigger ) {\n\t\t\t\t\t\t\tjQuery.event.simulate( \"submit\", this.parentNode, event, true );\n\t\t\t\t\t\t}\n\t\t\t\t\t});\n\t\t\t\t\tform._submit_attached = true;\n\t\t\t\t}\n\t\t\t});\n\t\t\t// return undefined since we don't need an event listener\n\t\t},\n\n\t\tteardown: function() {\n\t\t\t// Only need this for delegated form submit events\n\t\t\tif ( jQuery.nodeName( this, \"form\" ) ) {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\t// Remove delegated handlers; cleanData eventually reaps submit handlers attached above\n\t\t\tjQuery.event.remove( this, \"._submit\" );\n\t\t}\n\t};\n}\n\n// IE change delegation and checkbox/radio fix\nif ( !jQuery.support.changeBubbles ) {\n\n\tjQuery.event.special.change = {\n\n\t\tsetup: function() {\n\n\t\t\tif ( rformElems.test( this.nodeName ) ) {\n\t\t\t\t// IE doesn't fire change on a check/radio until blur; trigger it on click\n\t\t\t\t// after a propertychange. Eat the blur-change in special.change.handle.\n\t\t\t\t// This still fires onchange a second time for check/radio after blur.\n\t\t\t\tif ( this.type === \"checkbox\" || this.type === \"radio\" ) {\n\t\t\t\t\tjQuery.event.add( this, \"propertychange._change\", function( event ) {\n\t\t\t\t\t\tif ( event.originalEvent.propertyName === \"checked\" ) {\n\t\t\t\t\t\t\tthis._just_changed = true;\n\t\t\t\t\t\t}\n\t\t\t\t\t});\n\t\t\t\t\tjQuery.event.add( this, \"click._change\", function( event ) {\n\t\t\t\t\t\tif ( this._just_changed && !event.isTrigger ) {\n\t\t\t\t\t\t\tthis._just_changed = false;\n\t\t\t\t\t\t\tjQuery.event.simulate( \"change\", this, event, true );\n\t\t\t\t\t\t}\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\t// Delegated event; lazy-add a change handler on descendant inputs\n\t\t\tjQuery.event.add( this, \"beforeactivate._change\", function( e ) {\n\t\t\t\tvar elem = e.target;\n\n\t\t\t\tif ( rformElems.test( elem.nodeName ) && !elem._change_attached ) {\n\t\t\t\t\tjQuery.event.add( elem, \"change._change\", function( event ) {\n\t\t\t\t\t\tif ( this.parentNode && !event.isSimulated && !event.isTrigger ) {\n\t\t\t\t\t\t\tjQuery.event.simulate( \"change\", this.parentNode, event, true );\n\t\t\t\t\t\t}\n\t\t\t\t\t});\n\t\t\t\t\telem._change_attached = true;\n\t\t\t\t}\n\t\t\t});\n\t\t},\n\n\t\thandle: function( event ) {\n\t\t\tvar elem = event.target;\n\n\t\t\t// Swallow native change events from checkbox/radio, we already triggered them above\n\t\t\tif ( this !== elem || event.isSimulated || event.isTrigger || (elem.type !== \"radio\" && elem.type !== \"checkbox\") ) {\n\t\t\t\treturn event.handleObj.handler.apply( this, arguments );\n\t\t\t}\n\t\t},\n\n\t\tteardown: function() {\n\t\t\tjQuery.event.remove( this, \"._change\" );\n\n\t\t\treturn rformElems.test( this.nodeName );\n\t\t}\n\t};\n}\n\n// Create \"bubbling\" focus and blur events\nif ( !jQuery.support.focusinBubbles ) {\n\tjQuery.each({ focus: \"focusin\", blur: \"focusout\" }, function( orig, fix ) {\n\n\t\t// Attach a single capturing handler while someone wants focusin/focusout\n\t\tvar attaches = 0,\n\t\t\thandler = function( event ) {\n\t\t\t\tjQuery.event.simulate( fix, event.target, jQuery.event.fix( event ), true );\n\t\t\t};\n\n\t\tjQuery.event.special[ fix ] = {\n\t\t\tsetup: function() {\n\t\t\t\tif ( attaches++ === 0 ) {\n\t\t\t\t\tdocument.addEventListener( orig, handler, true );\n\t\t\t\t}\n\t\t\t},\n\t\t\tteardown: function() {\n\t\t\t\tif ( --attaches === 0 ) {\n\t\t\t\t\tdocument.removeEventListener( orig, handler, true );\n\t\t\t\t}\n\t\t\t}\n\t\t};\n\t});\n}\n\njQuery.fn.extend({\n\n\ton: function( types, selector, data, fn, /*INTERNAL*/ one ) {\n\t\tvar origFn, type;\n\n\t\t// Types can be a map of types/handlers\n\t\tif ( typeof types === \"object\" ) {\n\t\t\t// ( types-Object, selector, data )\n\t\t\tif ( typeof selector !== \"string\" ) {\n\t\t\t\t// ( types-Object, data )\n\t\t\t\tdata = selector;\n\t\t\t\tselector = undefined;\n\t\t\t}\n\t\t\tfor ( type in types ) {\n\t\t\t\tthis.on( type, selector, data, types[ type ], one );\n\t\t\t}\n\t\t\treturn this;\n\t\t}\n\n\t\tif ( data == null && fn == null ) {\n\t\t\t// ( types, fn )\n\t\t\tfn = selector;\n\t\t\tdata = selector = undefined;\n\t\t} else if ( fn == null ) {\n\t\t\tif ( typeof selector === \"string\" ) {\n\t\t\t\t// ( types, selector, fn )\n\t\t\t\tfn = data;\n\t\t\t\tdata = undefined;\n\t\t\t} else {\n\t\t\t\t// ( types, data, fn )\n\t\t\t\tfn = data;\n\t\t\t\tdata = selector;\n\t\t\t\tselector = undefined;\n\t\t\t}\n\t\t}\n\t\tif ( fn === false ) {\n\t\t\tfn = returnFalse;\n\t\t} else if ( !fn ) {\n\t\t\treturn this;\n\t\t}\n\n\t\tif ( one === 1 ) {\n\t\t\torigFn = fn;\n\t\t\tfn = function( event ) {\n\t\t\t\t// Can use an empty set, since event contains the info\n\t\t\t\tjQuery().off( event );\n\t\t\t\treturn origFn.apply( this, arguments );\n\t\t\t};\n\t\t\t// Use same guid so caller can remove using origFn\n\t\t\tfn.guid = origFn.guid || ( origFn.guid = jQuery.guid++ );\n\t\t}\n\t\treturn this.each( function() {\n\t\t\tjQuery.event.add( this, types, fn, data, selector );\n\t\t});\n\t},\n\tone: function( types, selector, data, fn ) {\n\t\treturn this.on.call( this, types, selector, data, fn, 1 );\n\t},\n\toff: function( types, selector, fn ) {\n\t\tif ( types && types.preventDefault && types.handleObj ) {\n\t\t\t// ( event )  dispatched jQuery.Event\n\t\t\tvar handleObj = types.handleObj;\n\t\t\tjQuery( types.delegateTarget ).off(\n\t\t\t\thandleObj.namespace? handleObj.type + \".\" + handleObj.namespace : handleObj.type,\n\t\t\t\thandleObj.selector,\n\t\t\t\thandleObj.handler\n\t\t\t);\n\t\t\treturn this;\n\t\t}\n\t\tif ( typeof types === \"object\" ) {\n\t\t\t// ( types-object [, selector] )\n\t\t\tfor ( var type in types ) {\n\t\t\t\tthis.off( type, selector, types[ type ] );\n\t\t\t}\n\t\t\treturn this;\n\t\t}\n\t\tif ( selector === false || typeof selector === \"function\" ) {\n\t\t\t// ( types [, fn] )\n\t\t\tfn = selector;\n\t\t\tselector = undefined;\n\t\t}\n\t\tif ( fn === false ) {\n\t\t\tfn = returnFalse;\n\t\t}\n\t\treturn this.each(function() {\n\t\t\tjQuery.event.remove( this, types, fn, selector );\n\t\t});\n\t},\n\n\tbind: function( types, data, fn ) {\n\t\treturn this.on( types, null, data, fn );\n\t},\n\tunbind: function( types, fn ) {\n\t\treturn this.off( types, null, fn );\n\t},\n\n\tlive: function( types, data, fn ) {\n\t\tjQuery( this.context ).on( types, this.selector, data, fn );\n\t\treturn this;\n\t},\n\tdie: function( types, fn ) {\n\t\tjQuery( this.context ).off( types, this.selector || \"**\", fn );\n\t\treturn this;\n\t},\n\n\tdelegate: function( selector, types, data, fn ) {\n\t\treturn this.on( types, selector, data, fn );\n\t},\n\tundelegate: function( selector, types, fn ) {\n\t\t// ( namespace ) or ( selector, types [, fn] )\n\t\treturn arguments.length == 1? this.off( selector, \"**\" ) : this.off( types, selector, fn );\n\t},\n\n\ttrigger: function( type, data ) {\n\t\treturn this.each(function() {\n\t\t\tjQuery.event.trigger( type, data, this );\n\t\t});\n\t},\n\ttriggerHandler: function( type, data ) {\n\t\tif ( this[0] ) {\n\t\t\treturn jQuery.event.trigger( type, data, this[0], true );\n\t\t}\n\t},\n\n\ttoggle: function( fn ) {\n\t\t// Save reference to arguments for access in closure\n\t\tvar args = arguments,\n\t\t\tguid = fn.guid || jQuery.guid++,\n\t\t\ti = 0,\n\t\t\ttoggler = function( event ) {\n\t\t\t\t// Figure out which function to execute\n\t\t\t\tvar lastToggle = ( jQuery._data( this, \"lastToggle\" + fn.guid ) || 0 ) % i;\n\t\t\t\tjQuery._data( this, \"lastToggle\" + fn.guid, lastToggle + 1 );\n\n\t\t\t\t// Make sure that clicks stop\n\t\t\t\tevent.preventDefault();\n\n\t\t\t\t// and execute the function\n\t\t\t\treturn args[ lastToggle ].apply( this, arguments ) || false;\n\t\t\t};\n\n\t\t// link all the functions, so any of them can unbind this click handler\n\t\ttoggler.guid = guid;\n\t\twhile ( i < args.length ) {\n\t\t\targs[ i++ ].guid = guid;\n\t\t}\n\n\t\treturn this.click( toggler );\n\t},\n\n\thover: function( fnOver, fnOut ) {\n\t\treturn this.mouseenter( fnOver ).mouseleave( fnOut || fnOver );\n\t}\n});\n\njQuery.each( (\"blur focus focusin focusout load resize scroll unload click dblclick \" +\n\t\"mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave \" +\n\t\"change select submit keydown keypress keyup error contextmenu\").split(\" \"), function( i, name ) {\n\n\t// Handle event binding\n\tjQuery.fn[ name ] = function( data, fn ) {\n\t\tif ( fn == null ) {\n\t\t\tfn = data;\n\t\t\tdata = null;\n\t\t}\n\n\t\treturn arguments.length > 0 ?\n\t\t\tthis.on( name, null, data, fn ) :\n\t\t\tthis.trigger( name );\n\t};\n\n\tif ( jQuery.attrFn ) {\n\t\tjQuery.attrFn[ name ] = true;\n\t}\n\n\tif ( rkeyEvent.test( name ) ) {\n\t\tjQuery.event.fixHooks[ name ] = jQuery.event.keyHooks;\n\t}\n\n\tif ( rmouseEvent.test( name ) ) {\n\t\tjQuery.event.fixHooks[ name ] = jQuery.event.mouseHooks;\n\t}\n});\n\n\n\n/*!\n * Sizzle CSS Selector Engine\n *  Copyright 2011, The Dojo Foundation\n *  Released under the MIT, BSD, and GPL Licenses.\n *  More information: http://sizzlejs.com/\n */\n(function(){\n\nvar chunker = /((?:\\((?:\\([^()]+\\)|[^()]+)+\\)|\\[(?:\\[[^\\[\\]]*\\]|['\"][^'\"]*['\"]|[^\\[\\]'\"]+)+\\]|\\\\.|[^ >+~,(\\[\\\\]+)+|[>+~])(\\s*,\\s*)?((?:.|\\r|\\n)*)/g,\n\texpando = \"sizcache\" + (Math.random() + '').replace('.', ''),\n\tdone = 0,\n\ttoString = Object.prototype.toString,\n\thasDuplicate = false,\n\tbaseHasDuplicate = true,\n\trBackslash = /\\\\/g,\n\trReturn = /\\r\\n/g,\n\trNonWord = /\\W/;\n\n// Here we check if the JavaScript engine is using some sort of\n// optimization where it does not always call our comparision\n// function. If that is the case, discard the hasDuplicate value.\n//   Thus far that includes Google Chrome.\n[0, 0].sort(function() {\n\tbaseHasDuplicate = false;\n\treturn 0;\n});\n\nvar Sizzle = function( selector, context, results, seed ) {\n\tresults = results || [];\n\tcontext = context || document;\n\n\tvar origContext = context;\n\n\tif ( context.nodeType !== 1 && context.nodeType !== 9 ) {\n\t\treturn [];\n\t}\n\t\n\tif ( !selector || typeof selector !== \"string\" ) {\n\t\treturn results;\n\t}\n\n\tvar m, set, checkSet, extra, ret, cur, pop, i,\n\t\tprune = true,\n\t\tcontextXML = Sizzle.isXML( context ),\n\t\tparts = [],\n\t\tsoFar = selector;\n\t\n\t// Reset the position of the chunker regexp (start from head)\n\tdo {\n\t\tchunker.exec( \"\" );\n\t\tm = chunker.exec( soFar );\n\n\t\tif ( m ) {\n\t\t\tsoFar = m[3];\n\t\t\n\t\t\tparts.push( m[1] );\n\t\t\n\t\t\tif ( m[2] ) {\n\t\t\t\textra = m[3];\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t} while ( m );\n\n\tif ( parts.length > 1 && origPOS.exec( selector ) ) {\n\n\t\tif ( parts.length === 2 && Expr.relative[ parts[0] ] ) {\n\t\t\tset = posProcess( parts[0] + parts[1], context, seed );\n\n\t\t} else {\n\t\t\tset = Expr.relative[ parts[0] ] ?\n\t\t\t\t[ context ] :\n\t\t\t\tSizzle( parts.shift(), context );\n\n\t\t\twhile ( parts.length ) {\n\t\t\t\tselector = parts.shift();\n\n\t\t\t\tif ( Expr.relative[ selector ] ) {\n\t\t\t\t\tselector += parts.shift();\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tset = posProcess( selector, set, seed );\n\t\t\t}\n\t\t}\n\n\t} else {\n\t\t// Take a shortcut and set the context if the root selector is an ID\n\t\t// (but not if it'll be faster if the inner selector is an ID)\n\t\tif ( !seed && parts.length > 1 && context.nodeType === 9 && !contextXML &&\n\t\t\t\tExpr.match.ID.test(parts[0]) && !Expr.match.ID.test(parts[parts.length - 1]) ) {\n\n\t\t\tret = Sizzle.find( parts.shift(), context, contextXML );\n\t\t\tcontext = ret.expr ?\n\t\t\t\tSizzle.filter( ret.expr, ret.set )[0] :\n\t\t\t\tret.set[0];\n\t\t}\n\n\t\tif ( context ) {\n\t\t\tret = seed ?\n\t\t\t\t{ expr: parts.pop(), set: makeArray(seed) } :\n\t\t\t\tSizzle.find( parts.pop(), parts.length === 1 && (parts[0] === \"~\" || parts[0] === \"+\") && context.parentNode ? context.parentNode : context, contextXML );\n\n\t\t\tset = ret.expr ?\n\t\t\t\tSizzle.filter( ret.expr, ret.set ) :\n\t\t\t\tret.set;\n\n\t\t\tif ( parts.length > 0 ) {\n\t\t\t\tcheckSet = makeArray( set );\n\n\t\t\t} else {\n\t\t\t\tprune = false;\n\t\t\t}\n\n\t\t\twhile ( parts.length ) {\n\t\t\t\tcur = parts.pop();\n\t\t\t\tpop = cur;\n\n\t\t\t\tif ( !Expr.relative[ cur ] ) {\n\t\t\t\t\tcur = \"\";\n\t\t\t\t} else {\n\t\t\t\t\tpop = parts.pop();\n\t\t\t\t}\n\n\t\t\t\tif ( pop == null ) {\n\t\t\t\t\tpop = context;\n\t\t\t\t}\n\n\t\t\t\tExpr.relative[ cur ]( checkSet, pop, contextXML );\n\t\t\t}\n\n\t\t} else {\n\t\t\tcheckSet = parts = [];\n\t\t}\n\t}\n\n\tif ( !checkSet ) {\n\t\tcheckSet = set;\n\t}\n\n\tif ( !checkSet ) {\n\t\tSizzle.error( cur || selector );\n\t}\n\n\tif ( toString.call(checkSet) === \"[object Array]\" ) {\n\t\tif ( !prune ) {\n\t\t\tresults.push.apply( results, checkSet );\n\n\t\t} else if ( context && context.nodeType === 1 ) {\n\t\t\tfor ( i = 0; checkSet[i] != null; i++ ) {\n\t\t\t\tif ( checkSet[i] && (checkSet[i] === true || checkSet[i].nodeType === 1 && Sizzle.contains(context, checkSet[i])) ) {\n\t\t\t\t\tresults.push( set[i] );\n\t\t\t\t}\n\t\t\t}\n\n\t\t} else {\n\t\t\tfor ( i = 0; checkSet[i] != null; i++ ) {\n\t\t\t\tif ( checkSet[i] && checkSet[i].nodeType === 1 ) {\n\t\t\t\t\tresults.push( set[i] );\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t} else {\n\t\tmakeArray( checkSet, results );\n\t}\n\n\tif ( extra ) {\n\t\tSizzle( extra, origContext, results, seed );\n\t\tSizzle.uniqueSort( results );\n\t}\n\n\treturn results;\n};\n\nSizzle.uniqueSort = function( results ) {\n\tif ( sortOrder ) {\n\t\thasDuplicate = baseHasDuplicate;\n\t\tresults.sort( sortOrder );\n\n\t\tif ( hasDuplicate ) {\n\t\t\tfor ( var i = 1; i < results.length; i++ ) {\n\t\t\t\tif ( results[i] === results[ i - 1 ] ) {\n\t\t\t\t\tresults.splice( i--, 1 );\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\treturn results;\n};\n\nSizzle.matches = function( expr, set ) {\n\treturn Sizzle( expr, null, null, set );\n};\n\nSizzle.matchesSelector = function( node, expr ) {\n\treturn Sizzle( expr, null, null, [node] ).length > 0;\n};\n\nSizzle.find = function( expr, context, isXML ) {\n\tvar set, i, len, match, type, left;\n\n\tif ( !expr ) {\n\t\treturn [];\n\t}\n\n\tfor ( i = 0, len = Expr.order.length; i < len; i++ ) {\n\t\ttype = Expr.order[i];\n\t\t\n\t\tif ( (match = Expr.leftMatch[ type ].exec( expr )) ) {\n\t\t\tleft = match[1];\n\t\t\tmatch.splice( 1, 1 );\n\n\t\t\tif ( left.substr( left.length - 1 ) !== \"\\\\\" ) {\n\t\t\t\tmatch[1] = (match[1] || \"\").replace( rBackslash, \"\" );\n\t\t\t\tset = Expr.find[ type ]( match, context, isXML );\n\n\t\t\t\tif ( set != null ) {\n\t\t\t\t\texpr = expr.replace( Expr.match[ type ], \"\" );\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tif ( !set ) {\n\t\tset = typeof context.getElementsByTagName !== \"undefined\" ?\n\t\t\tcontext.getElementsByTagName( \"*\" ) :\n\t\t\t[];\n\t}\n\n\treturn { set: set, expr: expr };\n};\n\nSizzle.filter = function( expr, set, inplace, not ) {\n\tvar match, anyFound,\n\t\ttype, found, item, filter, left,\n\t\ti, pass,\n\t\told = expr,\n\t\tresult = [],\n\t\tcurLoop = set,\n\t\tisXMLFilter = set && set[0] && Sizzle.isXML( set[0] );\n\n\twhile ( expr && set.length ) {\n\t\tfor ( type in Expr.filter ) {\n\t\t\tif ( (match = Expr.leftMatch[ type ].exec( expr )) != null && match[2] ) {\n\t\t\t\tfilter = Expr.filter[ type ];\n\t\t\t\tleft = match[1];\n\n\t\t\t\tanyFound = false;\n\n\t\t\t\tmatch.splice(1,1);\n\n\t\t\t\tif ( left.substr( left.length - 1 ) === \"\\\\\" ) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tif ( curLoop === result ) {\n\t\t\t\t\tresult = [];\n\t\t\t\t}\n\n\t\t\t\tif ( Expr.preFilter[ type ] ) {\n\t\t\t\t\tmatch = Expr.preFilter[ type ]( match, curLoop, inplace, result, not, isXMLFilter );\n\n\t\t\t\t\tif ( !match ) {\n\t\t\t\t\t\tanyFound = found = true;\n\n\t\t\t\t\t} else if ( match === true ) {\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif ( match ) {\n\t\t\t\t\tfor ( i = 0; (item = curLoop[i]) != null; i++ ) {\n\t\t\t\t\t\tif ( item ) {\n\t\t\t\t\t\t\tfound = filter( item, match, i, curLoop );\n\t\t\t\t\t\t\tpass = not ^ found;\n\n\t\t\t\t\t\t\tif ( inplace && found != null ) {\n\t\t\t\t\t\t\t\tif ( pass ) {\n\t\t\t\t\t\t\t\t\tanyFound = true;\n\n\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\tcurLoop[i] = false;\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t} else if ( pass ) {\n\t\t\t\t\t\t\t\tresult.push( item );\n\t\t\t\t\t\t\t\tanyFound = true;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif ( found !== undefined ) {\n\t\t\t\t\tif ( !inplace ) {\n\t\t\t\t\t\tcurLoop = result;\n\t\t\t\t\t}\n\n\t\t\t\t\texpr = expr.replace( Expr.match[ type ], \"\" );\n\n\t\t\t\t\tif ( !anyFound ) {\n\t\t\t\t\t\treturn [];\n\t\t\t\t\t}\n\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Improper expression\n\t\tif ( expr === old ) {\n\t\t\tif ( anyFound == null ) {\n\t\t\t\tSizzle.error( expr );\n\n\t\t\t} else {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\told = expr;\n\t}\n\n\treturn curLoop;\n};\n\nSizzle.error = function( msg ) {\n\tthrow new Error( \"Syntax error, unrecognized expression: \" + msg );\n};\n\n/**\n * Utility function for retreiving the text value of an array of DOM nodes\n * @param {Array|Element} elem\n */\nvar getText = Sizzle.getText = function( elem ) {\n    var i, node,\n\t\tnodeType = elem.nodeType,\n\t\tret = \"\";\n\n\tif ( nodeType ) {\n\t\tif ( nodeType === 1 || nodeType === 9 ) {\n\t\t\t// Use textContent || innerText for elements\n\t\t\tif ( typeof elem.textContent === 'string' ) {\n\t\t\t\treturn elem.textContent;\n\t\t\t} else if ( typeof elem.innerText === 'string' ) {\n\t\t\t\t// Replace IE's carriage returns\n\t\t\t\treturn elem.innerText.replace( rReturn, '' );\n\t\t\t} else {\n\t\t\t\t// Traverse it's children\n\t\t\t\tfor ( elem = elem.firstChild; elem; elem = elem.nextSibling) {\n\t\t\t\t\tret += getText( elem );\n\t\t\t\t}\n\t\t\t}\n\t\t} else if ( nodeType === 3 || nodeType === 4 ) {\n\t\t\treturn elem.nodeValue;\n\t\t}\n\t} else {\n\n\t\t// If no nodeType, this is expected to be an array\n\t\tfor ( i = 0; (node = elem[i]); i++ ) {\n\t\t\t// Do not traverse comment nodes\n\t\t\tif ( node.nodeType !== 8 ) {\n\t\t\t\tret += getText( node );\n\t\t\t}\n\t\t}\n\t}\n\treturn ret;\n};\n\nvar Expr = Sizzle.selectors = {\n\torder: [ \"ID\", \"NAME\", \"TAG\" ],\n\n\tmatch: {\n\t\tID: /#((?:[\\w\\u00c0-\\uFFFF\\-]|\\\\.)+)/,\n\t\tCLASS: /\\.((?:[\\w\\u00c0-\\uFFFF\\-]|\\\\.)+)/,\n\t\tNAME: /\\[name=['\"]*((?:[\\w\\u00c0-\\uFFFF\\-]|\\\\.)+)['\"]*\\]/,\n\t\tATTR: /\\[\\s*((?:[\\w\\u00c0-\\uFFFF\\-]|\\\\.)+)\\s*(?:(\\S?=)\\s*(?:(['\"])(.*?)\\3|(#?(?:[\\w\\u00c0-\\uFFFF\\-]|\\\\.)*)|)|)\\s*\\]/,\n\t\tTAG: /^((?:[\\w\\u00c0-\\uFFFF\\*\\-]|\\\\.)+)/,\n\t\tCHILD: /:(only|nth|last|first)-child(?:\\(\\s*(even|odd|(?:[+\\-]?\\d+|(?:[+\\-]?\\d*)?n\\s*(?:[+\\-]\\s*\\d+)?))\\s*\\))?/,\n\t\tPOS: /:(nth|eq|gt|lt|first|last|even|odd)(?:\\((\\d*)\\))?(?=[^\\-]|$)/,\n\t\tPSEUDO: /:((?:[\\w\\u00c0-\\uFFFF\\-]|\\\\.)+)(?:\\((['\"]?)((?:\\([^\\)]+\\)|[^\\(\\)]*)+)\\2\\))?/\n\t},\n\n\tleftMatch: {},\n\n\tattrMap: {\n\t\t\"class\": \"className\",\n\t\t\"for\": \"htmlFor\"\n\t},\n\n\tattrHandle: {\n\t\thref: function( elem ) {\n\t\t\treturn elem.getAttribute( \"href\" );\n\t\t},\n\t\ttype: function( elem ) {\n\t\t\treturn elem.getAttribute( \"type\" );\n\t\t}\n\t},\n\n\trelative: {\n\t\t\"+\": function(checkSet, part){\n\t\t\tvar isPartStr = typeof part === \"string\",\n\t\t\t\tisTag = isPartStr && !rNonWord.test( part ),\n\t\t\t\tisPartStrNotTag = isPartStr && !isTag;\n\n\t\t\tif ( isTag ) {\n\t\t\t\tpart = part.toLowerCase();\n\t\t\t}\n\n\t\t\tfor ( var i = 0, l = checkSet.length, elem; i < l; i++ ) {\n\t\t\t\tif ( (elem = checkSet[i]) ) {\n\t\t\t\t\twhile ( (elem = elem.previousSibling) && elem.nodeType !== 1 ) {}\n\n\t\t\t\t\tcheckSet[i] = isPartStrNotTag || elem && elem.nodeName.toLowerCase() === part ?\n\t\t\t\t\t\telem || false :\n\t\t\t\t\t\telem === part;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif ( isPartStrNotTag ) {\n\t\t\t\tSizzle.filter( part, checkSet, true );\n\t\t\t}\n\t\t},\n\n\t\t\">\": function( checkSet, part ) {\n\t\t\tvar elem,\n\t\t\t\tisPartStr = typeof part === \"string\",\n\t\t\t\ti = 0,\n\t\t\t\tl = checkSet.length;\n\n\t\t\tif ( isPartStr && !rNonWord.test( part ) ) {\n\t\t\t\tpart = part.toLowerCase();\n\n\t\t\t\tfor ( ; i < l; i++ ) {\n\t\t\t\t\telem = checkSet[i];\n\n\t\t\t\t\tif ( elem ) {\n\t\t\t\t\t\tvar parent = elem.parentNode;\n\t\t\t\t\t\tcheckSet[i] = parent.nodeName.toLowerCase() === part ? parent : false;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t} else {\n\t\t\t\tfor ( ; i < l; i++ ) {\n\t\t\t\t\telem = checkSet[i];\n\n\t\t\t\t\tif ( elem ) {\n\t\t\t\t\t\tcheckSet[i] = isPartStr ?\n\t\t\t\t\t\t\telem.parentNode :\n\t\t\t\t\t\t\telem.parentNode === part;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif ( isPartStr ) {\n\t\t\t\t\tSizzle.filter( part, checkSet, true );\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\n\t\t\"\": function(checkSet, part, isXML){\n\t\t\tvar nodeCheck,\n\t\t\t\tdoneName = done++,\n\t\t\t\tcheckFn = dirCheck;\n\n\t\t\tif ( typeof part === \"string\" && !rNonWord.test( part ) ) {\n\t\t\t\tpart = part.toLowerCase();\n\t\t\t\tnodeCheck = part;\n\t\t\t\tcheckFn = dirNodeCheck;\n\t\t\t}\n\n\t\t\tcheckFn( \"parentNode\", part, doneName, checkSet, nodeCheck, isXML );\n\t\t},\n\n\t\t\"~\": function( checkSet, part, isXML ) {\n\t\t\tvar nodeCheck,\n\t\t\t\tdoneName = done++,\n\t\t\t\tcheckFn = dirCheck;\n\n\t\t\tif ( typeof part === \"string\" && !rNonWord.test( part ) ) {\n\t\t\t\tpart = part.toLowerCase();\n\t\t\t\tnodeCheck = part;\n\t\t\t\tcheckFn = dirNodeCheck;\n\t\t\t}\n\n\t\t\tcheckFn( \"previousSibling\", part, doneName, checkSet, nodeCheck, isXML );\n\t\t}\n\t},\n\n\tfind: {\n\t\tID: function( match, context, isXML ) {\n\t\t\tif ( typeof context.getElementById !== \"undefined\" && !isXML ) {\n\t\t\t\tvar m = context.getElementById(match[1]);\n\t\t\t\t// Check parentNode to catch when Blackberry 4.6 returns\n\t\t\t\t// nodes that are no longer in the document #6963\n\t\t\t\treturn m && m.parentNode ? [m] : [];\n\t\t\t}\n\t\t},\n\n\t\tNAME: function( match, context ) {\n\t\t\tif ( typeof context.getElementsByName !== \"undefined\" ) {\n\t\t\t\tvar ret = [],\n\t\t\t\t\tresults = context.getElementsByName( match[1] );\n\n\t\t\t\tfor ( var i = 0, l = results.length; i < l; i++ ) {\n\t\t\t\t\tif ( results[i].getAttribute(\"name\") === match[1] ) {\n\t\t\t\t\t\tret.push( results[i] );\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\treturn ret.length === 0 ? null : ret;\n\t\t\t}\n\t\t},\n\n\t\tTAG: function( match, context ) {\n\t\t\tif ( typeof context.getElementsByTagName !== \"undefined\" ) {\n\t\t\t\treturn context.getElementsByTagName( match[1] );\n\t\t\t}\n\t\t}\n\t},\n\tpreFilter: {\n\t\tCLASS: function( match, curLoop, inplace, result, not, isXML ) {\n\t\t\tmatch = \" \" + match[1].replace( rBackslash, \"\" ) + \" \";\n\n\t\t\tif ( isXML ) {\n\t\t\t\treturn match;\n\t\t\t}\n\n\t\t\tfor ( var i = 0, elem; (elem = curLoop[i]) != null; i++ ) {\n\t\t\t\tif ( elem ) {\n\t\t\t\t\tif ( not ^ (elem.className && (\" \" + elem.className + \" \").replace(/[\\t\\n\\r]/g, \" \").indexOf(match) >= 0) ) {\n\t\t\t\t\t\tif ( !inplace ) {\n\t\t\t\t\t\t\tresult.push( elem );\n\t\t\t\t\t\t}\n\n\t\t\t\t\t} else if ( inplace ) {\n\t\t\t\t\t\tcurLoop[i] = false;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn false;\n\t\t},\n\n\t\tID: function( match ) {\n\t\t\treturn match[1].replace( rBackslash, \"\" );\n\t\t},\n\n\t\tTAG: function( match, curLoop ) {\n\t\t\treturn match[1].replace( rBackslash, \"\" ).toLowerCase();\n\t\t},\n\n\t\tCHILD: function( match ) {\n\t\t\tif ( match[1] === \"nth\" ) {\n\t\t\t\tif ( !match[2] ) {\n\t\t\t\t\tSizzle.error( match[0] );\n\t\t\t\t}\n\n\t\t\t\tmatch[2] = match[2].replace(/^\\+|\\s*/g, '');\n\n\t\t\t\t// parse equations like 'even', 'odd', '5', '2n', '3n+2', '4n-1', '-n+6'\n\t\t\t\tvar test = /(-?)(\\d*)(?:n([+\\-]?\\d*))?/.exec(\n\t\t\t\t\tmatch[2] === \"even\" && \"2n\" || match[2] === \"odd\" && \"2n+1\" ||\n\t\t\t\t\t!/\\D/.test( match[2] ) && \"0n+\" + match[2] || match[2]);\n\n\t\t\t\t// calculate the numbers (first)n+(last) including if they are negative\n\t\t\t\tmatch[2] = (test[1] + (test[2] || 1)) - 0;\n\t\t\t\tmatch[3] = test[3] - 0;\n\t\t\t}\n\t\t\telse if ( match[2] ) {\n\t\t\t\tSizzle.error( match[0] );\n\t\t\t}\n\n\t\t\t// TODO: Move to normal caching system\n\t\t\tmatch[0] = done++;\n\n\t\t\treturn match;\n\t\t},\n\n\t\tATTR: function( match, curLoop, inplace, result, not, isXML ) {\n\t\t\tvar name = match[1] = match[1].replace( rBackslash, \"\" );\n\t\t\t\n\t\t\tif ( !isXML && Expr.attrMap[name] ) {\n\t\t\t\tmatch[1] = Expr.attrMap[name];\n\t\t\t}\n\n\t\t\t// Handle if an un-quoted value was used\n\t\t\tmatch[4] = ( match[4] || match[5] || \"\" ).replace( rBackslash, \"\" );\n\n\t\t\tif ( match[2] === \"~=\" ) {\n\t\t\t\tmatch[4] = \" \" + match[4] + \" \";\n\t\t\t}\n\n\t\t\treturn match;\n\t\t},\n\n\t\tPSEUDO: function( match, curLoop, inplace, result, not ) {\n\t\t\tif ( match[1] === \"not\" ) {\n\t\t\t\t// If we're dealing with a complex expression, or a simple one\n\t\t\t\tif ( ( chunker.exec(match[3]) || \"\" ).length > 1 || /^\\w/.test(match[3]) ) {\n\t\t\t\t\tmatch[3] = Sizzle(match[3], null, null, curLoop);\n\n\t\t\t\t} else {\n\t\t\t\t\tvar ret = Sizzle.filter(match[3], curLoop, inplace, true ^ not);\n\n\t\t\t\t\tif ( !inplace ) {\n\t\t\t\t\t\tresult.push.apply( result, ret );\n\t\t\t\t\t}\n\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\n\t\t\t} else if ( Expr.match.POS.test( match[0] ) || Expr.match.CHILD.test( match[0] ) ) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\t\n\t\t\treturn match;\n\t\t},\n\n\t\tPOS: function( match ) {\n\t\t\tmatch.unshift( true );\n\n\t\t\treturn match;\n\t\t}\n\t},\n\t\n\tfilters: {\n\t\tenabled: function( elem ) {\n\t\t\treturn elem.disabled === false && elem.type !== \"hidden\";\n\t\t},\n\n\t\tdisabled: function( elem ) {\n\t\t\treturn elem.disabled === true;\n\t\t},\n\n\t\tchecked: function( elem ) {\n\t\t\treturn elem.checked === true;\n\t\t},\n\t\t\n\t\tselected: function( elem ) {\n\t\t\t// Accessing this property makes selected-by-default\n\t\t\t// options in Safari work properly\n\t\t\tif ( elem.parentNode ) {\n\t\t\t\telem.parentNode.selectedIndex;\n\t\t\t}\n\t\t\t\n\t\t\treturn elem.selected === true;\n\t\t},\n\n\t\tparent: function( elem ) {\n\t\t\treturn !!elem.firstChild;\n\t\t},\n\n\t\tempty: function( elem ) {\n\t\t\treturn !elem.firstChild;\n\t\t},\n\n\t\thas: function( elem, i, match ) {\n\t\t\treturn !!Sizzle( match[3], elem ).length;\n\t\t},\n\n\t\theader: function( elem ) {\n\t\t\treturn (/h\\d/i).test( elem.nodeName );\n\t\t},\n\n\t\ttext: function( elem ) {\n\t\t\tvar attr = elem.getAttribute( \"type\" ), type = elem.type;\n\t\t\t// IE6 and 7 will map elem.type to 'text' for new HTML5 types (search, etc) \n\t\t\t// use getAttribute instead to test this case\n\t\t\treturn elem.nodeName.toLowerCase() === \"input\" && \"text\" === type && ( attr === type || attr === null );\n\t\t},\n\n\t\tradio: function( elem ) {\n\t\t\treturn elem.nodeName.toLowerCase() === \"input\" && \"radio\" === elem.type;\n\t\t},\n\n\t\tcheckbox: function( elem ) {\n\t\t\treturn elem.nodeName.toLowerCase() === \"input\" && \"checkbox\" === elem.type;\n\t\t},\n\n\t\tfile: function( elem ) {\n\t\t\treturn elem.nodeName.toLowerCase() === \"input\" && \"file\" === elem.type;\n\t\t},\n\n\t\tpassword: function( elem ) {\n\t\t\treturn elem.nodeName.toLowerCase() === \"input\" && \"password\" === elem.type;\n\t\t},\n\n\t\tsubmit: function( elem ) {\n\t\t\tvar name = elem.nodeName.toLowerCase();\n\t\t\treturn (name === \"input\" || name === \"button\") && \"submit\" === elem.type;\n\t\t},\n\n\t\timage: function( elem ) {\n\t\t\treturn elem.nodeName.toLowerCase() === \"input\" && \"image\" === elem.type;\n\t\t},\n\n\t\treset: function( elem ) {\n\t\t\tvar name = elem.nodeName.toLowerCase();\n\t\t\treturn (name === \"input\" || name === \"button\") && \"reset\" === elem.type;\n\t\t},\n\n\t\tbutton: function( elem ) {\n\t\t\tvar name = elem.nodeName.toLowerCase();\n\t\t\treturn name === \"input\" && \"button\" === elem.type || name === \"button\";\n\t\t},\n\n\t\tinput: function( elem ) {\n\t\t\treturn (/input|select|textarea|button/i).test( elem.nodeName );\n\t\t},\n\n\t\tfocus: function( elem ) {\n\t\t\treturn elem === elem.ownerDocument.activeElement;\n\t\t}\n\t},\n\tsetFilters: {\n\t\tfirst: function( elem, i ) {\n\t\t\treturn i === 0;\n\t\t},\n\n\t\tlast: function( elem, i, match, array ) {\n\t\t\treturn i === array.length - 1;\n\t\t},\n\n\t\teven: function( elem, i ) {\n\t\t\treturn i % 2 === 0;\n\t\t},\n\n\t\todd: function( elem, i ) {\n\t\t\treturn i % 2 === 1;\n\t\t},\n\n\t\tlt: function( elem, i, match ) {\n\t\t\treturn i < match[3] - 0;\n\t\t},\n\n\t\tgt: function( elem, i, match ) {\n\t\t\treturn i > match[3] - 0;\n\t\t},\n\n\t\tnth: function( elem, i, match ) {\n\t\t\treturn match[3] - 0 === i;\n\t\t},\n\n\t\teq: function( elem, i, match ) {\n\t\t\treturn match[3] - 0 === i;\n\t\t}\n\t},\n\tfilter: {\n\t\tPSEUDO: function( elem, match, i, array ) {\n\t\t\tvar name = match[1],\n\t\t\t\tfilter = Expr.filters[ name ];\n\n\t\t\tif ( filter ) {\n\t\t\t\treturn filter( elem, i, match, array );\n\n\t\t\t} else if ( name === \"contains\" ) {\n\t\t\t\treturn (elem.textContent || elem.innerText || getText([ elem ]) || \"\").indexOf(match[3]) >= 0;\n\n\t\t\t} else if ( name === \"not\" ) {\n\t\t\t\tvar not = match[3];\n\n\t\t\t\tfor ( var j = 0, l = not.length; j < l; j++ ) {\n\t\t\t\t\tif ( not[j] === elem ) {\n\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\treturn true;\n\n\t\t\t} else {\n\t\t\t\tSizzle.error( name );\n\t\t\t}\n\t\t},\n\n\t\tCHILD: function( elem, match ) {\n\t\t\tvar first, last,\n\t\t\t\tdoneName, parent, cache,\n\t\t\t\tcount, diff,\n\t\t\t\ttype = match[1],\n\t\t\t\tnode = elem;\n\n\t\t\tswitch ( type ) {\n\t\t\t\tcase \"only\":\n\t\t\t\tcase \"first\":\n\t\t\t\t\twhile ( (node = node.previousSibling) )\t {\n\t\t\t\t\t\tif ( node.nodeType === 1 ) { \n\t\t\t\t\t\t\treturn false; \n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tif ( type === \"first\" ) { \n\t\t\t\t\t\treturn true; \n\t\t\t\t\t}\n\n\t\t\t\t\tnode = elem;\n\n\t\t\t\tcase \"last\":\n\t\t\t\t\twhile ( (node = node.nextSibling) )\t {\n\t\t\t\t\t\tif ( node.nodeType === 1 ) { \n\t\t\t\t\t\t\treturn false; \n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\treturn true;\n\n\t\t\t\tcase \"nth\":\n\t\t\t\t\tfirst = match[2];\n\t\t\t\t\tlast = match[3];\n\n\t\t\t\t\tif ( first === 1 && last === 0 ) {\n\t\t\t\t\t\treturn true;\n\t\t\t\t\t}\n\t\t\t\t\t\n\t\t\t\t\tdoneName = match[0];\n\t\t\t\t\tparent = elem.parentNode;\n\t\n\t\t\t\t\tif ( parent && (parent[ expando ] !== doneName || !elem.nodeIndex) ) {\n\t\t\t\t\t\tcount = 0;\n\t\t\t\t\t\t\n\t\t\t\t\t\tfor ( node = parent.firstChild; node; node = node.nextSibling ) {\n\t\t\t\t\t\t\tif ( node.nodeType === 1 ) {\n\t\t\t\t\t\t\t\tnode.nodeIndex = ++count;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} \n\n\t\t\t\t\t\tparent[ expando ] = doneName;\n\t\t\t\t\t}\n\t\t\t\t\t\n\t\t\t\t\tdiff = elem.nodeIndex - last;\n\n\t\t\t\t\tif ( first === 0 ) {\n\t\t\t\t\t\treturn diff === 0;\n\n\t\t\t\t\t} else {\n\t\t\t\t\t\treturn ( diff % first === 0 && diff / first >= 0 );\n\t\t\t\t\t}\n\t\t\t}\n\t\t},\n\n\t\tID: function( elem, match ) {\n\t\t\treturn elem.nodeType === 1 && elem.getAttribute(\"id\") === match;\n\t\t},\n\n\t\tTAG: function( elem, match ) {\n\t\t\treturn (match === \"*\" && elem.nodeType === 1) || !!elem.nodeName && elem.nodeName.toLowerCase() === match;\n\t\t},\n\t\t\n\t\tCLASS: function( elem, match ) {\n\t\t\treturn (\" \" + (elem.className || elem.getAttribute(\"class\")) + \" \")\n\t\t\t\t.indexOf( match ) > -1;\n\t\t},\n\n\t\tATTR: function( elem, match ) {\n\t\t\tvar name = match[1],\n\t\t\t\tresult = Sizzle.attr ?\n\t\t\t\t\tSizzle.attr( elem, name ) :\n\t\t\t\t\tExpr.attrHandle[ name ] ?\n\t\t\t\t\tExpr.attrHandle[ name ]( elem ) :\n\t\t\t\t\telem[ name ] != null ?\n\t\t\t\t\t\telem[ name ] :\n\t\t\t\t\t\telem.getAttribute( name ),\n\t\t\t\tvalue = result + \"\",\n\t\t\t\ttype = match[2],\n\t\t\t\tcheck = match[4];\n\n\t\t\treturn result == null ?\n\t\t\t\ttype === \"!=\" :\n\t\t\t\t!type && Sizzle.attr ?\n\t\t\t\tresult != null :\n\t\t\t\ttype === \"=\" ?\n\t\t\t\tvalue === check :\n\t\t\t\ttype === \"*=\" ?\n\t\t\t\tvalue.indexOf(check) >= 0 :\n\t\t\t\ttype === \"~=\" ?\n\t\t\t\t(\" \" + value + \" \").indexOf(check) >= 0 :\n\t\t\t\t!check ?\n\t\t\t\tvalue && result !== false :\n\t\t\t\ttype === \"!=\" ?\n\t\t\t\tvalue !== check :\n\t\t\t\ttype === \"^=\" ?\n\t\t\t\tvalue.indexOf(check) === 0 :\n\t\t\t\ttype === \"$=\" ?\n\t\t\t\tvalue.substr(value.length - check.length) === check :\n\t\t\t\ttype === \"|=\" ?\n\t\t\t\tvalue === check || value.substr(0, check.length + 1) === check + \"-\" :\n\t\t\t\tfalse;\n\t\t},\n\n\t\tPOS: function( elem, match, i, array ) {\n\t\t\tvar name = match[2],\n\t\t\t\tfilter = Expr.setFilters[ name ];\n\n\t\t\tif ( filter ) {\n\t\t\t\treturn filter( elem, i, match, array );\n\t\t\t}\n\t\t}\n\t}\n};\n\nvar origPOS = Expr.match.POS,\n\tfescape = function(all, num){\n\t\treturn \"\\\\\" + (num - 0 + 1);\n\t};\n\nfor ( var type in Expr.match ) {\n\tExpr.match[ type ] = new RegExp( Expr.match[ type ].source + (/(?![^\\[]*\\])(?![^\\(]*\\))/.source) );\n\tExpr.leftMatch[ type ] = new RegExp( /(^(?:.|\\r|\\n)*?)/.source + Expr.match[ type ].source.replace(/\\\\(\\d+)/g, fescape) );\n}\n\nvar makeArray = function( array, results ) {\n\tarray = Array.prototype.slice.call( array, 0 );\n\n\tif ( results ) {\n\t\tresults.push.apply( results, array );\n\t\treturn results;\n\t}\n\t\n\treturn array;\n};\n\n// Perform a simple check to determine if the browser is capable of\n// converting a NodeList to an array using builtin methods.\n// Also verifies that the returned array holds DOM nodes\n// (which is not the case in the Blackberry browser)\ntry {\n\tArray.prototype.slice.call( document.documentElement.childNodes, 0 )[0].nodeType;\n\n// Provide a fallback method if it does not work\n} catch( e ) {\n\tmakeArray = function( array, results ) {\n\t\tvar i = 0,\n\t\t\tret = results || [];\n\n\t\tif ( toString.call(array) === \"[object Array]\" ) {\n\t\t\tArray.prototype.push.apply( ret, array );\n\n\t\t} else {\n\t\t\tif ( typeof array.length === \"number\" ) {\n\t\t\t\tfor ( var l = array.length; i < l; i++ ) {\n\t\t\t\t\tret.push( array[i] );\n\t\t\t\t}\n\n\t\t\t} else {\n\t\t\t\tfor ( ; array[i]; i++ ) {\n\t\t\t\t\tret.push( array[i] );\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn ret;\n\t};\n}\n\nvar sortOrder, siblingCheck;\n\nif ( document.documentElement.compareDocumentPosition ) {\n\tsortOrder = function( a, b ) {\n\t\tif ( a === b ) {\n\t\t\thasDuplicate = true;\n\t\t\treturn 0;\n\t\t}\n\n\t\tif ( !a.compareDocumentPosition || !b.compareDocumentPosition ) {\n\t\t\treturn a.compareDocumentPosition ? -1 : 1;\n\t\t}\n\n\t\treturn a.compareDocumentPosition(b) & 4 ? -1 : 1;\n\t};\n\n} else {\n\tsortOrder = function( a, b ) {\n\t\t// The nodes are identical, we can exit early\n\t\tif ( a === b ) {\n\t\t\thasDuplicate = true;\n\t\t\treturn 0;\n\n\t\t// Fallback to using sourceIndex (in IE) if it's available on both nodes\n\t\t} else if ( a.sourceIndex && b.sourceIndex ) {\n\t\t\treturn a.sourceIndex - b.sourceIndex;\n\t\t}\n\n\t\tvar al, bl,\n\t\t\tap = [],\n\t\t\tbp = [],\n\t\t\taup = a.parentNode,\n\t\t\tbup = b.parentNode,\n\t\t\tcur = aup;\n\n\t\t// If the nodes are siblings (or identical) we can do a quick check\n\t\tif ( aup === bup ) {\n\t\t\treturn siblingCheck( a, b );\n\n\t\t// If no parents were found then the nodes are disconnected\n\t\t} else if ( !aup ) {\n\t\t\treturn -1;\n\n\t\t} else if ( !bup ) {\n\t\t\treturn 1;\n\t\t}\n\n\t\t// Otherwise they're somewhere else in the tree so we need\n\t\t// to build up a full list of the parentNodes for comparison\n\t\twhile ( cur ) {\n\t\t\tap.unshift( cur );\n\t\t\tcur = cur.parentNode;\n\t\t}\n\n\t\tcur = bup;\n\n\t\twhile ( cur ) {\n\t\t\tbp.unshift( cur );\n\t\t\tcur = cur.parentNode;\n\t\t}\n\n\t\tal = ap.length;\n\t\tbl = bp.length;\n\n\t\t// Start walking down the tree looking for a discrepancy\n\t\tfor ( var i = 0; i < al && i < bl; i++ ) {\n\t\t\tif ( ap[i] !== bp[i] ) {\n\t\t\t\treturn siblingCheck( ap[i], bp[i] );\n\t\t\t}\n\t\t}\n\n\t\t// We ended someplace up the tree so do a sibling check\n\t\treturn i === al ?\n\t\t\tsiblingCheck( a, bp[i], -1 ) :\n\t\t\tsiblingCheck( ap[i], b, 1 );\n\t};\n\n\tsiblingCheck = function( a, b, ret ) {\n\t\tif ( a === b ) {\n\t\t\treturn ret;\n\t\t}\n\n\t\tvar cur = a.nextSibling;\n\n\t\twhile ( cur ) {\n\t\t\tif ( cur === b ) {\n\t\t\t\treturn -1;\n\t\t\t}\n\n\t\t\tcur = cur.nextSibling;\n\t\t}\n\n\t\treturn 1;\n\t};\n}\n\n// Check to see if the browser returns elements by name when\n// querying by getElementById (and provide a workaround)\n(function(){\n\t// We're going to inject a fake input element with a specified name\n\tvar form = document.createElement(\"div\"),\n\t\tid = \"script\" + (new Date()).getTime(),\n\t\troot = document.documentElement;\n\n\tform.innerHTML = \"<a name='\" + id + \"'/>\";\n\n\t// Inject it into the root element, check its status, and remove it quickly\n\troot.insertBefore( form, root.firstChild );\n\n\t// The workaround has to do additional checks after a getElementById\n\t// Which slows things down for other browsers (hence the branching)\n\tif ( document.getElementById( id ) ) {\n\t\tExpr.find.ID = function( match, context, isXML ) {\n\t\t\tif ( typeof context.getElementById !== \"undefined\" && !isXML ) {\n\t\t\t\tvar m = context.getElementById(match[1]);\n\n\t\t\t\treturn m ?\n\t\t\t\t\tm.id === match[1] || typeof m.getAttributeNode !== \"undefined\" && m.getAttributeNode(\"id\").nodeValue === match[1] ?\n\t\t\t\t\t\t[m] :\n\t\t\t\t\t\tundefined :\n\t\t\t\t\t[];\n\t\t\t}\n\t\t};\n\n\t\tExpr.filter.ID = function( elem, match ) {\n\t\t\tvar node = typeof elem.getAttributeNode !== \"undefined\" && elem.getAttributeNode(\"id\");\n\n\t\t\treturn elem.nodeType === 1 && node && node.nodeValue === match;\n\t\t};\n\t}\n\n\troot.removeChild( form );\n\n\t// release memory in IE\n\troot = form = null;\n})();\n\n(function(){\n\t// Check to see if the browser returns only elements\n\t// when doing getElementsByTagName(\"*\")\n\n\t// Create a fake element\n\tvar div = document.createElement(\"div\");\n\tdiv.appendChild( document.createComment(\"\") );\n\n\t// Make sure no comments are found\n\tif ( div.getElementsByTagName(\"*\").length > 0 ) {\n\t\tExpr.find.TAG = function( match, context ) {\n\t\t\tvar results = context.getElementsByTagName( match[1] );\n\n\t\t\t// Filter out possible comments\n\t\t\tif ( match[1] === \"*\" ) {\n\t\t\t\tvar tmp = [];\n\n\t\t\t\tfor ( var i = 0; results[i]; i++ ) {\n\t\t\t\t\tif ( results[i].nodeType === 1 ) {\n\t\t\t\t\t\ttmp.push( results[i] );\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tresults = tmp;\n\t\t\t}\n\n\t\t\treturn results;\n\t\t};\n\t}\n\n\t// Check to see if an attribute returns normalized href attributes\n\tdiv.innerHTML = \"<a href='#'></a>\";\n\n\tif ( div.firstChild && typeof div.firstChild.getAttribute !== \"undefined\" &&\n\t\t\tdiv.firstChild.getAttribute(\"href\") !== \"#\" ) {\n\n\t\tExpr.attrHandle.href = function( elem ) {\n\t\t\treturn elem.getAttribute( \"href\", 2 );\n\t\t};\n\t}\n\n\t// release memory in IE\n\tdiv = null;\n})();\n\nif ( document.querySelectorAll ) {\n\t(function(){\n\t\tvar oldSizzle = Sizzle,\n\t\t\tdiv = document.createElement(\"div\"),\n\t\t\tid = \"__sizzle__\";\n\n\t\tdiv.innerHTML = \"<p class='TEST'></p>\";\n\n\t\t// Safari can't handle uppercase or unicode characters when\n\t\t// in quirks mode.\n\t\tif ( div.querySelectorAll && div.querySelectorAll(\".TEST\").length === 0 ) {\n\t\t\treturn;\n\t\t}\n\t\n\t\tSizzle = function( query, context, extra, seed ) {\n\t\t\tcontext = context || document;\n\n\t\t\t// Only use querySelectorAll on non-XML documents\n\t\t\t// (ID selectors don't work in non-HTML documents)\n\t\t\tif ( !seed && !Sizzle.isXML(context) ) {\n\t\t\t\t// See if we find a selector to speed up\n\t\t\t\tvar match = /^(\\w+$)|^\\.([\\w\\-]+$)|^#([\\w\\-]+$)/.exec( query );\n\t\t\t\t\n\t\t\t\tif ( match && (context.nodeType === 1 || context.nodeType === 9) ) {\n\t\t\t\t\t// Speed-up: Sizzle(\"TAG\")\n\t\t\t\t\tif ( match[1] ) {\n\t\t\t\t\t\treturn makeArray( context.getElementsByTagName( query ), extra );\n\t\t\t\t\t\n\t\t\t\t\t// Speed-up: Sizzle(\".CLASS\")\n\t\t\t\t\t} else if ( match[2] && Expr.find.CLASS && context.getElementsByClassName ) {\n\t\t\t\t\t\treturn makeArray( context.getElementsByClassName( match[2] ), extra );\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tif ( context.nodeType === 9 ) {\n\t\t\t\t\t// Speed-up: Sizzle(\"body\")\n\t\t\t\t\t// The body element only exists once, optimize finding it\n\t\t\t\t\tif ( query === \"body\" && context.body ) {\n\t\t\t\t\t\treturn makeArray( [ context.body ], extra );\n\t\t\t\t\t\t\n\t\t\t\t\t// Speed-up: Sizzle(\"#ID\")\n\t\t\t\t\t} else if ( match && match[3] ) {\n\t\t\t\t\t\tvar elem = context.getElementById( match[3] );\n\n\t\t\t\t\t\t// Check parentNode to catch when Blackberry 4.6 returns\n\t\t\t\t\t\t// nodes that are no longer in the document #6963\n\t\t\t\t\t\tif ( elem && elem.parentNode ) {\n\t\t\t\t\t\t\t// Handle the case where IE and Opera return items\n\t\t\t\t\t\t\t// by name instead of ID\n\t\t\t\t\t\t\tif ( elem.id === match[3] ) {\n\t\t\t\t\t\t\t\treturn makeArray( [ elem ], extra );\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\treturn makeArray( [], extra );\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\t\n\t\t\t\t\ttry {\n\t\t\t\t\t\treturn makeArray( context.querySelectorAll(query), extra );\n\t\t\t\t\t} catch(qsaError) {}\n\n\t\t\t\t// qSA works strangely on Element-rooted queries\n\t\t\t\t// We can work around this by specifying an extra ID on the root\n\t\t\t\t// and working up from there (Thanks to Andrew Dupont for the technique)\n\t\t\t\t// IE 8 doesn't work on object elements\n\t\t\t\t} else if ( context.nodeType === 1 && context.nodeName.toLowerCase() !== \"object\" ) {\n\t\t\t\t\tvar oldContext = context,\n\t\t\t\t\t\told = context.getAttribute( \"id\" ),\n\t\t\t\t\t\tnid = old || id,\n\t\t\t\t\t\thasParent = context.parentNode,\n\t\t\t\t\t\trelativeHierarchySelector = /^\\s*[+~]/.test( query );\n\n\t\t\t\t\tif ( !old ) {\n\t\t\t\t\t\tcontext.setAttribute( \"id\", nid );\n\t\t\t\t\t} else {\n\t\t\t\t\t\tnid = nid.replace( /'/g, \"\\\\$&\" );\n\t\t\t\t\t}\n\t\t\t\t\tif ( relativeHierarchySelector && hasParent ) {\n\t\t\t\t\t\tcontext = context.parentNode;\n\t\t\t\t\t}\n\n\t\t\t\t\ttry {\n\t\t\t\t\t\tif ( !relativeHierarchySelector || hasParent ) {\n\t\t\t\t\t\t\treturn makeArray( context.querySelectorAll( \"[id='\" + nid + \"'] \" + query ), extra );\n\t\t\t\t\t\t}\n\n\t\t\t\t\t} catch(pseudoError) {\n\t\t\t\t\t} finally {\n\t\t\t\t\t\tif ( !old ) {\n\t\t\t\t\t\t\toldContext.removeAttribute( \"id\" );\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\n\t\t\treturn oldSizzle(query, context, extra, seed);\n\t\t};\n\n\t\tfor ( var prop in oldSizzle ) {\n\t\t\tSizzle[ prop ] = oldSizzle[ prop ];\n\t\t}\n\n\t\t// release memory in IE\n\t\tdiv = null;\n\t})();\n}\n\n(function(){\n\tvar html = document.documentElement,\n\t\tmatches = html.matchesSelector || html.mozMatchesSelector || html.webkitMatchesSelector || html.msMatchesSelector;\n\n\tif ( matches ) {\n\t\t// Check to see if it's possible to do matchesSelector\n\t\t// on a disconnected node (IE 9 fails this)\n\t\tvar disconnectedMatch = !matches.call( document.createElement( \"div\" ), \"div\" ),\n\t\t\tpseudoWorks = false;\n\n\t\ttry {\n\t\t\t// This should fail with an exception\n\t\t\t// Gecko does not error, returns false instead\n\t\t\tmatches.call( document.documentElement, \"[test!='']:sizzle\" );\n\t\n\t\t} catch( pseudoError ) {\n\t\t\tpseudoWorks = true;\n\t\t}\n\n\t\tSizzle.matchesSelector = function( node, expr ) {\n\t\t\t// Make sure that attribute selectors are quoted\n\t\t\texpr = expr.replace(/\\=\\s*([^'\"\\]]*)\\s*\\]/g, \"='$1']\");\n\n\t\t\tif ( !Sizzle.isXML( node ) ) {\n\t\t\t\ttry { \n\t\t\t\t\tif ( pseudoWorks || !Expr.match.PSEUDO.test( expr ) && !/!=/.test( expr ) ) {\n\t\t\t\t\t\tvar ret = matches.call( node, expr );\n\n\t\t\t\t\t\t// IE 9's matchesSelector returns false on disconnected nodes\n\t\t\t\t\t\tif ( ret || !disconnectedMatch ||\n\t\t\t\t\t\t\t\t// As well, disconnected nodes are said to be in a document\n\t\t\t\t\t\t\t\t// fragment in IE 9, so check for that\n\t\t\t\t\t\t\t\tnode.document && node.document.nodeType !== 11 ) {\n\t\t\t\t\t\t\treturn ret;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t} catch(e) {}\n\t\t\t}\n\n\t\t\treturn Sizzle(expr, null, null, [node]).length > 0;\n\t\t};\n\t}\n})();\n\n(function(){\n\tvar div = document.createElement(\"div\");\n\n\tdiv.innerHTML = \"<div class='test e'></div><div class='test'></div>\";\n\n\t// Opera can't find a second classname (in 9.6)\n\t// Also, make sure that getElementsByClassName actually exists\n\tif ( !div.getElementsByClassName || div.getElementsByClassName(\"e\").length === 0 ) {\n\t\treturn;\n\t}\n\n\t// Safari caches class attributes, doesn't catch changes (in 3.2)\n\tdiv.lastChild.className = \"e\";\n\n\tif ( div.getElementsByClassName(\"e\").length === 1 ) {\n\t\treturn;\n\t}\n\t\n\tExpr.order.splice(1, 0, \"CLASS\");\n\tExpr.find.CLASS = function( match, context, isXML ) {\n\t\tif ( typeof context.getElementsByClassName !== \"undefined\" && !isXML ) {\n\t\t\treturn context.getElementsByClassName(match[1]);\n\t\t}\n\t};\n\n\t// release memory in IE\n\tdiv = null;\n})();\n\nfunction dirNodeCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) {\n\tfor ( var i = 0, l = checkSet.length; i < l; i++ ) {\n\t\tvar elem = checkSet[i];\n\n\t\tif ( elem ) {\n\t\t\tvar match = false;\n\n\t\t\telem = elem[dir];\n\n\t\t\twhile ( elem ) {\n\t\t\t\tif ( elem[ expando ] === doneName ) {\n\t\t\t\t\tmatch = checkSet[elem.sizset];\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tif ( elem.nodeType === 1 && !isXML ){\n\t\t\t\t\telem[ expando ] = doneName;\n\t\t\t\t\telem.sizset = i;\n\t\t\t\t}\n\n\t\t\t\tif ( elem.nodeName.toLowerCase() === cur ) {\n\t\t\t\t\tmatch = elem;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\telem = elem[dir];\n\t\t\t}\n\n\t\t\tcheckSet[i] = match;\n\t\t}\n\t}\n}\n\nfunction dirCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) {\n\tfor ( var i = 0, l = checkSet.length; i < l; i++ ) {\n\t\tvar elem = checkSet[i];\n\n\t\tif ( elem ) {\n\t\t\tvar match = false;\n\t\t\t\n\t\t\telem = elem[dir];\n\n\t\t\twhile ( elem ) {\n\t\t\t\tif ( elem[ expando ] === doneName ) {\n\t\t\t\t\tmatch = checkSet[elem.sizset];\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tif ( elem.nodeType === 1 ) {\n\t\t\t\t\tif ( !isXML ) {\n\t\t\t\t\t\telem[ expando ] = doneName;\n\t\t\t\t\t\telem.sizset = i;\n\t\t\t\t\t}\n\n\t\t\t\t\tif ( typeof cur !== \"string\" ) {\n\t\t\t\t\t\tif ( elem === cur ) {\n\t\t\t\t\t\t\tmatch = true;\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t} else if ( Sizzle.filter( cur, [elem] ).length > 0 ) {\n\t\t\t\t\t\tmatch = elem;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\telem = elem[dir];\n\t\t\t}\n\n\t\t\tcheckSet[i] = match;\n\t\t}\n\t}\n}\n\nif ( document.documentElement.contains ) {\n\tSizzle.contains = function( a, b ) {\n\t\treturn a !== b && (a.contains ? a.contains(b) : true);\n\t};\n\n} else if ( document.documentElement.compareDocumentPosition ) {\n\tSizzle.contains = function( a, b ) {\n\t\treturn !!(a.compareDocumentPosition(b) & 16);\n\t};\n\n} else {\n\tSizzle.contains = function() {\n\t\treturn false;\n\t};\n}\n\nSizzle.isXML = function( elem ) {\n\t// documentElement is verified for cases where it doesn't yet exist\n\t// (such as loading iframes in IE - #4833) \n\tvar documentElement = (elem ? elem.ownerDocument || elem : 0).documentElement;\n\n\treturn documentElement ? documentElement.nodeName !== \"HTML\" : false;\n};\n\nvar posProcess = function( selector, context, seed ) {\n\tvar match,\n\t\ttmpSet = [],\n\t\tlater = \"\",\n\t\troot = context.nodeType ? [context] : context;\n\n\t// Position selectors must be done after the filter\n\t// And so must :not(positional) so we move all PSEUDOs to the end\n\twhile ( (match = Expr.match.PSEUDO.exec( selector )) ) {\n\t\tlater += match[0];\n\t\tselector = selector.replace( Expr.match.PSEUDO, \"\" );\n\t}\n\n\tselector = Expr.relative[selector] ? selector + \"*\" : selector;\n\n\tfor ( var i = 0, l = root.length; i < l; i++ ) {\n\t\tSizzle( selector, root[i], tmpSet, seed );\n\t}\n\n\treturn Sizzle.filter( later, tmpSet );\n};\n\n// EXPOSE\n// Override sizzle attribute retrieval\nSizzle.attr = jQuery.attr;\nSizzle.selectors.attrMap = {};\njQuery.find = Sizzle;\njQuery.expr = Sizzle.selectors;\njQuery.expr[\":\"] = jQuery.expr.filters;\njQuery.unique = Sizzle.uniqueSort;\njQuery.text = Sizzle.getText;\njQuery.isXMLDoc = Sizzle.isXML;\njQuery.contains = Sizzle.contains;\n\n\n})();\n\n\nvar runtil = /Until$/,\n\trparentsprev = /^(?:parents|prevUntil|prevAll)/,\n\t// Note: This RegExp should be improved, or likely pulled from Sizzle\n\trmultiselector = /,/,\n\tisSimple = /^.[^:#\\[\\.,]*$/,\n\tslice = Array.prototype.slice,\n\tPOS = jQuery.expr.match.POS,\n\t// methods guaranteed to produce a unique set when starting from a unique set\n\tguaranteedUnique = {\n\t\tchildren: true,\n\t\tcontents: true,\n\t\tnext: true,\n\t\tprev: true\n\t};\n\njQuery.fn.extend({\n\tfind: function( selector ) {\n\t\tvar self = this,\n\t\t\ti, l;\n\n\t\tif ( typeof selector !== \"string\" ) {\n\t\t\treturn jQuery( selector ).filter(function() {\n\t\t\t\tfor ( i = 0, l = self.length; i < l; i++ ) {\n\t\t\t\t\tif ( jQuery.contains( self[ i ], this ) ) {\n\t\t\t\t\t\treturn true;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t});\n\t\t}\n\n\t\tvar ret = this.pushStack( \"\", \"find\", selector ),\n\t\t\tlength, n, r;\n\n\t\tfor ( i = 0, l = this.length; i < l; i++ ) {\n\t\t\tlength = ret.length;\n\t\t\tjQuery.find( selector, this[i], ret );\n\n\t\t\tif ( i > 0 ) {\n\t\t\t\t// Make sure that the results are unique\n\t\t\t\tfor ( n = length; n < ret.length; n++ ) {\n\t\t\t\t\tfor ( r = 0; r < length; r++ ) {\n\t\t\t\t\t\tif ( ret[r] === ret[n] ) {\n\t\t\t\t\t\t\tret.splice(n--, 1);\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn ret;\n\t},\n\n\thas: function( target ) {\n\t\tvar targets = jQuery( target );\n\t\treturn this.filter(function() {\n\t\t\tfor ( var i = 0, l = targets.length; i < l; i++ ) {\n\t\t\t\tif ( jQuery.contains( this, targets[i] ) ) {\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\t},\n\n\tnot: function( selector ) {\n\t\treturn this.pushStack( winnow(this, selector, false), \"not\", selector);\n\t},\n\n\tfilter: function( selector ) {\n\t\treturn this.pushStack( winnow(this, selector, true), \"filter\", selector );\n\t},\n\n\tis: function( selector ) {\n\t\treturn !!selector && ( \n\t\t\ttypeof selector === \"string\" ?\n\t\t\t\t// If this is a positional selector, check membership in the returned set\n\t\t\t\t// so $(\"p:first\").is(\"p:last\") won't return true for a doc with two \"p\".\n\t\t\t\tPOS.test( selector ) ? \n\t\t\t\t\tjQuery( selector, this.context ).index( this[0] ) >= 0 :\n\t\t\t\t\tjQuery.filter( selector, this ).length > 0 :\n\t\t\t\tthis.filter( selector ).length > 0 );\n\t},\n\n\tclosest: function( selectors, context ) {\n\t\tvar ret = [], i, l, cur = this[0];\n\t\t\n\t\t// Array (deprecated as of jQuery 1.7)\n\t\tif ( jQuery.isArray( selectors ) ) {\n\t\t\tvar level = 1;\n\n\t\t\twhile ( cur && cur.ownerDocument && cur !== context ) {\n\t\t\t\tfor ( i = 0; i < selectors.length; i++ ) {\n\n\t\t\t\t\tif ( jQuery( cur ).is( selectors[ i ] ) ) {\n\t\t\t\t\t\tret.push({ selector: selectors[ i ], elem: cur, level: level });\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tcur = cur.parentNode;\n\t\t\t\tlevel++;\n\t\t\t}\n\n\t\t\treturn ret;\n\t\t}\n\n\t\t// String\n\t\tvar pos = POS.test( selectors ) || typeof selectors !== \"string\" ?\n\t\t\t\tjQuery( selectors, context || this.context ) :\n\t\t\t\t0;\n\n\t\tfor ( i = 0, l = this.length; i < l; i++ ) {\n\t\t\tcur = this[i];\n\n\t\t\twhile ( cur ) {\n\t\t\t\tif ( pos ? pos.index(cur) > -1 : jQuery.find.matchesSelector(cur, selectors) ) {\n\t\t\t\t\tret.push( cur );\n\t\t\t\t\tbreak;\n\n\t\t\t\t} else {\n\t\t\t\t\tcur = cur.parentNode;\n\t\t\t\t\tif ( !cur || !cur.ownerDocument || cur === context || cur.nodeType === 11 ) {\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tret = ret.length > 1 ? jQuery.unique( ret ) : ret;\n\n\t\treturn this.pushStack( ret, \"closest\", selectors );\n\t},\n\n\t// Determine the position of an element within\n\t// the matched set of elements\n\tindex: function( elem ) {\n\n\t\t// No argument, return index in parent\n\t\tif ( !elem ) {\n\t\t\treturn ( this[0] && this[0].parentNode ) ? this.prevAll().length : -1;\n\t\t}\n\n\t\t// index in selector\n\t\tif ( typeof elem === \"string\" ) {\n\t\t\treturn jQuery.inArray( this[0], jQuery( elem ) );\n\t\t}\n\n\t\t// Locate the position of the desired element\n\t\treturn jQuery.inArray(\n\t\t\t// If it receives a jQuery object, the first element is used\n\t\t\telem.jquery ? elem[0] : elem, this );\n\t},\n\n\tadd: function( selector, context ) {\n\t\tvar set = typeof selector === \"string\" ?\n\t\t\t\tjQuery( selector, context ) :\n\t\t\t\tjQuery.makeArray( selector && selector.nodeType ? [ selector ] : selector ),\n\t\t\tall = jQuery.merge( this.get(), set );\n\n\t\treturn this.pushStack( isDisconnected( set[0] ) || isDisconnected( all[0] ) ?\n\t\t\tall :\n\t\t\tjQuery.unique( all ) );\n\t},\n\n\tandSelf: function() {\n\t\treturn this.add( this.prevObject );\n\t}\n});\n\n// A painfully simple check to see if an element is disconnected\n// from a document (should be improved, where feasible).\nfunction isDisconnected( node ) {\n\treturn !node || !node.parentNode || node.parentNode.nodeType === 11;\n}\n\njQuery.each({\n\tparent: function( elem ) {\n\t\tvar parent = elem.parentNode;\n\t\treturn parent && parent.nodeType !== 11 ? parent : null;\n\t},\n\tparents: function( elem ) {\n\t\treturn jQuery.dir( elem, \"parentNode\" );\n\t},\n\tparentsUntil: function( elem, i, until ) {\n\t\treturn jQuery.dir( elem, \"parentNode\", until );\n\t},\n\tnext: function( elem ) {\n\t\treturn jQuery.nth( elem, 2, \"nextSibling\" );\n\t},\n\tprev: function( elem ) {\n\t\treturn jQuery.nth( elem, 2, \"previousSibling\" );\n\t},\n\tnextAll: function( elem ) {\n\t\treturn jQuery.dir( elem, \"nextSibling\" );\n\t},\n\tprevAll: function( elem ) {\n\t\treturn jQuery.dir( elem, \"previousSibling\" );\n\t},\n\tnextUntil: function( elem, i, until ) {\n\t\treturn jQuery.dir( elem, \"nextSibling\", until );\n\t},\n\tprevUntil: function( elem, i, until ) {\n\t\treturn jQuery.dir( elem, \"previousSibling\", until );\n\t},\n\tsiblings: function( elem ) {\n\t\treturn jQuery.sibling( elem.parentNode.firstChild, elem );\n\t},\n\tchildren: function( elem ) {\n\t\treturn jQuery.sibling( elem.firstChild );\n\t},\n\tcontents: function( elem ) {\n\t\treturn jQuery.nodeName( elem, \"iframe\" ) ?\n\t\t\telem.contentDocument || elem.contentWindow.document :\n\t\t\tjQuery.makeArray( elem.childNodes );\n\t}\n}, function( name, fn ) {\n\tjQuery.fn[ name ] = function( until, selector ) {\n\t\tvar ret = jQuery.map( this, fn, until );\n\n\t\tif ( !runtil.test( name ) ) {\n\t\t\tselector = until;\n\t\t}\n\n\t\tif ( selector && typeof selector === \"string\" ) {\n\t\t\tret = jQuery.filter( selector, ret );\n\t\t}\n\n\t\tret = this.length > 1 && !guaranteedUnique[ name ] ? jQuery.unique( ret ) : ret;\n\n\t\tif ( (this.length > 1 || rmultiselector.test( selector )) && rparentsprev.test( name ) ) {\n\t\t\tret = ret.reverse();\n\t\t}\n\n\t\treturn this.pushStack( ret, name, slice.call( arguments ).join(\",\") );\n\t};\n});\n\njQuery.extend({\n\tfilter: function( expr, elems, not ) {\n\t\tif ( not ) {\n\t\t\texpr = \":not(\" + expr + \")\";\n\t\t}\n\n\t\treturn elems.length === 1 ?\n\t\t\tjQuery.find.matchesSelector(elems[0], expr) ? [ elems[0] ] : [] :\n\t\t\tjQuery.find.matches(expr, elems);\n\t},\n\n\tdir: function( elem, dir, until ) {\n\t\tvar matched = [],\n\t\t\tcur = elem[ dir ];\n\n\t\twhile ( cur && cur.nodeType !== 9 && (until === undefined || cur.nodeType !== 1 || !jQuery( cur ).is( until )) ) {\n\t\t\tif ( cur.nodeType === 1 ) {\n\t\t\t\tmatched.push( cur );\n\t\t\t}\n\t\t\tcur = cur[dir];\n\t\t}\n\t\treturn matched;\n\t},\n\n\tnth: function( cur, result, dir, elem ) {\n\t\tresult = result || 1;\n\t\tvar num = 0;\n\n\t\tfor ( ; cur; cur = cur[dir] ) {\n\t\t\tif ( cur.nodeType === 1 && ++num === result ) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\treturn cur;\n\t},\n\n\tsibling: function( n, elem ) {\n\t\tvar r = [];\n\n\t\tfor ( ; n; n = n.nextSibling ) {\n\t\t\tif ( n.nodeType === 1 && n !== elem ) {\n\t\t\t\tr.push( n );\n\t\t\t}\n\t\t}\n\n\t\treturn r;\n\t}\n});\n\n// Implement the identical functionality for filter and not\nfunction winnow( elements, qualifier, keep ) {\n\n\t// Can't pass null or undefined to indexOf in Firefox 4\n\t// Set to 0 to skip string check\n\tqualifier = qualifier || 0;\n\n\tif ( jQuery.isFunction( qualifier ) ) {\n\t\treturn jQuery.grep(elements, function( elem, i ) {\n\t\t\tvar retVal = !!qualifier.call( elem, i, elem );\n\t\t\treturn retVal === keep;\n\t\t});\n\n\t} else if ( qualifier.nodeType ) {\n\t\treturn jQuery.grep(elements, function( elem, i ) {\n\t\t\treturn ( elem === qualifier ) === keep;\n\t\t});\n\n\t} else if ( typeof qualifier === \"string\" ) {\n\t\tvar filtered = jQuery.grep(elements, function( elem ) {\n\t\t\treturn elem.nodeType === 1;\n\t\t});\n\n\t\tif ( isSimple.test( qualifier ) ) {\n\t\t\treturn jQuery.filter(qualifier, filtered, !keep);\n\t\t} else {\n\t\t\tqualifier = jQuery.filter( qualifier, filtered );\n\t\t}\n\t}\n\n\treturn jQuery.grep(elements, function( elem, i ) {\n\t\treturn ( jQuery.inArray( elem, qualifier ) >= 0 ) === keep;\n\t});\n}\n\n\n\n\nfunction createSafeFragment( document ) {\n\tvar list = nodeNames.split( \"|\" ),\n\tsafeFrag = document.createDocumentFragment();\n\n\tif ( safeFrag.createElement ) {\n\t\twhile ( list.length ) {\n\t\t\tsafeFrag.createElement(\n\t\t\t\tlist.pop()\n\t\t\t);\n\t\t}\n\t}\n\treturn safeFrag;\n}\n\nvar nodeNames = \"abbr|article|aside|audio|canvas|datalist|details|figcaption|figure|footer|\" +\n\t\t\"header|hgroup|mark|meter|nav|output|progress|section|summary|time|video\",\n\trinlinejQuery = / jQuery\\d+=\"(?:\\d+|null)\"/g,\n\trleadingWhitespace = /^\\s+/,\n\trxhtmlTag = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\\w:]+)[^>]*)\\/>/ig,\n\trtagName = /<([\\w:]+)/,\n\trtbody = /<tbody/i,\n\trhtml = /<|&#?\\w+;/,\n\trnoInnerhtml = /<(?:script|style)/i,\n\trnocache = /<(?:script|object|embed|option|style)/i,\n\trnoshimcache = new RegExp(\"<(?:\" + nodeNames + \")\", \"i\"),\n\t// checked=\"checked\" or checked\n\trchecked = /checked\\s*(?:[^=]|=\\s*.checked.)/i,\n\trscriptType = /\\/(java|ecma)script/i,\n\trcleanScript = /^\\s*<!(?:\\[CDATA\\[|\\-\\-)/,\n\twrapMap = {\n\t\toption: [ 1, \"<select multiple='multiple'>\", \"</select>\" ],\n\t\tlegend: [ 1, \"<fieldset>\", \"</fieldset>\" ],\n\t\tthead: [ 1, \"<table>\", \"</table>\" ],\n\t\ttr: [ 2, \"<table><tbody>\", \"</tbody></table>\" ],\n\t\ttd: [ 3, \"<table><tbody><tr>\", \"</tr></tbody></table>\" ],\n\t\tcol: [ 2, \"<table><tbody></tbody><colgroup>\", \"</colgroup></table>\" ],\n\t\tarea: [ 1, \"<map>\", \"</map>\" ],\n\t\t_default: [ 0, \"\", \"\" ]\n\t},\n\tsafeFragment = createSafeFragment( document );\n\nwrapMap.optgroup = wrapMap.option;\nwrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead;\nwrapMap.th = wrapMap.td;\n\n// IE can't serialize <link> and <script> tags normally\nif ( !jQuery.support.htmlSerialize ) {\n\twrapMap._default = [ 1, \"div<div>\", \"</div>\" ];\n}\n\njQuery.fn.extend({\n\ttext: function( text ) {\n\t\tif ( jQuery.isFunction(text) ) {\n\t\t\treturn this.each(function(i) {\n\t\t\t\tvar self = jQuery( this );\n\n\t\t\t\tself.text( text.call(this, i, self.text()) );\n\t\t\t});\n\t\t}\n\n\t\tif ( typeof text !== \"object\" && text !== undefined ) {\n\t\t\treturn this.empty().append( (this[0] && this[0].ownerDocument || document).createTextNode( text ) );\n\t\t}\n\n\t\treturn jQuery.text( this );\n\t},\n\n\twrapAll: function( html ) {\n\t\tif ( jQuery.isFunction( html ) ) {\n\t\t\treturn this.each(function(i) {\n\t\t\t\tjQuery(this).wrapAll( html.call(this, i) );\n\t\t\t});\n\t\t}\n\n\t\tif ( this[0] ) {\n\t\t\t// The elements to wrap the target around\n\t\t\tvar wrap = jQuery( html, this[0].ownerDocument ).eq(0).clone(true);\n\n\t\t\tif ( this[0].parentNode ) {\n\t\t\t\twrap.insertBefore( this[0] );\n\t\t\t}\n\n\t\t\twrap.map(function() {\n\t\t\t\tvar elem = this;\n\n\t\t\t\twhile ( elem.firstChild && elem.firstChild.nodeType === 1 ) {\n\t\t\t\t\telem = elem.firstChild;\n\t\t\t\t}\n\n\t\t\t\treturn elem;\n\t\t\t}).append( this );\n\t\t}\n\n\t\treturn this;\n\t},\n\n\twrapInner: function( html ) {\n\t\tif ( jQuery.isFunction( html ) ) {\n\t\t\treturn this.each(function(i) {\n\t\t\t\tjQuery(this).wrapInner( html.call(this, i) );\n\t\t\t});\n\t\t}\n\n\t\treturn this.each(function() {\n\t\t\tvar self = jQuery( this ),\n\t\t\t\tcontents = self.contents();\n\n\t\t\tif ( contents.length ) {\n\t\t\t\tcontents.wrapAll( html );\n\n\t\t\t} else {\n\t\t\t\tself.append( html );\n\t\t\t}\n\t\t});\n\t},\n\n\twrap: function( html ) {\n\t\tvar isFunction = jQuery.isFunction( html );\n\n\t\treturn this.each(function(i) {\n\t\t\tjQuery( this ).wrapAll( isFunction ? html.call(this, i) : html );\n\t\t});\n\t},\n\n\tunwrap: function() {\n\t\treturn this.parent().each(function() {\n\t\t\tif ( !jQuery.nodeName( this, \"body\" ) ) {\n\t\t\t\tjQuery( this ).replaceWith( this.childNodes );\n\t\t\t}\n\t\t}).end();\n\t},\n\n\tappend: function() {\n\t\treturn this.domManip(arguments, true, function( elem ) {\n\t\t\tif ( this.nodeType === 1 ) {\n\t\t\t\tthis.appendChild( elem );\n\t\t\t}\n\t\t});\n\t},\n\n\tprepend: function() {\n\t\treturn this.domManip(arguments, true, function( elem ) {\n\t\t\tif ( this.nodeType === 1 ) {\n\t\t\t\tthis.insertBefore( elem, this.firstChild );\n\t\t\t}\n\t\t});\n\t},\n\n\tbefore: function() {\n\t\tif ( this[0] && this[0].parentNode ) {\n\t\t\treturn this.domManip(arguments, false, function( elem ) {\n\t\t\t\tthis.parentNode.insertBefore( elem, this );\n\t\t\t});\n\t\t} else if ( arguments.length ) {\n\t\t\tvar set = jQuery.clean( arguments );\n\t\t\tset.push.apply( set, this.toArray() );\n\t\t\treturn this.pushStack( set, \"before\", arguments );\n\t\t}\n\t},\n\n\tafter: function() {\n\t\tif ( this[0] && this[0].parentNode ) {\n\t\t\treturn this.domManip(arguments, false, function( elem ) {\n\t\t\t\tthis.parentNode.insertBefore( elem, this.nextSibling );\n\t\t\t});\n\t\t} else if ( arguments.length ) {\n\t\t\tvar set = this.pushStack( this, \"after\", arguments );\n\t\t\tset.push.apply( set, jQuery.clean(arguments) );\n\t\t\treturn set;\n\t\t}\n\t},\n\n\t// keepData is for internal use only--do not document\n\tremove: function( selector, keepData ) {\n\t\tfor ( var i = 0, elem; (elem = this[i]) != null; i++ ) {\n\t\t\tif ( !selector || jQuery.filter( selector, [ elem ] ).length ) {\n\t\t\t\tif ( !keepData && elem.nodeType === 1 ) {\n\t\t\t\t\tjQuery.cleanData( elem.getElementsByTagName(\"*\") );\n\t\t\t\t\tjQuery.cleanData( [ elem ] );\n\t\t\t\t}\n\n\t\t\t\tif ( elem.parentNode ) {\n\t\t\t\t\telem.parentNode.removeChild( elem );\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn this;\n\t},\n\n\tempty: function() {\n\t\tfor ( var i = 0, elem; (elem = this[i]) != null; i++ ) {\n\t\t\t// Remove element nodes and prevent memory leaks\n\t\t\tif ( elem.nodeType === 1 ) {\n\t\t\t\tjQuery.cleanData( elem.getElementsByTagName(\"*\") );\n\t\t\t}\n\n\t\t\t// Remove any remaining nodes\n\t\t\twhile ( elem.firstChild ) {\n\t\t\t\telem.removeChild( elem.firstChild );\n\t\t\t}\n\t\t}\n\n\t\treturn this;\n\t},\n\n\tclone: function( dataAndEvents, deepDataAndEvents ) {\n\t\tdataAndEvents = dataAndEvents == null ? false : dataAndEvents;\n\t\tdeepDataAndEvents = deepDataAndEvents == null ? dataAndEvents : deepDataAndEvents;\n\n\t\treturn this.map( function () {\n\t\t\treturn jQuery.clone( this, dataAndEvents, deepDataAndEvents );\n\t\t});\n\t},\n\n\thtml: function( value ) {\n\t\tif ( value === undefined ) {\n\t\t\treturn this[0] && this[0].nodeType === 1 ?\n\t\t\t\tthis[0].innerHTML.replace(rinlinejQuery, \"\") :\n\t\t\t\tnull;\n\n\t\t// See if we can take a shortcut and just use innerHTML\n\t\t} else if ( typeof value === \"string\" && !rnoInnerhtml.test( value ) &&\n\t\t\t(jQuery.support.leadingWhitespace || !rleadingWhitespace.test( value )) &&\n\t\t\t!wrapMap[ (rtagName.exec( value ) || [\"\", \"\"])[1].toLowerCase() ] ) {\n\n\t\t\tvalue = value.replace(rxhtmlTag, \"<$1></$2>\");\n\n\t\t\ttry {\n\t\t\t\tfor ( var i = 0, l = this.length; i < l; i++ ) {\n\t\t\t\t\t// Remove element nodes and prevent memory leaks\n\t\t\t\t\tif ( this[i].nodeType === 1 ) {\n\t\t\t\t\t\tjQuery.cleanData( this[i].getElementsByTagName(\"*\") );\n\t\t\t\t\t\tthis[i].innerHTML = value;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t// If using innerHTML throws an exception, use the fallback method\n\t\t\t} catch(e) {\n\t\t\t\tthis.empty().append( value );\n\t\t\t}\n\n\t\t} else if ( jQuery.isFunction( value ) ) {\n\t\t\tthis.each(function(i){\n\t\t\t\tvar self = jQuery( this );\n\n\t\t\t\tself.html( value.call(this, i, self.html()) );\n\t\t\t});\n\n\t\t} else {\n\t\t\tthis.empty().append( value );\n\t\t}\n\n\t\treturn this;\n\t},\n\n\treplaceWith: function( value ) {\n\t\tif ( this[0] && this[0].parentNode ) {\n\t\t\t// Make sure that the elements are removed from the DOM before they are inserted\n\t\t\t// this can help fix replacing a parent with child elements\n\t\t\tif ( jQuery.isFunction( value ) ) {\n\t\t\t\treturn this.each(function(i) {\n\t\t\t\t\tvar self = jQuery(this), old = self.html();\n\t\t\t\t\tself.replaceWith( value.call( this, i, old ) );\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tif ( typeof value !== \"string\" ) {\n\t\t\t\tvalue = jQuery( value ).detach();\n\t\t\t}\n\n\t\t\treturn this.each(function() {\n\t\t\t\tvar next = this.nextSibling,\n\t\t\t\t\tparent = this.parentNode;\n\n\t\t\t\tjQuery( this ).remove();\n\n\t\t\t\tif ( next ) {\n\t\t\t\t\tjQuery(next).before( value );\n\t\t\t\t} else {\n\t\t\t\t\tjQuery(parent).append( value );\n\t\t\t\t}\n\t\t\t});\n\t\t} else {\n\t\t\treturn this.length ?\n\t\t\t\tthis.pushStack( jQuery(jQuery.isFunction(value) ? value() : value), \"replaceWith\", value ) :\n\t\t\t\tthis;\n\t\t}\n\t},\n\n\tdetach: function( selector ) {\n\t\treturn this.remove( selector, true );\n\t},\n\n\tdomManip: function( args, table, callback ) {\n\t\tvar results, first, fragment, parent,\n\t\t\tvalue = args[0],\n\t\t\tscripts = [];\n\n\t\t// We can't cloneNode fragments that contain checked, in WebKit\n\t\tif ( !jQuery.support.checkClone && arguments.length === 3 && typeof value === \"string\" && rchecked.test( value ) ) {\n\t\t\treturn this.each(function() {\n\t\t\t\tjQuery(this).domManip( args, table, callback, true );\n\t\t\t});\n\t\t}\n\n\t\tif ( jQuery.isFunction(value) ) {\n\t\t\treturn this.each(function(i) {\n\t\t\t\tvar self = jQuery(this);\n\t\t\t\targs[0] = value.call(this, i, table ? self.html() : undefined);\n\t\t\t\tself.domManip( args, table, callback );\n\t\t\t});\n\t\t}\n\n\t\tif ( this[0] ) {\n\t\t\tparent = value && value.parentNode;\n\n\t\t\t// If we're in a fragment, just use that instead of building a new one\n\t\t\tif ( jQuery.support.parentNode && parent && parent.nodeType === 11 && parent.childNodes.length === this.length ) {\n\t\t\t\tresults = { fragment: parent };\n\n\t\t\t} else {\n\t\t\t\tresults = jQuery.buildFragment( args, this, scripts );\n\t\t\t}\n\n\t\t\tfragment = results.fragment;\n\n\t\t\tif ( fragment.childNodes.length === 1 ) {\n\t\t\t\tfirst = fragment = fragment.firstChild;\n\t\t\t} else {\n\t\t\t\tfirst = fragment.firstChild;\n\t\t\t}\n\n\t\t\tif ( first ) {\n\t\t\t\ttable = table && jQuery.nodeName( first, \"tr\" );\n\n\t\t\t\tfor ( var i = 0, l = this.length, lastIndex = l - 1; i < l; i++ ) {\n\t\t\t\t\tcallback.call(\n\t\t\t\t\t\ttable ?\n\t\t\t\t\t\t\troot(this[i], first) :\n\t\t\t\t\t\t\tthis[i],\n\t\t\t\t\t\t// Make sure that we do not leak memory by inadvertently discarding\n\t\t\t\t\t\t// the original fragment (which might have attached data) instead of\n\t\t\t\t\t\t// using it; in addition, use the original fragment object for the last\n\t\t\t\t\t\t// item instead of first because it can end up being emptied incorrectly\n\t\t\t\t\t\t// in certain situations (Bug #8070).\n\t\t\t\t\t\t// Fragments from the fragment cache must always be cloned and never used\n\t\t\t\t\t\t// in place.\n\t\t\t\t\t\tresults.cacheable || ( l > 1 && i < lastIndex ) ?\n\t\t\t\t\t\t\tjQuery.clone( fragment, true, true ) :\n\t\t\t\t\t\t\tfragment\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif ( scripts.length ) {\n\t\t\t\tjQuery.each( scripts, evalScript );\n\t\t\t}\n\t\t}\n\n\t\treturn this;\n\t}\n});\n\nfunction root( elem, cur ) {\n\treturn jQuery.nodeName(elem, \"table\") ?\n\t\t(elem.getElementsByTagName(\"tbody\")[0] ||\n\t\telem.appendChild(elem.ownerDocument.createElement(\"tbody\"))) :\n\t\telem;\n}\n\nfunction cloneCopyEvent( src, dest ) {\n\n\tif ( dest.nodeType !== 1 || !jQuery.hasData( src ) ) {\n\t\treturn;\n\t}\n\n\tvar type, i, l,\n\t\toldData = jQuery._data( src ),\n\t\tcurData = jQuery._data( dest, oldData ),\n\t\tevents = oldData.events;\n\n\tif ( events ) {\n\t\tdelete curData.handle;\n\t\tcurData.events = {};\n\n\t\tfor ( type in events ) {\n\t\t\tfor ( i = 0, l = events[ type ].length; i < l; i++ ) {\n\t\t\t\tjQuery.event.add( dest, type + ( events[ type ][ i ].namespace ? \".\" : \"\" ) + events[ type ][ i ].namespace, events[ type ][ i ], events[ type ][ i ].data );\n\t\t\t}\n\t\t}\n\t}\n\n\t// make the cloned public data object a copy from the original\n\tif ( curData.data ) {\n\t\tcurData.data = jQuery.extend( {}, curData.data );\n\t}\n}\n\nfunction cloneFixAttributes( src, dest ) {\n\tvar nodeName;\n\n\t// We do not need to do anything for non-Elements\n\tif ( dest.nodeType !== 1 ) {\n\t\treturn;\n\t}\n\n\t// clearAttributes removes the attributes, which we don't want,\n\t// but also removes the attachEvent events, which we *do* want\n\tif ( dest.clearAttributes ) {\n\t\tdest.clearAttributes();\n\t}\n\n\t// mergeAttributes, in contrast, only merges back on the\n\t// original attributes, not the events\n\tif ( dest.mergeAttributes ) {\n\t\tdest.mergeAttributes( src );\n\t}\n\n\tnodeName = dest.nodeName.toLowerCase();\n\n\t// IE6-8 fail to clone children inside object elements that use\n\t// the proprietary classid attribute value (rather than the type\n\t// attribute) to identify the type of content to display\n\tif ( nodeName === \"object\" ) {\n\t\tdest.outerHTML = src.outerHTML;\n\n\t} else if ( nodeName === \"input\" && (src.type === \"checkbox\" || src.type === \"radio\") ) {\n\t\t// IE6-8 fails to persist the checked state of a cloned checkbox\n\t\t// or radio button. Worse, IE6-7 fail to give the cloned element\n\t\t// a checked appearance if the defaultChecked value isn't also set\n\t\tif ( src.checked ) {\n\t\t\tdest.defaultChecked = dest.checked = src.checked;\n\t\t}\n\n\t\t// IE6-7 get confused and end up setting the value of a cloned\n\t\t// checkbox/radio button to an empty string instead of \"on\"\n\t\tif ( dest.value !== src.value ) {\n\t\t\tdest.value = src.value;\n\t\t}\n\n\t// IE6-8 fails to return the selected option to the default selected\n\t// state when cloning options\n\t} else if ( nodeName === \"option\" ) {\n\t\tdest.selected = src.defaultSelected;\n\n\t// IE6-8 fails to set the defaultValue to the correct value when\n\t// cloning other types of input fields\n\t} else if ( nodeName === \"input\" || nodeName === \"textarea\" ) {\n\t\tdest.defaultValue = src.defaultValue;\n\t}\n\n\t// Event data gets referenced instead of copied if the expando\n\t// gets copied too\n\tdest.removeAttribute( jQuery.expando );\n}\n\njQuery.buildFragment = function( args, nodes, scripts ) {\n\tvar fragment, cacheable, cacheresults, doc,\n\tfirst = args[ 0 ];\n\n\t// nodes may contain either an explicit document object,\n\t// a jQuery collection or context object.\n\t// If nodes[0] contains a valid object to assign to doc\n\tif ( nodes && nodes[0] ) {\n\t\tdoc = nodes[0].ownerDocument || nodes[0];\n\t}\n\n\t// Ensure that an attr object doesn't incorrectly stand in as a document object\n\t// Chrome and Firefox seem to allow this to occur and will throw exception\n\t// Fixes #8950\n\tif ( !doc.createDocumentFragment ) {\n\t\tdoc = document;\n\t}\n\n\t// Only cache \"small\" (1/2 KB) HTML strings that are associated with the main document\n\t// Cloning options loses the selected state, so don't cache them\n\t// IE 6 doesn't like it when you put <object> or <embed> elements in a fragment\n\t// Also, WebKit does not clone 'checked' attributes on cloneNode, so don't cache\n\t// Lastly, IE6,7,8 will not correctly reuse cached fragments that were created from unknown elems #10501\n\tif ( args.length === 1 && typeof first === \"string\" && first.length < 512 && doc === document &&\n\t\tfirst.charAt(0) === \"<\" && !rnocache.test( first ) &&\n\t\t(jQuery.support.checkClone || !rchecked.test( first )) &&\n\t\t(jQuery.support.html5Clone || !rnoshimcache.test( first )) ) {\n\n\t\tcacheable = true;\n\n\t\tcacheresults = jQuery.fragments[ first ];\n\t\tif ( cacheresults && cacheresults !== 1 ) {\n\t\t\tfragment = cacheresults;\n\t\t}\n\t}\n\n\tif ( !fragment ) {\n\t\tfragment = doc.createDocumentFragment();\n\t\tjQuery.clean( args, doc, fragment, scripts );\n\t}\n\n\tif ( cacheable ) {\n\t\tjQuery.fragments[ first ] = cacheresults ? fragment : 1;\n\t}\n\n\treturn { fragment: fragment, cacheable: cacheable };\n};\n\njQuery.fragments = {};\n\njQuery.each({\n\tappendTo: \"append\",\n\tprependTo: \"prepend\",\n\tinsertBefore: \"before\",\n\tinsertAfter: \"after\",\n\treplaceAll: \"replaceWith\"\n}, function( name, original ) {\n\tjQuery.fn[ name ] = function( selector ) {\n\t\tvar ret = [],\n\t\t\tinsert = jQuery( selector ),\n\t\t\tparent = this.length === 1 && this[0].parentNode;\n\n\t\tif ( parent && parent.nodeType === 11 && parent.childNodes.length === 1 && insert.length === 1 ) {\n\t\t\tinsert[ original ]( this[0] );\n\t\t\treturn this;\n\n\t\t} else {\n\t\t\tfor ( var i = 0, l = insert.length; i < l; i++ ) {\n\t\t\t\tvar elems = ( i > 0 ? this.clone(true) : this ).get();\n\t\t\t\tjQuery( insert[i] )[ original ]( elems );\n\t\t\t\tret = ret.concat( elems );\n\t\t\t}\n\n\t\t\treturn this.pushStack( ret, name, insert.selector );\n\t\t}\n\t};\n});\n\nfunction getAll( elem ) {\n\tif ( typeof elem.getElementsByTagName !== \"undefined\" ) {\n\t\treturn elem.getElementsByTagName( \"*\" );\n\n\t} else if ( typeof elem.querySelectorAll !== \"undefined\" ) {\n\t\treturn elem.querySelectorAll( \"*\" );\n\n\t} else {\n\t\treturn [];\n\t}\n}\n\n// Used in clean, fixes the defaultChecked property\nfunction fixDefaultChecked( elem ) {\n\tif ( elem.type === \"checkbox\" || elem.type === \"radio\" ) {\n\t\telem.defaultChecked = elem.checked;\n\t}\n}\n// Finds all inputs and passes them to fixDefaultChecked\nfunction findInputs( elem ) {\n\tvar nodeName = ( elem.nodeName || \"\" ).toLowerCase();\n\tif ( nodeName === \"input\" ) {\n\t\tfixDefaultChecked( elem );\n\t// Skip scripts, get other children\n\t} else if ( nodeName !== \"script\" && typeof elem.getElementsByTagName !== \"undefined\" ) {\n\t\tjQuery.grep( elem.getElementsByTagName(\"input\"), fixDefaultChecked );\n\t}\n}\n\n// Derived From: http://www.iecss.com/shimprove/javascript/shimprove.1-0-1.js\nfunction shimCloneNode( elem ) {\n\tvar div = document.createElement( \"div\" );\n\tsafeFragment.appendChild( div );\n\n\tdiv.innerHTML = elem.outerHTML;\n\treturn div.firstChild;\n}\n\njQuery.extend({\n\tclone: function( elem, dataAndEvents, deepDataAndEvents ) {\n\t\tvar srcElements,\n\t\t\tdestElements,\n\t\t\ti,\n\t\t\t// IE<=8 does not properly clone detached, unknown element nodes\n\t\t\tclone = jQuery.support.html5Clone || !rnoshimcache.test( \"<\" + elem.nodeName ) ?\n\t\t\t\telem.cloneNode( true ) :\n\t\t\t\tshimCloneNode( elem );\n\n\t\tif ( (!jQuery.support.noCloneEvent || !jQuery.support.noCloneChecked) &&\n\t\t\t\t(elem.nodeType === 1 || elem.nodeType === 11) && !jQuery.isXMLDoc(elem) ) {\n\t\t\t// IE copies events bound via attachEvent when using cloneNode.\n\t\t\t// Calling detachEvent on the clone will also remove the events\n\t\t\t// from the original. In order to get around this, we use some\n\t\t\t// proprietary methods to clear the events. Thanks to MooTools\n\t\t\t// guys for this hotness.\n\n\t\t\tcloneFixAttributes( elem, clone );\n\n\t\t\t// Using Sizzle here is crazy slow, so we use getElementsByTagName instead\n\t\t\tsrcElements = getAll( elem );\n\t\t\tdestElements = getAll( clone );\n\n\t\t\t// Weird iteration because IE will replace the length property\n\t\t\t// with an element if you are cloning the body and one of the\n\t\t\t// elements on the page has a name or id of \"length\"\n\t\t\tfor ( i = 0; srcElements[i]; ++i ) {\n\t\t\t\t// Ensure that the destination node is not null; Fixes #9587\n\t\t\t\tif ( destElements[i] ) {\n\t\t\t\t\tcloneFixAttributes( srcElements[i], destElements[i] );\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Copy the events from the original to the clone\n\t\tif ( dataAndEvents ) {\n\t\t\tcloneCopyEvent( elem, clone );\n\n\t\t\tif ( deepDataAndEvents ) {\n\t\t\t\tsrcElements = getAll( elem );\n\t\t\t\tdestElements = getAll( clone );\n\n\t\t\t\tfor ( i = 0; srcElements[i]; ++i ) {\n\t\t\t\t\tcloneCopyEvent( srcElements[i], destElements[i] );\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tsrcElements = destElements = null;\n\n\t\t// Return the cloned set\n\t\treturn clone;\n\t},\n\n\tclean: function( elems, context, fragment, scripts ) {\n\t\tvar checkScriptType;\n\n\t\tcontext = context || document;\n\n\t\t// !context.createElement fails in IE with an error but returns typeof 'object'\n\t\tif ( typeof context.createElement === \"undefined\" ) {\n\t\t\tcontext = context.ownerDocument || context[0] && context[0].ownerDocument || document;\n\t\t}\n\n\t\tvar ret = [], j;\n\n\t\tfor ( var i = 0, elem; (elem = elems[i]) != null; i++ ) {\n\t\t\tif ( typeof elem === \"number\" ) {\n\t\t\t\telem += \"\";\n\t\t\t}\n\n\t\t\tif ( !elem ) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t// Convert html string into DOM nodes\n\t\t\tif ( typeof elem === \"string\" ) {\n\t\t\t\tif ( !rhtml.test( elem ) ) {\n\t\t\t\t\telem = context.createTextNode( elem );\n\t\t\t\t} else {\n\t\t\t\t\t// Fix \"XHTML\"-style tags in all browsers\n\t\t\t\t\telem = elem.replace(rxhtmlTag, \"<$1></$2>\");\n\n\t\t\t\t\t// Trim whitespace, otherwise indexOf won't work as expected\n\t\t\t\t\tvar tag = ( rtagName.exec( elem ) || [\"\", \"\"] )[1].toLowerCase(),\n\t\t\t\t\t\twrap = wrapMap[ tag ] || wrapMap._default,\n\t\t\t\t\t\tdepth = wrap[0],\n\t\t\t\t\t\tdiv = context.createElement(\"div\");\n\n\t\t\t\t\t// Append wrapper element to unknown element safe doc fragment\n\t\t\t\t\tif ( context === document ) {\n\t\t\t\t\t\t// Use the fragment we've already created for this document\n\t\t\t\t\t\tsafeFragment.appendChild( div );\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// Use a fragment created with the owner document\n\t\t\t\t\t\tcreateSafeFragment( context ).appendChild( div );\n\t\t\t\t\t}\n\n\t\t\t\t\t// Go to html and back, then peel off extra wrappers\n\t\t\t\t\tdiv.innerHTML = wrap[1] + elem + wrap[2];\n\n\t\t\t\t\t// Move to the right depth\n\t\t\t\t\twhile ( depth-- ) {\n\t\t\t\t\t\tdiv = div.lastChild;\n\t\t\t\t\t}\n\n\t\t\t\t\t// Remove IE's autoinserted <tbody> from table fragments\n\t\t\t\t\tif ( !jQuery.support.tbody ) {\n\n\t\t\t\t\t\t// String was a <table>, *may* have spurious <tbody>\n\t\t\t\t\t\tvar hasBody = rtbody.test(elem),\n\t\t\t\t\t\t\ttbody = tag === \"table\" && !hasBody ?\n\t\t\t\t\t\t\t\tdiv.firstChild && div.firstChild.childNodes :\n\n\t\t\t\t\t\t\t\t// String was a bare <thead> or <tfoot>\n\t\t\t\t\t\t\t\twrap[1] === \"<table>\" && !hasBody ?\n\t\t\t\t\t\t\t\t\tdiv.childNodes :\n\t\t\t\t\t\t\t\t\t[];\n\n\t\t\t\t\t\tfor ( j = tbody.length - 1; j >= 0 ; --j ) {\n\t\t\t\t\t\t\tif ( jQuery.nodeName( tbody[ j ], \"tbody\" ) && !tbody[ j ].childNodes.length ) {\n\t\t\t\t\t\t\t\ttbody[ j ].parentNode.removeChild( tbody[ j ] );\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\t// IE completely kills leading whitespace when innerHTML is used\n\t\t\t\t\tif ( !jQuery.support.leadingWhitespace && rleadingWhitespace.test( elem ) ) {\n\t\t\t\t\t\tdiv.insertBefore( context.createTextNode( rleadingWhitespace.exec(elem)[0] ), div.firstChild );\n\t\t\t\t\t}\n\n\t\t\t\t\telem = div.childNodes;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Resets defaultChecked for any radios and checkboxes\n\t\t\t// about to be appended to the DOM in IE 6/7 (#8060)\n\t\t\tvar len;\n\t\t\tif ( !jQuery.support.appendChecked ) {\n\t\t\t\tif ( elem[0] && typeof (len = elem.length) === \"number\" ) {\n\t\t\t\t\tfor ( j = 0; j < len; j++ ) {\n\t\t\t\t\t\tfindInputs( elem[j] );\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tfindInputs( elem );\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif ( elem.nodeType ) {\n\t\t\t\tret.push( elem );\n\t\t\t} else {\n\t\t\t\tret = jQuery.merge( ret, elem );\n\t\t\t}\n\t\t}\n\n\t\tif ( fragment ) {\n\t\t\tcheckScriptType = function( elem ) {\n\t\t\t\treturn !elem.type || rscriptType.test( elem.type );\n\t\t\t};\n\t\t\tfor ( i = 0; ret[i]; i++ ) {\n\t\t\t\tif ( scripts && jQuery.nodeName( ret[i], \"script\" ) && (!ret[i].type || ret[i].type.toLowerCase() === \"text/javascript\") ) {\n\t\t\t\t\tscripts.push( ret[i].parentNode ? ret[i].parentNode.removeChild( ret[i] ) : ret[i] );\n\n\t\t\t\t} else {\n\t\t\t\t\tif ( ret[i].nodeType === 1 ) {\n\t\t\t\t\t\tvar jsTags = jQuery.grep( ret[i].getElementsByTagName( \"script\" ), checkScriptType );\n\n\t\t\t\t\t\tret.splice.apply( ret, [i + 1, 0].concat( jsTags ) );\n\t\t\t\t\t}\n\t\t\t\t\tfragment.appendChild( ret[i] );\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn ret;\n\t},\n\n\tcleanData: function( elems ) {\n\t\tvar data, id,\n\t\t\tcache = jQuery.cache,\n\t\t\tspecial = jQuery.event.special,\n\t\t\tdeleteExpando = jQuery.support.deleteExpando;\n\n\t\tfor ( var i = 0, elem; (elem = elems[i]) != null; i++ ) {\n\t\t\tif ( elem.nodeName && jQuery.noData[elem.nodeName.toLowerCase()] ) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tid = elem[ jQuery.expando ];\n\n\t\t\tif ( id ) {\n\t\t\t\tdata = cache[ id ];\n\n\t\t\t\tif ( data && data.events ) {\n\t\t\t\t\tfor ( var type in data.events ) {\n\t\t\t\t\t\tif ( special[ type ] ) {\n\t\t\t\t\t\t\tjQuery.event.remove( elem, type );\n\n\t\t\t\t\t\t// This is a shortcut to avoid jQuery.event.remove's overhead\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tjQuery.removeEvent( elem, type, data.handle );\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\t// Null the DOM reference to avoid IE6/7/8 leak (#7054)\n\t\t\t\t\tif ( data.handle ) {\n\t\t\t\t\t\tdata.handle.elem = null;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif ( deleteExpando ) {\n\t\t\t\t\tdelete elem[ jQuery.expando ];\n\n\t\t\t\t} else if ( elem.removeAttribute ) {\n\t\t\t\t\telem.removeAttribute( jQuery.expando );\n\t\t\t\t}\n\n\t\t\t\tdelete cache[ id ];\n\t\t\t}\n\t\t}\n\t}\n});\n\nfunction evalScript( i, elem ) {\n\tif ( elem.src ) {\n\t\tjQuery.ajax({\n\t\t\turl: elem.src,\n\t\t\tasync: false,\n\t\t\tdataType: \"script\"\n\t\t});\n\t} else {\n\t\tjQuery.globalEval( ( elem.text || elem.textContent || elem.innerHTML || \"\" ).replace( rcleanScript, \"/*$0*/\" ) );\n\t}\n\n\tif ( elem.parentNode ) {\n\t\telem.parentNode.removeChild( elem );\n\t}\n}\n\n\n\n\nvar ralpha = /alpha\\([^)]*\\)/i,\n\tropacity = /opacity=([^)]*)/,\n\t// fixed for IE9, see #8346\n\trupper = /([A-Z]|^ms)/g,\n\trnumpx = /^-?\\d+(?:px)?$/i,\n\trnum = /^-?\\d/,\n\trrelNum = /^([\\-+])=([\\-+.\\de]+)/,\n\n\tcssShow = { position: \"absolute\", visibility: \"hidden\", display: \"block\" },\n\tcssWidth = [ \"Left\", \"Right\" ],\n\tcssHeight = [ \"Top\", \"Bottom\" ],\n\tcurCSS,\n\n\tgetComputedStyle,\n\tcurrentStyle;\n\njQuery.fn.css = function( name, value ) {\n\t// Setting 'undefined' is a no-op\n\tif ( arguments.length === 2 && value === undefined ) {\n\t\treturn this;\n\t}\n\n\treturn jQuery.access( this, name, value, true, function( elem, name, value ) {\n\t\treturn value !== undefined ?\n\t\t\tjQuery.style( elem, name, value ) :\n\t\t\tjQuery.css( elem, name );\n\t});\n};\n\njQuery.extend({\n\t// Add in style property hooks for overriding the default\n\t// behavior of getting and setting a style property\n\tcssHooks: {\n\t\topacity: {\n\t\t\tget: function( elem, computed ) {\n\t\t\t\tif ( computed ) {\n\t\t\t\t\t// We should always get a number back from opacity\n\t\t\t\t\tvar ret = curCSS( elem, \"opacity\", \"opacity\" );\n\t\t\t\t\treturn ret === \"\" ? \"1\" : ret;\n\n\t\t\t\t} else {\n\t\t\t\t\treturn elem.style.opacity;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t},\n\n\t// Exclude the following css properties to add px\n\tcssNumber: {\n\t\t\"fillOpacity\": true,\n\t\t\"fontWeight\": true,\n\t\t\"lineHeight\": true,\n\t\t\"opacity\": true,\n\t\t\"orphans\": true,\n\t\t\"widows\": true,\n\t\t\"zIndex\": true,\n\t\t\"zoom\": true\n\t},\n\n\t// Add in properties whose names you wish to fix before\n\t// setting or getting the value\n\tcssProps: {\n\t\t// normalize float css property\n\t\t\"float\": jQuery.support.cssFloat ? \"cssFloat\" : \"styleFloat\"\n\t},\n\n\t// Get and set the style property on a DOM Node\n\tstyle: function( elem, name, value, extra ) {\n\t\t// Don't set styles on text and comment nodes\n\t\tif ( !elem || elem.nodeType === 3 || elem.nodeType === 8 || !elem.style ) {\n\t\t\treturn;\n\t\t}\n\n\t\t// Make sure that we're working with the right name\n\t\tvar ret, type, origName = jQuery.camelCase( name ),\n\t\t\tstyle = elem.style, hooks = jQuery.cssHooks[ origName ];\n\n\t\tname = jQuery.cssProps[ origName ] || origName;\n\n\t\t// Check if we're setting a value\n\t\tif ( value !== undefined ) {\n\t\t\ttype = typeof value;\n\n\t\t\t// convert relative number strings (+= or -=) to relative numbers. #7345\n\t\t\tif ( type === \"string\" && (ret = rrelNum.exec( value )) ) {\n\t\t\t\tvalue = ( +( ret[1] + 1) * +ret[2] ) + parseFloat( jQuery.css( elem, name ) );\n\t\t\t\t// Fixes bug #9237\n\t\t\t\ttype = \"number\";\n\t\t\t}\n\n\t\t\t// Make sure that NaN and null values aren't set. See: #7116\n\t\t\tif ( value == null || type === \"number\" && isNaN( value ) ) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// If a number was passed in, add 'px' to the (except for certain CSS properties)\n\t\t\tif ( type === \"number\" && !jQuery.cssNumber[ origName ] ) {\n\t\t\t\tvalue += \"px\";\n\t\t\t}\n\n\t\t\t// If a hook was provided, use that value, otherwise just set the specified value\n\t\t\tif ( !hooks || !(\"set\" in hooks) || (value = hooks.set( elem, value )) !== undefined ) {\n\t\t\t\t// Wrapped to prevent IE from throwing errors when 'invalid' values are provided\n\t\t\t\t// Fixes bug #5509\n\t\t\t\ttry {\n\t\t\t\t\tstyle[ name ] = value;\n\t\t\t\t} catch(e) {}\n\t\t\t}\n\n\t\t} else {\n\t\t\t// If a hook was provided get the non-computed value from there\n\t\t\tif ( hooks && \"get\" in hooks && (ret = hooks.get( elem, false, extra )) !== undefined ) {\n\t\t\t\treturn ret;\n\t\t\t}\n\n\t\t\t// Otherwise just get the value from the style object\n\t\t\treturn style[ name ];\n\t\t}\n\t},\n\n\tcss: function( elem, name, extra ) {\n\t\tvar ret, hooks;\n\n\t\t// Make sure that we're working with the right name\n\t\tname = jQuery.camelCase( name );\n\t\thooks = jQuery.cssHooks[ name ];\n\t\tname = jQuery.cssProps[ name ] || name;\n\n\t\t// cssFloat needs a special treatment\n\t\tif ( name === \"cssFloat\" ) {\n\t\t\tname = \"float\";\n\t\t}\n\n\t\t// If a hook was provided get the computed value from there\n\t\tif ( hooks && \"get\" in hooks && (ret = hooks.get( elem, true, extra )) !== undefined ) {\n\t\t\treturn ret;\n\n\t\t// Otherwise, if a way to get the computed value exists, use that\n\t\t} else if ( curCSS ) {\n\t\t\treturn curCSS( elem, name );\n\t\t}\n\t},\n\n\t// A method for quickly swapping in/out CSS properties to get correct calculations\n\tswap: function( elem, options, callback ) {\n\t\tvar old = {};\n\n\t\t// Remember the old values, and insert the new ones\n\t\tfor ( var name in options ) {\n\t\t\told[ name ] = elem.style[ name ];\n\t\t\telem.style[ name ] = options[ name ];\n\t\t}\n\n\t\tcallback.call( elem );\n\n\t\t// Revert the old values\n\t\tfor ( name in options ) {\n\t\t\telem.style[ name ] = old[ name ];\n\t\t}\n\t}\n});\n\n// DEPRECATED, Use jQuery.css() instead\njQuery.curCSS = jQuery.css;\n\njQuery.each([\"height\", \"width\"], function( i, name ) {\n\tjQuery.cssHooks[ name ] = {\n\t\tget: function( elem, computed, extra ) {\n\t\t\tvar val;\n\n\t\t\tif ( computed ) {\n\t\t\t\tif ( elem.offsetWidth !== 0 ) {\n\t\t\t\t\treturn getWH( elem, name, extra );\n\t\t\t\t} else {\n\t\t\t\t\tjQuery.swap( elem, cssShow, function() {\n\t\t\t\t\t\tval = getWH( elem, name, extra );\n\t\t\t\t\t});\n\t\t\t\t}\n\n\t\t\t\treturn val;\n\t\t\t}\n\t\t},\n\n\t\tset: function( elem, value ) {\n\t\t\tif ( rnumpx.test( value ) ) {\n\t\t\t\t// ignore negative width and height values #1599\n\t\t\t\tvalue = parseFloat( value );\n\n\t\t\t\tif ( value >= 0 ) {\n\t\t\t\t\treturn value + \"px\";\n\t\t\t\t}\n\n\t\t\t} else {\n\t\t\t\treturn value;\n\t\t\t}\n\t\t}\n\t};\n});\n\nif ( !jQuery.support.opacity ) {\n\tjQuery.cssHooks.opacity = {\n\t\tget: function( elem, computed ) {\n\t\t\t// IE uses filters for opacity\n\t\t\treturn ropacity.test( (computed && elem.currentStyle ? elem.currentStyle.filter : elem.style.filter) || \"\" ) ?\n\t\t\t\t( parseFloat( RegExp.$1 ) / 100 ) + \"\" :\n\t\t\t\tcomputed ? \"1\" : \"\";\n\t\t},\n\n\t\tset: function( elem, value ) {\n\t\t\tvar style = elem.style,\n\t\t\t\tcurrentStyle = elem.currentStyle,\n\t\t\t\topacity = jQuery.isNumeric( value ) ? \"alpha(opacity=\" + value * 100 + \")\" : \"\",\n\t\t\t\tfilter = currentStyle && currentStyle.filter || style.filter || \"\";\n\n\t\t\t// IE has trouble with opacity if it does not have layout\n\t\t\t// Force it by setting the zoom level\n\t\t\tstyle.zoom = 1;\n\n\t\t\t// if setting opacity to 1, and no other filters exist - attempt to remove filter attribute #6652\n\t\t\tif ( value >= 1 && jQuery.trim( filter.replace( ralpha, \"\" ) ) === \"\" ) {\n\n\t\t\t\t// Setting style.filter to null, \"\" & \" \" still leave \"filter:\" in the cssText\n\t\t\t\t// if \"filter:\" is present at all, clearType is disabled, we want to avoid this\n\t\t\t\t// style.removeAttribute is IE Only, but so apparently is this code path...\n\t\t\t\tstyle.removeAttribute( \"filter\" );\n\n\t\t\t\t// if there there is no filter style applied in a css rule, we are done\n\t\t\t\tif ( currentStyle && !currentStyle.filter ) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// otherwise, set new filter values\n\t\t\tstyle.filter = ralpha.test( filter ) ?\n\t\t\t\tfilter.replace( ralpha, opacity ) :\n\t\t\t\tfilter + \" \" + opacity;\n\t\t}\n\t};\n}\n\njQuery(function() {\n\t// This hook cannot be added until DOM ready because the support test\n\t// for it is not run until after DOM ready\n\tif ( !jQuery.support.reliableMarginRight ) {\n\t\tjQuery.cssHooks.marginRight = {\n\t\t\tget: function( elem, computed ) {\n\t\t\t\t// WebKit Bug 13343 - getComputedStyle returns wrong value for margin-right\n\t\t\t\t// Work around by temporarily setting element display to inline-block\n\t\t\t\tvar ret;\n\t\t\t\tjQuery.swap( elem, { \"display\": \"inline-block\" }, function() {\n\t\t\t\t\tif ( computed ) {\n\t\t\t\t\t\tret = curCSS( elem, \"margin-right\", \"marginRight\" );\n\t\t\t\t\t} else {\n\t\t\t\t\t\tret = elem.style.marginRight;\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t\treturn ret;\n\t\t\t}\n\t\t};\n\t}\n});\n\nif ( document.defaultView && document.defaultView.getComputedStyle ) {\n\tgetComputedStyle = function( elem, name ) {\n\t\tvar ret, defaultView, computedStyle;\n\n\t\tname = name.replace( rupper, \"-$1\" ).toLowerCase();\n\n\t\tif ( (defaultView = elem.ownerDocument.defaultView) &&\n\t\t\t\t(computedStyle = defaultView.getComputedStyle( elem, null )) ) {\n\t\t\tret = computedStyle.getPropertyValue( name );\n\t\t\tif ( ret === \"\" && !jQuery.contains( elem.ownerDocument.documentElement, elem ) ) {\n\t\t\t\tret = jQuery.style( elem, name );\n\t\t\t}\n\t\t}\n\n\t\treturn ret;\n\t};\n}\n\nif ( document.documentElement.currentStyle ) {\n\tcurrentStyle = function( elem, name ) {\n\t\tvar left, rsLeft, uncomputed,\n\t\t\tret = elem.currentStyle && elem.currentStyle[ name ],\n\t\t\tstyle = elem.style;\n\n\t\t// Avoid setting ret to empty string here\n\t\t// so we don't default to auto\n\t\tif ( ret === null && style && (uncomputed = style[ name ]) ) {\n\t\t\tret = uncomputed;\n\t\t}\n\n\t\t// From the awesome hack by Dean Edwards\n\t\t// http://erik.eae.net/archives/2007/07/27/18.54.15/#comment-102291\n\n\t\t// If we're not dealing with a regular pixel number\n\t\t// but a number that has a weird ending, we need to convert it to pixels\n\t\tif ( !rnumpx.test( ret ) && rnum.test( ret ) ) {\n\n\t\t\t// Remember the original values\n\t\t\tleft = style.left;\n\t\t\trsLeft = elem.runtimeStyle && elem.runtimeStyle.left;\n\n\t\t\t// Put in the new values to get a computed value out\n\t\t\tif ( rsLeft ) {\n\t\t\t\telem.runtimeStyle.left = elem.currentStyle.left;\n\t\t\t}\n\t\t\tstyle.left = name === \"fontSize\" ? \"1em\" : ( ret || 0 );\n\t\t\tret = style.pixelLeft + \"px\";\n\n\t\t\t// Revert the changed values\n\t\t\tstyle.left = left;\n\t\t\tif ( rsLeft ) {\n\t\t\t\telem.runtimeStyle.left = rsLeft;\n\t\t\t}\n\t\t}\n\n\t\treturn ret === \"\" ? \"auto\" : ret;\n\t};\n}\n\ncurCSS = getComputedStyle || currentStyle;\n\nfunction getWH( elem, name, extra ) {\n\n\t// Start with offset property\n\tvar val = name === \"width\" ? elem.offsetWidth : elem.offsetHeight,\n\t\twhich = name === \"width\" ? cssWidth : cssHeight,\n\t\ti = 0,\n\t\tlen = which.length;\n\n\tif ( val > 0 ) {\n\t\tif ( extra !== \"border\" ) {\n\t\t\tfor ( ; i < len; i++ ) {\n\t\t\t\tif ( !extra ) {\n\t\t\t\t\tval -= parseFloat( jQuery.css( elem, \"padding\" + which[ i ] ) ) || 0;\n\t\t\t\t}\n\t\t\t\tif ( extra === \"margin\" ) {\n\t\t\t\t\tval += parseFloat( jQuery.css( elem, extra + which[ i ] ) ) || 0;\n\t\t\t\t} else {\n\t\t\t\t\tval -= parseFloat( jQuery.css( elem, \"border\" + which[ i ] + \"Width\" ) ) || 0;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn val + \"px\";\n\t}\n\n\t// Fall back to computed then uncomputed css if necessary\n\tval = curCSS( elem, name, name );\n\tif ( val < 0 || val == null ) {\n\t\tval = elem.style[ name ] || 0;\n\t}\n\t// Normalize \"\", auto, and prepare for extra\n\tval = parseFloat( val ) || 0;\n\n\t// Add padding, border, margin\n\tif ( extra ) {\n\t\tfor ( ; i < len; i++ ) {\n\t\t\tval += parseFloat( jQuery.css( elem, \"padding\" + which[ i ] ) ) || 0;\n\t\t\tif ( extra !== \"padding\" ) {\n\t\t\t\tval += parseFloat( jQuery.css( elem, \"border\" + which[ i ] + \"Width\" ) ) || 0;\n\t\t\t}\n\t\t\tif ( extra === \"margin\" ) {\n\t\t\t\tval += parseFloat( jQuery.css( elem, extra + which[ i ] ) ) || 0;\n\t\t\t}\n\t\t}\n\t}\n\n\treturn val + \"px\";\n}\n\nif ( jQuery.expr && jQuery.expr.filters ) {\n\tjQuery.expr.filters.hidden = function( elem ) {\n\t\tvar width = elem.offsetWidth,\n\t\t\theight = elem.offsetHeight;\n\n\t\treturn ( width === 0 && height === 0 ) || (!jQuery.support.reliableHiddenOffsets && ((elem.style && elem.style.display) || jQuery.css( elem, \"display\" )) === \"none\");\n\t};\n\n\tjQuery.expr.filters.visible = function( elem ) {\n\t\treturn !jQuery.expr.filters.hidden( elem );\n\t};\n}\n\n\n\n\nvar r20 = /%20/g,\n\trbracket = /\\[\\]$/,\n\trCRLF = /\\r?\\n/g,\n\trhash = /#.*$/,\n\trheaders = /^(.*?):[ \\t]*([^\\r\\n]*)\\r?$/mg, // IE leaves an \\r character at EOL\n\trinput = /^(?:color|date|datetime|datetime-local|email|hidden|month|number|password|range|search|tel|text|time|url|week)$/i,\n\t// #7653, #8125, #8152: local protocol detection\n\trlocalProtocol = /^(?:about|app|app\\-storage|.+\\-extension|file|res|widget):$/,\n\trnoContent = /^(?:GET|HEAD)$/,\n\trprotocol = /^\\/\\//,\n\trquery = /\\?/,\n\trscript = /<script\\b[^<]*(?:(?!<\\/script>)<[^<]*)*<\\/script>/gi,\n\trselectTextarea = /^(?:select|textarea)/i,\n\trspacesAjax = /\\s+/,\n\trts = /([?&])_=[^&]*/,\n\trurl = /^([\\w\\+\\.\\-]+:)(?:\\/\\/([^\\/?#:]*)(?::(\\d+))?)?/,\n\n\t// Keep a copy of the old load method\n\t_load = jQuery.fn.load,\n\n\t/* Prefilters\n\t * 1) They are useful to introduce custom dataTypes (see ajax/jsonp.js for an example)\n\t * 2) These are called:\n\t *    - BEFORE asking for a transport\n\t *    - AFTER param serialization (s.data is a string if s.processData is true)\n\t * 3) key is the dataType\n\t * 4) the catchall symbol \"*\" can be used\n\t * 5) execution will start with transport dataType and THEN continue down to \"*\" if needed\n\t */\n\tprefilters = {},\n\n\t/* Transports bindings\n\t * 1) key is the dataType\n\t * 2) the catchall symbol \"*\" can be used\n\t * 3) selection will start with transport dataType and THEN go to \"*\" if needed\n\t */\n\ttransports = {},\n\n\t// Document location\n\tajaxLocation,\n\n\t// Document location segments\n\tajaxLocParts,\n\n\t// Avoid comment-prolog char sequence (#10098); must appease lint and evade compression\n\tallTypes = [\"*/\"] + [\"*\"];\n\n// #8138, IE may throw an exception when accessing\n// a field from window.location if document.domain has been set\ntry {\n\tajaxLocation = location.href;\n} catch( e ) {\n\t// Use the href attribute of an A element\n\t// since IE will modify it given document.location\n\tajaxLocation = document.createElement( \"a\" );\n\tajaxLocation.href = \"\";\n\tajaxLocation = ajaxLocation.href;\n}\n\n// Segment location into parts\najaxLocParts = rurl.exec( ajaxLocation.toLowerCase() ) || [];\n\n// Base \"constructor\" for jQuery.ajaxPrefilter and jQuery.ajaxTransport\nfunction addToPrefiltersOrTransports( structure ) {\n\n\t// dataTypeExpression is optional and defaults to \"*\"\n\treturn function( dataTypeExpression, func ) {\n\n\t\tif ( typeof dataTypeExpression !== \"string\" ) {\n\t\t\tfunc = dataTypeExpression;\n\t\t\tdataTypeExpression = \"*\";\n\t\t}\n\n\t\tif ( jQuery.isFunction( func ) ) {\n\t\t\tvar dataTypes = dataTypeExpression.toLowerCase().split( rspacesAjax ),\n\t\t\t\ti = 0,\n\t\t\t\tlength = dataTypes.length,\n\t\t\t\tdataType,\n\t\t\t\tlist,\n\t\t\t\tplaceBefore;\n\n\t\t\t// For each dataType in the dataTypeExpression\n\t\t\tfor ( ; i < length; i++ ) {\n\t\t\t\tdataType = dataTypes[ i ];\n\t\t\t\t// We control if we're asked to add before\n\t\t\t\t// any existing element\n\t\t\t\tplaceBefore = /^\\+/.test( dataType );\n\t\t\t\tif ( placeBefore ) {\n\t\t\t\t\tdataType = dataType.substr( 1 ) || \"*\";\n\t\t\t\t}\n\t\t\t\tlist = structure[ dataType ] = structure[ dataType ] || [];\n\t\t\t\t// then we add to the structure accordingly\n\t\t\t\tlist[ placeBefore ? \"unshift\" : \"push\" ]( func );\n\t\t\t}\n\t\t}\n\t};\n}\n\n// Base inspection function for prefilters and transports\nfunction inspectPrefiltersOrTransports( structure, options, originalOptions, jqXHR,\n\t\tdataType /* internal */, inspected /* internal */ ) {\n\n\tdataType = dataType || options.dataTypes[ 0 ];\n\tinspected = inspected || {};\n\n\tinspected[ dataType ] = true;\n\n\tvar list = structure[ dataType ],\n\t\ti = 0,\n\t\tlength = list ? list.length : 0,\n\t\texecuteOnly = ( structure === prefilters ),\n\t\tselection;\n\n\tfor ( ; i < length && ( executeOnly || !selection ); i++ ) {\n\t\tselection = list[ i ]( options, originalOptions, jqXHR );\n\t\t// If we got redirected to another dataType\n\t\t// we try there if executing only and not done already\n\t\tif ( typeof selection === \"string\" ) {\n\t\t\tif ( !executeOnly || inspected[ selection ] ) {\n\t\t\t\tselection = undefined;\n\t\t\t} else {\n\t\t\t\toptions.dataTypes.unshift( selection );\n\t\t\t\tselection = inspectPrefiltersOrTransports(\n\t\t\t\t\t\tstructure, options, originalOptions, jqXHR, selection, inspected );\n\t\t\t}\n\t\t}\n\t}\n\t// If we're only executing or nothing was selected\n\t// we try the catchall dataType if not done already\n\tif ( ( executeOnly || !selection ) && !inspected[ \"*\" ] ) {\n\t\tselection = inspectPrefiltersOrTransports(\n\t\t\t\tstructure, options, originalOptions, jqXHR, \"*\", inspected );\n\t}\n\t// unnecessary when only executing (prefilters)\n\t// but it'll be ignored by the caller in that case\n\treturn selection;\n}\n\n// A special extend for ajax options\n// that takes \"flat\" options (not to be deep extended)\n// Fixes #9887\nfunction ajaxExtend( target, src ) {\n\tvar key, deep,\n\t\tflatOptions = jQuery.ajaxSettings.flatOptions || {};\n\tfor ( key in src ) {\n\t\tif ( src[ key ] !== undefined ) {\n\t\t\t( flatOptions[ key ] ? target : ( deep || ( deep = {} ) ) )[ key ] = src[ key ];\n\t\t}\n\t}\n\tif ( deep ) {\n\t\tjQuery.extend( true, target, deep );\n\t}\n}\n\njQuery.fn.extend({\n\tload: function( url, params, callback ) {\n\t\tif ( typeof url !== \"string\" && _load ) {\n\t\t\treturn _load.apply( this, arguments );\n\n\t\t// Don't do a request if no elements are being requested\n\t\t} else if ( !this.length ) {\n\t\t\treturn this;\n\t\t}\n\n\t\tvar off = url.indexOf( \" \" );\n\t\tif ( off >= 0 ) {\n\t\t\tvar selector = url.slice( off, url.length );\n\t\t\turl = url.slice( 0, off );\n\t\t}\n\n\t\t// Default to a GET request\n\t\tvar type = \"GET\";\n\n\t\t// If the second parameter was provided\n\t\tif ( params ) {\n\t\t\t// If it's a function\n\t\t\tif ( jQuery.isFunction( params ) ) {\n\t\t\t\t// We assume that it's the callback\n\t\t\t\tcallback = params;\n\t\t\t\tparams = undefined;\n\n\t\t\t// Otherwise, build a param string\n\t\t\t} else if ( typeof params === \"object\" ) {\n\t\t\t\tparams = jQuery.param( params, jQuery.ajaxSettings.traditional );\n\t\t\t\ttype = \"POST\";\n\t\t\t}\n\t\t}\n\n\t\tvar self = this;\n\n\t\t// Request the remote document\n\t\tjQuery.ajax({\n\t\t\turl: url,\n\t\t\ttype: type,\n\t\t\tdataType: \"html\",\n\t\t\tdata: params,\n\t\t\t// Complete callback (responseText is used internally)\n\t\t\tcomplete: function( jqXHR, status, responseText ) {\n\t\t\t\t// Store the response as specified by the jqXHR object\n\t\t\t\tresponseText = jqXHR.responseText;\n\t\t\t\t// If successful, inject the HTML into all the matched elements\n\t\t\t\tif ( jqXHR.isResolved() ) {\n\t\t\t\t\t// #4825: Get the actual response in case\n\t\t\t\t\t// a dataFilter is present in ajaxSettings\n\t\t\t\t\tjqXHR.done(function( r ) {\n\t\t\t\t\t\tresponseText = r;\n\t\t\t\t\t});\n\t\t\t\t\t// See if a selector was specified\n\t\t\t\t\tself.html( selector ?\n\t\t\t\t\t\t// Create a dummy div to hold the results\n\t\t\t\t\t\tjQuery(\"<div>\")\n\t\t\t\t\t\t\t// inject the contents of the document in, removing the scripts\n\t\t\t\t\t\t\t// to avoid any 'Permission Denied' errors in IE\n\t\t\t\t\t\t\t.append(responseText.replace(rscript, \"\"))\n\n\t\t\t\t\t\t\t// Locate the specified elements\n\t\t\t\t\t\t\t.find(selector) :\n\n\t\t\t\t\t\t// If not, just inject the full result\n\t\t\t\t\t\tresponseText );\n\t\t\t\t}\n\n\t\t\t\tif ( callback ) {\n\t\t\t\t\tself.each( callback, [ responseText, status, jqXHR ] );\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\n\t\treturn this;\n\t},\n\n\tserialize: function() {\n\t\treturn jQuery.param( this.serializeArray() );\n\t},\n\n\tserializeArray: function() {\n\t\treturn this.map(function(){\n\t\t\treturn this.elements ? jQuery.makeArray( this.elements ) : this;\n\t\t})\n\t\t.filter(function(){\n\t\t\treturn this.name && !this.disabled &&\n\t\t\t\t( this.checked || rselectTextarea.test( this.nodeName ) ||\n\t\t\t\t\trinput.test( this.type ) );\n\t\t})\n\t\t.map(function( i, elem ){\n\t\t\tvar val = jQuery( this ).val();\n\n\t\t\treturn val == null ?\n\t\t\t\tnull :\n\t\t\t\tjQuery.isArray( val ) ?\n\t\t\t\t\tjQuery.map( val, function( val, i ){\n\t\t\t\t\t\treturn { name: elem.name, value: val.replace( rCRLF, \"\\r\\n\" ) };\n\t\t\t\t\t}) :\n\t\t\t\t\t{ name: elem.name, value: val.replace( rCRLF, \"\\r\\n\" ) };\n\t\t}).get();\n\t}\n});\n\n// Attach a bunch of functions for handling common AJAX events\njQuery.each( \"ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend\".split( \" \" ), function( i, o ){\n\tjQuery.fn[ o ] = function( f ){\n\t\treturn this.on( o, f );\n\t};\n});\n\njQuery.each( [ \"get\", \"post\" ], function( i, method ) {\n\tjQuery[ method ] = function( url, data, callback, type ) {\n\t\t// shift arguments if data argument was omitted\n\t\tif ( jQuery.isFunction( data ) ) {\n\t\t\ttype = type || callback;\n\t\t\tcallback = data;\n\t\t\tdata = undefined;\n\t\t}\n\n\t\treturn jQuery.ajax({\n\t\t\ttype: method,\n\t\t\turl: url,\n\t\t\tdata: data,\n\t\t\tsuccess: callback,\n\t\t\tdataType: type\n\t\t});\n\t};\n});\n\njQuery.extend({\n\n\tgetScript: function( url, callback ) {\n\t\treturn jQuery.get( url, undefined, callback, \"script\" );\n\t},\n\n\tgetJSON: function( url, data, callback ) {\n\t\treturn jQuery.get( url, data, callback, \"json\" );\n\t},\n\n\t// Creates a full fledged settings object into target\n\t// with both ajaxSettings and settings fields.\n\t// If target is omitted, writes into ajaxSettings.\n\tajaxSetup: function( target, settings ) {\n\t\tif ( settings ) {\n\t\t\t// Building a settings object\n\t\t\tajaxExtend( target, jQuery.ajaxSettings );\n\t\t} else {\n\t\t\t// Extending ajaxSettings\n\t\t\tsettings = target;\n\t\t\ttarget = jQuery.ajaxSettings;\n\t\t}\n\t\tajaxExtend( target, settings );\n\t\treturn target;\n\t},\n\n\tajaxSettings: {\n\t\turl: ajaxLocation,\n\t\tisLocal: rlocalProtocol.test( ajaxLocParts[ 1 ] ),\n\t\tglobal: true,\n\t\ttype: \"GET\",\n\t\tcontentType: \"application/x-www-form-urlencoded\",\n\t\tprocessData: true,\n\t\tasync: true,\n\t\t/*\n\t\ttimeout: 0,\n\t\tdata: null,\n\t\tdataType: null,\n\t\tusername: null,\n\t\tpassword: null,\n\t\tcache: null,\n\t\ttraditional: false,\n\t\theaders: {},\n\t\t*/\n\n\t\taccepts: {\n\t\t\txml: \"application/xml, text/xml\",\n\t\t\thtml: \"text/html\",\n\t\t\ttext: \"text/plain\",\n\t\t\tjson: \"application/json, text/javascript\",\n\t\t\t\"*\": allTypes\n\t\t},\n\n\t\tcontents: {\n\t\t\txml: /xml/,\n\t\t\thtml: /html/,\n\t\t\tjson: /json/\n\t\t},\n\n\t\tresponseFields: {\n\t\t\txml: \"responseXML\",\n\t\t\ttext: \"responseText\"\n\t\t},\n\n\t\t// List of data converters\n\t\t// 1) key format is \"source_type destination_type\" (a single space in-between)\n\t\t// 2) the catchall symbol \"*\" can be used for source_type\n\t\tconverters: {\n\n\t\t\t// Convert anything to text\n\t\t\t\"* text\": window.String,\n\n\t\t\t// Text to html (true = no transformation)\n\t\t\t\"text html\": true,\n\n\t\t\t// Evaluate text as a json expression\n\t\t\t\"text json\": jQuery.parseJSON,\n\n\t\t\t// Parse text as xml\n\t\t\t\"text xml\": jQuery.parseXML\n\t\t},\n\n\t\t// For options that shouldn't be deep extended:\n\t\t// you can add your own custom options here if\n\t\t// and when you create one that shouldn't be\n\t\t// deep extended (see ajaxExtend)\n\t\tflatOptions: {\n\t\t\tcontext: true,\n\t\t\turl: true\n\t\t}\n\t},\n\n\tajaxPrefilter: addToPrefiltersOrTransports( prefilters ),\n\tajaxTransport: addToPrefiltersOrTransports( transports ),\n\n\t// Main method\n\tajax: function( url, options ) {\n\n\t\t// If url is an object, simulate pre-1.5 signature\n\t\tif ( typeof url === \"object\" ) {\n\t\t\toptions = url;\n\t\t\turl = undefined;\n\t\t}\n\n\t\t// Force options to be an object\n\t\toptions = options || {};\n\n\t\tvar // Create the final options object\n\t\t\ts = jQuery.ajaxSetup( {}, options ),\n\t\t\t// Callbacks context\n\t\t\tcallbackContext = s.context || s,\n\t\t\t// Context for global events\n\t\t\t// It's the callbackContext if one was provided in the options\n\t\t\t// and if it's a DOM node or a jQuery collection\n\t\t\tglobalEventContext = callbackContext !== s &&\n\t\t\t\t( callbackContext.nodeType || callbackContext instanceof jQuery ) ?\n\t\t\t\t\t\tjQuery( callbackContext ) : jQuery.event,\n\t\t\t// Deferreds\n\t\t\tdeferred = jQuery.Deferred(),\n\t\t\tcompleteDeferred = jQuery.Callbacks( \"once memory\" ),\n\t\t\t// Status-dependent callbacks\n\t\t\tstatusCode = s.statusCode || {},\n\t\t\t// ifModified key\n\t\t\tifModifiedKey,\n\t\t\t// Headers (they are sent all at once)\n\t\t\trequestHeaders = {},\n\t\t\trequestHeadersNames = {},\n\t\t\t// Response headers\n\t\t\tresponseHeadersString,\n\t\t\tresponseHeaders,\n\t\t\t// transport\n\t\t\ttransport,\n\t\t\t// timeout handle\n\t\t\ttimeoutTimer,\n\t\t\t// Cross-domain detection vars\n\t\t\tparts,\n\t\t\t// The jqXHR state\n\t\t\tstate = 0,\n\t\t\t// To know if global events are to be dispatched\n\t\t\tfireGlobals,\n\t\t\t// Loop variable\n\t\t\ti,\n\t\t\t// Fake xhr\n\t\t\tjqXHR = {\n\n\t\t\t\treadyState: 0,\n\n\t\t\t\t// Caches the header\n\t\t\t\tsetRequestHeader: function( name, value ) {\n\t\t\t\t\tif ( !state ) {\n\t\t\t\t\t\tvar lname = name.toLowerCase();\n\t\t\t\t\t\tname = requestHeadersNames[ lname ] = requestHeadersNames[ lname ] || name;\n\t\t\t\t\t\trequestHeaders[ name ] = value;\n\t\t\t\t\t}\n\t\t\t\t\treturn this;\n\t\t\t\t},\n\n\t\t\t\t// Raw string\n\t\t\t\tgetAllResponseHeaders: function() {\n\t\t\t\t\treturn state === 2 ? responseHeadersString : null;\n\t\t\t\t},\n\n\t\t\t\t// Builds headers hashtable if needed\n\t\t\t\tgetResponseHeader: function( key ) {\n\t\t\t\t\tvar match;\n\t\t\t\t\tif ( state === 2 ) {\n\t\t\t\t\t\tif ( !responseHeaders ) {\n\t\t\t\t\t\t\tresponseHeaders = {};\n\t\t\t\t\t\t\twhile( ( match = rheaders.exec( responseHeadersString ) ) ) {\n\t\t\t\t\t\t\t\tresponseHeaders[ match[1].toLowerCase() ] = match[ 2 ];\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\tmatch = responseHeaders[ key.toLowerCase() ];\n\t\t\t\t\t}\n\t\t\t\t\treturn match === undefined ? null : match;\n\t\t\t\t},\n\n\t\t\t\t// Overrides response content-type header\n\t\t\t\toverrideMimeType: function( type ) {\n\t\t\t\t\tif ( !state ) {\n\t\t\t\t\t\ts.mimeType = type;\n\t\t\t\t\t}\n\t\t\t\t\treturn this;\n\t\t\t\t},\n\n\t\t\t\t// Cancel the request\n\t\t\t\tabort: function( statusText ) {\n\t\t\t\t\tstatusText = statusText || \"abort\";\n\t\t\t\t\tif ( transport ) {\n\t\t\t\t\t\ttransport.abort( statusText );\n\t\t\t\t\t}\n\t\t\t\t\tdone( 0, statusText );\n\t\t\t\t\treturn this;\n\t\t\t\t}\n\t\t\t};\n\n\t\t// Callback for when everything is done\n\t\t// It is defined here because jslint complains if it is declared\n\t\t// at the end of the function (which would be more logical and readable)\n\t\tfunction done( status, nativeStatusText, responses, headers ) {\n\n\t\t\t// Called once\n\t\t\tif ( state === 2 ) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// State is \"done\" now\n\t\t\tstate = 2;\n\n\t\t\t// Clear timeout if it exists\n\t\t\tif ( timeoutTimer ) {\n\t\t\t\tclearTimeout( timeoutTimer );\n\t\t\t}\n\n\t\t\t// Dereference transport for early garbage collection\n\t\t\t// (no matter how long the jqXHR object will be used)\n\t\t\ttransport = undefined;\n\n\t\t\t// Cache response headers\n\t\t\tresponseHeadersString = headers || \"\";\n\n\t\t\t// Set readyState\n\t\t\tjqXHR.readyState = status > 0 ? 4 : 0;\n\n\t\t\tvar isSuccess,\n\t\t\t\tsuccess,\n\t\t\t\terror,\n\t\t\t\tstatusText = nativeStatusText,\n\t\t\t\tresponse = responses ? ajaxHandleResponses( s, jqXHR, responses ) : undefined,\n\t\t\t\tlastModified,\n\t\t\t\tetag;\n\n\t\t\t// If successful, handle type chaining\n\t\t\tif ( status >= 200 && status < 300 || status === 304 ) {\n\n\t\t\t\t// Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode.\n\t\t\t\tif ( s.ifModified ) {\n\n\t\t\t\t\tif ( ( lastModified = jqXHR.getResponseHeader( \"Last-Modified\" ) ) ) {\n\t\t\t\t\t\tjQuery.lastModified[ ifModifiedKey ] = lastModified;\n\t\t\t\t\t}\n\t\t\t\t\tif ( ( etag = jqXHR.getResponseHeader( \"Etag\" ) ) ) {\n\t\t\t\t\t\tjQuery.etag[ ifModifiedKey ] = etag;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// If not modified\n\t\t\t\tif ( status === 304 ) {\n\n\t\t\t\t\tstatusText = \"notmodified\";\n\t\t\t\t\tisSuccess = true;\n\n\t\t\t\t// If we have data\n\t\t\t\t} else {\n\n\t\t\t\t\ttry {\n\t\t\t\t\t\tsuccess = ajaxConvert( s, response );\n\t\t\t\t\t\tstatusText = \"success\";\n\t\t\t\t\t\tisSuccess = true;\n\t\t\t\t\t} catch(e) {\n\t\t\t\t\t\t// We have a parsererror\n\t\t\t\t\t\tstatusText = \"parsererror\";\n\t\t\t\t\t\terror = e;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t// We extract error from statusText\n\t\t\t\t// then normalize statusText and status for non-aborts\n\t\t\t\terror = statusText;\n\t\t\t\tif ( !statusText || status ) {\n\t\t\t\t\tstatusText = \"error\";\n\t\t\t\t\tif ( status < 0 ) {\n\t\t\t\t\t\tstatus = 0;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Set data for the fake xhr object\n\t\t\tjqXHR.status = status;\n\t\t\tjqXHR.statusText = \"\" + ( nativeStatusText || statusText );\n\n\t\t\t// Success/Error\n\t\t\tif ( isSuccess ) {\n\t\t\t\tdeferred.resolveWith( callbackContext, [ success, statusText, jqXHR ] );\n\t\t\t} else {\n\t\t\t\tdeferred.rejectWith( callbackContext, [ jqXHR, statusText, error ] );\n\t\t\t}\n\n\t\t\t// Status-dependent callbacks\n\t\t\tjqXHR.statusCode( statusCode );\n\t\t\tstatusCode = undefined;\n\n\t\t\tif ( fireGlobals ) {\n\t\t\t\tglobalEventContext.trigger( \"ajax\" + ( isSuccess ? \"Success\" : \"Error\" ),\n\t\t\t\t\t\t[ jqXHR, s, isSuccess ? success : error ] );\n\t\t\t}\n\n\t\t\t// Complete\n\t\t\tcompleteDeferred.fireWith( callbackContext, [ jqXHR, statusText ] );\n\n\t\t\tif ( fireGlobals ) {\n\t\t\t\tglobalEventContext.trigger( \"ajaxComplete\", [ jqXHR, s ] );\n\t\t\t\t// Handle the global AJAX counter\n\t\t\t\tif ( !( --jQuery.active ) ) {\n\t\t\t\t\tjQuery.event.trigger( \"ajaxStop\" );\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Attach deferreds\n\t\tdeferred.promise( jqXHR );\n\t\tjqXHR.success = jqXHR.done;\n\t\tjqXHR.error = jqXHR.fail;\n\t\tjqXHR.complete = completeDeferred.add;\n\n\t\t// Status-dependent callbacks\n\t\tjqXHR.statusCode = function( map ) {\n\t\t\tif ( map ) {\n\t\t\t\tvar tmp;\n\t\t\t\tif ( state < 2 ) {\n\t\t\t\t\tfor ( tmp in map ) {\n\t\t\t\t\t\tstatusCode[ tmp ] = [ statusCode[tmp], map[tmp] ];\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\ttmp = map[ jqXHR.status ];\n\t\t\t\t\tjqXHR.then( tmp, tmp );\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn this;\n\t\t};\n\n\t\t// Remove hash character (#7531: and string promotion)\n\t\t// Add protocol if not provided (#5866: IE7 issue with protocol-less urls)\n\t\t// We also use the url parameter if available\n\t\ts.url = ( ( url || s.url ) + \"\" ).replace( rhash, \"\" ).replace( rprotocol, ajaxLocParts[ 1 ] + \"//\" );\n\n\t\t// Extract dataTypes list\n\t\ts.dataTypes = jQuery.trim( s.dataType || \"*\" ).toLowerCase().split( rspacesAjax );\n\n\t\t// Determine if a cross-domain request is in order\n\t\tif ( s.crossDomain == null ) {\n\t\t\tparts = rurl.exec( s.url.toLowerCase() );\n\t\t\ts.crossDomain = !!( parts &&\n\t\t\t\t( parts[ 1 ] != ajaxLocParts[ 1 ] || parts[ 2 ] != ajaxLocParts[ 2 ] ||\n\t\t\t\t\t( parts[ 3 ] || ( parts[ 1 ] === \"http:\" ? 80 : 443 ) ) !=\n\t\t\t\t\t\t( ajaxLocParts[ 3 ] || ( ajaxLocParts[ 1 ] === \"http:\" ? 80 : 443 ) ) )\n\t\t\t);\n\t\t}\n\n\t\t// Convert data if not already a string\n\t\tif ( s.data && s.processData && typeof s.data !== \"string\" ) {\n\t\t\ts.data = jQuery.param( s.data, s.traditional );\n\t\t}\n\n\t\t// Apply prefilters\n\t\tinspectPrefiltersOrTransports( prefilters, s, options, jqXHR );\n\n\t\t// If request was aborted inside a prefiler, stop there\n\t\tif ( state === 2 ) {\n\t\t\treturn false;\n\t\t}\n\n\t\t// We can fire global events as of now if asked to\n\t\tfireGlobals = s.global;\n\n\t\t// Uppercase the type\n\t\ts.type = s.type.toUpperCase();\n\n\t\t// Determine if request has content\n\t\ts.hasContent = !rnoContent.test( s.type );\n\n\t\t// Watch for a new set of requests\n\t\tif ( fireGlobals && jQuery.active++ === 0 ) {\n\t\t\tjQuery.event.trigger( \"ajaxStart\" );\n\t\t}\n\n\t\t// More options handling for requests with no content\n\t\tif ( !s.hasContent ) {\n\n\t\t\t// If data is available, append data to url\n\t\t\tif ( s.data ) {\n\t\t\t\ts.url += ( rquery.test( s.url ) ? \"&\" : \"?\" ) + s.data;\n\t\t\t\t// #9682: remove data so that it's not used in an eventual retry\n\t\t\t\tdelete s.data;\n\t\t\t}\n\n\t\t\t// Get ifModifiedKey before adding the anti-cache parameter\n\t\t\tifModifiedKey = s.url;\n\n\t\t\t// Add anti-cache in url if needed\n\t\t\tif ( s.cache === false ) {\n\n\t\t\t\tvar ts = jQuery.now(),\n\t\t\t\t\t// try replacing _= if it is there\n\t\t\t\t\tret = s.url.replace( rts, \"$1_=\" + ts );\n\n\t\t\t\t// if nothing was replaced, add timestamp to the end\n\t\t\t\ts.url = ret + ( ( ret === s.url ) ? ( rquery.test( s.url ) ? \"&\" : \"?\" ) + \"_=\" + ts : \"\" );\n\t\t\t}\n\t\t}\n\n\t\t// Set the correct header, if data is being sent\n\t\tif ( s.data && s.hasContent && s.contentType !== false || options.contentType ) {\n\t\t\tjqXHR.setRequestHeader( \"Content-Type\", s.contentType );\n\t\t}\n\n\t\t// Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode.\n\t\tif ( s.ifModified ) {\n\t\t\tifModifiedKey = ifModifiedKey || s.url;\n\t\t\tif ( jQuery.lastModified[ ifModifiedKey ] ) {\n\t\t\t\tjqXHR.setRequestHeader( \"If-Modified-Since\", jQuery.lastModified[ ifModifiedKey ] );\n\t\t\t}\n\t\t\tif ( jQuery.etag[ ifModifiedKey ] ) {\n\t\t\t\tjqXHR.setRequestHeader( \"If-None-Match\", jQuery.etag[ ifModifiedKey ] );\n\t\t\t}\n\t\t}\n\n\t\t// Set the Accepts header for the server, depending on the dataType\n\t\tjqXHR.setRequestHeader(\n\t\t\t\"Accept\",\n\t\t\ts.dataTypes[ 0 ] && s.accepts[ s.dataTypes[0] ] ?\n\t\t\t\ts.accepts[ s.dataTypes[0] ] + ( s.dataTypes[ 0 ] !== \"*\" ? \", \" + allTypes + \"; q=0.01\" : \"\" ) :\n\t\t\t\ts.accepts[ \"*\" ]\n\t\t);\n\n\t\t// Check for headers option\n\t\tfor ( i in s.headers ) {\n\t\t\tjqXHR.setRequestHeader( i, s.headers[ i ] );\n\t\t}\n\n\t\t// Allow custom headers/mimetypes and early abort\n\t\tif ( s.beforeSend && ( s.beforeSend.call( callbackContext, jqXHR, s ) === false || state === 2 ) ) {\n\t\t\t\t// Abort if not done already\n\t\t\t\tjqXHR.abort();\n\t\t\t\treturn false;\n\n\t\t}\n\n\t\t// Install callbacks on deferreds\n\t\tfor ( i in { success: 1, error: 1, complete: 1 } ) {\n\t\t\tjqXHR[ i ]( s[ i ] );\n\t\t}\n\n\t\t// Get transport\n\t\ttransport = inspectPrefiltersOrTransports( transports, s, options, jqXHR );\n\n\t\t// If no transport, we auto-abort\n\t\tif ( !transport ) {\n\t\t\tdone( -1, \"No Transport\" );\n\t\t} else {\n\t\t\tjqXHR.readyState = 1;\n\t\t\t// Send global event\n\t\t\tif ( fireGlobals ) {\n\t\t\t\tglobalEventContext.trigger( \"ajaxSend\", [ jqXHR, s ] );\n\t\t\t}\n\t\t\t// Timeout\n\t\t\tif ( s.async && s.timeout > 0 ) {\n\t\t\t\ttimeoutTimer = setTimeout( function(){\n\t\t\t\t\tjqXHR.abort( \"timeout\" );\n\t\t\t\t}, s.timeout );\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\tstate = 1;\n\t\t\t\ttransport.send( requestHeaders, done );\n\t\t\t} catch (e) {\n\t\t\t\t// Propagate exception as error if not done\n\t\t\t\tif ( state < 2 ) {\n\t\t\t\t\tdone( -1, e );\n\t\t\t\t// Simply rethrow otherwise\n\t\t\t\t} else {\n\t\t\t\t\tthrow e;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn jqXHR;\n\t},\n\n\t// Serialize an array of form elements or a set of\n\t// key/values into a query string\n\tparam: function( a, traditional ) {\n\t\tvar s = [],\n\t\t\tadd = function( key, value ) {\n\t\t\t\t// If value is a function, invoke it and return its value\n\t\t\t\tvalue = jQuery.isFunction( value ) ? value() : value;\n\t\t\t\ts[ s.length ] = encodeURIComponent( key ) + \"=\" + encodeURIComponent( value );\n\t\t\t};\n\n\t\t// Set traditional to true for jQuery <= 1.3.2 behavior.\n\t\tif ( traditional === undefined ) {\n\t\t\ttraditional = jQuery.ajaxSettings.traditional;\n\t\t}\n\n\t\t// If an array was passed in, assume that it is an array of form elements.\n\t\tif ( jQuery.isArray( a ) || ( a.jquery && !jQuery.isPlainObject( a ) ) ) {\n\t\t\t// Serialize the form elements\n\t\t\tjQuery.each( a, function() {\n\t\t\t\tadd( this.name, this.value );\n\t\t\t});\n\n\t\t} else {\n\t\t\t// If traditional, encode the \"old\" way (the way 1.3.2 or older\n\t\t\t// did it), otherwise encode params recursively.\n\t\t\tfor ( var prefix in a ) {\n\t\t\t\tbuildParams( prefix, a[ prefix ], traditional, add );\n\t\t\t}\n\t\t}\n\n\t\t// Return the resulting serialization\n\t\treturn s.join( \"&\" ).replace( r20, \"+\" );\n\t}\n});\n\nfunction buildParams( prefix, obj, traditional, add ) {\n\tif ( jQuery.isArray( obj ) ) {\n\t\t// Serialize array item.\n\t\tjQuery.each( obj, function( i, v ) {\n\t\t\tif ( traditional || rbracket.test( prefix ) ) {\n\t\t\t\t// Treat each array item as a scalar.\n\t\t\t\tadd( prefix, v );\n\n\t\t\t} else {\n\t\t\t\t// If array item is non-scalar (array or object), encode its\n\t\t\t\t// numeric index to resolve deserialization ambiguity issues.\n\t\t\t\t// Note that rack (as of 1.0.0) can't currently deserialize\n\t\t\t\t// nested arrays properly, and attempting to do so may cause\n\t\t\t\t// a server error. Possible fixes are to modify rack's\n\t\t\t\t// deserialization algorithm or to provide an option or flag\n\t\t\t\t// to force array serialization to be shallow.\n\t\t\t\tbuildParams( prefix + \"[\" + ( typeof v === \"object\" || jQuery.isArray(v) ? i : \"\" ) + \"]\", v, traditional, add );\n\t\t\t}\n\t\t});\n\n\t} else if ( !traditional && obj != null && typeof obj === \"object\" ) {\n\t\t// Serialize object item.\n\t\tfor ( var name in obj ) {\n\t\t\tbuildParams( prefix + \"[\" + name + \"]\", obj[ name ], traditional, add );\n\t\t}\n\n\t} else {\n\t\t// Serialize scalar item.\n\t\tadd( prefix, obj );\n\t}\n}\n\n// This is still on the jQuery object... for now\n// Want to move this to jQuery.ajax some day\njQuery.extend({\n\n\t// Counter for holding the number of active queries\n\tactive: 0,\n\n\t// Last-Modified header cache for next request\n\tlastModified: {},\n\tetag: {}\n\n});\n\n/* Handles responses to an ajax request:\n * - sets all responseXXX fields accordingly\n * - finds the right dataType (mediates between content-type and expected dataType)\n * - returns the corresponding response\n */\nfunction ajaxHandleResponses( s, jqXHR, responses ) {\n\n\tvar contents = s.contents,\n\t\tdataTypes = s.dataTypes,\n\t\tresponseFields = s.responseFields,\n\t\tct,\n\t\ttype,\n\t\tfinalDataType,\n\t\tfirstDataType;\n\n\t// Fill responseXXX fields\n\tfor ( type in responseFields ) {\n\t\tif ( type in responses ) {\n\t\t\tjqXHR[ responseFields[type] ] = responses[ type ];\n\t\t}\n\t}\n\n\t// Remove auto dataType and get content-type in the process\n\twhile( dataTypes[ 0 ] === \"*\" ) {\n\t\tdataTypes.shift();\n\t\tif ( ct === undefined ) {\n\t\t\tct = s.mimeType || jqXHR.getResponseHeader( \"content-type\" );\n\t\t}\n\t}\n\n\t// Check if we're dealing with a known content-type\n\tif ( ct ) {\n\t\tfor ( type in contents ) {\n\t\t\tif ( contents[ type ] && contents[ type ].test( ct ) ) {\n\t\t\t\tdataTypes.unshift( type );\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\n\t// Check to see if we have a response for the expected dataType\n\tif ( dataTypes[ 0 ] in responses ) {\n\t\tfinalDataType = dataTypes[ 0 ];\n\t} else {\n\t\t// Try convertible dataTypes\n\t\tfor ( type in responses ) {\n\t\t\tif ( !dataTypes[ 0 ] || s.converters[ type + \" \" + dataTypes[0] ] ) {\n\t\t\t\tfinalDataType = type;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tif ( !firstDataType ) {\n\t\t\t\tfirstDataType = type;\n\t\t\t}\n\t\t}\n\t\t// Or just use first one\n\t\tfinalDataType = finalDataType || firstDataType;\n\t}\n\n\t// If we found a dataType\n\t// We add the dataType to the list if needed\n\t// and return the corresponding response\n\tif ( finalDataType ) {\n\t\tif ( finalDataType !== dataTypes[ 0 ] ) {\n\t\t\tdataTypes.unshift( finalDataType );\n\t\t}\n\t\treturn responses[ finalDataType ];\n\t}\n}\n\n// Chain conversions given the request and the original response\nfunction ajaxConvert( s, response ) {\n\n\t// Apply the dataFilter if provided\n\tif ( s.dataFilter ) {\n\t\tresponse = s.dataFilter( response, s.dataType );\n\t}\n\n\tvar dataTypes = s.dataTypes,\n\t\tconverters = {},\n\t\ti,\n\t\tkey,\n\t\tlength = dataTypes.length,\n\t\ttmp,\n\t\t// Current and previous dataTypes\n\t\tcurrent = dataTypes[ 0 ],\n\t\tprev,\n\t\t// Conversion expression\n\t\tconversion,\n\t\t// Conversion function\n\t\tconv,\n\t\t// Conversion functions (transitive conversion)\n\t\tconv1,\n\t\tconv2;\n\n\t// For each dataType in the chain\n\tfor ( i = 1; i < length; i++ ) {\n\n\t\t// Create converters map\n\t\t// with lowercased keys\n\t\tif ( i === 1 ) {\n\t\t\tfor ( key in s.converters ) {\n\t\t\t\tif ( typeof key === \"string\" ) {\n\t\t\t\t\tconverters[ key.toLowerCase() ] = s.converters[ key ];\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Get the dataTypes\n\t\tprev = current;\n\t\tcurrent = dataTypes[ i ];\n\n\t\t// If current is auto dataType, update it to prev\n\t\tif ( current === \"*\" ) {\n\t\t\tcurrent = prev;\n\t\t// If no auto and dataTypes are actually different\n\t\t} else if ( prev !== \"*\" && prev !== current ) {\n\n\t\t\t// Get the converter\n\t\t\tconversion = prev + \" \" + current;\n\t\t\tconv = converters[ conversion ] || converters[ \"* \" + current ];\n\n\t\t\t// If there is no direct converter, search transitively\n\t\t\tif ( !conv ) {\n\t\t\t\tconv2 = undefined;\n\t\t\t\tfor ( conv1 in converters ) {\n\t\t\t\t\ttmp = conv1.split( \" \" );\n\t\t\t\t\tif ( tmp[ 0 ] === prev || tmp[ 0 ] === \"*\" ) {\n\t\t\t\t\t\tconv2 = converters[ tmp[1] + \" \" + current ];\n\t\t\t\t\t\tif ( conv2 ) {\n\t\t\t\t\t\t\tconv1 = converters[ conv1 ];\n\t\t\t\t\t\t\tif ( conv1 === true ) {\n\t\t\t\t\t\t\t\tconv = conv2;\n\t\t\t\t\t\t\t} else if ( conv2 === true ) {\n\t\t\t\t\t\t\t\tconv = conv1;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\t// If we found no converter, dispatch an error\n\t\t\tif ( !( conv || conv2 ) ) {\n\t\t\t\tjQuery.error( \"No conversion from \" + conversion.replace(\" \",\" to \") );\n\t\t\t}\n\t\t\t// If found converter is not an equivalence\n\t\t\tif ( conv !== true ) {\n\t\t\t\t// Convert with 1 or 2 converters accordingly\n\t\t\t\tresponse = conv ? conv( response ) : conv2( conv1(response) );\n\t\t\t}\n\t\t}\n\t}\n\treturn response;\n}\n\n\n\n\nvar jsc = jQuery.now(),\n\tjsre = /(\\=)\\?(&|$)|\\?\\?/i;\n\n// Default jsonp settings\njQuery.ajaxSetup({\n\tjsonp: \"callback\",\n\tjsonpCallback: function() {\n\t\treturn jQuery.expando + \"_\" + ( jsc++ );\n\t}\n});\n\n// Detect, normalize options and install callbacks for jsonp requests\njQuery.ajaxPrefilter( \"json jsonp\", function( s, originalSettings, jqXHR ) {\n\n\tvar inspectData = s.contentType === \"application/x-www-form-urlencoded\" &&\n\t\t( typeof s.data === \"string\" );\n\n\tif ( s.dataTypes[ 0 ] === \"jsonp\" ||\n\t\ts.jsonp !== false && ( jsre.test( s.url ) ||\n\t\t\t\tinspectData && jsre.test( s.data ) ) ) {\n\n\t\tvar responseContainer,\n\t\t\tjsonpCallback = s.jsonpCallback =\n\t\t\t\tjQuery.isFunction( s.jsonpCallback ) ? s.jsonpCallback() : s.jsonpCallback,\n\t\t\tprevious = window[ jsonpCallback ],\n\t\t\turl = s.url,\n\t\t\tdata = s.data,\n\t\t\treplace = \"$1\" + jsonpCallback + \"$2\";\n\n\t\tif ( s.jsonp !== false ) {\n\t\t\turl = url.replace( jsre, replace );\n\t\t\tif ( s.url === url ) {\n\t\t\t\tif ( inspectData ) {\n\t\t\t\t\tdata = data.replace( jsre, replace );\n\t\t\t\t}\n\t\t\t\tif ( s.data === data ) {\n\t\t\t\t\t// Add callback manually\n\t\t\t\t\turl += (/\\?/.test( url ) ? \"&\" : \"?\") + s.jsonp + \"=\" + jsonpCallback;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\ts.url = url;\n\t\ts.data = data;\n\n\t\t// Install callback\n\t\twindow[ jsonpCallback ] = function( response ) {\n\t\t\tresponseContainer = [ response ];\n\t\t};\n\n\t\t// Clean-up function\n\t\tjqXHR.always(function() {\n\t\t\t// Set callback back to previous value\n\t\t\twindow[ jsonpCallback ] = previous;\n\t\t\t// Call if it was a function and we have a response\n\t\t\tif ( responseContainer && jQuery.isFunction( previous ) ) {\n\t\t\t\twindow[ jsonpCallback ]( responseContainer[ 0 ] );\n\t\t\t}\n\t\t});\n\n\t\t// Use data converter to retrieve json after script execution\n\t\ts.converters[\"script json\"] = function() {\n\t\t\tif ( !responseContainer ) {\n\t\t\t\tjQuery.error( jsonpCallback + \" was not called\" );\n\t\t\t}\n\t\t\treturn responseContainer[ 0 ];\n\t\t};\n\n\t\t// force json dataType\n\t\ts.dataTypes[ 0 ] = \"json\";\n\n\t\t// Delegate to script\n\t\treturn \"script\";\n\t}\n});\n\n\n\n\n// Install script dataType\njQuery.ajaxSetup({\n\taccepts: {\n\t\tscript: \"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript\"\n\t},\n\tcontents: {\n\t\tscript: /javascript|ecmascript/\n\t},\n\tconverters: {\n\t\t\"text script\": function( text ) {\n\t\t\tjQuery.globalEval( text );\n\t\t\treturn text;\n\t\t}\n\t}\n});\n\n// Handle cache's special case and global\njQuery.ajaxPrefilter( \"script\", function( s ) {\n\tif ( s.cache === undefined ) {\n\t\ts.cache = false;\n\t}\n\tif ( s.crossDomain ) {\n\t\ts.type = \"GET\";\n\t\ts.global = false;\n\t}\n});\n\n// Bind script tag hack transport\njQuery.ajaxTransport( \"script\", function(s) {\n\n\t// This transport only deals with cross domain requests\n\tif ( s.crossDomain ) {\n\n\t\tvar script,\n\t\t\thead = document.head || document.getElementsByTagName( \"head\" )[0] || document.documentElement;\n\n\t\treturn {\n\n\t\t\tsend: function( _, callback ) {\n\n\t\t\t\tscript = document.createElement( \"script\" );\n\n\t\t\t\tscript.async = \"async\";\n\n\t\t\t\tif ( s.scriptCharset ) {\n\t\t\t\t\tscript.charset = s.scriptCharset;\n\t\t\t\t}\n\n\t\t\t\tscript.src = s.url;\n\n\t\t\t\t// Attach handlers for all browsers\n\t\t\t\tscript.onload = script.onreadystatechange = function( _, isAbort ) {\n\n\t\t\t\t\tif ( isAbort || !script.readyState || /loaded|complete/.test( script.readyState ) ) {\n\n\t\t\t\t\t\t// Handle memory leak in IE\n\t\t\t\t\t\tscript.onload = script.onreadystatechange = null;\n\n\t\t\t\t\t\t// Remove the script\n\t\t\t\t\t\tif ( head && script.parentNode ) {\n\t\t\t\t\t\t\thead.removeChild( script );\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Dereference the script\n\t\t\t\t\t\tscript = undefined;\n\n\t\t\t\t\t\t// Callback if not abort\n\t\t\t\t\t\tif ( !isAbort ) {\n\t\t\t\t\t\t\tcallback( 200, \"success\" );\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t};\n\t\t\t\t// Use insertBefore instead of appendChild  to circumvent an IE6 bug.\n\t\t\t\t// This arises when a base node is used (#2709 and #4378).\n\t\t\t\thead.insertBefore( script, head.firstChild );\n\t\t\t},\n\n\t\t\tabort: function() {\n\t\t\t\tif ( script ) {\n\t\t\t\t\tscript.onload( 0, 1 );\n\t\t\t\t}\n\t\t\t}\n\t\t};\n\t}\n});\n\n\n\n\nvar // #5280: Internet Explorer will keep connections alive if we don't abort on unload\n\txhrOnUnloadAbort = window.ActiveXObject ? function() {\n\t\t// Abort all pending requests\n\t\tfor ( var key in xhrCallbacks ) {\n\t\t\txhrCallbacks[ key ]( 0, 1 );\n\t\t}\n\t} : false,\n\txhrId = 0,\n\txhrCallbacks;\n\n// Functions to create xhrs\nfunction createStandardXHR() {\n\ttry {\n\t\treturn new window.XMLHttpRequest();\n\t} catch( e ) {}\n}\n\nfunction createActiveXHR() {\n\ttry {\n\t\treturn new window.ActiveXObject( \"Microsoft.XMLHTTP\" );\n\t} catch( e ) {}\n}\n\n// Create the request object\n// (This is still attached to ajaxSettings for backward compatibility)\njQuery.ajaxSettings.xhr = window.ActiveXObject ?\n\t/* Microsoft failed to properly\n\t * implement the XMLHttpRequest in IE7 (can't request local files),\n\t * so we use the ActiveXObject when it is available\n\t * Additionally XMLHttpRequest can be disabled in IE7/IE8 so\n\t * we need a fallback.\n\t */\n\tfunction() {\n\t\treturn !this.isLocal && createStandardXHR() || createActiveXHR();\n\t} :\n\t// For all other browsers, use the standard XMLHttpRequest object\n\tcreateStandardXHR;\n\n// Determine support properties\n(function( xhr ) {\n\tjQuery.extend( jQuery.support, {\n\t\tajax: !!xhr,\n\t\tcors: !!xhr && ( \"withCredentials\" in xhr )\n\t});\n})( jQuery.ajaxSettings.xhr() );\n\n// Create transport if the browser can provide an xhr\nif ( jQuery.support.ajax ) {\n\n\tjQuery.ajaxTransport(function( s ) {\n\t\t// Cross domain only allowed if supported through XMLHttpRequest\n\t\tif ( !s.crossDomain || jQuery.support.cors ) {\n\n\t\t\tvar callback;\n\n\t\t\treturn {\n\t\t\t\tsend: function( headers, complete ) {\n\n\t\t\t\t\t// Get a new xhr\n\t\t\t\t\tvar xhr = s.xhr(),\n\t\t\t\t\t\thandle,\n\t\t\t\t\t\ti;\n\n\t\t\t\t\t// Open the socket\n\t\t\t\t\t// Passing null username, generates a login popup on Opera (#2865)\n\t\t\t\t\tif ( s.username ) {\n\t\t\t\t\t\txhr.open( s.type, s.url, s.async, s.username, s.password );\n\t\t\t\t\t} else {\n\t\t\t\t\t\txhr.open( s.type, s.url, s.async );\n\t\t\t\t\t}\n\n\t\t\t\t\t// Apply custom fields if provided\n\t\t\t\t\tif ( s.xhrFields ) {\n\t\t\t\t\t\tfor ( i in s.xhrFields ) {\n\t\t\t\t\t\t\txhr[ i ] = s.xhrFields[ i ];\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\t// Override mime type if needed\n\t\t\t\t\tif ( s.mimeType && xhr.overrideMimeType ) {\n\t\t\t\t\t\txhr.overrideMimeType( s.mimeType );\n\t\t\t\t\t}\n\n\t\t\t\t\t// X-Requested-With header\n\t\t\t\t\t// For cross-domain requests, seeing as conditions for a preflight are\n\t\t\t\t\t// akin to a jigsaw puzzle, we simply never set it to be sure.\n\t\t\t\t\t// (it can always be set on a per-request basis or even using ajaxSetup)\n\t\t\t\t\t// For same-domain requests, won't change header if already provided.\n\t\t\t\t\tif ( !s.crossDomain && !headers[\"X-Requested-With\"] ) {\n\t\t\t\t\t\theaders[ \"X-Requested-With\" ] = \"XMLHttpRequest\";\n\t\t\t\t\t}\n\n\t\t\t\t\t// Need an extra try/catch for cross domain requests in Firefox 3\n\t\t\t\t\ttry {\n\t\t\t\t\t\tfor ( i in headers ) {\n\t\t\t\t\t\t\txhr.setRequestHeader( i, headers[ i ] );\n\t\t\t\t\t\t}\n\t\t\t\t\t} catch( _ ) {}\n\n\t\t\t\t\t// Do send the request\n\t\t\t\t\t// This may raise an exception which is actually\n\t\t\t\t\t// handled in jQuery.ajax (so no try/catch here)\n\t\t\t\t\txhr.send( ( s.hasContent && s.data ) || null );\n\n\t\t\t\t\t// Listener\n\t\t\t\t\tcallback = function( _, isAbort ) {\n\n\t\t\t\t\t\tvar status,\n\t\t\t\t\t\t\tstatusText,\n\t\t\t\t\t\t\tresponseHeaders,\n\t\t\t\t\t\t\tresponses,\n\t\t\t\t\t\t\txml;\n\n\t\t\t\t\t\t// Firefox throws exceptions when accessing properties\n\t\t\t\t\t\t// of an xhr when a network error occured\n\t\t\t\t\t\t// http://helpful.knobs-dials.com/index.php/Component_returned_failure_code:_0x80040111_(NS_ERROR_NOT_AVAILABLE)\n\t\t\t\t\t\ttry {\n\n\t\t\t\t\t\t\t// Was never called and is aborted or complete\n\t\t\t\t\t\t\tif ( callback && ( isAbort || xhr.readyState === 4 ) ) {\n\n\t\t\t\t\t\t\t\t// Only called once\n\t\t\t\t\t\t\t\tcallback = undefined;\n\n\t\t\t\t\t\t\t\t// Do not keep as active anymore\n\t\t\t\t\t\t\t\tif ( handle ) {\n\t\t\t\t\t\t\t\t\txhr.onreadystatechange = jQuery.noop;\n\t\t\t\t\t\t\t\t\tif ( xhrOnUnloadAbort ) {\n\t\t\t\t\t\t\t\t\t\tdelete xhrCallbacks[ handle ];\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t// If it's an abort\n\t\t\t\t\t\t\t\tif ( isAbort ) {\n\t\t\t\t\t\t\t\t\t// Abort it manually if needed\n\t\t\t\t\t\t\t\t\tif ( xhr.readyState !== 4 ) {\n\t\t\t\t\t\t\t\t\t\txhr.abort();\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\tstatus = xhr.status;\n\t\t\t\t\t\t\t\t\tresponseHeaders = xhr.getAllResponseHeaders();\n\t\t\t\t\t\t\t\t\tresponses = {};\n\t\t\t\t\t\t\t\t\txml = xhr.responseXML;\n\n\t\t\t\t\t\t\t\t\t// Construct response list\n\t\t\t\t\t\t\t\t\tif ( xml && xml.documentElement /* #4958 */ ) {\n\t\t\t\t\t\t\t\t\t\tresponses.xml = xml;\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\tresponses.text = xhr.responseText;\n\n\t\t\t\t\t\t\t\t\t// Firefox throws an exception when accessing\n\t\t\t\t\t\t\t\t\t// statusText for faulty cross-domain requests\n\t\t\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\t\t\tstatusText = xhr.statusText;\n\t\t\t\t\t\t\t\t\t} catch( e ) {\n\t\t\t\t\t\t\t\t\t\t// We normalize with Webkit giving an empty statusText\n\t\t\t\t\t\t\t\t\t\tstatusText = \"\";\n\t\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t\t// Filter status for non standard behaviors\n\n\t\t\t\t\t\t\t\t\t// If the request is local and we have data: assume a success\n\t\t\t\t\t\t\t\t\t// (success with no data won't get notified, that's the best we\n\t\t\t\t\t\t\t\t\t// can do given current implementations)\n\t\t\t\t\t\t\t\t\tif ( !status && s.isLocal && !s.crossDomain ) {\n\t\t\t\t\t\t\t\t\t\tstatus = responses.text ? 200 : 404;\n\t\t\t\t\t\t\t\t\t// IE - #1450: sometimes returns 1223 when it should be 204\n\t\t\t\t\t\t\t\t\t} else if ( status === 1223 ) {\n\t\t\t\t\t\t\t\t\t\tstatus = 204;\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} catch( firefoxAccessException ) {\n\t\t\t\t\t\t\tif ( !isAbort ) {\n\t\t\t\t\t\t\t\tcomplete( -1, firefoxAccessException );\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Call complete if needed\n\t\t\t\t\t\tif ( responses ) {\n\t\t\t\t\t\t\tcomplete( status, statusText, responses, responseHeaders );\n\t\t\t\t\t\t}\n\t\t\t\t\t};\n\n\t\t\t\t\t// if we're in sync mode or it's in cache\n\t\t\t\t\t// and has been retrieved directly (IE6 & IE7)\n\t\t\t\t\t// we need to manually fire the callback\n\t\t\t\t\tif ( !s.async || xhr.readyState === 4 ) {\n\t\t\t\t\t\tcallback();\n\t\t\t\t\t} else {\n\t\t\t\t\t\thandle = ++xhrId;\n\t\t\t\t\t\tif ( xhrOnUnloadAbort ) {\n\t\t\t\t\t\t\t// Create the active xhrs callbacks list if needed\n\t\t\t\t\t\t\t// and attach the unload handler\n\t\t\t\t\t\t\tif ( !xhrCallbacks ) {\n\t\t\t\t\t\t\t\txhrCallbacks = {};\n\t\t\t\t\t\t\t\tjQuery( window ).unload( xhrOnUnloadAbort );\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t// Add to list of active xhrs callbacks\n\t\t\t\t\t\t\txhrCallbacks[ handle ] = callback;\n\t\t\t\t\t\t}\n\t\t\t\t\t\txhr.onreadystatechange = callback;\n\t\t\t\t\t}\n\t\t\t\t},\n\n\t\t\t\tabort: function() {\n\t\t\t\t\tif ( callback ) {\n\t\t\t\t\t\tcallback(0,1);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t};\n\t\t}\n\t});\n}\n\n\n\n\nvar elemdisplay = {},\n\tiframe, iframeDoc,\n\trfxtypes = /^(?:toggle|show|hide)$/,\n\trfxnum = /^([+\\-]=)?([\\d+.\\-]+)([a-z%]*)$/i,\n\ttimerId,\n\tfxAttrs = [\n\t\t// height animations\n\t\t[ \"height\", \"marginTop\", \"marginBottom\", \"paddingTop\", \"paddingBottom\" ],\n\t\t// width animations\n\t\t[ \"width\", \"marginLeft\", \"marginRight\", \"paddingLeft\", \"paddingRight\" ],\n\t\t// opacity animations\n\t\t[ \"opacity\" ]\n\t],\n\tfxNow;\n\njQuery.fn.extend({\n\tshow: function( speed, easing, callback ) {\n\t\tvar elem, display;\n\n\t\tif ( speed || speed === 0 ) {\n\t\t\treturn this.animate( genFx(\"show\", 3), speed, easing, callback );\n\n\t\t} else {\n\t\t\tfor ( var i = 0, j = this.length; i < j; i++ ) {\n\t\t\t\telem = this[ i ];\n\n\t\t\t\tif ( elem.style ) {\n\t\t\t\t\tdisplay = elem.style.display;\n\n\t\t\t\t\t// Reset the inline display of this element to learn if it is\n\t\t\t\t\t// being hidden by cascaded rules or not\n\t\t\t\t\tif ( !jQuery._data(elem, \"olddisplay\") && display === \"none\" ) {\n\t\t\t\t\t\tdisplay = elem.style.display = \"\";\n\t\t\t\t\t}\n\n\t\t\t\t\t// Set elements which have been overridden with display: none\n\t\t\t\t\t// in a stylesheet to whatever the default browser style is\n\t\t\t\t\t// for such an element\n\t\t\t\t\tif ( display === \"\" && jQuery.css(elem, \"display\") === \"none\" ) {\n\t\t\t\t\t\tjQuery._data( elem, \"olddisplay\", defaultDisplay(elem.nodeName) );\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Set the display of most of the elements in a second loop\n\t\t\t// to avoid the constant reflow\n\t\t\tfor ( i = 0; i < j; i++ ) {\n\t\t\t\telem = this[ i ];\n\n\t\t\t\tif ( elem.style ) {\n\t\t\t\t\tdisplay = elem.style.display;\n\n\t\t\t\t\tif ( display === \"\" || display === \"none\" ) {\n\t\t\t\t\t\telem.style.display = jQuery._data( elem, \"olddisplay\" ) || \"\";\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn this;\n\t\t}\n\t},\n\n\thide: function( speed, easing, callback ) {\n\t\tif ( speed || speed === 0 ) {\n\t\t\treturn this.animate( genFx(\"hide\", 3), speed, easing, callback);\n\n\t\t} else {\n\t\t\tvar elem, display,\n\t\t\t\ti = 0,\n\t\t\t\tj = this.length;\n\n\t\t\tfor ( ; i < j; i++ ) {\n\t\t\t\telem = this[i];\n\t\t\t\tif ( elem.style ) {\n\t\t\t\t\tdisplay = jQuery.css( elem, \"display\" );\n\n\t\t\t\t\tif ( display !== \"none\" && !jQuery._data( elem, \"olddisplay\" ) ) {\n\t\t\t\t\t\tjQuery._data( elem, \"olddisplay\", display );\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Set the display of the elements in a second loop\n\t\t\t// to avoid the constant reflow\n\t\t\tfor ( i = 0; i < j; i++ ) {\n\t\t\t\tif ( this[i].style ) {\n\t\t\t\t\tthis[i].style.display = \"none\";\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn this;\n\t\t}\n\t},\n\n\t// Save the old toggle function\n\t_toggle: jQuery.fn.toggle,\n\n\ttoggle: function( fn, fn2, callback ) {\n\t\tvar bool = typeof fn === \"boolean\";\n\n\t\tif ( jQuery.isFunction(fn) && jQuery.isFunction(fn2) ) {\n\t\t\tthis._toggle.apply( this, arguments );\n\n\t\t} else if ( fn == null || bool ) {\n\t\t\tthis.each(function() {\n\t\t\t\tvar state = bool ? fn : jQuery(this).is(\":hidden\");\n\t\t\t\tjQuery(this)[ state ? \"show\" : \"hide\" ]();\n\t\t\t});\n\n\t\t} else {\n\t\t\tthis.animate(genFx(\"toggle\", 3), fn, fn2, callback);\n\t\t}\n\n\t\treturn this;\n\t},\n\n\tfadeTo: function( speed, to, easing, callback ) {\n\t\treturn this.filter(\":hidden\").css(\"opacity\", 0).show().end()\n\t\t\t\t\t.animate({opacity: to}, speed, easing, callback);\n\t},\n\n\tanimate: function( prop, speed, easing, callback ) {\n\t\tvar optall = jQuery.speed( speed, easing, callback );\n\n\t\tif ( jQuery.isEmptyObject( prop ) ) {\n\t\t\treturn this.each( optall.complete, [ false ] );\n\t\t}\n\n\t\t// Do not change referenced properties as per-property easing will be lost\n\t\tprop = jQuery.extend( {}, prop );\n\n\t\tfunction doAnimation() {\n\t\t\t// XXX 'this' does not always have a nodeName when running the\n\t\t\t// test suite\n\n\t\t\tif ( optall.queue === false ) {\n\t\t\t\tjQuery._mark( this );\n\t\t\t}\n\n\t\t\tvar opt = jQuery.extend( {}, optall ),\n\t\t\t\tisElement = this.nodeType === 1,\n\t\t\t\thidden = isElement && jQuery(this).is(\":hidden\"),\n\t\t\t\tname, val, p, e,\n\t\t\t\tparts, start, end, unit,\n\t\t\t\tmethod;\n\n\t\t\t// will store per property easing and be used to determine when an animation is complete\n\t\t\topt.animatedProperties = {};\n\n\t\t\tfor ( p in prop ) {\n\n\t\t\t\t// property name normalization\n\t\t\t\tname = jQuery.camelCase( p );\n\t\t\t\tif ( p !== name ) {\n\t\t\t\t\tprop[ name ] = prop[ p ];\n\t\t\t\t\tdelete prop[ p ];\n\t\t\t\t}\n\n\t\t\t\tval = prop[ name ];\n\n\t\t\t\t// easing resolution: per property > opt.specialEasing > opt.easing > 'swing' (default)\n\t\t\t\tif ( jQuery.isArray( val ) ) {\n\t\t\t\t\topt.animatedProperties[ name ] = val[ 1 ];\n\t\t\t\t\tval = prop[ name ] = val[ 0 ];\n\t\t\t\t} else {\n\t\t\t\t\topt.animatedProperties[ name ] = opt.specialEasing && opt.specialEasing[ name ] || opt.easing || 'swing';\n\t\t\t\t}\n\n\t\t\t\tif ( val === \"hide\" && hidden || val === \"show\" && !hidden ) {\n\t\t\t\t\treturn opt.complete.call( this );\n\t\t\t\t}\n\n\t\t\t\tif ( isElement && ( name === \"height\" || name === \"width\" ) ) {\n\t\t\t\t\t// Make sure that nothing sneaks out\n\t\t\t\t\t// Record all 3 overflow attributes because IE does not\n\t\t\t\t\t// change the overflow attribute when overflowX and\n\t\t\t\t\t// overflowY are set to the same value\n\t\t\t\t\topt.overflow = [ this.style.overflow, this.style.overflowX, this.style.overflowY ];\n\n\t\t\t\t\t// Set display property to inline-block for height/width\n\t\t\t\t\t// animations on inline elements that are having width/height animated\n\t\t\t\t\tif ( jQuery.css( this, \"display\" ) === \"inline\" &&\n\t\t\t\t\t\t\tjQuery.css( this, \"float\" ) === \"none\" ) {\n\n\t\t\t\t\t\t// inline-level elements accept inline-block;\n\t\t\t\t\t\t// block-level elements need to be inline with layout\n\t\t\t\t\t\tif ( !jQuery.support.inlineBlockNeedsLayout || defaultDisplay( this.nodeName ) === \"inline\" ) {\n\t\t\t\t\t\t\tthis.style.display = \"inline-block\";\n\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tthis.style.zoom = 1;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif ( opt.overflow != null ) {\n\t\t\t\tthis.style.overflow = \"hidden\";\n\t\t\t}\n\n\t\t\tfor ( p in prop ) {\n\t\t\t\te = new jQuery.fx( this, opt, p );\n\t\t\t\tval = prop[ p ];\n\n\t\t\t\tif ( rfxtypes.test( val ) ) {\n\n\t\t\t\t\t// Tracks whether to show or hide based on private\n\t\t\t\t\t// data attached to the element\n\t\t\t\t\tmethod = jQuery._data( this, \"toggle\" + p ) || ( val === \"toggle\" ? hidden ? \"show\" : \"hide\" : 0 );\n\t\t\t\t\tif ( method ) {\n\t\t\t\t\t\tjQuery._data( this, \"toggle\" + p, method === \"show\" ? \"hide\" : \"show\" );\n\t\t\t\t\t\te[ method ]();\n\t\t\t\t\t} else {\n\t\t\t\t\t\te[ val ]();\n\t\t\t\t\t}\n\n\t\t\t\t} else {\n\t\t\t\t\tparts = rfxnum.exec( val );\n\t\t\t\t\tstart = e.cur();\n\n\t\t\t\t\tif ( parts ) {\n\t\t\t\t\t\tend = parseFloat( parts[2] );\n\t\t\t\t\t\tunit = parts[3] || ( jQuery.cssNumber[ p ] ? \"\" : \"px\" );\n\n\t\t\t\t\t\t// We need to compute starting value\n\t\t\t\t\t\tif ( unit !== \"px\" ) {\n\t\t\t\t\t\t\tjQuery.style( this, p, (end || 1) + unit);\n\t\t\t\t\t\t\tstart = ( (end || 1) / e.cur() ) * start;\n\t\t\t\t\t\t\tjQuery.style( this, p, start + unit);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// If a +=/-= token was provided, we're doing a relative animation\n\t\t\t\t\t\tif ( parts[1] ) {\n\t\t\t\t\t\t\tend = ( (parts[ 1 ] === \"-=\" ? -1 : 1) * end ) + start;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\te.custom( start, end, unit );\n\n\t\t\t\t\t} else {\n\t\t\t\t\t\te.custom( start, val, \"\" );\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// For JS strict compliance\n\t\t\treturn true;\n\t\t}\n\n\t\treturn optall.queue === false ?\n\t\t\tthis.each( doAnimation ) :\n\t\t\tthis.queue( optall.queue, doAnimation );\n\t},\n\n\tstop: function( type, clearQueue, gotoEnd ) {\n\t\tif ( typeof type !== \"string\" ) {\n\t\t\tgotoEnd = clearQueue;\n\t\t\tclearQueue = type;\n\t\t\ttype = undefined;\n\t\t}\n\t\tif ( clearQueue && type !== false ) {\n\t\t\tthis.queue( type || \"fx\", [] );\n\t\t}\n\n\t\treturn this.each(function() {\n\t\t\tvar index,\n\t\t\t\thadTimers = false,\n\t\t\t\ttimers = jQuery.timers,\n\t\t\t\tdata = jQuery._data( this );\n\n\t\t\t// clear marker counters if we know they won't be\n\t\t\tif ( !gotoEnd ) {\n\t\t\t\tjQuery._unmark( true, this );\n\t\t\t}\n\n\t\t\tfunction stopQueue( elem, data, index ) {\n\t\t\t\tvar hooks = data[ index ];\n\t\t\t\tjQuery.removeData( elem, index, true );\n\t\t\t\thooks.stop( gotoEnd );\n\t\t\t}\n\n\t\t\tif ( type == null ) {\n\t\t\t\tfor ( index in data ) {\n\t\t\t\t\tif ( data[ index ] && data[ index ].stop && index.indexOf(\".run\") === index.length - 4 ) {\n\t\t\t\t\t\tstopQueue( this, data, index );\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else if ( data[ index = type + \".run\" ] && data[ index ].stop ){\n\t\t\t\tstopQueue( this, data, index );\n\t\t\t}\n\n\t\t\tfor ( index = timers.length; index--; ) {\n\t\t\t\tif ( timers[ index ].elem === this && (type == null || timers[ index ].queue === type) ) {\n\t\t\t\t\tif ( gotoEnd ) {\n\n\t\t\t\t\t\t// force the next step to be the last\n\t\t\t\t\t\ttimers[ index ]( true );\n\t\t\t\t\t} else {\n\t\t\t\t\t\ttimers[ index ].saveState();\n\t\t\t\t\t}\n\t\t\t\t\thadTimers = true;\n\t\t\t\t\ttimers.splice( index, 1 );\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// start the next in the queue if the last step wasn't forced\n\t\t\t// timers currently will call their complete callbacks, which will dequeue\n\t\t\t// but only if they were gotoEnd\n\t\t\tif ( !( gotoEnd && hadTimers ) ) {\n\t\t\t\tjQuery.dequeue( this, type );\n\t\t\t}\n\t\t});\n\t}\n\n});\n\n// Animations created synchronously will run synchronously\nfunction createFxNow() {\n\tsetTimeout( clearFxNow, 0 );\n\treturn ( fxNow = jQuery.now() );\n}\n\nfunction clearFxNow() {\n\tfxNow = undefined;\n}\n\n// Generate parameters to create a standard animation\nfunction genFx( type, num ) {\n\tvar obj = {};\n\n\tjQuery.each( fxAttrs.concat.apply([], fxAttrs.slice( 0, num )), function() {\n\t\tobj[ this ] = type;\n\t});\n\n\treturn obj;\n}\n\n// Generate shortcuts for custom animations\njQuery.each({\n\tslideDown: genFx( \"show\", 1 ),\n\tslideUp: genFx( \"hide\", 1 ),\n\tslideToggle: genFx( \"toggle\", 1 ),\n\tfadeIn: { opacity: \"show\" },\n\tfadeOut: { opacity: \"hide\" },\n\tfadeToggle: { opacity: \"toggle\" }\n}, function( name, props ) {\n\tjQuery.fn[ name ] = function( speed, easing, callback ) {\n\t\treturn this.animate( props, speed, easing, callback );\n\t};\n});\n\njQuery.extend({\n\tspeed: function( speed, easing, fn ) {\n\t\tvar opt = speed && typeof speed === \"object\" ? jQuery.extend( {}, speed ) : {\n\t\t\tcomplete: fn || !fn && easing ||\n\t\t\t\tjQuery.isFunction( speed ) && speed,\n\t\t\tduration: speed,\n\t\t\teasing: fn && easing || easing && !jQuery.isFunction( easing ) && easing\n\t\t};\n\n\t\topt.duration = jQuery.fx.off ? 0 : typeof opt.duration === \"number\" ? opt.duration :\n\t\t\topt.duration in jQuery.fx.speeds ? jQuery.fx.speeds[ opt.duration ] : jQuery.fx.speeds._default;\n\n\t\t// normalize opt.queue - true/undefined/null -> \"fx\"\n\t\tif ( opt.queue == null || opt.queue === true ) {\n\t\t\topt.queue = \"fx\";\n\t\t}\n\n\t\t// Queueing\n\t\topt.old = opt.complete;\n\n\t\topt.complete = function( noUnmark ) {\n\t\t\tif ( jQuery.isFunction( opt.old ) ) {\n\t\t\t\topt.old.call( this );\n\t\t\t}\n\n\t\t\tif ( opt.queue ) {\n\t\t\t\tjQuery.dequeue( this, opt.queue );\n\t\t\t} else if ( noUnmark !== false ) {\n\t\t\t\tjQuery._unmark( this );\n\t\t\t}\n\t\t};\n\n\t\treturn opt;\n\t},\n\n\teasing: {\n\t\tlinear: function( p, n, firstNum, diff ) {\n\t\t\treturn firstNum + diff * p;\n\t\t},\n\t\tswing: function( p, n, firstNum, diff ) {\n\t\t\treturn ( ( -Math.cos( p*Math.PI ) / 2 ) + 0.5 ) * diff + firstNum;\n\t\t}\n\t},\n\n\ttimers: [],\n\n\tfx: function( elem, options, prop ) {\n\t\tthis.options = options;\n\t\tthis.elem = elem;\n\t\tthis.prop = prop;\n\n\t\toptions.orig = options.orig || {};\n\t}\n\n});\n\njQuery.fx.prototype = {\n\t// Simple function for setting a style value\n\tupdate: function() {\n\t\tif ( this.options.step ) {\n\t\t\tthis.options.step.call( this.elem, this.now, this );\n\t\t}\n\n\t\t( jQuery.fx.step[ this.prop ] || jQuery.fx.step._default )( this );\n\t},\n\n\t// Get the current size\n\tcur: function() {\n\t\tif ( this.elem[ this.prop ] != null && (!this.elem.style || this.elem.style[ this.prop ] == null) ) {\n\t\t\treturn this.elem[ this.prop ];\n\t\t}\n\n\t\tvar parsed,\n\t\t\tr = jQuery.css( this.elem, this.prop );\n\t\t// Empty strings, null, undefined and \"auto\" are converted to 0,\n\t\t// complex values such as \"rotate(1rad)\" are returned as is,\n\t\t// simple values such as \"10px\" are parsed to Float.\n\t\treturn isNaN( parsed = parseFloat( r ) ) ? !r || r === \"auto\" ? 0 : r : parsed;\n\t},\n\n\t// Start an animation from one number to another\n\tcustom: function( from, to, unit ) {\n\t\tvar self = this,\n\t\t\tfx = jQuery.fx;\n\n\t\tthis.startTime = fxNow || createFxNow();\n\t\tthis.end = to;\n\t\tthis.now = this.start = from;\n\t\tthis.pos = this.state = 0;\n\t\tthis.unit = unit || this.unit || ( jQuery.cssNumber[ this.prop ] ? \"\" : \"px\" );\n\n\t\tfunction t( gotoEnd ) {\n\t\t\treturn self.step( gotoEnd );\n\t\t}\n\n\t\tt.queue = this.options.queue;\n\t\tt.elem = this.elem;\n\t\tt.saveState = function() {\n\t\t\tif ( self.options.hide && jQuery._data( self.elem, \"fxshow\" + self.prop ) === undefined ) {\n\t\t\t\tjQuery._data( self.elem, \"fxshow\" + self.prop, self.start );\n\t\t\t}\n\t\t};\n\n\t\tif ( t() && jQuery.timers.push(t) && !timerId ) {\n\t\t\ttimerId = setInterval( fx.tick, fx.interval );\n\t\t}\n\t},\n\n\t// Simple 'show' function\n\tshow: function() {\n\t\tvar dataShow = jQuery._data( this.elem, \"fxshow\" + this.prop );\n\n\t\t// Remember where we started, so that we can go back to it later\n\t\tthis.options.orig[ this.prop ] = dataShow || jQuery.style( this.elem, this.prop );\n\t\tthis.options.show = true;\n\n\t\t// Begin the animation\n\t\t// Make sure that we start at a small width/height to avoid any flash of content\n\t\tif ( dataShow !== undefined ) {\n\t\t\t// This show is picking up where a previous hide or show left off\n\t\t\tthis.custom( this.cur(), dataShow );\n\t\t} else {\n\t\t\tthis.custom( this.prop === \"width\" || this.prop === \"height\" ? 1 : 0, this.cur() );\n\t\t}\n\n\t\t// Start by showing the element\n\t\tjQuery( this.elem ).show();\n\t},\n\n\t// Simple 'hide' function\n\thide: function() {\n\t\t// Remember where we started, so that we can go back to it later\n\t\tthis.options.orig[ this.prop ] = jQuery._data( this.elem, \"fxshow\" + this.prop ) || jQuery.style( this.elem, this.prop );\n\t\tthis.options.hide = true;\n\n\t\t// Begin the animation\n\t\tthis.custom( this.cur(), 0 );\n\t},\n\n\t// Each step of an animation\n\tstep: function( gotoEnd ) {\n\t\tvar p, n, complete,\n\t\t\tt = fxNow || createFxNow(),\n\t\t\tdone = true,\n\t\t\telem = this.elem,\n\t\t\toptions = this.options;\n\n\t\tif ( gotoEnd || t >= options.duration + this.startTime ) {\n\t\t\tthis.now = this.end;\n\t\t\tthis.pos = this.state = 1;\n\t\t\tthis.update();\n\n\t\t\toptions.animatedProperties[ this.prop ] = true;\n\n\t\t\tfor ( p in options.animatedProperties ) {\n\t\t\t\tif ( options.animatedProperties[ p ] !== true ) {\n\t\t\t\t\tdone = false;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif ( done ) {\n\t\t\t\t// Reset the overflow\n\t\t\t\tif ( options.overflow != null && !jQuery.support.shrinkWrapBlocks ) {\n\n\t\t\t\t\tjQuery.each( [ \"\", \"X\", \"Y\" ], function( index, value ) {\n\t\t\t\t\t\telem.style[ \"overflow\" + value ] = options.overflow[ index ];\n\t\t\t\t\t});\n\t\t\t\t}\n\n\t\t\t\t// Hide the element if the \"hide\" operation was done\n\t\t\t\tif ( options.hide ) {\n\t\t\t\t\tjQuery( elem ).hide();\n\t\t\t\t}\n\n\t\t\t\t// Reset the properties, if the item has been hidden or shown\n\t\t\t\tif ( options.hide || options.show ) {\n\t\t\t\t\tfor ( p in options.animatedProperties ) {\n\t\t\t\t\t\tjQuery.style( elem, p, options.orig[ p ] );\n\t\t\t\t\t\tjQuery.removeData( elem, \"fxshow\" + p, true );\n\t\t\t\t\t\t// Toggle data is no longer needed\n\t\t\t\t\t\tjQuery.removeData( elem, \"toggle\" + p, true );\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// Execute the complete function\n\t\t\t\t// in the event that the complete function throws an exception\n\t\t\t\t// we must ensure it won't be called twice. #5684\n\n\t\t\t\tcomplete = options.complete;\n\t\t\t\tif ( complete ) {\n\n\t\t\t\t\toptions.complete = false;\n\t\t\t\t\tcomplete.call( elem );\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn false;\n\n\t\t} else {\n\t\t\t// classical easing cannot be used with an Infinity duration\n\t\t\tif ( options.duration == Infinity ) {\n\t\t\t\tthis.now = t;\n\t\t\t} else {\n\t\t\t\tn = t - this.startTime;\n\t\t\t\tthis.state = n / options.duration;\n\n\t\t\t\t// Perform the easing function, defaults to swing\n\t\t\t\tthis.pos = jQuery.easing[ options.animatedProperties[this.prop] ]( this.state, n, 0, 1, options.duration );\n\t\t\t\tthis.now = this.start + ( (this.end - this.start) * this.pos );\n\t\t\t}\n\t\t\t// Perform the next step of the animation\n\t\t\tthis.update();\n\t\t}\n\n\t\treturn true;\n\t}\n};\n\njQuery.extend( jQuery.fx, {\n\ttick: function() {\n\t\tvar timer,\n\t\t\ttimers = jQuery.timers,\n\t\t\ti = 0;\n\n\t\tfor ( ; i < timers.length; i++ ) {\n\t\t\ttimer = timers[ i ];\n\t\t\t// Checks the timer has not already been removed\n\t\t\tif ( !timer() && timers[ i ] === timer ) {\n\t\t\t\ttimers.splice( i--, 1 );\n\t\t\t}\n\t\t}\n\n\t\tif ( !timers.length ) {\n\t\t\tjQuery.fx.stop();\n\t\t}\n\t},\n\n\tinterval: 13,\n\n\tstop: function() {\n\t\tclearInterval( timerId );\n\t\ttimerId = null;\n\t},\n\n\tspeeds: {\n\t\tslow: 600,\n\t\tfast: 200,\n\t\t// Default speed\n\t\t_default: 400\n\t},\n\n\tstep: {\n\t\topacity: function( fx ) {\n\t\t\tjQuery.style( fx.elem, \"opacity\", fx.now );\n\t\t},\n\n\t\t_default: function( fx ) {\n\t\t\tif ( fx.elem.style && fx.elem.style[ fx.prop ] != null ) {\n\t\t\t\tfx.elem.style[ fx.prop ] = fx.now + fx.unit;\n\t\t\t} else {\n\t\t\t\tfx.elem[ fx.prop ] = fx.now;\n\t\t\t}\n\t\t}\n\t}\n});\n\n// Adds width/height step functions\n// Do not set anything below 0\njQuery.each([ \"width\", \"height\" ], function( i, prop ) {\n\tjQuery.fx.step[ prop ] = function( fx ) {\n\t\tjQuery.style( fx.elem, prop, Math.max(0, fx.now) + fx.unit );\n\t};\n});\n\nif ( jQuery.expr && jQuery.expr.filters ) {\n\tjQuery.expr.filters.animated = function( elem ) {\n\t\treturn jQuery.grep(jQuery.timers, function( fn ) {\n\t\t\treturn elem === fn.elem;\n\t\t}).length;\n\t};\n}\n\n// Try to restore the default display value of an element\nfunction defaultDisplay( nodeName ) {\n\n\tif ( !elemdisplay[ nodeName ] ) {\n\n\t\tvar body = document.body,\n\t\t\telem = jQuery( \"<\" + nodeName + \">\" ).appendTo( body ),\n\t\t\tdisplay = elem.css( \"display\" );\n\t\telem.remove();\n\n\t\t// If the simple way fails,\n\t\t// get element's real default display by attaching it to a temp iframe\n\t\tif ( display === \"none\" || display === \"\" ) {\n\t\t\t// No iframe to use yet, so create it\n\t\t\tif ( !iframe ) {\n\t\t\t\tiframe = document.createElement( \"iframe\" );\n\t\t\t\tiframe.frameBorder = iframe.width = iframe.height = 0;\n\t\t\t}\n\n\t\t\tbody.appendChild( iframe );\n\n\t\t\t// Create a cacheable copy of the iframe document on first call.\n\t\t\t// IE and Opera will allow us to reuse the iframeDoc without re-writing the fake HTML\n\t\t\t// document to it; WebKit & Firefox won't allow reusing the iframe document.\n\t\t\tif ( !iframeDoc || !iframe.createElement ) {\n\t\t\t\tiframeDoc = ( iframe.contentWindow || iframe.contentDocument ).document;\n\t\t\t\tiframeDoc.write( ( document.compatMode === \"CSS1Compat\" ? \"<!doctype html>\" : \"\" ) + \"<html><body>\" );\n\t\t\t\tiframeDoc.close();\n\t\t\t}\n\n\t\t\telem = iframeDoc.createElement( nodeName );\n\n\t\t\tiframeDoc.body.appendChild( elem );\n\n\t\t\tdisplay = jQuery.css( elem, \"display\" );\n\t\t\tbody.removeChild( iframe );\n\t\t}\n\n\t\t// Store the correct default display\n\t\telemdisplay[ nodeName ] = display;\n\t}\n\n\treturn elemdisplay[ nodeName ];\n}\n\n\n\n\nvar rtable = /^t(?:able|d|h)$/i,\n\trroot = /^(?:body|html)$/i;\n\nif ( \"getBoundingClientRect\" in document.documentElement ) {\n\tjQuery.fn.offset = function( options ) {\n\t\tvar elem = this[0], box;\n\n\t\tif ( options ) {\n\t\t\treturn this.each(function( i ) {\n\t\t\t\tjQuery.offset.setOffset( this, options, i );\n\t\t\t});\n\t\t}\n\n\t\tif ( !elem || !elem.ownerDocument ) {\n\t\t\treturn null;\n\t\t}\n\n\t\tif ( elem === elem.ownerDocument.body ) {\n\t\t\treturn jQuery.offset.bodyOffset( elem );\n\t\t}\n\n\t\ttry {\n\t\t\tbox = elem.getBoundingClientRect();\n\t\t} catch(e) {}\n\n\t\tvar doc = elem.ownerDocument,\n\t\t\tdocElem = doc.documentElement;\n\n\t\t// Make sure we're not dealing with a disconnected DOM node\n\t\tif ( !box || !jQuery.contains( docElem, elem ) ) {\n\t\t\treturn box ? { top: box.top, left: box.left } : { top: 0, left: 0 };\n\t\t}\n\n\t\tvar body = doc.body,\n\t\t\twin = getWindow(doc),\n\t\t\tclientTop  = docElem.clientTop  || body.clientTop  || 0,\n\t\t\tclientLeft = docElem.clientLeft || body.clientLeft || 0,\n\t\t\tscrollTop  = win.pageYOffset || jQuery.support.boxModel && docElem.scrollTop  || body.scrollTop,\n\t\t\tscrollLeft = win.pageXOffset || jQuery.support.boxModel && docElem.scrollLeft || body.scrollLeft,\n\t\t\ttop  = box.top  + scrollTop  - clientTop,\n\t\t\tleft = box.left + scrollLeft - clientLeft;\n\n\t\treturn { top: top, left: left };\n\t};\n\n} else {\n\tjQuery.fn.offset = function( options ) {\n\t\tvar elem = this[0];\n\n\t\tif ( options ) {\n\t\t\treturn this.each(function( i ) {\n\t\t\t\tjQuery.offset.setOffset( this, options, i );\n\t\t\t});\n\t\t}\n\n\t\tif ( !elem || !elem.ownerDocument ) {\n\t\t\treturn null;\n\t\t}\n\n\t\tif ( elem === elem.ownerDocument.body ) {\n\t\t\treturn jQuery.offset.bodyOffset( elem );\n\t\t}\n\n\t\tvar computedStyle,\n\t\t\toffsetParent = elem.offsetParent,\n\t\t\tprevOffsetParent = elem,\n\t\t\tdoc = elem.ownerDocument,\n\t\t\tdocElem = doc.documentElement,\n\t\t\tbody = doc.body,\n\t\t\tdefaultView = doc.defaultView,\n\t\t\tprevComputedStyle = defaultView ? defaultView.getComputedStyle( elem, null ) : elem.currentStyle,\n\t\t\ttop = elem.offsetTop,\n\t\t\tleft = elem.offsetLeft;\n\n\t\twhile ( (elem = elem.parentNode) && elem !== body && elem !== docElem ) {\n\t\t\tif ( jQuery.support.fixedPosition && prevComputedStyle.position === \"fixed\" ) {\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcomputedStyle = defaultView ? defaultView.getComputedStyle(elem, null) : elem.currentStyle;\n\t\t\ttop  -= elem.scrollTop;\n\t\t\tleft -= elem.scrollLeft;\n\n\t\t\tif ( elem === offsetParent ) {\n\t\t\t\ttop  += elem.offsetTop;\n\t\t\t\tleft += elem.offsetLeft;\n\n\t\t\t\tif ( jQuery.support.doesNotAddBorder && !(jQuery.support.doesAddBorderForTableAndCells && rtable.test(elem.nodeName)) ) {\n\t\t\t\t\ttop  += parseFloat( computedStyle.borderTopWidth  ) || 0;\n\t\t\t\t\tleft += parseFloat( computedStyle.borderLeftWidth ) || 0;\n\t\t\t\t}\n\n\t\t\t\tprevOffsetParent = offsetParent;\n\t\t\t\toffsetParent = elem.offsetParent;\n\t\t\t}\n\n\t\t\tif ( jQuery.support.subtractsBorderForOverflowNotVisible && computedStyle.overflow !== \"visible\" ) {\n\t\t\t\ttop  += parseFloat( computedStyle.borderTopWidth  ) || 0;\n\t\t\t\tleft += parseFloat( computedStyle.borderLeftWidth ) || 0;\n\t\t\t}\n\n\t\t\tprevComputedStyle = computedStyle;\n\t\t}\n\n\t\tif ( prevComputedStyle.position === \"relative\" || prevComputedStyle.position === \"static\" ) {\n\t\t\ttop  += body.offsetTop;\n\t\t\tleft += body.offsetLeft;\n\t\t}\n\n\t\tif ( jQuery.support.fixedPosition && prevComputedStyle.position === \"fixed\" ) {\n\t\t\ttop  += Math.max( docElem.scrollTop, body.scrollTop );\n\t\t\tleft += Math.max( docElem.scrollLeft, body.scrollLeft );\n\t\t}\n\n\t\treturn { top: top, left: left };\n\t};\n}\n\njQuery.offset = {\n\n\tbodyOffset: function( body ) {\n\t\tvar top = body.offsetTop,\n\t\t\tleft = body.offsetLeft;\n\n\t\tif ( jQuery.support.doesNotIncludeMarginInBodyOffset ) {\n\t\t\ttop  += parseFloat( jQuery.css(body, \"marginTop\") ) || 0;\n\t\t\tleft += parseFloat( jQuery.css(body, \"marginLeft\") ) || 0;\n\t\t}\n\n\t\treturn { top: top, left: left };\n\t},\n\n\tsetOffset: function( elem, options, i ) {\n\t\tvar position = jQuery.css( elem, \"position\" );\n\n\t\t// set position first, in-case top/left are set even on static elem\n\t\tif ( position === \"static\" ) {\n\t\t\telem.style.position = \"relative\";\n\t\t}\n\n\t\tvar curElem = jQuery( elem ),\n\t\t\tcurOffset = curElem.offset(),\n\t\t\tcurCSSTop = jQuery.css( elem, \"top\" ),\n\t\t\tcurCSSLeft = jQuery.css( elem, \"left\" ),\n\t\t\tcalculatePosition = ( position === \"absolute\" || position === \"fixed\" ) && jQuery.inArray(\"auto\", [curCSSTop, curCSSLeft]) > -1,\n\t\t\tprops = {}, curPosition = {}, curTop, curLeft;\n\n\t\t// need to be able to calculate position if either top or left is auto and position is either absolute or fixed\n\t\tif ( calculatePosition ) {\n\t\t\tcurPosition = curElem.position();\n\t\t\tcurTop = curPosition.top;\n\t\t\tcurLeft = curPosition.left;\n\t\t} else {\n\t\t\tcurTop = parseFloat( curCSSTop ) || 0;\n\t\t\tcurLeft = parseFloat( curCSSLeft ) || 0;\n\t\t}\n\n\t\tif ( jQuery.isFunction( options ) ) {\n\t\t\toptions = options.call( elem, i, curOffset );\n\t\t}\n\n\t\tif ( options.top != null ) {\n\t\t\tprops.top = ( options.top - curOffset.top ) + curTop;\n\t\t}\n\t\tif ( options.left != null ) {\n\t\t\tprops.left = ( options.left - curOffset.left ) + curLeft;\n\t\t}\n\n\t\tif ( \"using\" in options ) {\n\t\t\toptions.using.call( elem, props );\n\t\t} else {\n\t\t\tcurElem.css( props );\n\t\t}\n\t}\n};\n\n\njQuery.fn.extend({\n\n\tposition: function() {\n\t\tif ( !this[0] ) {\n\t\t\treturn null;\n\t\t}\n\n\t\tvar elem = this[0],\n\n\t\t// Get *real* offsetParent\n\t\toffsetParent = this.offsetParent(),\n\n\t\t// Get correct offsets\n\t\toffset       = this.offset(),\n\t\tparentOffset = rroot.test(offsetParent[0].nodeName) ? { top: 0, left: 0 } : offsetParent.offset();\n\n\t\t// Subtract element margins\n\t\t// note: when an element has margin: auto the offsetLeft and marginLeft\n\t\t// are the same in Safari causing offset.left to incorrectly be 0\n\t\toffset.top  -= parseFloat( jQuery.css(elem, \"marginTop\") ) || 0;\n\t\toffset.left -= parseFloat( jQuery.css(elem, \"marginLeft\") ) || 0;\n\n\t\t// Add offsetParent borders\n\t\tparentOffset.top  += parseFloat( jQuery.css(offsetParent[0], \"borderTopWidth\") ) || 0;\n\t\tparentOffset.left += parseFloat( jQuery.css(offsetParent[0], \"borderLeftWidth\") ) || 0;\n\n\t\t// Subtract the two offsets\n\t\treturn {\n\t\t\ttop:  offset.top  - parentOffset.top,\n\t\t\tleft: offset.left - parentOffset.left\n\t\t};\n\t},\n\n\toffsetParent: function() {\n\t\treturn this.map(function() {\n\t\t\tvar offsetParent = this.offsetParent || document.body;\n\t\t\twhile ( offsetParent && (!rroot.test(offsetParent.nodeName) && jQuery.css(offsetParent, \"position\") === \"static\") ) {\n\t\t\t\toffsetParent = offsetParent.offsetParent;\n\t\t\t}\n\t\t\treturn offsetParent;\n\t\t});\n\t}\n});\n\n\n// Create scrollLeft and scrollTop methods\njQuery.each( [\"Left\", \"Top\"], function( i, name ) {\n\tvar method = \"scroll\" + name;\n\n\tjQuery.fn[ method ] = function( val ) {\n\t\tvar elem, win;\n\n\t\tif ( val === undefined ) {\n\t\t\telem = this[ 0 ];\n\n\t\t\tif ( !elem ) {\n\t\t\t\treturn null;\n\t\t\t}\n\n\t\t\twin = getWindow( elem );\n\n\t\t\t// Return the scroll offset\n\t\t\treturn win ? (\"pageXOffset\" in win) ? win[ i ? \"pageYOffset\" : \"pageXOffset\" ] :\n\t\t\t\tjQuery.support.boxModel && win.document.documentElement[ method ] ||\n\t\t\t\t\twin.document.body[ method ] :\n\t\t\t\telem[ method ];\n\t\t}\n\n\t\t// Set the scroll offset\n\t\treturn this.each(function() {\n\t\t\twin = getWindow( this );\n\n\t\t\tif ( win ) {\n\t\t\t\twin.scrollTo(\n\t\t\t\t\t!i ? val : jQuery( win ).scrollLeft(),\n\t\t\t\t\t i ? val : jQuery( win ).scrollTop()\n\t\t\t\t);\n\n\t\t\t} else {\n\t\t\t\tthis[ method ] = val;\n\t\t\t}\n\t\t});\n\t};\n});\n\nfunction getWindow( elem ) {\n\treturn jQuery.isWindow( elem ) ?\n\t\telem :\n\t\telem.nodeType === 9 ?\n\t\t\telem.defaultView || elem.parentWindow :\n\t\t\tfalse;\n}\n\n\n\n\n// Create width, height, innerHeight, innerWidth, outerHeight and outerWidth methods\njQuery.each([ \"Height\", \"Width\" ], function( i, name ) {\n\n\tvar type = name.toLowerCase();\n\n\t// innerHeight and innerWidth\n\tjQuery.fn[ \"inner\" + name ] = function() {\n\t\tvar elem = this[0];\n\t\treturn elem ?\n\t\t\telem.style ?\n\t\t\tparseFloat( jQuery.css( elem, type, \"padding\" ) ) :\n\t\t\tthis[ type ]() :\n\t\t\tnull;\n\t};\n\n\t// outerHeight and outerWidth\n\tjQuery.fn[ \"outer\" + name ] = function( margin ) {\n\t\tvar elem = this[0];\n\t\treturn elem ?\n\t\t\telem.style ?\n\t\t\tparseFloat( jQuery.css( elem, type, margin ? \"margin\" : \"border\" ) ) :\n\t\t\tthis[ type ]() :\n\t\t\tnull;\n\t};\n\n\tjQuery.fn[ type ] = function( size ) {\n\t\t// Get window width or height\n\t\tvar elem = this[0];\n\t\tif ( !elem ) {\n\t\t\treturn size == null ? null : this;\n\t\t}\n\n\t\tif ( jQuery.isFunction( size ) ) {\n\t\t\treturn this.each(function( i ) {\n\t\t\t\tvar self = jQuery( this );\n\t\t\t\tself[ type ]( size.call( this, i, self[ type ]() ) );\n\t\t\t});\n\t\t}\n\n\t\tif ( jQuery.isWindow( elem ) ) {\n\t\t\t// Everyone else use document.documentElement or document.body depending on Quirks vs Standards mode\n\t\t\t// 3rd condition allows Nokia support, as it supports the docElem prop but not CSS1Compat\n\t\t\tvar docElemProp = elem.document.documentElement[ \"client\" + name ],\n\t\t\t\tbody = elem.document.body;\n\t\t\treturn elem.document.compatMode === \"CSS1Compat\" && docElemProp ||\n\t\t\t\tbody && body[ \"client\" + name ] || docElemProp;\n\n\t\t// Get document width or height\n\t\t} else if ( elem.nodeType === 9 ) {\n\t\t\t// Either scroll[Width/Height] or offset[Width/Height], whichever is greater\n\t\t\treturn Math.max(\n\t\t\t\telem.documentElement[\"client\" + name],\n\t\t\t\telem.body[\"scroll\" + name], elem.documentElement[\"scroll\" + name],\n\t\t\t\telem.body[\"offset\" + name], elem.documentElement[\"offset\" + name]\n\t\t\t);\n\n\t\t// Get or set width or height on the element\n\t\t} else if ( size === undefined ) {\n\t\t\tvar orig = jQuery.css( elem, type ),\n\t\t\t\tret = parseFloat( orig );\n\n\t\t\treturn jQuery.isNumeric( ret ) ? ret : orig;\n\n\t\t// Set the width or height on the element (default to pixels if value is unitless)\n\t\t} else {\n\t\t\treturn this.css( type, typeof size === \"string\" ? size : size + \"px\" );\n\t\t}\n\t};\n\n});\n\n\n\n\n// Expose jQuery to the global object\nwindow.jQuery = window.$ = jQuery;\n\n// Expose jQuery as an AMD module, but only for AMD loaders that\n// understand the issues with loading multiple versions of jQuery\n// in a page that all might call define(). The loader will indicate\n// they have special allowances for multiple jQuery versions by\n// specifying define.amd.jQuery = true. Register as a named module,\n// since jQuery can be concatenated with other files that may use define,\n// but not use a proper concatenation script that understands anonymous\n// AMD modules. A named AMD is safest and most robust way to register.\n// Lowercase jquery is used because AMD module names are derived from\n// file names, and jQuery is normally delivered in a lowercase file name.\n// Do this after creating the global so that if an AMD module wants to call\n// noConflict to hide this version of jQuery, it will work.\nif ( typeof define === \"function\" && define.amd && define.amd.jQuery ) {\n\tdefine( \"jquery\", [], function () { return jQuery; } );\n}\n\n\n\n})( window );\n"
  },
  {
    "path": "js/lib/js.cookie.js",
    "content": "/*!\n * JavaScript Cookie v2.1.3\n * https://github.com/js-cookie/js-cookie\n *\n * Copyright 2006, 2015 Klaus Hartl & Fagner Brack\n * Released under the MIT license\n */\n;(function (factory) {\n\tvar registeredInModuleLoader = false;\n\tif (typeof define === 'function' && define.amd) {\n\t\tdefine(factory);\n\t\tregisteredInModuleLoader = true;\n\t}\n\tif (typeof exports === 'object') {\n\t\tmodule.exports = factory();\n\t\tregisteredInModuleLoader = true;\n\t}\n\tif (!registeredInModuleLoader) {\n\t\tvar OldCookies = window.Cookies;\n\t\tvar api = window.Cookies = factory();\n\t\tapi.noConflict = function () {\n\t\t\twindow.Cookies = OldCookies;\n\t\t\treturn api;\n\t\t};\n\t}\n}(function () {\n\tfunction extend () {\n\t\tvar i = 0;\n\t\tvar result = {};\n\t\tfor (; i < arguments.length; i++) {\n\t\t\tvar attributes = arguments[ i ];\n\t\t\tfor (var key in attributes) {\n\t\t\t\tresult[key] = attributes[key];\n\t\t\t}\n\t\t}\n\t\treturn result;\n\t}\n\n\tfunction init (converter) {\n\t\tfunction api (key, value, attributes) {\n\t\t\tvar result;\n\t\t\tif (typeof document === 'undefined') {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// Write\n\n\t\t\tif (arguments.length > 1) {\n\t\t\t\tattributes = extend({\n\t\t\t\t\tpath: '/'\n\t\t\t\t}, api.defaults, attributes);\n\n\t\t\t\tif (typeof attributes.expires === 'number') {\n\t\t\t\t\tvar expires = new Date();\n\t\t\t\t\texpires.setMilliseconds(expires.getMilliseconds() + attributes.expires * 864e+5);\n\t\t\t\t\tattributes.expires = expires;\n\t\t\t\t}\n\n\t\t\t\t// We're using \"expires\" because \"max-age\" is not supported by IE\n\t\t\t\tattributes.expires = attributes.expires ? attributes.expires.toUTCString() : '';\n\n\t\t\t\ttry {\n\t\t\t\t\tresult = JSON.stringify(value);\n\t\t\t\t\tif (/^[\\{\\[]/.test(result)) {\n\t\t\t\t\t\tvalue = result;\n\t\t\t\t\t}\n\t\t\t\t} catch (e) {}\n\n\t\t\t\tif (!converter.write) {\n\t\t\t\t\tvalue = encodeURIComponent(String(value))\n\t\t\t\t\t\t.replace(/%(23|24|26|2B|3A|3C|3E|3D|2F|3F|40|5B|5D|5E|60|7B|7D|7C)/g, decodeURIComponent);\n\t\t\t\t} else {\n\t\t\t\t\tvalue = converter.write(value, key);\n\t\t\t\t}\n\n\t\t\t\tkey = encodeURIComponent(String(key));\n\t\t\t\tkey = key.replace(/%(23|24|26|2B|5E|60|7C)/g, decodeURIComponent);\n\t\t\t\tkey = key.replace(/[\\(\\)]/g, escape);\n\n\t\t\t\tvar stringifiedAttributes = '';\n\n\t\t\t\tfor (var attributeName in attributes) {\n\t\t\t\t\tif (!attributes[attributeName]) {\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\tstringifiedAttributes += '; ' + attributeName;\n\t\t\t\t\tif (attributes[attributeName] === true) {\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\tstringifiedAttributes += '=' + attributes[attributeName];\n\t\t\t\t}\n\t\t\t\treturn (document.cookie = key + '=' + value + stringifiedAttributes);\n\t\t\t}\n\n\t\t\t// Read\n\n\t\t\tif (!key) {\n\t\t\t\tresult = {};\n\t\t\t}\n\n\t\t\t// To prevent the for loop in the first place assign an empty array\n\t\t\t// in case there are no cookies at all. Also prevents odd result when\n\t\t\t// calling \"get()\"\n\t\t\tvar cookies = document.cookie ? document.cookie.split('; ') : [];\n\t\t\tvar rdecode = /(%[0-9A-Z]{2})+/g;\n\t\t\tvar i = 0;\n\n\t\t\tfor (; i < cookies.length; i++) {\n\t\t\t\tvar parts = cookies[i].split('=');\n\t\t\t\tvar cookie = parts.slice(1).join('=');\n\n\t\t\t\tif (cookie.charAt(0) === '\"') {\n\t\t\t\t\tcookie = cookie.slice(1, -1);\n\t\t\t\t}\n\n\t\t\t\ttry {\n\t\t\t\t\tvar name = parts[0].replace(rdecode, decodeURIComponent);\n\t\t\t\t\tcookie = converter.read ?\n\t\t\t\t\t\tconverter.read(cookie, name) : converter(cookie, name) ||\n\t\t\t\t\t\tcookie.replace(rdecode, decodeURIComponent);\n\n\t\t\t\t\tif (this.json) {\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tcookie = JSON.parse(cookie);\n\t\t\t\t\t\t} catch (e) {}\n\t\t\t\t\t}\n\n\t\t\t\t\tif (key === name) {\n\t\t\t\t\t\tresult = cookie;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\n\t\t\t\t\tif (!key) {\n\t\t\t\t\t\tresult[name] = cookie;\n\t\t\t\t\t}\n\t\t\t\t} catch (e) {}\n\t\t\t}\n\n\t\t\treturn result;\n\t\t}\n\n\t\tapi.set = api;\n\t\tapi.get = function (key) {\n\t\t\treturn api.call(api, key);\n\t\t};\n\t\tapi.getJSON = function () {\n\t\t\treturn api.apply({\n\t\t\t\tjson: true\n\t\t\t}, [].slice.call(arguments));\n\t\t};\n\t\tapi.defaults = {};\n\n\t\tapi.remove = function (key, attributes) {\n\t\t\tapi(key, '', extend(attributes, {\n\t\t\t\texpires: -1\n\t\t\t}));\n\t\t};\n\n\t\tapi.withConverter = init;\n\n\t\treturn api;\n\t}\n\n\treturn init(function () {});\n}));\n"
  },
  {
    "path": "js/market.js",
    "content": "/**\n * Created by Gorlum on 14.10.2015.\n */\n\n/* ---- Страница выбора услуги */\n//jQuery(document).on('click', '.market_services a', function(){\n//  $(this).addClass('button_pseudo_pressed');\n//});\n\nvar eco_mrk_trader_recalc_lock = false;\n\n/* ---- Страница обмена ресурсов */\nfunction eco_mrk_trader_recalc() {\n  if(eco_mrk_trader_recalc_lock) {\n    return;\n  }\n\n  selected_resource_id = parseInt($('input:radio[name=exchangeTo]:checked').val());\n\n  var resource_increase = 0;\n  var block_exchange = false;\n  var selected_input = $('#spend' + selected_resource_id);\n  var selected_input_value = parseFloat(selected_input.val())\n  !(selected_input_value) ? selected_input_value = 0 : false;\n\n  var current_element, input_value;\n  for(var i in eco_market_resources) {\n    if(!eco_market_resources.hasOwnProperty(i)) {\n      continue;\n    }\n\n    current_element = $('#spend' + i);\n    !(input_value = parseFloat(current_element.val())) ? input_value = 0 : false;\n\n    if((selected_resource_id != RES_DARK_MATTER && i == selected_resource_id) || (selected_resource_id == RES_DARK_MATTER && i != RES_DARK_MATTER)) {\n      current_element.addClass('ok_bg');\n      jQuery('[id^=\"' + 'spend' + i +'\"]').prop('disabled', true);\n      jQuery('#' + 'spend' + i +'slide').slider('disable');\n    } else {\n      jQuery('[id^=\"' + 'spend' + i +'\"]').prop('disabled', false);\n      jQuery('#' + 'spend' + i +'slide').slider('enable');\n      if(input_value > eco_market_resources[i]['avail']) {\n        current_element.addClass('error_bg');\n        block_exchange = true;\n      } else {\n        current_element.removeClass('ok_bg').removeClass('error_bg');\n      }\n    }\n\n    if(selected_resource_id == RES_DARK_MATTER && i != RES_DARK_MATTER) {\n      input_value = - parseFloat(selected_input_value / 3 / eco_market_resources[i]['rate'] * eco_market_resources[selected_resource_id]['rate']);\n    }\n\n    if(selected_resource_id != RES_DARK_MATTER && i != selected_resource_id) {\n      resource_increase += Math.floor(parseFloat(input_value * eco_market_resources[i]['rate'] / eco_market_resources[selected_resource_id]['rate']));\n    }\n    input_value = Math.floor(input_value);\n    $('#res_left' + i).html(sn_format_number(eco_market_resources[i]['avail'] - input_value));\n    $('#res_delta' + i).html(sn_format_number(-input_value, 0, 'positive', 0, true));\n    current_element.val(Math.abs(input_value));\n  }\n\n  if(selected_resource_id == RES_DARK_MATTER) {\n    $('#res_delta' + selected_resource_id).html(sn_format_number(-selected_input_value, 0, 'positive', 0, true));\n    $('#res_left' + selected_resource_id).html(sn_format_number(eco_market_resources[selected_resource_id]['avail'] - selected_input_value));\n    block_exchange = block_exchange || !selected_input_value;\n  } else {\n    selected_input.val(resource_increase);\n    $('#res_delta' + selected_resource_id).html(sn_format_number(resource_increase, 0, 'positive', 0, true));\n    $('#res_left' + selected_resource_id).html(sn_format_number(eco_market_resources[selected_resource_id]['avail'] + resource_increase));\n    block_exchange = block_exchange || !resource_increase;\n  }\n  $('#submit_trade').prop('disabled', block_exchange);\n}\n\nfunction eco_mrk_trader_recourse(selected_resource) {\n  var operation_cost, current_rate;\n\n  selected_resource = parseInt(selected_resource);\n\n  $('#market_trader').find('.button_pseudo').removeClass('button_pseudo_pressed');\n  $('[resource_id=' + selected_resource + ']').addClass('button_pseudo_pressed');\n  $('input:radio[name=exchangeTo]').val([selected_resource]);\n\n  var rate_for_selected = eco_market_resources[selected_resource]['rate'];\n\n  eco_mrk_trader_recalc_lock = true;\n\n  for(var i in eco_market_resources) {\n    if(!eco_market_resources.hasOwnProperty(i)) {\n      continue;\n    }\n\n    current_rate = eco_market_resources[i]['rate'] / rate_for_selected;\n    selected_resource == RES_DARK_MATTER ? current_rate = \"1 : \" + (1 / current_rate) : (current_rate += \" : 1\");\n    $('#course' + i).html(current_rate);\n    jQuery('#' + 'spend' + i +'slide').slider(\"value\", 0);\n  }\n  operation_cost = C_rpg_cost_trader * (selected_resource == RES_DARK_MATTER ? 3 : 1);\n  eco_market_resources[RES_DARK_MATTER]['avail'] = eco_market_resources[RES_DARK_MATTER]['start'] - operation_cost;\n  $('#rpg_cost_trader').html(operation_cost);\n\n  eco_mrk_trader_recalc_lock = false;\n\n  eco_mrk_trader_recalc();\n}\n\n$(document).on('click', '#submit_trade', function(){\n  var confirm = true, dm_amount;\n\n  if(dm_amount = parseFloat(jQuery('#spend' + RES_DARK_MATTER).val())) {\n    confirm = window.confirm(language['LA_eco_mrk_trader_exchange_dm_confirm'].format(sn_format_number(dm_amount)));\n  }\n\n  if(confirm) {\n    $(this).prop('disabled', true);\n    $('#form_trade').submit();\n  }\n  return confirm;\n});\n\njQuery(document).on('click', '#market_trader .button_pseudo', function() {\n  eco_mrk_trader_recourse($(this).attr('resource_id'));\n});\n\njQuery(document).ready(function() {\n  // Запускается только на странице обмена ресурсов\n  if($('#market_trader').length) {\n    eco_mrk_trader_recourse(exchange_to_resource_id ? exchange_to_resource_id : RES_METAL);\n  }\n});\n"
  },
  {
    "path": "js/metamatter.js",
    "content": "jQuery(document).ready(function() {\n  // Натягиваем скины на элементы ввода\n  jQuery('.payment_block_item:not(.payment_mm_method_nobutton)').button().addClass('ui-textfield');\n\n  jQuery(document)\n    .on('keyup change', \"#metamatter\", function(event, ui) {\n      value = (value = parseInt(jQuery(this).val())) ? value : 0;\n      jQuery(this).val(value);\n\n      current_discount = 1;\n      for(i in mm_discounts) {\n        if(value >= mm_discounts[i]['sum']) {\n          current_discount = mm_discounts[i]['discount'];\n        }\n      }\n      jQuery(\"#metamatter_total\").html(sn_format_number(Math.floor(value * current_discount)));\n      if(current_discount > 1) {\n        jQuery(\"#metamatter_undiscounted\").show().html(sn_format_number(value));\n        jQuery(\"#metamatter_bonus_percent\").show().find('span').html(Math.round(current_discount * 100 - 100));\n      } else {\n        jQuery(\"#metamatter_undiscounted\").hide();\n        jQuery(\"#metamatter_bonus_percent\").hide();\n      }\n\n      jQuery(\"#metamatter_price\").html(sn_format_number(Math.ceil(value / exchange_mm_default * 100) / 100, 2));\n    })\n    .on('focus', \"#metamatter\", function(event, ui) {\n      this.value == '0' ? this.value='' : false;\n      this.select();\n    })\n    .on('blur', \"#metamatter\", function(event, ui) {\n      that = jQuery(this);\n      that.val(parseInt(that.val()) ? that.val() : 0);\n    })\n\n    .on('change', '#player_currency',function(){\n      sn_redirect('metamatter.php?player_currency=' + $(this).val());\n    })\n\n    .on(\"click\", \".js_payment_mm_amount\", function(){\n      jQuery(\"#metamatter\").val(jQuery(this).attr(\"value\")).change();\n      jQuery(\"#payment_form\").submit();\n    })\n    .on(\"click\", '.js_payment_mm_method', function(){\n      jQuery(\"#payment_method\").val(jQuery(this).attr(\"value\"));\n      jQuery(\"#payment_form\").submit();\n    })\n    .on(\"click\", '.js_payment_mm_module', function(){\n      jQuery(\"#payment_module\").val(jQuery(this).attr(\"value\"));\n      jQuery(\"#payment_form\").submit();\n    })\n\n    .on('click', '.js_show_hidden_methods', function () {\n      sn_show_hide2(this, '.js_hidden_payments_' + $(this).attr('method_id'), 1);\n      return false;\n    })\n    .on('click', '.js_show_dm_info', function () {\n      sn_show_hide2(this, '#dark_matter_what_it_is, #metamatter_what_description');\n      return false;\n    })\n  ;\n});\n"
  },
  {
    "path": "js/planet_manage.js",
    "content": "// Planet overview - Manage\n\n$(function () {\n  $(\"#dialog-rename-planet\").dialog({\n    autoOpen: false,\n    resizable: false,\n    modal: true,\n    open: function () {\n      var element = $(this).parent();\n      element.find('.ui-dialog-titlebar').css('display', 'block');\n    },\n    buttons: [\n      {\n        text: language['ov_rename'],\n        click: function () {\n          var element = $(this).parent();\n          element.find('input[name=cp]').val($('.js_navbar_planet_select').find('option:selected').val());\n          element.find(':button:not(.ui-dialog-titlebar-close)').button('disable');\n          jQuery('#dialog-rename-planet-form').submit();\n        }\n      },\n      {\n        text: language['sys_cancel'],\n        click: function () {\n          $(this).dialog(\"close\");\n        }\n      }\n    ]\n  });\n});\n\n$(document)\n  .on('click', \"#planet_rename\", function () {\n    $('#dialog-rename-planet').dialog('open');\n  })\n\n  .on('change', \"#density_type\", function () {\n    selected = jQuery(\"#density_type\").find(\"option:selected\");\n    $(\"#transmute_button\").button({disabled: parseInt(selected.attr(\"rest\")) <= 0 || selected.attr(\"current\") == '1'});\n    $(\"#transmutation_cost\").html(selected.attr(\"html\")).removeClass().addClass(selected.attr(\"html_class\"));\n  })\n\n  .on('click', '#planet_make_capital', function (e) {\n    if (!$(this).attr('disabled')) {\n      snConfirm({\n        that: $(this),\n      });\n    }\n    e.preventDefault();\n    return false;\n  })\n\n  .on('submit', '#planet_teleport_form, #planet_abandon_form', function (e) {\n    if ($(this).prop('submitted')) {\n      return true;\n    }\n    if (!$(this).attr('disabled')) {\n      snConfirm({\n        that: $(this),\n      });\n    }\n    e.preventDefault();\n    return false;\n  });\n"
  },
  {
    "path": "js/sn_ajax_send_fleet.js",
    "content": "function uni_set_ships(ship_data)\n{\n  ship_data = ship_data.split(\",\");\n  for(ship_data_id in ship_data)\n  {\n    single_ship_data = ship_data[ship_data_id].split('=');\n    jQuery('#' + single_ship_data[0]).html(single_ship_data[1]);\n    if(single_ship_data[0] == 'missile')\n    {\n      jQuery('#SendMI').val(single_ship_data[1]);\n      jQuery('#missile2').html(single_ship_data[1]);\n    }\n  }\n}\n\nfunction addToTable(strInfo, CmdCode)\n{\n  var e = document.getElementById('fleetstatusrow');\n  if(!e)\n  {\n    return;\n  }\n  e.style.display = '';\n\n  var e2 = document.getElementById('fleetstatustable');\n  if(e2.rows.length > 2)\n  {\n    e2.deleteRow(2);\n  }\n\n  var td1 = document.createElement(\"td\");\n  td1.appendChild(document.createTextNode(strInfo));\n\n  var row = e2.insertRow(0);\n  row.className = CmdCode ? \"error\" : \"success\";\n  row.appendChild(td1);\n\n  jQuery('#ov_recycle').hide();\n}\n\nfunction doit(missionId, planet, planettype, shipcount) {\n  var uni_send_fleet_params = {\n    \"mission\": missionId,\n    \"galaxy\": uni_galaxy,\n    \"system\": uni_system,\n    \"planet\": planet,\n    \"planet_type\": planettype,\n    \"fleet\": Array(),\n  };\n\n  if(missionId == MT_MISSILE) {\n    jQuery.extend(uni_send_fleet_params,{\n      \"missiles\": document.uni_missile_form.SendMI.value,\n      \"structures\": document.uni_missile_form.Target.value,\n    });\n  }\n\n  jQuery.post(\"flotenajax.php?action=send\", uni_send_fleet_params, function(data) {\n    if(data.indexOf('|') === -1) {\n      addToTable(data, 1);\n      popup_show($('#message_template').html().replace(/\\[MESSAGE\\]/g, data).replace(/\\[CLASS\\]/g, 'error_bg'));\n      return;\n    }\n    retVals   = data.split(\"|\");\n    strInfo   = retVals[0];\n    addToTable(retVals[0], 0);\n    uni_set_ships(retVals[1]);\n    popup_show($('#message_template').html().replace(/\\[MESSAGE\\]/g, retVals[0]).replace(/\\[CLASS\\]/g, 'ok_bg'));\n  });\n}\n"
  },
  {
    "path": "js/sn_global.js",
    "content": "if (typeof(window.LOADED_GLOBAL) === 'undefined') {\n\n  /*global sn_inframe:true LOADED_GLOBAL:true*/\n  /*eslint no-undef: \"error\"*/\n  var LOADED_GLOBAL = true;\n\n  // Constants\n  var CLASS_POSITIVE = \"positive\";\n\n\n  // Localization class\n  var LanguageObject = function() {\n  };\n  LanguageObject.prototype = {\n    /**\n     * Add list of locale strings to current locale\n     *\n     * @param {object} strings\n     */\n    addLocale: function (strings) {\n      jQuery.extend(this, strings);\n    }\n  };\n  var language = new LanguageObject();\n\n  var x = \"\";\n  var e = null;\n  var sn_inframe;\n\n  // Fix to jQuery-UI improper tab handling\n  $.fn.__tabs = $.fn.tabs;\n  $.fn.tabs = function (a, b, c, d, e, f) {\n    var base = window.location.href.replace(/#.*$/, '');\n    $('ul > li > a[href^=\"#\"]', this).each(function () {\n      var href = $(this).attr('href');\n      $(this).attr('href', base + href);\n    });\n    return $(this).__tabs(a, b, c, d, e, f);\n  };\n\n  /**\n   * Converts value to integer\n   *\n   * @param value\n   * @returns {int}\n   */\n  Math.intVal = function (value) {\n    var parsed = parseInt(value, 10);\n    return parsed ? parsed : 0;\n    // return typeof parsed === 'number' && !isNaN(parsed) && parsed !== Infinity ? parsed : 0;\n  };\n\n  /**\n   * Converts value to float\n   *\n   * @param value\n   * @returns {float}\n   */\n  Math.floatVal = function (value) {\n    var parsed = parseFloat(value);\n    return parsed ? parsed : 0;\n  };\n\n  Math.roundVal = function (value) {\n    return Math.round(Math.floatVal(value));\n  };\n  Math.floorVal = function (value) {\n    return Math.floor(Math.floatVal(value));\n  };\n  Math.ceilVal = function (value) {\n    return Math.ceil(Math.floatVal(value));\n  };\n\n  /**\n   * Вычисление процента с масштабированием (улучшенной точностью)\n   *\n   * @param {number} number1 - первое число\n   * @param {number} number2 - второе число\n   * @param {number} scale - масштаб/точность. Например, при scale = 10 - точность будет 0.1%\n   * @returns {number}\n   */\n  Math.percent = function (number1, number2, scale) {\n    //!(scale = Math.round(scale)) ? scale = 1 : false;\n    !scale ? scale = 1 : false;\n    return number2 ? Math.round((number1 / number2 * 100 * scale) / scale) : 0;\n  };\n  /**\n   * Ширина в процентных пикселях\n   *\n   * @param {number} number1 - первое число\n   * @param {number} number2 - второе число\n   * @param {number} pixels - оригинальная ширина в пикселях. Если отрицательная - возвращается дополнение до ширины (pixels - %pixels)\n   * @returns {number|string}\n   */\n  Math.percentPixels = function (number1, number2, pixels) {\n    return number2 ? Math.round((pixels < 0 ? -pixels : 0) + (number1 / number2 * pixels)) + 'px' : 0;\n  };\n\n\n  /**\n   * Gets name of supplied frame\n   *\n   * @param frame\n   * @returns {*}\n   */\n  function getFrameName(frame) {\n    for (var i = 0; i < parent.frames.length; i++) {\n      if (parent.frames[i] === frame) {\n        return (parent.frames[i].name); //\n      }\n    }\n  }\n  sn_inframe = window.frameElement ? getFrameName(self) : false;\n\n  function sn_blink(that) {\n    that = $(that);\n    that.animate(\n      {opacity: that.css('opacity') == 0 ? 1 : 0},\n      that.attr('duration') ? parseInt(that.attr('duration'), 10) : 1000,\n      function () {sn_blink(this)}\n    );\n  }\n\n  /**\n   * Delays function execution\n   *\n   * @param func\n   * @param wait\n   * @returns {number}\n   */\n  var sn_delay = function (func, wait) {\n    var args = Array.prototype.slice.call(arguments, 2);\n    return setTimeout(function () {\n      return func.apply(null, args);\n    }, wait);\n  };\n\n  /**\n   * Formats string, replacing {0}, {1}, {2} etc with zero, first, second etc param\n   *\n   * @returns {string}\n   */\n  String.prototype.format = function () {\n    var args = arguments;\n    return this.replace(/\\{\\{|\\}\\}|\\{(\\d+)\\}/g, function (m, n) {\n      if (m == \"{{\") {\n        return \"{\";\n      }\n      if (m == \"}}\") {\n        return \"}\";\n      }\n      return args[n];\n    });\n  };\n\n  /**\n   * Skins input elements\n   */\n  function skinInputs() {\n    var inputs = jQuery(\"input:not(.do-not-skin):not(.do-not-skin-child *),button:not(.do-not-skin *):not(.do-not-skin-child *)\");\n    inputs.filter(':button, :submit, :reset').button(); // .addClass('ui-textfield');\n    inputs.filter(':text, :password, :file').button().addClass('ui-textfield ui-input-text').off('keydown');\n    // inputs.filter(':checkbox, :radio').addClass(\"ui-corner-all ui-state-default ui-textfield\");\n    inputs.filter(':radio').addClass(\"ui-corner-all ui-state-default ui-textfield\");\n    jQuery(\"button:not(.do-not-skin):not(.do-not-skin-child *)\").button().addClass('ui-textfield');\n    // jQuery('textarea:not(#ally_text):not(.do-not-skin):not(.do-not-skin-child *)').button().addClass('ui-textfield ui-input-text').off('keydown');\n\n    //inputs.filter(':checkbox, :radio').checkator();\n  }\n\n  function makeBlink() {\n    if (!jQuery.fx.off) {\n      jQuery('.blink').each(function () {\n        sn_blink(this);\n      });\n    }\n  }\n\n  //jQuery(document).ready(function () {\n  // Нельзя полагаться на document.ready() из-за возможных проблем с загрузкой скриптов со сторонних серверов!\n  function document_ready() {\n    var theBody = $('body');\n\n    // Натягиваем скины на элементы ввода\n    skinInputs();\n\n    // Запуск таймеров\n    if (typeof(sn_timer) === 'function') {\n      sn_timer();\n    }\n\n    // It's here 'cause font manipulation is available only after DOM fully loaded\n    $(document).on('click', '#font_minus, #font_normal, #font_plus', function () {\n      var temp = snFont.size;\n\n      switch ($(this).attr('id')) {\n        case 'font_plus':\n          snFont.size += snFont.step;\n          break;\n        case 'font_minus':\n          snFont.size -= snFont.step;\n          break;\n        default:\n          snFont.size = snFont.sizeDefaultPercent;\n          break;\n      }\n\n      snFont.size = Math.min(snFont.size, snFont.max);\n      snFont.size = Math.max(snFont.size, snFont.min);\n\n      var new_size = Math.floatVal(theBody.css('font-size')) * snFont.size / temp;\n\n      if (temp != snFont.size) {\n        theBody.css('font-size', new_size + 'px');\n      }\n\n      jQuery.post(\"time_probe.php\", {'font_size': snFont.size + '%'}, function (data) {\n      });\n    });\n\n    makeBlink();\n  }\n\n  $(document).on('click', '.password_show', function () {\n    var button_value, input, type;\n    input = $(this).parent().find(\"[name=\" + $(this).attr('show_element') + \"]\").hide();\n    if (input.attr('type') == 'password') {\n      type = 'text';\n      button_value = language['sys_login_password_hide'];\n    }\n    else {\n      type = 'password';\n      button_value = language['sys_login_password_show'];\n    }\n\n    var rep = $('<input type=\"' + type + '\" maxlength=\"32\" />').attr('name', input.attr('name')).attr(\"class\", input.attr(\"class\")).val(input.val()).insertBefore(input);\n    rep.attr(\"id\", input.attr(\"id\"));\n\n    input.remove();\n\n    $(this).attr('value', button_value);\n  });\n\n  function sn_redirect(url) {\n    document.location.assign(url);\n  }\n\n  function openInNewTab(url) {\n    var win = window.open(url, '_blank');\n    win.focus();\n  }\n\n  function sn_reload() {\n    location.reload();\n  }\n\n  jQuery(document).on('click', '[news_toggle]', function () {\n    $('#news_' + $(this).attr('news_toggle')).show();\n    $(this).remove();\n  });\n\n  jQuery(document).on('click', '.survey_block [survey_id]', function () {\n    var survey_id = $(this).attr('survey_id');\n    $('.survey_block [survey_id=' + survey_id + ']').removeClass('button_pseudo_pressed');\n    $(this).addClass('button_pseudo_pressed');\n    $('input:radio[name=\"survey[' + survey_id + ']\"]').val([$(this).attr('answer_id')]);\n  });\n\n  jQuery(document).on('click', '.button_pseudo', function () {\n    $(this).addClass('button_pseudo_pressed');\n  });\n\n  jQuery(document).on('change', '#sort_elements,#sort_elements_inverse', function () {\n    jQuery.post(\n      $('#page_file_name').val()\n      + '?mode=' + $('#mode').val()\n      + '&sort_elements=' + $('#sort_elements').val()\n      + '&sort_elements_inverse=' + ($('#sort_elements_inverse').is(':checked') ? '1' : '0'),\n      function () {\n        sn_reload();\n      }\n    );\n  });\n\n  /* CHAT_ADVANCED specific */\n  jQuery(document).on('click', '.player_nick_award', function () {\n    document.location.assign(\"index.php?page=imperator&int_user_id=\" + jQuery(this).attr('player_id'));\n  });\n  jQuery(document).on('click', '.player_nick_race', function () {\n    document.location.assign(\"index.php?page=races\");\n  });\n\n  jQuery(document).on('click', 'button[href]', function (e) {\n    sn_redirect($(this).attr('href'));\n  });\n\n  /* Empire ------------------------------------------------------------------------------------------ */\n  jQuery(document).on('change', \"#empire_overview select[selector]\", function () {\n    if (jQuery(this).val() != '-') {\n      jQuery(\"select[name^='percent[\" + jQuery(this).attr('selector') + \"][']\").val(jQuery(this).val());\n    }\n  });\n\n  // Хэндлеры для слайдеров\n  jQuery(document).on('click', \"input:button[id$='_ai_zero']\", function (event, ui) {\n    jQuery(\"#\" + jQuery(this).attr('parent_id')).val(0).trigger('change', [event, ui]);\n  });\n  jQuery(document).on('click', \"input:button[id$='ai_max']\", function (event, ui) {\n    var field_name = '#' + jQuery(this).attr('parent_id');\n    jQuery(field_name).val(parseInt(jQuery(field_name + 'slide').slider(\"option\", \"max\"))).trigger('change', [event, ui]);\n  });\n  jQuery(document)\n    .on('mousedown', \"input:button[id$='ai_dec'],input:button[id$='ai_inc']\", function (event, ui) {\n      var that = jQuery(this);\n      var parentIEFix = jQuery('#' + that.attr('parent_id'));\n      if (parentIEFix.is('[disabled]') || parentIEFix.is('[slider_ticks]')) {\n        return;\n      }\n\n      var slider = jQuery(\"#\" + that.attr('parent_id') + 'slide');\n\n      parentIEFix.attr('slider_ticks', 0)\n        .attr('step_now', slider.slider('option', 'step'))\n        .attr('increase', that.attr('id') == parentIEFix.attr('id') + '_ai_inc' ? 1 : -1);\n      sn_ainput_mouselerate_jquery();\n    })\n    .on('mouseup', \"input:button[id$='ai_dec'],input:button[id$='ai_inc']\", function (event, ui) {\n      var parentIEFix = jQuery('#' + jQuery(this).attr('parent_id'));\n      clearTimeout(parentIEFix.attr('timeout'));\n      parentIEFix.removeAttr('slider_ticks').removeAttr('step_now').removeAttr('increase').removeAttr('timeout');\n    })\n  ;\n  jQuery(document)\n    .on('keyup change', \"[ainput]\", function (event, ui) {\n      if (ui != undefined && ui.type == 'slidechange') {\n        return;\n      }\n\n      var slider, value, min_slide, max_slide;\n\n      slider = jQuery('#' + jQuery(this).attr('id') + 'slide');\n      value = (value = parseInt(jQuery(this).val())) ? value : 0;\n      value = value > (max_slide = parseInt(slider.slider(\"option\", \"max\"))) ? max_slide :\n        (value < (min_slide = parseInt(slider.slider(\"option\", \"min\"))) ? min_slide : value);\n\n      jQuery(this).val(value);\n      slider.slider(\"value\", value);\n    })\n    .on('focus', \"[ainput]\", function (event, ui) {\n      if (this.value == '0') this.value = '';\n      this.select();\n    })\n    .on('blur', \"[ainput]\", function (event, ui) {\n      var that = jQuery(this);\n      that.val(parseInt(that.val()) ? that.val() : 0);\n    })\n  ;\n// Спецхэндлер - если мышку отпустят за пределом элемента\n  jQuery(document).on('mouseup', function (event, ui) {\n    jQuery('[slider_ticks]').each(function () {\n      clearTimeout(jQuery(this).attr('timeout'));\n      jQuery(this).removeAttr('slider_ticks').removeAttr('step_now').removeAttr('increase').removeAttr('timeout');\n    });\n    // TODO - Код для старых слайдеров. Убрать, когда все старые слайдеры не будут использоваться\n    if (accelerated) {\n      clearTimeout(accelerated['timeout']);\n      accelerated = undefined;\n    }\n  });\n\n// Хэндлеры других специальных элементов\n// Элементы редиректа\n  jQuery(document).on('click', \"[go_url]\", function () {\n    if (jQuery(this).attr('target') == '_blank') {\n      window.open(jQuery(this).attr('go_url'), '_blank');\n    } else {\n      document.location = jQuery(this).attr('go_url');\n    }\n  });\n\n  function attr_on_me_or_parent(that, attr) {\n    return parseInt(jQuery(that).attr(attr) ? jQuery(that).attr(attr) : jQuery(that).parent().attr(attr));\n  }\n\n  jQuery(document).on('click', \"[go]\", function () {\n    var location = [], planet_id, mode, unit_id;\n    var planet_system, planet_planet, planet_type, mission;\n\n    var target = jQuery(this).attr('target');\n    var page = jQuery(this).attr('go');\n\n    switch (page) {\n      case 'info':\n        page = 'infos';\n        break;\n      case 'flying':\n        page = 'flying_fleets';\n        break;\n      case 'phalanx':\n      case 'fleet':\n        window.uni_galaxy ? location.push('galaxy=' + window.uni_galaxy) : false;\n        window.uni_system ? location.push('system=' + window.uni_system) : false;\n        (planet_planet = attr_on_me_or_parent(this, 'planet_planet')) ? location.push('planet=' + planet_planet) : false;\n        (planet_type = attr_on_me_or_parent(this, 'planet_type')) ? location.push('planettype=' + planet_type) : false;\n        (mission = attr_on_me_or_parent(this, 'mission')) ? location.push('target_mission=' + mission) : false;\n        break;\n      case 'galaxy':\n        window.uni_galaxy ? location.push('galaxy=' + window.uni_galaxy) : false;\n        (planet_system = attr_on_me_or_parent(this, 'planet_system')) ? location.push('system=' + planet_system) : false;\n        break;\n      case 'build':\n        page = 'buildings';\n        break;\n      case 'res':\n        page = 'resources';\n        break;\n      case 'stat':\n        (mode = attr_on_me_or_parent(this, 'who')) ? location.push('who=' + mode) : false;\n        location.push('range=' + attr_on_me_or_parent(this, 'rank') + '#' + attr_on_me_or_parent(this, 'rank'));\n        break;\n      case 'imperator':\n        page = 'index';\n        location.push('page=imperator');\n        location.push('int_user_id=' + attr_on_me_or_parent(this, 'user_id'));\n        break;\n      case 'messages':\n        location.push('id=' + attr_on_me_or_parent(this, 'user_id'));\n        break;\n      case 'buddy':\n        location.push('request_user_id=' + attr_on_me_or_parent(this, 'user_id'));\n        break;\n      case 'alliance':\n        location.push('a=' + attr_on_me_or_parent(this, 'ally_id'));\n        break;\n      default:\n        page = 'overview';\n    }\n    (planet_id = attr_on_me_or_parent(this, 'planet_id')) ? location.push('cp=' + planet_id) : false;\n    (mode = jQuery(this).attr('mode')) ? location.push('mode=' + mode) : false;\n    (unit_id = attr_on_me_or_parent(this, 'unit_id')) ? location.push('gid=' + unit_id) : false;\n\n    unit_id && window.ALLY_ID !== undefined && parseInt(ALLY_ID) ? location.push('ally_id=' + ALLY_ID) : false;\n\n    location = page + '.php?' + location.join('&');\n\n    target ? window.open(location, target) : (document.location = location);\n    $(this).removeClass('button_pseudo_pressed');\n  });\n\n\n  // Сбор ресурсов\n  jQuery(document).on('click', \".unit_preview .icon_gather\", function () {\n    var that = $(this);\n    document.location = 'fleet.php?fleet_page=5' + (typeof PLANET_ID !== 'undefined' && parseInt(PLANET_ID) ? '&cp=' + parseInt(PLANET_ID) : '')\n      + (parseFloat(that.attr('metal')) ? '&metal=' + parseFloat(that.attr('metal')) : '')\n      + (parseFloat(that.attr('crystal')) ? '&crystal=' + parseFloat(that.attr('crystal')) : '')\n      + (parseFloat(that.attr('deuterium')) ? '&deuterium=' + parseFloat(that.attr('deuterium')) : '')\n    ;\n  });\n\n  function sn_ainput_mouselerate_jquery() {\n    jQuery('[slider_ticks]').each(function () {\n      var val, option_min, option_max, ticks;\n\n      var that = jQuery(this);\n      var slider = jQuery(\"#\" + that.attr('id') + 'slide');\n      val = (val = parseInt(that.val())) ? val : 0;\n      var step_now = parseInt(that.attr('step_now'));\n      var val_next = val + step_now * parseInt(that.attr('increase'));\n      val_next = val_next > (option_min = parseInt(slider.slider(\"option\", \"min\"))) ? val_next : option_min;\n      val_next = val_next < (option_max = parseInt(slider.slider(\"option\", \"max\"))) ? val_next : option_max;\n\n      if (val != val_next) {\n        that.val(val_next).trigger('change');\n\n        that\n          .attr('slider_ticks', ticks = parseInt(that.attr('slider_ticks')) + 1)\n          .attr('step_now', Math.round(step_now * (ticks % 4 == 0 ? 3 : 1)));\n        that.attr('timeout', window.setTimeout(sn_ainput_mouselerate_jquery, ticks == 1 ? 700 : 250));\n      }\n    });\n  }\n\n  function sn_ainput_make_jquery(field_name, options) {\n    jQuery('ainput').each(function () {\n      var step_value, start_value, min_value, max_value;\n\n      var col_span = 3;\n\n      var old = jQuery(this);\n      //min_value = (min_value = old.attr('min')) ? min_value : 0;\n      //max_value = (max_value = old.attr('max')) ? max_value : 0;\n      step_value = (step_value = old.attr('step')) ? step_value : 1; // TODO НЕ РАБОТАЕТ ИСПРАВИТЬ\n      start_value = (start_value = old.val()) ? start_value : 0; // TODO НЕ РАБОТАЕТ ИСПРАВИТЬ\n\n      var field_name_orig = old.attr('name');\n\n      field_name = field_name_orig.replace('[', '').replace(']', '');\n      var field_id = '#' + field_name;\n\n      var slider_name = field_name + 'slide';\n      var slider_id = \"#\" + slider_name;\n\n      var new_element = '<table width=\"100%\" class=\"markup\">'; // main container - sets width\n      new_element += '<tr>';\n      if (!old.is('[disable_min]')) {\n        new_element += '<td width=\"3em\"><input type=\"button\" value=\"0\" parent_id=\"' + field_name + '\" id=\"' + field_name + '_ai_zero\" style=\"width: 3em\"></td>';\n        col_span++;\n      }\n      new_element += '<td width=\"3em\"><input type=\"button\" value=\"-\" parent_id=\"' + field_name + '\" id=\"' + field_name + '_ai_dec\" style=\"width: 3em\"></td>'; // style=\"width: 6px\"\n      //new_element += '<td><input type=\"text\" ainput=\"true\" value=\"0\" id=\"' + field_name + '\" style=\"width: ' + '100%' + '; margin: 0em 5em 0em 0em; padding: 0em 5em 0em -20em\" name=\"' + field_name_orig + '\" /></td>'; // onfocus=\"if(this.value == \\'0\\') this.value=\\'\\';\" onblur=\"if(this.value == \\'\\') this.value=\\'0\\';\"\n      new_element += '<td style=\"padding-right: 2.25em;\"><input type=\"text\" ainput=\"true\" value=\"0\" id=\"' + field_name + '\" style=\"width: ' + '100%' + ';\" name=\"' + field_name_orig + '\" /></td>'; // onfocus=\"if(this.value == \\'0\\') this.value=\\'\\';\" onblur=\"if(this.value == \\'\\') this.value=\\'0\\';\"\n      new_element += '<td width=\"3em\"><input type=\"button\" value=\"+\" parent_id=\"' + field_name + '\" id=\"' + field_name + '_ai_inc\"  style=\"width: 3em\"></td>';\n      if (!old.is('[disable_max]')) {\n        new_element += '<td width=\"3em\"><input type=\"button\" value=\"M\" parent_id=\"' + field_name + '\" id=\"' + field_name + '_ai_max\"  style=\"width: 3em\"></td>';\n        col_span++;\n      }\n      new_element += '</tr>';\n      new_element += '<tr><td colspan=\"' + col_span + '\"><div parent_id=\"' + field_name + '\" id=\"' + slider_name + '\"></div></td></tr>'; // slider container\n      new_element += '</table>'; // main container\n\n      jQuery(new_element).insertBefore(old);\n      old.remove();\n\n      jQuery(\"#\" + field_name).val(start_value);\n\n      jQuery(slider_id).slider({\n        'range': \"min\",\n        'value': start_value,\n        'min': (min_value = old.attr('min')) ? min_value : 0,\n        'max': (max_value = old.attr('max')) ? max_value : 0,\n        'step': 1, // step_value,\n        slide: function (event, ui) {\n          jQuery(\"#\" + jQuery(this).attr('parent_id')).val(ui.value).trigger('change', [event, ui]);\n        },\n        change: function (event, ui) {\n          jQuery(\"#\" + jQuery(this).attr('parent_id')).val(ui.value).trigger('change', [event, ui]);\n        }\n      });\n    });\n  }\n\n\n  var accelerated;\n\n  function sn_ainput_make(field_name, options) {\n    var min_value = options['min'] ? options['min'] : 0;\n    var max_value = options['max'] ? options['max'] : 0;\n    var step_value = options['step'] ? options['step'] : 1;\n    var start_value = options['value'] ? options['value'] : min_value;\n    var col_span = 3;\n\n    var field_name_orig = field_name;\n\n    field_name = field_name.replace('[', '').replace(']', '');\n\n    var slider_id = \"#\" + field_name + 'slide';\n\n    document.write('<table width=\"100%\" class=\"markup\">'); // main container - sets width\n    document.write('<tr>');\n    if (options['button_zero']) {\n      document.write('<td><input type=\"button\" value=\"0\" id=\"' + field_name + 'zero\" style=\"max-width: 31px\"></td>'); //\n      col_span++;\n    }\n    document.write('<td><input type=\"button\" value=\"-\" id=\"' + field_name + 'dec\" style=\"max-width: 31px\"></td>'); // style=\"width: 6px\"\n    document.write('<td><input type=\"text\" value=\"0\" id=\"' + field_name + '\" style=\"width: ' + '80%' + ';\" name=\"' + field_name_orig + '\" onfocus=\"if(this.value == \\'0\\') this.value=\\'\\';\" onblur=\"if(this.value == \\'\\') this.value=\\'0\\';\"/></td>');\n    document.write('<td><input type=\"button\" value=\"+\" id=\"' + field_name + 'inc\"  style=\"max-width: 31px\"></td>');\n    if (options['button_max']) {\n      document.write('<td><input type=\"button\" value=\"M\" id=\"' + field_name + 'max\"  style=\"max-width: 31px\"></td>');\n      col_span++;\n    }\n    document.write('</tr>');\n    document.write('<tr><td colspan=\"' + col_span + '\"><div id=\"' + field_name + 'slide\"></div></td></tr>'); // slider container\n    document.write('</table>'); // main container\n\n\n    jQuery(slider_id).slider(\n      {\n        range: \"min\",\n        value: start_value,\n        min: min_value,\n        max: max_value,\n        step: 1,\n        slide: function (event, ui) {\n          jQuery(\"#\" + field_name).val(ui.value).trigger('change', [event, ui]);\n        },\n        change: function (event, ui) {\n          jQuery(\"#\" + field_name).val(ui.value).trigger('change', [event, ui]);\n        }\n      });\n    jQuery(\"#\" + field_name).val(jQuery(slider_id).slider(\"value\"));\n\n    jQuery(\"#\" + field_name).on('keyup change', function (event, ui) {\n      if (ui != undefined && ui.type == 'slidechange') {\n        return;\n      }\n\n      value = parseInt(jQuery(this).val());\n      value = value ? value : 0;\n\n      slider = jQuery(slider_id);\n\n      if (value > parseInt(slider.slider(\"option\", \"max\"))) {\n        jQuery(this).val(slider.slider(\"option\", \"max\"));\n      }\n\n      if (value < parseInt(slider.slider(\"option\", \"min\"))) {\n        jQuery(this).val(slider.slider(\"option\", \"min\"));\n      }\n\n      slider.slider(\"value\", value);\n    }).button().addClass('ui-textfield');\n\n    jQuery(\"#\" + field_name + 'zero').on('click', function (event, ui) {\n      jQuery(\"#\" + field_name).val(0).trigger('change', [event, ui]);\n    }).button();\n\n    jQuery(\"#\" + field_name + 'max').on('click', function (event, ui) {\n      jQuery(\"#\" + field_name).val(jQuery(slider_id).slider(\"option\", \"max\")).trigger('change', [event, ui]);\n    }).button();\n\n    jQuery(\"#\" + field_name + 'dec, ' + \"#\" + field_name + 'inc')\n      .on('mousedown', function (event, ui) {\n        var element = jQuery(\"#\" + field_name);\n        if (element.is('[disabled]')) {\n          return;\n        }\n        accelerated = {\n          'element': element,\n          'step': step_value,\n          'slider': slider_id,\n          'ticks': 0,\n          'step_now': step_value,\n          'timeout': 0,\n          'increase': $(this).attr('id') == field_name + 'inc'\n        };\n        sn_ainput_mouselerate();\n      })\n      .on('mouseup', function (event, ui) {\n          if (accelerated) {\n            clearTimeout(accelerated['timeout']);\n            accelerated = undefined;\n          }\n        }\n      ).button();\n  }\n\n  function sn_ainput_mouselerate() {\n    var donext = false;\n    if (accelerated['increase'] && (val = parseInt(accelerated['element'].val())) < (option_max = jQuery(accelerated['slider']).slider(\"option\", \"max\"))) {\n      donext = val + accelerated['step_now'] < option_max;\n      accelerated['element'].val(donext ? val + accelerated['step_now'] : option_max).trigger('change'); // , [event, ui]\n    }\n    if (!accelerated['increase'] && (val = parseInt(accelerated['element'].val())) > 0) {\n      donext = val - accelerated['step_now'] > 0;\n      accelerated['element'].val(donext ? val - accelerated['step_now'] : 0).trigger('change'); // , [event, ui]\n    }\n\n    if (donext) {\n      accelerated['ticks']++;\n      if (accelerated['ticks'] % 3 == 0) {\n        accelerated['step_now'] = accelerated['step_now'] * 2;\n      }\n      accelerated['timeout'] = window.setTimeout(sn_ainput_mouselerate, 200);\n    }\n  }\n\n  var popup = jQuery(document.createElement(\"span\"));\n  var popupIsOpen = false;\n  popup.dialog({autoOpen: false}); // , width: auto, resizable: false\n  popup.mouseleave(function () {\n    popup_hide()\n  });\n\n  function popup_hide() {\n    $('[popup_opened_here]').removeAttr('popup_opened_here');\n    popup.dialog(\"close\");\n    popupIsOpen = false;\n  }\n\n  function popup_show(html, positioning, width) {\n    popup_hide();\n    popup.dialog(\"option\", \"width\", width ? width : 'auto');\n    popup.dialog(\"option\", \"position\", positioning ? positioning : {my: 'center', at: 'center', of: window});\n    popup.html(html);\n    popup.dialog(\"open\");\n    popupIsOpen = true;\n  }\n\n  //\n  /**\n   * Helper to probe element's CSS attribute\n   *\n   * @param element\n   * @param css_attribute\n   * @returns {string|boolean}\n   */\n  function sn_probe_style(element, css_attribute) {\n    switch (css_attribute) {\n      case 'border-top-color':\n        if (element.currentStyle)\n          return element.currentStyle.borderTopColor;\n        if (document.defaultView)\n          return document.defaultView.getComputedStyle(element, '').getPropertyValue('border-top-color');\n        break;\n    }\n\n    return false;\n  }\n\n  /**\n   * Switch element(s) visibility basing on his/their current state\n   *\n   * @param {element} control\n   * @param {string} jQueryQualifier\n   * @param {boolean} hideControl\n   */\n  function sn_show_hide2(control, jQueryQualifier, hideControl) {\n    var element_to_hide = jQuery(jQueryQualifier);\n\n    element_to_hide.each(function () {\n      var that = $(this);\n      var tag_name = that.tagName;\n\n      that.css(\n        'display',\n        that.css('display') === 'none'\n          ? (\n            tag_name === 'TR'\n              ? 'table-row'\n              : (\n                // Special HTML property 'flex' used to return flex-ability on \"show\"\n                that.attr('flex') ?\n                  'flex'\n                  : (\n                    tag_name === 'UL' || tag_name === 'DIV'\n                      ? 'block'\n                      : 'inline'\n                  )\n              )\n          )\n          : 'none');\n    });\n\n    if (hideControl) {\n      $(control).hide();\n    } else {\n      var\n        newHtml,\n        newText = element_to_hide.first().css('display') === 'none' ? language.sys_show : language.sys_hide,\n        textToReplace = newText === language.sys_show ? language.sys_hide : language.sys_show;\n\n      if ($(control).html().search(textToReplace) === -1) {\n        newHtml = \"[&nbsp;\" + (newText) + \"&nbsp;]\";\n      } else {\n        newHtml = $(control).html().replace(textToReplace, newText);\n      }\n      $(control).html(newHtml);\n    }\n  }\n\n  function cntchar(m) {\n    if (window.document.forms[0].text.value.length > m) {\n      window.document.forms[0].text.value = x;\n    } else {\n      x = window.document.forms[0].text.value;\n    }\n    if (e == null)\n      e = document.getElementById('cntChars');\n    else\n      e.childNodes[0].data = window.document.forms[0].text.value.length;\n  }\n\n  /**\n   *\n   * @param num\n   * @param cssPositive\n   * @param limit\n   * @returns {string|*|string}\n   */\n  function numberGetCssClass(num, cssPositive, limit) {\n    num = Math.floatVal(num);\n    limit = Math.floatVal(limit);\n\n    return Math.round(num - limit) == 0 ? \"zero\" : (\n      (limit > 0 && limit > num) || (limit <= 0 && -limit <= num) ? cssPositive : \"negative\"\n    );\n  }\n\n  function numberFormat(num, precision, plus_sign) {\n    var str_number, arr_int, nachkomma, Begriff, i, j, Extrakt, str_first, result;\n\n    precision = Math.intVal(precision);\n    num = Math.round(Math.floatVal(num) * Math.pow(10, precision)) / Math.pow(10, precision);\n\n    if (num > 0) {\n      str_number = num + '';\n    } else {\n      str_number = (-num) + '';\n    }\n    arr_int = str_number.split('.');\n    if (!arr_int[0]) arr_int[0] = '0';\n    if (!arr_int[1]) arr_int[1] = '';\n    if (arr_int[1].length < precision) {\n      nachkomma = arr_int[1];\n      for (i = arr_int[1].length + 1; i <= precision; i++) {\n        nachkomma += '0';\n      }\n      arr_int[1] = nachkomma;\n    }\n\n    if (arr_int[0].length > 3) {\n      Begriff = arr_int[0];\n      arr_int[0] = '';\n      for (j = 3; j < Begriff.length; j += 3) {\n        Extrakt = Begriff.slice(Begriff.length - j, Begriff.length - j + 3);\n        arr_int[0] = '.' + Extrakt + arr_int[0] + '';\n      }\n      str_first = Begriff.substr(0, (Begriff.length % 3 == 0) ? 3 : (Begriff.length % 3));\n      arr_int[0] = str_first + arr_int[0];\n    }\n\n    result = arr_int[0] + (arr_int[1] ? ',' + arr_int[1] : '');\n    if (num < 0) {\n      result = '-' + result;\n    } else if (num > 0 && plus_sign) {\n      result = '+' + result;\n    }\n\n    return result;\n  }\n\n  /**\n   *\n   * @param num\n   * @param precision\n   * @param cssPositive\n   * @param limit\n   * @param forcePlus\n   * @returns {string|*}\n   */\n  function sn_format_number(num, precision, cssPositive, limit, forcePlus) {\n    var result = numberFormat(num, precision, forcePlus);\n\n    if (cssPositive) {\n      result = '<span class=\"' + numberGetCssClass(num, cssPositive === true ? CLASS_POSITIVE : cssPositive, limit) + '\">' + result + '</span>';\n    }\n\n    return result;\n  }\n\n  function elementPrettyNumber(element, num, limit) {\n    element.html(numberFormat(num))\n      .removeClass(\"positive zero negative\")\n      .addClass(numberGetCssClass(num, \"positive\", limit));\n  }\n\n  function elementColorValue(element) {\n    element.html();\n  }\n\n  function elementIsEnabled(element) {\n    // Checking if elements is enabled\n    // TODO - можно ли упростить выражение?\n    return $(element).is(\":enabled\") && !($(this).attr(\"aria-disabled\"));\n  }\n\n\n  function sn_timestampToString(timestamp, useDays) {\n    var strTime = '', tmp;\n\n    !(timestamp = parseInt(timestamp)) ? timestamp = 0 : false;\n\n    if (useDays) {\n      tmp = Math.floor(timestamp / (60 * 60 * 24));\n      timestamp -= tmp * 60 * 60 * 24;\n      strTime += (tmp > 0 ? tmp + 'd' : '');\n    }\n\n    tmp = Math.floor(timestamp / (60 * 60));\n    timestamp -= tmp * 60 * 60;\n    strTime += (tmp <= 9 ? '0' : '') + tmp + ':';\n\n    tmp = Math.floor(timestamp / 60);\n    timestamp -= tmp * 60;\n    strTime += (tmp <= 9 ? '0' : '') + tmp + ':';\n\n    strTime += (timestamp <= 9 ? '0' : '') + timestamp;\n\n    return strTime;\n  }\n\n  function sn_timestampToStringHuman(timestamp, useDays) {\n    var strTime = '', tmp;\n\n    !(timestamp = parseInt(timestamp)) ? timestamp = 0 : false;\n    tmp = Math.floor(timestamp / (60 * 60 * 24));\n    if(tmp >= 3) {\n      strTime = tmp + 'd';\n    } else {\n      tmp = Math.floor(timestamp / (60 * 60));\n      if(tmp >= 3) {\n        strTime = tmp + 'h';\n      } else {\n        tmp = Math.floor(timestamp / 60);\n        if(tmp >= 3) {\n          strTime = tmp + 'm';\n        } else {\n          strTime = timestamp + 's';\n        }\n      }\n    }\n\n    return strTime;\n  }\n\n  /**\n   *\n   *\n   * @param local_time\n   * @param format\n   * bit 1 - have date\n   * bit 2 - have time\n   * bit 3 - date in YYYY-MM-DD format - otherwise toLocalDateString() format\n   * @param nbsp - Replace space with &nbsp;\n   * @returns {string}\n   */\n  function snDateToString(local_time, format, nbsp) {\n    format === undefined ? format = 3 : false;\n\n    return (format & 1 ? (format & 4 ? local_time.getFullYear() + '-' + ('0' + (local_time.getMonth() + 1)).slice(-2) + '-' + ('0' + local_time.getDate()).slice(-2) : local_time.toLocaleDateString()) : '') +\n      (format & 3 ? (nbsp ? '&nbsp;' : ' ') : '') +\n      (format & 2 ? local_time.toTimeString().substring(0, 8) : '');\n  }\n\n  jQuery(document).on('click', '#news_close', function (e) {\n    jQuery('#fresh_news_table').css('display', 'none');\n    jQuery.post('announce.php?only_hide_news=1');\n  });\n\n}\n\nfunction snConfirm(params) {\n  !params ? params = {} : false;\n  // , message, title\n  var that = $(params.that);\n  var d = $.Deferred();\n  $('<div><div class=\"sn-dialog-confirm\">' + (params.message ? params.message : (that.attr('message') ? that.attr('message') : language.sys_confirm_action)) + '</div></div>').dialog({\n    modal: true,\n    autoOpen: true,\n    resizable: false,\n    width: params.width ? params.width : '40em',\n    title: params.title ? params.title : (that.attr('title') ? that.attr('title') : language.sys_confirm_action_title),\n    open: function () {\n      var element = $(this).parent();\n      element.find('.ui-dialog-titlebar').css('display', 'block');\n      element.find('.ui-dialog-buttonpane button:last').focus();\n    },\n    buttons: {\n      ok: {\n        text: language.sys_confirm,\n        'class': 'ok',\n        click: function () {\n          var element = $(this).parent();\n          // Отключаем все кнопки кроме крестика закрытия в тайтл-баре\n          element.find(':button:not(.ui-dialog-titlebar-close)').button('disable');\n          //var form = element.find(\"form\");\n          //if(form) {\n          //  form.submit();\n          //} else {\n          //  $(this).dialog(\"close\");\n          //\n          //  d.resolve(true);\n          //\n          //  //return true;\n          //}\n          $(this).dialog(\"close\");\n\n          d.resolve(true);\n        }\n      },\n      cancel: {\n        text: language.sys_cancel,\n        click: function () {\n          $(this).dialog(\"close\");\n          d.resolve(false);\n\n          //return false;\n        }\n      }\n    }\n  });\n\n  d.promise().then(function (result) {\n    if(result) {\n      if (that.attr('href')) {\n        sn_redirect(that.attr('href'));\n      }\n\n      if (that.prop('tagName') == 'FORM') {\n        that.prop('submitted', true).submit();\n      }\n\n      if(params.hasOwnProperty('confirm')) {\n        params.confirm();\n      }\n    } else {\n      that.removeClass('button_pseudo_pressed');\n    }\n\n    // !result ? that.removeClass('button_pseudo_pressed') : false;\n    //\n    // if (result && that.attr('href')) {\n    //   sn_redirect(that.attr('href'));\n    // }\n    //\n    // if (result && that.prop('tagName') == 'FORM') {\n    //   that.prop('submitted', true).submit();\n    // }\n    //\n    // if(result && params.hasOwnProperty('confirm')) {\n    //   params.confirm();\n    // }\n  });\n\n  return false;\n}\n\n/**\n * Calculates CSS class based on (value / maximum) expression - i.e. class for fill rate\n *\n * @param {number} value\n * @param {number} maximum\n * @returns {string}\n */\nfunction numberCssClass(value, maximum) {\n  var result;\n  maximum = Math.floatVal(maximum);\n  value = Math.floatVal(value);\n\n  switch (true) {\n    case maximum == 0 && value == 0:\n      result = 'zero_number';\n      break;\n    case value > maximum:\n      result = 'error';\n      break;\n    case value == maximum:\n      result = 'warning';\n      break;\n    case maximum == 0:\n      result = 'zero_number';\n      break;\n\n    case (percent = value / maximum) > 0.9:\n      result = 'warning';\n      break;\n    case percent > 0.75:\n      result = 'notice';\n      break;\n    case percent > 0.50:\n      result = 'info';\n      break;\n    default:\n      result = 'ok';\n      break;\n  }\n\n  return result;\n}\n\n/**\n * For current value returns CSS-style from numberCssClass() with maximum value\n *\n * @param {number} maximum\n * @returns {string}\n */\nNumber.prototype.classByMaximum = function (maximum) {\n  return numberCssClass(this, maximum);\n};\n\n/**\n * For current value returns <span> styled with numberCssClass()\n *\n * @param {number} maximum\n * @param {boolean} format\n * @returns {string}\n */\nNumber.prototype.spanByMaximum = function (maximum, format) {\n  var output = format ? sn_format_number(this) : this;\n  return \"<span class=\\\"\" + this.classByMaximum(maximum) + \"\\\">\" + output + \"</span>\";\n};\n\n/**\n * For maximum value returns <span> styled with numberCssClass()\n *\n * @param {number} value\n * @param {boolean} format\n * @returns {string}\n */\nNumber.prototype.spanByValue = function (value, format) {\n  var output = format ? sn_format_number(this) : this;\n  return \"<span class=\\\"\" + numberCssClass(value, this) + \"\\\">\" + output + \"</span>\";\n};\n\nvar navbarResources = {};\nvar PLAYER_OPTION_NAVBAR_PLANET_VERTICAL = 0;\n$(document).ready(function () {\n  var tooltipPosition = { my: \"left top+15\", at: \"left bottom\", collision: \"flipfit\" };\n  if(PLAYER_OPTION_NAVBAR_PLANET_VERTICAL) {\n    tooltipPosition = { my: \"right-10 top\", at: \"left top\", collision: \"flipfit\" }\n  }\n\n  $(\"[data-resource]\").tooltip({\n    items: \"[data-resource]\",\n    position: tooltipPosition,\n\n//      disabled: true,\n//      close: function( event, ui ) {\n//        $(this).tooltip('disable');\n//        /* instead of $(this) you could also use $(event.target) */\n//      },\n    \"content\": function () {\n      var\n        result = '',\n        that = $(this),\n        resourceName = that.attr(\"data-resource\"),\n        resourceData;\n\n      if (that.is(\"[data-resource]\") && resourceName && (resourceData = navbarResources[resourceName])) {\n        var resourceNameText, currentValue, fullness,\n          storage = Math.intVal(resourceData.max_value);\n        if (resourceData['resourceNameLongId']) {\n          resourceNameText = language[resourceData['resourceNameLongId']];\n        }\n        if (!resourceNameText) {\n          resourceNameText = resourceName;\n        }\n\n        if (timer = timerById(\"top_\" + resourceName)) {\n          currentValue = timer['current'];\n        } else {\n          currentValue = resourceData.start_value;\n        }\n        currentValue = Math.floatVal(currentValue);\n\n        if(resourceName == \"energy\") {\n          result = $(\"#navbar_resource_flex_tooltip_pattern_energy\");\n          currentValue = Math.roundVal(resourceData.used_value);\n        } else {\n          result = $(\"#navbar_resource_flex_tooltip_pattern\");\n        }\n\n        fullness = storage ? Math.roundVal(currentValue / storage * 100).spanByMaximum(100, true) : '---';\n\n        result = result.html().format(\n          resourceNameText,\n          currentValue.spanByMaximum(storage, true),\n          storage.spanByValue(currentValue, true),\n          fullness\n        );\n      }\n\n      return result;\n    }\n  });\n\n//    $(\".navbar_resources_flex_resource > :nth-child(2)\").each(function () {\n//      $(this).progressbar({\n//        value: 90,\n//        max: 100\n////      change: function() {\n////        progressLabel.text( progressbar.progressbar( \"value\" ) + \"%\" );\n////      },\n////      complete: function() {\n////        progressLabel.text( \"Complete!\" );\n////      }\n//      })\n//    });\n\n\n});\n$(document).on(\"click\", \"[data-resource]\", function () {\n  var that = $(this);\n\n  if ($(\".ui-tooltip\").length) {\n    that.tooltip(\"close\");\n  } else {\n    that.tooltip(\"open\");\n  }\n});\n\n$(document).on(\"click\", \"#universeScanStart,#universeScanStop\", function () {\n  $('#universe_scan_mode').val($('#universe_scan_mode').val() ? 0 : 1);\n  $('#galaxy_form').submit();\n});\n\nvar NAVBAR_MODE = 0;\n\nfunction changePlanet(obj) {\n  var pathAdd = 'cp=' + obj.options[obj.selectedIndex].value + (NAVBAR_MODE ? '&mode=' + NAVBAR_MODE : '');\n  var regexp=/\\?page\\=(.*)/i;\n  var parsed = regexp.exec(window.location.href);\n  if(parsed) {\n    pathAdd = parsed[0].replace(/\\&cp=\\d*/i, '').replace(/\\&mode=\\d*/i, '') + '&' + pathAdd;\n  } else {\n    pathAdd = '?' + pathAdd;\n  }\n  window.location.href = window.location.pathname + pathAdd;\n}\n\n$(document).ready(\n  function() {\n    $('body').append($('#benchmark').detach());\n  }\n);\n\n$(document).on('change', '#filterQuestStatus', function () {\n  var that = $(this);\n\n  jQuery.get(\n    SN_ROOT_VIRTUAL + \"index.php?page=ajax&mode=quest&action=saveFilter&filterQuestStatus=\" + that.val(),\n    function(data) {\n      sn_reload();\n    },\n    \"json\"\n  );\n});\n\nfunction canIUseWebp() {\n  var elem = document.createElement('canvas');\n  ctx = elem.getContext('2d');\n  ctx.fillStyle = \"red\";\n  ctx.fillRect(0, 0, 8, 8);\n\n  // console.log(elem);\n  // console.log(elem.getContext);\n  // console.log(elem.getContext('2d'));\n  // console.log(elem.toDataURL('image/webp'));\n\n  if (!!(elem.getContext && elem.getContext('2d'))) {\n    // was able or not to get WebP representation\n    return elem.toDataURL('image/webp').indexOf('data:image/webp') == 0;\n  }\n\n  // very old browser like IE 8, canvas not supported\n  return false;\n}\n\nvar hasWebP = (function() {\n  var images = {\n    basic: \"data:image/webp;base64,UklGRjIAAABXRUJQVlA4ICYAAACyAgCdASoCAAEALmk0mk0iIiIiIgBoSygABc6zbAAA/v56QAAAAA==\",\n    lossless: \"data:image/webp;base64,UklGRh4AAABXRUJQVlA4TBEAAAAvAQAAAAfQ//73v/+BiOh/AAA=\"\n  };\n\n  return function(feature) {\n    var deferred = $.Deferred();\n\n    $(\"<img>\").on(\"load\", function() {\n      if(this.width === 2 && this.height === 1) {\n        deferred.resolve();\n      } else {\n        deferred.reject();\n      }\n    }).on(\"error\", function() {\n      deferred.reject();\n    }).attr(\"src\", images[feature || \"basic\"]);\n\n    return deferred.promise();\n  }\n})();\n"
  },
  {
    "path": "js/sn_notes.js",
    "content": "/**\n * Created by Gorlum on 22.01.2015.\n */\n\nvar NOTE_CLASS_PREVIOUSLY_SELECTED = '';\n\nfunction note_select_do(status) {\n  status = status == undefined ? jQuery(\"#note_select_all\").prop(\"checked\") : status;\n  jQuery('[name^=\"note[\"][name$=\"]\"]').prop(\"checked\", status);\n}\n\nfunction note_edit(obj) {\n  var elementNoteForm = jQuery('#note_form');\n  jQuery('#note_id_edit').val(jQuery(obj).data('note_id'));\n  jQuery('#note_text,#note_title').val(\"\");\n  elementNoteForm.prop('action', elementNoteForm.prop('action') + '#a' + jQuery(obj).data('note_id'));\n  elementNoteForm.submit();\n}\n\nfunction note_validate() {\n  // TODO: Validate\n  jQuery('#note_form').submit();\n}\n\njQuery(document).ready(function () {\n  jQuery(\"#note_form\").on(\"click\", '[data-note_id]', function (event, ui) {\n    note_edit(jQuery(this));\n  });\n\n  jQuery(document).on('focus', '#note_title', function () {\n    $(this).val() == LA_note_new_title ? $(this).val('') : false;\n  });\n  jQuery(document).on('blur', '#note_title', function () {\n    $(this).val() == '' ? $(this).val(LA_note_new_title) : false;\n  });\n\n  jQuery(document).on('focus', '#note_text', function () {\n    $(this).val() == LA_note_new_text ? $(this).val('') : false;\n  });\n  jQuery(document).on('blur', '#note_text', function () {\n    $(this).val() == '' ? $(this).val(LA_note_new_text) : false;\n  });\n\n  $('#note_priority').change();\n});\n\njQuery(document).on('change', '.note_delete_range', function () {\n  $('#note_delete_range,.note_delete_range').val(jQuery(this).val());\n\n  var element = jQuery('.note_delete_button');\n  jQuery(this).val() ? element.button('enable') : element.button('disable');\n});\n\njQuery(document).on('change', '#note_priority', function () {\n  var currentNoteClass = $(this).find('option:selected').attr('class');\n  $('#note_priority,#note_title,#note_text').removeClass(NOTE_CLASS_PREVIOUSLY_SELECTED).addClass(currentNoteClass);\n  NOTE_CLASS_PREVIOUSLY_SELECTED = currentNoteClass;\n});\n"
  },
  {
    "path": "js/sn_sound.js",
    "content": "var SN_SOUND_INIT = false;\n\nfunction sn_sound_play(sound) {\n  SN_SOUND_INIT && SN_SOUND_ENABLED ? ion.sound.play(sound) : false;\n}\n\n$(document).ready(function() {\n  ion.sound({\n    sounds: [\n      {\n        alias: \"chat_message\",\n        name: \"button_tiny\",\n      },\n      {\n        alias: \"key_press\",\n        name: \"snap\",\n      },\n      {\n        alias: \"halloween_success\",\n        name: \"bell_ring\",\n        volume: 0.2,\n      },\n      {\n        alias: \"halloween_fail\",\n        name: \"light_bulb_breaking\",\n        volume: 0.05,\n      },\n    ],\n    path: \"sounds/\",\n    multiplay: true,\n    preload: true,\n    allow_caching: true,\n    volume: 1.0\n  });\n\n  SN_SOUND_INIT = true;\n});\n"
  },
  {
    "path": "js/sn_survey.js",
    "content": "$(document).on('click', '[name=\"survey_confirm\"]', function() {\n  var survey_id = $(this).val();\n  var survey_vote = $('[name=\"survey[' + survey_id + ']\"]:checked').val();\n  if(typeof survey_vote != 'undefined') {\n    jQuery.post('announce.php?survey_id=' + survey_id + '&survey_vote=' + survey_vote);\n    $('.survey_block[survey_id=\"' + survey_id + '\"]').html(LA_survey_result_sent);\n  }\n});"
  },
  {
    "path": "js/sn_timer.js",
    "content": "if(window.LOADED_TIMER === undefined) {\n  var LOADED_TIMER = true;\n  /*\n   SuperNova JavaScript timer system\n\n   1.6 - copyright (c) 2010-2011 by Gorlum for http://supernova.ws\n   [+] - implemented event list\n\n   1.5 - copyright (c) 2010-2011 by Gorlum for http://supernova.ws\n   [+] - implemented date&time with delta\n\n   1.4 - copyright (c) 2010-2011 by Gorlum for http://supernova.ws\n   [+] - implemented pictured que\n\n   1.3 - copyright (c) 2010 by Gorlum for http://supernova.ws\n   [+] - implemented time-independent counter that works correctly even on browser's \"back\"\n   [~] - simple counter now uses objects instead of arrays\n\n   1.2 - copyright (c) 2010 by Gorlum for http://supernova.ws\n   [~] - changed sn_timer from array to objects\n\n   1.1 - copyright (c) 2010 by Gorlum for http://supernova.ws\n   [~] - optimization: now HTML elements for timers is caching after first tick and didn't search every tick\n   This should rise perfomance a bit\n\n   1.0 - copyright (c) 2010 by Gorlum for http://supernova.ws\n   [!] - initial release\n\n   Object structure:\n   'id'          - timer ID (name)\n   'type'        - timer type:\n   0 - que display;\n   1 - counter;\n   2 - date&time;\n   3 - pictured que;\n   4 - date&time with delta\n   5 - event list\n   'active'      - is timer active?\n   'start_time'  - start time\n   'options'     - timer options\n   'html_main'   - reserved for internal use (link to main HTML element)\n   'html_timer'  - reserved for internal use (link to 'timer' HTML element)\n   'html_finish' - reserved for internal use (link to 'finish' HTML element) Дата окончания постройки текущего юнита. Пока не используется\n   'html_que'    - reserved for internal use (link to 'que' HTML element)\n   'html_total'  - reserved for internal use (link to 'total time' HTML element)\n\n   Options for que display:\n   'template'  - template for que item\n   'msg_done'  - inactive message\n   'que'       - que: array\n   [0] - element ID\n   [1] - element name\n   [2] - build length\n   [3] - element amount\n   [4] - element level\n   'total'     - reserved for internal use (total que building time)\n\n   Options for counter:\n   'start_value' - start value\n   'per_second'  - delta value per second\n   'max_value'   - max value\n\n   Options for date&time:\n   bit 1 - date\n   bit 2 - time\n   It means: 1 - just date; 2 - just time; 3 - both date & time\n\n   Options for date&time with delta:\n   'format' - bit 1 - date\n   bit 2 - time\n   It means: 1 - just date; 2 - just time; 3 - both date & time\n   'delta'  - seconds to add or substract\n\n   Options for event list:\n   'msg_done'  - inactive message\n   'que'       - que: array\n   [0] - time left for this event\n   [1] - string for this event\n   [2] - hint for this event\n   'changed' - reserved for internal use (flag that event text was changed)\n   */\n\n  var UNIT_ID = 0;\n  var UNIT_NAME = 1;\n  var UNIT_TIME = 2;\n  var UNIT_AMOUNT = 3;\n  var UNIT_LEVEL = 4;\n  var UNIT_TIME_FULL = 5;\n  var UNIT_IMAGE = 6;\n  var UNIT_TIME_DISPLAY_OPTION = 'displayType';\n  var UNIT_TIME_DISPLAY_OPTION_HUMAN = 'human'; // Human-readable display type\n\n  var EVENT_TIME = 0;\n  var EVENT_STRING = 1;\n  var EVENT_HINT = 2;\n\n  // var TIMER_UNSUPPORTED = -1;\n  var TIMER_BUILD_QUE_V1 = 0;\n  var TIMER_COUNTER = 1;\n  var TIMER_BUILD_QUE_V2 = 3;\n  var TIMER_CLOCK_REALTIME = 4;\n  var TIMER_EVENT_QUE = 5; // Event que in title (NavBar)\n\n  var sn_timers = new Array();\n  var timer_is_started = false;\n  var timer_is_prepared = false;\n\n  function sn_timer_prepare() {\n    var timer, timerID, timer_options;\n\n    if(timer_is_prepared) {\n      return;\n    }\n\n    for (timerID in sn_timers) {\n      if (!sn_timers.hasOwnProperty(timerID)) {\n        continue;\n      }\n\n      timer = sn_timers[timerID];\n      timer['start_time'] = timeTimerStart; // new Date((SN_TIME_NOW - timeDiff ) * 1000  );\n      if (timer['type'] === undefined) {\n        timer['active'] = false;\n        continue;\n      }\n      timer['active'] = true;\n\n      timer.prefixClass = '.' + timer['id'];\n      timer.prefixId = '#' + timer['id'];\n\n      if(timer.hasOwnProperty('className')) {\n        timer.className = '.' + timer['className'];\n        timer['html_main'] = $(timer.className);\n      } else {\n        // Кэшируем DOM-ики\n        timer['html_main'] = $(timer.prefixId);\n      }\n\n\n      // Если нет настроек - создаём пустой объект\n      timer['options'] === undefined ? timer['options'] = {} : false;\n      timer_options = timer['options'];\n\n      timer['type'] = parseInt(timer['type']);\n\n      switch (timer['type']) {\n        case TIMER_BUILD_QUE_V1: {\n          // Если нет ни основного элемента вывода, ни элемента таймера - тогда можно даже не начинать работу счётчика\n          // TODO - Проверка на is(':visible')\n          timer['html_timer'] = $(timer.prefixId + '_timer');\n          if (!timer['html_main'].length && !timer['html_timer'].length) {\n            timer['active'] = false;\n            break;\n          }\n\n          // Если у нас только html_main, но нет html_timer - надо каждый шаг обновлять html_main, потому что в нём содержится и таймер\n          timer['always_refresh'] = timer['html_main'].length && !timer['html_timer'].length;\n          timer['changed'] = true;\n\n          break;\n        }\n\n        case TIMER_BUILD_QUE_V2: {\n          if (!timer_options['que'].length) {\n            if (timer_options['url'] !== undefined) {\n              document.location = timer_options['url'];\n            }\n            timer['active'] = false;\n            break;\n          }\n\n          timer['slotAmountBarWidthLast'] = 0;\n          timer['unit_amount_original'] = timer_options['que'][0][UNIT_AMOUNT];\n\n          timer['html_total_js'] = $(timer.prefixId + '_total:visible');\n          timer['html_que_js'] = $(timer.prefixId + '_que');\n          timer['html_finish'] = $(timer.prefixId + '_finish:visible');\n          timer['html_slots_current'] = $(timer.prefixId + '_slots');\n          timer['html_queProgressBar'] = $(timer.prefixId + '_progress_bar');\n          // Unit amount/level in que\n          timer['html_timer_units'] = $(timer.prefixId + '_units');\n          timer.queProgressBarWidth = timer['html_queProgressBar'].width() ? timer['html_queProgressBar'].width() : 0;\n\n          timer['html_level_current'] = $(timer.prefixClass + '_level_0:visible');\n          timer['html_slotUnitTimeBar'] = $(timer.prefixClass + '_unit_bar_0:visible');\n          timer.slotUnitTimeBarWidth = timer['html_slotUnitTimeBar'].parent().width() ? timer['html_slotUnitTimeBar'].parent().width() : 0;\n\n          timer['html_timer_current'] = $(timer.prefixClass + '_timer_0:visible');\n\n          timer.slotUnitTimeBarWidthCurrent = -1;\n          timer.slotAmountBarWidthCurrent = -1;\n          timer.queProgressBarWidthCurrent = -1;\n\n          timer['que_compiled'] = '';\n          break;\n        }\n\n        case TIMER_EVENT_QUE: {\n          // Если нет ни основного элемента вывода, ни видимого элемента с подсказкой - тогда можно даже не начинать работу счётчика\n          // TODO - Проверка на is(':visible')\n          timer['html_total_js'] = $(timer.prefixId + '_total:visible');\n          if (!timer['html_main'].length && !timer['html_total_js'].length) {\n            timer['active'] = false;\n            break;\n          }\n\n          timer['changed'] = true;\n          break;\n        }\n\n        case TIMER_COUNTER: {\n          // TODO - Проверка на is(':visible')\n          timer_options['round'] = timer_options['round'] === undefined ? 0 : parseInt(timer_options['round']);\n          timer_options['start_value'] = timer_options['start_value'] === undefined ? 0 : parseInt(timer_options['start_value']);\n          timer_options['per_second'] = timer_options['per_second'] === undefined ? 0 : parseFloat(timer_options['per_second']);\n\n          timer['current'] = timer_options['start_value'];\n\n          if (timer['html_main'].length <= 0) {\n            timer['active'] = false;\n            break;\n          }\n          break;\n        }\n\n        case TIMER_CLOCK_REALTIME: {\n          // TODO - Проверка на is(':visible')\n          if (timer['html_main'].length <= 0) {\n            timer['active'] = false;\n            break;\n          }\n\n          timer_options['format'] = parseInt(timer_options['format'] === undefined ? timer_options : timer_options['format']);\n          timer_options['delta'] = timer_options['delta'] === undefined ? 0 : parseInt(timer_options['delta']);\n          break;\n        }\n\n\n        default: {\n          timer['active'] = false;\n          break;\n        }\n\n      }\n    }\n    timer_is_prepared = true;\n\n  }\n\n  function sn_timer_compile_que(timer) {\n    var timer_options = timer['options'];\n    var que = timer_options['que'];\n    var compiled = '';\n    var total = 0;\n    var unit_count, que_id;\n    // TODO - Хак, что бы не рендерить очередь для навбара. Переделать при общем рефакторинге кода\n    var timer_invisible = timer['html_que_js'].is(':not(:visible)');\n\n    for (que_id in que) {\n      if(!que.hasOwnProperty(que_id)) {\n        continue;\n      }\n\n      total += (que[que_id][UNIT_AMOUNT] - (que_id == 0 ? 1 : 0)) * que[que_id][UNIT_TIME_FULL];\n\n      if(timer_invisible) {\n        continue;\n      }\n\n      unit_count = que[que_id][que[que_id][UNIT_LEVEL] > 0 ? UNIT_LEVEL : UNIT_AMOUNT];\n\n      compiled += timer_options['template']\n        .replace(/\\[UNIT_ID\\]/gi, que[que_id][UNIT_ID])\n        .replace(/\\[UNIT_TIME\\]/gi, sn_timestampToString(que[que_id][UNIT_TIME]))\n        .replace(/\\[UNIT_LEVEL\\]/gi, unit_count)\n        .replace(/\\[UNIT_NAME\\]/gi, que[que_id][UNIT_NAME])\n        .replace(/\\[UNIT_IMAGE\\]/gi, que[que_id][UNIT_IMAGE])\n        .replace(/\\[UNIT_QUE_PLACE\\]/gi, que_id);\n    }\n    timer_options['total'] = total;\n    !timer_options['total_original'] ? timer_options['total_original'] = timer_options['total'] : false;\n\n    return compiled;\n  }\n\n  function sn_timer() {\n    if(timer_is_started) {\n      window.setTimeout(\"sn_timer();\", 1000);\n      return;\n    }\n\n    timer_is_started = true;\n\n    sn_timer_prepare();\n\n    var timer, timerID, timeLeftTotalText, infoText, timer_options, local_time_plus, timeLeftText, new_value, hintText,\n      timeLeft, timeSinceLastUpdate, que;\n    var activeTimers = 0;\n    var textUnitsLeft = '';\n\n    var time_local_now = new Date();\n\n    for (timerID in sn_timers) {\n      if(!sn_timers.hasOwnProperty(timerID) || !sn_timers[timerID]['active']) {\n        continue;\n      }\n\n      infoText = '';\n      timeLeftText = '';\n\n      timer = sn_timers[timerID];\n      timer_options = timer['options'];\n      timeSinceLastUpdate = Math.round((time_local_now.valueOf() - timer['start_time'].valueOf()) / 1000);\n\n      switch (timer['type']) {\n        case TIMER_BUILD_QUE_V1: {\n          que = timer_options['que'];\n          if(que.length) {\n            if (que[0][UNIT_TIME] <= timeSinceLastUpdate) {\n              timer['start_time'] = time_local_now;\n              que[0][UNIT_AMOUNT]--;\n              if (que[0][UNIT_AMOUNT] <= 0) {\n                que.shift();\n                timer['changed'] = true;\n              }\n            }\n          }\n\n          if (que.length && que[0][UNIT_ID]) {\n            //completionDateTime = new Date((timer['start_time'] + que_item[UNIT_TIME]) * 1000); // Дата окончания постройки текущего юнита. Пока не используется\n            // TODO - Нам не обязательно каждый раз обновлять html_main, если у нас есть html_timer\n            infoText = que[0][UNIT_NAME] + (que[0][UNIT_LEVEL] ? ' (' + (que[0][UNIT_LEVEL]) + ')' : '');\n            timeSinceLastUpdate = Math.round((time_local_now.valueOf() - timer['start_time'].valueOf()) / 1000);\n            timeLeftText = sn_timestampToString(- (timeSinceLastUpdate) + parseInt(que[0][UNIT_TIME]));\n          } else {\n            infoText = timer_options['msg_done'];\n            timer['changed'] = true;\n            timer['active'] = false;\n          }\n\n          if (timer['html_timer'].length) {\n            timer['html_timer'].text(timeLeftText);\n          } else {\n            infoText += (infoText && timeLeftText ? '<br>' : '') + timeLeftText;\n          }\n          if((timer['always_refresh'] || timer['changed']) && timer['html_main'].length) {\n            timer['html_main'].html(infoText);\n          }\n\n          //typeof timer['html_finish'] != 'undefined' && timer['html_finish'].length ? timer['html_finish'].text(completionDateTime) : false; // Дата окончания постройки текущего юнита. Пока не используется\n          break;\n        }\n\n        case TIMER_BUILD_QUE_V2: {\n          textUnitsLeft = '';\n\n          timer['start_time'] = time_local_now;\n\n          que = timer_options['que'];\n\n          que[0][UNIT_TIME] -= timeSinceLastUpdate;\n          if(que[0][UNIT_TIME] <= 0) {\n            timeSinceLastUpdate = -que[0][UNIT_TIME];\n            que[0][UNIT_AMOUNT]--;\n            if (que[0][UNIT_AMOUNT] <= 0) {\n              que.shift();\n              timer['que_compiled'] = '';\n            } else {\n              que[0][UNIT_TIME] = que[0][UNIT_TIME_FULL];\n              timer_options['total'] -= que[0][UNIT_TIME_FULL];\n              timer['html_level_current'].text(que[0][UNIT_AMOUNT]);\n              timer.slotAmountBarWidthCurrent = Math.percentPixels(que[0][UNIT_AMOUNT], timer['unit_amount_original'], timer.slotUnitTimeBarWidth);\n            }\n          } else {\n            timeSinceLastUpdate = 0;\n          }\n\n          if (!timer['que_compiled']) {\n            // TODO - проверка на timer['html_que_js'].length\n            timer['que_compiled'] = sn_timer_compile_que(timer);\n\n            timer['html_slots_current'].length ? timer['html_slots_current'].html(que.length) : false;\n            timer['html_que_js'].length ? timer['html_que_js'].html(timer['que_compiled']) : false;\n\n            timer['html_level_current'] = $(timer.prefixClass + '_level_0:visible');\n            timer['html_timer_seconds'] = $(timer.prefixClass + '_seconds_0:visible');\n            timer['html_timer_current'] = $(timer.prefixClass + '_timer_0:visible');\n\n            timer['html_slotUnitTimeBar'] = $(timer.prefixClass + '_unit_bar_0:visible');\n            timer.slotUnitTimeBarWidth = timer['html_slotUnitTimeBar'].parent().width() ? timer['html_slotUnitTimeBar'].parent().width() : 0;\n            timer['html_slotAmountBar'] = $(timer.prefixClass + '_slot_bar_0:visible');\n          }\n\n          if (que.length && que[0][UNIT_ID]) {\n            que[0][UNIT_TIME] -= timeSinceLastUpdate; // Вычитаем то, что могло остаться с прошлого юнита/стэка\n            timeLeft = que[0][UNIT_TIME] <= 0 ? 1 : que[0][UNIT_TIME];\n\n            // infoText = que[0][UNIT_NAME] + '<br />(' + que[0][que[0][UNIT_LEVEL] ? UNIT_LEVEL : UNIT_AMOUNT] + ')';\n            infoText = que[0][UNIT_NAME];\n            textUnitsLeft = '(' + que[0][que[0][UNIT_LEVEL] ? UNIT_LEVEL : UNIT_AMOUNT] + ')';\n            if(timer.hasOwnProperty('options') && timer.options.hasOwnProperty(UNIT_TIME_DISPLAY_OPTION) && timer.options[UNIT_TIME_DISPLAY_OPTION] === UNIT_TIME_DISPLAY_OPTION_HUMAN) {\n              timeLeftText = sn_timestampToStringHuman(timeLeft);\n              timeLeftTotalText = sn_timestampToStringHuman(timeLeft + timer_options['total']);\n            } else {\n              timeLeftText = sn_timestampToString(timeLeft);\n              timeLeftTotalText = sn_timestampToString(timeLeft + timer_options['total']);\n            }\n\n            if(!timer['html_finish'].already_tagged && timer['html_finish'].length) {\n              // Дата окончания постройки текущего юнита\n              timer['html_finish'].html(snDateToString(\n                new Date(timeBrowser.valueOf() + (timeLeft + timer_options['total']) * 1000), 7\n              ));\n              timer['html_finish'].already_tagged = true;\n            }\n\n            timer.slotUnitTimeBarWidthCurrent = Math.percentPixels(timeLeft, que[0][UNIT_TIME_FULL], -timer.slotUnitTimeBarWidth);\n            timer.queProgressBarWidthCurrent = Math.percentPixels(timeLeft + timer_options['total'], que[0][UNIT_TIME_FULL] + timer_options['total_original'], timer.queProgressBarWidth);\n          } else {\n            if (timer_options['url'] !== undefined) {\n              document.location = timer_options['url'];\n            }\n\n            timeLeft = 0;\n\n            infoText = timer_options['msg_done'];\n            // timeLeftText = '';\n            // timeLeftTotalText = '00:00:00';\n\n            // timer['html_finish'].hide();\n            $(timer.prefixClass + '_hide_on_complete').hide();\n            // ov_{$QUE_ID}_finish_hide\n            timer['active'] = false;\n\n            //timer.slotUnitTimeBarWidthCurrent = 0;\n            //timer.slotAmountBarWidthCurrent = 0;\n            //timer.queProgressBarWidthCurrent = 0;\n          }\n\n          // Вывод строковых значений\n          var barWidth = Math.round((timeLeft % 60 + 1) / 60 * 100);\n          barWidth > 100 ? barWidth = 100 : false;\n          timer['html_timer_seconds'].length ? timer['html_timer_seconds'].width(barWidth + '%') : false;\n          if (timer['html_total_js'].length) {\n            timer['html_total_js'].html(timeLeftTotalText);\n          } else {\n            timeLeftText += (timeLeftText ? '<br />' : '') + timeLeftTotalText;\n          }\n\n          if (timer['html_timer_units'].length) {\n            timer['html_timer_units'].html(\"<span>\" + textUnitsLeft + \"</span>\");\n          } else {\n            infoText && textUnitsLeft ? infoText += \"<br />\" + textUnitsLeft : false;\n          }\n\n          if (timer['html_timer_current'].length) {\n            timer['html_timer_current'].html(\"<span>\" + timeLeftText + \"</span>\");\n          } else {\n            infoText += (infoText && timeLeftText ? \"<br />\" : \"\") + timeLeftText;\n          }\n\n          // ProgressBars\n          if(!PLAYER_OPTION_PROGRESS_BARS_DISABLED) {\n            if (timer['html_queProgressBar'].length && timer.queProgressBarWidthCurrent != timer.queProgressBarWidthLast) {\n              timer['html_queProgressBar'].css('width', timer.queProgressBarWidthLast = timer.queProgressBarWidthCurrent);\n            }\n            if (timer['html_slotUnitTimeBar'].length && timer.slotUnitTimeBarWidthCurrent != timer.slotUnitTimeBarWidthLast) {\n              timer['html_slotUnitTimeBar'].css('width', timer.slotUnitTimeBarWidthLast = timer.slotUnitTimeBarWidthCurrent);\n            }\n            if (timer['html_slotAmountBar'].length && timer.slotAmountBarWidthCurrent != timer.slotAmountBarWidthLast) {\n              timer['html_slotAmountBar'].css('width', timer.slotAmountBarWidthLast = timer.slotAmountBarWidthCurrent);\n            }\n          }\n\n          // Анимация\n          if(!jQuery.fx.off && que.length){\n            if(que[0][UNIT_TIME] == 1 && que[0][UNIT_AMOUNT] == 1) {\n              $(timer.prefixClass + '_container_0:visible').animate({opacity: 0}, que[0][UNIT_TIME] * 1000);\n            }\n            if(que[0][UNIT_TIME_FULL] == timeLeft) {\n              timer['html_timer_current'].children().animate({opacity: 0}, 50, function() {\n                $(this).animate({opacity: 1}, 300)});\n            }\n          }\n\n          timer['html_main'].length && infoText != timer.infoText ? timer['html_main'].html(timer.infoText = infoText) : false;\n          break;\n        }\n\n        case TIMER_EVENT_QUE: {\n          // Event que in title (NavBar for now)\n          hintText = '';\n\n          if (timer_options['que'].length && timer_options['que'][0][EVENT_TIME] <= timeSinceLastUpdate) { // TODO - проверить. Может тут ошибка - генерятся длительности не от SN_TIME_NOW, а дельты\n            //timer['start_time'] = timestamp_server; // TODO - а вот это тогда всё исправит\n            timer_options['que'].shift();\n            timer['changed'] = true;\n          }\n\n          if (timer_options['que'].length) {\n            if(timer['changed']) {\n              infoText = timer_options['que'][0][EVENT_STRING];\n              hintText = timer_options['que'][0][EVENT_HINT];\n            }\n          } else {\n            infoText = timer_options['msg_done'];\n            timer['changed'] = true;\n            timer['active'] = false;\n          }\n\n          if (timer['changed']) {\n            timer['html_main'].length ? timer['html_main'].html(infoText) : false;\n            // Если нет видимого элемента total - выводим подсказку в main. Уж один-то из них точно видимый!\n            timer[timer['html_total_js'].length ? 'html_total_js' : 'html_main'].prop('title', hintText);\n            timer['changed'] = false;\n          }\n          break;\n        }\n\n        case TIMER_COUNTER: {\n          if(timer_options['per_second'] == 0) {\n            new_value = timer_options['start_value'];\n            timer['active'] = false;\n          } else {\n            new_value = timer_options['start_value'] + timeSinceLastUpdate * timer_options['per_second'];\n            if (new_value < 0) {\n              new_value = 0;\n              timer['active'] = false;\n            } else {\n              if (new_value >= timer_options['max_value'] && timer_options['per_second'] > 0) {\n                new_value = Math.max(timer_options['start_value'], timer_options['max_value']);\n                timer['active'] = false;\n              }\n            }\n          }\n          infoText = sn_format_number(new_value, timer_options['round'], 'positive', timer_options['max_value']);\n\n          timer['current'] = new_value;\n\n          if(timer.hasOwnProperty('className')) {\n            $(timer.className).html(infoText);\n          } else {\n            timer['html_main'].html(infoText);\n          }\n\n          break;\n        }\n\n        case TIMER_CLOCK_REALTIME: {\n          // date&time with delta\n          local_time_plus = new Date(time_local_now.valueOf() + timer_options['delta'] * 1000);\n\n          timer['html_main'].text(snDateToString(local_time_plus, timer_options['format']));\n          break;\n        }\n      }\n\n      activeTimers++;\n    }\n\n    if (activeTimers) {\n      window.setTimeout(\"sn_timer();\", 1000);\n    }\n    timer_is_started = false;\n  }\n\n}\n\nfunction timerById(id) {\n  var timer = null;\n  for(var idNumeric in sn_timers) {\n    if(!sn_timers.hasOwnProperty(idNumeric)) {\n      continue;\n    }\n\n    if(sn_timers[idNumeric].id == id) {\n      timer = sn_timers[idNumeric];\n      break;\n    }\n\n  }\n  return timer;\n}\n"
  },
  {
    "path": "js/sn_universe.js",
    "content": "function fenster(target_url,win_name) {\n  var new_win = window.open(target_url,win_name,'resizable=yes,scrollbars=yes,menubar=no,toolbar=no,width=640,height=480,top=0,left=0');\n  new_win.focus();\n}\n\nfunction universe_popup_show(popup) {\n  if($(popup['of']).attr('popup_opened_here')) {\n    popup_hide();\n  } else {\n    popup_show(popup['html'], popup);\n    $(popup['of']).attr('popup_opened_here', 1);\n  }\n}\n\nvar universe_popup;\n\nfunction sn_universe_show_planet(that) {\n  var a_parent = that.parents('.uni_planet_row');\n  var planet_pos = a_parent.attr('planet_pos');\n  var planet_type = that.attr('planet_type');\n  if(!uni_row[planet_pos] || !planet_type || (planet_type == 3 && !parseInt(uni_row[planet_pos]['moon_diameter'])) || parseInt(uni_row[planet_pos]['planet_destroyed'])) {\n    return;\n  }\n\n  var user_is_planet_owner = uni_row[planet_pos]['owner'] == user_id;\n  var planet_type_string = planet_type == 1 ? 'planet' : 'moon';\n\n  var result = jQuery('#planet_template').html()\n    .replace(/\\[PLANET_POS\\]/g, planet_pos)\n    .replace(/\\[PLANET_TYPE\\]/g, planet_type)\n    .replace(/\\[PLANET_TYPE_TEXT\\]/g, language[planet_type == 1 ? 'sys_planet' : 'sys_moon'])\n    .replace(/\\[PLANET_TYPE_TEXT_SHORT\\]/g, language['sys_' + planet_type_string + '_short'])\n    .replace(/\\[PLANET_NAME\\]/g, uni_row[planet_pos][planet_type_string + '_name'])\n    .replace(/\\[PLANET_IMAGE\\]/g, uni_row[planet_pos][planet_type_string + '_image'])\n    .replace(/\\[PLANET_DIAMETER\\]/g, uni_row[planet_pos][planet_type_string + '_diameter'])\n    .replace(/\\[FLEET_TABLE\\]/g, fleet_table_make(uni_row[planet_pos][planet_type_string + '_fleet_id']))\n    .replace(/\\[HIDE_PLANET_PHALANX\\]/g, !user_is_planet_owner && uni_phalanx && planet_type == 1 ? '' : 'display: none;')\n    .replace(/\\[HIDE_PLANET_MISSILE\\]/g, !user_is_planet_owner && window.uni_missiles && planet_type == 1 ? '' : 'display: none;')\n    .replace(/\\[HIDE_PLANET_DESTROY\\]/g, !user_is_planet_owner && parseFloat(uni_death_stars) && planet_type == 3 ? '' : 'display: none;')\n    .replace(/\\[HIDE_PLANET_RELOCATE\\]/g, user_is_planet_owner ? '' : 'display: none;')\n    .replace(/\\[HIDE_PLANET_SPY\\]/g, user_is_planet_owner ? 'display: none;' : '')\n    .replace(/\\[HIDE_PLANET_ATTACK\\]/g, user_is_planet_owner ? 'display: none;' : '')\n    .replace(/\\[HIDE_PLANET_HOLD\\]/g, user_is_planet_owner ? 'display: none;' : '');\n\n  return {html: result, my: planet_type == 3 ? 'left middle' : 'right middle', at: planet_type == 3 ? 'right middle' : 'left middle', of: that};\n}\n\nfunction sn_universe_show_debris(that) {\n  var a_parent = that.parents('.uni_planet_row');\n  var planet_pos = a_parent.attr('planet_pos');\n  if(!uni_row[planet_pos] || !parseFloat(uni_row[planet_pos]['debris'])) {\n    return;\n  }\n\n  var metal_debris_percent = Math.round(uni_row[planet_pos]['debris_metal'] / uni_row[planet_pos]['debris'] * 100);\n\n  var result = jQuery('#debris_template').html()\n    .replace(/\\[CURRENT_PLANET\\]/g, planet_pos)\n    .replace('[DEBRIS]', sn_format_number(uni_row[planet_pos]['debris']))\n    .replace('[DEBRIS_METAL]', sn_format_number(uni_row[planet_pos]['debris_metal']))\n    .replace('[DEBRIS_METAL_PERCENT]', metal_debris_percent)\n    .replace('[DEBRIS_CRYSTAL]', sn_format_number(uni_row[planet_pos]['debris_crystal']))\n    .replace('[DEBRIS_CRYSTAL_PERCENT]', 100 - metal_debris_percent)\n    .replace('[DEBRIS_GATHER_TOTAL]', sn_format_number(uni_row[planet_pos]['debris_gather_total']))\n    .replace('[DEBRIS_GATHER_TOTAL_PERCENT]', sn_format_number(uni_row[planet_pos]['debris_gather_total_percent']))\n    .replace('[DEBRIS_RESERVED]', sn_format_number(uni_row[planet_pos]['debris_reserved']))\n    .replace('[DEBRIS_RESERVED_PERCENT]', sn_format_number(uni_row[planet_pos]['debris_reserved_percent']))\n    .replace('[DEBRIS_WILL_GATHER]', sn_format_number(uni_row[planet_pos]['debris_will_gather']))\n    .replace('[DEBRIS_WILL_GATHER_PERCENT]', sn_format_number(uni_row[planet_pos]['debris_will_gather_percent']))\n    .replace('[HIDE_RECYCLER_LINK]', PLANET_RECYCLERS > 0 && parseFloat(uni_row[planet_pos]['debris_will_gather']) ? '' : 'display: none;');\n\n  return {html: result, my: 'middle bottom', at: 'middle top', of: that};\n}\n\nfunction sn_universe_show_user(that) {\n  var a_parent = that.parents('.uni_planet_row');\n  var user_id = a_parent.attr('user_id');\n  if(!user_id || !users[user_id]) {\n    return;\n  }\n\n  var result = jQuery('#user_template').html()\n    .replace(/\\[USER_ID\\]/g, user_id)\n    .replace(/\\[USER_NAME\\]/g, users[user_id]['name'])\n    .replace(/\\[USER_RANK\\]/g, users[user_id]['rank'])\n    .replace(/\\[HIDE_USER_AVATAR\\]/g, opt_uni_avatar_user && users[user_id]['avatar'] == 1 ? '' : 'display: none;')\n    .replace(/\\[USER_COLSPAN]/g, opt_uni_avatar_user && users[user_id]['avatar'] == 1 ? 2 : 1)\n    .replace(/\\[USER_ALLY_TAG\\]/g, users[user_id]['ally_tag'])\n    .replace(/\\[USER_ALLY_NAME\\]/g, allies[users[user_id]['ally_id']] ? allies[users[user_id]['ally_id']]['name'] : '')\n    .replace(/\\[USER_ALLY_TITLE\\]/g, users[user_id]['ally_title'])\n    .replace(/\\[HIDE_USER_ALLY\\]/g, users[user_id]['ally_title'] && users[user_id]['ally_title'] !== undefined ? '' : 'display: none;');\n\n  return {html: result, my: 'left middle', at: 'right middle', of: that};\n}\n\nfunction sn_universe_show_ally(that) {\n  var a_parent = that.parents('.uni_planet_row');\n  var ally_id = a_parent.attr('ally_id');\n  if(!ally_id || !allies[ally_id]) {\n    return;\n  }\n\n  var result = jQuery('#ally_template').html()\n    .replace(/\\[ALLY_ID\\]/g, ally_id)\n    .replace(/\\[ALLY_NAME\\]/g, allies[ally_id]['name'])\n    .replace(/\\[ALLY_RANK\\]/g, allies[ally_id]['rank'])\n    .replace(/\\[ALLY_MEMBERS\\]/g, allies[ally_id]['members'])\n    .replace(/\\[HIDE_ALLY_AVATAR\\]/g, opt_uni_avatar_ally && allies[ally_id]['avatar'] == 1 ? '' : 'display: none;')\n    .replace(/\\[ALLY_COLSPAN]/g, opt_uni_avatar_ally && allies[ally_id]['avatar'] == 1 ? 2 : 1)\n    .replace(/\\[ALLY_URL\\]/g, allies[ally_id]['url'])\n    .replace(/\\[HIDE_ALLY_URL\\]/g, allies[ally_id]['url'] ? '' : 'display: none');\n\n  return {html: result, my: 'left top', at: 'right top', of: that};\n}\n\n$(function(){\n  $(document).on('click mouseenter', '#universe_legend', function() {\n    popup_show(jQuery('#legend_template').html(), {my: 'right top', at: 'right bottom', of: this});\n  });\n\n  $(document).on('mouseleave', '#universe_legend', function() {\n    universe_popup = null;\n  });\n\n  $(document).on('mouseleave', '.uni_show_planet,.uni_show_debris,.uni_show_user,.uni_show_ally', function() {\n    universe_popup = null;\n  });\n\n  $(document).on('click mouseenter', '.uni_show_planet,.uni_show_debris,.uni_show_user,.uni_show_ally', function(event) {\n    that = $(this);\n    universe_popup = that;\n    sn_delay(function(that) {\n      if(that != universe_popup) {\n        return false;\n      }\n      result = false;\n      switch(true) {\n        case that.hasClass('uni_show_planet'):\n          result = sn_universe_show_planet(that);\n          break;\n        case that.hasClass('uni_show_debris'):\n          result = sn_universe_show_debris(that);\n          break;\n        case that.hasClass('uni_show_user'):\n          result = sn_universe_show_user(that);\n          break;\n        case that.hasClass('uni_show_ally'):\n          result = sn_universe_show_ally(that);\n          break;\n      }\n      if(result) {\n        universe_popup_show(result);\n      }\n    }, event.type == 'mouseenter' ? opt_uni_tooltip_time : 1, that);\n  });\n});\n\n$(document).on('click', '.planet_template .ownmissile, .missile_attack_prepare', function() {\n  popup_hide();\n  uni_missile_planet = attr_on_me_or_parent(this, 'planet_planet');\n  jQuery('#uni_missile_planet').html(uni_missile_planet);\n  jQuery('#uni_missile_form').show();\n  $('html, body').animate({\n    scrollTop: $(\"#uni_missile_form\").offset().top\n  }, 2000);\n});\n\n$(document).on('click', '.debris_template .ownharvest', function() {\n  popup_hide();\n  doit(MT_RECYCLE, $(this).attr('planet_planet'), PT_DEBRIS);\n});\n\n$(document).on('click', '#galaxyLeft', function() {\n  $('#galaxy').val((parseInt($('#galaxy').val()) ? parseInt($('#galaxy').val()) : 0) - 1);\n  $('#galaxy_form').submit();\n});\n\n$(document).on('click', '#galaxyRight', function() {\n  $('#galaxy').val((parseInt($('#galaxy').val()) ? parseInt($('#galaxy').val()) : 0) + 1);\n  $('#galaxy_form').submit();\n});\n\n$(document).on('click', '#systemLeft', function() {\n  $('#system').val((parseInt($('#system').val()) ? parseInt($('#system').val()) : 0) - 1);\n  $('#galaxy_form').submit();\n});\n\n$(document).on('click', '#systemRight', function() {\n  $('#system').val((parseInt($('#system').val()) ? parseInt($('#system').val()) : 0) + 1);\n  $('#galaxy_form').submit();\n});\n"
  },
  {
    "path": "js/tutorial.js",
    "content": "/**\n * Created by Gorlum on 12.02.2017.\n */\n\nconst TUTORIAL_BLOCK = \"tutorial\";\nconst TUTORIAL_ID = \"id\";\nconst TUTORIAL_CONTENT = \"content\";\nconst TUTORIAL_TITLE = \"title\";\nconst TUTORIAL_NEXT = \"next\";\nconst TUTORIAL_PREV = \"prev\";\nconst TUTORIAL_COOKIE_NAME = \"tutorial\";\n\nvar tutorial = [];\nvar tutorial_window;\nvar tutorial_windowed = Cookies.getJSON(TUTORIAL_COOKIE_NAME);\nif (typeof tutorial_windowed === \"undefined\") {\n  Cookies.set(TUTORIAL_COOKIE_NAME, tutorial_windowed = {windowed: 0}, {expires: 365});\n}\n\nvar tutorialClose = function (e) {\n  if (tutorial_windowed.windowed) {\n    tutorial_window.dialog(\"close\");\n  } else {\n    jQuery(\"#tutorial_block\").addClass(\"hide\");\n  }\n};\n\n\nfunction tutorialError(errorText) {\n  jQuery(\"#tutorial_footer\").addClass(\"error\").text(errorText).removeClass(\"hide\");\n}\n\nfunction tutorialMakeAction(action, handler) {\n  jQuery.get(\n    SN_ROOT_VIRTUAL + \"index.php?page=ajax&mode=tutorial&action=\" + action + \"&id=\" + jQuery(\"#tutorial_current\").val(),\n    handler,\n    \"json\"\n  );\n}\n\nfunction tutorialGet(action, errorEmpty) {\n  jQuery.get(\n    SN_ROOT_VIRTUAL + \"index.php?page=ajax&mode=tutorial&action=\" + action + \"&id=\" + jQuery(\"#tutorial_current\").val(),\n    function (data) {\n      if (data.hasOwnProperty(TUTORIAL_BLOCK) && data[TUTORIAL_BLOCK]) {\n        tutorial = data[TUTORIAL_BLOCK];\n        if (tutorial.hasOwnProperty(TUTORIAL_ID) && Math.intVal(tutorial[TUTORIAL_ID])) {\n          tutorial_change();\n          jQuery(\"#tutorial_footer\").addClass(\"hide\").removeClass(\"error\");\n        } else {\n          tutorialError(errorEmpty);\n        }\n      } else {\n        tutorialError(language.tutorial_error_load);\n      }\n    },\n    \"json\"\n  );\n}\n\nfunction tutorial_store_postion(element) {\n  tutorial_windowed = {\n    windowed: 1,\n    position: {\n      top: $(element).css(\"top\"),\n      left: $(element).css(\"left\")\n    }\n  };\n  Cookies.set(TUTORIAL_COOKIE_NAME, tutorial_windowed, {expires: 365});\n}\n\nfunction tutorial_change() {\n  jQuery(\"#tutorial_current\").val(tutorial[TUTORIAL_ID]);\n  jQuery(\"#tutorial_text\").html(tutorial[TUTORIAL_CONTENT]);\n  jQuery(\"#tutorial_header_additional\").html(\" - \" + tutorial[TUTORIAL_TITLE]);\n\n  if (Math.intVal(tutorial[TUTORIAL_NEXT])) {\n    jQuery(\"#tutorial_button_next\").removeClass(\"hide\");\n    jQuery(\"#tutorial_button_finish\").addClass(\"hide\");\n  } else {\n    jQuery(\"#tutorial_button_next\").addClass(\"hide\");\n    jQuery(\"#tutorial_button_finish\").removeClass(\"hide\");\n  }\n\n  if (Math.intVal(tutorial[TUTORIAL_PREV])) {\n    jQuery(\"#tutorial_button_prev\").removeClass(\"hide\");\n  } else {\n    jQuery(\"#tutorial_button_prev\").addClass(\"hide\");\n  }\n}\n\nfunction tutorial_window_switch(windowed, reload) {\n  if (typeof windowed !== \"undefined\") {\n    tutorial_windowed.windowed = Math.intVal(windowed);\n  }\n\n  tutorial_window = jQuery(\"#tutorial_block\");\n\n  if (tutorial_windowed.windowed) {\n    // Removing border from tutorial block\n    jQuery(\"#tutorial_container\").removeClass(\"border_image_small\");\n    // Hiding in-navbar header\n    jQuery(\"#tutorial_header\").addClass(\"hide\").removeClass(\"contFJ\");\n    // Switching buttons\n    jQuery(\"#tutorial_button_window\").addClass(\"hide\");\n    jQuery(\"#tutorial_button_window_off\").removeClass(\"hide\");\n\n    var aPosition = {my: \"right bottom\", at: \"right bottom\"};\n    // Moving dialog window - if there are stored coordinates\n    if (typeof tutorial_windowed.position !== \"undefined\") {\n      aPosition = {\n        my: \"left top\",\n        // at: \"top+\" + tutorial_windowed.position.top + \" left+\" + tutorial_windowed.position.left\n        at: \"left+\" + tutorial_windowed.position.left + \" top+\" + tutorial_windowed.position.top\n      };\n    }\n\n    // Dialog class\n    tutorial_window.dialog({\n      resizable: false,\n      width: \"auto\",\n      height: \"auto\",\n      position: aPosition,\n      \"create\": function (event) {\n        $(event.target).dialog(\"widget\")\n        // Making window position stuck on screen\n          .css({\"position\": \"fixed\"})\n          // Making titlebar visible\n          .find(\".ui-dialog-titlebar\").addClass(\"ui-dialog-titlebar-show tutorial_dialog_title\");\n      },\n      \"open\": function (event) {\n        var dialogElement = $(event.target).parent();\n\n        // Moving tutorial header text to dialog title\n        $(\"#tutorial_header_text\").detach().appendTo(\n          // Removing &nbsp; from header\n          dialogElement.find(\".ui-dialog-title\").html(\"\")\n        );\n\n        tutorial_store_postion($(event.target).parent());\n      },\n      \"dragStop\": function (event, ui) {\n        tutorial_store_postion($(event.target).parent());\n      }\n    });\n  } else {\n    jQuery(\"#tutorial_button_window_off\").addClass(\"hide\");\n    jQuery(\"#tutorial_button_window\").removeClass(\"hide\");\n    tutorial_windowed.windowed = 0;\n    Cookies.set(TUTORIAL_COOKIE_NAME, tutorial_windowed, {expires: 365});\n    if (reload) {\n      sn_reload();\n    }\n  }\n}\n\njQuery(document).on(\"click\", \"#tutorial_close\", tutorialClose);\n\njQuery(document).on(\"click\", \"#tutorial_button_next\", function (e) {\n  tutorialGet(\"ajaxNext\", language.tutorial_error_next);\n});\n\njQuery(document).on(\"click\", \"#tutorial_button_prev\", function (e) {\n  tutorialGet(\"ajaxPrev\", language.tutorial_error_prev);\n});\n\njQuery(document).on(\"click\", \"#tutorial_button_finish\", function (e) {\n  tutorialMakeAction(\"ajaxFinish\", tutorialClose);\n});\n\njQuery(document).on(\"click\", \"#tutorial_button_window,#tutorial_button_window_off\", function (e) {\n  tutorial_window_switch($(this).attr(\"data-windowed\"), true);\n});\n"
  },
  {
    "path": "jumpgate.php",
    "content": "<?php\n/** @noinspection PhpDeprecationInspection */\n\n/**\n * jumpgate.php\n *\n * Jump Gate interface, I presume\n */\n\nuse DBAL\\db_mysql;\nuse DBAL\\OldDbChangeSet;\nuse Planet\\DBStaticPlanet;\n\ninclude('common.' . substr(strrchr(__FILE__, '.'), 1));\n\nglobal $user, $planetrow, $lang;\n\nlng_include('fleet');\n\n/** @noinspection SpellCheckingInspection */\nif ($TargetPlanet = sys_get_param_id('jmpto')) {\n  db_mysql::db_transaction_start();\n  db_user_by_id($user['id'], true);\n  $planetrow = DBStaticPlanet::db_planet_by_id($planetrow['id'], true);\n  if (!($NextJumpTime = uni_get_time_to_jump($planetrow))) {\n    $TargetGate = DBStaticPlanet::db_planet_by_id($TargetPlanet, true);\n    if (mrc_get_level($user, $TargetGate, STRUC_MOON_GATE) > 0) {\n      $NextDestTime = uni_get_time_to_jump($TargetGate);\n      if (!$NextDestTime) {\n        // $SubQueryOri = \"\";\n        // $SubQueryDes = \"\";\n        $ship_list    = sys_get_param('ships');\n        $db_changeset = array();\n        foreach ($ship_list as $ship_id => $ship_count) {\n          if (!in_array($ship_id, sn_get_groups('fleet'))) {\n            continue;\n          }\n\n          $ship_count = max(0, min(floor($ship_count), mrc_get_level($user, $planetrow, $ship_id)));\n          if ($ship_count) {\n            $db_changeset['unit'][] = OldDbChangeSet::db_changeset_prepare_unit($ship_id, -$ship_count, $user, $planetrow['id']);\n            $db_changeset['unit'][] = OldDbChangeSet::db_changeset_prepare_unit($ship_id, $ship_count, $user, $TargetGate['id']);\n          }\n        }\n\n        if (!empty($db_changeset)) {\n          DBStaticPlanet::db_planet_set_by_id($TargetGate['id'], \"`last_jump_time` = \" . SN_TIME_NOW);\n          DBStaticPlanet::db_planet_set_by_id($planetrow['id'], \"`last_jump_time` = \" . SN_TIME_NOW);\n          OldDbChangeSet::db_changeset_apply($db_changeset);\n\n          db_user_set_by_id($user['id'], \"`current_planet` = '{$TargetGate['id']}'\");\n\n          $planetrow['last_jump_time'] = SN_TIME_NOW;\n          $RetMessage                  = $lang['gate_jump_done'] . \" - \" . pretty_time(uni_get_time_to_jump($planetrow));\n        } else {\n          $RetMessage = $lang['gate_wait_data'];\n        }\n      } else {\n        $RetMessage = $lang['gate_wait_dest'] . \" - \" . pretty_time($NextDestTime);\n      }\n    } else {\n      $RetMessage = $lang['gate_no_dest_g'];\n    }\n  } else {\n    $RetMessage = $lang['gate_wait_star'] . \" - \" . pretty_time($NextJumpTime);\n  }\n  db_mysql::db_transaction_commit();\n  SnTemplate::messageBox($RetMessage, $lang['tech'][STRUC_MOON_GATE], \"jumpgate.php\", 10);\n} else {\n  $template = SnTemplate::gettemplate('jumpgate', true);\n  if (mrc_get_level($user, $planetrow, STRUC_MOON_GATE) > 0) {\n    $Combo    = '';\n    $MoonList = DBStaticPlanet::db_planet_list_moon_other($user['id'], $planetrow['id']);\n    // while($CurMoon = db_fetch($MoonList))\n    foreach ($MoonList as $CurMoon) {\n      if (mrc_get_level($user, $CurMoon, STRUC_MOON_GATE) >= 1) {\n        $NextJumpTime = uni_get_time_to_jump($CurMoon);\n        $template->assign_block_vars('moon', array(\n          'ID'             => $CurMoon['id'],\n          'GALAXY'         => $CurMoon['galaxy'],\n          'SYSTEM'         => $CurMoon['system'],\n          'PLANET'         => $CurMoon['planet'],\n          'NAME'           => $CurMoon['name'],\n          'NEXT_JUMP_TIME' => $NextJumpTime ? pretty_time($NextJumpTime) : '',\n        ));\n      }\n    }\n\n    foreach (sn_get_groups('fleet') as $Ship) {\n      if (($ship_count = mrc_get_level($user, $planetrow, $Ship)) <= 0) {\n        continue;\n      }\n\n      $template->assign_block_vars('fleet', array(\n        'SHIP_ID'         => $Ship,\n        'SHIP_NAME'       => $lang['tech'][$Ship],\n        'SHIP_COUNT'      => $ship_count,\n        'SHIP_COUNT_TEXT' => HelperString::numberFloorAndFormat($ship_count),\n      ));\n    }\n\n    $template->assign_vars(array(\n      'GATE_JUMP_REST_TIME' => uni_get_time_to_jump($planetrow),\n      'gate_start_name'     => $planetrow['name'],\n      'gate_start_link'     => uni_render_coordinates_href($planetrow, '', 3),\n    ));\n\n    SnTemplate::display($template, $lang['tech'][STRUC_MOON_GATE]);\n  } else {\n    SnTemplate::messageBox($lang['gate_no_src_ga'], $lang['tech'][STRUC_MOON_GATE], \"overview.php\", 10);\n  }\n}\n"
  },
  {
    "path": "language/.htaccess",
    "content": "<Files *>\n  order allow,deny\n  deny from all\n</Files>\n\n<Files ~ \"\\.png$\">\n  order allow,deny\n  allow from all\n</Files>\n"
  },
  {
    "path": "language/de/admin.mo.php",
    "content": "<?php\n\n/*\n#############################################################################\n#  Filename: admin.mo.php\n#  Project: SuperNova.WS\n#  Website: http://www.supernova.ws\n#  Description: Massive Multiplayer Online Browser Space Strategy Game\n#\n#  Copyright © 2009-2018 Gorlum for Project \"SuperNova.WS\"\n#############################################################################\n*/\n\n/**\n*\n* @package language\n* @system [German]\n* @version 46d0\n*\n*/\n\n/**\n* DO NOT CHANGE\n*/\n\nif (!defined('INSIDE')) die();\n\n\n/** @noinspection HttpUrlsUsage */\n$a_lang_array = array(\n  'menu_admin_ally' => 'Allianzen',\n\n  'adm_tool_md5_header' => 'Passwortgenerierung und Verschlüsselung (MD5)',\n  'adm_tool_md5_hash' => 'MD5-Hash',\n  'adm_tool_md5_encode' => '[ Verschlüsseln ]',\n  'adm_tool_md5_generate' => '[ Generieren ]',\n\n  'adm_tool_sql_page_header' => 'SQL-Serverparameter',\n\n  'adm_tool_sql_server_version' => 'Serverversion',\n  'adm_tool_sql_client_version' => 'Bibliotheksversion',\n  'adm_tool_sql_host_info' => 'Verbindungsmethode',\n\n  'adm_confirm_do' => 'Bestätigen',\n\n  'adm_tool_sql_table' => array(\n    'server' => array(\n      'TABLE_HEADER'  => 'SQL-Server',\n      'COLUMN_NAME_1' => 'Parameter',\n      'COLUMN_NAME_2' => 'Wert',\n//      'TABLE_FOOTER'  => '',\n//      'TABLE_EMPTY'   => '',\n    ),\n\n    'status' => array(\n      'TABLE_HEADER'  => 'SQL-Serverstatus',\n      'COLUMN_NAME_1' => 'Parameter',\n      'COLUMN_NAME_2' => 'Wert',\n//      'TABLE_FOOTER'  => '',\n    ),\n\n    'params' => array(\n      'TABLE_HEADER'  => 'SQL-Servereinstellungen',\n      'COLUMN_NAME_1' => 'Parameter',\n      'COLUMN_NAME_2' => 'Wert',\n//      'TABLE_FOOTER'  => '',\n    ),\n  ),\n\n  'adm_pl_image' => 'Planetenbild',\n  'adm_pl_fields_max' => 'Maximale Felder',\n  'adm_pl_temp_min' => 'Minimale Temperatur',\n  'adm_pl_temp_max' => 'Maximale Temperatur',\n  'adm_pl_fields_busy' => 'Belegte Felder',\n  'adm_pl_governor' => 'Gouverneur',\n  'adm_pl_debris_metal' => 'Trümmer, Metall',\n  'adm_pl_debris_crystal' => 'Trümmer, Kristall',\n\n  'adm_opt_user_settings' => 'Spielereinstellungen',\n  'adm_opt_user_birthday_gift' => 'Geschenk zum Geburtstag des Spielers',\n  'adm_opt_user_birthday_gift_disable' => '0 - Geschenke deaktivieren',\n  'adm_opt_user_birthday_range' => 'Rückwirkender Geburtstag, in Tagen',\n  'adm_opt_user_birthday_range_hint' => 'Wie weit in der Vergangenheit der Geburtstag liegen darf, damit der Spieler ein Geschenk erhält. Offensichtlich macht es keinen Sinn, diesen Wert auf mehr als 364 Tage zu setzen',\n\n  'adm_ul_title' => 'Spielerliste',\n  'adm_ul_title_online' => 'Spieler online',\n  'adm_ul_time_registered' => 'Registrierungsdatum',\n  'adm_ul_time_played' => 'Letzter Login',\n  'adm_ul_time_banned' => 'Sperrdauer',\n  'adm_ul_delete_confirm' => 'Bestätigen Sie die Löschung des Benutzers ',\n  'adm_ul_referral' => 'Referrals',\n  'adm_ul_players' => 'Spieler',\n  'adm_ul_dms' => 'DM',\n  'adm_sys_actions' => 'Aktionen',\n  'adm_sys_write_message' => 'Private Nachricht schreiben',\n  'adm_sys_delete_user' => 'Spieler löschen',\n\n  'adm_done' => 'Erfolgreich ausgeführt',\n  'adm_inactive_removed' => '<li>Inaktive Spielereinträge gelöscht: %d</li>',\n  'adm_stat_title' => 'Statistik aktualisieren',\n  'adm_maintenance_title' => 'Datenbankwartung',\n  'adm_records' => 'Einträge verarbeitet',\n  'adm_cleaner_title' => 'Bauliste bereinigen',\n  'adm_cleaned' => 'Anzahl gelöschter Aufgaben: ',\n  'adm_schedule_none' => 'Derzeit keine geplanten Aufgaben',\n\n  'Fix' => 'Aktualisiert',\n  'Welcome_to_Fix_section' => 'Patch-Bereich',\n  'There_is_not_need_fix' => 'Fix nicht benötigt!',\n  'Fix_welldone' => 'Erledigt!',\n  'adm_ov_title' => 'Übersicht',\n  'adm_ov_infos' => 'Informationen',\n  'adm_ov_yourv' => 'Aktuelle Version',\n  'adm_ov_lastv' => 'Verfügbare Version',\n  'adm_ov_here' => 'hier',\n  'adm_ov_onlin' => 'Online',\n  'adm_ov_ally' => 'Allianz',\n  'adm_ov_point' => 'Punkte',\n  'adm_ov_activ' => 'Aktiv',\n  'adm_ov_count' => 'Spieler online',\n  'adm_ov_wrtpm' => 'Private Nachricht schreiben',\n  'adm_ov_altpm' => '[PN]',\n  'adm_ov_hint' => '<ul><li>Die Online-Spielertabelle kann nach den Spalten \"ID\", \"Spielername\", \"Allianz\", \"Punkte\" und \"Aktivität\" sortiert werden. Klicken Sie auf die Spaltenüberschrift, um nach einer bestimmten Spalte zu sortieren</li></ul>',\n\n  'adm_ul_ttle2' => 'Aufgelistete Spieler',\n  'adm_ul_id' => 'ID',\n  'adm_ul_name' => 'Spielername',\n  'adm_ul_mail' => 'E-Mail',\n  'adm_ul_adip' => 'IP',\n  'adm_ul_regd' => 'Registriert von',\n  'adm_ul_lconn' => 'Letzter Login',\n  'adm_ul_bana' => 'Ban',\n  'adm_ul_detai' => 'Details',\n  'adm_ul_actio' => 'Aktionen',\n  'adm_ul_playe' => ' Spieler',\n  'adm_ul_yes' => 'Ja',\n  'adm_ul_no' => 'Nein',\n  'adm_pl_title' => 'Aktive Planeten',\n  'adm_pl_activ' => 'Aktive Planeten',\n  'adm_pl_name' => 'Planetenname',\n  'adm_pl_posit' => 'Koordinaten',\n  'adm_pl_point' => 'Wert',\n  'adm_pl_since' => 'Aktiv seit',\n  'adm_pl_they' => 'Gesamt',\n  'adm_pl_apla' => 'Planet(en)',\n  'adm_am_plid' => 'Planeten-ID',\n  'adm_am_done' => 'Hinzufügen erfolgreich',\n  'adm_am_ttle' => 'Ressourcen hinzufügen',\n  'adm_am_add' => 'Bestätigen',\n  'adm_am_form' => 'Ressourcen hinzufügen',\n  'adm_ban_title' => 'Spieler sperren',\n  'adm_bn_plto' => 'Spieler sperren',\n  'adm_bn_name' => 'Spielername',\n  'adm_bn_reas' => 'Grund für Ban',\n  'adm_bn_isvc' => 'Mit Urlaubsmodus',\n  'adm_bn_time' => 'Sperrdauer',\n  'adm_bn_days' => 'Tage',\n  'adm_bn_hour' => 'Stunden',\n  'adm_bn_mins' => 'Minuten',\n  'adm_bn_secs' => 'Sekunden',\n  'adm_bn_bnbt' => 'Sperren',\n  'adm_bn_thpl' => 'Spieler',\n  'adm_bn_isbn' => 'erfolgreich gesperrt!',\n  'adm_bn_vctn' => ' Urlaubsmodus aktiviert.',\n  'adm_bn_errr' => 'Fehler beim Sperren des Spielers! Vielleicht wurde der Name %s nicht gefunden.',\n  'adm_bn_err2' => 'Fehler beim Deaktivieren der Produktion auf Planeten!',\n  'adm_bn_plnt' => 'Produktion auf Planeten deaktiviert.',\n  'adm_ban_msg_issued_date' => 'hat den Spieler gesperrt',\n  'adm_unbn_ttle' => 'Entsperren',\n  'adm_unbn_plto' => 'Spieler entsperren',\n  'adm_unbn_name' => 'Name',\n  'adm_unbn_bnbt' => 'Entsperren',\n  'adm_unbn_thpl' => 'Spieler',\n  'adm_unbn_isbn' => 'entsperrt!',\n  'adm_rz_ttle' => 'Universum zurücksetzen',\n  'adm_rz_done' => 'Benutzer der Übertragung(en)',\n  'adm_rz_conf' => 'Bestätigung',\n  'adm_rz_text' => 'Durch Klicken auf die Schaltfläche (zurücksetzen) werden alle Daten in der Datenbank gelöscht. Haben Sie ein Backup erstellt??? Konten werden nicht gelöscht...',\n  'adm_rz_doit' => 'Zurücksetzen',\n  'adm_ch_ttle' => 'Chat-Administration',\n  'adm_ch_list' => 'Nachrichtenliste',\n  'adm_ch_clear' => 'Löschen',\n  'adm_ch_idmsg' => 'ID',\n  'adm_ch_delet' => 'löschen',\n  'adm_ch_play' => 'Spieler',\n  'adm_ch_time' => 'Datum',\n  'adm_ch_chat' => 'Nachricht',\n  'adm_ch_nbs' => 'Nachrichten insgesamt...',\n  'adm_er_ttle' => 'Systemprotokolleinträge',\n  'adm_er_clear' => 'Löschen',\n  'adm_er_idmsg' => 'ID',\n  'adm_er_type' => '[Code] Titel',\n  'adm_er_play' => 'Spieler',\n  'adm_er_time' => 'Datum',\n  'adm_er_page' => 'Seitenadresse',\n  'adm_er_nbs' => 'Einträge im Protokoll:',\n  'adm_er_text' => 'Protokolleintrag',\n  'adm_er_bktr' => 'Debug-Informationen',\n  'adm_dm_title' => 'Änderung der Dunklen Materie',\n  'adm_dm_planet' => 'ID, Koordinaten oder Name des Planeten',\n  'adm_dm_oruser' => 'ODER',\n  'adm_dm_user' => 'ID oder Name des Spielers aus der Spielerliste',\n  'adm_or_caption' => 'ODER',\n  'adm_dm_no_quant' => 'Geben Sie die Menge der DM an (positiv - zum Hinzufügen, negativ - zum Entfernen)',\n  'adm_dm_no_dest' => 'Geben Sie die ID oder den Namen des Spielers für die DM-Änderung an',\n  'adm_dm_add_err' => 'Es scheint, dass während der DM-Zuweisung ein Fehler aufgetreten ist',\n  'adm_dm_user_none' => 'Fehler: Kein Spieler mit der ID oder dem Namen \"%s\" gefunden',\n  'adm_dm_user_added' => 'Die Menge der DM des Spielers [%2$d] \"%1$s\" wurde erfolgreich auf %3$s DM geändert',\n  'adm_dm_user_conflict' => 'Fehler: Es scheint, dass in der Datenbank sowohl ein Spieler mit diesem Namen als auch mit dieser ID existiert',\n  'adm_dm_planet_none' => 'Fehler bei der Planeten-Suche: Kein Planet mit der ID, den Koordinaten oder dem Namen %s gefunden',\n  'adm_dm_planet_added' => 'Die Menge der DM des Spielers ID %1$d (Besitzer des Planeten %4$s %2$s ID %3$d) wurde erfolgreich auf %5$d DM geändert.',\n  'adm_dm_planet_conflict' => 'Nicht eindeutige Daten für die Planeten-Suche.<br>Dies bedeutet, dass in der Datenbank gleichzeitig ',\n  'adm_dm_planet_conflict_id' => 'ein Planet mit dem Namen \"%1$s\" und ein Planet mit der ID %1$s existieren.<br>Versuchen Sie, die Koordinaten des Planeten zu verwenden.',\n  'adm_dm_planet_conflict_name' => 'mehrere Planeten mit dem Namen \"%1$s\" existieren.<br>Versuchen Sie, die Koordinaten oder die ID des Planeten zu verwenden.',\n  'adm_dm_planet_conflict_coords' => 'ein Planet mit dem Namen \"%1$s\" und ein Planet mit den Koordinaten %1$s existieren.<br>Versuchen Sie, die ID des Planeten zu verwenden.',\n  'adm_apply' => 'Anwenden',\n  'adm_maint' => 'Wartung',\n  'adm_backup' => 'Backup',\n  'adm_tools' => 'Werkzeuge',\n  'adm_tools_reloadConfig' => 'Konfiguration neu berechnen',\n  'adm_reason' => 'Grund',\n  'adm_opt_title' => 'Universum-Einstellungen',\n  'adm_opt_game_settings' => 'Spielparameter',\n  'adm_opt_game_name' => 'Universumsname',\n  'adm_opt_multiaccount_enabled' => 'Interaktion von Konten mit derselben IP erlauben',\n  'adm_opt_speed' => 'Geschwindigkeit',\n  'adm_opt_game_gspeed' => 'Spiel',\n  'adm_opt_game_fspeed' => 'Flotte',\n  'adm_opt_game_pspeed' => 'Ressourcenproduktion',\n  'adm_opt_colonies_not_counted' => '(ohne Hauptplanet)',\n  'adm_opt_colonies_no_restrictions' => '(-1 - keine Einschränkungen)',\n  'adm_opt_game_speed_normal' => '(1&nbsp;-&nbsp;normal)',\n  'adm_opt_game_faq' => 'FAQ-Link',\n  'adm_opt_game_forum' => 'Forum-Adresse',\n  'adm_opt_game_metamatter' => 'Link \"Metamaterie kaufen\"',\n  'adm_opt_game_copyrigh' => 'Copyright',\n  'adm_opt_game_online' => 'Spiel deaktivieren. Benutzer sehen folgende Nachricht:',\n  'adm_opt_game_offreaso' => 'Nachricht',\n  'adm_opt_plan_settings' => 'Planetenparameter',\n  'adm_opt_plan_initial' => 'Größe des Startplaneten',\n  'adm_opt_plan_base_inc' => 'Grundproduktion',\n  'adm_opt_game_debugmod' => 'Debug-Modus aktivieren',\n  'adm_opt_geoip_whois_url' => 'WHOIS-Service-URL',\n  'adm_opt_geoip_whois_url_example' => '(z.B. \"http://1whois.ru/?ip=\")',\n  'adm_opt_game_counter' => 'Besucherzähler aktivieren',\n  'adm_opt_game_oth_info' => 'Weitere Parameter',\n  'adm_opt_int_news_count' => 'Anzahl der Nachrichten',\n  'adm_opt_int_page_imperor' => 'Auf der Seite \"Imperator\"',\n  'adm_opt_game_zero_disable' => '(0&nbsp;-&nbsp;deaktivieren)',\n  'adm_opt_game_advertise' => 'Werbeblöcke',\n  'adm_opt_game_oth_adds' => 'Werbeblock im linken Menü aktivieren. Banner-Code:',\n  'adm_opt_game_oth_gala' => 'Galaxie',\n  'adm_opt_game_oth_syst' => 'System',\n  'adm_opt_game_oth_plan' => 'Planet',\n  'adm_opt_btn_save' => 'Speichern',\n  'adm_opt_vacation_mode' => 'Urlaubsmodus deaktivieren',\n  'adm_opt_sectors' => 'Felder',\n  'adm_opt_per_hour' => 'pro Stunde',\n  'adm_opt_saved' => 'Spieleinstellungen erfolgreich gespeichert',\n  'adm_opt_players_online' => 'Spieler auf dem Server',\n  'adm_opt_vacation_mode_is' => 'Urlaubsmodus',\n  'adm_opt_game_status' => 'Spielstatus',\n  'adm_opt_links' => 'Links und Banner',\n  'adm_opt_universe_size' => 'Universumsgröße',\n  'adm_opt_galaxies' => 'Galaxien',\n  'adm_opt_systems' => 'Systeme',\n  'adm_opt_planets' => 'Planeten',\n  'adm_opt_build_on_research' => 'Labor während der Forschung bauen',\n  'adm_opt_eco_scale_storage' => 'Lager entsprechend der Produktionsgeschwindigkeit skalieren',\n  'adm_opt_game_rules' => 'Link zu den Regeln',\n  'adm_opt_max_colonies' => 'Anzahl der Kolonien',\n  'adm_opt_exchange' => 'Ressourcenaustauschkurs',\n  'adm_opt_game_mode' => 'Universumstyp',\n  'adm_opt_chat' => 'Chat-Einstellungen',\n  'adm_opt_chat_timeout' => 'Inaktivitäts-Timeout',\n  'adm_opt_allow_buffing' => 'Buffing erlauben',\n  'adm_opt_ally_help_weak' => 'Halten auf schwachem Verbündeten erlauben',\n  'adm_opt_email_pm' => 'Weiterleitung von PN an E-Mail erlauben',\n  'adm_opt_player_defaults' => 'Standard-Spielereinstellungen',\n  'adm_opt_game_default_language' => 'Oberflächensprache',\n  'adm_opt_game_default_skin' => 'Design/Skin',\n  'adm_opt_game_default_template' => 'Vorlage',\n  'adm_opt_player_change_name' => 'Spitznamenänderung durch Spieler',\n  'adm_opt_player_change_name_options' => [\n    SERVER_PLAYER_NAME_CHANGE_NONE => 'Spitznamenänderung verboten',\n    SERVER_PLAYER_NAME_CHANGE_FREE => 'Kostenlose Spitznamenänderung',\n    SERVER_PLAYER_NAME_CHANGE_PAY => 'Spitznamenänderung für DM',\n  ],\n  'adm_opt_player_change_name_cost' => 'Kosten in DM für Spitznamenänderung',\n  'adm_opt_empire_mercenary_temporary' => 'Temporäre Söldner',\n  'adm_opt_empire_mercenary_temporary_base' => 'Grundzeit für Anstellung, Sekunden',\n  'adm_opt_empire_mercenary_temporary_hint' => 'Wenn diese Option aktiviert ist, werden alle Söldner in temporäre mit einer Grundlaufzeit umgewandelt<br />Wenn diese Option deaktiviert ist, werden alle Söldner in permanente umgewandelt. Dabei können rekrutierte Söldner, die nicht den Anstellungsanforderungen entsprechen, zwar nicht verbessert werden, bleiben aber aktiv - d.h. sie beeinflussen das Spiel',\n  'adm_opt_experimental' => 'EXPERIMENTELLE OPTIONEN! MIT VORSICHT VERWENDEN!',\n  'adm_opt_tpl_minifier' => 'Template-Minifier',\n  'adm_opt_tpl_minifier_hint' => 'Der Minifier komprimiert Templates, indem er mehrere aufeinanderfolgende \"leere\" Zeichen (Zeilenumbruch, Tabulator, Leerzeichen) durch ein Leerzeichen ersetzt. Weitere Informationen zur Funktionsweise des Minifiers finden Sie in /docs/changelog.txt',\n  'adm_lm_compensate' => 'Kompensieren',\n  'adm_pl_comp_title' => 'Kompensation für zerstörten Planeten',\n  'adm_pl_comp_src' => 'Planeten zerstören',\n  'adm_pl_comp_dst' => 'Ressourcen auf Planeten gutschreiben',\n  'adm_pl_comp_bonus' => 'Spielerbonus',\n  'adm_pl_comp_check' => 'Überprüfen',\n  'adm_pl_comp_confirm' => 'Bestätigen',\n  'adm_pl_comp_done' => 'Fertig',\n  'adm_pl_comp_price' => 'Gebäudekosten',\n  'adm_pl_comp_got' => 'Wird gutgeschrieben',\n  'adm_pl_com_of_plr' => 'Spieler',\n  'adm_pl_comp_will_be' => 'wird',\n  'adm_pl_comp_destr' => 'zerstört.',\n  'adm_pl_comp_recieve' => 'Die angegebene Menge an Ressourcen',\n  'adm_pl_comp_recieve2' => 'wird auf den Planeten gutgeschrieben',\n  'adm_pl_comp_err_0' => 'Kein zu zerstörender Planet gefunden',\n  'adm_pl_comp_err_1' => 'Planet bereits zerstört',\n  'adm_pl_comp_err_2' => 'Kein Planet gefunden, auf den Ressourcen gutgeschrieben werden sollen',\n  'adm_pl_comp_err_3' => 'Die angegebenen Planeten haben unterschiedliche Besitzer. Ressourcen können nur auf einen Planeten desselben Spielers gutgeschrieben werden',\n  'adm_pl_comp_err_4' => 'Planet gehört nicht dem angegebenen Spieler',\n  'adm_pl_comp_err_5' => 'Planet zum Zerstören und zum Gutschreiben von Ressourcen ist derselbe',\n  'adm_ver_versions' => 'Serverkomponentenversionen',\n  'adm_ver_version_sn' => 'Engine-Version',\n  'adm_ver_version_db' => 'Datenbankversionen',\n  'adm_update_force' => 'Update von Grund auf erzwingen',\n  'adm_update_repeat' => 'Vorheriges Update wiederholen',\n  'adm_ptl_test' => 'Test der phpBB Template Engine',\n  'adm_counter_recalc' => 'Tabelle `counter` neu berechnen',\n  'adm_lm_planet_edit' => 'Bearbeiten',\n  'adm_planet_edit' => 'Planetenbearbeitung',\n  'adm_planet_id' => 'Planeten-ID',\n  'adm_name' => 'Name',\n  'adm_planet_change' => 'Änderung',\n  'adm_planet_parent' => 'Hauptplanet',\n  'adm_planet_active' => 'Aktive Planeten',\n  'adm_planet_edit_hint' => '<ul>    <li>Wenn Sie auf einer leeren Seite eine Planeten-ID eingeben und auf die Schaltfläche \"Bestätigen\" klicken, versucht die Engine, Informationen zu einem Planeten mit dieser ID anzuzeigen: Typ, Name und Koordinaten,    sowie die aktuelle Anzahl der Einheiten/Ressourcen des ausgewählten Typs auf dem Planeten</li>    <li>Um eine bestimmte Anzahl von Einheiten/Ressourcen vom Planeten zu entfernen, geben Sie eine negative Zahl ein</li>  </ul>',\n  'adm_planet_list_title' => 'Planetenliste',\n  'adm_sys_owner' => 'Besitzer',\n  'adm_sys_owner_id' => 'Besitzer-ID',\n  'addm_title' => 'Mond hinzufügen',\n  'addm_addform' => 'Formular für neuen Mond',\n  'addm_playerid' => 'ID des Planeten für die Platzierung',\n  'addm_moonname' => 'Mondname',\n  'addm_moongala' => 'Geben Sie die Galaxie an',\n  'addm_moonsyst' => 'Geben Sie das System an',\n  'addm_moonplan' => 'Geben Sie die Position an',\n  'addm_moondoit' => 'Hinzufügen',\n  'addm_done' => 'Mond erstellt',\n  'adm_usr_level' => array(\n    '0' => 'Spieler',\n    '1' => 'Operator',\n    '2' => 'Moderator',\n    '3' => 'Administrator',\n  ),\n\n  'adm_usr_genre' => array(\n    GENDER_UNKNOWN => 'Nicht ausgewählt',\n    GENDER_MALE => 'Mann',\n    GENDER_FEMALE => 'Frau',\n  ),\n\n  'panel_mainttl' => 'Administrator-Panel',\n  'adm_panel_mnu' => 'Spielersuche',\n  'adm_panel_ttl' => 'Suchansicht',\n  'adm_search_pl' => 'Suche nach Name',\n  'adm_search_ip' => 'Suche nach IP',\n  'adm_stat_play' => 'Spielerstatistik',\n  'adm_mod_level' => 'Zugriffslevel',\n  'adm_player_nm' => 'Spielername',\n  'adm_ip' => 'IP',\n  'adm_plyer_wip' => 'Spieler mit IP',\n  'adm_frm1_id' => 'ID',\n  'adm_frm1_name' => 'Name',\n  'adm_frm1_ip' => 'IP',\n  'adm_frm1_mail' => 'E-Mail',\n  'adm_frm1_acc' => 'Rang',\n  'adm_frm1_gen' => 'Geschlecht',\n  'adm_frm1_main' => 'Planeten-ID',\n  'adm_frm1_gpos' => 'Koordinaten',\n  'adm_mess_lvl1' => 'Zugriffslevel',\n  'adm_mess_lvl2' => '&quot;jetzt&quot; ',\n  'adm_colony' => 'Kolonien',\n  'adm_planet' => 'Planet',\n  'adm_moon' => 'Mond',\n  'adm_technos' => 'Technologien',\n  'adm_bt_search' => 'Suchen',\n  'adm_bt_change' => 'Ändern',\n  'flt_id' => 'ID',\n  'flt_fleet' => 'Flotte',\n  'flt_ships' => 'Zusammensetzung',\n  'flt_mission' => 'Auftrag',\n  'flt_here' => 'Zurück',\n  'flt_there' => 'Hin',\n  'flt_here_there' => 'Hin/Zurück',\n  'flt_departure' => 'Startpunkt',\n  'flt_owner' => 'Besitzer',\n  'flt_planet' => 'Planet',\n  'flt_time_return' => 'Rückkehr',\n  'flt_e_owner' => 'Zielpunkt',\n  'flt_time_arrive' => 'Ankunft',\n  'flt_staying' => 'Wartezeit',\n  'flt_action' => 'Aktion',\n  'flt_title' => 'Flotten im Flug',\n  'flt_no_fleet' => 'Derzeit ist keine Flotte unterwegs',\n  'mlst_title' => 'Nachrichtenliste',\n  'mlst_mess_del' => 'Nachrichten löschen',\n  'mlst_hdr_page' => 'Seite',\n  'mlst_hdr_title' => ' ) Nachrichten :',\n  'mlst_hdr_prev' => '[ &lt;- ]',\n  'mlst_hdr_next' => '[ -&gt; ]',\n  'mlst_hdr_id' => 'ID',\n  'mlst_hdr_type' => 'Nachrichtentyp',\n  'mlst_hdr_time' => 'Sendezeit',\n  'mlst_hdr_from' => 'Von',\n  'mlst_hdr_to' => 'An',\n  'mlst_hdr_text' => 'Inhalt',\n  'mlst_hdr_action' => 'Mark.',\n  'mlst_del_mess' => 'Löschen',\n  'mlst_bt_delsel' => 'Ausgewählte Nachrichten löschen',\n  'mlst_bt_deldate' => 'Löschen',\n  'mlst_hdr_delfrom' => 'Nachrichten des aktuellen Typs vor dem Datum löschen',\n  'mlst_no_messages' => 'Keine Nachrichten',\n  'mlst_messages_deleted' => 'Nachrichten mit ID %s gelöscht',\n  'mlst_messages_deleted_date' => 'Nachrichten vom Typ \"%s\" bis zum Datum %s gelöscht (ohne Nachrichten am angegebenen Datum)',\n\n  'adm_lng_title' => 'Lokalisierung',\n  'adm_lng_warning' => 'WARNUNG! Dies ist eine Alpha-Version des Lokalisierungseditors! Verwenden Sie ihn auf eigene Gefahr!',\n  'adm_lng_domain' => 'Domain',\n  'adm_lng_string_name' => 'String-Name',\n  'adm_lng_string_add' => 'String hinzufügen',\n  'adm_uni_price_galaxy' => 'Grundkosten für die Umbenennung einer Galaxie',\n  'adm_uni_price_system' => 'Grundkosten für die Umbenennung eines Systems',\n\n  'adm_opt_ver_check' => 'Versionsprüfung',\n  'adm_opt_ver_check_hint' => 'Bei jeder Art der Versionsprüfung werden nur anonyme Daten übertragen - die aktuelle Datenbankversion, die Release-Nummer und die Spielversion. Sie können die Version \"manuell\" überprüfen - indem Sie auf die Schaltfläche \"Version prüfen\" klicken.',\n  'adm_opt_ver_check_do' => 'Version prüfen',\n  'adm_opt_ver_check_last' => 'Letzte Versionsprüfung durchgeführt am',\n  'adm_opt_ver_check_auto' => 'Automatische Versionsprüfung',\n  'adm_opt_ver_check_auto_hint' => 'Sie können die automatische Versionsprüfung des Spiels aktivieren. Die Prüfung wird dann automatisch in einem festgelegten Zeitintervall durchgeführt (standardmäßig - einmal täglich). Weitere Informationen finden Sie in der Dokumentation',\n\n  'adm_opt_ver_response' => array(\n    SNC_VER_NEVER => 'Versionsprüfung nicht durchgeführt',\n\n    SNC_VER_ERROR_CONNECT => 'Fehler bei der Versionsprüfung. Das Spiel konnte keine Verbindung zum Update-Server herstellen. Stellen Sie sicher, dass CURL in PHP installiert und aktiviert ist oder dass der Zugriff auf Remote-Server in den PHP-Einstellungen erlaubt ist',\n    SNC_VER_ERROR_SERVER => 'Fehler des Update-Servers. Überprüfen Sie, ob eine neuere Version der Engine mit erweiterter Update-Unterstützung verfügbar ist. Andernfalls benachrichtigen Sie umgehend den Entwickler!',\n\n    SNC_VER_EXACT => 'Sie haben die neueste Alpha-Version des zukünftigen Releases installiert. Vielen Dank für Ihre Teilnahme am Test!',\n    SNC_VER_LESS => 'Sie verwenden eine Alpha-Version des zukünftigen Releases. Es gibt jedoch bereits eine neuere Alpha! Aktualisieren Sie, wenn Sie Fehlerbehebungen für die aktuellen Versionen erhalten und an der Testung neuer Spielmöglichkeiten teilnehmen möchten.',\n    SNC_VER_FUTURE => 'Sie haben eine Spielversion aus der Zukunft! Kontaktieren Sie umgehend den Entwickler und übermitteln Sie ihm diese Version! Bereiten Sie sich auch auf einen Besuch der Temporalen Miliz wegen der Verletzung des Raum-Zeit-Kontinuums und der Kausalität vor...',\n\n    SNC_VER_RELEASE_EXACT => 'Sie haben die neueste Version des aktuellsten Spielreleases',\n    SNC_VER_RELEASE_MINOR => 'Sie haben eine veraltete Spielversion - es gibt bereits ein Update des aktuellen Releases. Darin sind wahrscheinlich einige Fehler Ihrer Version behoben. Es wird empfohlen, das Spiel zu aktualisieren.',\n    SNC_VER_RELEASE_MAJOR => 'Sie haben eine stark veraltete Spielversion - es gibt bereits einen neuen Release. Fehlerbehebungen, neue Funktionen - aktualisieren Sie unbedingt!',\n    SNC_VER_RELEASE_ALPHA => 'Sie haben die neueste Version des Spielreleases. Es gibt jedoch bereits eine Alpha-Version des nächsten Releases. Möchten Sie vielleicht an deren Test teilnehmen?',\n\n    SNC_VER_MAINTENANCE => 'Der Update-Server ist wegen Wartungsarbeiten deaktiviert. Bitte versuchen Sie es später erneut',\n    SNC_VER_UNKNOWN_RESPONSE => 'Der Update-Server hat eine unbekannte Antwort gegeben. Dies bedeutet höchstwahrscheinlich, dass eine neuere Version der Engine mit erweiterten Update-Funktionen verfügbar ist',\n    SNC_VER_INVALID => 'Ich kann nicht verstehen, was für eine seltsame und unverständliche Version Sie haben. Kontaktieren Sie den Entwickler zur Problemdiagnose.',\n    SNC_VER_STRANGE => 'Sie sollten diese Nachricht nicht sehen. Wenn Sie sie sehen, ist etwas schiefgelaufen. Kontaktieren Sie den Entwickler zur Problemdiagnose.',\n\n    SNC_VER_REGISTER_UNREGISTERED => 'Ihr Server ist noch nicht registriert',\n    SNC_VER_REGISTER_ERROR_MULTISERVER => 'Fehler - Ihr Server ist mehrfach registriert! Wenden Sie sich an den Entwickler zur Problemdiagnose.',\n    SNC_VER_REGISTER_ERROR_REGISTERED => 'Fehler - Ihr Server ist bereits registriert! Überprüfen Sie den eindeutigen Schlüssel und die ID in der Serverkonfiguration!',\n    SNC_VER_REGISTER_ERROR_NO_NAME => 'Fehler - kein Servername! Sie müssen einen Servernamen vergeben!',\n    SNC_VER_REGISTER_ERROR_WRONG_URL => 'Fehler - falsche URL! Die übergebene Zeichenkette ist keine gültige URL. Möglicherweise haben Sie versucht, einen Server zu registrieren, der auf localhost läuft - der Update-Server arbeitet nicht mit solchen Servern.',\n    SNC_VER_REGISTER_REGISTERED => 'Ihre Website wurde erfolgreich registriert',\n\n\n    SNC_VER_ERROR_INCOMPLETE_REQUEST => 'Fehler im Schlüssel oder ID',\n    SNC_VER_ERROR_UNKNOWN_KEY => 'Unbekannter Schlüssel',\n    SNC_VER_ERROR_MISSMATCH_KEY_ID => 'Schlüssel passt nicht zur ID',\n  ),\n\n  'adm_upd_register' => 'Serverregistrierung',\n\n  'adm_upd_register_hint' => '\n    Die Serverregistrierung ist für bestimmte Anfragen an den Update-Server erforderlich. Bei der Registrierung werden nur die minimal notwendigen Informationen zur Serveridentifikation übermittelt:\n    <ul>\n      <li>Vollständige Server-URL - d.h. HTTP-Adresse und Serverunterverzeichnis. Zum Beispiel: http://meinserver.com/meinordner/. Dies ist für die primäre Serveridentifikation notwendig. Der vollständige Pfad ist erforderlich, um mehrere SuperNova-Installationen auf derselben IP oder Domain zu unterscheiden.</li>\n      <li>Interner Servername. Wird für die Einbindung in Nachrichten verwendet.</li>\n    </ul>\n    Warum sollte man seinen Server überhaupt registrieren? In Zukunft sind mehrere Funktionen geplant, die nur registrierten Servern zur Verfügung stehen werden. Dazu gehören (sortiert nach geplanten Implementierungszeiträumen):\n    <ul>\n      <li>Automatischer Erhalt von Changelogs</li>\n      <li>Automatisierte Engine-Updates</li>\n      <li>Teilnahme am Server-Ranking</li>\n      <li>Bugreports von Serveradministratoren</li>\n      <li>Chat für Serveradministratoren</li>\n      <li>Auf Anfrage - Remote-Diagnose des Servers</li>\n      <li>...und vieles, vieles mehr</li>\n    </ul>\n    Warum sollte man seinen Server jetzt registrieren?\n    <ul>\n      <li>Anfragen von Administratoren registrierter Server haben höhere Priorität bei der Problemdiagnose und Bearbeitung von Bugreports.</li>\n      <li>Bei der Registrierung erhält der Server neben einem individuellen Schlüssel eine eindeutige Identifikationsnummer, die für die primäre Sortierung der Server verwendet wird. Je früher ein Server registriert wird, desto höher wird er beispielsweise im allgemeinen Serververzeichnis platziert...</li>\n    </ul>\n  ',\n\n  'adm_upd_register_do' => 'Server registrieren',\n  'adm_upd_register_already' => 'Sie sind bereits beim Update-Server registriert. Bewahren Sie unbedingt die ID und den eindeutigen Schlüssel Ihres Servers auf!',\n  'adm_upd_register_id' => 'Registrierungsnummer',\n  'adm_upd_register_key' => 'Registrierungsschlüssel',\n\n  'adm_opt_stats_and_records' => 'Statistik und Rekorde',\n  'adm_opt_stats_hide_admins' => 'Admins verstecken',\n  'adm_opt_stats_hide_admins_detail' => 'Alle Konten mit authlevel > 0 werden ausgeblendet',\n  'adm_opt_stats_hide_player_list' => 'Spieler verstecken',\n  'adm_opt_stats_hide_player_list_detail' => 'Liste der auszublendenden Spieler-IDs, durch Kommas getrennt',\n  'adm_opt_stats_schedule' => 'Zeitplan für Statistik-Updates',\n  'adm_opt_stats_schedule_detail' => 'Format: \"[JJJJ:[MM:[TT:[HH:[MM:[SS]]]]]][,(...)]\"<br />\n    Null-Parameter links sind optional<br />\n    Leere Parameter rechts werden als Null gewertet<br />\n    Beispiele:<br />\n     - \"00:00:27:00\" bedeutet \"Start bei 27 Minuten jeder Stunde\";<br />\n     - \"04::\" - \"Start um 4 Uhr morgens jeden Tag\";<br />\n     - \"02::,17:00\" - \"Start um 2 Uhr morgens jeden Tag und bei 17 Minuten jeder Stunde\";<br />\n     - \"1:4:30:00\" - \"Start am 1. jedes Monats um 04:30 Uhr\" usw.',\n  'adm_opt_stats_hide_pm_link' => 'PN-Links ausblenden',\n\n  'adm_pay' => 'Zahlungen',\n  'adm_pay_stats' => 'Zahlungsstatistik',\n  'adm_pay_th_payer' => 'Zahler',\n  'adm_pay_th_payer_id' => 'ID',\n  'adm_pay_th_payer_name' => 'Name',\n  'adm_pay_th_payment' => 'Zahlung',\n  'adm_pay_th_payment_id' => 'ID',\n  'adm_pay_th_payment_date' => 'Datum',\n  'adm_pay_th_payment_status' => 'Status',\n  'adm_pay_th_payment_amount' => 'Betrag',\n  'adm_pay_th_payment_currency' => 'Währung',\n  'adm_pay_th_mm_paid' => 'Bezahlt',\n  'adm_pay_th_mm_gained' => 'Gutgeschrieben',\n  'adm_pay_th_module' => 'Zahlungssystem',\n  'adm_pay_th_module_name' => 'Typ',\n\n  'adm_pay_filter_all' => '-- Alle --',\n  'adm_pay_filter_status' => array(\n    PAYMENT_STATUS_ALL => '-- Alle --',\n    PAYMENT_STATUS_NONE => 'Nicht abgeschlossen',\n    PAYMENT_STATUS_COMPLETE => 'Abgeschlossen',\n  ),\n  'adm_pay_filter_test' => array(\n    PAYMENT_TEST_ALL => '-- Alle --',\n    PAYMENT_TEST_REAL => 'Echt',\n    PAYMENT_TEST_PROBE => 'Test',\n  ),\n  'adm_pay_filter_stat' => array(\n    PAYMENT_FILTER_STAT_NORMAL => '-- Keine --',\n    PAYMENT_FILTER_STAT_MONTH => 'Nach Monaten',\n    PAYMENT_FILTER_STAT_YEAR => 'Nach Jahren',\n    PAYMENT_FILTER_STAT_ALL => 'Gesamter Zeitraum',\n  ),\n  'adm_pay_filter_stat_name' => 'Statistik',\n\n  'adm_user_stat' => 'Benutzerstatistik',\n  'adm_user_online' => 'Online von %s bis %s',\n\n  'adm_ban_unban' => 'Ban/Entban',\n  'adm_metametter_payment' => 'MM & Zahlungen',\n\n  'adm_stat_already_started' => 'Die Statistik wird bereits aktualisiert',\n\n  'adm_dm_change_hint' => 'Die Suche erfolgt zuerst nach der Spieler-ID und falls nicht gefunden, nach dem Namen',\n\n  'adm_matter_change_log_record' => 'Über die Admin-Oberfläche von Benutzer [%3$s] \"%4$s\" für Konto [%1$d] \"%2$s\" aus Grund \"%5$s\"',\n\n  'adm_game_status' => 'Aktueller Spielstatus',\n\n  'adm_log_delete_update_info' => 'Informationen zur Wartung der Datenbank, Statistik- und Engine-Updates löschen',\n\n  'admin_tab_status' => 'Status',\n  'admin_tab_game' => 'Spiel',\n  'admin_tab_universe' => 'Universum',\n  'admin_tab_planets' => 'Planeten',\n  'admin_tab_stats_and_records' => 'Statistik',\n  'admin_tab_urls' => 'Links',\n  'admin_tab_players' => 'Spieler',\n  'admin_tab_UBE' => 'Kampf',\n  'admin_tab_advertise' => 'Werbung',\n\n  'admin_tab_universe_main' => 'Universum',\n\n  'admin_ptl_test_la_' => \"Single'Double\\\"Zero\\0End\",\n\n  'admin_title_access_denied' => 'Zugriff verweigert',\n\n  'menu_admin_modules' => 'Module',\n\n  'adm_player' => 'Spieler',\n  'adm_planets' => 'Planeten',\n\n  // ------------------ NOT LOCALIZED -------------------------------\n  'adm_mm_title'                        => 'Änderung der Metamaterie-Menge',\n  'adm_mm_account'                      => 'Konto: ID, Name oder Registrierungs-E-Mail',\n  'adm_mm_account_hint'                 => 'Die Kontosuche erfolgt zuerst nach ID, dann nach Name, dann nach Registrierungs-E-Mail',\n  'adm_mm_player'                       => 'Spieler: ID oder Name aus der Spielerliste',\n  'adm_mm_player_hint'                  => 'Die Spielersuche erfolgt zuerst nach ID, dann nach Name',\n  'adm_mm_err_points_empty'             => 'Geben Sie die MM-Menge an (positiv - für Gutschrift, negativ - für Abbuchung)',\n  'adm_mm_err_account_not_found'        => 'Fehler: Konto mit ID, Name oder E-Mail \"%1$s\" nicht gefunden',\n  'adm_mm_err_player_not_found'         => 'Fehler: Spieler mit ID oder Name \"%1$s\" nicht gefunden',\n  'adm_mm_err_player_no_account'        => 'Fehler: Kein Konto für Spieler \"%1$s\" gefunden',\n  'adm_mm_err_account_and_player_empty' => 'Fehler: Weder Konto noch Spieler für MM-Änderung angegeben',\n  'adm_mm_err_mm_change_failed'         => 'Fehler: Interner Fehler bei der MM-Gutschrift. Bitte kontaktieren Sie den Entwickler',\n  'adm_mm_msg_mm_changed'               => 'Für Konto [%2$d] \"%1$s\" (Spieler [%4$s] \"%5$s\") wurde die MM-Menge erfolgreich auf <span class=\"metamatter\">%3$s MM</span> geändert',\n  'adm_mm_msg_confirm_mm_change'        => 'Bestätigen Sie die Änderung der MM-Menge für Konto [%2$d] \"%1$s\" (Spieler [%4$s] \"%5$s\") auf <span class=\"metamatter\">%3$s MM</span>',\n  'adm_mm_msg_change_mm_log_record'     => 'Admin [%6$s] \"%7$s\" (Spieler [%3$s] \"%4$s\") Grund \"%5$s\" für [%1$d] \"%2$s\" (Spieler [%8$d] \"%9$s\")',\n\n  'admin_ally_list' => 'Allianzliste',\n);"
  },
  {
    "path": "language/de/affilates.mo.php",
    "content": "<?php\n\n/*\n#############################################################################\n#  Filename: affilates.mo\n#  Project: SuperNova.WS\n#  Website: http://www.supernova.ws\n#  Description: Massen-Mehrspieler-Online-Browser-Weltraumstrategiespiel\n#\n#  Copyright © 2009-2018 Gorlum for Project \"SuperNova.WS\"\n#############################################################################\n*/\n\n/**\n*\n* @package language\n* @system [German]\n* @version 46d0\n*\n*/\n\n/**\n* DO NOT CHANGE\n*/\n\n!defined('INSIDE') && die();\n\n$a_lang_array = (array(\n  'aff_title' => 'Partnerprogramm',\n  'aff_text1' => 'Platzieren Sie Ihren persönlichen Link, Banner oder Userbar in einem Forum oder auf einer Website, und jeder, der über den Link kommt, wird Ihr Eingeladener. Sie erhalten 1 DM für jeweils',\n  'aff_text2' => 'DM, die der Eingeladene verdient hat.',\n  'aff_text3' => 'Die Bonuszahlungen beginnen, nachdem Ihr Eingeladener mindestens',\n  'aff_link' => 'Persönlicher Link im Partnerprogramm',\n  'aff_link_direct' => 'Direkter Link',\n  'aff_link_bb' => 'BBCode für die Platzierung des persönlichen Links im Forum',\n  'aff_link_html' => 'HTML-Code für die Platzierung des persönlichen Links auf einer Webseite',\n  'aff_banner' => 'Banner 416x58',\n  'aff_banner_bb' => 'BBCode für die Platzierung des Banners im Forum',\n  'aff_banner_html' => 'HTML-Code für die Platzierung des Banners auf einer Webseite',\n  'aff_userbar' => 'Userbar 350x19',\n  'aff_userbar_bb' => 'BBCode für die Platzierung des Userbars im Forum',\n  'aff_userbar_html' => 'HTML-Code für die Platzierung des Userbars auf einer Webseite',\n  'aff_list' => 'Liste der Eingeladenen',\n  'aff_none' => 'Keine Eingeladenen',\n  'aff_gained' => 'DM verdient vom Spieler',\n  'aff_your_bonus' => 'Ihr Bonus',\n));"
  },
  {
    "path": "language/de/alliance.mo.php",
    "content": "<?php\n\n/*\n#############################################################################\n#  Filename: alliance.mo\n#  Project: SuperNova.WS\n#  Website: http://www.supernova.ws\n#  Description: Massen-Mehrspieler-Online-Browser-Weltraumstrategiespiel\n#\n#  Copyright © 2009-2018 Gorlum for Project \"SuperNova.WS\"\n#############################################################################\n*/\n\n/**\n*\n* @package language\n* @system [German]\n* @version 46d0\n*\n*/\n\n/**\n* DO NOT CHANGE\n*/\n\nif (!defined('INSIDE')) die();\n\n$a_lang_array = [\n  'ali_dip_title' => 'Diplomatie',\n  'ali_dip_negotiate' => 'Verhandlungen',\n  'ali_adm_msg_subject' => 'Allianz-Rundmail',\n  'ali_dip_offers_your' => 'Ihre Angebote',\n  'ali_dip_offers_to_you' => 'Angebote an Sie',\n  'ali_dip_offer_none' => 'Keine Angebote',\n  'ali_dip_offer' => 'Angebot',\n  'ali_dip_offers' => 'Angebote',\n  'ali_dip_offer_new' => 'Verhandlungen aufnehmen',\n  'ali_dip_offer_to_ally' => 'Allianz anbieten',\n  'ali_dip_offer_make' => 'Verhandlungen beginnen',\n  'ali_dip_offer_answer' => 'Allianz hat Ihr Angebot abgelehnt',\n  'ali_dip_offer_deny_reason' => 'Sie haben das Angebot abgelehnt',\n  'ali_dip_offer_to' => 'An Allianz',\n  'ali_dip_offer_from' => 'Von Allianz',\n  'ali_dip_offer_deny' => 'Angebot ablehnen',\n  'ali_dip_offer_accept' => 'Angebot annehmen',\n  'ali_dip_offer_delete' => 'Angebot zurückziehen',\n  'ali_dip_err_no_ally' => 'Allianz existiert nicht',\n  'ali_dip_err_same_ally' => 'Verhandlungen mit der eigenen Allianz nicht möglich',\n  'ali_dip_err_wrong_offer' => 'Dieses Angebot kann nicht gemacht werden',\n  'ali_dip_err_offer_none' => 'Angebot existiert nicht',\n  'ali_dip_err_offer_same' => 'Sie haben bereits %s-Beziehungen mit dieser Allianz',\n  'ali_dip_err_offer_alien' => 'Dieses Angebot wurde nicht an Sie gerichtet!',\n  'ali_dip_err_offer_accept_own' => 'Eigenes Angebot kann nicht angenommen werden!',\n  'ali_dip_err_offer_empty' => 'Kein Angebot angegeben',\n  'ali_dip_relation_none' => 'Keine Beziehungen',\n  'ali_dip_relation_change_auto_accept' => 'Allianz \"%1$s\" hat Beziehung zu Allianz \"%2$s\" auf \"%3$s\" geändert',\n  'ali_dip_relation_change_own' => 'Wir haben das Angebot der Allianz \"%2$s\" angenommen, die Beziehung auf \"%3$s\" zu ändern',\n  'ali_dip_relation_change_other' => 'Allianz \"%1$s\" hat unser Angebot angenommen, die Beziehung auf \"%3$s\" zu ändern',\n  'ali_dip_relations' => [\n    ALLY_DIPLOMACY_NEUTRAL => 'Neutralität',\n    ALLY_DIPLOMACY_WAR => 'Krieg',\n    ALLY_DIPLOMACY_PEACE => 'Frieden',\n    ALLY_DIPLOMACY_CONFEDERATION => 'Konföderation',\n    ALLY_DIPLOMACY_FEDERATION => 'Föderation',\n    ALLY_DIPLOMACY_UNION => 'Union',\n    ALLY_DIPLOMACY_MASTER => 'Herrscher',\n    ALLY_DIPLOMACY_SLAVE => 'Vasall',\n  ],\n\n  'ali_lessThen15min' => '&lt; 15 min',\n  'ali_confirm' => 'Bestätigen',\n  'ali_confirmation' => 'Bestätigung',\n  'ali_adm_disband' => 'Allianz auflösen',\n  'ali_adm_options' => 'Allianz-Einstellungen',\n  'ali_adm_transfer' => 'Allianz an Spieler übertragen',\n  'ali_adm_return' => 'Zurück zur Allianzverwaltung',\n  'ali_adm_kick' => 'Spieler aus Allianz entfernen',\n  'ali_adm_kick_confirm' => 'Sind Sie sicher, dass Sie diesen Spieler aus der Allianz entfernen möchten?',\n  'ali_adm_requests' => 'Aufnahmeanträge',\n  'ali_adm_newLeader' => 'SPIELER AUSWÄHLEN',\n  'ali_adm_lastRank' => 'Letzter Rang kann nicht gelöscht werden!',\n  'ali_adm_rights_title' => 'Zugriffsrechte konfigurieren',\n  'ali_adm_rights_rank_new' => 'Neuer Rang',\n  'ali_adm_rights_rank_delete' => 'Rang löschen',\n  'ali_adm_rights_rank_none' => 'Keine Ränge',\n  'ali_adm_rights_rank_name' => 'Rang',\n  'ali_adm_rights_mass_mail' => 'Nachricht an gesamte Allianz',\n  'ali_adm_rights_view_online' => 'Online-Status der Mitglieder einsehen',\n  'ali_adm_rights_helper' => 'Stellvertreter (Für Übertragung wird Gründerrang benötigt)',\n  'ali_adm_rights_legend' => 'Allianzrechte',\n  'ali_leaderRank' => 'Allianzführer',\n  'ali_defaultRankName' => 'Neuling',\n  'ali_make_title' => 'Allianz gründen',\n  'ali_make_tag_length' => '(3 bis 8 Zeichen)',\n  'ali_make_name_length' => '(bis zu 35 Zeichen)',\n  'ali_make_confirm' => 'Allianz gründen',\n  'ali_req_cancel' => 'Antrag zurückziehen',\n  'ali_req_candidate' => 'Kandidat',\n  'ali_req_characters' => 'Zeichen',\n  'ali_req_date' => 'Antragsdatum',\n  'ali_req_deny_msg' => 'Ihr Aufnahmeantrag für die Allianz [%s] wurde abgelehnt.<br>Ablehnungsgrund: \"%s\".<br>Sie können den Antrag löschen und es später erneut versuchen oder einer anderen Allianz beitreten.',\n  'ali_req_deny_admin' => '<font color=red>Antrag bereits abgelehnt</font>. Solange der Nutzer seinen Aufnahmeantrag nicht gelöscht hat, können Sie Ihre Entscheidung ändern',\n  'ali_req_deny_reason' => 'Ihr Aufnahmeantrag wurde abgelehnt',\n  'ali_req_emptyList' => 'Keine Anträge zur Prüfung',\n  'ali_req_inAlly' => 'Sie sind bereits Mitglied einer Allianz.',\n  'ali_req_make' => 'Antrag stellen',\n  'ali_req_not_allowed' => 'KEINE AUFNAHME',\n  'ali_req_otherRequest' => 'Sie haben bereits einen Antrag bei einer anderen Allianz gestellt.',\n  'ali_req_template' => 'Ich bitte um Aufnahme in Ihre Allianz',\n  'ali_req_text' => 'Antragstext',\n  'ali_req_title' => 'Allianzbeitrittsantrag',\n  'ali_req_waiting' => 'Ihr Aufnahmeantrag für die Allianz [%s] wird vom Allianzführer geprüft.<br>Sie werden über die Entscheidung benachrichtigt.',\n  'ali_req_check' => 'Antragsverwaltung',\n  'ali_req_requestCount' => 'Aufnahmeanträge',\n  'ali_req_admin_title' => 'Antragsübersicht',\n  'ali_req_accept' => 'Antrag annehmen',\n  'ali_req_deny' => 'Antrag ablehnen',\n  'ali_search_title' => 'Allianzsuche',\n  'ali_search_action' => 'Suchen',\n  'ali_search_tip' => 'Suche nach Teilen des Namens oder Tags der Allianz möglich',\n  'ali_search_result_none' => 'Keine Allianzen entsprechen Ihrer Suche.',\n  'ali_search_show_all' => 'Liste und Statistik aller Allianzen',\n  'ali_sys_name' => 'Name',\n  'ali_sys_tag' => 'Tag',\n  'ali_sys_members' => 'Mitglieder',\n  'ali_sys_notFound' => 'Allianz existiert nicht',\n  'ali_sys_memberName' => 'Name',\n  'ali_sys_points' => 'Punkte',\n  'ali_sys_lastActive' => 'Aktivität',\n  'ali_sys_totalMembers' => 'Gesamt',\n  'ali_sys_clear' => 'Zurücksetzen',\n  'ali_sys_main_page' => 'Zurück zur Allianzhauptseite',\n  'ali_sys_joined' => 'Beitrittsdatum',\n  'ali_frm_write' => 'Forum schreiben',\n  'ali_info_title' => 'Allianzinformationen',\n  'ali_info_internal' => 'Interne Informationen',\n  'ali_info_leave' => 'Allianz verlassen',\n  'ali_info_bonus_rate' => 'Bonusmultiplikator',\n  'Name' => 'Name',\n  'Tag' => 'Tag',\n  'Members' => 'Mitglieder',\n  'Accept_cand' => 'Annehmen',\n  'alliance' => 'Allianz',\n  'alliances' => 'Allianzen',\n  'Alliance_information' => 'Allianzinformationen',\n  'Alliance_logo' => 'Allianzlogo',\n  'alliance_tag' => 'Allianz-Tag',\n  'Allow_request' => 'Anträge annehmen',\n  'allyance_name' => 'Allianzname',\n  'ally_admin' => 'Allianzverwaltung',\n  'ally_been_maked' => 'Allianz %s erfolgreich gegründet',\n  'ally_description' => 'Allianzbeschreibung',\n  'ally_dissolve' => 'Allianz auflösen',\n  'Ally_info_1' => 'Allianzinformationen',\n  'ally_maked' => '%s gegründet',\n  'Ally_nodescription' => 'Allianz hat keine Beschreibung',\n  'ally_notexist' => 'Allianz existiert nicht mehr',\n  'Ally_not_exist' => 'Leider keine Informationen über diese Allianz verfügbar',\n  'Ally_transfer' => 'Allianz übertragen',\n  'All_players' => 'Alle Spieler',\n  'always_exist' => '%s existiert bereits',\n  'Aplication_acepted' => 'Sie wurden aufgenommen',\n  'Aplication_hello' => 'Willkommen<br>Allianz:',\n  'Aplication_rejected' => 'Ihr Aufnahmeantrag wurde abgelehnt.<br>Grund:<br>',\n  'apply_cantbeadded' => 'Antrag fehlgeschlagen, bitte erneut versuchen!',\n  'apply_registered' => 'Ihr Antrag wurde versendet.<br><br><a href=alliance.php>Zurück</a>',\n  'Back' => 'Zurück',\n  'Canceld_req_text' => 'Sie haben Ihren Antrag für [%s] zurückgezogen',\n  'Change' => 'Ändern',\n  'ch_allyname' => 'Allianznamen ändern',\n  'ch_allytag' => 'Allianz-Tag ändern',\n  'Circular_message' => 'Allianznachricht',\n  'Circular_sended' => 'Nachricht erfolgreich versendet',\n  'Clear' => 'Leeren',\n  'Click_writerequest' => 'Hier klicken um Antrag zu schreiben',\n  'Continue' => 'Fortsetzen',\n  'Delete_apply' => 'Antrag ablehnen',\n  'Denied_access' => 'Zugriff verweigert!',\n  'Destiny' => 'Empfänger',\n  'Exit_of_this_alliance' => 'Allianz verlassen',\n  'External_text' => 'Externer Text',\n  'Founder' => 'Gründer',\n  'Founder_name' => 'Gründertitel',\n  'Function' => 'Funktion',\n  'Go_out_welldone' => 'Sie haben die Allianz erfolgreich verlassen',\n  'have_not_name' => 'Allianznamen eingeben',\n  'have_not_tag' => 'Allianz-Tag eingeben',\n  'Help' => 'Hilfe',\n  'Inactive' => 'Inaktiv',\n  'Inner_section' => 'Interner Bereich',\n  'Internal_text' => 'Interner Text',\n  'knowed_allys' => 'Existierende Allianzen',\n  'laws_config' => 'Rechteverwaltung',\n  'Main_Page' => 'Startseite',\n  'make_alliance' => 'Allianz gründen',\n  'make_alliance_owner' => 'Allianz gründen',\n  'max' => 'max.',\n  'member' => 'Mitglied',\n  'memberlist_view' => 'Mitgliederliste anzeigen',\n  'members' => 'Mitglieder',\n  'members_admin' => 'Mitgliederverwaltung',\n  'Members_list' => 'Mitgliederliste',\n  'members_who_recived_message' => 'Folgende Allianzmitglieder haben die Nachricht erhalten:',\n  'Message' => 'Nachricht',\n  'Motive_optional' => 'Grund (optional)',\n  'New_name' => 'Neuer Name',\n  'New_tag' => 'Neuer Tag',\n  'not_allow_request' => 'Anträge ablehnen',\n  'Novate' => 'Neuling',\n  'Number' => 'Nr.',\n  'Off' => 'Offline',\n  'Ok' => 'OK',\n  'On' => 'Online',\n  'Online' => 'Status',\n  'Position' => 'Position',\n  'Public_text_of_alliance' => 'Öffentlicher Text',\n  'Range' => 'Rang',\n  'Reject_cand' => 'Ablehnen',\n  'Reload' => 'Beispiel',\n  'Repel' => 'Abweisen',\n  'requests_view' => 'Anträge anzeigen',\n  'Request_answer' => 'Antrag abgelehnt',\n  'Request_date' => 'Antragsdatum',\n  'Request_text' => 'Antragstext',\n  's' => '[N/A]',\n  'Search' => 'Suche',\n  'searchd_ally_avail' => 'Gefundene Allianzen:',\n  'search_alliance' => 'Suche',\n  'Send' => 'Senden',\n  'Send_Apply' => 'Antrag annehmen',\n  'Send_circular_mail' => 'Rundmail an Allianz senden',\n  'Set_range' => 'Rang ändern',\n  'Show_of_request_text' => 'Antragstext',\n  'Texts' => 'Textbearbeitung',\n  'Text_mail' => 'Nachricht an Allianz senden',\n  'top10alliance' => 'Top 10 Allianzen',\n  'transfer' => 'Übertragung',\n  'transfer_ally' => 'Allianzübertragung',\n  'transfer_to' => 'Allianz übertragen an:',\n  'Want_go_out' => 'Möchten Sie die Allianz wirklich verlassen?',\n  'write_apply' => 'Antrag stellen',\n  'your_alliance' => 'Ihre Allianz',\n  'your_apply' => 'Ihr Antrag',\n  'ali_info_leave_success' => 'Sie haben die Allianz [%s] verlassen.<br />Sie können nun eine eigene Allianz gründen oder einen Aufnahmeantrag bei einer anderen Allianz stellen<br />',\n\n  'opt_avatar' => 'Allianzlogo',\n  'opt_avatar_search' => 'Bei Google suchen',\n  'opt_avatar_remove' => 'Logo entfernen',\n  'opt_upload' => 'Hochladen',\n\n  'opt_msg_avatar_removed' => 'Logo wurde entfernt',\n  'opt_msg_avatar_uploaded' => 'Logo erfolgreich geändert',\n  'opt_msg_avatar_error_delete' => 'Fehler beim Löschen des Logos. Bitte wenden Sie sich an die Serveradministration',\n  'opt_msg_avatar_error_writing' => 'Fehler beim Speichern des Logos. Bitte wenden Sie sich an die Serveradministration',\n  'opt_msg_avatar_error_upload' => 'Fehler beim Hochladen des Bildes %1. Bitte wenden Sie sich an die Serveradministration',\n  'opt_msg_avatar_error_unsupported' => 'Das Bildformat wird nicht unterstützt. Nur JPG, GIF, PNG Dateien bis 200KB sind erlaubt',\n\n  'ali_admin_mercenaries' => 'Allianz-Söldner',\n  'ali_admin_plans' => 'Allianz-Pläne',\n  'ali_admin_techs' => 'Allianz-Forschung',\n  'ali_admin_market_trader' => 'Ressourcentausch am Schwarzmarkt',\n\n  'ali_res_player_bonus' => 'Mitgliederbonus',\n  'ali_res_transfer' => 'Übertragen',\n  'ali_res_transfer_long' => 'Auf Allianzkonto übertragen',\n  'ali_res_no_resources' => 'Allianz hat keine Ressourcen',\n  'ali_res_transfer_dm_log' => 'Mitglied \\'%s\\' hat %d DM auf das Konto der Allianz [%s] übertragen',\n\n  'ali_res_err_not_enough' => 'Nicht genug %s!',\n  'ali_res_err_wrong_unit' => 'Nur Ressourcen können auf das Allianzkonto übertragen werden!',\n\n  'ali_res_alliance_bonus' => 'Allianz-Boni',\n  'ali_res_alliance_bonus_players' => 'Anzahl Mitglieder für Bonus',\n\n  'ally_message_tag_exists' => 'Allianz mit Tag [%1$s] existiert bereits',\n  'ally_message_name_exists' => 'Allianz mit Namen \"%1$s\" existiert bereits',\n\n  'ally_alliances_recommended' => 'Empfohlene Allianzen',\n  'ally_recommended_diff' => 'Punkteunterschied',\n  'ally_recommended_rates' => 'Rate',\n  'ali_search_result_tip' => '\n    <li>Klicken Sie auf den Namen oder Tag der Allianz, um Informationen anzuzeigen</li>\n    <li>Klicken Sie auf \"Antrag senden\", um einen Aufnahmeantrag zu stellen</li>\n    <li>Die Spalte \"Punkteunterschied\" zeigt die Differenz zwischen Ihren Punkten und dem Durchschnitt der Allianz. Ein negativer Wert bedeutet, dass der Durchschnittsspieler der Allianz mehr Punkte hat als Sie</li>\n    <li>Die Spalte \"Rate\" zeigt das Verhältnis Ihrer Punkte zum Allianzdurchschnitt. Ein Wert unter 1 bedeutet, dass der Durchschnittsspieler schwächer ist als Sie</li>\n    <li>Empfohlen werden Allianzen mit geringem Punkteunterschied und einer Rate zwischen 0,75 und 1,3. Die endgültige Entscheidung liegt jedoch beim Spieler</li>\n   ',\n];"
  },
  {
    "path": "language/de/announce.mo.php",
    "content": "<?php\n\n/*\n#############################################################################\n#  Filename: announce.mo\n#  Project: SuperNova.WS\n#  Website: http://www.supernova.ws\n#  Description: Massen-Mehrspieler-Online-Browser-Weltraumstrategiespiel\n#\n#  Copyright © 2009-2018 Gorlum for Project \"SuperNova.WS\"\n#############################################################################\n*/\n\n/**\n*\n* @package language\n* @system [German]\n* @version 46d0\n*\n*/\n\n/**\n* DO NOT CHANGE\n*/\n\nif (!defined('INSIDE')) die();\n\n$a_lang_array = (array(\n  'add_announce' => 'Ankündigung hinzufügen',\n  'metal' => 'Metall',\n  'crystal' => 'Kristall',\n  'deuterium' => 'Deuterium',\n  'Resources_to_be_sold' => 'Angebotene Ressourcen',\n  'Desired_resources' => 'Gewünschte Ressourcen',\n  'send' => 'Absenden',\n  'Your_announce_was_recorded' => 'Ihre Ankündigung wurde erfolgreich veröffentlicht',\n  'return_to_announce' => 'Zurück zur Ankündigungsseite',\n  'Classifieds' => 'Veröffentlichte Ankündigungen',\n  'Action' => 'Aktion',\n  'Galaxy' => 'Galaxie',\n  'Solar_system' => 'Sonnensystem',\n  'Infos_of_delivery' => 'Händlerinformationen',\n  'Salesman' => 'Spieler',\n  'Delete' => 'Löschen',\n  'announce_status' => 'Ankündigungsstatus',\n  'Your_announce_not_recorded' => 'Ihre Ankündigung wurde nicht veröffentlicht',\n  'Your_announce_was_deleted' => 'Ihre Ankündigung wurde erfolgreich gelöscht',\n));"
  },
  {
    "path": "language/de/artifacts.mo.php",
    "content": "<?php\n\n/*\n#############################################################################\n#  Filename: artifacts.mo\n#  Project: SuperNova.WS\n#  Website: http://www.supernova.ws\n#  Description: Massen-Mehrspieler-Online-Browser-Weltraumstrategiespiel\n#\n#  Copyright © 2009-2018 Gorlum for Project \"SuperNova.WS\"\n#############################################################################\n*/\n\n/**\n*\n* @package language\n* @system [German]\n* @version 46d0\n*\n*/\n\n/**\n* DO NOT CHANGE\n*/\n\nif (!defined('INSIDE')) die();\n\n$a_lang_array = (array(\n  'art_use'             => 'Artefakt verwenden',\n\n  'art_lhc_from'          => 'Großer Hadronen-Speicherring',\n  'art_lhc_subj'          => 'Monderschaffungsversuch',\n  'art_moon_create'   => array(\n    ART_LHC => 'Die Gravitationswelle des GHS verband große Metall- und Kristallbrocken in der Umlaufbahn, wodurch ein neuer Mond %s bei den Koordinaten %s entstand!',\n    ART_HOOK_SMALL => 'Kleiner Haken erschuf Mond %1$s mit einem Durchmesser von %3$s Kilometern bei den Koordinaten %2$s!',\n    ART_HOOK_MEDIUM => 'Mittlerer Haken erschuf Mond %1$s mit einem Durchmesser von %3$s Kilometern bei den Koordinaten %2$s!',\n    ART_HOOK_LARGE => 'Großer Haken erschuf Mond %1$s mit einem Durchmesser von %3$s Kilometern bei den Koordinaten %2$s!',\n  ),\n  'art_moon_exists'   => 'In der Mondumlaufbahn an diesen Koordinaten existiert bereits ein Mond',\n  'art_lhc_moon_fail'     => 'Die Gravitationswelle des GHS war nicht stark genug, um einen neuen Mond zu erschaffen',\n\n  'art_rcd_from'          => 'Autonomes Kolonisierungs-Komplex',\n  'art_rcd_subj'          => 'Kolonie errichtet',\n  'art_rcd_ok'            => '%1$s hat erfolgreich eine Kolonie auf Planet %2$s bei den Koordinaten %3$s errichtet',\n  'art_rcd_err_moon'      => 'AKK kann nur auf Planeten eingesetzt werden',\n  'art_rcd_err_no_sense'  => 'AKK erkannte, dass keine Gebäude verbessert werden würden und brach den Einsatz ab',\n  'art_rcd_err_que'       => 'AKK kann nicht auf Planeten mit laufenden Bauaufträgen eingesetzt werden. Bitte alle Bauaufträge abbrechen und den AKK erneut einsetzen',\n\n  'art_heurestic_chip_ok' => 'Forschungszeit für Technologie \"%s\" (Stufe %d) wurde um %s reduziert',\n  'art_heurestic_chip_subj' => 'Forschungszeitbeschleunigung',\n  'art_heurestic_chip_no_research' => 'Derzeit wird keine Forschung durchgeführt oder die aktuelle Forschungszeit beträgt weniger als 1 Minute',\n\n  'art_nano_builder_ok' => 'Die %s-Zeit für Gebäude \"%s\" (Stufe %d) auf Planet %s %s wurde um %s reduziert',\n  'art_nano_builder_build' => 'Bau',\n  'art_nano_builder_destroy' => 'Abriss',\n  'art_nano_builder_subj' => 'Bauoperationsbeschleunigung',\n  'art_nano_builder_no_que' => 'Derzeit werden auf diesem Planeten keine Bauoperationen durchgeführt oder die aktuelle Operationszeit beträgt weniger als 1 Minute',\n\n  'art_err_no_artifact'  => 'Sie besitzen das benötigte Artefakt nicht',\n\n  'art_page_hint'        => '<ul>\n    <li>Artefakte sind seltene Objekte mit einzigartigen Eigenschaften</li>\n    <li>Artefakte sind Einweg-Gegenstände - nach Gebrauch verschwinden sie</li>\n    <li>Manche Artefakte sind so mächtig, dass ihre Anzahl in einem Imperium begrenzt ist</li>\n    <li>Normalerweise wirken Artefakte auf den Anwendungsplaneten, aber einige haben imperiumsweite Effekte.\n    Die seltensten und wertvollsten Artefakte können auf ganze Sonnensysteme, Galaxien oder sogar das gesamte Universum wirken!</li>\n  </ul>',\n));"
  },
  {
    "path": "language/de/buddy.mo.php",
    "content": "<?php\n\n/*\n#############################################################################\n#  Filename: buddy.mo\n#  Project: SuperNova.WS\n#  Website: http://www.supernova.ws\n#  Description: Massen-Mehrspieler-Online-Browser-Weltraumstrategiespiel\n#\n#  Copyright © 2009-2018 Gorlum for Project \"SuperNova.WS\"\n#############################################################################\n*/\n\n/**\n*\n* @package language\n* @system [German]\n* @version 46d0\n*\n*/\n\n/**\n* DO NOT CHANGE\n*/\n\nif (!defined('INSIDE')) die();\n\n$a_lang_array = (array(\n  'buddy_buddies' => 'Freunde',\n  'buddy_request_text' => 'Anfragetext',\n  'buddy_request_text_default' => 'Ich bitte um Aufnahme in Ihre Freundesliste',\n  'buddy_request_none' => 'Keine Freunde oder Freundschaftsanfragen vorhanden',\n  'buddy_request_write_header' => 'Freundschaftsanfrage senden',\n  'buddy_request_player_name' => 'Spielername',\n  'buddy_request_accept' => 'Spieler zur Freundesliste hinzufügen',\n\n  'buddy_status' => 'Status',\n  'buddy_status_active' => 'Dies ist Ihr gegenseitiger Freund',\n  'buddy_status_incoming_waiting' => 'Sie haben eine Freundschaftsanfrage erhalten',\n  'buddy_status_incoming_denied' => 'Sie haben die Freundschaftsanfrage abgelehnt',\n  'buddy_status_outcoming_waiting' => 'Ihre Anfrage wurde gesendet. Bitte warten Sie auf Antwort',\n  'buddy_status_outcoming_denied' => 'Ihre Anfrage wurde abgelehnt',\n\n  // Ergebnisnachrichten\n  'buddy_err_not_exist' => 'Die angegebene Anfrage existiert nicht. Möglicherweise wurde sie gelöscht, abgelehnt oder vom Absender zurückgezogen',\n\n  'buddy_err_accept_own' => 'Sie können Ihre eigene Anfrage nicht annehmen',\n  'buddy_err_accept_alien' => 'Sie können keine Anfrage annehmen, die nicht an Sie gerichtet ist',\n  'buddy_err_accept_already' => 'Sie haben diese Anfrage bereits angenommen und sind mit diesem Spieler befreundet',\n  'buddy_err_accept_denied' => 'Sie haben diese Anfrage bereits abgelehnt und können sie nun nicht mehr annehmen',\n  'buddy_err_accept_internal' => 'Bei der Annahme der Anfrage ist ein Fehler aufgetreten. Bitte versuchen Sie es später erneut. Falls der Fehler bestehen bleibt, wenden Sie sich an die Serveradministration',\n  'buddy_err_accept_none' => 'Anfrage erfolgreich angenommen',\n\n  'buddy_err_delete_alien' => 'Diese Anfrage wurde nicht von Ihnen erstellt! Mischen Sie sich nicht in die Beziehungen anderer ein! Suchen Sie sich lieber eigene Freunde!',\n  'buddy_err_unfriend_none' => 'Sie haben die Freundschaft beendet',\n  'buddy_err_delete_own' => 'Ihre Anfrage wurde erfolgreich gelöscht',\n\n  'buddy_err_deny_none' => 'Sie haben die Freundschaft mit diesem Spieler abgelehnt. Warum?',\n\n  'buddy_err_adding_exists' => 'Anfrage nicht möglich - Sie sind bereits befreundet oder es existieren bereits Freundschaftsanfragen zwischen Ihnen',\n  'buddy_err_adding_none' => 'Ihre Freundschaftsanfrage wurde gesendet',\n  'buddy_err_adding_self' => 'Sie können sich nicht selbst als Freund hinzufügen',\n\n  // PN-Nachrichten\n  'buddy_msg_accept_title' => 'Sie haben einen neuen Freund!',\n  'buddy_msg_accept_text' => 'Spieler %s hat Sie zu seiner Freundesliste hinzugefügt!',\n  'buddy_msg_unfriend_title' => 'Sie haben einen Freund verloren!',\n  'buddy_msg_unfriend_text' => 'Spieler %s hat die Freundschaft mit Ihnen beendet und Sie von seiner Freundesliste entfernt. Wie traurig...',\n  'buddy_msg_deny_title' => 'Freundschaftsanfrage abgelehnt',\n  'buddy_msg_deny_text' => 'Spieler %s möchte nicht mit Ihnen befreundet sein',\n  'buddy_msg_adding_title' => 'Freundschaftsanfrage',\n  'buddy_msg_adding_text' => 'Spieler %s möchte mit Ihnen befreundet sein',\n\n  'buddy_hint' => '\n    <li>Freundschaftsanfragen können über das Menü <a href=\"search.php\">Suche</a> gesendet werden</li>\n    <li>Sie können den Status Ihrer Freunde sehen - ob sie online oder offline sind. Beachten Sie jedoch, dass auch Ihre Freunde Ihren Status sehen können. Bedenken Sie dies, bevor Sie Freundschaftsanfragen annehmen.</li>\n    <li>Wenn Sie eine Freundschaftsanfrage abgelehnt haben, können Sie erst dann eine Freundschaft mit diesem Spieler eingehen, wenn er seine Anfrage löscht</li>',\n));"
  },
  {
    "path": "language/de/buildings.mo.php",
    "content": "<?php\n\n/*\n#############################################################################\n#  Filename: buildings.mo\n#  Project: SuperNova.WS\n#  Website: http://www.supernova.ws\n#  Description: Massen-Mehrspieler-Online-Browser-Weltraumstrategiespiel\n#\n#  Copyright © 2009-2018 Gorlum for Project \"SuperNova.WS\"\n#############################################################################\n*/\n\n/**\n*\n* @package language\n* @system [German]\n* @version 46d0\n*\n*/\n\n/**\n* DO NOT CHANGE\n*/\n\nif (!defined('INSIDE')) die();\n\n$a_lang_array = array(\n  'built' => 'Gebaut',\n  'Fleet' => 'Flotte',\n  'fleet' => 'Flotte',\n  'Defense' => 'Verteidigung',\n  'defense' => 'Verteidigung',\n  'Research' => 'Forschung',\n  'level' => 'Stufe',\n  'dispo' => 'Verfügbar',\n  'load_det' => 'Klicken Sie auf das Bild für 3D-Ansicht',\n  'off_det' => 'Erneut klicken deaktiviert 3D-Ansicht',\n  'allowed_aya' => 'Verfügbare',\n  'allowed_ye' => 'Verfügbare',\n  'allowed_yi' => 'Verfügbarer',\n  'mech_info' => 'Technische Spezifikationen',\n  'fst_bld_load' => 'Auftrag wird bearbeitet.<br>Bitte warten...',\n  'fst_bld' => 'Schnellauftrag:',\n  'price' => 'Kosten',\n  'builds' => 'Gebäude',\n  'destroy_price' => 'Abbaukosten',\n  'no_fields' => 'Keine freien Felder auf dem Planeten',\n  'can_build' => 'Bau möglich: ',\n  'Requirements' => 'Voraussetzungen: ',\n  'Requires' => 'Benötigte Ressourcen ',\n  'Rest_ress' => 'Verbleibende Ressourcen ',\n  'Rest_ress_fleet' => 'Inkl. ankommender Flotten',\n  'Rechercher' => 'Erforschen',\n  'ConstructionTime' => 'Bauzeit ',\n  'DestructionTime' => 'Abbauzeit ',\n  'ResearchTime' => 'Forschungszeit ',\n  'Construire' => 'Bauen',\n  'BuildFirstLevel' => 'Bauen',\n  'BuildNextLevel' => 'Nächste Stufe bauen ',\n  'completed' => 'Abgeschlossen',\n  'in_working' => 'Beschäftigt',\n  'work_todo' => 'Beschäftigt',\n  'total_left_time' => 'Verbleibende Zeit',\n  'only_one' => 'Sie können nur einen Schildgenerator bauen.',\n  'b_no_silo_space' => 'Raketensilo ist voll.',\n  'que_full' => 'Bauwarteschlange ist voll!',\n  'Build_lab' => 'Baufehler',\n  'NoMoreSpace' => 'Planet ist voll!',\n  'InBuildQueue' => 'In Bauwarteschlange',\n  'bld_usedcells' => 'Belegte Felder',\n  'bld_theyare' => 'Es sind',\n  'bld_cellfree' => 'freie Felder vorhanden',\n  'DelFromQueue' => 'abbrechen',\n  'DelFirstQueue' => 'Pausieren',\n  'cancel' => 'Abbrechen',\n  'continue' => 'Fortsetzen',\n  'ready' => 'Warten',\n  'destroy' => 'Abbauen',\n  'on' => 'auf',\n  'attention' => 'Achtung! Hackversuch festgestellt! Aktion wurde protokolliert!',\n  'no_laboratory' => 'Forschungslabor nicht gebaut!',\n  'need_hangar' => 'Werft nicht gebaut!',\n  'labo_on_update' => 'Labor wird aktualisiert!',\n  'fleet_on_update' => 'Werft wird modernisiert!',\n  'Total_techs' => 'Gesamte Forschungen',\n  'eco_bld_page_hint' => '<ul><li>Für Einheiteninformationen Mauszeiger über Bild bewegen</li>\n  <li>Klick auf Bild wählt Einheit. Erneuter Klick hebt Auswahl auf</li>\n  <li>Detaillierte Beschreibung durch Klick auf blaues \"i\"-Symbol</li>\n  <li>Bau möglich durch Klick auf \"+\" oder \"Bauen\"-Link</li>\n  <li>Abbau durch Klick auf \"-\" oder entsprechenden Link</li></ul>',\n  'eco_price' => 'Kosten',\n  'eco_left' => 'Rest',\n  'eco_bld_resources_not_enough' => 'Nicht genug Ressourcen für Bauaufträge',\n\n  'eco_bld_msg_err_research_in_progress' => 'Forschung läuft bereits',\n  'eco_bld_msg_err_not_research' => 'Nur Technologien können erforscht werden',\n  'eco_bld_msg_err_requirements_not_meet' => 'Forschungsvoraussetzungen nicht erfüllt',\n  'eco_bld_msg_err_laboratory_upgrading' => 'Forschungslabore werden umgebaut.<br/><br/>Während Bau/Umbau von Laboren (auch in Warteschlange) ist Forschung nicht möglich.<br/><br/>Entfernen Sie alle Labor-Bauaufträge um Forschung zu starten',\n\n  'eco_bld_unit_info_extra_show' => 'Zusatzinformationen anzeigen',\n  'eco_bld_unit_info_extra_hide' => 'Zusatzinformationen verbergen',\n  'eco_bld_unit_info_extra_none' => 'Keine Zusatzinformationen',\n\n  'eco_bld_autoconvert' => 'Autokonvertierung',\n  'eco_bld_autoconvert_explain' => 'Fehlende Ressourcen werden automatisch umgewandelt (Metall, Kristall, Deuterium) und Bau/Forschung gestartet.\\r\\n\\r\\n',\n  'eco_bld_autoconvert_dark_matter_none' => 'Für Autokonvertierung fehlen {0} Dunkle Materie.',\n  'eco_bld_autoconvert_confirm' => 'Diese Aktion kostet {0} Dunkle Materie.\\r\\n\\r\\nFortfahren?',\n\n  'eco_que_clear_dialog_title' => 'Warteschlange leeren',\n  'eco_que_clear_dialog_text' => 'Diese Aktion löscht die gesamte Warteschlange!<br /><br />Alle unfertigen Bauten/Forschungen werden abgebrochen.<br />Ressourcen werden zurückerstattet.<br /><br />Wirklich fortfahren?',\n\n  'eco_que_artifact_dialog_title' => '{0} verwenden',\n  'eco_que_artifact_dialog_text' => \"Artefakt \\\"{0}\\\" beschleunigt Bau/Forschung.<br /><br />Bei >1h Restzeit: Halbierung<br />Bei <1h: Sofortige Fertigstellung<br /><br />Nicht nutzbar bei <1min Restzeit\",\n\n  'eco_bld_research_page_name' => 'Technologieforschung',\n  'eco_bld_research_page_novapedia' => 'Technologieliste in Novapedia',\n);"
  },
  {
    "path": "language/de/chat_advanced.mo.php",
    "content": "<?php\n\n/*\n#############################################################################\n#  Filename: chat_advanced.mo.php\n#  Project: SuperNova.WS\n#  Website: http://www.supernova.ws\n#  Description: Massen-Mehrspieler-Online-Browser-Weltraumstrategiespiel\n#\n#  Copyright © 2012-2018 Gorlum for Project \"SuperNova.WS\"\n#############################################################################\n*/\n\n/**\n*\n* @package language\n* @system [German]\n* @version 46d0\n*\n*/\n\n/**\n* DO NOT CHANGE\n*/\n\nif (!defined('INSIDE')) die();\n\n$a_lang_array = (array(\n  'chat_advanced_chat_players' => 'Spieler im Chat',\n  'chat_advanced_online_players' => 'Spieler online',\n  'chat_advanced_online_invisibles' => 'Davon unsichtbar',\n  'chat_advanced_invisibility' => 'Unsichtbarkeit',\n\n  'chat_advanced_frame_on' => 'Anheften',\n  'chat_advanced_frame_off' => 'Lösen',\n\n  'chat_advanced_smile_tooltip' => 'Klicken um Smiley auszuwählen',\n\n  'chat_advanced_visible' => array(\n    0 => 'Sie sind für andere Spieler sichtbar',\n    1 => 'Sie sind für andere Spieler unsichtbar',\n  ),\n\n  'chat_advanced_help_description' => \"Verwenden Sie \\\"/help <Befehl>\\\" für detaillierte Informationen zu einem bestimmten Befehl. Zum Beispiel: \\\"/help whisper\\\"\",\n  'chat_advanced_help_commands_accessible' => 'Folgende Chat-Befehle stehen Ihnen zur Verfügung:',\n  'chat_advanced_help_command' => 'Befehl \"/%s\"',\n  'chat_advanced_help_command_aliases' => 'Aliase dieses Befehls: ',\n\n  'chat_advanced_whisper_recipient_prefix' => '',\n  'chat_advanced_whisper_recipient_midfix' => ' -> ',\n  'chat_advanced_whisper_recipient_suffix' => '> ',\n  'chat_advanced_whisper_sender_prefix' => '',\n  'chat_advanced_whisper_sender_midfix' => ' -> ',\n  'chat_advanced_whisper_sender_suffix' => '> ',\n\n  'chat_advanced_command_reason' => '. Grund: %s',\n  'chat_advanced_command_reason2' => 'Grund:',\n  'chat_advanced_command_mute' => 'Spieler \"%1$s\" wurde bis %2$s Serverzeit gesperrt%3$s',\n  'chat_advanced_command_unmute' => 'Spieler \"%s\" darf wieder chatten',\n  'chat_advanced_command_ban' => 'Spieler \"%1$s\" wurde bis %2$s Serverzeit gebannt',\n  'chat_advanced_command_ban_no_vacancy' => 'Spieler \"%1$s\" wurde OHNE URLAUBSMODUS bis %2$s Serverzeit gebannt',\n  'chat_advanced_command_unban' => 'Spieler \"%s\" wurde entbannt',\n\n  'chat_advanced_command_interval' => array(\n    '1h' => '1 Stunde',\n    '3h' => '3 Stunden',\n    '6h' => '6 Stunden',\n    '12h' => '12 Stunden',\n    '1d' => '1 Tag',\n    '3d' => '3 Tage',\n    '1w' => '1 Woche',\n    '2w' => '2 Wochen',\n    '1m' => '30 Tage',\n    '2m' => '60 Tage',\n    '3m' => '90 Tage',\n    '10y' => 'Permanent*',\n  ),\n  'chat_advanced_ban_vacancy' => 'Urlaubsmodus',\n\n  'chat_advanced_online_ban' => 'Spieler \"%1$s\" bannen für...',\n  'chat_advanced_online_mute' => 'Spieler \"%1$s\" chatten sperren für...',\n  'chat_advanced_online_unmute' => 'Spieler \"%1$s\" Chatsperre aufheben',\n  'chat_advanced_online_invisible' => 'Unsichtbarer Spieler',\n  'chat_advanced_online_banned_via_chat' => 'Vom Chat gebannt',\n\n  'chat_advanced_help' => array(\n    'help' => \"Der Befehl '/help' bietet detaillierte Hilfe zu allen verfügbaren Chat-Befehlen\\r\\n\n               Syntax: /help [<Befehlsname>]\\r\\n\n               <Befehlsname> ist optional. Ohne Angabe wird eine Befehlsliste angezeigt. Aliase sind möglich, z.B. '/help w' statt '/help whisper'\",\n    'whisper' => \"Der Befehl '/whisper' sendet eine private Nachricht an einen bestimmten Spieler. Private Nachrichten erscheinen in allen Chat-Kanälen, sind aber nur für Sie und den Empfänger sichtbar.\\r\\n\n                  Klicken Sie auf einen Namen in der Online-Liste, um den Befehl automatisch einzufügen.\\r\\n\n                  Syntax: /whisper <Spielername> <Nachricht>\\r\\n\n                  Bei Spielernamen mit Leerzeichen oder Sonderzeichen setzen Sie den Namen in Anführungszeichen:\\r\\n\n                  /w \\\"Name mit Leerzeichen\\\" Hallo!\",\n    'ban' => \"Der Befehl '/ban' sperrt einen Spieler für die angegebene Zeit. Das entsprechende Symbol in der Online-Liste bannt für 1 Woche.\\r\\n\n              Syntax: /ban id <Spieler-ID> <Dauer>[!] [<Grund>]\\r\\n\n              Die Spieler-ID sehen Sie beim Überfahren des Namens in der Online-Liste.\\r\\n\n              <Dauer> Format: <Zahl>{y|m|w|d|h} (Jahre|Monate|Wochen|Tage|Stunden)\\r\\n\n              Ein '!' deaktiviert den Urlaubsmodus.\\r\\n\n              <Grund> ist optional und wird im Chat und den Ban-Protokollen angezeigt.\",\n    'unban' => \"Der Befehl '/unban' hebt einen Bann auf.\\r\\n\n                Syntax: /unban id <Spieler-ID>\",\n    'mute'  => \"Der Befehl '/mute' sperrt einen Spieler für den Chat. Die Sperre gilt für alle Kanäle und private Nachrichten. Das entsprechende Symbol in der Online-Liste sperrt für 1 Stunde.\\r\\n\n                Syntax: /mute id <Spieler-ID> <Dauer> [<Grund>]\\r\\n\n                <Dauer> Format: <Zahl>{y|m|w|d|h}\\r\\n\n                <Grund> ist optional und wird im Chat angezeigt.\",\n    'unmute' =>  \"Der Befehl '/unmute' hebt eine Chat-Sperre auf.\\r\\n\n                  Syntax: /unmute id <Spieler-ID>\",\n    'invisible' => \"Der Befehl '/invisible' macht Sie im Chat unsichtbar. Das Häkchen \\\"Unsichtbarkeit\\\" hat denselben Effekt.\\r\\n\n                    Die Unsichtbarkeit gilt global für alle Chat-Kanäle.\\r\\n\n                    Syntax: /invisible [on|off]\\r\\n\n                    /invisible on - Unsichtbar schalten\\r\\n\n                    /invisible off - Sichtbar schalten\\r\\n\n                    /invisible - Zeigt Ihren Unsichtbarkeitsstatus\",\n  ),\n\n  'chat_advanced_help_short' => array(\n    'help' => '/help',\n    'whisper' => '/whisper',\n    'ban' => '/ban',\n    'unban' => '/unban',\n    'mute' => '/mute',\n    'unmute' => '/unmute',\n    'invisible' => '/invisible',\n  ),\n\n  'chat_advanced_err_command_inacessible' => 'Dieser Chat-Befehl ist nicht verfügbar. Nutzen Sie \"/help\" für eine Befehlsliste',\n  'chat_advanced_err_command_unknown' => 'Unbekannter Befehl',\n  'chat_advanced_err_player_name_unknown' => 'Spieler existiert nicht',\n  'chat_advanced_err_message_empty' => 'Leere Nachrichten können nicht gesendet werden',\n  'chat_advanced_err_message_player_empty' => 'Geben Sie einen Spielernamen für die Nachricht an',\n  'chat_advanced_err_player_id_need' => 'Für diesen Befehl wird eine Spieler-ID benötigt',\n  'chat_advanced_err_player_id_incorrect' => 'Ungültige ID',\n  'chat_advanced_err_player_id_unknown' => 'Spieler-ID existiert nicht',\n  'chat_advanced_err_player_same' => 'Dieser Befehl kann nicht auf sich selbst angewendet werden',\n  'chat_advanced_err_player_higher' => 'Dieser Befehl kann nicht auf Spieler mit gleichen oder höheren Rechten angewendet werden',\n  'chat_advanced_err_term_need' => 'Für diesen Befehl wird eine Dauer benötigt',\n  'chat_advanced_err_term_wrong' => 'Ungültige Dauerangabe',\n));"
  },
  {
    "path": "language/de/fleet.mo.php",
    "content": "<?php\n\n/*\n#############################################################################\n#  Filename: fleet.mo\n#  Project: SuperNova.WS\n#  Website: http://www.supernova.ws\n#  Description: Massen-Mehrspieler-Online-Browser-Weltraumstrategiespiel\n#\n#  Copyright © 2009-2018 Gorlum for Project \"SuperNova.WS\"\n#############################################################################\n*/\n\n/**\n*\n* @package language\n* @system [German]\n* @version 46d0\n*\n*/\n\n/**\n* DO NOT CHANGE\n*/\n\nif (!defined('INSIDE')) die();\n\n$a_lang_array = (array(\n  'flt_page2_title' => 'Auftragsauswahl',\n  'fl_title' => 'Flotten',\n  'fl_expttl' => 'Expeditionen',\n  'fl_mission' => 'Auftrag',\n  'fl_count' => 'Anzahl',\n  'fl_count_short' => 'Anz',\n  'fl_where' => 'Ort',\n  'fl_from' => 'Start',\n  'fl_from_t' => 'Rückkehr',\n  'fl_start_t' => 'Zeit',\n  'fl_dest' => 'Ziel',\n  'fl_dest_t' => 'Ankunft',\n  'fl_back_t' => 'Rückkehr',\n  'fl_back_in' => 'Verbleibend',\n  'fl_order' => 'Befehl',\n  'fl_get_to' => '(H)',\n  'fl_get_to_ttl' => 'Hinweg',\n  'fl_back_to' => '(R)',\n  'fl_back_to_ttl' => 'Rückweg',\n  'fl_associate' => 'Kampfgruppe',\n  'fl_noslotfree' => 'Kein Flottenkommandant verfügbar!',\n  'fl_notback' => 'Flotte kann nicht zurückgerufen werden!',\n  'fl_onlyyours' => 'Nur eigene Flotten können zurückgerufen werden!',\n  'fl_isback' => 'Flotte kehrt zurück',\n  'fl_sback' => 'Zurück',\n  'fl_error' => 'Fehler',\n  'fl_new_miss' => 'Neuer Auftrag: Schiffe auswählen',\n  'fl_fleet_typ' => 'Schiffstyp',\n  'fl_fleet_disp' => 'Anzahl',\n  'fl_noplanetrow' => 'Systemfehler aufgetreten!',\n  'fl_fleetspeed' => 'Geschwindigkeit: ',\n  'fl_selmax' => 'max',\n  'fl_sur' => 'von',\n  'fl_continue' => 'Weiter',\n  'fl_noships' => 'Keine Schiffe im Orbit',\n  'fl_unselectall' => 'Alle abwählen',\n  'fl_selectall' => 'Alle Schiffe',\n  'fl_orbiting' => 'Im Orbit',\n  'fl_to_fly' => 'Starten',\n  'fl_no_flying_fleets' => 'Keine Flotten unterwegs',\n  'fl_floten1_ttl' => 'Zielauswahl',\n  'fl_noenought' => 'Nicht genügend Schiffe!',\n  'fl_speed' => 'Geschwindigkeit',\n  'fl_planet' => 'Planet',\n  'fl_ruins' => 'Trümmerfeld',\n  'fl_moon' => 'Mond',\n  'fl_dist' => 'Entfernung',\n  'fl_fltime' => 'Flugdauer (einfache Strecke)',\n  'fl_time_go' => 'Ankunft am Ziel',\n  'fl_time_back' => 'Rückkehr zum Start',\n  'fl_deute_need' => 'Treibstoffverbrauch',\n  'fl_speed_max' => 'Maximalgeschwindigkeit',\n  'fl_shortcut' => 'Koordinaten-Lesezeichen',\n  'fl_shortlnk' => 'Lesezeichen bearbeiten',\n  'fl_shrtcup1' => '(P)',\n  'fl_shrtcup2' => '(T)',\n  'fl_shrtcup3' => '(M)',\n  'fl_planettype1' => 'Planet',\n  'fl_planettype2' => 'Trümmerfeld',\n  'fl_planettype3' => 'Mond',\n  'fl_myplanets' => 'Planeten',\n  'fl_nocolonies' => 'keine Planeten',\n  'fl_noacss' => 'Keine Kampfgruppen-Einladungen',\n  'fl_grattack' => 'Kampfgruppen',\n  'fl_allressources' => 'Alle Ressourcen',\n  'fl_space_left' => 'Laderaum frei',\n  'fl_attack_error' => array(\n    ATTACK_ALLOWED => 'Flotte erfolgreich gestartet',\n    ATTACK_NO_TARGET => 'Ziel existiert nicht',\n    ATTACK_OWN => 'Eigene Planeten können nicht angegriffen werden',\n    ATTACK_WRONG_MISSION => 'Auftrag für dieses Ziel nicht möglich',\n    ATTACK_NO_ALLY_DEPOSIT => 'Kein Allianzlager auf dem Planeten',\n    ATTACK_NO_DEBRIS => 'Trümmerfeld existiert nicht',\n    ATTACK_VACATION => 'Spieler im Urlaubsmodus können nicht angegriffen werden',\n    ATTACK_SAME_IP => 'MULTI-ACCOUNT-SCHUTZ!<br>Interaktion mit gleicher IP nicht möglich',\n    ATTACK_BUFFING => 'Ressourcentransfer von schwachen zu starken Spielern ist verboten',\n    ATTACK_ADMIN => 'Administratoren können nicht angegriffen werden',\n    ATTACK_NOOB => 'Dieser Spieler ist zu schwach für Sie',\n    ATTACK_OWN_VACATION => 'Sie sind im Urlaubsmodus',\n    ATTACK_NO_SILO => 'Raketensilo-Level zu niedrig',\n    ATTACK_NO_MISSILE => 'Keine Raketen für Angriff verfügbar',\n    ATTACK_NO_FLEET => 'Leere Flotte kann nicht gestartet werden',\n    ATTACK_NO_SLOTS => 'Kein Flottenkommandant verfügbar',\n    // ATTACK_NO_SHIP => 'Nicht genügend Schiffe oder Ressourcen',\n    ATTACK_NO_RECYCLERS => 'Keine Recycler in der Flotte',\n    ATTACK_NO_SPIES => 'Keine Spionagesonden in der Flotte',\n    ATTACK_NO_COLONIZER => 'Kein Kolonieschiff in der Flotte',\n    ATTACK_MISSILE_TOO_FAR => 'Raketenreichweite zu gering',\n    ATTACK_WRONG_STRUCTURE => 'Raketen können nur Verteidigungsanlagen angreifen',\n    ATTACK_NO_FUEL => 'Nicht genug Deuterium für Flug',\n    ATTACK_NO_RESOURCES => 'Nicht genug Ressourcen für Transport',\n    ATTACK_NO_ACS => 'Kampfgruppe existiert nicht',\n    ATTACK_ACS_MISSTARGET => 'Ziel der Kampfgruppe stimmt nicht überein',\n    ATTACK_WRONG_SPEED => 'Falsche Fluggeschwindigkeit',\n    ATTACK_ACS_TOO_LATE => 'Flotte zu langsam - kann Kampfgruppe nicht erreichen',\n    ATTACK_BASHING => 'Bashing-Schutz: Maximalangriffe pro Tag erreicht',\n    ATTACK_BASHING_WAR_DELAY => 'Bashing-Schutz: Kriegserklärung noch nicht aktiv',\n    ATTACK_ACS_WRONG_TARGET => 'Flottenziel stimmt nicht mit Kampfgruppe überein',\n    ATTACK_SAME => 'Start- und Zielplanet identisch',\n    ATTACK_RESOURCE_FORBIDDEN => 'Ressourcentransport für diesen Auftrag nicht erlaubt',\n    ATTACK_TRANSPORT_EMPTY => 'Keine Ressourcen für Transportauftrag',\n    ATTACK_SPIES_LONLY => 'Nur Spionagesonden für diesen Auftrag nicht erlaubt',\n    ATTACK_TOO_FAR => 'Flottenreichweite zu gering',\n    ATTACK_OVERLOADED => 'Schiffe überladen. Ladung reduzieren oder Transporter hinzufügen',\n    ATTACK_MISSION_ABSENT => 'Auftragstyp existiert nicht',\n    ATTACK_WRONG_UNIT => 'Falscher Einheitentyp',\n    ATTACK_ZERO_SPEED => 'Orbitale Einheit in Flotte - nicht flugfähig',\n    ATTACK_SHIP_COUNT_WRONG => 'Negative Schiffszahl nicht möglich',\n    ATTACK_RESOURCE_COUNT_WRONG => 'Negative Ressourcenmenge nicht möglich',\n    ATTACK_MORATORIUM => 'Startverbot für diesen Auftragstyp',\n    ATTACK_CHILD_PROTECTION => 'Kinderschutz-Tag! Angriffe auf schwächere Spieler gesperrt!',\n    ATTACK_ACS_MAX_FLEETS => 'Maximale Flottenanzahl in Kampfgruppe erreicht',\n  ),\n\n  'fl_fleet_err' => 'Fehler!',\n  'fl_unknow_target' => '<li>Ziel existiert nicht!</li>',\n  'fl_nodebris' => '<li>Trümmerfeld existiert nicht!</li>',\n  'fl_nomoon' => '<li>Mond existiert nicht!</li>',\n  'fl_vacation_ttl' => 'Urlaubsmodus',\n  'fl_vacation_pla' => 'Spieler im Urlaubsmodus!',\n  'fl_noob_title' => 'Anfängerschutz',\n  'fl_noob_mess_n' => 'Spieler steht unter Anfängerschutz!',\n  'fl_bad_planet01' => '<li>Planet bereits besiedelt!</li>',\n  'fl_colonized' => '<li>Planet bereits kolonisiert!</li>',\n  'fl_dont_stay_here' => 'Landung auf feindlichem Planeten nicht möglich!',\n  'fl_no_allydeposit' => 'Kein Allianzlager auf dem Planeten!',\n  'fl_no_self_attack' => '<li>Eigene Planeten können nicht angegriffen werden!</li>',\n  'fl_no_self_spy' => '<li>Spionage auf eigene Planeten nicht möglich!</li>',\n  'fl_only_stay_at_home' => '<li>Umlagerung auf fremden Planeten nicht möglich!</li>',\n  'fl_cheat_speed' => 'Cheat-Versuch! Administrator wurde benachrichtigt!',\n  'fl_cheat_origine' => 'Cheat-Versuch! Administrator wurde benachrichtigt!',\n  'fl_limit_planet' => '<li>Ungültiger Planet!</li>',\n  'fl_limit_system' => '<li>Ungültiges System!</li>',\n  'fl_limit_galaxy' => '<li>Ungültige Galaxie!</li>',\n  'fl_ownpl_err' => '<li>Angriff auf eigenen Planeten nicht möglich!</li>',\n  'fl_no_planet_type' => '<li>Ungültiges Ziel!</li>',\n  'fl_fleet_err_pl' => '<li>Zielplanetenfehler!</li>',\n  'fl_bad_mission' => '<li>Ungültiger oder fehlender Auftrag!</li>',\n  'fl_no_fleetarray' => '<li>Flottenkonfigurationsfehler!</li>',\n  'fl_noenoughtgoods' => '<li>Transportauftrag ohne Ressourcen nicht möglich!</li>',\n  'fl_expe_notech' => '<li>Astrokartographie erforderlich!</li>',\n  'fl_expe_max' => '<li>Maximale Expeditionen erreicht. Astrokartographie ausbauen.</li>',\n  'fl_no_deuterium' => 'Nicht genug Deuterium für Ladung und Flug. Fehlend: ',\n  'fl_no_resources' => 'Nicht genug Ressourcen für Transport',\n  'fl_nostoragespa' => 'Nicht genug Laderaum für Ressourcen! Fehlend: ',\n  'fl_fleet_send' => 'Flotte gestartet',\n  'fl_expe_warning' => 'Warnung: Schiffsverluste bei Expeditionen möglich!',\n  'fl_not_enough_fuel' => 'FEHLER! Laderaum reicht nicht für benötigten Treibstoff! Mehr Transporter hinzufügen oder Geschwindigkeit reduzieren',\n  'fl_expe_staytime' => 'Verweildauer',\n  'fl_expe_hours' => 'Stunden',\n  'fl_adm_attak' => 'Administratoren können nicht angegriffen werden',\n  'fl_warning' => 'Warnung',\n  'fl_page0_hint' => '<ul><li>Lesezeichen verwalten über Menüpunkt \"Lesezeichen\"<li>Kampfgruppenbeitritt durch Klick auf Gruppennamen</ul>',\n  'fl_page1_hint' => '<ul><li>Flugzeit inklusive Start/Landezeit<li>Lesezeichen verwalten über Menüpunkt \"Lesezeichen\"<li>Kampfgruppenbeitritt durch Klick auf Gruppennamen</ul>',\n  'fl_page5_hint' => '<ul><li>Häkchen setzen für zu transportierende Ressourcen<li>Kopfzeilen-Häkchen wählt alle Ressourcen eines Typs<li>\"GESAMT\"-Häkchen wählt alle Ressourcen eines Planeten<li>Nur Transportschiffe werden berücksichtigt<li>Schiffe werden nach Laderaum sortiert geladen<li>Gesamtkapazität wird in der Summenzeile angezeigt</ul>',\n  'fl_err_no_ships' => 'Flotte enthält keine Schiffe. Bitte Schiffe auswählen',\n  'fl_shrtcup' => array(\n    PT_PLANET => '(P)',\n    PT_DEBRIS => '(T)',\n    PT_MOON => '(M)',\n  ),\n\n  'fl_planettype' => array(\n    PT_PLANET => 'Planet',\n    PT_DEBRIS => 'Trümmerfeld',\n    PT_MOON => 'Mond',\n  ),\n\n  'fl_aks_invite_message_header' => 'Kampfgruppen-Einladung',\n  'fl_aks_invite_message' => '<font color=\"red\">Spieler %s hat Sie zu einer Kampfgruppe eingeladen. Beitritt über Flottenseite möglich.</font>',\n  'fl_aks_player_invited' => '<font color=\"lime\">Spieler %s wurde eingeladen.</font>',\n  'fl_aks_player_invited_already' => '<font color=\"lime\">Spieler %s bereits eingeladen. Erneute Einladung gesendet.</font>',\n  'fl_aks_player_error' => '<font color=\"red\">Fehler. Spieler %s nicht gefunden.</font>',\n  'fl_aks_already_in_aks' => 'Flotte bereits in Kampfgruppe!',\n  'fl_aks_adding_error' => 'Fehler beim Gruppenbeitritt:<br>%s',\n  'fl_aks_hack_wrong_fleet' => 'Hackversuch! Fremde Flottenmanipulation! Administrator benachrichtigt!',\n  'fl_aks_too_slow' => 'Flotte zu langsam für Kampfgruppe',\n  'fl_aks_too_power' => 'Gruppe würde durch diesen Spieler zu stark werden',\n  'fl_aks_full' => 'Maximale Flottenanzahl in Kampfgruppe erreicht',\n  'fl_fleet_not_exists' => 'Flotte nicht gefunden',\n  'fl_multi_ip_protection' => 'MULTI-ACCOUNT-SCHUTZ!<br>Ressourcentransfer zu gleicher IP nicht möglich!',\n  'fl_load_cargo' => 'Im Laderaum',\n  'fl_rest_on_planet' => 'Verbleibend',\n  'fl_none_resources' => 'Zurücksetzen',\n  'fl_planet_resources' => 'Planetenressourcen',\n  'fl_fleet_data' => 'Aktuelle Flottenparameter',\n  'flt_gather_report' => 'Ereignisbericht',\n  'flt_report' => 'Bericht',\n  'flt_no_transports' => 'keine Transporter',\n  'flt_no_fuel' => 'kein Treibstoff',\n  'fl_id' => 'Nr',\n  'fl_ressources' => 'Rohstoffe',\n\n  'fl_fuel_on_planet' => 'Treibstoff auf Lager',\n\n  'flt_aks_players_in_aks' => 'Spieler in Kampfgruppe',\n  'flt_aks_player_invite' => 'Spieler einladen',\n  'flt_aks_player_invite_do' => 'Einladen',\n  'flt_aks_player_same' => 'Zielspieler kann nicht eingeladen werden!',\n  'flt_aks_error_too_much_players' => 'Maximal 5 Spieler pro Kampfgruppe',\n\n  'flt_return_all' => 'Markierte zurückrufen',\n  'flt_return_fleet' => 'Flotte zurückrufen',\n\n  'flt_acs_prefix' => 'KG ',\n\n  'ship_consumption_short' => 'Verbrauch',\n  'ship_capacity_short' => 'Laderaum',\n\n  'ship_is_satellite' => 'Satellit',\n));"
  },
  {
    "path": "language/de/infos.mo.php",
    "content": "<?php\n\n/*\n#############################################################################\n#  Filename: infos.mo\n#  Project: SuperNova.WS\n#  Website: http://www.supernova.ws\n#  Description: Massen-Mehrspieler-Online-Browser-Weltraum-Strategiespiel\n#\n#  Copyright © 2009-2018 Gorlum for Project \"SuperNova.WS\"\n#############################################################################\n*/\n\n/**\n*\n* @package language\n* @system [German]\n* @version 46d0\n*\n*/\n\n/**\n* DO NOT CHANGE\n*/\n\nif (!defined('INSIDE')) die();\n\n$a_lang_array = array(\n  'wiki_title' => 'NovaPedia',\n\n  'wiki_char_nominal' => 'Nominal',\n  'wiki_char_actual' => 'Aktuell',\n\n  'wiki_ship_engine_header' => 'Triebwerksmerkmale',\n\n  'wiki_ship_header' => 'Transportmerkmale',\n  'wiki_ship_speed' => 'Geschwindigkeit',\n  'wiki_ship_consumption' => 'Deuteriumverbrauch',\n  'wiki_ship_capacity' => 'Laderaumkapazität',\n  'wiki_ship_hint' => '<li>Aktuelle Geschwindigkeit und Verbrauch werden unter Berücksichtigung aller Boni angezeigt - Technologien, Söldner usw.</li>',\n\n  'wiki_combat_header' => 'Kampfmerkmale',\n  'wiki_combat_attack' => 'Schusskraft, Treffer',\n  'wiki_combat_shield' => 'Schildkapazität, Treffer',\n  'wiki_combat_armor' => 'Strukturelle Integrität, Treffer',\n\n  'wiki_combat_volley_header' => 'Schnellfeuer',\n  'wiki_combat_volley_to' => 'Trifft Einheiten',\n  'wiki_combat_volley_from' => 'Verliert Einheiten',\n  'info' => array(\n    STRUC_MINE_METAL => array(\n      'description' => 'Der Hauptlieferant von Rohstoffen für den Bau von Gebäuden und Schiffen. Metall ist der billigste Rohstoff, wird aber in größeren Mengen benötigt als alles andere. Die Metallproduktion benötigt am wenigsten Energie. Je größer die Minen sind, desto tiefer reichen sie. Auf den meisten Planeten befindet sich Metall in großen Tiefen, in diesen tieferen Minen kann mehr Metall abgebaut werden, die Produktion steigt. Gleichzeitig benötigen größere Minen mehr Energie.',\n      'description_short' => 'Hauptlieferant von Rohstoffen für den Bau von Gebäuden und Schiffen.',\n    ),\n\n    STRUC_MINE_CRYSTAL => array(\n      'description' => 'Für die Kristallsynthese wird etwa doppelt so viel Energie benötigt wie für die Gewinnung der gleichen Menge Metall, daher ist es entsprechend wertvoller. Kristalle sind ein Hauptbestandteil moderner Computer und ein Schlüsselkomponente von Warp-Antrieben. Daher werden sie für alle Schiffe und fast alle Gebäude benötigt. Die Verbesserung des Synthesizers erhöht die Menge der produzierten Kristalle.',\n      'description_short' => 'Hauptlieferant von Rohstoffen für Computersysteme und Warp-Antriebe.',\n    ),\n\n    STRUC_MINE_DEUTERIUM => array(\n      'description' => 'Deuterium ist schwerer Wasserstoff. Wie bei den Minen befinden sich größere Vorkommen am Meeresboden. Die Verbesserung des Synthesizers fördert auch die Erschließung dieser tiefen Deuteriumvorkommen. Deuterium wird als Treibstoff für Schiffe, für fast alle Forschungen, zur Galaxieansicht und für die Verwendung der Sensorphalanx benötigt.',\n      'description_short' => 'Extrahiert einen geringen Anteil Deuterium aus dem Planetenwasser.',\n    ),\n\n    STRUC_MINE_SOLAR => array(\n      'description' => 'Für die Energieversorgung der Minen und Synthesizer sind riesige Solarkraftwerke erforderlich. Je mehr Kraftwerke gebaut werden, desto mehr Fläche ist mit Solarzellen bedeckt, die Lichtenergie in Elektrizität umwandeln. Solarkraftwerke sind die Grundlage der Energieversorgung eines Planeten.',\n      'description_short' => 'Erzeugt Energie aus Sonnenlicht. Energie wird für den Betrieb der meisten Gebäude benötigt.',\n    ),\n\n    STRUC_MINE_FUSION => array(\n      'description_short' => 'Erzeugt Energie durch die Fusion von zwei schweren Wasserstoffatomen zu einem Heliumatom',\n      'description' => 'In Fusionskraftwerken verschmelzen unter enormem Druck und hoher Temperatur zwei schwere Wasserstoffatome zu einem Heliumatom. Dabei wird Energie in Form von Strahlung freigesetzt (41,32*10^-13 J pro Fusion, also 172 MW*h pro Gramm Wasserstoff). Je größer der Fusionsreaktor ist, desto komplexer werden die Fusionsprozesse, und der Reaktor produziert mehr Energie.<br><br>Produktionsformel:<br>30 * [Fusionskraftwerk-Level] * (1,05 + [Energietechnik-Level] * 0,01) ^ [Fusionskraftwerk-Level]<br>Die Energieproduktion kann auch durch die Entwicklung der Energietechnik erhöht werden.',\n    ),\n\n    STRUC_FACTORY_ROBOT => array(\n      'description' => 'Stellt einfache Arbeitskräfte bereit, die beim Bau der planetaren Infrastruktur eingesetzt werden können. Jede Stufe der Fabrik erhöht die Baugeschwindigkeit von Gebäuden.',\n      'description_short' => 'Produziert Maschinen und Mechanismen, die beim Bau der planetaren Infrastruktur eingesetzt werden. Jede Stufe der Fabrik erhöht die Baugeschwindigkeit von Gebäuden.',\n    ),\n\n    STRUC_FACTORY_NANO => array(\n      'description' => 'Nanofabriken sind die finale Evolutionsstufe der robotergesteuerten Fabriken. Ihre einzige Ausstattung sind Nano-Assembler, die die Manipulation einzelner Moleküle und sogar Atome ermöglichen. Mit ihrer Erfindung wurde die Produktion nahezu aller Materialien mit vordefinierten Eigenschaften möglich. Darüber hinaus können Nano-Assembler sofort fertige Teile jeder Form und Konfiguration produzieren. Dennoch haben Nano-Assembler die herkömmlichen Fabriken nicht überflüssig gemacht. Obwohl eine Nanofabrik jede Konstruktion produzieren kann, ist es energetisch oft günstiger, Dinge \"altmodisch\" herzustellen. Aber selbst mit diesen Einschränkungen halbiert jede Stufe der Nanofabrik die Bauzeit aller Gebäude, Verteidigungsanlagen und Schiffe.',\n      'description_short' => 'Die einzige Ausstattung dieser Fabrik sind Nano-Assembler - spezialisierte Komplexe, die Objekte aus einzelnen Molekülen und Atomen konstruieren können.',\n      'effect' => 'Jede Stufe der Nanofabrik verdoppelt die Baugeschwindigkeit aller Konstruktionen.',\n    ),\n\n    STRUC_FACTORY_HANGAR => array(\n      'description' => 'In der Werft werden alle Arten von Schiffen und Verteidigungsanlagen produziert. Je größer sie ist, desto schneller können komplexere und größere Schiffe und Verteidigungsanlagen gebaut werden. Durch den Bau von Nanofabriken werden viele technologische Ketten vereinfacht, was die Produktivität der Werft erheblich steigert.',\n      'description_short' => 'Werften produzieren Raumschiffe, orbitale Strukturen und Verteidigungsanlagen.',\n    ),\n\n    STRUC_STORE_METAL => array(\n      'description' => 'Lager für abgebautes Erz. Je größer es ist, desto mehr Erz kann gelagert werden. Wenn das Lager voll ist, wird die Metallproduktion eingestellt. Das Lager wird AUSSCHLIESSLICH für die Produktion verwendet - die Gesamtmenge an Metall auf dem Planeten (z.B. durch Transport von einer anderen Planeten) kann die maximale Lagerkapazität überschreiten.',\n      'description_short' => 'Lager für abgebautes Erz vor der weiteren Verarbeitung.',\n    ),\n\n    STRUC_STORE_CRYSTAL => array(\n      'description' => 'In diesem Lager werden Halbfabrikate für die Kristallsynthese gelagert. Je größer es ist, desto mehr Material kann gelagert werden. Wenn das Lager voll ist, wird die Kristallsynthese eingestellt. Das Lager wird AUSSCHLIESSLICH für die Produktion verwendet - die Gesamtmenge an Kristall auf dem Planeten (z.B. durch Transport von einer anderen Planeten) kann die maximale Lagerkapazität überschreiten.',\n      'description_short' => 'Lager für die Lagerung von Kristall-Halbfabrikaten vor der weiteren Verarbeitung.',\n    ),\n\n    STRUC_STORE_DEUTERIUM => array(\n      'description' => 'Spezielle Tanks für die Lagerung von schwerem Wasser. Sie befinden sich normalerweise in der Nähe von Raumhäfen. Je größer sie sind, desto mehr schweres Wasser kann gelagert werden. Wenn sie voll sind, wird die Deuteriumproduktion eingestellt. Die Tanks werden AUSSCHLIESSLICH für die Produktion verwendet - die Gesamtmenge an Deuterium auf dem Planeten (z.B. durch Transport von einer anderen Planeten) kann ihre maximale Kapazität überschreiten.',\n      'description_short' => 'Tanks für die Lagerung von schwerem Wasser vor der Deuteriumextraktion.',\n    ),\n\n    STRUC_LABORATORY => array(\n      'description' => 'Für die Erforschung neuer Technologien ist eine Forschungsstation erforderlich. Der Entwicklungsstand der Forschungsstation ist entscheidend dafür, wie schnell neue Technologien erforscht werden können. Je höher der Entwicklungsstand der Forschungslabor ist, desto mehr neue Technologien können erforscht werden. Um die Forschungsarbeiten auf einem Planeten so schnell wie möglich abzuschließen, werden alle verfügbaren Wissenschaftler dorthin geschickt und verlassen somit ihre Heimatplaneten. Sobald eine Technologie erforscht ist, kehren die Wissenschaftler auf ihre Heimatplaneten zurück und bringen das Wissen darüber mit. So können neue Technologien auch auf anderen Planeten angewendet werden.',\n      'description_short' => 'Im Labor werden neue Technologien erforscht.',\n    ),\n\n    STRUC_TERRAFORMER => array(\n      'description' => 'Mit zunehmender Bebauung der Planeten wurde die Frage der begrenzten nutzbaren Flächen immer wichtiger. Traditionelle Methoden wie das Bauen in die Höhe oder Tiefe erwiesen sich als unzureichend. Eine kleine Gruppe von Physikern und Nanotechnikern fand eine Lösung - den Terraformer.<br><br>Durch den Einsatz enormer Energiemengen kann der Terraformer große Gebiete oder sogar ganze Kontinente umwandeln und für die Bebauung nutzbar machen. In dieser Struktur werden kontinuierlich spezielle Naniten produziert, die für die ständige Bodenqualität verantwortlich sind.',\n      'description_short' => 'Der Terraformer kann große Gebiete umwandeln und die Anzahl der verfügbaren Bausektoren erhöhen.',\n    ),\n\n    STRUC_ALLY_DEPOSIT => array(\n      'description' => 'Das Allianzlager bietet die Möglichkeit, befreundete Flotten, die bei der Verteidigung helfen und im Orbit sind, mit Treibstoff zu versorgen. Je höher der Entwicklungsstand, desto mehr Deuterium kann an die Flotten im Orbit gesendet werden.',\n      'description_short' => 'Das Allianzlager bietet die Möglichkeit, befreundete Flotten, die bei der Verteidigung helfen und im Orbit sind, mit Treibstoff zu versorgen.',\n    ),\n\n    STRUC_LABORATORY_NANO => array(\n      'description' => 'Halbiert die Zeit der Forschungsphase.',\n      'description_short' => 'Nanolabore sind mit modernster Technologie ausgestattet. Hochleistungs-Kristallcomputer und ultrapräzise Nano-Assembler ermöglichen es, jede Forschung zu beschleunigen.',\n    ),\n\n    STRUC_MOON_STATION => array(\n      'description' => 'Der Mond hat keine Atmosphäre, daher muss vor der Besiedlung eine Mondbasis errichtet werden. Sie stellt die notwendige Luft, Schwerkraft und Wärme bereit. Je höher der Entwicklungsstand der Mondbasis ist, desto größer ist die mit einer Biosphäre versorgte Fläche. Jede Stufe der Mondbasis kann 3 Sektoren bebauen, maximal bis zur gesamten Mondfläche. Die Mondfläche beträgt 2 (Monddurchmesser/1000)^2, wobei jede Stufe der Mondbasis selbst ein Feld belegt.',\n      'description_short' => 'Der Mond hat keine Atmosphäre, daher muss vor der Besiedlung eine Mondbasis errichtet werden.',\n    ),\n\n    STRUC_MOON_PHALANX => array(\n      'description' => 'Hochfrequente Sensoren scannen das gesamte Spektrum der auf die Phalanx auftreffenden Strahlung. Leistungsstarke Computer kombinieren die kleinsten Energiefluktuationen und erhalten so Informationen über Schiffsbewegungen auf entfernten Planeten. Für die Ansicht muss dem Mond Energie in Form von Deuterium bereitgestellt werden (1000 * Phalanx-Level pro Ansicht). Die Ansicht erfolgt durch den Wechsel vom Mond zum Galaxiemenü und zum Namen des feindlichen Planeten, der sich im Sensorradius befindet (Formel: (Phalanx-Level)^2-1 Systeme).',\n      'description_short' => 'Hochfrequente Sensoren scannen das gesamte Spektrum der auf die Phalanx auftreffenden Strahlung.',\n    ),\n\n    STRUC_MOON_GATE => array(\n      'description' => 'Tore sind riesige Teleporter, die Flotten jeder Größe ohne Zeitaufwand zwischen sich versenden können. Diese Teleporter benötigen kein Deuterium, jedoch muss zwischen zwei Sprüngen eine Stunde vergehen, sonst überhitzen die Tore. Eine Ressourcenversendung ist ebenfalls nicht möglich. Der gesamte Prozess erfordert eine extrem hoch entwickelte Technologie.',\n      'description_short' => 'Tore sind riesige Teleporter, die Flotten jeder Größe ohne Zeitaufwand zwischen sich versenden können.',\n    ),\n\n    STRUC_SILO => array(\n      'description' => 'Raketensilos dienen der Lagerung von Raketen. Mit jeder Stufe können vier interplanetare oder zwölf Abfangraketen mehr gelagert werden. Eine interplanetare Rakete benötigt dreimal so viel Platz wie eine Abfangrakete. Jede Kombination verschiedener Raketentypen ist möglich.',\n      'description_short' => 'Das Raketensilo ermöglicht den Start von Raketen und dient gleichzeitig als Raketenlager.',\n    ),\n\n    TECH_SPY => array(\n      'description_short' => 'Mit dieser Technologie werden Daten über andere Planeten gesammelt.',\n      'description' => 'Spionage dient der Erforschung neuer und effizienterer Sensoren. Je höher diese Technologie entwickelt ist, desto mehr Informationen hat der Spieler über die Ereignisse in seiner Umgebung. Der Unterschied im Spionagelevel zum Gegner spielt eine entscheidende Rolle - je höher die eigene Spionagetechnologie erforscht ist, desto mehr Informationen enthalten die Spionagedaten und desto geringer ist die Chance, entdeckt zu werden. Je mehr Sonden gesendet werden, desto mehr Details werden über den Gegner gesammelt, aber gleichzeitig steigt die Gefahr, entdeckt zu werden. Die Spionage verbessert auch die Ortung fremder Flotten. Dabei ist ebenfalls das eigene Spionagelevel entscheidend. Ab dem zweiten Entwicklungslevel wird bei einem Angriff neben der Angriffsmeldung auch die Gesamtzahl der angreifenden Schiffe angezeigt. Ab dem vierten Level werden die Arten der angreifenden Schiffe sowie ihre Gesamtzahl erkannt, und ab dem achten Level die genaue Anzahl jedes Schiffstyps. Für Angreifer ist diese Technologie sehr wichtig, da sie Informationen darüber liefert, ob das Opfer eine Flotte und/oder Verteidigung aufgestellt hat oder nicht. Daher sollte sie so früh wie möglich erforscht werden. Am besten direkt nach der Erforschung kleiner Transporter.',\n    ),\n\n    TECH_COMPUTER => array(\n      'description' => 'Die Computertechnologie dient der Erweiterung der verfügbaren Rechenleistung. Dadurch werden auf dem Planeten produktivere und effizientere Computersysteme entwickelt, die Rechenleistung und die Geschwindigkeit der Berechnungen steigen. Mit zunehmender Computerleistung können gleichzeitig immer mehr Flotten befehligt werden. Jede Stufe der Computertechnologie ermöglicht das Kommando über +1 Flotte. Je mehr Flotten versendet werden, desto mehr Angriffe können durchgeführt und damit mehr Rohstoffe erbeutet werden. Natürlich ist diese Technologie auch für Händler nützlich, da sie ihnen ermöglicht, gleichzeitig mehr Handelsflotten zu versenden. Aus diesem Grund sollte die Computertechnologie während des gesamten Spiels kontinuierlich weiterentwickelt werden.',\n      'description_short' => 'Mit zunehmender Computerleistung können immer mehr Flotten befehligt werden. Jede Stufe der Computertechnologie erhöht die maximale Anzahl der Flotten um eins.',\n    ),\n\n    TECH_WEAPON => array(\n      'description' => 'Die Waffentechnologie beschäftigt sich vor allem mit der Weiterentwicklung der vorhandenen Waffensysteme. Dabei wird besonderer Wert darauf gelegt, die vorhandenen Systeme mit mehr Energie zu versorgen und diese Energie präziser zu lenken. Dadurch werden die Waffensysteme effektiver und die Waffen richten mehr Schaden an. Jede Stufe der Waffentechnologie erhöht die Feuerkraft der Truppen um 10%. Die Waffentechnologie ist wichtig für die Wettbewerbsfähigkeit der Truppen. Daher sollte sie während des gesamten Spiels kontinuierlich weiterentwickelt werden.',\n      'description_short' => 'Die Waffentechnologie macht die Waffensysteme effektiver. Jede Stufe erhöht die Feuerkraft der Truppen um 10% des Basiswerts.',\n    ),\n\n    TECH_SHIELD => array(\n      'description' => 'Die Entwicklung dieser Technologie ermöglicht eine erhöhte Energieversorgung der Schilde und Schutzschirme, was wiederum ihre Stabilität und ihre Fähigkeit erhöht, die Energie von Angriffen des Gegners zu absorbieren oder zu reflektieren. Dadurch steigt die Effektivität der Schiffe und stationären Kraftfelder mit jedem erforschten Level um 10% der Nennleistung.',\n      'description_short' => 'Diese Technologie beschäftigt sich mit der Erforschung neuer Möglichkeiten zur besseren Energieversorgung von Schilden, was sie effektiver und stabiler macht. Dadurch steigt die Effektivität der Schilde mit jedem erforschten Level um 10%.',\n    ),\n\n    TECH_ARMOR => array(\n      'description' => 'Spezielle Legierungen verbessern die Panzerung von Raumschiffen. Sobald eine sehr widerstandsfähige Legierung gefunden wurde, verändern spezielle Strahlen die molekulare Struktur des Raumschiffs und bringen sie in den Zustand der erforschten Legierung. So kann die Widerstandsfähigkeit der Panzerung mit jedem Level um 10% steigen.',\n      'description_short' => 'Spezielle Legierungen verbessern die Panzerung von Raumschiffen. Mit jedem Level erhöht sich die Panzerungsstärke um 10% des Basiswerts.',\n    ),\n\n    TECH_ENERGY => array(\n      'description' => 'Die Energietechnologie beschäftigt sich mit der Weiterentwicklung von Systemen zur Energieübertragung und -speicherung, die für viele neue Technologien benötigt werden.',\n      'description_short' => 'Die Erforschung der Energietechnologie ermöglicht eine verbesserte Leistung von Fusionskraftwerken.',\n    ),\n\n    TECH_HYPERSPACE => array(\n      'description' => 'Durch die Verflechtung der 4. und 5. Dimension wurde es möglich, einen neuen sparsameren und effizienteren Antrieb zu erforschen.',\n      'description_short' => 'Durch die Verflechtung der 4. und 5. Dimension wurde es möglich, einen neuen sparsameren und effizienteren Antrieb zu erforschen.',\n    ),\n\n    TECH_ENGINE_CHEMICAL => array(\n      'description' => 'Der chemische Raketenantrieb ist die einfachste Art von Antrieb. Dabei werden exotherme chemische Reaktionen von Treibstoff und Oxidator (zusammen als Treibstoff bezeichnet) genutzt, bei denen die Verbrennungsprodukte in der Brennkammer auf hohe Temperaturen erhitzt werden, sich ausdehnen, in einer Überschall-Düse beschleunigt werden und aus dem Antrieb strömen. Der Treibstoff eines chemischen Raketenantriebs ist sowohl die Quelle der thermischen Energie als auch des gasförmigen Arbeitsmediums, dessen innere Energie bei der Expansion in kinetische Energie des Strahls umgewandelt wird. Die Effizienz dieser Antriebe ist relativ gering, aber sie sind sehr zuverlässig, günstig in der Produktion und anspruchslos in der Wartung. Außerdem nehmen sie im Vergleich zu anderen Antrieben viel weniger Platz auf dem Schiff ein, daher findet man sie oft auf kleinen Schiffen. Da chemische Antriebe die Grundlage für jeden Raumflug sind, sollten sie so früh wie möglich erforscht werden. Jede Stufe der Entwicklung dieser Antriebe macht die folgenden Schiffe 10% schneller: Kleiner Transporter (bis zur Erforschung des Ionenantriebs Level 5), Großer Transporter, Recycler, Spionagesonde und Leichter Jäger.',\n      'description_short' => 'Die weitere Entwicklung dieser Antriebe macht einige Schiffe schneller. Jedes Level erhöht die Geschwindigkeit des Schiffes um 10% des Basiswerts.',\n    ),\n\n    TECH_ENGINE_ION => array(\n      'description' => 'Das Funktionsprinzip des Ionenantriebs besteht darin, Gas zu ionisieren und es mit einem elektrostatischen Feld zu beschleunigen. Aufgrund des hohen Verhältnisses von Ladung zu Masse können Ionen auf sehr hohe Geschwindigkeiten beschleunigt werden (bis zu 210 km/s im Vergleich zu 3-4,5 km/s bei chemischen Raketenantrieben). So kann im Ionenantrieb ein sehr hoher spezifischer Impuls erreicht werden. Dadurch kann der Verbrauch des ionisierten Gases im Vergleich zu chemischen Raketen deutlich reduziert werden, erfordert aber einen höheren Energieaufwand. Daher tragen alle Schiffe mit Ionenantrieb einen zusätzlichen Fusionsreaktor, der ausschließlich für den Antrieb arbeitet, und zeichnen sich durch einen erhöhten Deuteriumverbrauch aus. Am sinnvollsten ist der Einbau dieser Antriebe entweder in leichte Schiffe, die schnell beschleunigen müssen, oder in schwere Schiffe, deren Beschleunigung keine Rolle spielt. Daher umfasst die Liste der Schiffe mit diesem Antrieb den Kleinen Transporter (nach der Entwicklung des Antriebs Level 5), den Supertransporter, den Kolonisierer, den Schweren Jäger, den Zerstörer und den Bomber (bis zur Entwicklung der Hyperantriebstechnologie Level 8). Diese Antriebe werden auch in interplanetaren Raketen eingesetzt - in diesem Fall wird der Reaktor so eingestellt, dass die Treibstoffreste als zusätzlicher Sprengstoff dienen.',\n      'description_short' => 'Der Ionenantrieb funktioniert nach dem Prinzip der Ionisierung von Gas und dessen Beschleunigung durch ein elektrostatisches Feld. Jede Stufe der Entwicklung dieser Antriebe macht die damit ausgerüsteten Schiffe um 20% der Basisschnelligkeit schneller.',\n    ),\n\n    TECH_ENGINE_HYPER => array(\n      'description' => 'Durch die Raum-Zeit-Krümmung in der unmittelbaren Umgebung des Schiffes wird der Raum komprimiert, wodurch große Entfernungen schneller überwunden werden können. Je höher der Hyperantrieb entwickelt ist, desto stärker ist die Raumkompression, wodurch die folgenden Schiffe mit jedem Level um 30% schneller werden: Hypertransporter, Kreuzer, Bomber (nach der Entwicklung des Antriebs Level 8), Schlachtkreuzer, Zerstörer, Todesstern und der Kreuzer der \"SuperNova\"-Klasse.',\n      'description_short' => 'Der Antrieb komprimiert die Raum-Zeit um das Schiff herum und beschleunigt es auf Überlichtgeschwindigkeit. Mit jedem Level erhöht sich die Geschwindigkeit der Schiffe um 30%.',\n    ),\n\n    TECH_LASER => array(\n      'description' => 'Laser (Lichtverstärkung durch stimulierte Emission von Strahlung) erzeugen einen energiereichen Strahl kohärenten Lichts. Diese Geräte finden in allen möglichen Bereichen Anwendung, von optischen Computern bis hin zu schweren Lasern, die problemlos die Panzerung von Raumschiffen durchschneiden. Die Lasertechnologie ist ein wichtiges Element für die Erforschung weiterer Waffentechnologien.',\n      'description_short' => 'Durch die Fokussierung von Licht entsteht ein Strahl, der beim Auftreffen auf ein Objekt Schaden verursacht.',\n    ),\n\n    TECH_ION => array(\n      'description' => 'Ein wahrhaft tödlicher Strahl aus beschleunigten Ionen. Beim Auftreffen auf ein Objekt verursachen sie enormen Schaden.',\n      'description_short' => 'Ein wahrhaft tödlicher Strahl aus beschleunigten Ionen. Beim Auftreffen auf ein Objekt verursachen sie enormen Schaden.',\n    ),\n\n    TECH_PLASMA => array(\n      'description' => 'Eine Weiterentwicklung der Ionentechnologie, die nicht Ionen, sondern hochenergetisches Plasma beschleunigt. Es hat eine verheerende Wirkung beim Auftreffen auf ein Objekt.',\n      'description_short' => 'Eine Weiterentwicklung der Ionentechnologie, die nicht Ionen, sondern hochenergetisches Plasma beschleunigt. Es hat eine verheerende Wirkung beim Auftreffen auf ein Objekt.',\n    ),\n\n    TECH_RESEARCH => array(\n      'description' => 'Dieses Netzwerk ermöglicht die Kommunikation zwischen Wissenschaftlern, die in Forschungslabors auf verschiedenen Planeten arbeiten. Jede neue Stufe ermöglicht die Verbindung eines zusätzlichen Labors (zuerst werden die Labors mit den höchsten Stufen verbunden). Von allen vernetzten Labors nehmen nur diejenigen an einer Forschung teil, die das erforderliche Level für diese Forschung haben. Die Forschungsgeschwindigkeit entspricht der Summe der Level der beteiligten Labors.',\n      'description_short' => 'Dieses Netzwerk ermöglicht die Kommunikation zwischen Wissenschaftlern, die in Forschungslabors auf verschiedenen Planeten arbeiten. Jede neue Stufe ermöglicht die Verbindung eines zusätzlichen Labors.',\n    ),\n\n    TECH_EXPEDITION => array(\n      'description' => 'Die Expeditionstechnologie umfasst verschiedene Scan-Technologien und ermöglicht die Ausstattung von Schiffen verschiedener Klassen mit einem Forschungsmodul. Es enthält eine Datenbank, ein kleines mobiles Labor sowie verschiedene Biokammern und Probenbehälter. Für die Sicherheit des Schiffes bei der Erforschung gefährlicher Objekte ist das Forschungsmodul mit einer autonomen Energieversorgung und einem Kraftfeldgenerator ausgestattet, der in Extremsituationen das Modul mit einem starken Kraftfeld umgeben kann.',\n      'description_short' => 'Schiffe können nun mit einem Forschungsmodul ausgestattet werden, das die Verarbeitung gesammelter Daten während langer Flüge ermöglicht.',\n    ),\n\n    TECH_COLONIZATION => array(\n      'description' => 'Ein Herrscher mit vielen Kolonien im Universum hat mehr Vorteile gegenüber anderen.',\n      'description_short' => 'Diese Technologie ist sehr wichtig, damit du ein Imperium mit vielen Kolonien gründen kannst.',\n    ),\n\n    TECH_ASTROTECH => array(\n      'description' => 'Die Astrokartografie ermöglicht es, die maximale Anzahl an Kolonien und Expeditionen sowie die maximale Dauer einer Expedition zu erhöhen.',\n      'description_short' => 'Die Astrokartografie ermöglicht es, die maximale Anzahl an Kolonien und Expeditionen sowie die maximale Dauer einer Expedition zu erhöhen.',\n    ),\n\n    TECH_GRAVITON => array(\n      'description' => 'Ein Graviton ist ein Teilchen, das weder Masse noch Ladung besitzt und die Anziehungskraft bestimmt. Durch das Abfeuern eines konzentrierten Gravitonenstrahls kann ein künstliches Gravitationsfeld erzeugt werden, das ähnlich wie ein schwarzes Loch Masse anzieht und so Schiffe oder sogar Monde zerstören kann. Um genügend Gravitonen zu produzieren, werden enorme Energiemengen benötigt.',\n      'description_short' => 'Durch das Abfeuern eines konzentrierten Gravitonenstrahls kann ein künstliches Gravitationsfeld erzeugt werden, das ähnlich wie ein schwarzes Loch Masse anzieht und so Schiffe oder sogar Monde zerstören kann.',\n    ),\n\n    SHIP_CARGO_SMALL => array(\n      'description' => 'Transporter haben etwa die gleiche Größe wie Jäger, aber sie besitzen keine leistungsstarken Antriebe und Bordwaffen, um Platz zu sparen. Ein kleiner Transporter kann 5000 Einheiten Rohstoffe transportieren. Aufgrund der geringen Feuerkraft werden kleine Transporter oft von anderen Schiffen begleitet. Wenn der Ionenantrieb bis zur Stufe 5 erforscht ist, erhöht sich die Basisschnelligkeit des kleinen Transporters, und er wird mit diesem Antriebstyp ausgestattet.',\n      'description_short' => 'Der kleine Transporter ist ein wendiges Schiff, das schnell Rohstoffe zu anderen Planeten transportieren kann. Nach der Erforschung des Ionenantriebs Level 5 werden die Schiffe umgerüstet.',\n    ),\n\n    SHIP_CARGO_BIG => array(\n      'description' => 'An Bord dieses Schiffes gibt es nur schwache Bewaffnung und keine ernsthaften Technologien... Aus diesem Grund sollten sie niemals ohne Begleitung losgeschickt werden. Dank seines hoch entwickelten chemischen Antriebs dient der große Transporter als schneller interplanetarer Ressourcenlieferant und begleitet Flotten bei Angriffen auf feindliche Planeten, um so viele Rohstoffe wie möglich zu erbeuten.',\n      'description_short' => 'Der große Transporter hat eine größere Kapazität als der kleine Transporter. Die Geschwindigkeit des großen Transporters ist ebenfalls höher, aber nur bis die kleinen Transporter mit Ionenantrieben Level 5 ausgestattet werden.',\n    ),\n\n    SHIP_CARGO_SUPER => array(\n      'description' => 'Das letzte Wort in der Transporttechnologie. Der Supertransporter ist ein riesiges Transportschiff, das mit Ionenantrieben ausgestattet ist. Seine Geschwindigkeit ist gering und der Treibstoffverbrauch sehr hoch, aber dies wird durch seine außergewöhnliche Kapazität mehr als wettgemacht. Der Supertransporter ist nur mit Laser-Antimeteoriten-Türmen ausgestattet, hat aber einen starken Schild.',\n      'description_short' => 'Ein riesiges selbstfahrendes Schiff, das mit Ionenantrieben ausgestattet ist. Es ist mit einem starken Schild ausgestattet, hat aber nur Laser-Antimeteoriten-Türme als Bewaffnung.',\n    ),\n\n    SHIP_CARGO_HYPER => array(\n      'description' => 'Wenn der Supertransporter das letzte Wort in der Transporttechnologie ist - dann ist der Hypertransporter der Endpunkt. \"Riesig\" ist ein zu schwaches Wort, um dieses Schiff zu beschreiben. Mit der Größe eines kleinen Mondes kann dieser Transporter eine unglaubliche Menge an Ressourcen transportieren. Nur Hyperantriebe können ein voll beladenes Schiff bewegen. Sie ermöglichen es dem Hypertransporter, sich mit akzeptabler Geschwindigkeit durch das Universum zu bewegen. Aber der Preis ist hoch - die Kosten des Transporters nähern sich denen eines Dutzends Zerstörern. Und der Treibstoffverbrauch kann selbst einen Kaiser zum Weinen bringen. Daher ist der Hypertransporter ein Schiff für riesige und mächtige Imperien, die Dutzende oder Hunderte von Millionen Tonnen Ressourcen auf einmal transportieren müssen.',\n      'description_short' => 'Ein Transportschiff von der Größe eines kleinen Mondes. Ausgestattet mit Hyperantrieben und in der Lage, eine Million Tonnen Ressourcen in einem einzigen Transport zu befördern.',\n    ),\n\n    SHIP_SMALL_FIGHTER_LIGHT => array(\n      'description' => 'Der leichte Jäger ist ein wendiges Schiff, das auf fast jedem Planeten zu finden ist. Die Kosten dafür sind nicht besonders hoch, aber die Schildkraft und Kapazität sind sehr gering.',\n      'description_short' => 'Der leichte Jäger ist ein wendiges Schiff, das auf fast jedem Planeten zu finden ist. Die Kosten dafür sind nicht besonders hoch, aber die Schildkraft und Kapazität sind sehr gering.',\n    ),\n\n    SHIP_SMALL_FIGHTER_HEAVY => array(\n      'description' => 'Bei der weiteren Entwicklung des leichten Jägers kamen die Wissenschaftler zu dem Punkt, an dem klar wurde, dass ein herkömmlicher Antrieb nicht über die erforderliche Leistung verfügte. Um das Schiff optimal zu bewegen, wurde erstmals ein Ionenantrieb eingesetzt. Obwohl dies die Kosten erhöhte, eröffnete es auch neue Möglichkeiten. Durch den Einsatz dieses Antriebs blieb mehr Energie für Bewaffnung und Schilde übrig, außerdem wurden für diese Art von Jägern auch wertvolle Materialien verwendet. Dies führte zu einer verbesserten strukturellen Integrität und einer stärkeren Feuerkraft, wodurch er im Kampf eine größere Bedrohung darstellt als sein Vorgänger. Nach diesen Änderungen repräsentiert der schwere Jäger eine neue Ära der Schiffbautechnologie, die Grundlage der Kreuzerbautechnologie.',\n      'description_short' => 'Eine Weiterentwicklung des leichten Jägers, er ist besser geschützt und hat eine höhere Angriffskraft.',\n    ),\n\n    SHIP_MEDIUM_DESTROYER => array(\n      'description' => 'Mit der Entwicklung schwerer Laser und Ionenkanonen wurden schwere Jäger immer mehr verdrängt. Trotz zahlreicher Verbesserungen konnten Feuerkraft und Panzerung nicht so verändert werden, dass sie effektiv gegen diese Verteidigungswaffen bestehen konnten. Daher wurde beschlossen, eine neue Schiffsklasse zu bauen, die mehr Panzerung und Feuerkraft vereint. So entstanden die Zerstörer. Zerstörer sind fast dreimal stärker geschützt als schwere Jäger und verfügen über mehr als doppelt so viel Feuerkraft. Außerdem sind sie sehr schnell. Es gibt keine bessere Waffe gegen eine mittlere Verteidigung. Fast ein Jahrhundert lang herrschten Kreuzer uneingeschränkt im Universum. Mit dem Aufkommen von Gauß- und Plasmakanonen endete ihre Herrschaft. Dennoch werden sie auch heute noch gerne gegen Jägergruppen eingesetzt.',\n      'description_short' => 'Zerstörer sind fast dreimal stärker geschützt als schwere Jäger und haben fast die doppelte Feuerkraft. Außerdem sind sie sehr schnell.',\n    ),\n\n    SHIP_LARGE_CRUISER => array(\n      'description' => 'Kreuzer bilden in der Regel das Rückgrat der Flotte. Ihre schweren Geschütze, hohe Geschwindigkeit und große Ladekapazität machen sie zu ernsthaften Gegnern.',\n      'description_short' => 'Kreuzer bilden in der Regel das Rückgrat der Flotte. Ihre schweren Geschütze, hohe Geschwindigkeit und große Ladekapazität machen sie zu ernsthaften Gegnern.',\n    ),\n\n    SHIP_COLONIZER => array(\n      'description' => 'Dieses gut geschützte Schiff dient der Eroberung neuer Planeten, die ein wachsendes Imperium benötigt. Es wird in der neuen Kolonie als Rohstofflieferant verwendet - es wird auseinandergenommen und das gesamte nutzbare Material für die Erschließung der neuen Welt verwendet. Die maximale Anzahl der Kolonien hängt von den Universumseinstellungen ab, die über den Menüpunkt \"Weltkonstanten\" eingesehen werden können.',\n      'description_short' => 'Mit diesem Schiff können unbewohnte Planeten besiedelt werden.',\n    ),\n\n    SHIP_RECYCLER => array(\n      'description' => 'Raumkäufe nahmen immer größere Ausmaße an. Tausende von Schiffen wurden zerstört, und die dabei entstandenen Trümmer schienen für immer verloren zu sein. Normale Transporter konnten sich ihnen nicht nähern, ohne durch kleine Trümmerteile schwer beschädigt zu werden. Mit einer neuen Entdeckung im Bereich der Schildtechnologie wurde es möglich, dieses Problem effektiv zu lösen, und eine neue Schiffsklasse entstand, ähnlich dem großen Transporter - der Recycler. Mit ihm konnten die scheinbar verlorenen Ressourcen wieder genutzt werden. Dank der neuen Schilde stellten kleine Trümmerteile keine Gefahr mehr dar. Leider benötigen diese Geräte Platz, daher ist seine Ladekapazität auf 20.000 begrenzt.',\n      'description_short' => 'Mit dem Recycler werden Rohstoffe aus Trümmern gewonnen.',\n    ),\n\n    SHIP_SPY => array(\n      'description' => 'Spionagesonden sind kleine, wendige Schiffe, die aus großer Entfernung Daten über Flotten und Planeten liefern. Ihr hochleistungsfähiger Antrieb ermöglicht es ihnen, große Entfernungen in wenigen Sekunden zu überwinden. Einmal in der Umlaufbahn eines Planeten angekommen, sammeln sie einige Zeit lang Daten. Während dieser Zeit kann der Feind sie relativ leicht entdecken und angreifen. Um Platz zu sparen, wurden weder Panzerung, Schilde noch Waffen installiert, was die Sonden im Falle einer Entdeckung zu leichten Zielen macht.',\n      'description_short' => 'Spionagesonden sind kleine, wendige Schiffe, die aus großer Entfernung Daten über Flotten und Planeten liefern.',\n    ),\n\n    SHIP_LARGE_BOMBER => array(\n      'description' => 'Der Bomber wurde speziell entwickelt, um die planetare Verteidigung zu zerstören. Mit einem Laserzielgerät wirft er präzise Plasmabomben auf die Planetenoberfläche und richtet so enorme Schäden an den Verteidigungsanlagen an. Wenn der Hyperantrieb bis zur Stufe 8 erforscht ist, erhöht sich die Basisschnelligkeit des Bombers, und er wird mit diesem Antriebstyp ausgestattet.',\n      'description_short' => 'Der Bomber wurde speziell entwickelt, um die planetare Verteidigung zu zerstören.',\n    ),\n\n    SHIP_SATTELITE_SOLAR => array(\n      'description' => 'Solarsatelliten werden in die Umlaufbahn eines Planeten geschickt. Sie sammeln Sonnenenergie und übertragen sie an eine Bodenstation. Die Effizienz der Solarsatelliten hängt von der Stärke der Sonneneinstrahlung ab. Grundsätzlich ist die Energiegewinnung in Umlaufbahnen, die näher an der Sonne liegen, höher als auf Planeten, die weiter von der Sonne entfernt sind. Aufgrund ihres Preis-Leistungs-Verhältnisses lösen Solarsatelliten die Energieprobleme vieler Welten. Aber Achtung: Solarsatelliten können im Kampf zerstört werden.',\n      'description_short' => 'Solarsatelliten sind einfache Plattformen aus Solarzellen, die sich in einer hohen Umlaufbahn befinden. Sie sammeln Sonnenlicht und übertragen es per Laser an eine Bodenstation.',\n    ),\n\n    SHIP_LARGE_DESTRUCTOR => array(\n      'description' => 'Der Zerstörer ist der König unter den Kriegsschiffen. Seine mehrflügeligen Ionen-, Plasma- und Gaußkanonentürme können dank ihrer verbesserten Peilsensoren sogar schnelle, wendige Jäger mit einer Treffergenauigkeit von 99% treffen. Da Zerstörer sehr groß sind, ist ihre Manövrierfähigkeit sehr eingeschränkt, und im Kampf ähneln sie eher einer Kampfstation als einem Kriegsschiff. Ihr Deuteriumverbrauch ist ebenso hoch wie ihre Kampfkraft.',\n      'description_short' => 'Der Zerstörer ist der König unter den Kriegsschiffen.',\n    ),\n\n    SHIP_HUGE_DEATH_STAR => array(\n      'description' => 'Der Todesstern ist mit einer Gravitonenkanone ausgestattet, die alle Arten von Schiffen und sogar Monde zerstören kann. Um genügend Energie zu produzieren, besteht der Todesstern praktisch vollständig aus Generatoren. Nur riesige Sternenimperien haben genug Ressourcen und Arbeitskräfte, um dieses gewaltige Schiff zu bauen.',\n      'description_short' => 'Der Todesstern ist mit einer riesigen Gravitonenkanone ausgestattet, die Schiffe und sogar Monde zerstören kann.',\n    ),\n\n    SHIP_LARGE_BATTLESHIP => array(\n      'description' => 'Dieses Hochtechnologieschiff bringt Tod für angreifende Flotten. Seine verbesserten Lasergeschütze halten schwere feindliche Schiffe auf Distanz und können mehrere Einheiten mit einem Salvo vernichten. Aufgrund seiner geringen Größe und unglaublich starken Bewaffnung ist die Ladekapazität des Schlachtkreuzers sehr gering, aber dank des Hyperantriebs ist auch der Treibstoffverbrauch gering.',\n      'description_short' => 'Der Schlachtkreuzer ist auf die Abwehr feindlicher Flotten spezialisiert.',\n    ),\n\n    SHIP_HUGE_SUPERNOVA => array(\n      'description_short' => 'Der Kreuzer der \"SuperNova\"-Klasse ist das Flaggschiff der Raumflotte des Imperiums.',\n      'description' => 'Der Kreuzer der \"SuperNova\"-Klasse ist das Flaggschiff der Raumflotte des Imperiums. Die enorme Baukosten werden durch die erschreckende Feuerkraft und fortschrittliche Schutzsysteme mehr als wettgemacht. Ein einziges Schiff dieser Klasse kann eine mittlere Flotte allein vernichten.',\n    ),\n\n    UNIT_DEF_TURRET_MISSILE => array(\n      'description' => 'Die Raketenanlage ist ein einfaches und billiges Verteidigungsmittel. Da es sich um eine Weiterentwicklung herkömmlicher ballistischer Geschütze handelt, benötigt es keine weitere Modernisierung. Die geringen Produktionskosten rechtfertigen seinen Einsatz gegen kleinere Flotten, aber mit der Zeit verliert es an Bedeutung. Später wird es nur noch verwendet, um feindliche Schüsse abzulenken. Verteidigungsanlagen deaktivieren sich selbst, sobald sie stark beschädigt werden. Die Möglichkeit, Verteidigungsanlagen nach einem Kampf wiederherzustellen, beträgt bis zu 70%.',\n      'description_short' => 'Die Raketenanlage ist ein einfaches und billiges Verteidigungsmittel.',\n    ),\n\n    UNIT_DEF_TURRET_LASER_SMALL => array(\n      'description_short' => 'Durch den konzentrierten Beschuss eines Ziels mit Photonen können deutlich größere Zerstörungen erreicht werden als mit herkömmlicher ballistischer Bewaffnung.',\n      'description' => 'Um die übermäßigen Erfolge in der Raumschifftechnologie auszugleichen, mussten Wissenschaftler eine Verteidigungsanlage schaffen, die mit größeren und besser bewaffneten Flotten fertig wird. Dies führte zur Entwicklung des leichten Lasers. Durch den konzentrierten Beschuss eines Ziels mit Photonen können deutlich größere Zerstörungen erreicht werden als mit herkömmlicher ballistischer Bewaffnung. Um der stärkeren Feuerkraft neuer Schiffstypen standzuhalten, ist er auch mit verbesserten Schilden ausgestattet. Um jedoch die Produktionskosten niedrig zu halten, wurde die Struktur nicht weiter verstärkt. Der leichte Laser bietet das beste Preis-Leistungs-Verhältnis und ist daher auch für weiter entwickelte Zivilisationen interessant. Verteidigungsanlagen deaktivieren sich selbst, sobald sie stark beschädigt werden. Die Möglichkeit, Verteidigungsanlagen nach einem Kampf wiederherzustellen, beträgt bis zu 70%.',\n    ),\n\n    UNIT_DEF_TURRET_LASER_BIG => array(\n      'description' => 'Der schwere Laser ist eine Weiterentwicklung des leichten Lasers. Die Struktur wurde verstärkt und mit neuen Materialien verbessert. Die Hülle konnte deutlich widerstandsfähiger gemacht werden. Gleichzeitig wurden das Energiesystem und der Zielcomputer verbessert, sodass der schwere Laser deutlich mehr Energie auf das Ziel konzentrieren kann. Verteidigungsanlagen deaktivieren sich selbst, sobald sie stark beschädigt werden. Die Möglichkeit, Verteidigungsanlagen nach einem Kampf wiederherzustellen, beträgt bis zu 70%.',\n      'description_short' => 'Der schwere Laser ist eine Weiterentwicklung des leichten Lasers.',\n    ),\n\n    UNIT_DEF_TURRET_GAUSS => array(\n      'description_short' => 'Die Gaußkanone beschleunigt tonnenschwere Geschosse mit enormem Energieaufwand.',\n      'description' => 'Lange Zeit galten Artilleriegeschütze angesichts der Entwicklung moderner Laser- und Ionentechnik und der ständig verbesserten Schutzsysteme als veraltet, bis neue Forschungen im Bereich der Energietechnik die Artillerie auf ein qualitativ neues Niveau hoben. Eigentlich waren die Prinzipien elektromagnetischer Massenbeschleuniger auf der Erde schon seit dem 20. Jahrhundert bekannt. Einer davon ist die Gaußkanone. Mit der Lösung der technischen Schwierigkeiten wurde ihr Einsatz als Waffe Realität. Tonnenschwere Geschosse werden durch ein Magnetfeld mit enormem Energieaufwand beschleunigt und haben eine so hohe Austrittsgeschwindigkeit, dass Staubpartikel in der Luft um das Geschoss herum verbrennen und die Schallwelle den Boden erschüttert. Selbst moderne Panzerungen und Schilde können der Durchschlagskraft der Gaußkanone nur schwer standhalten, und es kommt nicht selten vor, dass das Ziel einfach durchschossen wird. Verteidigungsanlagen deaktivieren sich selbst, sobald sie stark beschädigt werden. Die Möglichkeit, Verteidigungsanlagen nach einem Kampf wiederherzustellen, beträgt bis zu 70%.',\n    ),\n\n    UNIT_DEF_TURRET_ION => array(\n      'description_short' => 'Die Ionenkanone richtet eine Ionenwelle auf das Ziel, die Schilde destabilisiert und Elektronik beschädigt.',\n      'description' => 'Im 21. Jahrhundert gab es auf der Erde bereits das, was allgemein als EMP bekannt ist. EMP steht für elektromagnetischer Impuls, der die Fähigkeit besitzt, zusätzliche Spannungen in Schaltkreise zu induzieren und damit massive Störungen zu verursachen, die alle empfindlichen Geräte zerstören können. Damals basierten EMP-Waffen hauptsächlich auf Raketen und Bomben, auch in Kombination mit Nuklearwaffen. Inzwischen haben sich EMP-Waffen ständig weiterentwickelt, da man in ihnen ein großes Potenzial sah, Ziele nicht zu zerstören, sondern kampf- und manövrierunfähig zu machen und damit ihre Eroberung zu vereinfachen. Bisher ist die höchste Form der EMP-Waffen die Ionenkanone. Sie richtet eine Welle aus Ionen (elektrisch geladene Teilchen) auf das Ziel, die Schilde destabilisiert und Elektronik beschädigt, sofern sie nicht sehr gut geschützt ist, was manchmal einer vollständigen Zerstörung gleichkommt. Die kinetische Durchschlagskraft kann vernachlässigt werden. Die Ionentechnik wird nur auf Kreuzern eingesetzt, da der Energieverbrauch der Ionenkanonen enorm ist und im Kampf oft Ziele zerstört werden müssen, anstatt sie zu lähmen. Verteidigungsanlagen deaktivieren sich selbst, sobald sie stark beschädigt werden. Die Möglichkeit, Verteidigungsanlagen nach einem Kampf wiederherzustellen, beträgt bis zu 70%.',\n    ),\n\n    UNIT_DEF_TURRET_PLASMA => array(\n      'description' => 'Die Lasertechnologie wurde zur Perfektion gebracht, die Ionentechnik erreichte ihr Endstadium und es wurde für praktisch unmöglich gehalten, selbst aus qualitativer Sicht der Waffensysteme, noch größere Effektivität zu erreichen. Aber alles sollte sich ändern, als die Idee aufkam, beide Systeme zu kombinieren. Mit Hilfe der Fusionsforschung werden Substanzen (normalerweise Deuterium) mit Lasern auf ultrahohe Temperaturen von Millionen Grad erhitzt. Die Ionentechnik sorgt für die Anreicherung des Plasmas mit elektrischer Ladung, seine Stabilisierung und Beschleunigung. Sobald die Ladung ausreichend erhitzt, ionisiert und unter Druck steht, wird sie mit Hilfe von Beschleunigern in Richtung des Ziels abgefeuert. Die bläulich leuchtende Plasmakugel sieht beeindruckend aus, nur fragt man sich, wie lange die Besatzung des Zielschiffs sie genießen wird, wenn die Panzerung innerhalb weniger Sekunden in Stücke gerissen wird und die Elektronik durchbrennt... Die Plasmakanone gilt allgemein als die schrecklichste Waffe, und diese Technik hat ihren Preis. Verteidigungsanlagen deaktivieren sich selbst, sobald sie stark beschädigt werden. Die Möglichkeit, Verteidigungsanlagen nach einem Kampf wiederherzustellen, beträgt bis zu 70%.',\n      'description_short' => 'Das letzte Wort in planetaren Verteidigungstechnologien, geboren aus der Symbiose von Laser- und Ionentechnik.',\n    ),\n\n    UNIT_DEF_SHIELD_SMALL => array(\n      'description' => 'Lange bevor Schildgeneratoren klein genug waren, um auf Schiffen eingesetzt zu werden, gab es bereits riesige Generatoren auf Planetenoberflächen. Sie umhüllten einen ganzen Planeten mit einem Kraftfeld, das Angriffsschläge absorbieren konnte. Kleine Angriffsflotten scheitern ständig an diesen Schildkuppeln. Dank des wachsenden technologischen Fortschritts können diese Schilde noch verstärkt werden. Später kann eine stärkere große Schildkuppel gebaut werden. Auf jedem Planeten kann nur eine kleine Schildkuppel gebaut werden.',\n      'description_short' => 'Die kleine Schildkuppel schützt den Planeten und absorbiert Angriffsschläge.',\n    ),\n\n    UNIT_DEF_SHIELD_BIG => array(\n      'description' => 'Eine Weiterentwicklung der kleinen Schildkuppel. Sie kann noch stärkere Angriffe auf den Planeten abwehren, indem sie deutlich mehr Energie absorbiert.',\n      'description_short' => 'Eine Weiterentwicklung der kleinen Schildkuppel. Sie kann noch stärkere Angriffe auf den Planeten abwehren, indem sie deutlich mehr Energie absorbiert.',\n    ),\n\n    UNIT_DEF_SHIELD_PLANET => array(\n      'description' => 'Der beste Schutz für deine Planeten',\n      'description_short' => 'Der beste Schutz für deine Planeten',\n    ),\n\n    UNIT_DEF_MISSILE_INTERCEPTOR => array(\n      'description' => 'Abfangraketen zerstören angreifende interplanetare Raketen. Eine Abfangrakete zerstört eine interplanetare Rakete.',\n      'description_short' => 'Abfangraketen zerstören angreifende interplanetare Raketen.',\n    ),\n\n    UNIT_DEF_MISSILE_INTERPLANET => array(\n      'description_short' => 'Interplanetare Raketen zerstören die feindliche Verteidigung.',\n      'description' => 'Interplanetare Raketen zerstören die Verteidigung des Gegners. Durch interplanetare Raketen zerstörte Verteidigungsanlagen werden nicht wiederhergestellt.',\n    ),\n\n    MRC_TECHNOLOGIST => array(\n      'description' => 'Der Technologe ist ein anerkannter Experte für die Optimierung der Gewinnung und Verarbeitung von Ressourcen. Mit seinem Team aus Metallurgen, Chemikern und Energietechnikern unterstützt er planetare Regierungen bei der Entwicklung neuer Ressourcenquellen und optimiert deren Aufbereitung.',\n      'description_short' => 'Der Technologe ist ein anerkannter Experte für die Optimierung der Gewinnung und Verarbeitung von Ressourcen. Mit seinem Team aus Metallurgen, Chemikern und Energietechnikern unterstützt er planetare Regierungen bei der Entwicklung neuer Ressourcenquellen und optimiert deren Aufbereitung.',\n      'effect' => 'zur Gewinnung von Metall, Kristallen und Deuterium, zur Energieerzeugung in Solar- und Fusionskraftwerken für jedes Level.',\n    ),\n\n    MRC_ENGINEER => array(\n      'description' => 'Der Ingenieur ist der modernste Baumeister des Imperiums. Seine DNA wurde mutiert, was ihm einen außergewöhnlichen Verstand und übermenschliche Kraft verlieh. Ein Ingenieur kann allein eine ganze Stadt entwerfen und bauen.',\n      'description_short' => 'Der Ingenieur ist der modernste Baumeister des Imperiums. Seine DNA wurde mutiert, was ihm einen außergewöhnlichen Verstand und übermenschliche Kraft verlieh. Ein Ingenieur kann allein eine ganze Stadt entwerfen und bauen.',\n      'effect' => 'zur Bauzeit von Gebäuden und Schiffen für jedes Level<br />+1 Slot für Bau- und Schiffswarteschlangen für jedes Level',\n    ),\n\n    MRC_FORTIFIER => array(\n      'description' => 'Der Festungsbauer ist ein militärischer Ingenieurspezialist. Sein tiefes Wissen über Verteidigungssysteme ermöglicht es, die Bauzeiten planetarer Verteidigungssysteme zu verkürzen.',\n      'description_short' => 'Der Festungsbauer ist ein militärischer Ingenieurspezialist. Sein tiefes Wissen über Verteidigungssysteme ermöglicht es, die Bauzeiten planetarer Verteidigungssysteme zu verkürzen.',\n      'effect' => 'zur Bauzeit von Verteidigungsanlagen und Raketen für jedes Level.<br />+10% für jedes Level zu Angriff, Panzerung und Schilden des Planetenbesitzers bei der Verteidigung<br />+1 Slot für die Warteschlange von Verteidigungsanlagen und Raketen für jedes Level',\n    ),\n\n    MRC_STOCKMAN => array(\n      'description' => 'Der Lagerist ist ein hochqualifizierter Spezialist für die Lagerung. Sein Genie ermöglicht es, das Maximum aus den Ressourcenlagern herauszuholen und deren effektive Kapazität über die von den Bauherren vorgesehenen Grenzen hinaus zu erhöhen.',\n      'description_short' => 'Der Lagerist ist ein hochqualifizierter Spezialist für die Lagerung. Sein Genie ermöglicht es, das Maximum aus den Ressourcenlagern herauszuholen und deren effektive Kapazität über die von den Bauherren vorgesehenen Grenzen hinaus zu erhöhen.',\n      'effect' => 'zur Größe der Lager für jedes Level.',\n    ),\n\n    MRC_SPY => array(\n      'description' => 'Der Spion ist die mysteriöseste Persönlichkeit des Imperiums. Er hat hunderte Gesichter, tausend Persönlichkeiten und eine Million Ideen, die sich auf die Tarnung von Bauwerken, Verteidigungsnetzen und Flotten beziehen. Alle, die sein wahres Gesicht gesehen haben, sind jetzt tot.',\n      'description_short' => 'Der Spion ist die mysteriöseste Persönlichkeit des Imperiums. Er hat hunderte Gesichter, tausend Persönlichkeiten und eine Million Ideen, die sich auf die Tarnung von Bauwerken, Verteidigungsnetzen und Flotten beziehen. Alle, die sein wahres Gesicht gesehen haben, sind jetzt tot.',\n      'effect' => 'zum Spionagelevel für jedes Level.',\n    ),\n\n    MRC_ACADEMIC => array(\n      'description' => 'Akademiker sind Mitglieder der Gilde der Technokraten. Ihr Verstand und ihre akademischen Grade ermöglichen es ihnen, sogar die Konstrukteure in ihren Taten zu übertreffen. Sie sind auf technologischen Fortschritt spezialisiert.',\n      'description_short' => 'Akademiker sind Mitglieder der Gilde der Technokraten. Ihr Verstand und ihre akademischen Grade ermöglichen es ihnen, sogar die Konstrukteure in ihren Taten zu übertreffen. Sie sind auf technologischen Fortschritt spezialisiert.',\n      'effect' => 'zur Forschungsgeschwindigkeit für jedes Level.',\n    ),\n\n//    MRC_DESTRUCTOR => array(\n//      'description' => 'Der Zerstörer ist ein gnadenloser Offizier. Er bringt mit brutalen Methoden Ordnung auf den Planeten des Imperiums. Ebenso hat der Zerstörer die Technologie zur Produktion von Todessternen entwickelt.',\n//      'effect' => 'Ermöglicht den Bau von Todessternen in der Werft.',\n//    ),\n\n    MRC_ADMIRAL => array(\n      'description' => 'Der Admiral ist ein kriegserprobter Veteran und genialer Stratege. Selbst in den heißesten Gefechten verliert er nicht den Überblick und hält Kontakt zu den Flottenkommandanten. Ein weiser Herrscher kann sich im Kampf voll und ganz auf ihn verlassen und so mehr Schiffe für den Kampf einsetzen.',\n      'description_short' => 'Der Admiral ist ein kriegserprobter Veteran und genialer Stratege. Selbst in den heißesten Gefechten verliert er nicht den Überblick und hält Kontakt zu den Flottenkommandanten. Ein weiser Herrscher kann sich im Kampf voll und ganz auf ihn verlassen und so mehr Schiffe für den Kampf einsetzen.',\n      'effect' => 'zu Panzerung, Schilden und Angriff der Schiffe für jedes Level.',\n    ),\n\n    MRC_COORDINATOR => array(\n      'description' => 'Der Koordinator ist ein Experte in der Flottenverwaltung. Sein Wissen ermöglicht es, das Maximum aus dem Flottenkontrollsystem herauszuholen.',\n      'description_short' => 'Der Koordinator ist ein Experte in der Flottenverwaltung. Sein Wissen ermöglicht es, das Maximum aus dem Flottenkontrollsystem herauszuholen.',\n      'effect' => 'zusätzliche Flotte für jedes Level.',\n    ),\n\n    MRC_NAVIGATOR => array(\n      'description' => 'Der Navigator ist ein Genie in der Berechnung von Flugbahnen. Seine Kenntnisse der Warp-Raum-Gesetze, der Funktionsweise des Jump-Antriebs und der Technologien aller existierenden Antriebstypen ermöglichen es, die Geschwindigkeit der Schiffe zu erhöhen.',\n      'description_short' => 'Der Navigator ist ein Genie in der Berechnung von Flugbahnen. Seine Kenntnisse der Warp-Raum-Gesetze, der Funktionsweise des Jump-Antriebs und der Technologien aller existierenden Antriebstypen ermöglichen es, die Geschwindigkeit der Schiffe zu erhöhen.',\n      'effect' => 'zur Geschwindigkeit der Schiffe für jedes Level.',\n    ),\n\n    MRC_EMPEROR => array(\n      'description' => 'Der Kaiser ist Ihr persönlicher Assistent und Stellvertreter. Die Genauigkeit seiner Berichte und seine penible Pünktlichkeit in allem sind seine besten Eigenschaften, die eine totale Kontrolle über das Imperium ermöglichen.',\n      'description_short' => 'Der Kaiser ist Ihr persönlicher Assistent und Stellvertreter. Die Genauigkeit seiner Berichte und seine penible Pünktlichkeit in allem sind seine besten Eigenschaften, die eine totale Kontrolle über das Imperium ermöglichen.',\n      'effect' => 'Ermöglicht die Änderung der Eigenschaften des Kaisers.',\n    ),\n\n    ART_LHC => array(\n      'description' => 'Der LHC erzeugt einen Gravitonenstrahl, der auf die Stelle mit der höchsten Trümmerkonzentration im Orbit gerichtet ist, wodurch die Trümmer sich gegenseitig anziehen.<br /><span class=warning>ACHTUNG! Die Verwendung des LHC garantiert nicht die Entstehung eines Mondes!</span>',\n      'effect' => 'Ermöglicht einen erneuten Versuch, einen Mond zu erschaffen<br />1% pro Million Trümmer, aber maximal 30%',\n    ),\n\n    ART_HOOK_SMALL => array(\n      'description' => 'Das Funktionsprinzip dieses Artefakts ist nicht vollständig verstanden, was seine Verwendung jedoch nicht behindert. Der kleine Haken teleportiert einen kleinen Asteroiden auf eine stabile Umlaufbahn des Planeten. Dadurch erhält der Planet einen Mond mit minimalem Durchmesser.',\n      'effect' => 'Erzeugt einen Mond mit minimaler Größe um den Planeten.',\n    ),\n\n    ART_HOOK_MEDIUM => array(\n      'description' => 'Das Funktionsprinzip dieses Artefakts ist nicht vollständig verstanden, was seine Verwendung jedoch nicht behindert. Der mittlere Haken teleportiert einen Asteroiden auf eine stabile Umlaufbahn des Planeten und erzeugt so einen Mond.<br /><span class=warning>ACHTUNG! Die Größe des Mondes ist ZUFÄLLIG!</span>',\n      'effect' => 'Erzeugt einen Mond mit zufälliger Größe um den Planeten.',\n    ),\n\n    ART_HOOK_LARGE => array(\n      'description' => 'Das Funktionsprinzip dieses Artefakts ist nicht vollständig verstanden, was seine Verwendung jedoch nicht behindert. Der große Haken teleportiert einen riesigen Asteroiden auf eine stabile Umlaufbahn des Planeten. Dadurch erhält der Planet einen Mond mit maximalem Durchmesser.',\n      'effect' => 'Erzeugt einen Mond mit maximaler Größe um den Planeten.',\n    ),\n\n    ART_RCD_SMALL => array(\n      'description' => 'Der kleine Autonome Kolonisationskomplex (kurz AKK) ist ein Satz fertiger Konstruktionen und Programme, die es ermöglichen, auf einem neu entdeckten Planeten sofort eine Basis-Kolonie zu errichten.<br />Wenn auf dem Planeten bereits Gebäude vorhanden sind, die Teil des AKK sind, werden sie entweder verbessert oder bleiben unverändert. Der AKK kann auch auf einem Planeten mit unzureichenden freien Sektoren vollständig eingesetzt werden. Der AKK kann nicht auf einem Mond eingesetzt werden.<br />Die Kolonie umfasst eine Metallmine, einen Kristallsynthesizer und einen Deuteriumsynthesizer der Stufe 10, ein Solarkraftwerk der Stufe 14 und eine Roboterfabrik der Stufe 4.',\n      'effect' => 'Ermöglicht die sofortige Errichtung einer Basis-Kolonie auf einem Planeten.',\n    ),\n\n    ART_RCD_MEDIUM => array(\n      'description' => 'Der mittlere Autonome Kolonisationskomplex (kurz AKK) ist ein Satz fertiger Konstruktionen und Programme, die es ermöglichen, auf einem neu entdeckten Planeten sofort eine Kolonie mittleren Niveaus zu errichten.<br />Wenn auf dem Planeten bereits Gebäude vorhanden sind, die Teil des AKK sind, werden sie entweder verbessert oder bleiben unverändert. Der AKK kann auch auf einem Planeten mit unzureichenden freien Sektoren vollständig eingesetzt werden. Der AKK kann nicht auf einem Mond eingesetzt werden.<br />Die Kolonie umfasst eine Metallmine, einen Kristallsynthesizer und einen Deuteriumsynthesizer der Stufe 15, ein Solarkraftwerk der Stufe 20 und eine Roboterfabrik der Stufe 8.',\n      'effect' => 'Ermöglicht die sofortige Errichtung einer Kolonie mittleren Niveaus auf einem Planeten.',\n    ),\n\n    ART_RCD_LARGE => array(\n      'description' => 'Der große Autonome Kolonisationskomplex (kurz AKK) ist ein Satz fertiger Konstruktionen und Programme, die es ermöglichen, auf einem neu entdeckten Planeten sofort eine fortgeschrittene Kolonie zu errichten.<br />Wenn auf dem Planeten bereits Gebäude vorhanden sind, die Teil des AKK sind, werden sie entweder verbessert oder bleiben unverändert. Der AKK kann auch auf einem Planeten mit unzureichenden freien Sektoren vollständig eingesetzt werden. Der AKK kann nicht auf einem Mond eingesetzt werden.<br />Die Kolonie umfasst eine Metallmine, einen Kristallsynthesizer und einen Deuteriumsynthesizer der Stufe 20, ein Solarkraftwerk der Stufe 25, eine Roboterfabrik der Stufe 10 und eine Nanofabrik der Stufe 1.',\n      'effect' => 'Ermöglicht die sofortige Errichtung einer fortgeschrittenen Kolonie auf einem Planeten.',\n    ),\n\n    ART_HEURISTIC_CHIP => array(\n      'description' => 'Der heuristische Chip ist ein einzigartiger, vorinstallierter Satz von Programmen, die auf einem kristallinen Speichermedium gespeichert sind. Durch die Verbindung mit dem Forschungsnetzwerk können die Algorithmen des Chips den aktuellen Forschungsstand analysieren und neue effektive Heuristiken liefern, wodurch die Forschungszeit erheblich verkürzt wird. Einmal aktiviert, kann der Chip nicht für eine andere Forschung neu konfiguriert werden. Leider ist, wie bei jedem anderen kristallinen Chip, die Dekompilierung der \"eingebetteten\" Programm grundsätzlich unmöglich, ebenso wie die Kopie durch Assembler.',\n      'effect' => 'Verringert die Zeit der aktuellen Forschung um die Hälfte (wenn mehr als eine Stunde bis zum Ende der Forschung verbleibt) oder beendet sie sofort (wenn weniger als 1 Stunde, aber mehr als 1 Minute bis zum Ende der Forschung verbleibt).',\n    ),\n\n    ART_NANO_BUILDER => array(\n      'description' => 'Wie bekannt ist, werden Assembler normalerweise nicht im Bau großer Objekte wie Gebäude eingesetzt. Es ist wirtschaftlich sinnvoller, Gebäude mit der traditionellen \"Blockbauweise\" zu errichten, bei der einzelne standardisierte Teile in robotergesteuerten Fabriken produziert werden. Spezialisierte Nano-Assembler erweisen sich jedoch als effizienter als traditionelle Methoden. Diese winzigen Roboter sind in vorkonfigurierten Paketen zusammengefasst, die jeweils ihre eigene Schwarm-Sub-KI besitzen. Durch die Analyse des aktuellen Zustands des im Bau befindlichen Gebäudes finden die Nano-Bauer fehlerfrei Engpässe und berechnen die effektivsten Wege zur Beschleunigung des Baus. Das Paket ist Einweg und nach Gebrauch nicht mehr verwendbar. Zudem kann ein initiiertes Paket nicht mehr für die Integration mit einer anderen Baustelle neu konfiguriert werden. Obwohl Assembler in der Lage sind, einen einzelnen Nano-Bauer zu reproduzieren, ist eine solche Replik ohne den Steuerkristall nicht mehr als ein maßstabsgetreues Modell...',\n      'effect' => 'Verringert die Bau-/Abbauzeit des aktuellen Gebäudes auf diesem Planeten um die Hälfte (wenn mehr als eine Stunde bis zum Ende des Prozesses verbleibt) oder beendet ihn sofort (wenn weniger als 1 Stunde, aber mehr als 1 Minute bis zum Ende des Prozesses verbleibt).',\n    ),\n\n    ART_DENSITY_CHANGER  => array(\n      'description' => '',\n      'effect' => '',\n    ),\n\n    UNIT_PLAN_STRUC_MINE_FUSION  => array(\n      'description' => 'Bauplan für das Gebäude \"Fusionskraftwerk\"',\n      'effect' => 'Ermöglicht den Bau des Gebäudes \"Fusionskraftwerk\" auf Planeten.',\n    ),\n\n    UNIT_PLAN_SHIP_CARGO_SUPER  => array(\n      'description' => 'Bauplan für das Schiff \"Supertransporter\"',\n      'effect' => 'Ermöglicht den Bau des Schiffes \"Supertransporter\" in Werften.',\n    ),\n\n    UNIT_PLAN_SHIP_CARGO_HYPER  => array(\n      'description' => 'Bauplan für das Schiff \"Hypertransporter\"',\n      'effect' => 'Ermöglicht den Bau des Schiffes \"Hypertransporter\" in Werften.',\n    ),\n\n    UNIT_PLAN_SHIP_DEATH_STAR  => array(\n      'description' => 'Bauplan für das Schiff \"Todesstern\"',\n      'effect' => 'Ermöglicht den Bau des Schiffes \"Todesstern\" in Werften.',\n    ),\n\n    UNIT_PLAN_SHIP_SUPERNOVA  => array(\n      'description' => 'Bauplan für den Kreuzer der \"SuperNova\"-Klasse',\n      'effect' => 'Ermöglicht den Bau des Kreuzers der \"SuperNova\"-Klasse in Werften.',\n    ),\n\n    UNIT_PLAN_DEF_SHIELD_PLANET  => array(\n      'description' => 'Bauplan für die Verteidigungsanlage \"Planetare Verteidigung\"',\n      'effect' => 'Ermöglicht den Bau der Verteidigungsanlage \"Planetare Verteidigung\" auf einem Planeten.',\n    ),\n\n    RES_METAL => array(\n      'description' => 'Metametallische Eisen-normierte energieneutrale Verbindung (oder einfach \"Metall\") ist der Grundrohstoff, aus dem Nanobots alle notwendigen Materialien und/oder Konstruktionen herstellen, die im Bau von Gebäuden, Schiffen, Verteidigungsanlagen usw. verwendet werden. Geliefert wird es in Form von schwach radioaktiven Barren mit einem Volumen von 127 Litern und einem Gewicht von 1 Tonne, einschließlich der Schutzverpackung. \"Eisen-normiert\" bedeutet, dass ein Standard-Satz von Nanobots aus einem Barren eine Tonne Eisen produzieren kann. \"Energieneutral\" bedeutet, dass dabei genau so viel Energie verbraucht wird, wie im Barren selbst enthalten ist. Und schließlich bedeutet \"metametallische Verbindung\", dass, obwohl der Hauptteil des Barrens aus verschiedenen Metallen besteht, auch andere Substanzen - sowohl komplexe als auch einfache - in seiner Zusammensetzung enthalten sein können. Die Zusammensetzung der Verbindung kann von Planet zu Planet und sogar von Mine zu Mine variieren, im Grenzfall reines Eisen sein, aber Größe, Form und Gewicht der Barren bleiben immer gleich.',\n      'effect' => '',\n    ),\n\n    RES_CRYSTAL => array(\n      'description' => 'Kristall - ein komplexer thermoplastischer Polymer, der den Effekt der Überlichtgeschwindigkeitsleitung (EÜL) demonstriert. EÜL - eine Erhöhung der Photonengeschwindigkeit im Kristallkörper über 300000 km/s. Alle modernen Rechengeräte verwenden Kristalle als Grundbaumaterial. Produktionsabfälle (\"anomale Assemblies\" - d.h. Polymere, die in der Formel Kristallen entsprechen, aber keinen EÜL demonstrieren) werden in Solarzellen verwendet, deren Wirkungsgrad sich 100% nähert. Darüber hinaus werden speziell ausgewählte Kristalle in Jump-Antrieben verwendet - Geräten, die interplanetare und interstellare Reisen mit Überlichtgeschwindigkeit ermöglichen.',\n      'effect' => '',\n    ),\n\n    RES_DEUTERIUM => array(\n      'description' => 'Deuterium, schwerer Wasserstoff - ein stabiles Isotop von Wasserstoff mit einer Atommasse von 2. Der Kern (Deuteron) besteht aus einem Proton und einem Neutron. Deuterium ist der Treibstoff für Fusionsreaktoren und alle Arten von Antrieben. Es wird in flüssiger Form in standardmäßigen thermoisolierten Behältern gelagert, die auch als Treibstoffblöcke für Antriebsanlagen und Fusionsreaktoren dienen. Daher sind die Laderäume, die mit einem automatischen Zuführ- und Stapelsystem ausgestattet sind, auch der \"Treibstofftank\" jedes Raumschiffs.',\n      'effect' => '',\n    ),\n\n    RES_ENERGY => array(\n      'description' => 'Elektrische Energie - die einzige universelle Energieart, die überall verwendet wird. Auf Planeten wird sie normalerweise von Solarkraftwerken und Solarsatelliten erzeugt. Auf besonders weit von der Sonne entfernten kalten Planeten und Raumschiffen wird elektrische Energie von Deuterium-Fusionsreaktoren erzeugt.',\n      'effect' => '',\n    ),\n\n    RES_DARK_MATTER => array(\n      'description' => '<span class=\"dark_matter\">Dunkle Materie</span> (abgekürzt <span class=\"dark_matter\">DM</span>) - eine mit Standardmethoden nicht nachweisbare nicht-baryonische Materie, auf die 23% der Masse des Universums entfallen. Aus ihr kann eine unglaubliche Menge an Energie gewonnen werden. Aufgrund dessen und der mit ihrer Gewinnung verbundenen Schwierigkeiten wird <span class=\"dark_matter\">Dunkle Materie</span> sehr hoch geschätzt.',\n      'effect' => '',\n    ),\n\n    RES_METAMATTER => array(\n      'description' => 'Metamaterie - das ist so ein neues Zeug, für das eine Beschreibung geschrieben werden muss.',\n      'effect' => '',\n    ),\n\n\n    UNIT_CAN_NOT_BE_BUILD => array(\n      'description'       => 'Diese Einheit kann derzeit nicht vom Spieler gebaut werden. Nein, man kann sie nicht kaufen. Vielleicht konnte man sie früher kaufen/bauen. Und nein, frag nicht, ob man sie jemals bauen oder irgendwie bekommen kann. Das ist unbekannt.<br/>Obwohl... vielleicht kann man sie auf andere Weise bekommen? Frag andere Spieler... Die AD-Administration zu fragen ist nutzlos.',\n      'description_short' => 'Diese Einheit kann derzeit nicht gebaut oder gekauft werden.',\n      'effect'            => 'Diese Einheit kann nicht gebaut oder gekauft werden.',\n    ),\n  )\n);"
  },
  {
    "path": "language/de/language.mo.php",
    "content": "<?php\n/*\n#############################################################################\n#  Filename: language.mo\n#  Create date: Sunday, March 30, 2008   19:56:23\n#  Project: SuperNova.WS\n#  Description: Massen-Mehrspieler-Online-Browser-Weltraum-Strategiespiel\n#\n#  Copyright © 2011-2018 Gorlum für Projekt \"SuperNova.WS\"\n#############################################################################\n*/\n\n/**\n*\n* system [German]\n*\n* @package language\n* @version $Id$\n*\n*/\n\n/**\n* DO NOT CHANGE\n*/\nif (!defined('INSIDE'))\n{\n  exit;\n}\n\n$lang_info = array(\n  'LANG_VERSION'      => 'V26e20',\n\n  'LANG_NAME_NATIVE'  => 'Deutsch',\n  'LANG_NAME_ENGLISH' => 'German',\n  'LANG_FLAG'         => 'de.png',\n  'LANG_FLAG_MEDIUM'  => 'de_medium.png',\n  'LANG_NAME_ISO2'    => 'de',\n  'LANG_NAME_ISO3'    => 'deu',\n  'LANG_DIRECTION'    => 'ltr',\n\n  'LANG_MAINTAINER'   => 'Gorlum',\n  'LANG_HOMEPAGE'     => 'http://forum.supernova.ws/viewforum.php?f=73',\n\n  'LANG_COPYRIGHT'    => array(\n    'Copyright &copy; 2009 Gorlum für Projekt &quot;SuperNova.WS&quot;',\n    'Copyright &copy; 2009 MSW',\n  ),\n);"
  },
  {
    "path": "language/de/login.mo.php",
    "content": "<?php\n\n/*\n#############################################################################\n#  Filename: login.mo\n#  Project: SuperNova.WS\n#  Website: http://www.supernova.ws\n#  Description: Massen-Mehrspieler-Online-Browser-Weltraum-Strategiespiel\n#\n#  Copyright © 2009-2018 Gorlum für Projekt \"SuperNova.WS\"\n#############################################################################\n*/\n\n/**\n*\n* @package language\n* @system [German]\n* @version 46d0\n*\n*/\n\n/**\n* DO NOT CHANGE\n*/\n\nif (!defined('INSIDE')) die();\n\nglobal $config;\n\n$a_lang_array = (array(\n  'Login' => 'Anmeldung',\n  'User_name' => 'Benutzername:',\n  'Authorization' => 'Autorisierung',\n  'Please_Login' => 'Bitte <a href=\"login.php\" target=\"_main\">anmelden...</a>',\n  'Please_Wait' => 'Bitte warten',\n  'Remember_me' => 'Angemeldet bleiben',\n  'Register' => 'Fehlerinformation',\n  'Login_Error' => 'Fehler',\n  'PleaseWait' => 'Bitte warten',\n  'PasswordLost' => 'Passwort zurücksetzen',\n  'Login_Ok' => 'Erfolgreich verbunden, <a href=\"./\"><blink>Weiterleitung...</blink></a><br><center><img src=\"design/images/progressbar.gif\"></center>',\n  'Login_FailPassword' => 'Falscher Benutzername und/oder Passwort<br /><a href=\"login.php\" target=\"_top\">Zurück</a>',\n  'Login_FailUser' => 'Dieser Spieler existiert nicht.<br><a href=login.php>Zurück</a>',\n  'log_univ' => 'Willkommen in unserem Universum!',\n  'log_reg' => 'Registrieren',\n  'log_reg_main' => 'Registrieren',\n  'log_menu' => 'Menü',\n  'log_stat_menu' => 'Statistik',\n  'log_enter' => 'Einloggen',\n  'log_news' => 'Server-Neuigkeiten',\n  'log_cred' => 'Über den Server',\n  'log_faq' => 'Spiel-FAQ',\n  'log_forums' => 'Forum',\n  'log_contacts' => 'Administration',\n  'log_desc' => '<strong>SuperNova ist ein Online-Multiplayer-Browser-Weltraum-Strategiespiel.</strong> Tausende von Spielern treten gleichzeitig gegeneinander an. Für das Spiel benötigen Sie nur einen normalen Browser.',\n  'log_toreg' => 'Jetzt registrieren!',\n  'log_online' => 'Spieler online',\n  'log_lastreg' => 'Neuling',\n  'log_numbreg' => 'Gesamtkonten',\n  'log_welcome' => 'Willkommen in',\n  'vacation_mode' => 'Sie sind im Urlaubsmodus<br> Urlaubsmodus kann in deaktiviert werden ',\n  'hours' => ' Stunden',\n  'vacations' => 'Urlaubsmodus',\n  'log_scr1' => 'Screenshot der Werft, hier werden Schiffe für den aktuellen Planeten gebaut und bestellt. Klicken Sie auf das Bild, um es zu vergrößern.',\n  'log_scr2' => 'Screenshot der Statistik, hier wird Ihre Bewertung im Vergleich zu anderen Spielern nach verschiedenen Parametern angezeigt. Klicken Sie auf das Bild, um es zu vergrößern.',\n  'log_scr3' => 'Screenshot des Universums, hier können Sie Ihren Planeten im Universum sehen sowie Planeten anderer Spieler finden. Klicken Sie auf das Bild, um es zu vergrößern.',\n  'log_rules' => 'Spielregeln',\n  'log_banned' => 'Liste der gebannten Spieler',\n  'log_see_you' => 'Wir hoffen, Sie wieder in den Weiten unseres Universums zu sehen. Viel Glück!<br><a href=\"login.php\">Zur Anmeldeseite</a>',\n  'log_session_closed' => 'Sitzung beendet.',\n  'registry' => 'Registrierung',\n  'form' => 'Registrierungsformular',\n  'Undefined' => '- nicht definiert -',\n  'Male' => 'Männlich',\n  'Female' => 'Weiblich',\n  'Multiverse' => 'XNova',\n  'E-Mail' => 'E-Mail-Adresse',\n  'MainPlanet' => 'Name des Hauptplaneten',\n  'GameName' => 'Name',\n  'gender' => 'Geschlecht',\n  'accept' => 'Ich stimme den Regeln zu',\n  'reg_i_agree' => 'Ich habe die gelesen und stimme den',\n  'reg_with_rules' => 'Spielregeln',\n  'signup' => 'Registrieren',\n  'Languese' => 'Sprache',\n  'log_reg_text0' => 'Bitte lesen Sie die vor der Registrierung',\n  'log_reg_text1' => 'Die Registrierung bedeutet, dass Sie alle Punkte der Regeln vollständig gelesen und zugestimmt haben. Wenn Sie mit irgendeinem Punkt der Regeln nicht einverstanden sind - bitte registrieren Sie sich nicht.',\n  'thanksforregistry' => 'Herzlichen Glückwunsch zur erfolgreichen Registrierung! Sie werden in 10 Sekunden zur Hauptseite Ihres Planeten weitergeleitet. Falls dies nicht geschieht, klicken Sie auf diesen <a href=overview.php><u>Link!</u></a>',\n  'welcome_to_universe' => 'Willkommen bei OGame!!!',\n  'please_click_url' => 'Um das Konto zu nutzen, müssen Sie es aktivieren, indem Sie auf diesen Link klicken',\n  'regards' => 'Viel Glück!',\n  'error_lang' => 'Diese Sprache wird nicht unterstützt!<br />',\n  'error_mail' => 'Ungültige E-Mail!<br />',\n  'error_planet' => 'Ein anderer Planet hat bereits denselben Namen!<br />',\n  'error_hplanetnum' => 'Der Planetname darf NUR mit lateinischen Buchstaben geschrieben werden!<br />',\n  'error_character' => 'Ungültiger Name!<br />',\n  'error_charalpha' => 'Sie dürfen NUR lateinische Buchstaben verwenden!<br />',\n  'error_password' => 'Das Passwort muss mindestens 4 Zeichen lang sein!<br />',\n  'error_rgt' => 'Sie müssen den Regeln zustimmen!<br />',\n  'error_userexist' => 'Dieser Name wird bereits verwendet!<br />',\n  'error_emailexist' => 'Diese E-Mail wird bereits verwendet!<br />',\n  'error_sex' => 'Fehler bei der Geschlechtsauswahl!<br />',\n  'error_mailsend' => 'Fehler beim Senden der E-Mail, Ihr Passwort: ',\n  'reg_welldone' => 'Registrierung abgeschlossen! Ihr Passwort wurde an die bei der Registrierung angegebene E-Mail-Adresse gesendet. Hier ist es noch einmal zur Sicherheit:<br>',\n  'error_captcha' => 'Falscher grafischer Code!<br/>',\n  'error_v' => 'Bitte versuchen Sie es erneut!<br />',\n  'log_login_page' => 'Zum Spiel anmelden',\n  'log_reg_already' => 'Bereits registriert? Nutzen Sie den Link ',\n  'log_reg_already_lost' => 'Passwort vergessen? Nutzen Sie den Link ',\n\n  'log_lost_header' => 'Passwort zurücksetzen',\n  'log_lost_code' => 'Bestätigungscode',\n  'log_lost_description2' => 'Wenn Sie einen Bestätigungscode haben, geben Sie ihn unten ein und klicken Sie auf \"Passwort zurücksetzen\". Eine E-Mail mit einem neuen Passwort wird an Ihre E-Mail-Adresse gesendet.<br /><br />\n    Wenn Sie bereits einen Bestätigungscode angefordert haben, aber keine E-Mail in Ihrem Posteingang sehen - überprüfen Sie den Spam-Ordner. Ihr E-Mail-Server könnte unsere E-Mail als \"unerwünscht\" markiert haben.<br /><span style=\"color: red;\">ACHTUNG! mail.ru und seine Projekte blockieren E-Mails vom Spiel! Schreiben Sie eine E-Mail an den Admin - siehe unten</span><br /><br />\n    Wenn Sie dort auch keine E-Mail finden - schreiben Sie eine E-Mail an die Serveradministration an <span class=\"ok\">' . $config->server_email . '</span>',\n  'log_lost_reset_pass' => 'Passwort zurücksetzen',\n  'log_lost_send_mail' => 'Bestätigungscode senden',\n  'log_lost_sent_code' => 'Eine E-Mail mit weiteren Anweisungen zum Zurücksetzen des Passworts wurde an die angegebene E-Mail gesendet',\n  'log_lost_sent_pass' => 'Eine E-Mail mit Ihrem neuen Passwort wurde ebenfalls an Ihre E-Mail-Adresse gesendet',\n\n  'log_lost_err_email' => 'Die angegebene E-Mail ist nicht in der Datenbank registriert. Dies kann eines der folgenden bedeuten:<br>Sie haben sich bei der Eingabe der E-Mail geirrt. Gehen Sie zurück und versuchen Sie es erneut<br>Ihr Konto wurde aufgrund von Inaktivität gelöscht. Registrieren Sie sich neu<br>Sie versuchen, sich in einem falschen Spieluniversum anzumelden. Überprüfen Sie den Namen des aktuellen Universums und melden Sie sich gegebenenfalls im richtigen Universum an',\n  'log_lost_err_sending' => 'Fehler beim Senden der Nachricht an die angegebene E-Mail. Melden Sie den Fehler dem Administrator',\n  'log_lost_err_code' => 'Der angegebene Bestätigungscode ist nicht in der Datenbank registriert. Dies kann eines der folgenden bedeuten:<br>Sie haben sich bei der Eingabe des Bestätigungscodes geirrt. Gehen Sie zurück und geben Sie den Code sorgfältig ein<br>Sie versuchen, den Bestätigungscode in einem anderen Universum einzugeben, als dem, für das er generiert wurde. Überprüfen Sie den Namen des aktuellen Universums<br>Ihr Konto wurde aufgrund von Inaktivität gelöscht. Registrieren Sie sich neu<br>Der Bestätigungscode ist abgelaufen. Überprüfen Sie das Ablaufdatum in der E-Mail. Wenn es abgelaufen ist, fordern Sie einen neuen Bestätigungscode an',\n  'log_lost_err_admin' => 'Mitglieder des Serverteams (Moderatoren, Operatoren, Administratoren usw.) können die Passwortzurücksetzungsfunktion nicht nutzen. Wenden Sie sich an den Serveradministrator, um Ihr Passwort zu ändern',\n  'log_lost_err_change' => 'Fehler beim Ändern des Passworts in der Datenbank. Melden Sie den Fehler dem Administrator',\n\n  'log_lost_description1' => 'Geben Sie die primäre E-Mail-Adresse ein, mit der Ihr Konto registriert ist. Eine E-Mail mit einem Bestätigungscode zum Zurücksetzen Ihres Passworts wird an diese Adresse gesendet',\n  'login_register_offer' => 'Klicken Sie hier, um sich zu registrieren',\n  'login_password_restore_offer' => 'Klicken Sie hier, um Ihr Passwort zurückzusetzen',\n\n  'login_register_email_hint' => 'Geben Sie eine funktionierende E-Mail-Adresse an - der Inhaber der angegebenen E-Mail-Adresse gilt als Kontoinhaber<br />\n    <span style=\"color: red;\">ACHTUNG! Verwenden Sie keine E-Mail-Adressen von \"@mail.ru\" und seinen Projekten! Sie werden keine E-Mails vom Spiel erhalten!</span>',\n\n  'login_account_name_or_email' => 'E-Mail',\n\n));"
  },
  {
    "path": "language/de/market.mo.php",
    "content": "<?php\n\n/*\n#############################################################################\n#  Filename: market.mo\n#  Project: SuperNova.WS\n#  Website: http://www.supernova.ws\n#  Description: Massive Multiplayer Online Browser Space Strategy Game\n#\n#  Copyright © 2009-2018 Gorlum for Project \"SuperNova.WS\"\n#############################################################################\n*/\n\n/**\n*\n* @package language\n* @system [German]\n* @version 46d0\n* @condition clear\n*\n*/\n\n/**\n* DO NOT CHANGE\n*/\n\nif (!defined('INSIDE')) die();\n\n$a_lang_array = (array(\n  'eco_mrk_title' => 'Schwarzmarkt',\n  'eco_mrk_description' => 'Seltsam, aber in der Beschreibung der Imperiumsverwaltung gab es keinen solchen Punkt... Ich frage mich, woher er kommt?',\n  'eco_mrk_service' => 'Dienstleistung',\n  'eco_mrk_service_cost' => 'Kosten der Dienstleistung',\n\n  'eco_mrk_trader_do' => 'Ressourcen tauschen',\n  'eco_mrk_trader' => 'Ressourcentausch',\n  'eco_mrk_trader_cost' => 'Kosten des Ressourcentauschs',\n  'eco_mrk_trader_exchange' => 'Menge der zu tauschenden Ressourcen',\n  'eco_mrk_trader_to' => 'Tauschen gegen',\n  'eco_mrk_trader_course' => 'Kurs',\n  'eco_mrk_trader_left' => 'Ergebnis der Operation',\n  'eco_mrk_trader_resources_all' => 'Alle Ressourcen',\n  'eco_mrk_trader_exchange_dm_confirm' => 'Möchten Sie wirklich {0} Dunkle Materie gegen Ressourcen tauschen?',\n\n  'eco_mrk_scraper_do' => 'Schiffe an Schrotthändler verkaufen',\n  'eco_mrk_scraper' => 'Schiffsverwertung',\n  'eco_mrk_scraper_price' => 'Schrottwert',\n  'eco_mrk_scraper_perShip' => 'pro Schiff',\n  'eco_mrk_scraper_total' => 'Gesamt',\n  'eco_mrk_scraper_cost' => 'Kosten für den Verkauf von Schiffen als Schrott',\n  'eco_mrk_scraper_onOrbit' => 'Im Orbit',\n  'eco_mrk_scraper_to' => 'Verschrotten',\n  'eco_mrk_scraper_res' => 'Folgender Schrott wurde erhalten:',\n  'eco_mrk_scraper_ships' => 'Folgende Schiffe wurden verschrottet:',\n  'eco_mrk_scraper_noShip' => 'Keine Schiffe im Orbit',\n\n  'eco_mrk_stockman_do' => 'Schiffe vom Schrotthändler kaufen',\n  'eco_mrk_stockman' => 'Gebrauchtschiffhändler',\n  'eco_mrk_stockman_price' => 'Preis',\n  'eco_mrk_stockman_perShip' => 'pro Schiff',\n  'eco_mrk_stockman_onStock' => 'Beim Händler',\n  'eco_mrk_stockman_buy' => 'Schiffe kaufen',\n  'eco_mrk_stockman_res' => 'Kosten der gekauften Schiffe:',\n  'eco_mrk_stockman_ships' => 'Folgende Schiffe wurden gekauft:',\n  'eco_mrk_stockman_noShip' => 'Der Händler hat derzeit keine Schiffe zum Verkauf',\n\n  'eco_mrk_exchange' => 'Ressourcenbörse',\n  'eco_mrk_banker'   => 'Bankier',\n  'eco_mrk_pawnshop' => 'Pfandhaus',\n\n  'eco_mrk_info_do' => 'Information kaufen',\n  'eco_mrk_info' => 'Informationshändler',\n  'eco_mrk_info_description' => 'Im Posteingang wurde eine Nachricht mit folgendem Inhalt gefunden:',\n  'eco_mrk_info_description_2' => 'Ich habe Zugang zu vielen interessanten Informationen. Ich kann sie mit Ihnen teilen... gegen eine bescheidene Gegenleistung. Für eine Anfrage nur',\n  'eco_mrk_info_buy' => 'Information kaufen',\n\n  'eco_mrk_info_player' => 'Spielerinformationen',\n  'eco_mrk_info_player_description' => 'Ich kann herausfinden, welche Söldner derzeit für einen Spieler arbeiten',\n  'eco_mrk_info_player_message' => 'Nach meinen zuverlässigen Quellen sieht die Liste der Söldner für Spieler ID %1$d [%2$s] wie folgt aus:',\n\n  'eco_mrk_info_not_hired' => 'nicht angeheuert',\n\n  'eco_mrk_info_ally' => 'Allianzinformationen',\n  'eco_mrk_info_online' => 'Aktuelle Aktivität im Universum',\n\n  'eco_mrk_info_msg_from' => 'Nicht nachverfolgbare Quelle',\n\n  'eco_mrk_error_title' => 'Schwarzmarkt - Fehler',\n  'eco_mrk_errors' => array(\n    MARKET_RESOURCES => 'Operation erfolgreich abgeschlossen',\n    MARKET_SCRAPPER => 'Ressourcentausch erfolgreich durchgeführt',\n    MARKET_NOT_A_SHIP => 'Versuchen Sie nicht, etwas anderes als Schiffe zu verkaufen!',\n    MARKET_STOCKMAN => 'Nicht genug Dunkle Materie, um die Operation abzuschließen',\n    MARKET_NO_RESOURCES => 'Nicht genug Ressourcen, um die Operation abzuschließen',\n    MARKET_PAWNSHOP => 'Sie versuchen, mehr Schiffe zu verschrotten, als im Orbit vorhanden sind',\n    MARKET_NO_STOCK => 'Sie versuchen, mehr Schiffe zu kaufen, als der Händler hat. Möglicherweise hat jemand anderes die Schiffe bereits gekauft, während Sie ausgewählt haben',\n    MARKET_ZERO_DEAL => 'Keine Ressourcenmenge für den Tausch angegeben',\n    MARKET_NOTHING => 'Sie müssen Schiffe zum Verkauf auswählen',\n    MARKET_ZERO_RES_STOCK => 'Sie müssen Schiffe zum Kauf auswählen',\n    MARKET_NEGATIVE_SHIPS => 'Versuchen Sie nicht, eine negative Anzahl von Schiffen zu verkaufen!',\n\n    MARKET_NO_DM => 'Nicht genug Dunkle Materie, um die Operation abzuschließen',\n    MARKET_INFO_WRONG => 'Keine solche Information verfügbar',\n    MARKET_INFO_PLAYER => 'Information erfolgreich gekauft. Überprüfen Sie Ihren Posteingang',\n    MARKET_INFO_PLAYER_WRONG => 'Sie müssen die ID oder den Namen des Spielers angeben',\n    MARKET_INFO_PLAYER_NOT_FOUND => 'Spieler konnte nicht identifiziert werden. Wenn der Spielername aus Zahlen besteht oder nicht lesbar ist, versuchen Sie, die ID zu verwenden',\n    MARKET_INFO_PLAYER_SAME => 'Warum Informationen über sich selbst erfragen?',\n  ),\n));"
  },
  {
    "path": "language/de/menu.mo.php",
    "content": "<?php\n\n/*\n#############################################################################\n#  Filename: leftmenu.mo\n#  Project: SuperNova.WS\n#  Website: http://www.supernova.ws\n#  Description: Massive Multiplayer Online Browser Space Strategy Game\n#\n#  Copyright © 2009-2018 Gorlum for Project \"SuperNova.WS\"\n#############################################################################\n*/\n\n/**\n*\n* @package language\n* @system [German]\n* @version 46d0\n*\n*/\n\n/**\n* DO NOT CHANGE\n*/\n\nif (!defined('INSIDE')) die();\n\n$a_lang_array = (array(\n  'Multiverse' => '<b>Server</b> Uni',\n  'm_h_rules' => 'Regeln',\n  'm_faq' => 'Wie spielt man?',\n  'm_faq_hint' => 'Häufig gestellte Fragen und Antworten',\n  'm_h_control' => 'Steuerung',\n  'm_forum' => 'Forum',\n  'm_others' => 'Sonstiges',\n  'm_simulator' => 'Kampfsimulator',\n  'm_communication' => 'Kommunikation',\n  'm_exchange' => 'Ressourcenbörse',\n  'm_affilates' => 'VERDIENE DM!',\n  'Overview' => 'Planetenübersicht',\n  'Officiers' => 'Offiziere',\n//  'Buildings' => 'Gebäude',\n  'Building' => 'Bau',\n  'Research' => 'Forschung',\n  'Shipyard' => 'Werft',\n  'Defense' => 'Verteidigung',\n  'Resources' => 'Ressourcen',\n  'Imperium' => 'Imperium',\n  'Marchand' => 'Händler',\n  'Annonces' => 'Ankündigungen',\n  'Technology' => 'Technologien',\n  'Galaxy' => 'Galaxie',\n  'lm_fleet_orbiting' => 'Flotte im Orbit',\n  'Alliance' => 'Dein Allianz',\n  'Allianc' => 'Allianz',\n  'AllyChat' => 'Allianz-Chat',\n  'Statistics' => 'Statistiken',\n  'Search' => 'Suche',\n  'Records' => 'Rekorde',\n  'Messages' => 'Nachrichten',\n  'Notes' => 'Notizen',\n  'Buddylist' => 'Freunde',\n  'Chat' => 'Chat',\n  'Contact' => 'Administration',\n  'Options' => 'Einstellungen',\n  'Bug' => 'Techn. Hilfe',\n  'Logout' => 'Abmelden',\n  'Rules' => 'Regeln',\n  'devlp' => 'Entwicklung',\n  'navig' => 'Information',\n  'Economy' => 'Wirtschaft',\n  'trade' => 'Handel',\n  'Society' => 'Gemeinschaft',\n  'rinok' => 'Schwarzmarkt',\n  'observ' => 'Observatorium',\n  'commun' => 'Administration',\n  'infog' => 'Information',\n  'lm_combat_reports' => 'Kampfberichte',\n  'adm_over' => 'Übersicht',\n  'adm_conf' => 'Einstellungen',\n  'adm_reset' => 'Zurücksetzen',\n  'adm_plrlst' => 'Spielerliste',\n  'adm_panel' => 'Admin-Panel',\n  'adm_plrsch' => 'Spielersuche',\n  'adm_addres' => 'Ressourcen hinzufügen',\n  'adm_pltlst' => 'Planetenliste',\n  'adm_actplt' => 'Aktive Planeten',\n  'adm_moonlst' => 'Mondliste',\n  'adm_addmoon' => 'Mond hinzufügen',\n  'adm_fleet' => 'Flotten im Flug',\n  'adm_ban' => 'Bannen',\n  'adm_unban' => 'Entbannen',\n  'adm_chat' => 'Chat-Editor',\n  'adm_updpt' => 'Status aktualisieren',\n  'adm_msg' => 'Nachrichtenliste',\n  'adm_md5' => 'Verschlüsselung',\n  'adm_updrank' => 'Datenbank zurücksetzen',\n  'adm_log_main' => 'Log-Einträge',\n  'adm_help' => 'Entwicklerforum',\n  'adm_back' => 'Zurück',\n  'admin' => 'Administration',\n  'player' => 'Spieler',\n  'tool' => 'Werkzeuge',\n  'lm_ifo_serv' => 'Rohstoffe',\n  'lm_ifo_game' => 'Spiel',\n  'lm_ifo_fleet' => 'Flotte',\n  'lm_ifo_queue' => 'Warteschlange',\n  'lm_shortcuts' => 'Lesezeichen',\n  'lm_banned' => 'Bannliste',\n  'lm_announce_fresh' => 'NEU',\n  'lm_server_info' => 'Über den Server',\n\n  'menu_quest_list' => 'Questliste',\n  'menu_universe_overview' => 'Universumsübersicht',\n  'menu_stat_players' => 'Statistiken',\n  'menu_stat_records' => 'Rekorde',\n  'menu_races' => 'Heimatwelten',\n  'menu_metamatter' => 'Metamaterie',\n\n  'menu_hide' => '<<',\n  'menu_show' => '>>',\n\n  'menu_pin' => 'Menü fixieren',\n  'menu_unpin' => 'Menü lösen',\n\n  'matter_analyze' => 'Materiebilanz',\n));"
  },
  {
    "path": "language/de/messages.mo.php",
    "content": "<?php\n\n/*\n#############################################################################\n#  Filename: messages.mo\n#  Project: SuperNova.WS\n#  Website: http://www.supernova.ws\n#  Description: Massive Multiplayer Online Browser Space Strategy Game\n#\n#  Copyright © 2009-2018 Gorlum for Project \"SuperNova.WS\"\n#############################################################################\n*/\n\n/**\n*\n* @package language\n* @system [German]\n* @version 46d0\n*\n*/\n\n/**\n* DO NOT CHANGE\n*/\n\nif (!defined('INSIDE')) die();\n\n$a_lang_array = [\n  'msg_page_header' => 'Private Nachrichten',\n  'msg_head_type' => 'Kategorien',\n  'msg_head_count' => 'Ungelesen',\n  'msg_head_total' => 'Gesamt',\n  'msg_mark_select' => '-- BEREICH AUSWÄHLEN --',\n  'msg_mark_checked' => 'Markierte Nachrichten',\n  'msg_mark_unchecked' => 'Nicht markierte Nachrichten',\n  'msg_mark_class' => 'Alle Nachrichten der Kategorie',\n  'msg_mark_all' => 'ALLE privaten Nachrichten',\n  'msg_select_all' => 'Alle auswählen',\n  'msg_delete_checked' => 'Markierte Nachrichten löschen',\n  'msg_show_all' => 'Alle anzeigen',\n  'msg_date' => 'Datum',\n  'msg_from' => 'Von',\n  'msg_recipient' => 'An',\n  'msg_subject' => 'Betreff',\n  'msg_answer' => 'Antworten',\n  'msg_answer_prefix' => 'AW:',\n  'msg_compose' => 'Nachricht schreiben',\n  'msg_text' => 'Nachrichtentext',\n  'msg_subject_default' => 'Neue Nachricht',\n  'msg_not_message_sent' => 'Nachricht gesendet',\n  'msg_warn_no_messages' => 'Keine Nachrichten in dieser Kategorie',\n  'msg_err_player_not_found' => 'Spieler nicht gefunden',\n  'msg_err_no_text' => 'Leere Nachrichten können nicht gesendet werden',\n  'msg_err_self_send' => 'Sie können sich keine Nachricht selbst senden',\n  'msg_del_class' => 'Alle Nachrichten dieser Kategorie löschen',\n  'msg_page_hint_class' =>\n    '<ul>\n      <li>Die Kategorie \"Gesendete Nachrichten\" enthält eine Liste der von Ihnen gesendeten Nachrichten, die vom Empfänger noch nicht gelöscht wurden. Sie können Nachrichten in dieser Kategorie nicht löschen</li>\n      <li>Um alle Nachrichten einer bestimmten Kategorie zu löschen, klicken Sie auf das Löschsymbol in der entsprechenden Zeile</li>\n      <li>Das Löschen von Nachrichten aus der Kategorie \"Alle Nachrichten\" führt zur Leerung des Posteingangs</li>\n      <li>Eine langsame Verbindung und/oder eine große Anzahl von Nachrichten in einer Kategorie kann dazu führen, dass Nachrichten nicht angezeigt werden können. In diesem Fall müssen Sie den Posteingang vollständig leeren oder alle Nachrichten aus der problematischen Kategorie löschen</li>\n    </ul>',\n  'msg_header_dialog' => 'Dialog mit',\n\n  'msg_ignore' => 'Ignorieren',\n  'msg_ignore_title' => \"Spieler [PLAYER_NAME] zur Ignorierliste hinzufügen?\",\n  'msg_ignore_message' => \"Sie werden keine privaten Nachrichten von Spielern auf der Ignorierliste mehr sehen.<br><br>Sie können Ihre Ignorierliste auf der Seite 'Einstellungen' verwalten.<br><br>Spieler [PLAYER_NAME] zur Ignorierliste hinzufügen?\",\n  'msg_message' => 'Nachricht',\n  'msg_ignored_messages' => 'Nachrichten von ignorierten Benutzern werden nicht angezeigt',\n  'msg_ignore_control' => 'Sie können Ihre Ignorierliste auf der Seite \"Einstellungen\" verwalten',\n];"
  },
  {
    "path": "language/de/mrc_mercenary.mo.php",
    "content": "<?php\n\n/*\n#############################################################################\n#  Filename: mercenary.mo\n#  Project: SuperNova.WS\n#  Website: http://www.supernova.ws\n#  Description: Massive Multiplayer Online Browser Space Strategy Game\n#\n#  Copyright © 2009-2018 Gorlum for Project \"SuperNova.WS\"\n#############################################################################\n*/\n\n/**\n*\n* @package language\n* @system [German]\n* @version 46d0\n*\n* @clean - all constants is used\n*\n*/\n\n/**\n* DO NOT CHANGE\n*/\n\nif (!defined('INSIDE'))\n{\n  exit;\n}\n\n$a_lang_array = (array(\n  'mrc_up_to' => 'bis',\n  'mrc_hire' => 'Anheuern',\n  'mrc_hire_for' => 'Anheuern für',\n  'mrc_allowed' => 'Verfügbare',\n  'mrc_msg_error_wrong_mercenary' => 'Falsche Söldner-ID',\n  'mrc_msg_error_wrong_level' => 'Falsches Söldner-Level',\n  'mrc_msg_error_wrong_period' => 'Ungültige Anheuerungsdauer',\n  'mrc_msg_error_already_hired' => 'Söldner bereits angeheuert. Entlassen Sie ihn oder warten Sie das Ende der Anheuerungszeit ab',\n  'mrc_msg_error_no_resource' => 'Nicht genug Dunkle Materie zum Anheuern',\n  'mrc_msg_error_requirements' => 'Anheuerungsvoraussetzungen nicht erfüllt',\n\n  'mrc_dismiss' => 'Entlassen',\n  'mrc_dismiss_confirm' => 'Bei Entlassung des Söldners geht die gesamte für seine Anheuerung ausgegebene DM verloren! Möchten Sie den Söldner wirklich entlassen?',\n  'mrc_dismiss_before_hire' => 'Um das Level eines angeheuerten Söldners zu ändern, müssen Sie zuerst den aktuellen entlassen - mit Verlust der für die Anheuerung ausgegebenen DM',\n\n  'mrc_mercenary_hired_log' => 'Söldner \"%1$s\" ID %2$d für %3$d DM auf %4$d Tage angeheuert',\n  'mrc_mercenary_dismissed_log' => 'VERLOREN: %7$d Tage Anheuerung und %8$d DM zum aktuellen Preis! Söldner \"%1$s\" ID %2$d entlassen, angeheuert auf %4$d Tage (von %5$s bis %6$s) mit aktuellem Wert von %3$d DM',\n  'mrc_plan_bought_log' => '\"%1$s\" ID %2$d für %3$d DM gekauft',\n));"
  },
  {
    "path": "language/de/notes.mo.php",
    "content": "<?php\n\n/*\n#############################################################################\n#  Filename: notes.mo\n#  Project: SuperNova.WS\n#  Website: http://www.supernova.ws\n#  Description: Massive Multiplayer Online Browser Space Strategy Game\n#\n#  Copyright © 2009-2018 Gorlum for Project \"SuperNova.WS\"\n#############################################################################\n*/\n\n/**\n*\n* @package language\n* @system [German]\n* @version 46d0\n*\n*/\n\n/**\n* DO NOT CHANGE\n*/\n\n!defined('INSIDE') && die();\n\n$a_lang_array = (array(\n  'note_page_header' => 'Notizen',\n  'note_date' => 'Datum',\n  'note_note' => 'Notiz',\n  'note_priority' => 'Priorität',\n  'note_sticky' => 'Angeheftet',\n  'note_new_title' => 'Titel der neuen Notiz',\n  'note_new_text' => 'Text der neuen Notiz',\n  'note_stick_it' => 'Notiz unter der Navigation auf jeder Seite anheften',\n\n  'note_err_none_added' => 'Notiz erfolgreich hinzugefügt',\n  'note_err_none_changed' => 'Notiz erfolgreich geändert',\n  'note_err_note_not_found' => 'Notiz mit dieser ID nicht gefunden. Möglicherweise wurde sie in einem anderen Fenster gelöscht',\n  'note_err_owner_wrong' => 'Sie sind nicht der Besitzer dieser Notiz',\n  'note_err_note_empty' => 'Sie haben keinen Text in die Notiz eingegeben - sie wird nicht hinzugefügt',\n\n  'note_delete' => 'Notizen löschen',\n  'note_range_select' => '-- BEREICH AUSWÄHLEN --',\n  'note_range_marked' => 'Markierte Notizen',\n  'note_range_marked_not' => 'Nicht markierte Notizen',\n  'note_range_all' => 'Alle Notizen',\n\n  'note_warn_no_range' => 'Sie haben keinen Bereich ausgewählt. Nichts zu löschen',\n  'note_err_none_selected' => 'Keine Notizen ausgewählt - Löschung wird nicht durchgeführt. Um alle Notizen zu löschen, wählen Sie den Bereich \"Alle Notizen\"',\n));"
  },
  {
    "path": "language/de/options.mo.php",
    "content": "<?php\n/** @noinspection HtmlUnknownTarget */\n\n/*\n#############################################################################\n#  Filename: options.mo\n#  Project: SuperNova.WS\n#  Website: http://www.supernova.ws\n#  Description: Massive Multiplayer Online Browser Space Strategy Game\n#\n#  Copyright © 2009-2018 Gorlum for Project \"SuperNova.WS\"\n#############################################################################\n*/\n\n/**\n*\n* @package language\n* @system [German]\n* @version 46d0\n*\n*/\n\n/**\n* DO NOT CHANGE\n*/\n\nif (!defined('INSIDE')) die();\n\n$a_lang_array = [\n  'opt_account' => 'Profil',\n  'opt_int_options' => 'Interface',\n  'opt_settings_statistics' => 'Spielerstatistiken',\n  'opt_settings_info' => 'Spielerinformation',\n  'opt_alerts' => 'Benachrichtigungen',\n  'opt_common' => 'Allgemein',\n  'opt_tutorial' => 'Tutorial',\n\n  'opt_birthday' => 'Geburtstag',\n\n  'opt_header' => 'Benutzereinstellungen',\n  'opt_messages' => 'Automatische Benachrichtigungen',\n  'opt_msg_saved' => 'Einstellungen erfolgreich geändert',\n  'opt_msg_name_changed' => 'Benutzername erfolgreich geändert',\n  'opt_msg_name_change_err_used_name' => 'Dieser Name gehört bereits einem anderen Spieler',\n  'opt_msg_name_change_err_no_dm' => 'Nicht genug DM für Namensänderung',\n\n  'username_old' => 'Aktueller Name',\n  'username_new' => 'Neuer Name',\n  'username_change_confirm' => 'Namen ändern',\n  'username_change_confirm_payed' => 'für',\n\n  'opt_msg_pass_changed' => 'Passwort erfolgreich geändert',\n  'opt_err_pass_wrong' => 'Falsches aktuelles Passwort. Passwort wurde nicht geändert',\n  'opt_err_pass_unmatched' => 'Die eingegebenen Passwörter stimmen nicht überein. Passwort wurde nicht geändert',\n  'changue_pass' => 'Passwort ändern',\n  'Download' => 'Download',\n  'userdata' => 'Information',\n  'username' => 'Name',\n  'lastpassword' => 'Altes Passwort',\n  'newpassword' => 'Neues Passwort<br>(min. 8 Zeichen)',\n  'newpasswordagain' => 'Neues Passwort wiederholen',\n  'emaildir' => 'E-Mail-Adresse',\n  'emaildir_tip' => 'Diese Adresse kann jederzeit geändert werden. Die Adresse wird zur Hauptadresse, wenn sie innerhalb von 7 Tagen nicht geändert wird.',\n  'permanentemaildir' => 'Haupt-E-Mail-Adresse',\n  'opt_planet_sort_title' => 'Planeten sortieren nach',\n  'opt_planet_sort_options' => [\n    SORT_ID       => 'Kolonisierungszeit',\n    SORT_LOCATION => 'Koordinaten',\n    SORT_NAME     => 'Alphabet',\n    SORT_SIZE     => 'Feldanzahl',\n  ],\n  'opt_planet_sort_ascending' => [\n    SORT_ASCENDING  => 'Aufsteigend',\n    SORT_DESCENDING => 'Absteigend',\n  ],\n\n  'opt_navbar_title' => 'Navigationsleiste',\n  'opt_navbar_description' => 'Die Navigationsleiste (kurz \"Navbar\") befindet sich oben auf dem Bildschirm. Dieser Abschnitt ermöglicht die Anpassung der Navbar.',\n  'opt_navbar_resourcebar_description' => 'Ressourcenleiste - Ressourcenpanel',\n  'opt_navbar_buttons_title' => 'Navbar-Schaltflächen Einstellungen',\n  'opt_player_options' => [\n    PLAYER_OPTION_NAVBAR_PLANET_VERTICAL        => 'Vertikale Ressourcenleiste',\n    PLAYER_OPTION_NAVBAR_PLANET_DISABLE_STORAGE => 'Lagerkapazität in der Ressourcenleiste ausblenden',\n    PLAYER_OPTION_NAVBAR_PLANET_OLD             => 'Alte Tabellenansicht der Ressourcen verwenden',\n\n    PLAYER_OPTION_NAVBAR_RESEARCH_WIDE          => 'Breite Forschungsschaltfläche (alte Ansicht)',\n    PLAYER_OPTION_NAVBAR_DISABLE_RESEARCH       => 'Forschungsschaltfläche deaktivieren',\n    PLAYER_OPTION_NAVBAR_DISABLE_PLANET         => 'Planetenschaltfläche deaktivieren',\n    PLAYER_OPTION_NAVBAR_DISABLE_HANGAR         => 'Werftschaltfläche deaktivieren',\n    PLAYER_OPTION_NAVBAR_DISABLE_DEFENSE        => 'Verteidigungsschaltfläche deaktivieren',\n    PLAYER_OPTION_NAVBAR_DISABLE_EXPEDITIONS    => 'Expeditionsschaltfläche deaktivieren',\n    PLAYER_OPTION_NAVBAR_DISABLE_FLYING_FLEETS  => 'Fliegende Flotten Schaltfläche deaktivieren',\n    PLAYER_OPTION_NAVBAR_DISABLE_QUESTS         => 'Questschaltfläche deaktivieren',\n    PLAYER_OPTION_NAVBAR_DISABLE_META_MATTER    => 'MetaMaterie Schaltfläche deaktivieren',\n\n    PLAYER_OPTION_UNIVERSE_OLD                  => 'Alte \"Universumsübersicht\" Ansicht verwenden',\n    PLAYER_OPTION_UNIVERSE_DISABLE_COLONIZE     => 'Kolonisierungsschaltfläche deaktivieren',\n    PLAYER_OPTION_DESIGN_DISABLE_BORDERS        => 'Tabellenrahmen-Bilder deaktivieren',\n    PLAYER_OPTION_TECH_TREE_TABLE               => 'Technologieseite als Tabelle (alte Ansicht)',\n    PLAYER_OPTION_FLEET_SHIP_SELECT_OLD         => 'Schiffsanzahl in separater Spalte (alte Ansicht)',\n    PLAYER_OPTION_FLEET_SHIP_HIDE_SPEED         => 'Schiffsgeschwindigkeit nicht anzeigen',\n    PLAYER_OPTION_FLEET_SHIP_HIDE_CAPACITY      => 'Lagerkapazität des Schiffes nicht anzeigen',\n    PLAYER_OPTION_FLEET_SHIP_HIDE_CONSUMPTION   => 'Treibstoffverbrauch des Schiffes nicht anzeigen',\n    PLAYER_OPTION_TUTORIAL_DISABLED             => 'Tutorial komplett deaktivieren',\n    PLAYER_OPTION_TUTORIAL_WINDOWED             => 'Tutorial-Text in Popup-Fenster anzeigen',\n    PLAYER_OPTION_TUTORIAL_CURRENT              => 'Tutorial zurücksetzen - Tutorial beginnt von neuem',\n\n    PLAYER_OPTION_PLANET_SORT_INVERSE           => 'In umgekehrter Reihenfolge',\n    PLAYER_OPTION_BUILD_AUTOCONVERT_HIDE        => 'Autokonvertierungsschaltfläche ausblenden',\n\n    PLAYER_OPTION_SOUND_ENABLED                 => 'Spielsounds aktivieren',\n    PLAYER_OPTION_ANIMATION_DISABLED            => 'Animationseffekte deaktivieren',\n    PLAYER_OPTION_PROGRESS_BARS_DISABLED        => 'Fortschrittsbalken deaktivieren',\n  ],\n\n  'opt_chk_skin' => 'Design verwenden',\n  'opt_adm_title' => 'Administrationsoptionen',\n  'opt_adm_planet_prot' => 'Planetenschutz',\n  'thanksforregistry' => 'Danke für die Registrierung.<br />In wenigen Minuten erhalten Sie eine E-Mail mit Ihrem Passwort.',\n  'general_settings' => 'Allgemeine Einstellungen',\n  'skins_example' => 'Design',\n\n  'opt_avatar' => 'Avatar',\n  'opt_avatar_search' => 'In Google suchen',\n  'opt_avatar_remove' => 'Avatar entfernen',\n  'opt_upload' => 'Hochladen',\n\n  'opt_msg_avatar_removed' => 'Avatar entfernt',\n  'opt_msg_avatar_uploaded' => 'Avatar erfolgreich geändert',\n  'opt_msg_avatar_error_delete' => 'Fehler beim Löschen der Avatar-Datei. Bitte wenden Sie sich an die Serveradministration',\n  'opt_msg_avatar_error_writing' => 'Fehler beim Speichern der Avatar-Datei. Bitte wenden Sie sich an die Serveradministration',\n  'opt_msg_avatar_error_upload' => 'Fehler beim Hochladen des Bildes %1. Bitte wenden Sie sich an die Serveradministration',\n  'opt_msg_avatar_error_unsupported' => 'Das Format des hochgeladenen Bildes wird nicht unterstützt. Es werden nur JPG, GIF, PNG Dateien bis zu 200KB unterstützt',\n\n  'untoggleip' => 'IP-Überprüfung deaktivieren',\n  'untoggleip_tip' => 'IP-Überprüfung bedeutet, dass Sie sich nicht von zwei verschiedenen IPs aus mit Ihrem Namen anmelden können. Die Überprüfung erhöht Ihre Sicherheit!',\n  'galaxyvision_options' => 'Universum',\n  'spy_cant' => 'Anzahl der Sonden',\n  'spy_cant_tip' => 'Anzahl der Sonden, die beim Spionieren versendet werden.',\n  'tooltip_time' => 'Verzögerung vor dem Anzeigen des Tooltips',\n  'mess_ammount_max' => 'Maximale Anzahl der Flottennachrichten',\n  'seconds' => 'Sekunde(n)',\n  'shortcut' => 'Schnellzugriff',\n  'show' => 'Anzeigen',\n  'write_a_messege' => 'Nachricht schreiben',\n  'spy' => 'Spionieren',\n  'add_to_buddylist' => 'Zu Freunden hinzufügen',\n  'attack_with_missile' => 'Raketenangriff',\n  'show_report' => 'Bericht anzeigen',\n  'delete_vacations' => 'Profilverwaltung',\n  'mode_vacations' => 'Urlaubsmodus aktivieren',\n  'vacations_tip' => 'Der Urlaubsmodus schützt Ihre Planeten während Ihrer Abwesenheit.',\n  'deleteaccount' => 'Profil deaktivieren',\n  'deleteaccount_tip' => 'Das Profil wird nach 45 Tagen Inaktivität gelöscht.',\n  'deleteaccount_on' => 'Bei Inaktivität des Accounts erfolgt die Löschung',\n  'save_settings' => 'Änderungen speichern',\n  'exit_vacations' => 'Urlaubsmodus verlassen',\n  'Vaccation_mode' => 'Urlaubsmodus aktiviert. Er bleibt aktiv bis: ',\n  'You_cant_exit_vmode' => 'Sie können den Urlaubsmodus nicht verlassen, bis die Mindestzeit abgelaufen ist',\n  'Error' => 'Fehler',\n  'cans_resource' => 'Stoppen Sie die Ressourcenproduktion auf den Planeten',\n  'cans_reseach' => 'Stoppen Sie die Forschung auf den Planeten',\n  'cans_build' => 'Stoppen Sie den Bau auf den Planeten',\n  'cans_fleet_build' => 'Stoppen Sie den Flotten- und Verteidigungsbau',\n  'cans_fly_fleet2' => 'Fremde Flotte nähert sich... Sie können nicht in den Urlaub gehen',\n  'vacations_exit' => 'Urlaubsmodus deaktiviert... Bitte melden Sie sich erneut an',\n  'select_skin_path' => 'AUSWÄHLEN',\n  'opt_language' => 'Interface-Sprache',\n  'opt_compatibility' => 'Kompatibilität - alte Interfaces',\n  'opt_compat_structures' => 'Altes Gebäudebau-Interface',\n  'opt_vacation_err_your_fleet' => 'Sie können nicht in den Urlaub gehen, solange mindestens eine Ihrer Flotten unterwegs ist',\n  'opt_vacation_err_building' => 'Sie bauen oder forschen auf %s und können daher nicht in den Urlaub gehen',\n  'opt_vacation_err_research' => 'Ihre Wissenschaftler forschen an einer Technologie und daher können Sie nicht in den Urlaub gehen',\n  'opt_vacation_err_que' => 'Sie forschen entweder an einer Technologie oder bauen etwas auf einem Ihrer Planeten und können daher nicht in den Urlaub gehen. Verwenden Sie den Link \"Imperium\", um die Bauwarteschlangen auf den Planeten anzuzeigen',\n  'opt_vacation_err_timeout' => 'Sie haben noch nicht genug für den Urlaub gearbeitet - die Timeout-Zeit für den Urlaub ist noch nicht abgelaufen',\n  'opt_vacation_next' => 'Sie können in den Urlaub gehen nach',\n  'opt_vacation_min' => 'mindestens bis',\n  'succeful_changepass' => 'Passwort erfolgreich geändert.<br /><a href=\"login.php\" target=\"_top\">Zurück</a>',\n\n  'opt_time_diff_clear' => 'Differenz zwischen Spielerzeit und Serverzeit messen',\n  'opt_time_diff_manual' => 'Zeitdifferenz manuell einstellen',\n  'opt_time_diff_explain' => 'Bei korrekt eingestellter Zeitdifferenz sollten die Uhren \"Spielerzeit\" in der Navbar sekundengenau mit den Uhren auf dem Gerät des Spielers übereinstimmen.<br />\n  Normalerweise stellt das Spiel die richtige Zeitdifferenz automatisch ein. Bei falsch eingestellter Zeitzone auf dem Gerät des Spielers, beim Spielen mit mehreren Geräten oder bei sehr langsamer Internetverbindung muss die Zeitdifferenz manuell eingestellt werden.',\n\n  'opt_custom' => [\n    'opt_uni_avatar_user' => 'Benutzeravatar anzeigen',\n    'opt_uni_avatar_ally' => 'Allianzlogo anzeigen',\n    'opt_int_struc_vertical' => 'Vertikale Bauwarteschlange',\n    'opt_int_navbar_resource_force' => 'Ressourcenleiste immer anzeigen',\n    'opt_int_overview_planet_columns' => 'Anzahl der Spalten in der Planetenliste',\n    'opt_int_overview_planet_columns_hint' => '0 - basierend auf maximaler Zeilenanzahl berechnen',\n    'opt_int_overview_planet_rows' => 'Maximale Anzahl der Zeilen in der Planetenliste',\n    'opt_int_overview_planet_rows_hint' => 'Wird ignoriert, wenn Spaltenanzahl angegeben ist',\n  ],\n\n  'opt_mail_optional_description' => 'An diese E-Mail-Adresse werden private Nachrichten von anderen Spielern und Benachrichtigungen über Spielereignisse (z.B. Expeditionsberichte und Spionageberichte) gesendet',\n  'opt_mail_permanent_description' => 'Diese E-Mail-Adresse ist mit dem Spielaccount verknüpft. Sie kann nur einmal eingegeben werden. Alle Systembenachrichtigungen (z.B. Passwortänderung) werden an diese Adresse gesendet',\n\n  'opt_account_name' => 'Ihr Login<br />Diesen Namen müssen Sie beim Einloggen ins Spiel eingeben',\n  'opt_game_user_name' => 'Spielname (Nickname)<br />Unter diesem Namen sind Sie für andere Spieler des Servers sichtbar',\n\n  'opt_universe_title' => 'Universum',\n\n  'option_fleets' => 'Flotten',\n  'option_fleet_send' => 'Flottenversand',\n\n  'option_change_nick_disabled' => 'Nickname-Änderung ist durch Servereinstellungen deaktiviert',\n\n  'opt_ignores' => 'Ignore-Liste',\n  'opt_unignore_do' => 'Aus Ignore-Liste entfernen',\n  'opt_ignore_list_empty' => 'Ihre Ignore-Liste ist leer',\n];"
  },
  {
    "path": "language/de/overview.mo.php",
    "content": "<?php\n\n/*\n#############################################################################\n#  Filename: overview.mo\n#  Project: SuperNova.WS\n#  Website: http://www.supernova.ws\n#  Description: Massive Multiplayer Online Browser Space Strategy Game\n#\n#  Copyright © 2009-2018 Gorlum for Project \"SuperNova.WS\"\n#############################################################################\n*/\n\n/**\n*\n* @package language\n* @system [German]\n* @version 46d0\n*\n*/\n\n/**\n* DO NOT CHANGE\n*/\n\nif (!defined('INSIDE')) die();\n\n\n$a_lang_array = (array(\n  'ov_hack_alert' => 'Versuch eines Datenbank-Hacks!!!',\n  'ov_you_have' => 'Sie haben',\n  'ov_new_message' => 'eine neue Nachricht',\n  'ov_new_messages' => 'neue Nachrichten',\n  'cancel' => 'Abbrechen',\n  'Planet_menu' => 'Gebäudebau auf dem Planeten',\n  'Planet' => 'Planet',\n  'Moon' => 'Mond',\n  'Have_new_level_mineur' => 'Für wirtschaftliche Erfolge haben Sie einen Offiziers-Entwicklungspunkt gewonnen!',\n  'Have_new_level_raid' => 'Für erfolgreiche Angriffe haben Sie einen Offiziers-Entwicklungspunkt gewonnen!',\n  'Server_time' => 'Zeit',\n  'Left_time' => 'Verbleibende Zeit',\n  'Now_build' => 'Aktuell im Bau',\n  'Events' => 'Ereignisse',\n  'Free' => 'keine Aufgaben',\n  'Diameter' => 'Durchmesser',\n  'fields' => 'Felder',\n  'Developed_fields' => 'Bebaute Felder',\n  'max_eveloped_fields' => 'Maximale Anzahl an Feldern',\n  'Temperature' => 'Temperatur',\n  'min_avg_max' => 'min/avg/max',\n  'approx' => 'ungefähr',\n  'to' => 'bis',\n  'Centigrade' => '°C',\n  'Position' => 'Koordinaten',\n//  'Buildings' => 'Gebäude',\n  'Fleet' => 'Flotte',\n  'Research' => 'Forschung',\n  'Total' => 'Gesamt',\n  'Rank' => 'Rang',\n  'of' => 'von',\n  'Miner' => 'Geologe',\n  'Raider' => 'Raider',\n  'Debris_Field' => 'Trümmerfeld',\n  'rename_and_abandon_planet' => 'Planetenverwaltung',\n  'functions' => 'Funktionen',\n  'coords' => 'Koordinaten',\n  'your_planet' => 'Ihr Planet',\n  'colony_abandon' => 'Kolonie aufgeben',\n  'deleteplanet' => 'Planet löschen!',\n  'security_query' => 'Sicherheitsabfrage',\n  'name' => 'Name',\n  'namer' => 'Umbenennen',\n//  'password' => 'Passwort',\n  'confirm_planet_delete' => 'Planetenlöschung bestätigen',\n  'confirmed_with_password' => 'Mit Passwort bestätigen',\n  'ov_delete_ok' => 'Kolonie erfolgreich gelöscht',\n  'ov_delete_wrong_planet' => 'Planet kann nicht verlassen werden! Sie versuchen Ihren Hauptplaneten zu verlassen oder haben den aktuellen Planeten in einem anderen Browserfenster geändert.',\n  'ov_delete_wrong_pass' => 'Falsches Passwort!',\n  'MembersOnline' => 'Spieler',\n  'ov_fleet_list' => 'Flottenbewegungsplan',\n  'ov_fleet' => 'Flotte',\n  'ov_destination' => 'Ziel',\n  'ov_source' => 'Herkunft',\n  'event_time' => 'Zeit',\n  'ov_mission' => 'Mission',\n  'ov_event' => 'Ereignis',\n  'ov_flying_fleets' => 'Flotten unterwegs nach',\n  'ov_other_planets' => 'andere Planeten',\n  'ov_fleet_arrive' => 'Ankunft',\n  'ov_fleet_return' => 'Rückkehr',\n  'ov_fleet_hold' => 'Ende der Mission',\n  'ov_fleet_rocket' => 'Raketenangriff',\n  'ov_fleet_exploration' => 'Erkundung',\n  'ov_fleet_colonization' => 'Planetenkolonisierung',\n  'ov_fleet_no_flying' => 'Keine Flotten unterwegs',\n  'ov_vennant' => ' gesendet von ',\n  'ov_planet_to' => 'vom Planet ',\n  'ov_moon_to' => 'vom Mond ',\n  'ov_atteint' => ' gesendet zu ',\n  'ov_planet_to_target' => 'Planet ',\n  'ov_moon_to_target' => 'Mond ',\n  'ov_debris_to_target' => 'Trümmerfeld ',\n  'ov_explo_to_target' => 'Position ',\n  'ov_explo_stay' => ' zur Erkundung ',\n  'ov_explo_mission' => '. Mission: ',\n  'ov_rentrant' => ' kehrt zurück von ',\n  'ov_planet_from' => 'vom Planet ',\n  'ov_moon_from' => 'vom Mond ',\n  'ov_debris_from' => 'vom Trümmerfeld ',\n  'ov_explo_from' => 'von Position ',\n  'ov_back_planet' => ' zum Planet ',\n  'ov_back_moon' => ' zum Mond ',\n  'ov_hostile' => ' Spieler ',\n  'ov_message' => 'Nachricht senden',\n\n  'imp_user_points_struc' => 'Für Gebäude',\n  'imp_user_points_tech' => 'Für Forschung',\n  'imp_user_points_fleet' => 'Für Flotte',\n  'imp_user_points_def' => 'Für Verteidigung',\n  'imp_user_points_res' => 'Für Ressourcen',\n  'imp_user_points_all' => 'Gesamt',\n  'NumberOfRaids' => 'Durchgeführt',\n  'RaidsWin' => 'Gewonnen',\n  'RaidsLoose' => 'Verloren',\n  'Economica' => 'Gebäude',\n  'Teching' => 'Forschung',\n  'imp_statistics' => 'Statistik',\n\n  'Points_1' => 'Felder',\n  'km' => 'km',\n  'orb' => 'Trümmer in der Umlaufbahn',\n  'buildings_on_planet' => 'Bebauung',\n  'ov_planet_details' => 'Planetendetails',\n  'ov_building' => 'Gebäude',\n  'ov_hangar' => 'Werft',\n  'ov_rank' => 'Rang',\n  'ov_rpg_new_level_miner' => 'Für wirtschaftliche Erfolge erhalten Sie Dunkle Materie.',\n  'ov_rpg_new_level_raid' => 'Für erfolgreiche Angriffe erhalten Sie Dunkle Materie.',\n  'ov_points' => 'Punkte',\n  'ov_raids' => 'Raids',\n  'ov_experience' => 'Erfahrung',\n  'ov_player_rpg' => 'Spielerstatistik',\n  'ov_banner' => 'Banner',\n  'ov_userbar' => 'Userbar',\n  'ov_banner_empty_id' => 'SuperNova - Join The Game!',\n  'ov_new' => 'NEU',\n  'ov_overview' => 'Übersicht',\n  'ov_manage' => 'Verwaltung',\n  'ov_return' => 'Zurück zur Übersicht',\n  'ov_rename' => 'Umbenennen',\n  'ov_planet_rename_dialog_title' => 'Planet/Mond umbenennen',\n  'ov_new_name' => 'Neuer Name',\n  'cur_governor' => 'Aktueller Gouverneur',\n  'ov_mrc_confirm_1' => 'Möchten Sie wirklich den Gouverneur',\n  'ov_mrc_confirm_2' => 'Level',\n  'ov_mrc_confirm_3' => 'durch den Gouverneur',\n  'ov_mrc_confirm_4' => 'ersten Levels ersetzen? Die gesamte in den aktuellen Gouverneur investierte Dunkle Materie geht VERLOREN!',\n  'ov_manage_page_hint' => '<li class=\"warning\">ACHTUNG! Bei der Einstellung eines anderen Gouverneurs wird der vorherige Gouverneur OHNE ENTSCHÄDIGUNG DER DUNKLEN MATERIE entlassen und der Level des neuen Gouverneurs wird auf eins gesetzt! Planen Sie daher die Entwicklung Ihres Imperiums im Voraus, indem Sie für jeden Planeten je nach dessen geplanter Rolle einen geeigneten Gouverneur auswählen!</li>\n  <li>Gouverneure sind Söldner, die einen einzelnen Planeten verwalten und bestimmte Boni für diesen Planeten gewähren</li>\n  <li>Klicken Sie auf das Bild des Gouverneurs in der Liste, um dessen Beschreibung und die gewährten Boni zu sehen</li>\n  <li>Klicken Sie auf \"Einstellen\", um einen bestimmten Gouverneur einzustellen</li>\n  <li>Die erneute Einstellung des aktuellen Gouverneurs erhöht dessen Level und damit den planetaren Effekt</li>\n  <li>Einige Gouverneure haben Levelbeschränkungen. Andere nicht</li>\n  <li>Die Teleportation bewegt den Planeten zusammen mit den in der Umlaufbahn befindlichen Flotten zu neuen Koordinaten</li>\n  <li>Wenn der Planet einen Mond hat, wird dieser ebenfalls zusammen mit den Flotten zu den neuen Koordinaten bewegt</li>\n  <li>Teleportation ist nicht möglich, wenn in der Nähe des Planeten Flottenaktivitäten stattfinden (d.h. es gibt Flotten, die den Planeten, Mond oder das Trümmerfeld als Start- oder Zielpunkt haben)</li>\n  <li>Nach der Teleportation muss eine gewisse Zeit bis zur nächsten Teleportation gewartet werden - die gestörte Raummetrik um den Planeten muss sich normalisieren</li>',\n\n  'ov_gate_time_left' => 'Zeit bis zum nächsten Sprung',\n\n  'ov_teleport' => 'Teleportieren',\n  'ov_teleport_new_coordinates' => 'Neue Koordinaten',\n  'ov_teleport_err_none' => 'Planet erfolgreich teleportiert',\n  'ov_teleport_err_wrong_coordinates' => 'Falsche Koordinaten',\n  'ov_teleport_err_fleet' => 'In der Nähe des Planeten werden Flottenaktivitäten beobachtet',\n  'ov_teleport_err_destination_busy' => 'Zielort ist besetzt',\n  'ov_teleport_err_cooldown' => 'Teleportation kann nicht zweimal hintereinander durchgeführt werden. Warten Sie, bis sich die Raummetrik normalisiert hat',\n  'ov_teleport_err_no_dark_matter' => 'Nicht genug Dunkle Materie für die Teleportation',\n  'ov_teleport_log_record' => 'Planet {%2$d} %1$s wurde von Koordinaten %3$s zu Koordinaten %4$s teleportiert',\n\n  'ov_capital' => 'Zur Hauptstadt des Imperiums machen',\n  'ov_capital_err_none' => 'Der Planet ist jetzt die Hauptstadt des Imperiums',\n  'ov_capital_err_capital_already' => 'Dieser Planet ist bereits die Hauptstadt des Imperiums',\n  'ov_capital_err_no_dark_matter' => 'Nicht genug Dunkle Materie, um die Hauptstadt zu verlegen',\n  'ov_capital_err_not_a_planet' => 'Nur ein Planet kann zur Hauptstadt des Imperiums gemacht werden',\n\n  'read_all_news' => 'Alle als gelesen markieren',\n\n  'imp_TBA' => 'Etwas kommt...',\n  'imp_experience_current' => 'Aktuell',\n  'imp_experience_to_level' => 'Levelup',\n\n  'ov_manage_special' => 'Spezialfunktionen',\n  'ov_password' => 'Ihr Passwort für den Spielzugang',\n\n  'ov_governor_purchase' => 'Spieler hat Gouverneur %1$s ID %2$d Level %3$d für Planet %4$s gekauft',\n));"
  },
  {
    "path": "language/de/payment.mo.php",
    "content": "<?php\n\n/*\n#############################################################################\n#  Filename: payment.mo.php\n#  Project: SuperNova.WS\n#  Website: http://www.supernova.ws\n#  Description: Massive Multiplayer Online Browser Space Strategy Game\n#\n#  Copyright © 2009-2018 Gorlum for Project \"SuperNova.WS\"\n#############################################################################\n*/\n\n/**\n*\n* @package language\n* @system [Russian]\n* @version 46d0\n*\n*/\n\n/**\n* DO NOT CHANGE\n*/\n\nif (!defined('INSIDE')) die();\n\n$a_lang_array = (array(\n  // Metamatter\n  'sys_metamatter_what_header' => 'Что такое <span class=\"metamatter\">Метаматерия</span>',\n  'sys_metamatter_what_description' => '<span class=\"metamatter\">Метаматерия</span> (сокращенно <span class=\"metamatter\">ММ</span>) - это весьма условное название для особого состояния Вселенной. Фактически - это даже не материя, а факторизируемая вероятность.<br /><br />\n  У <span class=\"metamatter\">Метаматерии</span> нет состояния - и в то же время она находится во всех состояних. <span class=\"metamatter\">Метаматерия</span> нигде не находится - и в то же время находится везде. Потенциально метаматерия может стать чем угодно и где угодно - если правильно актуализировать вероятность.',\n  'sys_metamatter_what_purchase' => '<span class=\"metamatter\">Метаматерию</span> можно рассматривать как \"Покупную <span class=\"dark_matter\">Тёмную Материю</span>\" - при нехватке <span class=\"metamatter\">ТМ</span> для приобретения чего-либо <span class=\"metamatter\">ММ</span> будет <span class=\"ok\">автоматически сконвертирована</span> в недостающее количество <span class=\"dark_matter\">ТМ</span> по курсу <span class=\"dark_matter\">1 ТМ</span> = <span class=\"metamatter\">1 ММ</span>',\n\n  'pay_mm_convert_header' => 'Конвертация Метаматерии в Тёмную Материю',\n  'pay_mm_convert_text' => 'Метаматерия конвертируется в Тёмную Материю по курсу 1 к 1',\n  'pay_mm_convert_no_mm' => 'Нет Метаматерии - купите её сначала',\n  'pay_mm_convert_prefix' => 'Единицы Метаматерии',\n  'pay_mm_convert_suffix' => '',\n  'pay_mm_convert_do' => 'Сконвертировать в ТМ',\n\n  'pay_msg_mm_convert_wrong_amount' => 'Неправильное количество Метаматерии',\n  'pay_msg_mm_convert_not_enough' => 'Не хватает Метаматерии для конвертации в Тёмную Материю',\n  'pay_msg_mm_convert_mm_error' => 'Ошибка изменения количеста Метаматерии',\n  'pay_msg_mm_convert_dm_error' => 'Ошибка изменения количеста Тёмной Материи',\n\n  'pay_mm_buy' => 'Приобрести <span class=\"metamatter\">Метаматерию</span>',\n  'pay_mm_buy_text_cost' => 'Стоимость',\n  'pay_mm_buy_text_unit' => 'составляет',\n  'pay_mm_buy_url_description' => '<span class=\"metamatter\">ММ</span> можно приобрести только за реальные деньги',\n  'pay_mm_buy_url_get'  => 'Откройте эту ссылку, что бы узнать подробности',\n  'pay_mm_buy_url_none' => 'Свяжитесь с Администрацией сервера по вопросам получения <span class=\"metamatter\">Метаматерии</span>',\n\n  'pay_mm_bonus_header' => 'Стоимость <span class=\"metamatter\">Метаматерии</span> и бонусы за оптовую покупку',\n  'pay_mm_bonus' => 'При оптовой покупке <span class=\"metamatter\">ММ</span> предоставляются бонусы',\n  'pay_mm_bonus_each' => 'от %s <span class=\"metamatter\">ММ</span> - бонус %d%% к количеству <span class=\"metamatter\">ММ</span>',\n  'pay_mm_bonus_text' => 'Бонус',\n\n  'pay_mm_buy_step1_text' => 'Выберите количество <span class=\"metamatter\">ММ</span>, способ оплаты и подтвердите свой выбор',\n  'pay_mm_buy_metamatter_amount' => 'Выберите количество <span class=\"metamatter\">Метаматерии</span> из списка',\n  'pay_mm_buy_metamatter_amount_enter' => '...или введите другое количество <span class=\"metamatter\">Метаматерии</span>',\n  'pay_mm_buy_price_for' => 'Цена за',\n  'pay_mm_buy_unit' => '<span class=\"metamatter\">Метаматерии</span>',\n  'pay_mm_buy_select' => 'Выберите платёжную систему',\n  'pay_mm_buy_method_detail' => 'Некоторые способы оплаты предлагают выбор разных платёжных систем. Если платёж не проходит через одну платёжную систему - попробуйте использовать тот же способ оплаты с другой платёжной системой',\n  'pay_mm_buy_confirm' => 'Подтвердить выбор',\n  'pay_mm_buy_payment_selected' => 'Оплата будет произведена с использованием платёжной системы',\n  'pay_mm_buy_purchase' => 'Покупка',\n\n  'pay_mm_buy_payment_method_more' => 'Нажмите здесь, что бы увидеть больше способов оплаты',\n\n  'pay_mm_buy_payment_method_select' => 'Выберите способ оплаты',\n  'pay_mm_buy_payment_method_selected' => 'Вы выбрали способ оплаты',\n\n  'pay_mm_buy_step2_text' => 'Рассчётная стоимость не включает дополнительные комиссии, которые могут взимать платёжные системы и/или разнообразные посредники. Проверьте выбранное количество <span class=\"metamatter\">Метаматерии</span> и способ оплаты. Если все правильно - нажмите кнопку \"Оплатить <span class=\"metamatter\">Метаматерию</span>\". Если вы ошиблись - нажмите кнопку \"Начать заново\"',\n  'pay_mm_buy_pay' => 'Оплатить <span class=\"metamatter\">Метаматерию</span>',\n  'pay_mm_buy_reset' => 'Начать заново',\n  'pay_mm_buy_in_progress' => 'Происходит оплата...',\n  'pay_mm_buy_conversion_cost' => 'Рассчётная стоимость %1$s единиц <span class=\"metamatter\">Метаматерии</span> в валюте платежной системы составит <span class=\"%4$s\">%2$s</span> %3$s',\n  'pay_mm_buy_cost_base' => 'Стоимость составит',\n  'pay_mm_buy_real_income' => 'Бонус за оптовую покупку составит %s%% и на ваш игровой счёт будет зачислено %s <span class=\"metamatter\">ММ</span>',\n  'pay_mm_buy_approximate_cost' => 'Приблизительная стоимость ММ на платёжной системе составляет <span class=\"notice\">%1$s %2$s</span> (стоимость дана ПРИБЛИЗИТЕЛЬНО. Итоговая сумма на платёжной системе может отличаться)',\n\n  'pay_currency_name' => 'Валюта',\n  'pay_currency_symbol' => 'Символ',\n  'pay_currency_choose' => 'Выберите валюту платежа',\n  'pay_currency_list' => array(\n    'RUB' => 'Российский рубль',\n    'USD' => 'Доллар США',\n    'EUR' => 'Евро',\n    'UAH' => 'Украинская гривна',\n//    'WMR' => 'WebMoney рубль',\n//    'WMZ' => 'WebMoney доллар',\n//    'WME' => 'WebMoney евро',\n//    'WMU' => 'WebMoney гривна',\n//    'WMB' => 'WebMoney белорусский рубль',\n  ),\n\n  'pay_methods' => array(\n    PAYMENT_METHOD_EMONEY => 'Электронный кошелёк',\n    PAYMENT_METHOD_EMONEY_YANDEX => 'Яндекс.Деньги',\n    PAYMENT_METHOD_EMONEY_WEBMONEY_WMR => 'WebMoney WMR',\n    PAYMENT_METHOD_EMONEY_WEBMONEY_WMZ => 'WebMoney WMZ',\n    PAYMENT_METHOD_EMONEY_WEBMONEY_WMU => 'WebMoney WMU',\n    PAYMENT_METHOD_EMONEY_WEBMONEY_WME => 'WebMoney WME',\n    PAYMENT_METHOD_EMONEY_WEBMONEY_WMB => 'WebMoney WMB',\n    PAYMENT_METHOD_EMONEY_QIWI => 'QIWI Кошелек',\n    PAYMENT_METHOD_EMONEY_ELECSNET => 'Кошелек Элекснет',\n    PAYMENT_METHOD_EMONEY_MAILRU => 'Деньги@Mail.Ru',\n    PAYMENT_METHOD_EMONEY_EASYPAY => 'EasyPay',\n    PAYMENT_METHOD_EMONEY_RUR_W1R => 'RUR Единый Кошелек',\n    PAYMENT_METHOD_EMONEY_TELEMONEY => 'TeleMoney',\n\n    PAYMENT_METHOD_BANK_CARD => 'Платежная карта (VISA, MasterCard итд)',\n    PAYMENT_METHOD_BANK_CARD_STANDARD => 'Банковская карта',\n    PAYMENT_METHOD_BANK_CARD_LIQPAY => 'LiqPay',\n    PAYMENT_METHOD_BANK_CARD_EASYPAY => 'EasyPay',\n    PAYMENT_METHOD_BANK_CARD_AMERICAN_EXPRESS => 'American Express',\n    PAYMENT_METHOD_BANK_CARD_JCB => 'JCB',\n    PAYMENT_METHOD_BANK_CARD_UNIONPAY => 'UnionPay',\n\n    PAYMENT_METHOD_BANK_INTERNET => 'Через интернет-банк',\n    PAYMENT_METHOD_BANK_INTERNET_ALFA_BANK => 'Альфа-Клик',\n    PAYMENT_METHOD_BANK_INTERNET_RUSSKIY_STANDART => 'Банк Русский Стандарт',\n    PAYMENT_METHOD_BANK_INTERNET_PROSMVYAZBANK => 'Промсвязьбанк',\n    PAYMENT_METHOD_BANK_INTERNET_VTB24 => 'ВТБ24',\n    PAYMENT_METHOD_BANK_INTERNET_OCEAN_BANK => 'Океан Банк',\n    PAYMENT_METHOD_BANK_INTERNET_HANDY_BANK => 'HandyBank',\n    PAYMENT_METHOD_BANK_INTERNET_007 => 'Банк Богородский',\n    PAYMENT_METHOD_BANK_INTERNET_008 => 'Банк Образование',\n    PAYMENT_METHOD_BANK_INTERNET_009 => 'ФлексБанк',\n    PAYMENT_METHOD_BANK_INTERNET_010 => 'ФьючерБанк',\n    PAYMENT_METHOD_BANK_INTERNET_011 => 'КранБанк',\n    PAYMENT_METHOD_BANK_INTERNET_012 => 'Костромаселькомбанк',\n    PAYMENT_METHOD_BANK_INTERNET_013 => 'Липецкий областной банк',\n    PAYMENT_METHOD_BANK_INTERNET_014 => 'Независимый строительный банк',\n    PAYMENT_METHOD_BANK_INTERNET_015 => 'Русский Трастовый Банк',\n    PAYMENT_METHOD_BANK_INTERNET_016 => 'ВестИнтерБанк',\n    PAYMENT_METHOD_BANK_INTERNET_017 => 'Межтопэнергобанк',\n    PAYMENT_METHOD_BANK_INTERNET_018 => 'Московский Индустриальный Банк',\n    PAYMENT_METHOD_BANK_INTERNET_019 => 'Банк Интеза',\n    PAYMENT_METHOD_BANK_INTERNET_020 => 'Банк Город',\n    PAYMENT_METHOD_BANK_INTERNET_021 => 'Банк АВБ',\n    PAYMENT_METHOD_BANK_INTERNET_BANK24 => 'Банк24 Национальный кредит',\n    PAYMENT_METHOD_BANK_INTERNET_PRIVAT24 => \"Приват24\",\n    PAYMENT_METHOD_BANK_INTERNET_SBERBANK => \"Сбербанк Онлайн\",\n\n    PAYMENT_METHOD_BANK_TRANSFER => 'Банковский перевод',\n\n    PAYMENT_METHOD_MOBILE => 'С мобильного телефона',\n    PAYMENT_METHOD_MOBILE_SMS => 'SMS',\n//    PAYMENT_METHOD_MOBILE_XSOLLA => 'Со счёта мобильного',\n    PAYMENT_METHOD_MOBILE_PAYPAL_ZONG => 'Со счёта или SMS',\n    PAYMENT_METHOD_MOBILE_MEGAPHONE => 'Мегафон',\n    PAYMENT_METHOD_MOBILE_MTS => 'МТС',\n    PAYMENT_METHOD_MOBILE_KYIVSTAR => 'Киевстар',\n    PAYMENT_METHOD_MOBILE_BEELINE => 'Билайн',\n\n    PAYMENT_METHOD_TERMINAL => 'Терминал оплаты',\n    PAYMENT_METHOD_TERMINAL_QIWI => 'QIWI Кошелек',\n    PAYMENT_METHOD_TERMINAL_ELECSNET => 'Элекснет',\n    PAYMENT_METHOD_TERMINAL_ELEMENT => 'Мобил Элемент',\n    PAYMENT_METHOD_TERMINAL_KASSIRANET => 'Кассира.нет',\n    PAYMENT_METHOD_TERMINAL_IBOX => 'Ibox',\n    PAYMENT_METHOD_TERMINAL_UKRAINE => 'Терминалы Украины',\n    PAYMENT_METHOD_TERMINAL_RUSSIA => 'Терминалы России',\n    PAYMENT_METHOD_TERMINAL_EASYPAY => 'EasyPay',\n\n    PAYMENT_METHOD_OTHER => 'Другие способы',\n    PAYMENT_METHOD_OTHER_EVROSET => 'Евросеть',\n    PAYMENT_METHOD_OTHER_SVYAZNOY => 'Связной',\n    PAYMENT_METHOD_OTHER_ROBOKASSA_MOBILE => 'Мобильная ROBOKASSA',\n\n    PAYMENT_METHOD_GENERIC => 'Выше перечислены далеко не все возможнные способы оплаты. Если вы не нашли подходящего для себя способа - воспользуйтесь услугами агрегаторов',\n//    PAYMENT_METHOD_GENERIC_XSOLLA => 'xSolla',\n//    PAYMENT_METHOD_GENERIC_ROBOKASSA => 'RoboKassa',\n  ),\n\n  'pay_currency_exchange_title' => 'Внутренние курсы валют',\n  'pay_currency_exchange_rate' => 'Курс',\n  'pay_currency_exchange_direct' => 'Прямой',\n  'pay_currency_exchange_reverse' => 'Обратный',\n  'pay_currency_exchange_mm' => '<span class=\"metamatter\">ММ</span> за 1 у.е.',\n  'pay_currency_exchange_note' => 'Внутренний курс используется для пересчета из основной валюты сервера в валюту платёжной системы. Курс не включает комиссию посредников и/или платёжных систем',\n\n  'pay_msg_mm_purchase_complete'   => 'Вы успешно заплатили за %d единиц Метаматерии через сервис %s. Вам начислено %s единиц <span class=\"metamatter\">Метаматерии</span>',\n  'pay_msg_mm_purchase_incomplete' => 'Ваш платёж за %d единиц <span class=\"metamatter\">Метаматерии</span> через сервис %s не завершен. Если вы считаете, что произошла ошибка - свяжитесь с Администрацией сервера',\n  'pay_msg_mm_purchase_test'       => 'На самом деле - шутка. Платеж был тестовый, поэтому ты ничего не получил ха-ха-ха! Если считаешь, что это ошибка - обратись к Администрации сервера',\n\n  'pay_msg_request_user_found' => 'Пользователь найден',\n  'pay_msg_request_payment_complete' => 'Платёж завершен',\n  'pay_msg_request_payment_cancel_complete' => 'Платёж успешно отменён',\n\n  'pay_msg_request_unsupported' => 'Данный тип запроса не поддерживается',\n  'pay_msg_request_signature_invalid' => 'Неправильная подпись запроса',\n  'pay_msg_request_user_invalid' => 'Неправильный идентификатор пользователя',\n  'pay_msg_request_server_wrong' => 'Неправильный сервер',\n  'pay_msg_request_payment_amount_invalid' => 'Неправильная сумма платежа',\n  'pay_msg_request_payment_id_invalid' => 'Неправильный идентификатор платежа',\n  'pay_msg_request_payment_date_invalid' => 'Неправильная дата платежа',\n  'pay_msg_request_internal_error' => 'Внутренняя ошибка сервера. Попробуйте повторить платёж позже',\n  'pay_msg_request_paylink_unsupported' => 'Данный тип платёжной ссылке не поддерживается. Возможно используется устаревшая версия СН, не совместимая с данным платёжным модулем',\n  'pay_msg_request_payment_write_error' => 'Ошибка записи платежа',\n  'pay_msg_request_payment_cancelled_already' => 'Платёж уже отменен',\n  'pay_msg_request_payment_cancel_not_complete' => 'Платёж еще не завершен и не может быть отменен',\n  'pay_msg_request_payment_cancelled' => '!!! Платёж отозван платёжной системой!!!',\n  'pay_msg_request_payment_not_found' => 'Платёж не найден',\n\n  'pay_msg_module_disabled' => 'Платёжный модуль отключен',\n\n  'pay_msg_mm_request_money_and_mm_mismatched' => 'Не совпадает сумма оплаты и количество покупаемой ММ',\n\n  'pay_msg_mm_request_amount_invalid' => 'Неправильное количество <span class=\"metamatter\">Метаматерии</span>',\n  'pay_msg_mm_request_config_invalid' => 'Ошибка в конфигурации модуля платежа. Свяжитесь с Администрацией сервера',\n  'pay_msg_mm_request_mm_adjust_error' => 'Ошибка начисления <span class=\"metamatter\">Метаматерии</span>',\n\n  'pay_msg_request_error_db_payment_create' => 'Ошибка создания платежа в БД',\n  'pay_msg_request_error_test_payment' => 'Статус платежа в БД не совпадает с информацией в запросе',\n  'pay_error_internal_no_external_currency_set' => 'ВНУТРЕННЯЯ ОШИБКА или ОШИБКА КОНФИГУРАЦИИ ПЛАТЁЖНОГО МОДУЛЯ! Не установлена валюта платёжной системы! Пожалуйста, сообщите Администрации сервера!',\n\n));\n"
  },
  {
    "path": "language/de/quest.mo.php",
    "content": "<?php\n\n/*\n#############################################################################\n#  Filename: quest.mo\n#  Project: SuperNova.WS\n#  Website: http://www.supernova.ws\n#  Description: Massive Multiplayer Online Browser Space Strategy Game\n#\n#  Copyright © 2009-2018 Gorlum for Project \"SuperNova.WS\"\n#############################################################################\n*/\n\n/**\n*\n* @package language\n* @system [German]\n* @version 46d0\n*\n*/\n\n/**\n* DO NOT CHANGE\n*/\n\nif (!defined('INSIDE')) die();\n\n\n$a_lang_array = (array(\n  'qst_quest' => 'Quest',\n  'qst_quest_of' => 'Quest',\n  'qst_name' => 'Name',\n  'qst_description' => 'Beschreibung',\n  'qst_adm_conditions' => 'Anforderungen',\n  'qst_conditions' => 'Zu bauende/erforschende Einheiten',\n  'qst_rewards' => 'Belohnung für Questabschluss',\n  'qst_total' => 'Quests',\n  'qst_status' => 'Status',\n  'qst_status_list' => array(\n    QUEST_STATUS_ALL => '-- Alle Quests --',\n    QUEST_STATUS_NOT_STARTED => 'Nicht&nbsp;begonnen',\n    QUEST_STATUS_STARTED => 'Begonnen',\n    QUEST_STATUS_EXCEPT_COMPLETE => 'Alle,&nbsp;außer&nbsp;abgeschlossene',\n    QUEST_STATUS_COMPLETE => 'Abgeschlossen',\n  ),\n\n  'qst_filter_by_status' => 'Quests mit Status anzeigen',\n\n  'qst_add' => 'Quest hinzufügen',\n  'qst_edit' => 'Quest bearbeiten',\n  'qst_copy' => 'Quest kopieren',\n  'qst_mode_add' => 'Hinzufügen',\n  'qst_mode_edit' => 'Bearbeiten',\n  'qst_mode_copy' => 'Kopieren',\n  'qst_adm_err_unit_id' => 'Ungültige Einheit',\n  'qst_adm_err_unit_amount' => 'Ungültige Einheitenanzahl',\n  'qst_adm_err_reward_amount' => 'Ungültige Belohnungsmenge',\n  'qst_adm_err_reward_type' => 'Ungültiger Belohnungstyp',\n  'qst_adm_err_reward_empty' => 'Leere Questbelohnung',\n));"
  },
  {
    "path": "language/de/search.mo.php",
    "content": "<?php\n\n/*\n#############################################################################\n#  Filename: search.mo\n#  Project: SuperNova.WS\n#  Website: http://www.supernova.ws\n#  Description: Massive Multiplayer Online Browser Space Strategy Game\n#\n#  Copyright © 2009-2018 Gorlum for Project \"SuperNova.WS\"\n#############################################################################\n*/\n\n/**\n*\n* @package language\n* @system [German]\n* @version #46d0#\n*\n*/\n\n/**\n* DO NOT CHANGE\n*/\n\nif (!defined('INSIDE')) die();\n\n$a_lang_array = (array(\n  'srch_title' => 'Serversuche',\n  'srch_search_do' => 'Suchen',\n  'srch_result_none' => 'Keine Ergebnisse für diese Suche gefunden',\n  'srch_player_name' => 'Spielername',\n  'srch_ally_name' => 'Allianzname',\n  'srch_ally_members' => 'Mitglieder',\n  'srch_planet_name' => 'Planetenname',\n  'srch_rank' => 'Rang',\n  'srch_action_pm' => 'Nachricht senden',\n  'srch_action_buddy' => 'Zu Freunden hinzufügen',\n\n  'srch_aka' => 'AKA',\n\n  'srch_page_hint' => '<li>Es werden nur die ersten 30 Einträge angezeigt</li>\n  <li>Wenn ein Spieler seinen Namen geändert hat und einer seiner alten Namen den Suchkriterien entspricht, wird dieser ebenfalls in einer separaten Zeile angezeigt.\n  In diesem Fall wird der aktuelle Spielername angezeigt und der alte Name in <span class=\"warning\">farbiger</span> Schrift in Klammern</li>',\n));"
  },
  {
    "path": "language/de/stat.mo.php",
    "content": "<?php\n\n/*\n#############################################################################\n#  Filename: stat.mo\n#  Project: SuperNova.WS\n#  Website: http://www.supernova.ws\n#  Description: Massive Multiplayer Online Browser Space Strategy Game\n#\n#  Copyright © 2009-2018 Gorlum for Project \"SuperNova.WS\"\n#############################################################################\n*/\n\n/**\n*\n* @package language\n* @system [German]\n* @version 46d0\n*\n*/\n\n/**\n* DO NOT CHANGE\n*/\n\nif (!defined('INSIDE')) die();\n\n$a_lang_array = (array(\n  'stat_header' => 'Statistik',\n  'stat_refresh_last' => 'Letzte Aktualisierung:',\n  'stat_refresh_next' => 'Nächste Aktualisierung:',\n\n  'stat_rank' => 'Rang',\n  'stat_rank_diff' => 'Änd.',\n  'stat_points' => 'Punkte',\n  'stat_per_member' => 'Pro Mitglied',\n  'stat_members' => 'Mitglieder',\n  'stat_message_write' => 'Nachricht schreiben',\n\n  'stat_show' => 'Statistik anzeigen',\n  'stat_type' => array(\n    STAT_TOTAL => 'Gesamt',\n    STAT_FLEET => 'Flotten',\n    STAT_TECH => 'Forschung',\n    STAT_BUILDING => 'Gebäude',\n    STAT_DEFENSE => 'Verteidigung',\n    STAT_RESOURCE => 'Ressourcen',\n    STAT_RAID_TOTAL => 'Geführte Kämpfe',\n    STAT_RAID_WON => 'Gewonnene Kämpfe',\n    STAT_RAID_LOST => 'Verlorene Kämpfe',\n    STAT_LVL_BUILDING => 'Gebäudelevel',\n    STAT_LVL_TECH => 'Forschungslevel',\n    STAT_LVL_RAID => 'Raiderlevel',\n  ),\n\n  'stat_by' => 'für',\n  'stat_player' => 'Spieler',\n  'stat_allys' => 'Allianzen',\n  'stat_range' => 'Plätze',\n\n  'stat_details' => 'Spielerinformation',\n));"
  },
  {
    "path": "language/de/system.mo.php",
    "content": "<?php\n\n/*\n#############################################################################\n#  Filename: system.mo.php\n#  Project: SuperNova.WS\n#  Website: http://www.supernova.ws\n#  Description: Massen-Mehrspieler-Online-Browser-Weltraumstrategiespiel\n#\n#  Copyright © 2009 Gorlum für Projekt \"SuperNova.WS\"\n#  Copyright © 2009 MSW\n#############################################################################\n*/\n\n/**\n*\n* @package language\n* @system [Deutsch]\n* @version #46d0#\n*\n*/\n\n/**\n* NICHT ÄNDERN\n*/\n\nuse Fleet\\Constants;\n\nif (!defined('INSIDE'))\n{\n  exit;\n}\n\n// Systemweite Lokalisierung\n\nglobal $config;\n\n$a_lang_array = [\n  'sys_birthday' => 'Geburtstag',\n  'sys_birthday_message' => '%1$s! Die SuperNova-Administration gratuliert dir herzlich zu deinem Geburtstag am %2$s und überreicht dir als Geschenk %3$d %4$s! Wir wünschen dir viel Erfolg im Spiel und hohe Plätze in der Statistik! Diese Gratulation mag verspätet sein, aber besser spät als nie.',\n\n  'adm_err_denied' => 'Zugriff verweigert. Sie haben nicht die erforderlichen Berechtigungen, um diese Seite der Serververwaltung zu nutzen',\n\n  'sys_empire'          => 'Imperium',\n  'VacationMode'\t\t\t=> \"Ihre Produktion ist eingestellt, da Sie sich im Urlaubsmodus befinden\",\n  'sys_moon_destruction_report' => \"Mondzerstörungsbericht\",\n  'sys_moon_destroyed' => \"Ihre Todessterne haben eine starke Gravitationswelle erzeugt, die den Mond zerstört hat! \",\n  'sys_rips_destroyed' => \"Ihre Todessterne haben eine starke Gravitationswelle erzeugt, aber ihre Stärke reichte nicht aus, um einen Mond dieser Größe zu zerstören. Die Gravitationswelle reflektierte von der Mondoberfläche und zerstörte Ihre Flotte.\",\n  'sys_rips_come_back' => \"Ihre Todessterne haben nicht genug Energie, um diesen Mond zu beschädigen. Ihre Flotte kehrt zurück, ohne den Mond zu zerstören.\",\n  'sys_chance_moon_destroy' => \"Chance der Mondzerstörung: \",\n  'sys_chance_rips_destroy' => \"Chance der Risszerstörung: \",\n\n  'sys_impersonate' => 'Einloggen als',\n  'sys_impersonate_done' => 'Ausloggen',\n  'sys_impersonated_as' => 'WARNUNG! Sie sind aktuell als Spieler %1$s eingeloggt. Vergessen Sie nicht, dass Sie eigentlich %2$s sind! Sie können sich über das entsprechende Menüpunkt ausloggen.',\n\n  'menu_admin_mining'          => 'Spielerabbau',\n  'menu_admin_units'          => 'Einheiten',\n  'menu_admin_ube_balance'          => 'UBE-Bilanz',\n\n  'sys_day' => \"Tage\",\n  'sys_hrs' => \"Stunden\",\n  'sys_min' => \"Minuten\",\n  'sys_sec' => \"Sekunden\",\n  'sys_day_short' => \"T\",\n  'sys_hrs_short' => \"Std\",\n  'sys_min_short' => \"Min\",\n  'sys_sec_short' => \"Sek\",\n\n  'sys_ask_admin' => 'Fragen und Vorschläge senden an',\n\n  'sys_wait'      => 'Anfrage wird bearbeitet. Bitte warten Sie.',\n\n  'sys_fleets'       => 'Flotten',\n  'sys_expeditions'  => 'Expeditionen',\n  'sys_fleet'        => 'Flotte',\n  'sys_expedition'   => 'Expedition',\n  'sys_event_next'   => 'Nächstes Ereignis:',\n  'sys_event_arrive' => 'wird ankommen',\n  'sys_event_stay'   => 'wird Aufgabe beenden',\n  'sys_event_return' => 'wird zurückkehren',\n\n  'sys_total'           => \"GESAMT\",\n  'sys_need'\t\t\t\t=> 'Benötigt',\n  'sys_register_date'   => 'Registrierungsdatum',\n\n  'sys_attacker' \t\t=> \"Angreifer\",\n  'sys_defender' \t\t=> \"Verteidiger\",\n\n  'COE_combatSimulator' => \"Kampfsimulator\",\n  'COE_simulate'        => \"Simulator starten\",\n  'COE_fleet'           => \"Flotte\",\n  'COE_defense'         => \"Verteidigung\",\n  'sys_coe_combat_start'=> \"Gegnerische Flotten sind aufeinandergetroffen\",\n  'sys_coe_combat_end'  => \"Kampfergebnis\",\n  'sys_coe_round'       => \"Runde\",\n\n  'sys_coe_attacker_turn'=> 'Der Angreifer feuert mit einer Gesamtstärke von %1$s. Die Schilde des Verteidigers absorbieren %2$s Schüsse<br />',\n  'sys_coe_defender_turn'=> 'Der Verteidiger feuert mit einer Gesamtstärke von %1$s. Die Schilde des Angreifers absorbieren %2$s Schüsse<br /><br /><br />',\n  'sys_coe_outcome_win'  => 'Der Verteidiger hat die Schlacht gewonnen!<br />',\n  'sys_coe_outcome_loss' => 'Der Angreifer hat die Schlacht gewonnen!<br />',\n  'sys_coe_outcome_loot' => 'Er erhält %1$s Metall, %2$s Kristall, %3$s Deuterium<br />',\n  'sys_coe_outcome_draw' => 'Die Schlacht endete unentschieden.<br />',\n  'sys_coe_attacker_lost'=> 'Der Angreifer hat %1$s Einheiten verloren.<br />',\n  'sys_coe_defender_lost'=> 'Der Verteidiger hat %1$s Einheiten verloren.<br />',\n  'sys_coe_debris_left'  => 'An diesen Raumkoordinaten befinden sich nun %1$s Metall und %2$s Kristall.<br /><br />',\n  'sys_coe_moon_chance'  => 'Die Chance auf eine Mondentstehung beträgt %1$s%%<br />',\n  'sys_coe_rw_time'      => 'Seitengenerierungszeit %1$s Sekunden<br />',\n\n  'sys_resources'       => \"Ressourcen\",\n  'sys_ships'           => \"Schiffe\",\n  'sys_metal'          => \"Metall\",\n  'sys_metal_sh'       => \"M\",\n  'sys_crystal'        => \"Kristall\",\n  'sys_crystal_sh'     => \"K\",\n  'sys_deuterium'      => \"Deuterium\",\n  'sys_deuterium_sh'   => \"D\",\n  'sys_energy'         => \"Energie\",\n  'sys_energy_sh'      => \"E\",\n  'sys_dark_matter'    => \"Dunkle Materie\",\n  'sys_dark_matter_sh' => \"DM\",\n  'sys_metamatter'     => \"Metamaterie\",\n  'sys_metamatter_sh'  => \"MM\",\n\n  'sys_reset'           => \"Zurücksetzen\",\n  'sys_send'            => \"Senden\",\n  'sys_characters'      => \"Zeichen\",\n  'sys_back'            => \"Zurück\",\n  'sys_return'          => \"Zurückkehren\",\n  'sys_delete'          => \"Löschen\",\n  'sys_writeMessage'    => \"Nachricht schreiben\",\n  'sys_hint'            => \"Hinweis\",\n\n  'sys_alliance'        => \"Allianz\",\n  'sys_player'          => \"Spieler\",\n  'sys_coordinates'     => \"Koordinaten\",\n\n  'sys_online'          => \"Online\",\n  'sys_offline'         => \"Offline\",\n  'sys_status'          => \"Status\",\n\n  'sys_universe'        => \"Universum\",\n  'sys_goto'            => \"Gehe zu\",\n\n  'sys_time'            => \"Zeit\",\n  'sys_temperature'\t\t=> 'Temperatur',\n\n  'sys_no_task'         => \"keine Aufgabe\",\n\n  'sys_affilates'       => \"Eingeladene Spieler\",\n\n  'sys_fleet_arrived'   => \"Flotte angekommen\",\n\n  'sys_planet_type' => [\n    PT_PLANET => 'Planet',\n    PT_DEBRIS => 'Trümmerfeld',\n    PT_MOON   => 'Mond',\n  ],\n\n  'sys_planet_type_sh' => [\n    PT_PLANET => '(P)',\n    PT_DEBRIS => '(T)',\n    PT_MOON   => '(M)',\n  ],\n\n  'sys_planet_expedition' => 'unerforschter Raum',\n\n  'sys_capacity' \t\t\t=> 'Ladekapazität',\n  'sys_cargo_bays' \t\t=> 'Laderäume',\n\n  'sys_supernova' \t\t=> 'SuperNova',\n  'sys_server' \t\t\t=> 'Server',\n\n  'sys_unbanned'\t\t\t=> 'Entsperrt',\n\n  'sys_date_time'\t\t\t=> 'Datum und Zeit',\n  'sys_from_person'\t   => 'Von wem',\n  'sys_from_speed'\t   => 'von',\n\n  'sys_from'\t\t  => 'von',\n  'tp_on'            => 'auf',\n\n// Ressourcen-Seite\n  'res_planet_production' => 'Ressourcenproduktion auf dem Planeten',\n  'res_basic_starting_resources' => 'Startressourcen auf dem Planeten',\n  'res_basic_income' => 'Natürliche Produktion',\n  'res_basic_storage_size' => 'Lagergröße',\n  'res_total' => 'GESAMT',\n  'res_calculate' => 'Berechnen',\n  'res_hourly' => 'Pro Stunde',\n  'res_daily' => 'Pro Tag',\n  'res_weekly' => 'Pro Woche',\n  'res_monthly' => 'Pro Monat',\n  'res_storage_fill' => 'Lagerfüllstand',\n  'res_hint' => '<ul><li>Ressourcenproduktion <100% bedeutet Energiemangel. Bauen Sie zusätzliche Kraftwerke oder reduzieren Sie die Ressourcenproduktion<li>Wenn Ihre Produktion 0% beträgt, sind Sie wahrscheinlich aus dem Urlaubsmodus zurückgekehrt und müssen alle Fabriken einschalten<li>Um den Abbau für alle Fabriken gleichzeitig einzustellen, verwenden Sie die Dropdown-Liste in der Tabellenüberschrift. Besonders nützlich nach dem Urlaubsmodus</ul>',\n\n// Bau-Seite\n  'bld_destroy' => 'Zerstören',\n  'bld_create'  => 'Bauen',\n  'bld_research' => 'Erforschen',\n  'bld_hire' => 'Einstellen',\n\n// Imperium-Seite\n  'imp_imperator' => \"Imperator\",\n  'imp_overview' => \"Imperiumsübersicht\",\n  'imp_fleets' => \"Flotten im Flug\",\n  'imp_production' => \"Produktion\",\n  'imp_name' => \"Name\",\n  'imp_research' => \"Forschung\",\n  'imp_exploration' => \"Expeditionen\",\n  'imp_imperator_none' => \"Kein solcher Imperator im Universum!\",\n  'sys_fields' => \"Sektoren\",\n\n// Cookies\n  'err_cookie' => \"Fehler! Der Benutzer kann nicht anhand der Cookie-Informationen autorisiert werden.<br />Löschen Sie die Browser-Cookies und versuchen Sie erneut, sich <a href='login\" . DOT_PHP_EX . \"'>anzumelden</a> oder <a href='reg\" . DOT_PHP_EX . \"'>zu registrieren</a>.\",\n\n// Unterstützte Sprachen\n  'ru'              \t  => 'Russisch',\n  'en'              \t  => 'Englisch',\n\n  'sys_vacation'        => 'Sie sind im Urlaubsmodus bis',\n  'sys_vacation_leave'  => 'Ich habe mich ausgeruht - Urlaubsmodus beenden!',\n  'sys_vacation_in'     => 'Im Urlaub',\n  'sys_level'           => 'Level',\n  'sys_level_short'     => 'Lvl',\n  'sys_level_max'       => 'Maximales Level',\n\n  'sys_yes'             => 'Ja',\n  'sys_no'              => 'Nein',\n\n  'sys_on'              => 'Ein',\n  'sys_off'             => 'Aus',\n\n  'sys_confirm'         => 'Bestätigen',\n  'sys_save'            => 'Speichern',\n  'sys_create'          => 'Erstellen',\n  'sys_write_message'   => 'Nachricht schreiben',\n\n// Top-Leiste\n  'top_of_year' => 'Jahr',\n  'top_online'\t\t\t=> 'Spieler',\n\n  'sys_first_round_crash_1'\t=> 'Der Kontakt zur angegriffenen Flotte wurde verloren.',\n  'sys_first_round_crash_2'\t=> 'Das bedeutet, dass sie in der ersten Kampfrunde zerstört wurde.',\n\n  'sys_ques' => [\n    QUE_STRUCTURES => 'Gebäude',\n    QUE_HANGAR     => 'Werft',\n    SUBQUE_DEFENSE => 'Verteidigung',\n    QUE_RESEARCH   => 'Forschung',\n  ],\n\n  'navbar_button_expeditions_short' => 'Expa',\n  'navbar_button_fleets' => 'Flotten',\n  'navbar_button_quests' => 'Quests',\n  'navbar_font' => 'Schriftart',\n  'navbar_font_normal' => 'Normal',\n  'sys_que_structures' => 'Gebäude',\n  'sys_que_hangar' => 'Werft',\n  'sys_que_defense' => 'Verteidigung',\n  'sys_que_research' => 'Forschung',\n  'sys_que_research_short' => 'Wissenschaft',\n\n  'eco_que' => 'Warteschlange',\n  'eco_que_empty' => 'Warteschlange ist leer',\n  'eco_que_clear' => 'Warteschlange leeren',\n  'eco_que_trim'  => 'Letztes abbrechen',\n  'eco_que_artifact'  => 'Artefakt verwenden',\n\n  'sys_cancel' => 'Abbrechen',\n\n  'sys_overview'\t\t\t=> 'Übersicht',\n  'mod_marchand'\t\t\t=> 'Händler',\n  'sys_galaxy'\t\t\t=> 'Galaxie',\n  'sys_system'\t\t\t=> 'System',\n  'sys_planet'\t\t\t=> 'Planet',\n  'sys_planet_title'\t\t\t=> 'Planetentyp',\n  'sys_planet_title_short'\t\t\t=> 'Typ',\n  'sys_moon'\t\t\t=> 'Mond',\n  'sys_error'\t\t\t=> 'Fehler',\n  'sys_done'\t\t\t\t=> 'Fertig',\n  'sys_no_vars'\t\t\t=> 'Fehler bei der Variableninitialisierung, wenden Sie sich an die Administration!',\n  'sys_attacker_lostunits'\t\t=> 'Der Angreifer verlor %s Einheiten.',\n  'sys_defender_lostunits'\t\t=> 'Der Verteidiger verlor %s Einheiten.',\n  'sys_gcdrunits' \t\t\t=> 'An diesen Raumkoordinaten befinden sich nun %s %s und %s %s.',\n  'sys_moonproba' \t\t\t=> 'Die Chance auf eine Mondentstehung beträgt: %d %% ',\n  'sys_moonbuilt' \t\t\t=> 'Dank der enormen Energie verbinden sich große Metall- und Kristallbrocken und bilden einen neuen Mond %s %s!',\n  'sys_attack_title'    \t\t=> '%s. Ein Kampf fand zwischen den folgenden Flotten statt:',\n  'sys_attack_attacker_pos'      \t=> 'Angreifer %s [%s:%s:%s]',\n  'sys_attack_techologies' \t=> 'Bewaffnung: %d %% Schilde: %d %% Rüstung: %d %% ',\n  'sys_attack_defender_pos' \t=> 'Verteidiger %s [%s:%s:%s]',\n  'sys_ship_type' \t\t\t=> 'Typ',\n  'sys_ship_count' \t\t=> 'Anzahl',\n  'sys_ship_weapon' \t\t=> 'Bewaffnung',\n  'sys_ship_shield' \t\t=> 'Schilde',\n  'sys_ship_armour' \t\t=> 'Rüstung',\n  'sys_ship_speed' \t\t=> 'Geschwindigkeit',\n  'sys_ship_consumption' \t\t=> 'Verbrauch',\n  'sys_ship_capacity' \t\t=> 'Laderaum/Tank',\n  'sys_destroyed' \t\t\t=> 'zerstört',\n  'sys_attack_attack_wave' \t=> 'Der Angreifer feuert mit einer Gesamtstärke von %s auf den Verteidiger. Die Schilde des Verteidigers absorbieren %s Schüsse.',\n  'sys_attack_defend_wave'\t\t=> 'Der Verteidiger feuert mit einer Gesamtstärke von %s auf den Angreifer. Die Schilde des Angreifers absorbieren %s Schüsse.',\n  'sys_attacker_won' \t\t=> 'Der Angreifer hat die Schlacht gewonnen!',\n  'sys_defender_won' \t\t=> 'Der Verteidiger hat die Schlacht gewonnen!',\n  'sys_both_won' \t\t\t=> 'Die Schlacht endete unentschieden!',\n  'sys_stealed_ressources' \t=> 'Er erhält %s Metall %s %s Kristall %s und %s Deuterium.',\n  'sys_rapport_build_time' \t=> 'Seitengenerierungszeit %s Sekunden',\n  'sys_mess_tower' \t\t=> 'Transport',\n  'sys_coe_lost_contact' \t\t=> 'Die Verbindung zu Ihrer Flotte wurde verloren',\n  'sys_spy_activity' => 'In der Nähe Ihrer Planeten wurde Spionageaktivität festgestellt',\n  'sys_spy_materials' \t\t=> 'Rohstoffe auf',\n  'sys_spy_fleet' \t\t\t=> 'Flotte',\n  'sys_spy_defenses' \t\t=> 'Verteidigung',\n  'sys_mess_qg' \t\t\t=> 'Flottenkommando',\n  'sys_mess_spy_report' \t\t=> 'Spionagebericht',\n  'sys_mess_spy_lostproba' \t=> 'Ungenauigkeit der vom Satelliten erhaltenen Informationen %d %% ',\n  'sys_mess_spy_detect_chance' \t=> 'Die Chance, Ihre Spionageflotte zu entdecken, beträgt %d%%',\n  'sys_mess_spy_detect_chance_no_percent' \t=> 'Chance, Ihre Spionageflotte zu entdecken',\n  'sys_mess_spy_control' \t\t=> 'Spionageabwehr',\n  'sys_mess_spy_activity' \t\t=> 'Spionageaktivität',\n  'sys_mess_spy_enemy_fleet' \t=> 'Eine fremde Flotte vom Planeten',\n  'sys_mess_spy_seen_at'\t\t=> 'wurde in der Nähe des Planeten entdeckt',\n  'sys_mess_spy_destroyed'\t\t=> 'Die Spionageflotte wurde zerstört',\n  'sys_mess_spy_destroyed_enemy'\t\t=> 'Feindliche Spionageflotte zerstört',\n  'sys_object_arrival'\t\t=> 'Auf dem Planeten angekommen',\n  'sys_stay_mess_stay' => 'Flottenverlegung',\n  'sys_stay_mess_start' \t\t=> 'Ihre Flotte ist auf dem Planeten angekommen',\n  'sys_stay_mess_back'\t\t=> 'Ihre Flotte ist zurückgekehrt ',\n  'sys_stay_mess_end'\t\t=> ' und hat geliefert:',\n  'sys_stay_mess_bend'\t\t=> ' und hat folgende Ressourcen geliefert:',\n  'sys_adress_planet' \t\t=> '[%s:%s:%s]',\n  'sys_stay_mess_goods' \t\t=> '%s : %s, %s : %s, %s : %s',\n  'sys_colo_mess_from' \t\t=> 'Kolonisierung',\n  'sys_colo_mess_report' \t\t=> 'Kolonisierungsbericht',\n  'sys_colo_defaultname' \t\t=> 'Kolonie',\n  'sys_colo_arrival' \t\t=> 'Die Flotte erreicht die Koordinaten ',\n  'sys_colo_maxcolo' \t\t=> ', aber der Planet kann nicht kolonisiert werden, die maximale Anzahl an Kolonien für Ihr Kolonisierungslevel wurde erreicht',\n  'sys_colo_allisok' \t\t=> ', und die Kolonisten beginnen, einen neuen Planeten zu besiedeln.',\n  'sys_colo_badpos'  \t\t\t=> ', und die Kolonisten fanden die Umgebung für Ihr Imperium wenig vorteilhaft. Die Kolonisierungsmission kehrt zum Startplaneten zurück.',\n  'sys_colo_notfree' \t\t\t=> ', und die Kolonisten fanden keinen Planeten an diesen Koordinaten. Sie müssen enttäuscht den Rückweg antreten.',\n  'sys_colo_no_colonizer'     => 'In der Flotte befindet sich kein Kolonisierer',\n  'sys_colo_planet'  \t\t=> ' Planet kolonisiert!',\n  'sys_expe_report' \t\t=> 'Expeditionsbericht',\n  'sys_recy_report' \t\t=> 'Systeminformation',\n  'sys_expe_blackholl_1' \t\t=> 'Ihre Flotte ist in ein schwarzes Loch geraten und teilweise verloren gegangen!',\n  'sys_expe_blackholl_2' \t\t=> 'Ihre Flotte ist in ein schwarzes Loch geraten und vollständig verloren gegangen!',\n  'sys_expe_nothing_1' \t\t=> 'Ihre Forscher waren Zeugen einer SuperNova! Und Ihre Speicher konnten einen Teil der freigesetzten Energie aufnehmen.',\n  'sys_expe_nothing_2' \t\t=> 'Ihre Forscher haben nichts entdeckt!',\n  'sys_expe_found_goods' \t\t=> 'Ihre Forscher haben einen rohstoffreichen Planeten gefunden!<br>Sie erhalten %s %s, %s %s und %s %s',\n  'sys_expe_found_ships' \t\t=> 'Ihre Forscher haben eine makellos neue Flotte gefunden!<br>Sie erhalten: ',\n  'sys_expe_back_home' \t\t=> 'Ihre Flotte kehrt zurück.',\n  'sys_mess_transport' \t\t=> 'Transport',\n  'sys_tran_mess_user'  \t\t=> 'Eine Flotte vom Planeten %s %s ist auf %s %s angekommen und hat %s %s, %s %s und %s %s geliefert.',\n  'sys_relocate_mess_user'  \t\t=> 'Außerdem wurden folgende Kampfeinheiten auf den Planeten verlegt:<br />',\n  'sys_mess_fleetback' \t\t=> 'Rückkehr',\n  'sys_tran_mess_back' \t\t=> 'Eine Ihrer Flotten ist zum Planeten %s %s zurückgekehrt.',\n  'sys_recy_gotten' \t\t=> 'Eine Ihrer Flotten hat %s %s und %s %s abgebaut. Kehrt zum Planeten zurück.',\n  'sys_notenough_money' \t\t=> 'Ihnen fehlen die Ressourcen, um zu bauen: %s. Sie haben aktuell: %s %s , %s %s und %s %s. Für den Bau benötigt: %s %s , %s %s und %s %s.',\n  'sys_nomore_level'\t\t=> 'Sie können dies nicht weiter verbessern. Es hat das maximale Level (%s) erreicht.',\n  'sys_buildlist' \t\t\t=> 'Bauauflistung',\n  'sys_buildlist_fail' \t\t=> 'keine Gebäude',\n  'sys_gain' \t\t\t=> 'Beute: ',\n  'sys_debris' \t\t\t=> 'Trümmer: ',\n  'sys_noaccess' \t\t\t=> 'Zugriff verweigert',\n  'sys_noalloaw' \t\t\t=> 'Der Zugang zu diesem Bereich ist Ihnen verwehrt!',\n  'sys_governor'        => 'Gouverneur',\n\n  'flt_error_duration_wrong' => 'Flotte kann nicht gesendet werden - keine verfügbaren Intervalle für die Verzögerung. Studieren Sie weitere Astrokartographie-Level',\n  'flt_stay_duration' => 'Zeit',\n\n  'flt_mission_expedition' => [\n    'msg_sender' => 'Expeditionsbericht',\n    'msg_title' => 'Expeditionsbericht',\n\n    'found_dark_matter' => '%1$d Einheiten DM erhalten',\n    'found_resources' => \"Ressourcen gefunden:\\r\\n\",\n    'found_fleet' => \"Schiffe gefunden:\\r\\n\",\n    'lost_fleet' => \"Folgende Schiffe verloren:\\r\\n\",\n\n    'outcomes' => [\n      Constants::OUTCOME_NONE => [\n        'messages' => [\n          'Ihre Forscher haben nichts entdeckt',\n        ],\n      ],\n\n      Constants::EXPEDITION_OUTCOME_LOST_FLEET => [\n        'messages' => [\n          'Die Flotte ist in ein schwarzes Loch geraten und teilweise verloren gegangen',\n        ],\n      ],\n\n      Constants::EXPEDITION_OUTCOME_LOST_FLEET_ALL => [\n        'messages' => [\n          'Wenn Sie das nur sehen könnten! Es ist so schön... Es ruft... (Verbindung zur Flotte verloren)',\n          'Flottenbericht %1$s. Wir haben die Sektorerforschung abgeschlossen. Die Crew ist unzufrieden. Hey, was machst du auf der Brücke?! (Verbindung zur Flotte verloren)',\n          'Flottenbericht %1$s. Alles ruhig (Störungen) (Verbindung zur Flotte verloren)',\n          'AAAAAH! WAS IST DAS?! WO KOMMT ES HER (Verbindung zur Flotte verloren)',\n          'Unbekanntes Objekt entdeckt. Es reagiert nicht auf Standardprotokollanfragen. Wir senden eine Sonde zur Untersuchung aus (Verbindung zur Flotte verloren)',\n        ],\n      ],\n\n      Constants::EXPEDITION_OUTCOME_FOUND_FLEET => [\n        'no_result' => 'Leider reichte die kombinierte Leistung aller Flottencomputer nicht einmal aus, um das kleinste Schiff zu kontrollieren. Versuchen Sie, mehr Schiffe und/oder größere Schiffe zu senden',\n        'messages' => [\n          0 => [\n            'Sie haben eine brandneue Flotte gefunden',\n          ],\n          1 => [\n            'Sie haben eine Flotte gefunden',\n          ],\n          2 => [\n            'Sie haben eine gebrauchte Flotte gefunden',\n          ],\n        ],\n      ],\n\n      Constants::EXPEDITION_OUTCOME_FOUND_RESOURCES => [\n        'no_result' => 'Die Laderäume Ihrer Flotte waren nicht in der Lage, auch nur einen Ressourcencontainer aufzunehmen. Versuchen Sie, eine Flotte mit mehr Transportern zu senden',\n        'messages' => [\n          0 => [\n            'Sie haben einen Piratenschatz mit Ressourcen gefunden. Wie viele Schiffe wurden zerstört, um so viel Beute zu sammeln?',\n          ],\n          1 => [\n            'Sie haben eine verlassene Asteroidenbasis gefunden. Interessant, wohin ihre Bewohner verschwunden sind? Bei der Untersuchung der Ruinen fanden Sie einige intakte Lagerhäuser mit Ressourcen',\n          ],\n          2 => [\n            'Sie sind auf einen zerstörten Transportkonvoi gestoßen. Bei der Durchsuchung der Laderäume der zerstörten Schiffe fanden Sie einige Ressourcen',\n          ],\n        ],\n      ],\n\n      Constants::EXPEDITION_OUTCOME_FOUND_DM => [\n        'no_result' => 'Leider reichten alle Flottenspeicher nicht aus, um auch nur eine einzige DM zu sammeln. Versuchen Sie, eine größere Flotte zu senden',\n        'messages' => 'Ihre Flotte war Zeuge der Geburt einer SuperNova',\n      ],\n    ],\n  ],\n\n  // Nachrichten-Seite & ein bisschen Imperator-Seite\n  'news_fresh'      => 'Aktuelle Nachrichten',\n  'news_all'        => 'Alle Nachrichten',\n  'news_title'      => 'Nachrichten',\n  'news_none'       => 'Keine Nachrichten',\n  'news_new'        => 'NEU',\n  'news_future'     => 'ANKÜNDIGUNG',\n  'news_more'       => 'Mehr...',\n  'news_hint'       => 'Um die Liste der letzten Nachrichten zu entfernen - lesen Sie sie alle, indem Sie auf die Überschrift \"[ Nachrichten ]\" klicken',\n\n  'news_date'       => 'Datum',\n  'news_announce'   => 'Inhalt',\n  'news_detail_url' => 'Link zu Details',\n  'news_mass_mail'  => 'Nachricht an alle Spieler senden',\n\n  'news_total'      => 'Gesamte Nachrichten: ',\n\n  'news_add'        => 'Nachricht hinzufügen',\n  'news_edit'       => 'Nachricht bearbeiten',\n  'news_copy'       => 'Nachricht kopieren',\n  'news_mode_new'   => 'Neu',\n  'news_mode_edit'  => 'Bearbeiten',\n  'news_mode_copy'  => 'Kopie',\n\n  'sys_administration' => 'Serveradministration',\n\n  'note_add'        => 'Notiz hinzufügen',\n  'note_del'        => 'Notiz löschen',\n  'note_edit'        => 'Notiz ändern',\n\n  // Verknüpfungen\n  'shortcut_title'     => 'Lesezeichen',\n  'shortcut_none'      => 'Keine Lesezeichen',\n  'shortcut_new'       => 'NEU',\n  'shortcut_text'      => 'Text',\n\n  'shortcut_add'       => 'Lesezeichen hinzufügen',\n  'shortcut_edit'      => 'Lesezeichen bearbeiten',\n  'shortcut_copy'      => 'Lesezeichen kopieren',\n  'shortcut_mode_new'  => 'Neu',\n  'shortcut_mode_edit' => 'Bearbeiten',\n  'shortcut_mode_copy' => 'Kopie',\n\n  // Raketenbezogen\n  'mip_h_launched'\t\t\t=> 'Start von Interplanetarraketen',\n  'mip_launched'\t\t\t\t=> 'Interplanetarraketen gestartet: <b>%s</b>!',\n\n  'mip_no_silo'\t\t\t\t=> 'Unzureichendes Level der Raketensilos auf dem Planeten <b>%s</b>.',\n  'mip_no_impulse'\t\t\t=> 'Impulsantrieb muss erforscht werden.',\n  'mip_too_far'\t\t\t\t=> 'Die Rakete kann nicht so weit fliegen.',\n  'mip_planet_error'\t\t\t=> 'Fehler - mehr als ein Planet an einer Koordinate',\n  'mip_no_rocket'\t\t\t\t=> 'Nicht genug Raketen im Silo für einen Angriff.',\n  'mip_hack_attempt'\t\t\t=> ' Bist du ein Hacker? Noch so ein Witz und du wirst gebannt. IP-Adresse und Login wurden aufgezeichnet.',\n\n  'mip_all_destroyed' \t\t=> 'Alle Interplanetarraketen wurden von Abfangraketen zerstört<br>',\n  'mip_destroyed'\t\t\t\t=> '%s Interplanetarraketen wurden von Abfangraketen zerstört.<br>',\n  'mip_defense_destroyed'\t=> 'Folgende Verteidigungsanlagen wurden zerstört:<br />',\n  'mip_recycled'\t\t\t\t=> 'Aus den Trümmern der Verteidigungsanlagen recycelt: ',\n  'mip_no_defense'\t\t\t=> 'Auf dem angegriffenen Planeten gab es keine Verteidigung!',\n\n  'mip_sender_amd'\t\t\t=> 'Raumfahrtraketentruppen',\n  'mip_subject_amd'\t\t\t=> 'Raketenangriff',\n  'mip_body_attack'\t\t\t=> 'Angriff mit Interplanetarraketen (%1$s Stück) vom Planeten %2$s <a href=\"galaxy.php?mode=3&galaxy=%3$d&system=%4$d&planet=%5$d\">[%3$d:%4$d:%5$d]</a> auf den Planeten %6$s <a href=\"galaxy.php?mode=3&galaxy=%7$d&system=%8$d&planet=%9$d\">[%7$d:%8$d:%9$d]</a><br><br>',\n\n  // Verschiedenes\n  'sys_game_rules' => 'Spielregeln',\n  'sys_game_documentation' => 'Spielbeschreibung',\n  'sys_banned_msg' => 'Sie sind gesperrt. Für weitere Informationen besuchen Sie <a href=\"banned.php\">hier</a>. Sperrungsende des Kontos: ',\n  'sys_total_time' => 'Gesamtzeit',\n  'sys_total_time_short' => 'Warteschlange',\n  'eco_que_finish' => 'Abschluss',\n\n  // Universum\n  'uni_moon_of_planet' => 'des Planeten',\n\n  // Kampfberichte\n  'cr_view_title'  => \"Kampfberichte anzeigen\",\n  'cr_view_button' => \"Bericht anzeigen\",\n  'cr_view_prompt' => \"Code eingeben\",\n  'cr_view_my'     => \"Meine Kampfberichte\",\n  'cr_view_hint'   => '<ul><li>Sie können Ihre eigenen Kampfberichte anzeigen, indem Sie auf den Link \"Meine Kampfberichte\" in der Überschrift klicken</li><li>Der Kampfberichtscode befindet sich in der letzten Zeile und ist eine Sequenz aus 32 Ziffern und lateinischen Buchstaben</li></ul>',\n\n  // Flotte\n  'flt_gather_all'    => 'Ressourcen sammeln',\n\n  // Bann-System\n  'ban_title'      => 'Schwarze Liste',\n  'ban_name'       => 'Name',\n  'ban_reason'     => 'Sperrungsgrund',\n  'ban_from'       => 'Sperrungsdatum',\n  'ban_to'         => 'Sperrungsdauer',\n  'ban_by'         => 'Ausgestellt von',\n  'ban_no'         => 'Keine gesperrten Spieler',\n  'ban_thereare'   => 'Gesamt',\n  'ban_players'    => 'gesperrt',\n  'ban_banned'     => 'Spieler gesperrt: ',\n\n  // Kontakte\n  'ctc_title' => 'Administration',\n  'ctc_intro' => 'Hier finden Sie die Adressen aller Administratoren und Operatoren des Spiels für Feedback',\n  'ctc_name'  => 'Name',\n  'ctc_rank'  => 'Rang',\n  'ctc_mail'  => 'eMail',\n\n  // Rekord-Seite\n  'rec_title'  => 'Universumsrekorde',\n  'rec_build'  => 'Gebäude',\n  'rec_specb'  => 'Spezialgebäude',\n  'rec_playe'  => 'Spieler',\n  'rec_defes'  => 'Verteidigung',\n  'rec_fleet'  => 'Flotte',\n  'rec_techn'  => 'Technologien',\n  'rec_level'  => 'Level',\n  'rec_nbre'   => 'Anzahl',\n  'rec_rien'   => '-',\n\n  // Credits-Seite\n  'cred_link'    => 'Internet',\n  'cred_site'    => 'Website',\n  'cred_forum'   => 'Forum',\n  'cred_credit'  => 'Autoren',\n  'cred_creat'   => 'Direktor',\n  'cred_prog'    => 'Programmierer',\n  'cred_master'  => 'Leiter',\n  'cred_design'  => 'Designer',\n  'cred_web'     => 'Webmaster',\n  'cred_thx'     => 'Danksagungen',\n  'cred_based'   => 'Basis für die Erstellung von XNova',\n  'cred_start'   => 'Debütort von XNova',\n\n  // Eingebauter Chat\n  'chat_common'   => 'Allgemeiner Chat',\n  'chat_ally'     => 'Allianz-Chat',\n  'chat_history'  => 'Chat-Verlauf',\n  'chat_message'  => 'Nachricht',\n  'chat_send'     => 'Senden',\n  'chat_page'     => 'Seite',\n  'chat_timeout'  => 'Chat aufgrund Ihrer Inaktivität deaktiviert. Aktualisieren Sie die Seite.',\n\n  // ----------------------------------------------------------------------------------------------------------\n  // Interface des Sprungtors\n  'gate_start_moon' => 'Startmond',\n  'gate_dest_moon'  => 'Zielmond',\n  'gate_use_gate'   => 'Tor benutzen',\n  'gate_ship_sel'   => 'Schiffe auswählen',\n  'gate_ship_dispo' => 'verfügbar',\n  'gate_jump_btn'   => 'Sprung ausführen!!',\n  'gate_jump_done'  => 'Das Tor befindet sich im Nachladezustand!<br>Das Tor wird in bereit sein: ',\n  'gate_wait_dest'  => 'Das Zieltor befindet sich im Vorbereitungszustand! Das Tor wird in bereit sein: ',\n  'gate_no_dest_g'  => 'Am Zielort wurde kein Tor zur Flottenverlegung gefunden',\n  'gate_no_src_ga'  => 'Kein Tor zur Flottenverlegung vorhanden',\n  'gate_wait_star'  => 'Das Tor befindet sich im Nachladezustand!<br>Das Tor wird in bereit sein: ',\n  'gate_wait_data'  => 'Fehler, keine Daten für den Sprung!',\n  'gate_vacation'   => 'Fehler, Sie können keinen Sprung durchführen, da Sie sich im Urlaubsmodus befinden!',\n  'gate_ready'      => 'Tor sprungbereit',\n\n  // Quests\n  'qst_quests'               => 'Quests',\n  'qst_msg_complete_subject' => 'Quest abgeschlossen',\n  'qst_msg_complete_body'    => 'Sie haben die Quest \"%s\" abgeschlossen.',\n  'qst_msg_your_reward'      => 'Ihre Belohnung:',\n\n  // Nachrichten\n  'msg_from_admin' => 'Universumsadministration',\n  'msg_class' => [\n    MSG_TYPE_OUTBOX => 'Gesendete Nachrichten',\n    MSG_TYPE_SPY => 'Spionageberichte',\n    MSG_TYPE_PLAYER => 'Nachrichten von Spielern',\n    MSG_TYPE_ALLIANCE => 'Allianznachrichten',\n    MSG_TYPE_COMBAT => 'Kampfberichte',\n    MSG_TYPE_RECYCLE => 'Recyclingberichte',\n    MSG_TYPE_TRANSPORT => 'Flottenankunft',\n    MSG_TYPE_ADMIN => 'Administrationsnachrichten',\n    MSG_TYPE_EXPLORE => 'Expeditionsberichte',\n    MSG_TYPE_QUE => 'Bauwarteschlangennachrichten',\n    MSG_TYPE_NEW => 'Alle Nachrichten',\n  ],\n\n  'msg_que_research_from'    => 'Forschungsinstitut',\n  'msg_que_research_subject' => 'Neue Technologie',\n  'msg_que_research_message' => 'Eine neue Technologie \\'%s\\' wurde erforscht. Neues Level - %d',\n\n  'msg_que_planet_from'    => 'Gouverneur',\n\n  'msg_que_hangar_subject' => 'Werftarbeit abgeschlossen',\n  'msg_que_hangar_message' => \"Die Werft auf %s hat die Arbeit abgeschlossen\",\n\n  'msg_que_built_subject'   => 'Planetare Arbeiten abgeschlossen',\n  'msg_que_built_message'   => \"Der Bau des Gebäudes '%2\\$s' auf %1\\$s wurde abgeschlossen. Gebaute Level: %3\\$d\",\n  'msg_que_destroy_message' => \"Die Zerstörung des Gebäudes '%2\\$s' auf %1\\$s wurde abgeschlossen. Zerstörte Level: %3\\$d\",\n\n  'msg_personal_messages' => 'Persönliche Nachrichten',\n\n  'sys_opt_bash_info'    => 'Einstellungen des Anti-Bashing-Systems',\n  'sys_opt_bash_attacks' => 'Anzahl der Angriffe in einer Welle',\n  'sys_opt_bash_interval' => 'Intervall zwischen Wellen',\n  'sys_opt_bash_scope' => 'Bashing-Berechnungszeitraum',\n  'sys_opt_bash_war_delay' => 'Moratorium nach Kriegserklärung',\n  'sys_opt_bash_waves' => 'Anzahl der Wellen pro Zeitraum',\n  'sys_opt_bash_disabled'    => 'Anti-Bashing-System deaktiviert',\n\n  'sys_id' => 'ID',\n  'sys_identifier' => 'Identifikator',\n\n  'sys_email'   => 'E-Mail',\n  'sys_ip' => 'IP',\n\n  'sys_max' => 'Max',\n  'sys_maximum' => 'Maximum',\n  'sys_maximum_level' => 'Maximales Level',\n\n  'sys_user_name' => 'Benutzername',\n  'sys_player_name' => 'Spielername',\n  'sys_user_name_short' => 'Name',\n\n  'sys_planets' => 'Planeten',\n  'sys_moons' => 'Monde',\n\n  'sys_quantity' => 'Menge',\n  'sys_quantity_maximum' => 'Maximale Menge',\n  'sys_qty' => 'Anz',\n  'sys_quantity_total' => 'Gesamtmenge',\n\n  'sys_buy_for' => 'Kaufen für',\n  'sys_buy' => 'Kaufen',\n  'sys_for' => 'für',\n\n  'sys_eco_lack_dark_matter' => 'Nicht genug Dunkle Materie',\n\n  'time_local' => 'Spielerzeit',\n  'time_server' => 'Serverzeit',\n\n  'sys_result' => [\n    'error_dark_matter_not_enough' => 'Nicht genug Dunkle Materie, um den Vorgang abzuschließen',\n    'error_dark_matter_change' => 'Fehler bei der Änderung der Dunklen Materie! Wiederholen Sie den Vorgang. Wenn der Fehler erneut auftritt, informieren Sie die Serveradministration',\n  ],\n\n  // Arrays\n  'sys_build_result' => [\n    BUILD_ALLOWED => 'Kann gebaut werden',\n    BUILD_REQUIRE_NOT_MEET => 'Anforderungen nicht erfüllt',\n    BUILD_AMOUNT_WRONG => 'Zu viel',\n    BUILD_QUE_WRONG => 'Ungültige Warteschlange',\n    BUILD_QUE_UNIT_WRONG => 'Falsche Warteschlange',\n    BUILD_INDESTRUCTABLE => 'Kann nicht zerstört werden',\n    BUILD_NO_RESOURCES => 'Nicht genug Ressourcen',\n    BUILD_NO_UNITS => 'Keine Einheiten',\n    BUILD_UNIT_BUSY => [\n      0 => 'Gebäude beschäftigt',\n      STRUC_LABORATORY => 'Forschung läuft',\n      STRUC_LABORATORY_NANO => 'Forschung läuft',\n    ],\n    BUILD_QUE_FULL => 'Warteschlange voll',\n    BUILD_SILO_FULL => 'Raketensilo voll',\n    BUILD_MAX_REACHED => 'Sie haben bereits die maximale Anzahl an Einheiten dieses Typs gebaut und/oder in die Warteschlange gestellt',\n    BUILD_SECTORS_NONE => 'Keine freien Sektoren',\n    BUILD_AUTOCONVERT_AVAILABLE => 'Autokonvertierung verfügbar',\n    BUILD_HIGHSPOT_NOT_ACTIVE => 'Event nicht aktiv',\n  ],\n\n  'sys_game_mode' => [\n    GAME_SUPERNOVA => 'SuperNova',\n    GAME_OGAME     => 'oGame',\n    GAME_BLITZ     => 'Blitz-Server',\n  ],\n\n  'months' => [\n     1 =>'Januar',\n     2 =>'Februar',\n     3 =>'März',\n     4 =>'April',\n     5 =>'Mai',\n     6 =>'Juni',\n     7 =>'Juli',\n     8 =>'August',\n     9 =>'September',\n    10 =>'Oktober',\n    11 =>'November',\n    12 =>'Dezember'\n  ],\n\n  'weekdays' => [\n    0 => 'Sonntag',\n    1 => 'Montag',\n    2 => 'Dienstag',\n    3 => 'Mittwoch',\n    4 => 'Donnerstag',\n    5 => 'Freitag',\n    6 => 'Samstag'\n  ],\n\n  'user_level' => [\n    0 => 'Spieler',\n    1 => 'Moderator',\n    2 => 'Operator',\n    3 => 'Administrator',\n    4 => 'Entwickler',\n  ],\n\n  'user_level_shortcut' => [\n    0 => 'S',\n    1 => 'M',\n    2 => 'O',\n    3 => 'A',\n    4 => 'E',\n  ],\n\n  'sys_lessThen15min'   => '&lt; 15 Min',\n\n  'sys_no_points'         => 'Sie haben nicht genug <span class=\"dark_matter\">Dunkle Materie</span>!',\n  'sys_dark_matter_obtain_header' => 'Wie man <span class=\"dark_matter\">Dunkle Materie</span> erhält',\n  'sys_dark_matter_desc' => 'Dunkle Materie ist eine mit Standardmethoden nicht nachweisbare nicht-baryonische Materie, die 23% der Masse des Universums ausmacht. Daraus kann eine unglaubliche Menge an Energie gewonnen werden. Aufgrund dessen und der mit ihrer Gewinnung verbundenen Schwierigkeiten ist Dunkle Materie sehr wertvoll.',\n  'sys_dark_matter_hint' => 'Mit dieser Substanz können Sie Offiziere und Kommandeure anheuern.',\n\n  'sys_dark_matter_what_why_how' => 'Was ist <span class=\"dark_matter\">Dunkle Materie</span> und <span class=\"metamatter\">Metamaterie</span>',\n  'sys_dark_matter_what_header' => 'Was ist <span class=\"dark_matter\">Dunkle Materie</span>',\n  'sys_dark_matter_description_header' => 'Wofür wird <span class=\"dark_matter\">Dunkle Materie</span> benötigt',\n  'sys_dark_matter_description_text' => '<span class=\"dark_matter\">Dunkle Materie</span> ist eine In-Game-Ressource, mit der Sie verschiedene Aktionen durchführen können:\n    <ul>\n      <li><a href=\"index.php?page=premium\"><span class=\"link\">Premium-Account</span></a> kaufen</li>\n      <li><a href=\"officer.php?mode=600\"><span class=\"link\">Söldner</span></a> für Ihr Imperium anheuern</li>\n      <li>Gouverneure anheuern und zusätzliche Sektoren <a href=\"overview.php?mode=manage\"><span class=\"link\">auf Planeten</span></a> kaufen</li>\n      <li><a href=\"officer.php?mode=1100\"><span class=\"link\">Blaupausen</span></a> kaufen</li>\n      <li><a href=\"artifacts.php\"><span class=\"link\">Artefakte</span></a> kaufen</li>\n      <li><a href=\"market.php\"><span class=\"link\">Schwarzmarkt</span></a> nutzen: Ressourcen tauschen; Schiffe verkaufen; Gebrauchtschiffe kaufen usw.</li>\n      <li>...und vieles mehr</li>\n    </ul>',\n  'sys_dark_matter_obtain_text' => 'Sie erhalten <span class=\"metamatter\">Dunkle Materie</span> während des Spiels: durch Erfahrungspunkte für erfolgreiche Überfälle auf fremde Planeten, Erforschung neuer Technologien sowie durch Bau und Zerstörung von Gebäuden.\n    Manchmal können auch Forschungsexpeditionen <span class=\"metamatter\">DM</span> bringen.',\n  'sys_dark_matter_obtain_text_convert' => '<br />Wenn Ihnen <span class=\"dark_matter\">Dunkle Materie</span> fehlt - kaufen Sie <span class=\"metamatter\">Metamaterie</span>. Bei Dunkle-Materie-Mangel wird die benötigte Menge <span class=\"metamatter\">Metamaterie</span> anstelle von <span class=\"dark_matter\">DM</span> verwendet',\n\n  'sys_msg_err_update_dm' => 'Fehler beim Aktualisieren der DM-Menge!',\n\n  'sys_na' => 'Nicht verfügbar',\n  'sys_na_short' => 'N/V',\n\n  'sys_ali_res_title' => 'Allianzressourcen',\n\n  'sys_bonus' => 'Bonus',\n\n  'sys_of_ally' => 'der Allianz',\n\n  'sys_hint_player_name' => 'Die Spielersuche kann nach ID oder Name erfolgen. Wenn der Spielername aus nicht lesbaren Zeichen oder nur aus Zahlen besteht - verwenden Sie die ID für die Suche',\n  'sys_hint_ally_name' => 'Die Allianzsuche kann nach ID, Tag oder Name erfolgen. Wenn der Tag oder Allianzname aus nicht lesbaren Zeichen oder nur aus Zahlen besteht - verwenden Sie die ID für die Suche',\n\n  'sys_fleet_and' => '+ Flotten',\n\n  'sys_on_planet' => 'Auf dem Planeten',\n  'fl_on_stores' => 'Auf Lager',\n\n  'sys_ali_bonus_members' => 'Mindestgröße der Allianz für den Bonus',\n\n  'sys_premium' => 'Premium',\n\n  'mrc_period_list' => [\n    PERIOD_MINUTE    => '1 Minute',\n    PERIOD_MINUTE_3  => '3 Minuten',\n    PERIOD_MINUTE_5  => '5 Minuten',\n    PERIOD_MINUTE_10 => '10 Minuten',\n    PERIOD_DAY       => '1 Tag',\n    PERIOD_DAY_3     => '3 Tage',\n    PERIOD_WEEK      => '1 Woche',\n    PERIOD_WEEK_2    => '2 Wochen',\n    PERIOD_MONTH     => '30 Tage',\n    PERIOD_MONTH_2   => '60 Tage',\n    PERIOD_MONTH_3   => '90 Tage',\n  ],\n\n  'sys_sector_buy' => '1 Sektor kaufen',\n\n  'sys_select_confirm' => 'Auswahl bestätigen',\n\n  'sys_capital' => 'Hauptstadt',\n\n  'sys_result_operation' => 'Meldungen',\n\n  'sys_password' => 'Passwort',\n  'sys_password_length' => 'Passwortlänge',\n  'sys_password_seed' => 'Verwendete Zeichen',\n\n  'sys_msg_ube_report_err_not_found' => 'Kampfbericht nicht gefunden. Überprüfen Sie den Schlüssel. Es besteht auch die Möglichkeit, dass der Bericht als veraltet gelöscht wurde',\n\n  'sys_mess_attack_report' \t=> 'Kampfbericht',\n  'sys_perte_attaquant' \t\t=> 'Angreifer verlor',\n  'sys_perte_defenseur' \t\t=> 'Verteidiger verlor',\n\n\n  'ube_report_info_page_header' => 'Kampfbericht',\n  'ube_report_info_page_header_cypher' => 'Zugangscode',\n  'ube_report_info_main' => 'Grundlegende Kampfinformationen',\n  'ube_report_info_date' => 'Datum und Uhrzeit',\n  'ube_report_info_location' => 'Ort',\n  'ube_report_info_rounds_number' => 'Anzahl der Runden',\n  'ube_report_info_outcome' => 'Kampfergebnis',\n  'ube_report_info_outcome_win' => 'Angreifer hat den Kampf gewonnen',\n  'ube_report_info_outcome_loss' => 'Angreifer hat den Kampf verloren',\n  'ube_report_info_outcome_draw' => 'Kampf endete unentschieden',\n  'ube_report_info_link' => 'Link zum Kampfbericht',\n  'ube_report_info_bbcode' => 'BBCode für den Chat',\n  'ube_report_info_sfr' => 'Der Kampf endete in einer Runde mit einer Niederlage des Angreifers<br />Wahrscheinlich RMF',\n  'ube_report_info_debris' => 'Trümmer im Orbit',\n  'ube_report_info_debris_simulator' => '(ohne Mondentstehung)',\n  'ube_report_info_loot' => 'Beute',\n  'ube_report_info_loss' => 'Kampfverluste',\n  'ube_report_info_generate' => 'Seitengenerierungszeit',\n\n  'ube_report_moon_was' => 'Dieser Planet hatte bereits einen Mond',\n  'ube_report_moon_chance' => 'Chance auf Mondentstehung',\n  'ube_report_moon_created' => 'Im Orbit des Planeten entstand ein Mond mit einem Durchmesser von',\n\n  'ube_report_moon_reapers_none' => 'Alle Schiffe mit Gravitationsantrieben wurden während des Kampfes zerstört',\n  'ube_report_moon_reapers_wave' => 'Die Schiffe des Angreifers erzeugten eine fokussierte Gravitationswelle',\n  'ube_report_moon_reapers_chance' => 'Chance auf Mondzerstörung',\n  'ube_report_moon_reapers_success' => 'Mond zerstört',\n  'ube_report_moon_reapers_failure' => 'Die Wellenstärke reichte nicht aus, um den Mond zu zerstören',\n\n  'ube_report_moon_reapers_outcome' => 'Chance auf Antriebsexplosion',\n  'ube_report_moon_reapers_survive' => 'Die genaue Kompensation der Gravitationsfelder des Systems ermöglichte es, den Rückstoß der Mondzerstörung zu dämpfen',\n  'ube_report_moon_reapers_died' => 'Da die zusätzlichen Gravitationsfelder des Systems nicht kompensiert werden konnten, wurde die Flotte zerstört',\n\n  'ube_report_side_attacker' => 'Angreifer',\n  'ube_report_side_defender' => 'Verteidiger',\n\n  'ube_report_round' => 'Runde',\n  'ube_report_unit' => 'Kampfeinheit',\n  'ube_report_attack' => 'Angriff',\n  'ube_report_shields' => 'Schilde',\n  'ube_report_shields_passed' => 'Durchbruch',\n  'ube_report_armor' => 'Rüstung',\n  'ube_report_damage' => 'Schaden',\n  'ube_report_loss' => 'Verluste',\n\n\n  'ube_report_info_restored' => 'Verteidigungsanlagen wiederhergestellt',\n  'ube_report_info_loss_final' => 'Endgültige Verluste an Kampfeinheiten',\n  'ube_report_info_loss_resources' => 'Verluste in Ressourcen umgerechnet',\n  'ube_report_info_loss_dropped' => 'Ressourcenverluste durch verringerte Laderäume',\n  'ube_report_info_loot_lost' => 'Ressourcen von den Planetenlagern abtransportiert',\n  'ube_report_info_loss_gained' => 'Verluste durch Ressourcenabtransport vom Planeten',\n  'ube_report_info_loss_in_metal' => 'Gesamtverluste in Metall umgerechnet',\n\n\n  'ube_report_msg_body_common' => 'Der Kampf fand %s im Orbit %s [%d:%d:%d] %s<br />%s<br /><br />',\n  'ube_report_msg_body_debris' => 'Infolge des Kampfes entstanden im Orbit des Planeten Trümmer:<br />',\n  'ube_report_msg_body_sfr' => 'Verbindung zur Flotte verloren',\n\n  'ube_report_capture' => 'Planeteneroberung',\n  'ube_report_capture_result' => [\n    UBE_CAPTURE_DISABLED => 'Planeteneroberung deaktiviert',\n    UBE_CAPTURE_NON_PLANET => 'Nur Planeten können erobert werden',\n    UBE_CAPTURE_NOT_A_WIN_IN_1_ROUND => 'Für die Planeteneroberung muss der Kampf in der ersten Runde gewonnen werden',\n    UBE_CAPTURE_TOO_MUCH_FLEETS => 'Beim Erobern eines Planeten dürfen nur die Eroberungsflotte und die planetare Flotte am Kampf teilnehmen',\n    UBE_CAPTURE_NO_ATTACKER_USER_ID => 'INTERNER FEHLER - Keine Angreifer-ID! Melden Sie dies dem Entwickler!',\n    UBE_CAPTURE_NO_DEFENDER_USER_ID => 'INTERNER FEHLER - Keine Verteidiger-ID! Melden Sie dies dem Entwickler!',\n    UBE_CAPTURE_CAPITAL => 'Hauptstadt kann nicht erobert werden',\n    UBE_CAPTURE_TOO_LOW_POINTS => 'Planeten können nur von Spielern erobert werden, deren Gesamtpunktzahl mindestens doppelt so hoch ist wie die des Angreifers',\n    UBE_CAPTURE_NOT_ENOUGH_SLOTS => 'Keine Eroberungsslots mehr verfügbar',\n    UBE_CAPTURE_SUCCESSFUL => 'Planet wurde vom angreifenden Spieler erobert',\n  ],\n\n  'sys_kilometers_short' => 'km',\n\n  'ube_simulation' => 'Simulation',\n\n  'sys_hire_do' => 'Anheuern',\n\n  'sys_captains' => 'Kapitäne',\n\n  'sys_fleet_composition' => 'Flottenzusammensetzung',\n\n  'sys_continue' => 'Fortsetzen',\n\n  'uni_planet_density_types' => [\n    PLANET_DENSITY_NONE => 'Kommt nicht vor',\n    PLANET_DENSITY_ICE_HYDROGEN => 'Wasserstoffeis',\n    PLANET_DENSITY_ICE_METHANE => 'Methaneis',\n    PLANET_DENSITY_ICE_WATER => 'Wassereis',\n    PLANET_DENSITY_CRYSTAL_RAW => 'Kristall',\n    PLANET_DENSITY_CRYSTAL_SILICATE => 'Silikat',\n    PLANET_DENSITY_CRYSTAL_STONE => 'Stein',\n    PLANET_DENSITY_STANDARD => 'Standard',\n    PLANET_DENSITY_METAL_ORE => 'Erz',\n    PLANET_DENSITY_METAL_PERIDOT => 'Peridot',\n    PLANET_DENSITY_METAL_RAW => 'Metall',\n  ],\n\n  'sys_planet_density' => 'Dichte',\n  'sys_planet_density_units' => 'kg/m&sup3;',\n  'sys_planet_density_core' => 'Kerntyp',\n\n  'sys_change' => 'Ändern',\n  'sys_show' => 'Anzeigen',\n  'sys_hide' => 'Ausblenden',\n  'sys_close' => 'Schließen',\n  'sys_unlimited' => 'Keine Begrenzung',\n\n  'ov_core_type_current' => 'Aktueller Kerntyp',\n  'ov_core_change_to' => 'Ändern zu',\n  'ov_core_err_none' => 'Der Kerntyp des Planeten wurde erfolgreich von \"%s\" zu \"%s\" geändert.<br />Neue Planetendichte %d kg/m3',\n  'ov_core_err_not_a_planet' => 'Nur auf Planeten kann die Kerndichte geändert werden',\n  'ov_core_err_denisty_type_wrong' => 'Falscher Kerntyp',\n  'ov_core_err_same_density' => 'Der neue Kerntyp unterscheidet sich nicht vom aktuellen - nichts zu ändern',\n  'ov_core_err_no_dark_matter' => 'Nicht genug Dunkle Materie, um den Kerntyp zu ändern',\n\n  'sys_color'    => \"Farbe\",\n\n  'topnav_imp_attack' => 'Ihr Imperium wurde angegriffen!',\n  'topnav_user_rank' => 'Ihr aktueller Platz in der Rangstatistik',\n  'topnav_users' => 'Gesamtzahl registrierter Spieler',\n  'topnav_users_online' => 'Aktuelle Anzahl online Spieler',\n\n  'topnav_refresh_page' => 'Seite neu laden',\n\n  'sys_colonies' => 'Kolonien',\n  'sys_radio' => 'Radio \"Kosmos\"',\n\n  'sys_auth_provider_list' => [\n    ACCOUNT_PROVIDER_NONE => 'USERS-Tabelle',\n    ACCOUNT_PROVIDER_LOCAL => 'ACCOUNT-Tabelle',\n    ACCOUNT_PROVIDER_CENTRAL => 'Zentrale ACCOUNT-Tabelle',\n  ],\n\n  'sys_login_messages' => [\n    LOGIN_UNDEFINED => 'Login-Prozess nicht gestartet',\n    LOGIN_SUCCESS => 'Login erfolgreich',\n    LOGIN_ERROR_USERNAME_EMPTY => 'Spielername darf nicht leer sein',\n    LOGIN_ERROR_USERNAME_RESTRICTED_CHARACTERS => 'Im Spielernamen und Login sind folgende Zeichen nicht erlaubt: ',\n    LOGIN_ERROR_USERNAME => 'Spieler mit diesem Namen nicht gefunden',\n    LOGIN_ERROR_USERNAME_ALLY_OR_BOT => 'Dieser Name gehört einer Allianz oder einem Bot. Man kann sich damit nicht einloggen... zumindest noch nicht',\n    LOGIN_ERROR_PASSWORD_EMPTY => 'Passwort darf nicht leer sein',\n    LOGIN_ERROR_PASSWORD_TRIMMED => 'Passwort darf nicht mit Leerzeichen, Tabulatoren oder Zeilenumbrüchen beginnen oder enden',\n    LOGIN_ERROR_PASSWORD => 'Falsches Passwort',\n  //    LOGIN_ERROR_COOKIE => '',\n\n    REGISTER_SUCCESS => 'Registrierung erfolgreich abgeschlossen',\n    REGISTER_ERROR_BLITZ_MODE => 'Die Registrierung neuer Spieler im Blitz-Server-Modus ist deaktiviert',\n    REGISTER_ERROR_USERNAME_WRONG => 'Ungültiger Spielername',\n    REGISTER_ERROR_ACCOUNT_NAME_EXISTS => 'Der Kontoname ist bereits vergeben. Versuchen Sie, sich mit diesem Namen und Ihrem Passwort anzumelden oder das Passwort zurückzusetzen',\n    REGISTER_ERROR_PASSWORD_INSECURE => 'Ungültiges Passwort. Das Passwort muss mindestens ' . PASSWORD_LENGTH_MIN . ' Zeichen lang sein',\n    REGISTER_ERROR_USERNAME_SHORT => 'Name zu kurz. Der Name muss mindestens ' . LOGIN_LENGTH_MIN. ' Zeichen lang sein',\n    REGISTER_ERROR_PASSWORD_DIFFERENT => 'Passwort und Bestätigungspasswort stimmen nicht überein. Überprüfen Sie die Eingabe',\n    REGISTER_ERROR_EMAIL_EMPTY => 'E-Mail darf nicht leer sein',\n    REGISTER_ERROR_EMAIL_WRONG => 'Die eingegebene E-Mail ist keine gültige E-Mail-Adresse. Überprüfen Sie die Schreibweise oder verwenden Sie eine andere E-Mail-Adresse',\n    REGISTER_ERROR_EMAIL_EXISTS => 'Diese E-Mail-Adresse ist bereits registriert. Wenn Sie sich bereits im Spiel registriert haben - versuchen Sie, das Passwort zurückzusetzen. Andernfalls - verwenden Sie eine andere E-Mail-Adresse',\n\n    PASSWORD_RESTORE_ERROR_EMAIL_NOT_EXISTS => 'Kein Spieler mit dieser primären E-Mail',\n    PASSWORD_RESTORE_ERROR_TOO_OFTEN => 'Wiederherstellungscode kann nur alle 10 Minuten angefordert werden. Wenn Sie keine E-Mail erhalten haben - überprüfen Sie den Spam-Ordner oder schreiben Sie eine E-Mail an die Serveradministration an die Adresse <span class=\"ok\">' . $config->server_email . '</span> von der E-Mail, die Sie bei der Registrierung verwendet haben',\n    PASSWORD_RESTORE_ERROR_SENDING => 'Fehler beim Senden der E-Mail. Schreiben Sie eine E-Mail an die Serveradministration an die Adresse <span class=\"ok\">' . $config->server_email . '</span>',\n    PASSWORD_RESTORE_SUCCESS_CODE_SENT => 'E-Mail mit Wiederherstellungscode erfolgreich gesendet',\n\n    PASSWORD_RESTORE_ERROR_CODE_EMPTY => 'Wiederherstellungscode darf nicht leer sein',\n    PASSWORD_RESTORE_ERROR_CODE_WRONG => 'Falscher Wiederherstellungscode',\n    PASSWORD_RESTORE_ERROR_CODE_TOO_OLD => 'Wiederherstellungscode abgelaufen. Holen Sie einen neuen',\n    PASSWORD_RESTORE_ERROR_CODE_OK_BUT_NO_ACCOUNT_FOR_EMAIL => 'Der Wiederherstellungscode ist korrekt, aber es wurde kein Konto mit dieser E-Mail gefunden. Möglicherweise wurde es gelöscht oder ein interner Fehler ist aufgetreten. Wenden Sie sich an die Serveradministration',\n    PASSWORD_RESTORE_SUCCESS_PASSWORD_SENT => 'Passwort erfolgreich zurückgesetzt. Ihnen wurde eine E-Mail mit dem neuen Passwort gesendet',\n    PASSWORD_RESTORE_SUCCESS_PASSWORD_SEND_ERROR => 'Fehler beim Senden der E-Mail mit dem neuen Passwort. Holen Sie einen neuen Wiederherstellungscode und versuchen Sie es erneut',\n\n    REGISTER_ERROR_PLAYER_NAME_TRIMMED => 'Der Spielername darf nicht mit Leerzeichen (Zeichen \"Leerzeichen\", \"Tabulator\", \"Zeilenumbruch\" usw.) beginnen oder enden',\n    REGISTER_ERROR_PLAYER_NAME_EMPTY => 'Der Spielername darf nicht leer sein',\n    REGISTER_ERROR_PLAYER_NAME_RESTRICTED_CHARACTERS => 'Der Spielername enthält unzulässige Zeichen',\n    REGISTER_ERROR_PLAYER_NAME_SHORT => 'Der Spielername darf nicht kürzer als ' . LOGIN_LENGTH_MIN . ' Zeichen sein',\n    REGISTER_ERROR_PLAYER_NAME_EXISTS => 'Dieser Spielername ist bereits vergeben. Bitte wählen Sie einen anderen',\n\n    // Interne Fehler\n    AUTH_ERROR_INTERNAL_PASSWORD_CHANGE_ON_RESTORE => 'INTERNER FEHLER! MELDEN SIE DIES DER ADMINISTRATION! Fehler beim Passwortwechsel. Bitte melden Sie diesen Fehler der Universumsadministration!',\n    PASSWORD_RESTORE_ERROR_ADMIN_ACCOUNT => 'Passwortwiederherstellung für das Serverteam verboten. Wenden Sie sich an den Administrator',\n    REGISTER_ERROR_ACCOUNT_CREATE => 'Fehler beim Erstellen des Kontos! Bitte melden Sie dies der Administration!',\n    LOGIN_ERROR_SYSTEM_ACCOUNT_TRANSLATION => 'SYSTEMFEHLER - FEHLER IN DER PROVIDER-ÜBERSETZUNGSTABELLE! Melden Sie dies der Serveradministration!',\n    PASSWORD_RESTORE_ERROR_ACCOUNT_NOT_EXISTS => 'Interner Fehler - beim Zurücksetzen des Passworts wurde kein Konto gefunden! Melden Sie diesen Fehler der Administration!',\n    AUTH_PASSWORD_RESET_INSIDE_ERROR_NO_ACCOUNT_FOR_CONFIRMATION => 'INTERNER FEHLER! Keine Konten zum Zurücksetzen des Passworts bei korrektem Bestätigungscode! Bitte melden Sie diesen Fehler der Universumsadministration!',\n    LOGIN_ERROR_NO_ACCOUNT_FOR_COOKIE_SET => 'INTERNER FEHLER! MELDEN SIE DIES DER ADMINISTRATION! Kein Konto bei cookie_set() gesetzt! Bitte melden Sie diesen Fehler der Universumsadministration!',\n  ],\n\n  'log_reg_email_title' => \"Ihre Registrierung auf dem Server %1\\$s des Spiels SuperNova\",\n  'log_reg_email_text' => \"Registrierungsbestätigung für %3\\$s\\r\\n\\r\\n\n  Diese E-Mail enthält Ihre Registrierungsdaten auf dem Server %1\\$s des Spiels SuperNova\\r\\n\n  Bewahren Sie diese Daten an einem sicheren Ort auf\\r\\n\\r\\n\n  Serveradresse: %2\\$s\\r\\n\n  Ihr Login: %3\\$s\\r\\n\n  Ihr Passwort: %4\\$s\\r\\n\\r\\n\n  Vielen Dank für Ihre Registrierung auf unserem Server! Wir wünschen Ihnen viel Erfolg im Spiel!\\r\\n\n  Die Administration des Servers %1\\$s %2\\$s\\r\\n\\r\\n\n  Der Server läuft auf der freien Engine 'Project SuperNova.WS'. Entfache deine SuperNova http://supernova.ws/\",\n\n   'log_lost_email_title' => 'SuperNova, Universum %s: Passwort zurücksetzen',\n  'log_lost_email_code' => \"Jemand (möglicherweise Sie) hat eine Passwortzurücksetzung im Universum %1\\$4 des Spiels SuperNova angefordert. Falls Sie dies nicht veranlasst haben, ignorieren Sie diese E-Mail einfach.\\r\\n\\r\\nUm Ihr Passwort zurückzusetzen, besuchen Sie die folgende Adresse:\\r\\n%1\\$s?password_reset_confirm=1&password_reset_code=%2\\$s#tab_password_reset\\r\\n oder geben Sie den Bestätigungscode \\\"%2\\$s\\\" (OHNE ANFÜHRUNGSZEICHEN!) auf der Seite %1\\$s#tab_password_reset ein.\\r\\n\\r\\nDieser Code ist gültig bis %3\\$s. Danach müssen Sie einen neuen Bestätigungscode anfordern.\",\n  'log_lost_email_pass' => \"Sie haben Ihr Passwort auf dem Server %1\\$s des Spiels 'SuperNova' zurückgesetzt.\\r\\n\\r\\nIhr Spielername:\\r\\n%2\\$s\\r\\n\\r\\nIhr neues Passwort:\\r\\n%3\\$s\\r\\n\\r\\nMerken Sie es sich!\\r\\n\\r\\nSie können sich unter \" . SN_ROOT_VIRTUAL . \"login.php mit den oben genannten Daten anmelden.\",\n\n  'login_player_register_player_name' => 'Spielername',\n  'login_player_register_description' => 'Nur noch ein Schritt! Wählen Sie einen Spielernamen - den Namen, der anderen Spielern in diesem Universum angezeigt wird.',\n  'login_player_register_do' => 'Namen wählen',\n  'login_player_register_logout' => 'Mit einem anderen Konto anmelden',\n  'login_player_register_logout_description' => 'Wenn Sie sich mit einem anderen Konto anmelden möchten, klicken Sie auf die Schaltfläche',\n\n  'sys_password_reset_message_body' => \"Sie haben Ihr Passwort für den Zugang zum Spiel in diesem Universum zurückgesetzt.\\r\\n\\r\\nIhr neues Passwort:\\r\\n\\r\\n%1\\$s\\r\\n\\r\\nMerken Sie es sich!\\r\\n\\r\\nSie können Ihr Passwort jederzeit unter 'Einstellungen' ändern.\",\n\n  'sys_login_password_show' => 'Passwort anzeigen',\n  'sys_login_password_hide' => 'Passwort verbergen',\n  'sys_password_repeat' => 'Passwort wiederholen',\n\n  'sys_game_disable_reason' => [\n    GAME_DISABLE_NONE => 'Spiel aktiviert',\n    GAME_DISABLE_REASON => 'Spiel deaktiviert. Spieler sehen die Nachricht',\n    GAME_DISABLE_UPDATE => 'Spiel wird aktualisiert',\n    GAME_DISABLE_STAT => 'Statistik wird neu berechnet',\n    GAME_DISABLE_INSTALL => 'Spiel ist noch nicht konfiguriert',\n    GAME_DISABLE_MAINTENANCE => 'Wartung der Serverdatenbank',\n    GAME_DISABLE_EVENT_BLACK_MOON => 'Schwarzer Mond!',\n    GAME_DISABLE_EVENT_OIS => 'Objekte im Weltraum',\n  ],\n\n  'sys_sector_purchase_log' => 'Benutzer {%2$d} {%1$s} hat 1 Sektor auf Planet {%5$d} {%3$s} Typ \"%4$s\" für %6$d DM gekauft',\n\n  'sys_notes' => 'Notizen',\n  'sys_notes_priorities' => [\n    0 => 'Ganz unwichtig',\n    1 => 'Unwichtig',\n    2 => 'Normal',\n    3 => 'Wichtig',\n    4 => 'Sehr wichtig',\n  ],\n\n  'sys_milliseconds' => 'Millisekunden',\n\n  'sys_gender' => 'Geschlecht',\n  'sys_gender_list' => [\n    GENDER_UNKNOWN => 'Wird es selbst entscheiden',\n    GENDER_MALE => 'Männlich',\n    GENDER_FEMALE => 'Weiblich',\n  ],\n\n  'imp_stat_header' => 'Diagramm der Statistikänderungen',\n  'imp_stat_types' => [\n    'TOTAL_RANK' => 'Platz in der Gesamtstatistik',\n    'TOTAL_POINTS' => 'Gesamtpunktzahl',\n    'TECH_RANK' => 'Platz in der Forschungsstatistik',\n    'TECH_POINTS' => 'Punkte für Forschung',\n    'BUILD_RANK' => 'Platz in der Gebäudestatistik',\n    'BUILD_POINTS' => 'Punkte für Gebäude',\n    'DEFS_RANK' => 'Platz in der Verteidigungsstatistik',\n    'DEFS_POINTS' => 'Punkte für Verteidigung',\n    'FLEET_RANK' => 'Platz in der Flottenstatistik',\n    'FLEET_POINTS' => 'Punkte für Flotten',\n    'RES_RANK' => 'Platz in der Ressourcenstatistik',\n    'RES_POINTS' => 'Punkte für freie Ressourcen',\n  ],\n\n  'sys_date' => 'Datum',\n\n  'sys_blitz_global_button' => 'Blitz-Server',\n  'sys_blitz_page_disabled' => 'Im Blitz-Server-Modus ist diese Seite nicht verfügbar',\n  'sys_blitz_registration_disabled' => 'Registrierung für den Blitz-Server ist deaktiviert',\n  'sys_blitz_registration_no_users' => 'Keine registrierten Spieler',\n  'sys_blitz_registration_player_register' => 'Für das Spiel registrieren',\n  'sys_blitz_registration_player_register_un' => 'Registrierung zurückziehen',\n  'sys_blitz_registration_closed' => 'Registrierung ist derzeit geschlossen. Bitte versuchen Sie es später erneut',\n  'sys_blitz_registration_player_generate' => 'Logins und Passwörter generieren',\n  'sys_blitz_registration_player_import_generated' => 'Generierte Zeichenkette importieren',\n  'sys_blitz_registration_player_name' => 'Ihr Login für den Blitz-Server:',\n  'sys_blitz_registration_player_password' => 'Ihr Passwort für den Blitz-Server:',\n  'sys_blitz_registration_server_link' => 'Link zum Blitz-Server',\n  'sys_blitz_registration_player_blitz_name' => 'Name auf dem Blitz-Server',\n  'sys_blitz_registration_price' => 'Kosten für die Bewerbung',\n  'sys_blitz_registration_mode_list' => [\n    BLITZ_REGISTER_DISABLED => 'Registrierung deaktiviert',\n    BLITZ_REGISTER_OPEN => 'Registrierung geöffnet',\n    BLITZ_REGISTER_CLOSED => 'Registrierung geschlossen',\n    BLITZ_REGISTER_SHOW_LOGIN => 'Logins und Passwörter sichtbar',\n    BLITZ_REGISTER_DISCLOSURE_NAMES => 'Ergebnisbekanntgabe',\n  ],\n\n  'survey' => 'Umfrage',\n  'survey_questions' => 'Auswahlmöglichkeiten',\n  'survey_questions_hint' => '1 Option pro Zeile',\n  'survey_questions_hint_edit' => 'Das Bearbeiten der Umfrage setzt die Ergebnisse zurück',\n  'survey_until' => 'Dauer der Umfrage (standardmäßig 1 Tag)',\n\n  'survey_votes_total_none' => 'Noch hat niemand abgestimmt... Seien Sie der Erste!',\n  'survey_votes_total_voted' => 'Bisher abgestimmt:',\n  'survey_votes_total_voted_join' => 'Stimmen Sie ab - oder Sie verlieren!',\n  'survey_votes_total_voted_has_answer' => 'Sie haben bereits abgestimmt. Zusammen mit Ihnen haben abgestimmt:',\n\n  'survey_lasts_until' => 'Die Umfrage läuft bis',\n\n  'survey_select_one' => 'Wählen Sie eine Antwortmöglichkeit und klicken Sie auf',\n  'survey_confirm' => 'Abstimmen!',\n  'survey_result_sent' => 'Ihre Stimme wurde gezählt. Aktualisieren Sie die Seite oder nutzen Sie den Link <a class=\"link\" href=\"announce.php\">Neuigkeiten</a>, um die aktuellen Umfrageergebnisse zu sehen.',\n  'survey_complete' => 'Umfrage abgeschlossen',\n\n  'player_option_fleet_ship_sort' => [\n    PLAYER_OPTION_SORT_DEFAULT => 'Standard',\n    PLAYER_OPTION_SORT_NAME => 'Nach Name',\n    PLAYER_OPTION_SORT_ID => 'Nach ID',\n    PLAYER_OPTION_SORT_SPEED => 'Nach Geschwindigkeit',\n    PLAYER_OPTION_SORT_COUNT => 'Nach Anzahl',\n  ],\n\n  'player_option_building_sort' => [\n    PLAYER_OPTION_SORT_DEFAULT => 'Standard',\n    PLAYER_OPTION_SORT_NAME => 'Nach Name',\n    PLAYER_OPTION_SORT_ID => 'Nach ID',\n    PLAYER_OPTION_SORT_CREATE_TIME_LENGTH => 'Nach Bauzeit',\n  ],\n\n  'sys_sort' => 'Sortierung',\n  'sys_sort_inverse' => 'In umgekehrter Reihenfolge',\n\n  'sys_blitz_reward_log_message' => 'Blitz-Server %1$d Platz Blitz-Name \"%2$s\"',\n  'sys_blitz_registration_view_stat' => 'Blitz-Server-Statistik anzeigen',\n\n  'sys_login_register_message_title' => \"Ihr Name und Passwort für den Spielzugang\",\n  'sys_login_register_message_body' => \"Ihr Spielname (Login)\\r\\n%1\\$s\\r\\n\\r\\nIhr Passwort\\r\\n%2\\$s\\r\\n\\r\\nNotieren oder merken Sie sich diese Daten!\",\n\n  'auth_provider_list' => [\n    ACCOUNT_PROVIDER_NONE => 'Users-Tabelle',\n    ACCOUNT_PROVIDER_LOCAL => 'Account-Tabelle',\n    ACCOUNT_PROVIDER_CENTRAL => 'Zentrale Speicherung',\n  ],\n\n  'bld_autoconvert' => 'Automatische Konvertierung bei der Erstellung von Einheit {%1$d} \"%4$s\" in Menge %2$d auf Planet %3$s zum Preis \"%5$s\". Debug: $resource_got = \"%6$s\", $exchange = %7$s\"\"',\n\n  'news_show_rest' => 'Nachrichtentext anzeigen',\n\n  'wiki_requrements' => 'Voraussetzungen',\n  'wiki_grants' => 'Gewährt',\n\n  'que_slot_length' => 'Slots',\n  'que_slot_length_long' => 'Warteschlangen-Slots',\n\n  'sys_buy_doing' => 'Sie kaufen',\n  'sys_planet_sector' => 'Sektor',\n  'sys_planet_on' => 'auf',\n\n  'sys_purchase_confirm' => 'Kauf bestätigen',\n\n  'sys_confirm_action_title' => 'Bestätigen Sie Ihre Aktion',\n  'sys_confirm_action' => 'Möchten Sie dies wirklich tun?',\n\n  'sys_system_speed_original' => 'Originalgeschwindigkeit',\n  'sys_system_speed_for_action' => 'Im Rahmen der Aktion',\n\n  'menu_info_best_battles' => 'Beste Schlachten',\n\n  'sys_cost' => 'Kosten',\n  'sys_price' => 'Preis',\n\n  'sys_governor_none' => 'Gouverneur nicht eingestellt',\n  'sys_governor_hire' => 'Gouverneur einstellen',\n  'sys_governor_upgrade_or_change' => 'Gouverneur verbessern oder wechseln',\n\n  'tutorial_prev' => '<< Zurück',\n  'tutorial_next' => 'Weiter >>',\n  'tutorial_finish' => 'Abschließen',\n  'tutorial_window' => 'In Fenster öffnen',\n  'tutorial_window_off' => 'Zur Seite zurückkehren',\n\n  'tutorial_error_load' => \"Fehler beim Laden des Tutorials - versuchen Sie es erneut! Bei wiederholtem Fehler - melden Sie es der Spieladministration.\",\n  'tutorial_error_next' => \"Fehler: Nächste Tutorialseite existiert nicht - melden Sie es der Spieladministration.\",\n  'tutorial_error_prev' => \"Fehler: Vorherige Tutorialseite existiert nicht - melden Sie es der Spieladministration.\",\n\n  'sys_click_here_to_continue' => 'Klicken Sie hier, um fortzufahren',\n\n  'sys_module_error_not_found' => 'Belohnungsmodul \"%1$s\" nicht gefunden oder deaktiviert!',\n\n  'rank_page_title' => 'Militärränge',\n  'rank' => 'Rang',\n  'ranks' => [\n    0  => 'Kadett',\n    1  => 'Rekrut',\n    2  => 'Gefreiter',\n    3  => 'Obergefreiter',\n    4  => 'Korporal',\n    5  => 'Feldwebel',\n    6  => 'Stabsfeldwebel',\n    7  => 'Fähnrich',\n    8  => 'Oberfähnrich',\n    9  => 'Leutnant',\n    10 => 'Oberleutnant',\n    11 => 'Hauptmann',\n    12 => 'Major',\n    13 => 'Oberstleutnant',\n    14 => 'Oberst',\n    15 => 'Konteradmiral',\n    16 => 'Vizeadmiral',\n    17 => 'Admiral',\n    18 => 'Flottenadmiral',\n    19 => 'Marschall',\n    20 => 'Generalissimus',\n  ],\n];"
  },
  {
    "path": "language/de/tech.mo.php",
    "content": "<?php\n\n/*\n#############################################################################\n#  Filename: tech.mo\n#  Project: SuperNova.WS\n#  Website: http://www.supernova.ws\n#  Description: Massen-Mehrspieler-Online-Browser-Weltraumstrategiespiel\n#\n#  Copyright © 2009-2018 Gorlum für Projekt \"SuperNova.WS\"\n#############################################################################\n*/\n\n/**\n*\n* @package language\n* @system [Deutsch]\n* @version 46d0\n*\n*/\n\n/**\n* NICHT ÄNDERN\n*/\n\nif (!defined('INSIDE')) die();\n\n\n$a_lang_array = (array(\n  'tech_storage_max' => 'Lagerkapazität',\n  'tech_storage' => 'Auf Lager',\n  'tech_storage_energy' => 'Verbrauch',\n  'tech_storage_energy_max' => 'Produktion',\n  'tech_storage_energy_fullness' => 'Auslastung',\n  'Tech' => 'Technologie',\n  'Requirements' => 'Voraussetzungen',\n  'No_requirements' => 'Keine Voraussetzungen',\n  'Metal' => 'Metall',\n  'Crystal' => 'Kristall',\n  'Deuterium' => 'Deuterium',\n  'Energy' => 'Energie',\n  'dark_matter' => 'Dunkle Materie',\n  'ds' => 'Nachrichten',\n  'Message' => 'Nachrichten',\n  'level' => 'Stufe',\n  'treeinfo' => '[i]',\n  'comingsoon' => 'Demnächst',\n  'te_dt_tx_pre' => 'Schwache Förderung',\n  'tech_fullness' => 'Füllstand',\n\n  'type_mission' => array(\n    MT_ATTACK => 'Angriff',\n    MT_AKS => 'Gemeinsamer Angriff',\n    MT_TRANSPORT => 'Transport',\n    MT_RELOCATE => 'Verlegung',\n    MT_HOLD => 'Halten',\n    MT_SPY => 'Spionage',\n    MT_COLONIZE => 'Kolonisierung',\n    MT_RECYCLE => 'Recyceln',\n    MT_DESTROY => 'Zerstörung',\n    MT_MISSILE => 'Raketenangriff',\n    MT_EXPLORE => 'Expedition',\n  ),\n  'fleet_events' => array(\n    EVENT_FLEET_NONE   => 'Kein Ereignis',\n    EVENT_FLEET_ARRIVE => 'Ankunft',\n    EVENT_FLEET_STAY   => 'Halten/Expedition',\n    EVENT_FLEET_RETURN => 'Rückkehr',\n  ),\n\n  'tech' => array(\n    UNIT_STRUCTURES => 'Gebäude',\n    STRUC_MINE_METAL => 'Metallmine',\n    STRUC_MINE_CRYSTAL => 'Kristallsynthesizer',\n    STRUC_MINE_DEUTERIUM => 'Deuteriumsynthesizer',\n    STRUC_MINE_SOLAR => 'Solarkraftwerk',\n    STRUC_MINE_FUSION => 'Fusionskraftwerk',\n    STRUC_FACTORY_ROBOT => 'Roboterfabrik',\n    STRUC_FACTORY_NANO => 'Nanofabrik',\n    STRUC_FACTORY_HANGAR => 'Werft',\n    STRUC_STORE_METAL => 'Metalllager',\n    STRUC_STORE_CRYSTAL => 'Kristalllager',\n    STRUC_STORE_DEUTERIUM => 'Deuteriumtank',\n    STRUC_LABORATORY => 'Labor',\n    STRUC_TERRAFORMER => 'Terraformer',\n    STRUC_ALLY_DEPOSIT => 'Allianzlager',\n    STRUC_LABORATORY_NANO => 'Nanolabor',\n\n    UNIT_STRUCTURES_SPECIAL => 'Mondgebäude',\n    STRUC_MOON_STATION => 'Mondbasis',\n    STRUC_MOON_PHALANX => 'Sensorphalanx',\n    STRUC_MOON_GATE => 'Sprungtor',\n    STRUC_SILO => 'Raketensilo',\n\n    UNIT_TECHNOLOGIES => 'Technologien',\n    TECH_ENERGY => 'Energietechnik',\n    TECH_COMPUTER => 'Computertechnik',\n    TECH_ARMOR => 'Raumschiffpanzerung',\n    TECH_WEAPON => 'Waffentechnik',\n    TECH_SHIELD => 'Schildtechnik',\n    TECH_ENGINE_CHEMICAL => 'Verbrennungsantrieb',\n    TECH_ENGINE_ION => 'Ionenantrieb',\n    TECH_ENGINE_HYPER => 'Hyperraumantrieb',\n    TECH_LASER => 'Lasertechnik',\n    TECH_ION => 'Ionentechnik',\n    TECH_PLASMA => 'Plasmatechnik',\n    TECH_HYPERSPACE => 'Hyperraumtechnik',\n    TECH_SPY => 'Spionagetechnik',\n    TECH_EXPEDITION => 'Expeditionstechnik',\n    TECH_COLONIZATION => 'Kolonialisierungstechnik',\n    TECH_ASTROTECH => 'Astrokartographie',\n    TECH_GRAVITON => 'Gravitontechnik',\n    TECH_RESEARCH => 'Forschungsnetzwerk',\n\n    UNIT_SHIPS               => 'Flotte',\n    SHIP_SATTELITE_SOLAR     => 'Solarsatellit',\n    SHIP_SPY                 => 'Spionagesonde',\n    SHIP_CARGO_SMALL         => 'Kleiner Transporter',\n    SHIP_CARGO_BIG           => 'Großer Transporter',\n    SHIP_CARGO_SUPER         => 'Supertransporter',\n    SHIP_CARGO_HYPER         => 'Hypertransporter',\n    SHIP_RECYCLER            => 'Recycler',\n    SHIP_COLONIZER           => 'Kolonisierungsschiff',\n    SHIP_SMALL_FIGHTER_LIGHT => 'Leichter Jäger',\n    SHIP_SMALL_FIGHTER_HEAVY => 'Schwerer Jäger',\n    SHIP_MEDIUM_DESTROYER    => 'Zerstörer',\n    SHIP_LARGE_CRUISER       => 'Kreuzer',\n    SHIP_LARGE_BOMBER        => 'Bomber',\n    SHIP_LARGE_BATTLESHIP    => 'Schlachtschiff',\n    SHIP_LARGE_DESTRUCTOR    => 'Zerstörer',\n    SHIP_HUGE_DEATH_STAR     => 'Todesstern',\n    SHIP_HUGE_SUPERNOVA      => 'Kreuzer der Klasse &quot;SuperNova&quot;',\n\n    UNIT_DEFENCE => 'Verteidigung',\n    UNIT_DEF_TURRET_MISSILE => 'Raketenwerfer',\n    UNIT_DEF_TURRET_LASER_SMALL => 'Leichtes Lasergeschütz',\n    UNIT_DEF_TURRET_LASER_BIG => 'Schweres Lasergeschütz',\n    UNIT_DEF_TURRET_GAUSS => 'Gaußkanone',\n    UNIT_DEF_TURRET_ION => 'Ionengeschütz',\n    UNIT_DEF_TURRET_PLASMA => 'Plasmawerfer',\n    UNIT_DEF_SHIELD_SMALL => 'Kleine Schildkuppel',\n    UNIT_DEF_SHIELD_BIG => 'Große Schildkuppel',\n    UNIT_DEF_SHIELD_PLANET => 'Planetarer Schild',\n    UNIT_DEF_MISSILE_INTERCEPTOR => 'Abfangrakete',\n    UNIT_DEF_MISSILE_INTERPLANET => 'Interplanetarrakete',\n\n    UNIT_MERCENARIES => 'Söldner',\n    MRC_STOCKMAN => 'Cargo-Meister',\n    MRC_SPY => 'Spion',\n    MRC_ACADEMIC => 'Akademiker',\n    MRC_ADMIRAL => 'Admiral',\n    MRC_COORDINATOR => 'Koordinator',\n    MRC_NAVIGATOR => 'Navigator',\n\n    UNIT_GOVERNORS => 'Gouverneure',\n    MRC_TECHNOLOGIST => 'Technologe',\n    MRC_ENGINEER => 'Ingenieur',\n    MRC_FORTIFIER => 'Festungsbauer',\n\n    UNIT_RESOURCES => 'Ressourcen',\n    RES_METAL => 'Metall',\n    RES_CRYSTAL => 'Kristall',\n    RES_DEUTERIUM => 'Deuterium',\n    RES_ENERGY => 'Energie',\n    RES_DARK_MATTER => 'Dunkle Materie',\n    RES_METAMATTER => 'Metamaterie',\n\n    UNIT_ARTIFACTS     => 'Artefakte',\n    ART_LHC            => 'Großer Hadronen-Speicherring',\n    ART_HOOK_SMALL     => 'Kleiner Haken',\n    ART_HOOK_MEDIUM    => 'Mittlerer Haken',\n    ART_HOOK_LARGE     => 'Großer Haken',\n    ART_RCD_SMALL      => 'Kleiner AKK',\n    ART_RCD_MEDIUM     => 'Mittlerer AKK',\n    ART_RCD_LARGE      => 'Großer AKK',\n    ART_HEURISTIC_CHIP => 'Heuristischer Chip',\n    ART_NANO_BUILDER   => 'Nano-Builder',\n    ART_DENSITY_CHANGER => 'Transmutationsmatrix',\n\n    UNIT_PLANS => 'Baupläne',\n    UNIT_PLAN_STRUC_MINE_FUSION => 'Bauplan &quot;Fusionskraftwerk&quot;',\n    UNIT_PLAN_SHIP_CARGO_SUPER => 'Bauplan &quot;Supertransporter&quot;',\n    UNIT_PLAN_SHIP_CARGO_HYPER => 'Bauplan &quot;Hypertransporter&quot;',\n    UNIT_PLAN_SHIP_DEATH_STAR => 'Bauplan &quot;Todesstern&quot;',\n    UNIT_PLAN_SHIP_SUPERNOVA => 'Bauplan Kreuzer &quot;SuperNova&quot;',\n    UNIT_PLAN_DEF_SHIELD_PLANET => 'Bauplan &quot;Planetarer Schild&quot;',\n\n    UNIT_PREMIUM => 'Premium',\n    UNIT_CAPTAIN => 'Kapitän',\n\n    UNIT_PLANET_DENSITY => 'Dichte',\n\n    UNIT_CAN_NOT_BE_BUILD => 'Einheit kann nicht vom Spieler gebaut werden',\n  ),\n\n  'tech_short' => array(\n    UNIT_STRUCTURES          => 'Gebäude',\n    STRUC_MINE_METAL         => 'Metallmine',\n    STRUC_MINE_CRYSTAL       => 'KristSyn',\n    STRUC_MINE_DEUTERIUM     => 'DeutSyn',\n    STRUC_MINE_SOLAR         => 'SolarKS',\n    STRUC_MINE_FUSION        => 'FusionsKS',\n    STRUC_FACTORY_ROBOT      => 'RoboFab',\n    STRUC_FACTORY_NANO       => 'NanoFab',\n    STRUC_FACTORY_HANGAR     => 'Werft',\n    STRUC_STORE_METAL        => 'MetLager',\n    STRUC_STORE_CRYSTAL      => 'KrisLager',\n    STRUC_STORE_DEUTERIUM    => 'DeutLager',\n    STRUC_LABORATORY         => 'Labor',\n    STRUC_TERRAFORMER        => 'Terra',\n    STRUC_ALLY_DEPOSIT       => 'AllianzL',\n    STRUC_LABORATORY_NANO    => 'NanoLab',\n    UNIT_STRUCTURES_SPECIAL  => 'Spezialgebäude',\n    STRUC_MOON_STATION       => 'MondBasis',\n    STRUC_MOON_PHALANX       => 'Phalanx',\n    STRUC_MOON_GATE          => 'Tor',\n    STRUC_SILO               => 'RakSilo',\n    UNIT_TECHNOLOGIES        => 'Technologien',\n    TECH_ENERGY              => 'EnergieTech',\n    TECH_COMPUTER            => 'CompTech',\n    TECH_ARMOR               => 'Panzerung',\n    TECH_WEAPON              => 'Waffen',\n    TECH_SHIELD              => 'Schilde',\n    TECH_ENGINE_CHEMICAL     => 'Verbrenner',\n    TECH_ENGINE_ION          => 'IonenAnt',\n    TECH_ENGINE_HYPER        => 'HyperAnt',\n    TECH_LASER               => 'LaserTech',\n    TECH_ION                 => 'IonenTech',\n    TECH_PLASMA              => 'PlasmaTech',\n    TECH_HYPERSPACE          => 'HyperTech',\n    TECH_SPY                 => 'Spionage',\n    TECH_ASTROTECH           => 'AstroTech',\n    TECH_GRAVITON            => 'GravTech',\n    TECH_RESEARCH            => 'ForschNetz',\n\n    UNIT_SHIPS               => 'Flotte',\n    SHIP_SATTELITE_SOLAR     => 'SolarSat',\n    SHIP_SPY                 => 'SpionSon',\n    SHIP_CARGO_SMALL         => 'KlTransp',\n    SHIP_CARGO_BIG           => 'GrTransp',\n    SHIP_CARGO_SUPER         => 'SuperTransp',\n    SHIP_CARGO_HYPER         => 'HyperTransp',\n    SHIP_RECYCLER            => 'Recycler',\n    SHIP_COLONIZER           => 'Koloni',\n    SHIP_SMALL_FIGHTER_LIGHT => 'LeJäger',\n    SHIP_SMALL_FIGHTER_HEAVY => 'SchwJäger',\n    SHIP_MEDIUM_DESTROYER    => 'Zerstörer',\n    SHIP_LARGE_CRUISER       => 'Kreuzer',\n    SHIP_LARGE_BOMBER        => 'Bomber',\n    SHIP_LARGE_BATTLESHIP    => 'Schlachtsch',\n    SHIP_LARGE_DESTRUCTOR    => 'Zerstörer',\n    SHIP_HUGE_DEATH_STAR     => 'TodStern',\n    SHIP_HUGE_SUPERNOVA      => 'SN',\n\n    UNIT_DEFENCE                 => 'Verteidigung',\n    UNIT_DEF_TURRET_MISSILE      => 'Raketen',\n    UNIT_DEF_TURRET_LASER_SMALL  => 'LeLaser',\n    UNIT_DEF_TURRET_LASER_BIG    => 'SchwLaser',\n    UNIT_DEF_TURRET_GAUSS        => 'Gauß',\n    UNIT_DEF_TURRET_ION          => 'Ionen',\n    UNIT_DEF_TURRET_PLASMA       => 'Plasma',\n    UNIT_DEF_SHIELD_SMALL        => 'KlSchild',\n    UNIT_DEF_SHIELD_BIG          => 'GrSchild',\n    UNIT_DEF_SHIELD_PLANET       => 'PlanSchild',\n    UNIT_DEF_MISSILE_INTERCEPTOR => 'Abfang',\n    UNIT_DEF_MISSILE_INTERPLANET => 'IPR',\n\n    UNIT_RESOURCES => 'Ress',\n    RES_METAL => 'Met',\n    RES_CRYSTAL => 'Kris',\n    RES_DEUTERIUM => 'Deut',\n    RES_ENERGY => 'Ener',\n    RES_DARK_MATTER => 'DM',\n    RES_METAMATTER => 'MM',\n\n    UNIT_CAN_NOT_BE_BUILD => 'Nicht baubar',\n  ),\n));"
  },
  {
    "path": "language/de/universe.mo.php",
    "content": "<?php\n\n/*\n#############################################################################\n#  Filename: universe.mo\n#  Project: SuperNova.WS\n#  Website: http://www.supernova.ws\n#  Description: Massen-Mehrspieler-Online-Browser-Weltraumstrategiespiel\n#\n#  Copyright © 2009-2018 Gorlum für Projekt \"SuperNova.WS\"\n#############################################################################\n*/\n\n/**\n*\n* @package language\n* @system [Deutsch]\n* @version 46d0\n*\n*/\n\n/**\n* NICHT ÄNDERN\n*/\n\nif (!defined('INSIDE')) die();\n\n\n$a_lang_array = (array(\n  'Galaxy' => 'Galaxie',\n  'Solar_system' => 'Sonnensystem',\n  'Show' => 'Anzeigen',\n  'vacation_shortcut' => 'U',\n  'banned_shortcut' => 'G',\n  'active_shortcut' => '*',\n  'inactif_7_shortcut' => 'i',\n  'inactif_28_shortcut' => 'I',\n  'strong_player_shortcut' => 'S',\n  'weak_player_shortcut' => 'N',\n  'uni_protected_player_shortcut' => 'n',\n  'Legend' => 'Legende',\n  'Strong_player' => 'Starker Spieler',\n  'Weak_player' => 'Schwacher Spieler',\n  'Way_vacation' => 'Urlaubsmodus',\n  'Pendent_user' => 'Gesperrt',\n  'Active' => 'Aktiver Spieler',\n  'Inactive_7_days' => 'Inaktiv seit 7 Tagen',\n  'Inactive_28_days' => 'Inaktiv seit 28 Tagen',\n  'uni_protected_player' => 'Anfängerschutz',\n  'uni_legend_myplanet' => 'Meine Planeten',\n  'uni_legend_allyplanet' => 'Allianzplaneten',\n  'Solar_system_at' => 'Sonnensystem %g:%s',\n  'Activity' => 'Aktivität',\n  'Planet_info' => 'Planeteninfo',\n  'Moon_info' => 'Mondinfo',\n  'Available' => 'Verfügbar',\n  'type1' => 'Planet',\n  'type3' => 'Mond',\n  'Pos' => 'Pos',\n  'Planet' => 'Typ',\n  'Name' => 'Name',\n  'Moon' => 'Mond',\n  'Debris' => 'Trümmerfeld',\n  'caracters' => 'Eigenschaften',\n  'diameter' => 'Durchmesser',\n  'temperature' => 'Temperatur',\n  'Place' => 'Platz',\n  'unPlace' => '(außerhalb der Statistik)',\n  'State' => 'Status',\n  'Alliance' => 'Allianz',\n  'Actions' => 'Aktionen',\n  'aava' => 'Avatar',\n  'Player' => 'Spieler (Status)',\n  'AllyInfoText' => 'Allianz %n auf Platz %r mit %m Mitgliedern',\n  'Sending_fleet' => 'Flotte wird gesendet...',\n  'Sent_fleet' => 'Flotte senden...',\n  'Obtaining_data' => 'Überprüfung',\n  'Planet_info_tip' => 'Bewegen Sie den Mauszeiger über einen Planeten, um Informationen anzuzeigen und Aktionen durchzuführen',\n  'an_error_has_happened_while_it_was_sent' => 'Beim Senden ist ein Fehler aufgetreten',\n  'error_there_is_no_moon' => 'Fehler, dies ist kein Mond',\n  'error_the_player_is_under_the_protection_of_beginners' => 'Fehler, Spieler steht unter Anfängerschutz',\n  'error_the_player_is_too_strong' => 'Fehler, Spieler ist zu stark',\n  'error_the_player_is_in_way_vacation' => 'Fehler, Spieler ist im Urlaubsmodus',\n  'error_only_x_available_probes_sending' => 'Fehler, nur \"+retVals[1]+\" Spionagesonden verfügbar',\n  'error_there_are_no_available_probes_of_spying' => 'Fehler, keine Spionagesonden verfügbar',\n  'error_you_cannot_send_any_more_fleets' => 'Fehler, Sie können keine weiteren Flotten senden',\n  'error_you_do_not_have_sufficient_deuterium' => 'Fehler, nicht genug Deuterium',\n  'There_is_not_planet' => 'Dies ist kein Planet',\n  'error_there_is_no_sufficient_fuel' => 'Fehler, nicht genug Treibstoff',\n  'multialarm' => 'Multi-Alarm',\n  'gm_all' => 'Alle',\n  'gm_launch' => 'Raketenangriff starten auf',\n  'gm_send' => 'Senden',\n  'gm_target' => 'Ziel:',\n  'gal_mis_toLaunch' => 'Anzahl Raketen ',\n  'gal_mis_rest' => 'Raketen: ',\n  'gal_mis_launch' => 'START',\n  'gl_espionner' => 'Spionage',\n  'gl_mipattack' => 'Interplanetarer Angriff',\n  'gl_shortcut' => 'Zu Lesezeichen hinzufügen',\n  'gl_with' => 'mit',\n  'gl_membre' => 'Mitgliedern',\n  'gl_ally_internal' => 'Allianzinfo',\n  'gl_ally_web' => 'Allianzwebseite',\n  'gl_sendmess' => 'Nachricht senden',\n  'gl_buddy' => 'Freundesliste',\n  'gl_buddyreq' => 'Als Freund hinzufügen',\n  'gl_stats' => 'Statistik',\n  'gl_planet' => 'Planet',\n  'gl_destroyedplanet' => 'Planet zerstört',\n  'gl_phalanx' => 'Phalanx',\n  'gl_ressource' => 'Ressourcen',\n  'gl_action' => 'Mission',\n  'gs_c00' => 'Fehler, Mission nicht verfügbar im Galaxie-Interface',\n  'gs_c01' => 'Fehler, Planet nicht gefunden.',\n  'gs_c02' => 'Fehler, ungültige Koordinaten',\n  'gs_c03' => 'Fehler, Spieler ist zu schwach.',\n  'gs_c04' => 'Fehler, Spieler ist zu stark.',\n  'gs_c04k' => ' Sie haben keine Spionagesonden!',\n  'gs_c04r' => ' Sie haben keine Recycler!',\n  'gs_c05' => 'Fehler, Spieler ist im Urlaubsmodus',\n  'gs_c610' => 'Fehler, nicht genug Spionagesonden',\n  'gs_c610a' => 'Fehler, mit ',\n  'gs_c610b' => 'Fehler, nicht genug Spionagesonden.',\n  'gs_c611' => 'Fehler, nicht genug Schiffe.',\n  'gs_c612' => 'Fehler: Nicht genug Computerkapazität.',\n  'gs_c613' => 'Fehler: Nicht genug Deuterium',\n  'gs_c616' => 'Fehler, Alarm!',\n  'gs_c618' => 'Fehler, Sie können sich nicht selbst angreifen!',\n  'gs_c619' => 'Fehler',\n  'gs_c620' => 'Fehler, Sie sind im Urlaubsmodus!',\n  'gs_sending' => 'gesendet',\n  'gs_to' => 'zu Koordinaten',\n  'Sending' => 'Senden',\n  'gal_planets' => 'Besiedelte Planeten: ',\n  'gal_planetNone' => 'Keine besiedelten Planeten',\n  'gf_cntmone' => 'besiedelte Planeten',\n  'gf_cntmnone' => 'Keine besiedelten Planeten.',\n  'gf_cntmsome' => 'besiedelte Planeten.',\n  'gf_mi_title' => 'interplanetare Raketen',\n  'gf_fleetslt' => 'Flotten',\n  'gf_rc_title' => 'Recycler',\n  'gf_sp_title' => 'Spionagesonden',\n  'gf_unknowsp' => 'Unerforschter Raum - Expedition senden',\n  'phalanx_header' => 'Flotten im Flug',\n  'phalanx_noflotes' => 'Keine Flotten unterwegs',\n  'phalanx_nodeuterium' => 'Nicht genug Deuterium für die Phalanx.',\n  'phalanx_onlyformoons' => 'Dieses Gebäude ist nur auf Monden verfügbar.',\n  'phalanx_nosensoravailable' => 'Sie haben keine Phalanx.',\n  'phalanx_rangeerror' => 'Phalanx-Level zu niedrig!',\n  'phalanx_planet_destroyed' => 'Zerstörte Planeten können nicht gescannt werden!',\n  'phalanx_planet_not_exists' => 'Dieser Planet existiert nicht!',\n  'gal_sys_members' => 'Mitglieder:&nbsp;',\n  'gal_sys_hint' => '<ul><li>Bewegen Sie den Mauszeiger über \"Legende\" in der rechten oberen Ecke der Systemtabelle, um eine Erklärung der Farben, Symbole und Icons zu erhalten</li><li>Bewegen Sie den Mauszeiger über einen Planeten, Mond, Trümmerfeld oder Spielernamen/Allianznamen, um ein Kontextmenü mit weiteren Optionen anzuzeigen</li></ul>',\n  'uni_debris' => 'Trümmer',\n  'uni_need' => 'Benötigt',\n  'uni_flying' => 'Unterwegs',\n  'uni_incoming_fleets' => 'Zusammensetzung ankommender Flotten',\n  'Planets_count' => '( %n Planet(en) besiedelt )',\n\n  'uni_rename' => 'Umbenennen',\n  'uni_name' => 'Name',\n  'uni_to_name' => 'Benennen',\n  'uni_naming' => 'Benennung',\n  'uni_for' => 'für',\n  'uni_msg_error_wrong_galaxy' => 'Ungültige Galaxiennummer',\n  'uni_msg_error_wrong_system' => 'Ungültige Systemnummer',\n  'uni_msg_error_low_price' => 'Benennungspreis zu niedrig',\n  'uni_msg_error_no_dm' => 'Nicht genug DM für die Benennung',\n  'uni_name_page_hint' => \"\n    <li>Sie können einer Galaxie oder einem System einen Namen geben</li>\n    <li>Der gewählte Name ist für alle Spieler auf der \\\"Universum\\\"-Seite sichtbar</li>\n    <li>Die Benennung einer unbenannten Galaxie oder eines Systems hat einen Grundpreis, der auf der \\\"Weltkonstanten\\\"-Seite eingesehen werden kann</li>\n    <li>Sie können den Benennungspreis selbst festlegen, dieser darf jedoch nicht unter dem Grundpreis liegen</li>\n    <li>Warum den Preis erhöhen? Der nächste Spieler, der die Galaxie oder das System umbenennen möchte, muss Ihren Preis plus den Grundpreis zahlen. So schützt eine höhere Benennungsgebühr das Objekt vor Umbenennungen</li>\n  \",\n\n  'uni_galaxy_of' => 'Galaxie',\n  'uni_system_of' => 'System',\n  'uni_msg_admin_rename' => 'Spieler ID %d [%s] hat für %d DM %s [%s%s] umbenannt in: %s',\n\n  'uni_debris_recyclable' => 'Recycelbar',\n  'uni_debris_incoming_recyclers' => 'Unterwegs',\n  'uni_debris_on_planet' => 'Im Orbit',\n  'uni_recyclers_send' => 'Recycler senden',\n\n  'uni_colonize' => 'Kolonisierungsschiff senden, um eine Kolonie auf Position',\n\n  'uni_scan_start' => 'Scanmodus aktivieren',\n  'uni_scan_stop' => 'Scanmodus verlassen',\n));"
  },
  {
    "path": "language/en/admin.mo.php",
    "content": "<?php\n\n/*\n#############################################################################\n#  Filename: admin.mo\n#  Project: SuperNova.WS\n#  Website: http://www.supernova.ws\n#  Description: Massive Multiplayer Online Browser Space Strategy Game\n#\n#  Copyright © 2009-2018 Gorlum for Project \"SuperNova.WS\"\n#  Copyright © 2008 Aleksandar Spasojevic <spalekg@gmail.com>\n#  Copyright © 2005 - 2008 KGsystem\n#############################################################################\n*/\n\n/**\n*\n* @package language\n* @system [English]\n* @version 45d0\n*\n*/\n\n/**\n* DO NOT CHANGE\n*/\n\nif (!defined('INSIDE')) die();\n\n\n$a_lang_array = (array(\n  'menu_admin_ally' => 'Alliances',\n\n  'adm_tool_md5_header' => 'Password encoding (MD5)',\n  'adm_tool_md5_hash' => 'MD5 hash',\n  'adm_tool_md5_encode' => '[ Encode ]',\n  'adm_tool_md5_generate' => '[ Generate ]',\n\n  'adm_tool_sql_page_header' => 'SQL server parameters',\n\n  'adm_tool_sql_server_version' => 'Server version',\n  'adm_tool_sql_client_version' => 'Libriary version',\n  'adm_tool_sql_host_info' => 'OS communication method',\n\n  'adm_tool_sql_table' => array(\n    'server' => array(\n      'TABLE_HEADER'  => 'SQL server',\n      'COLUMN_NAME_1' => 'Parameter',\n      'COLUMN_NAME_2' => 'Value',\n    ),\n\n    'status' => array(\n      'TABLE_HEADER'  => 'SQL status',\n      'COLUMN_NAME_1' => 'Parameter',\n      'COLUMN_NAME_2' => 'Value',\n    ),\n\n    'params' => array(\n      'TABLE_HEADER'  => 'SQL settings',\n      'COLUMN_NAME_1' => 'Parameter',\n      'COLUMN_NAME_2' => 'Value',\n    ),\n  ),\n\n  'adm_pl_image' => 'Planet image',\n  'adm_pl_fields_max' => 'Max sectors',\n  'adm_pl_fields_busy' => 'Sectors occupied',\n  'adm_pl_temp_min' => 'Minimal temperature',\n  'adm_pl_temp_max' => 'Maximal temperature',\n  'adm_pl_governor' => 'Governor',\n  'adm_pl_debris_metal' => 'Metal debris',\n  'adm_pl_debris_crystal' => 'Crystal debris',\n\n  'adm_sys_write_message' => 'Write message',\n\n  'adm_opt_user_settings' => 'User settings',\n  'adm_opt_user_birthday_gift' => 'Birthday gift',\n  'adm_opt_user_birthday_gift_disable' => '0 - disable gifts',\n  'adm_opt_user_birthday_range' => 'Retro-birthday, in days',\n  'adm_opt_user_birthday_range_hint' => 'How far in past can be user birthday for giving him gift. Obviously can not be more then 364 days',\n\n  'adm_done' => 'Successfully Completed',\n  'adm_inactive_removed' => '<li>Remove inactive players: %d</li>',\n  'adm_stat_title' => 'Update statistics',\n  'adm_maintenance_title' => 'Database Services',\n  'adm_records' => 'Processed records',\n  'adm_cleaner_title' => 'Clean queue structures',\n  'adm_cleaned' => 'Number of deleted tasks: ',\n  'adm_schedule_none' => 'There are no tasks in the schedule for now',\n  'Fix' => 'Updated',\n  'Welcome_to_Fix_section' => 'Section Patches',\n  'There_is_not_need_fix' => 'Fix unnecessary!',\n  'Fix_welldone' => 'Done!',\n  'adm_ov_title' => 'Overview',\n  'adm_ov_infos' => 'Information',\n  'adm_ov_yourv' => 'The current version',\n  'adm_ov_lastv' => 'Available version',\n  'adm_ov_here' => 'Here',\n  'adm_ov_onlin' => 'Online',\n  'adm_ov_ally' => 'Alliance',\n  'adm_ov_point' => 'Point',\n  'adm_ov_activ' => 'Active',\n  'adm_ov_count' => 'Online players',\n  'adm_ov_wrtpm' => 'Write Private Message',\n  'adm_ov_altpm' => '[PM]',\n  'adm_ov_hint' => '<ul><li>Таблица пользователей онлайн может быть отсортирована по колонкам \"ID\", \"Имя игрока\", \"Альянс\", \"Очки\" и \"Активность\". Для сортировки по определенной колонке кликните на её заголовке</li></ul>',\n\n\n  'adm_ul_title' => 'Player list',\n    'adm_ul_title_online' => 'Players online',\n  'adm_ul_time_registered' => 'Register date',\n  'adm_ul_time_played' => 'Last played',\n  'adm_ul_time_banned' => 'Banned until',\n  'adm_ul_referral' => 'Referrals',\n  'adm_ul_players' => 'Players',\n  'adm_ul_dms' => 'DM',\n  'adm_sys_actions' => 'Actions',\n\n\n  'adm_ul_bana' => 'Ban',\n  'adm_ul_detai' => 'Details',\n  'adm_ul_actio' => 'Actions',\n  'adm_ul_playe' => ' Players',\n  'adm_ul_yes' => 'Yes',\n  'adm_ul_no' => 'No',\n  'adm_pl_title' => 'Active planet',\n  'adm_pl_activ' => 'Active planet',\n  'adm_pl_name' => 'Planet name',\n  'adm_pl_posit' => 'Coordinates',\n  'adm_pl_point' => 'Value',\n  'adm_pl_since' => 'Is Active',\n  'adm_pl_they' => 'Total',\n  'adm_pl_apla' => 'Planet(s)',\n  'adm_am_plid' => 'Planet ID',\n  'adm_am_done' => 'Add was successful',\n  'adm_am_ttle' => 'Add resources',\n  'adm_am_add' => 'Confirm',\n  'adm_am_form' => 'One-step add link form resources',\n  'adm_ban_title' => 'Banned Players',\n  'adm_bn_plto' => 'Banned Players',\n  'adm_bn_name' => 'Player name',\n  'adm_bn_reas' => 'Reason for ban',\n  'adm_bn_isvc' => 'Vacation mode',\n  'adm_bn_time' => 'Duration of Ban',\n  'adm_bn_days' => 'Days',\n  'adm_bn_hour' => 'Hours',\n  'adm_bn_mins' => 'Mins',\n  'adm_bn_secs' => 'Seconds',\n  'adm_bn_bnbt' => 'The ban',\n  'adm_bn_thpl' => 'Player',\n  'adm_bn_isbn' => 'Successfully Banned!',\n  'adm_bn_vctn' => ' Vacation mode.',\n  'adm_bn_errr' => 'Error: Banning player! perhaps Name %s not found.',\n  'adm_bn_err2' => 'Error: Unable to stop production on the planets!',\n  'adm_bn_plnt' => 'Production on the planets is disabled.',\n  'adm_ban_msg_issued_date' => 'banned player on',\n  'adm_unbn_ttle' => 'Unban',\n  'adm_unbn_plto' => 'Unban player',\n  'adm_unbn_name' => 'Name',\n  'adm_unbn_bnbt' => 'Unban',\n  'adm_unbn_thpl' => 'Player',\n  'adm_unbn_isbn' => 'Unbanned!',\n  'adm_rz_ttle' => 'Zeroing Universe',\n  'adm_rz_done' => 'User(s) of transfer(s)',\n  'adm_rz_conf' => 'Confirmation',\n  'adm_rz_text' => 'Clicking (reset) You delete all database. You did backup??? Accounts will not be removed...',\n  'adm_rz_doit' => 'Zero out',\n  'adm_ch_ttle' => 'Admin chat',\n  'adm_ch_list' => 'Message list',\n  'adm_ch_clear' => 'Clear',\n  'adm_ch_idmsg' => 'ID',\n  'adm_ch_delet' => 'Delete',\n  'adm_ch_play' => 'Player',\n  'adm_ch_time' => 'Date',\n  'adm_ch_chat' => 'Chat',\n  'adm_ch_nbs' => 'Total messages...',\n  'adm_er_ttle' => 'Log records',\n  'adm_er_clear' => 'Clear',\n  'adm_er_idmsg' => 'ID',\n  'adm_er_type' => '[Code] Title',\n  'adm_er_play' => 'Player',\n  'adm_er_time' => 'Date',\n  'adm_er_page' => 'Address of the page',\n  'adm_er_nbs' => 'Total log records:',\n  'adm_er_text' => 'Log record',\n  'adm_er_bktr' => 'Debugging information',\n\n\n\n  'adm_dm_title' => 'Change the number of dark matter',\n  'adm_dm_planet' => 'ID, Coordinates or name of the planet',\n  'adm_dm_oruser' => 'Or',\n  'adm_dm_user' => 'ID or username',\n  'adm_or_large' => 'OR',\n  'adm_dm_no_quant' => 'Specify amount of Dark Matter (negative - for removal)',\n  'adm_dm_no_dest' => 'Specify the player ID or name to change Dark Matter',\n  'adm_dm_add_err' => 'It look like during charging Dark Matter error occured.',\n  'adm_dm_user_none' => 'Error: could not find user with ID or name \"%s\"',\n  'adm_dm_user_added' => 'Dark Matter on user [%2$d] \"%1$s\" succesfully changed by %3$s DM',\n  'adm_dm_user_conflict' => 'Error locating user: looks like the Database is the user and with the same name, and with the same ID',\n  'adm_dm_planet_none' => 'Error locating planet: Planet ID is not found, coordinates or name %s',\n  'adm_dm_planet_added' => 'The user ID number DM %1$d (owner of planet %4$s %2$s ID %3$d) successfully renamed to %5$d DM.',\n  'adm_dm_planet_conflict' => 'Non-unique data to search for the planet.<br>This means that the Database at the same time there is a ',\n  'adm_dm_planet_conflict_id' => 'Planet named \"%1$s\" and the planet with ID %1$s .<br>try using the coordinates of the planet.',\n  'adm_dm_planet_conflict_name' => 'Multiple planets named \"%1$s\".<br>try using coordinates or ID planet.',\n  'adm_dm_planet_conflict_coords' => 'Planet named \"%1$s\" and the planet coordinates %1$s.<br>try using the ID of the planet.',\n\n  'adm_mm_change_hint' => 'User IDs searched first. If not found - name search performed',\n\n  'adm_apply' => 'Apply',\n  'adm_maint' => 'Servicing',\n  'adm_backup' => 'Backup',\n  'adm_tools' => 'Utilities',\n  'adm_tools_reloadConfig' => 'Recalculate configuration',\n  'adm_reason' => 'The reason for',\n  'adm_opt_title' => 'Configuration of the universe',\n  'adm_opt_game_settings' => 'Game parameters',\n  'adm_opt_game_name' => 'Universe name',\n  'adm_opt_multiaccount_enabled' => 'Enable multiaccounts',\n  'adm_opt_speed' => 'Speed',\n  'adm_opt_game_gspeed' => 'Games',\n  'adm_opt_game_fspeed' => 'Fleet',\n  'adm_opt_game_pspeed' => 'Resource',\n  'adm_opt_colonies_not_counted' => '(apart from Capital)',\n  'adm_opt_colonies_no_restrictions' => '(-1 - no restrictions)',\n  'adm_opt_game_speed_normal' => '(1&nbsp;-&nbsp;normal)',\n  'adm_opt_game_faq' => 'Link to FAQ',\n  'adm_opt_game_forum' => 'Forum address',\n  'adm_opt_game_metamatter' => 'Reference &quot;Purchase Metamatter&quot;',\n  'adm_opt_game_copyrigh' => 'Copyright',\n  'adm_opt_game_online' => 'Turn off the game. Users will see the following message:',\n  'adm_opt_game_offreaso' => 'Turn off reason',\n  'adm_opt_plan_settings' => 'Planet settings',\n  'adm_opt_plan_initial' => 'Size of start planet',\n  'adm_opt_plan_base_inc' => 'Basic production',\n  'adm_opt_game_debugmod' => 'Enable debug mode',\n  'adm_opt_game_counter' => 'Add game counter',\n  'adm_opt_geoip_whois_url' => 'WHOIS provider URL',\n  'adm_opt_geoip_whois_url_example' => '(i.e. \"http://1whois.ru/?ip=\")',\n  'adm_opt_game_oth_info' => 'Other options',\n  'adm_opt_int_news_count' => 'News count',\n  'adm_opt_int_page_imperor' => 'On the page &quot;Emperor&quot;',\n  'adm_opt_game_zero_disable' => '(0&nbsp;-&nbsp;Disable)',\n  'adm_opt_game_advertise' => 'Ad units',\n  'adm_opt_game_oth_adds' => 'Enable the ad block in the left menu. Banner code:',\n  'adm_opt_game_oth_gala' => 'Galaxy',\n  'adm_opt_game_oth_syst' => 'System',\n  'adm_opt_game_oth_plan' => 'Planet',\n  'adm_opt_btn_save' => 'Save',\n  'adm_opt_vacation_mode' => 'Turn off vacation',\n  'adm_opt_sectors' => 'Fields',\n  'adm_opt_per_hour' => 'per hour',\n  'adm_opt_saved' => 'Game settings saved successfully',\n  'adm_opt_players_online' => 'Players on the server',\n  'adm_opt_vacation_mode_is' => 'Vacation mode',\n  'adm_opt_game_status' => 'Game status',\n  'adm_opt_links' => 'Links and banners',\n  'adm_opt_universe_size' => 'Universe size',\n  'adm_opt_galaxies' => 'Galaxies',\n  'adm_opt_systems' => 'Systems',\n  'adm_opt_planets' => 'Planets',\n  'adm_opt_build_on_research' => 'Build on research',\n  'adm_opt_eco_scale_storage' => 'Scale storages with production speed',\n  'adm_opt_game_rules' => 'Game rules',\n  'adm_opt_max_colonies' => 'Number of colonies',\n  'adm_opt_exchange' => 'Exchange resources',\n  'adm_opt_game_mode' => 'Type of universe',\n  'adm_opt_chat' => 'Chat settings',\n  'adm_opt_chat_timeout' => 'Timeout for idle',\n  'adm_opt_allow_buffing' => 'Allow buffing',\n  'adm_opt_ally_help_weak' => 'Allow HOLD on weak co-ally',\n  'adm_opt_email_pm' => 'Enables sending PM to e-mail',\n  'adm_opt_player_defaults' => 'Default player setting',\n  'adm_opt_game_default_language' => 'Default language',\n  'adm_opt_game_default_skin' => 'Skin',\n  'adm_opt_game_default_template' => 'Template',\n  'adm_opt_player_change_name' => 'Player can change nickname',\n  'adm_opt_player_change_name_options' => [\n    SERVER_PLAYER_NAME_CHANGE_NONE => 'Name change is forbidden',\n    SERVER_PLAYER_NAME_CHANGE_FREE => 'Player can chane nickname',\n    SERVER_PLAYER_NAME_CHANGE_PAY  => 'Player can change nickname for DM',\n  ],\n  'adm_opt_player_change_name_cost' => 'DM cost for player to change nickname',\n  'adm_opt_empire_mercenary_temporary' => 'Temporary mercenaries',\n  'adm_opt_empire_mercenary_temporary_base' => 'Base hire period, seconds',\n  'adm_opt_empire_mercenary_temporary_hint' => 'When switching this option on all permanent Mercenaries would be converted to temporary with base active period<br />When switching this option off all active Mercenaries would be converted to permanent ones. If newly converted Mercenaries are not accessible for hiring they can not be bought but still be active and will affect game',\n  'adm_opt_experimental' => 'EXPERIMENTAL OPTIONS! USE WITH CAUTION!',\n  'adm_opt_tpl_minifier' => 'Template minifier',\n  'adm_opt_tpl_minifier_hint' => 'Minifier compress templates by replacing several repetive spacechars (new line, tabulation, space) with single space. More infor about template minifier in /docs/changelog.txt',\n  'adm_lm_compensate' => 'Compensation',\n  'adm_pl_comp_title' => 'Compensation for destroyed planet',\n  'adm_pl_comp_src' => 'Destroy the planet',\n  'adm_pl_comp_dst' => 'Credit resources on the planet',\n  'adm_pl_comp_bonus' => 'Bonus player',\n  'adm_pl_comp_check' => 'Check',\n  'adm_pl_comp_confirm' => 'Confirm',\n  'adm_pl_comp_done' => 'Finish',\n  'adm_pl_comp_price' => 'Cost structures',\n  'adm_pl_comp_got' => 'Be enrolled',\n  'adm_pl_com_of_plr' => 'Player',\n  'adm_pl_comp_will_be' => 'will be',\n  'adm_pl_comp_destr' => 'destroyed.',\n  'adm_pl_comp_recieve' => 'The specified number of resources',\n  'adm_pl_comp_recieve2' => 'enrolled on the planet',\n  'adm_pl_comp_err_0' => 'Not found to be destroyed planet',\n  'adm_pl_comp_err_1' => 'Planet destroyed',\n  'adm_pl_comp_err_2' => 'Not found, the planet you want to enroll',\n  'adm_pl_comp_err_3' => 'From the planets different owners. Credit resources can only be the same player on the planet',\n  'adm_pl_comp_err_4' => 'Planet belongs to the specified player',\n  'adm_pl_comp_err_5' => 'Planet for -- and for credit resources match',\n  'adm_ver_versions' => 'Version of server components',\n  'adm_ver_version_sn' => 'Version',\n  'adm_ver_version_db' => 'Database version',\n  'adm_update_force' => 'Force Update',\n  'adm_update_repeat' => 'Repeat last system update',\n  'adm_ptl_test' => 'phpBB Template Engine test',\n  'adm_counter_recalc' => 'Recalc `counter` table',\n  'adm_lm_planet_edit' => 'Edit planet',\n  'adm_planet_edit' => 'Edit planet',\n  'adm_planet_id' => 'Planet ID',\n  'adm_name' => 'Name',\n  'adm_planet_change' => 'Change',\n  'adm_planet_parent' => 'Parent Planet',\n  'adm_planet_active' => 'Active Planets',\n  'adm_planet_edit_hint' => '<ul>    <li>Entering planet ID and pressing \"Confirm\" on empty page will print info about planet: type, name, coordinates and current amount of units/resources of    selected type</li>    <li>To remove units/resources from planet enter negative value</li>  </ul>',\n  'adm_planet_list_title' => 'Planet List',\n  'adm_sys_owner' => 'Owner',\n  'adm_sys_owner_id' => 'Onwer ID',\n  'addm_title' => 'Add Moon',\n  'addm_addform' => 'Form new moon',\n  'addm_playerid' => 'ID world accommodation',\n  'addm_moonname' => 'The name of the Moon',\n  'addm_moongala' => 'Specify the Galaxy',\n  'addm_moonsyst' => 'Specify system',\n  'addm_moonplan' => 'Specify position',\n  'addm_moondoit' => 'Add',\n  'addm_done' => 'The Moon formed',\n  'adm_usr_level' => array(\n    '0' => 'Player',\n    '1' => 'Operator',\n    '2' => 'Moderator',\n    '3' => 'Administrator',\n  ),\n\n  'adm_usr_genre' => array(\n    GENDER_UNKNOWN => 'Not set',\n    GENDER_MALE => 'Male',\n    GENDER_FEMALE => 'Female',\n  ),\n\n  'panel_mainttl' => 'Admin Panel',\n  'adm_panel_mnu' => 'Search player',\n  'adm_panel_ttl' => 'Type of search',\n  'adm_search_pl' => 'Search by name',\n  'adm_search_ip' => 'Search by IP',\n  'adm_stat_play' => 'Player statistics',\n  'adm_mod_level' => 'Access level',\n  'adm_player_nm' => 'Player name',\n  'adm_ip' => 'IP',\n  'adm_plyer_wip' => 'Players with IP',\n  'adm_frm1_id' => 'ID',\n  'adm_frm1_name' => 'Name',\n  'adm_frm1_ip' => 'IP',\n  'adm_frm1_mail' => 'E-Mail',\n  'adm_frm1_acc' => 'Rank',\n  'adm_frm1_gen' => 'Gender',\n  'adm_frm1_main' => 'Planet ID',\n  'adm_frm1_gpos' => 'Coordinates',\n  'adm_mess_lvl1' => 'Access level',\n  'adm_mess_lvl2' => '&quot;now&quot; ',\n  'adm_colony' => 'Colony',\n  'adm_planet' => 'Planet',\n  'adm_moon' => 'Moon',\n  'adm_technos' => 'Technology',\n  'adm_bt_search' => 'Search',\n  'adm_bt_change' => 'Change',\n  'flt_id' => 'ID',\n  'flt_fleet' => 'Fleet',\n  'flt_ships' => 'Composition',\n  'flt_mission' => 'Mission',\n  'flt_here' => 'Back',\n  'flt_there' => 'There',\n  'flt_here_there' => 'There/Back',\n  'flt_departure' => 'Source',\n  'flt_owner' => 'Owner',\n  'flt_planet' => 'Planet',\n  'flt_time_return' => 'Return',\n  'flt_e_owner' => 'Destination',\n  'flt_time_arrive' => 'Arrival',\n  'flt_staying' => 'Stay',\n  'flt_action' => 'Action',\n  'flt_title' => 'Fleets in flight',\n  'flt_no_fleet' => 'There are no fleets in flight',\n  'mlst_title' => 'Message list',\n  'mlst_mess_del' => 'Delete messages',\n  'mlst_hdr_page' => 'Page.',\n  'mlst_hdr_title' => ' ) Messages:',\n  'mlst_hdr_prev' => '[ &lt;- ]',\n  'mlst_hdr_next' => '[ -&gt; ]',\n  'mlst_hdr_id' => 'ID',\n  'mlst_hdr_type' => 'Type',\n  'mlst_hdr_time' => 'Here',\n  'mlst_hdr_from' => 'From',\n  'mlst_hdr_to' => 'To',\n  'mlst_hdr_text' => 'Text',\n  'mlst_hdr_action' => 'Action.',\n  'mlst_del_mess' => 'Delete',\n  'mlst_bt_delsel' => 'Delete selected',\n  'mlst_bt_deldate' => 'Delete',\n  'mlst_hdr_delfrom' => 'Delete selected type messages before date',\n  'mlst_no_messages' => 'No messages',\n  'mlst_messages_deleted' => 'Deleted messages with ID(s) %s',\n  'mlst_messages_deleted_date' => 'Deleted messages with type \"%s\" before date %s (does not includes messages on indicated date)',\n\n  'adm_lng_title' => 'Localization',\n  'adm_lng_warning' => 'WARNING! Locale editor is in alpha stage! Use it on your own risk!',\n  'adm_lng_domain' => 'Domain',\n  'adm_lng_string_name' => 'String name',\n  'adm_lng_string_add' => 'Add string',\n  'adm_uni_price_galaxy' => 'Base galactic rename cost',\n  'adm_uni_price_system' => 'Base system rename cost',\n\n  'adm_opt_ver_check' => 'Version check',\n  'adm_opt_ver_check_hint' => 'Version check activated by admin by pressing button \"Check version\" below. This action is transferring only anonymous data: current DB version, release and full game version.',\n  'adm_opt_ver_check_do' => 'Check version',\n  'adm_opt_ver_check_last' => 'Last version check was performed at',\n  'adm_opt_ver_check_auto' => 'Automatic version check',\n  'adm_opt_ver_check_auto_hint' => 'You can enable automatic game version check. Data transferred to update server will be exact the same as when you use manual version check. But with automatic check engine will check version by itself once in period of time (by default - once per day). More info in documentation',\n\n  'adm_opt_ver_response' => array(\n    SNC_VER_NEVER => 'Version check was never performed',\n\n    SNC_VER_ERROR_CONNECT => 'Version check error. Game can not communicate with update server. Be sure you do not have restriction in PHP to communicate with remote servers or that you have installed CURL and activated it on PHP',\n    SNC_VER_ERROR_SERVER => 'Fatal update server error! Check - if there is newer version of game with advanced update server support. Otherwise immediatly contact developer to diagnose and fix this problem!',\n\n    SNC_VER_EXACT => 'You have latest alpha version of incoming release. Thanks for participate in testing!',\n    SNC_VER_LESS => 'You using alpha version of incoming release. However there is more recent alpha! You can update your game if you want to have fixes for errors in current version and participate in testing new features of game.',\n    SNC_VER_FUTURE => 'You have game version from future! Immediatly contact develpoer and pass him this version! Also be prpeated to Time Militias visit for broking space-time continuum and violating laws of causality...',\n\n    SNC_VER_RELEASE_EXACT => 'You have most recent version of latest release',\n    SNC_VER_RELEASE_MINOR => 'Your game version is outdated - there is new version of your release. Most luckely it contains some bugfixes to current release. It is recomeded to update your game.',\n    SNC_VER_RELEASE_MAJOR => 'You have very outdated game version - there is available new release. It is provide new features and bugfixes. Please update your game version!',\n    SNC_VER_RELEASE_ALPHA => 'You have most recent version of latest release. However there is alpha version of next release. May be you would like to explore and test new features of upcoming release?',\n\n    SNC_VER_MAINTENANCE => 'Update server currently turned off for maintenance. Please check again later',\n    SNC_VER_UNKNOWN_RESPONSE => 'Update server gives unknown response. In most ases it means that there is new game version with more advanced update server support. Also it can mean error in update server. Please check and update your game or contact developer to diagnose and fix problem.',\n    SNC_VER_INVALID => 'I just can not understand which version of game you have. Please contact developer to diagnose and fix this problem.',\n    SNC_VER_STRANGE => 'You should not see this message. If you saw this message - something going terrible wrong. Please contact developer to diagnose and fix this problem.',\n\n    SNC_VER_REGISTER_UNREGISTERED => 'Your server still not registered',\n    SNC_VER_REGISTER_ERROR_MULTISERVER => 'Error - your server has multiply registration entries! Contact developer for diagnose and fix problem',\n    SNC_VER_REGISTER_ERROR_REGISTERED => 'Error - your server already registered! Check your key and ID in server configuration.',\n    SNC_VER_REGISTER_ERROR_NO_NAME => 'Error - no server name supplied! You should define your server name to register.',\n    SNC_VER_REGISTER_ERROR_WRONG_URL => 'Error - wrong URL! Passed string is not a correct URL. If you tried to registered server from localhost you should be awared that update server did not work with local servers.',\n    SNC_VER_REGISTER_REGISTERED => 'Your site sucesfully registered',\n\n    SNC_VER_ERROR_INCOMPLETE_REQUEST => 'Error - missed correct ID or key in request! Check your key and ID in server configuration.',\n    SNC_VER_ERROR_UNKNOWN_KEY => 'Error - unknown key! Passed key did not found in update server DB. Check your key in server configuration.',\n    SNC_VER_ERROR_MISSMATCH_KEY_ID => 'Error - passed key did not match server ID! Check your key and ID in server configuration.',\n  ),\n\n  'adm_opt_ver_response_short' => array(\n    SNC_VER_NEVER => 'Never made',\n\n    SNC_VER_ERROR_CONNECT => 'Error connect',\n    SNC_VER_ERROR_SERVER => 'Server error',\n\n    SNC_VER_EXACT => 'Latest alpha',\n    SNC_VER_LESS => 'Old alpha',\n    SNC_VER_FUTURE => 'Future alpha',\n\n    SNC_VER_RELEASE_EXACT => 'Freshest release',\n    SNC_VER_RELEASE_MINOR => 'Update recomended',\n    SNC_VER_RELEASE_MAJOR => 'Update mandatory',\n    SNC_VER_RELEASE_ALPHA => 'Fresh release',\n\n    SNC_VER_MAINTENANCE => 'Maintenance',\n    SNC_VER_UNKNOWN_RESPONSE => 'Unknown response',\n    SNC_VER_INVALID => 'Version error',\n    SNC_VER_STRANGE => 'Unpredictable shit',\n\n    SNC_VER_REGISTER_UNREGISTERED => 'Not registered',\n    SNC_VER_REGISTER_ERROR_MULTISERVER => 'Multiregistration',\n    SNC_VER_REGISTER_ERROR_REGISTERED => 'Key error',\n    SNC_VER_REGISTER_ERROR_NO_NAME => 'Server name error',\n    SNC_VER_REGISTER_REGISTERED => 'Registered',\n\n    SNC_VER_ERROR_INCOMPLETE_REQUEST => 'ID or key error',\n    SNC_VER_ERROR_UNKNOWN_KEY => 'Unknown key',\n    SNC_VER_ERROR_MISSMATCH_KEY_ID => 'Key not match ID',\n  ),\n\n  'adm_upd_register' => 'Server registration',\n  'adm_upd_register_hint' => '\n    Server registration need for specials queries to update server. When you register there are passed required minimum for unique server identification:\n    <ul>\n      <li>Full server URL - i.e. http://myserver.com/myfolder/. It is necessary to distinguish several servers that shares same IP or domain</li>\n      <li>Internal server name. Used in server messages.</li>\n    </ul>\n    Why would you like to register? In future there are many features planned wich would be acessed only to registered servers. Short list of planned features:\n    <ul>\n     <li>Automatic changelog refresh</li>\n     <li>Automatic game update</li>\n     <li>Listing in server ratings</li>\n     <li>Bugreport and ticket system</li>\n     <li>Chat for server admins</li>\n     <li>Remote server diagnostic - at request</li>\n     <li>...and many others</li>\n    </ul>\n    Why would you like to register NOW?\n    <ul>\n      <li>Requests of registered server administrators are prioritized for developers.</li>\n      <li>Upon registration each server assigned uniq ID which will be used for basic server sorting. It means that servers with lesser ID (i.e. those who registered first) would be higher in server list...</li>\n    </ul>\n  ',\n  'adm_upd_register_do' => 'Register server',\n  'adm_upd_register_already' => 'You already registered your server. Write down server ID and key and store it in safe place!',\n  'adm_upd_register_id' => 'Registration ID',\n  'adm_upd_register_key' => 'Registration key',\n\n  'adm_opt_stats_and_records' => 'Statistics and records',\n  'adm_opt_stats_hide_admins' => 'Hide admins',\n  'adm_opt_stats_hide_admins_detail' => 'Will be hidden all accounts with authlevel > 0',\n  'adm_opt_stats_hide_player_list' => 'Hide players',\n  'adm_opt_stats_hide_player_list_detail' => 'List of hidden players ID separated with comma',\n  'adm_opt_stats_schedule' => 'Statistics update schedule',\n  'adm_opt_stats_schedule_detail' => 'Format: \"[YYYY:[MM:[DD:[HH:[MM:[SS]]]]]][,(...)]\"<br />\n    Zero left parameters is optional<br />\n    Empty right parameters counts as zeros<br />\n    Examples:<br />\n     - \"00:00:27:00\" means \"run every hour at 27 minutes\";<br />\n     - \"04::\" - \"run at 4am every day\";<br />\n     - \"02::,17:00\" - \"run at 2am every day AND each hour at 17 minutes\";<br />\n     - \"1:4:30:00\" - \"run every 1st of each month at 04:30am\" etc',\n  'adm_opt_stats_hide_pm_link' => 'Hide PM links',\n\n  'adm_pay' => 'Payments',\n  'adm_pay_stats' => 'Payments Stats',\n  'adm_pay_th_payer' => 'Payer',\n  'adm_pay_th_payer_id' => 'ID',\n  'adm_pay_th_payer_name' => 'Name',\n  'adm_pay_th_payment' => 'Payment',\n  'adm_pay_th_payment_id' => 'ID',\n  'adm_pay_th_payment_date' => 'Date',\n  'adm_pay_th_payment_status' => 'Status',\n  'adm_pay_th_payment_amount' => 'Amount',\n  'adm_pay_th_payment_currency' => 'Currency',\n  'adm_pay_th_mm_paid' => 'Paid for',\n  'adm_pay_th_mm_gained' => 'Gained',\n  'adm_pay_th_module' => 'Payment system',\n  'adm_pay_th_module_name' => 'Type',\n\n  'adm_pay_filter_all' => '-- All --',\n  'adm_pay_filter_status' => array(\n    PAYMENT_STATUS_ALL => '-- All --',\n    PAYMENT_STATUS_NONE => 'Not finished',\n    PAYMENT_STATUS_COMPLETE => 'Finished',\n  ),\n  'adm_pay_filter_test' => array(\n    PAYMENT_TEST_ALL => '-- All --',\n    PAYMENT_TEST_REAL => 'Real',\n    PAYMENT_TEST_PROBE => 'Test',\n  ),\n  'adm_pay_filter_stat' => array(\n    PAYMENT_FILTER_STAT_NORMAL => '-- None --',\n    PAYMENT_FILTER_STAT_MONTH => 'By months',\n    PAYMENT_FILTER_STAT_YEAR => 'By years',\n    PAYMENT_FILTER_STAT_ALL => 'All time',\n  ),\n\n  'adm_user_stat' => 'Статистика пользователей',\n  'adm_user_online' => 'Онлайн с %s по %s',\n\n  'adm_ban_unban' => 'Бан/Разбан',\n  'adm_metametter_payment' => 'ММ & Платежи',\n\n  'adm_stat_already_started' => 'Statistics already updated just now',\n\n  'adm_dm_change_hint' => 'User IDs searched first. If not found - name search performed',\n\n  'adm_matter_change_log_record' => 'Through admin interface for user {%1$d} %2$s by user {%3$s} %4$s reason \"%5$s\"',\n\n  'adm_game_status' => 'Current game status',\n\n  'adm_log_delete_update_info' => 'Delete info about maintenance and updates of stats, DB and engine',\n\n  'admin_ptl_test_la_' => \"Single'Double\\\"Zero\\0End\",\n\n  'admin_title_access_denied' => 'Access denied',\n\n  'adm_player' => 'Player',\n  'adm_planets' => 'Planets',\n));\n"
  },
  {
    "path": "language/en/affilates.mo.php",
    "content": "<?php\n\n/*\n#############################################################################\n#  Filename: affilates.mo\n#  Project: SuperNova.WS\n#  Website: http://www.supernova.ws\n#  Description: Massive Multiplayer Online Browser Space Strategy Game\n#\n#  Copyright © 2009-2018 Gorlum for Project \"SuperNova.WS\"\n#  Copyright © 2008 Aleksandar Spasojevic <spalekg@gmail.com>\n#  Copyright © 2005 - 2008 KGsystem\n#############################################################################\n*/\n\n/**\n*\n* @package language\n* @system [English]\n* @version 43a16.13\n*\n*/\n\n/**\n* DO NOT CHANGE\n*/\n\n!defined('INSIDE') && die();\n\n$a_lang_array = (array(\n  'aff_title' => 'Affiliate program',\n  'aff_text1' => 'Place a personal link, banner or userbar on forum or site and each coming under the link will be your Invited. You will have 1 DM for every',\n  'aff_text2' => 'DM earned by Invited.',\n  'aff_text3' => 'Bonus starts when your Invited will gain',\n  'aff_link' => 'Personal link partner program',\n  'aff_link_direct' => 'Direct link',\n  'aff_link_bb' => 'BBCode for placing personal references to Forum',\n  'aff_link_html' => 'HTML code to place personal links on a Web page',\n  'aff_banner' => 'Banner 416x58',\n  'aff_banner_bb' => 'BBCode for placing a banner on the Forum',\n  'aff_banner_html' => 'HTML code to place a banner on a Web page',\n  'aff_userbar' => 'Userbar 350x19',\n  'aff_userbar_bb' => 'BBCode for userbar Forum',\n  'aff_userbar_html' => 'HTML code to place on the Web page userbar',\n  'aff_list' => 'List of invited players',\n  'aff_none' => 'Not Invited',\n  'aff_gained' => 'DM earned player',\n  'aff_your_bonus' => 'Your bonus',\n));\n"
  },
  {
    "path": "language/en/alliance.mo.php",
    "content": "<?php\n\n/*\n#############################################################################\n#  Filename: alliance.mo\n#  Project: SuperNova.WS\n#  Website: http://www.supernova.ws\n#  Description: Massive Multiplayer Online Browser Space Strategy Game\n#\n#  Copyright © 2009-2018 Gorlum for Project \"SuperNova.WS\"\n#  Copyright © 2009 Gorlum for Project \"SuperNova.WS\"\n#  Copyright © 2008 Aleksandar Spasojevic <spalekg@gmail.com>\n#  Copyright © 2005 - 2008 KGsystem\n#############################################################################\n*/\n\n/**\n*\n* @package language\n* @system [English]\n* @version 45d0\n*\n*/\n\n/**\n* DO NOT CHANGE\n*/\n\nif (!defined('INSIDE')) die();\n\n\n$a_lang_array = [\n  'ali_dip_title' => 'Diplomacy',\n  'ali_dip_negotiate' => 'Negotiations',\n  'ali_adm_msg_subject' => 'Alliance Maillist',\n  'ali_dip_offers_your' => 'Your offers',\n  'ali_dip_offers_to_you' => 'Offers to you',\n  'ali_dip_offer_none' => 'No offers',\n  'ali_dip_offer' => 'Offer',\n  'ali_dip_offers' => 'Offers',\n  'ali_dip_offer_new' => 'Start negotiations',\n  'ali_dip_offer_to_ally' => 'Offer to Alliance',\n  'ali_dip_offer_make' => 'Start negotiations',\n  'ali_dip_offer_answer' => 'Alliance declined your offer',\n  'ali_dip_offer_deny_reason' => 'You declared offer',\n  'ali_dip_offer_to' => 'To Alliance',\n  'ali_dip_offer_from' => 'From Alliance',\n  'ali_dip_offer_deny' => 'Declare offer',\n  'ali_dip_offer_accept' => 'Accept offer',\n  'ali_dip_offer_delete' => 'Withdraw offer',\n  'ali_dip_err_no_ally' => 'There is no such Alliance',\n  'ali_dip_err_same_ally' => 'You can not start negotiation with your\\'s Alliance',\n  'ali_dip_err_wrong_offer' => 'You can not make THIS offer',\n  'ali_dip_err_offer_none' => 'No such offer',\n  'ali_dip_err_offer_same' => 'You already have relation %s with this Alliance',\n  'ali_dip_err_offer_alien' => 'This offer was made not for you!',\n  'ali_dip_err_offer_accept_own' => 'You can not accept own offer!',\n  'ali_dip_err_offer_empty' => 'Offer not defined',\n  'ali_dip_relation_none' => 'No relations',\n  'ali_dip_relation_change_auto_accept' => 'Alliance \"%1$s\" changes his relations to Alliance \"%2$s\" to \"%3$s\"',\n  'ali_dip_relation_change_own' => 'We accept offer of Alliance \"%2$s\" to change relations to \"%3$s\"',\n  'ali_dip_relation_change_other' => 'Alliance \"%1$s\" accepted our offer to change relations to \"%3$s\"',\n  'ali_dip_relations' => [\n    ALLY_DIPLOMACY_NEUTRAL => 'Neutral',\n    ALLY_DIPLOMACY_WAR => 'War',\n    ALLY_DIPLOMACY_PEACE => 'Peace',\n    ALLY_DIPLOMACY_CONFEDERATION => 'Confederation',\n    ALLY_DIPLOMACY_FEDERATION => 'Federation',\n    ALLY_DIPLOMACY_UNION => 'Union',\n    ALLY_DIPLOMACY_MASTER => 'Master',\n    ALLY_DIPLOMACY_SLAVE => 'Slave',\n  ],\n\n  'ali_lessThen15min' => '&lt; 15 min',\n  'ali_confirm' => 'Confirm',\n  'ali_confirmation' => 'Confirmation',\n  'ali_adm_disband' => 'To Dissolve The Alliance',\n  'ali_adm_options' => 'Configuring Alliance',\n  'ali_adm_transfer' => 'Send Alliance player',\n  'ali_adm_return' => 'Return to management Alliance',\n  'ali_adm_kick' => 'Exclude the player from Alliance',\n  'ali_adm_kick_confirm' => 'Are you sure you want to exclude the player from Alliance?',\n  'ali_adm_requests' => 'Applications',\n  'ali_adm_newLeader' => 'SELECT PLAYER',\n  'ali_adm_lastRank' => 'You cannot delete the only rank!',\n  'ali_adm_rights_title' => 'Setting permissions',\n  'ali_adm_rights_rank_new' => 'New rank',\n  'ali_adm_rights_rank_delete' => 'Delete rank',\n  'ali_adm_rights_rank_none' => 'No ranks',\n  'ali_adm_rights_rank_name' => 'Rank',\n  'ali_adm_rights_mass_mail' => 'Message to the entire Alliance',\n  'ali_adm_rights_view_online' => 'View the online status of Members',\n  'ali_adm_rights_helper' => 'Assistant Head (to pass the required rank founder)',\n  'ali_adm_rights_legend' => 'Law Alliance',\n  'ali_leaderRank' => 'Leader',\n  'ali_defaultRankName' => 'Rookie',\n  'ali_make_title' => 'Establishment Of The Alliance',\n  'ali_make_tag_length' => '(from 3 to 8 characters)',\n  'ali_make_name_length' => '(up to 35 characters)',\n  'ali_make_confirm' => 'Create Alliance',\n  'ali_req_cancel' => 'Remove application',\n  'ali_req_candidate' => 'Candidate',\n  'ali_req_characters' => 'Characters',\n  'ali_req_date' => 'Filing date',\n  'ali_req_deny_msg' => 'Your application for membership in the Alliance [%s] was rejected by.<br>Reason for refusal: \"%s\".<br>You can remove the application and try again later or enter into another Alliance.',\n  'ali_req_deny_admin' => '<font color=red>Request already rejected</font>. However, until you have deleted an entry, you may change your mind',\n  'ali_req_deny_reason' => 'Your request for membership is rejected',\n  'ali_req_emptyList' => 'No requests for review',\n  'ali_req_inAlly' => 'You are already a member of the Alliance.',\n  'ali_req_make' => 'Apply now',\n  'ali_req_not_allowed' => 'NO RECEPTION',\n  'ali_req_otherRequest' => 'You have already submitted an application to the other Alliance.',\n  'ali_req_template' => 'Please accept me in your Alliance',\n  'ali_req_text' => 'Application text',\n  'ali_req_title' => 'Enroll in the Alliance',\n  'ali_req_waiting' => 'Your application for membership in the Alliance [%s] will be sent to the head of the Alliance.<br>You will be automatically notified of the decision.',\n  'ali_req_check' => 'Application management',\n  'ali_req_requestCount' => 'Applications',\n  'ali_req_admin_title' => 'Review of applications',\n  'ali_req_accept' => 'Accept the application',\n  'ali_req_deny' => 'Reject the application',\n  'ali_search_title' => 'Search Alliance',\n  'ali_search_action' => 'Search',\n  'ali_search_tip' => 'Search can be performed on the part of the name or tag of the Alliance',\n  'ali_search_result_none' => 'No items found matching your search for Alliances.',\n  'ali_search_show_all' => 'List and statistics for all Alliances',\n  'ali_sys_name' => 'The Name',\n  'ali_sys_tag' => 'Tag',\n  'ali_sys_members' => 'Members',\n  'ali_sys_notFound' => 'The Alliance does not exist',\n  'ali_sys_memberName' => 'Member Name',\n  'ali_sys_points' => 'Points',\n  'ali_sys_lastActive' => 'Activity',\n  'ali_sys_totalMembers' => 'Total',\n  'ali_sys_clear' => 'Reset',\n  'ali_sys_main_page' => 'Return to the home page of the Alliance',\n  'ali_sys_joined' => 'Date of entry',\n  'ali_frm_write' => 'Write in the Forum',\n  'ali_info_title' => 'Information about the Alliance',\n  'ali_info_internal' => 'Internal information',\n  'ali_info_leave' => 'Leave Alliance',\n  'ali_info_bonus_rate' => 'Bonus rate',\n  'Name' => 'The Name',\n  'Tag' => 'Tag',\n  'Members' => 'Members',\n  'Accept_cand' => 'Accept',\n  'alliance' => 'Alliance',\n  'alliances' => 'Alliances',\n  'Alliance_information' => 'Information about the Alliance',\n  'Alliance_logo' => 'Alliance Logo',\n  'alliance_tag' => 'Alliance Tag',\n  'Allow_request' => 'Accept applications',\n  'allyance_name' => 'Alliance Name',\n  'ally_admin' => 'Alliance Management',\n  'ally_been_maked' => 'Alliance %s successfully created',\n  'ally_description' => 'Alliance Description',\n  'ally_dissolve' => 'Delete Alliance',\n  'Ally_info_1' => 'Information about the Alliance',\n  'ally_maked' => '%s created',\n  'Ally_nodescription' => 'The Alliance has no description',\n  'ally_notexist' => 'Alliance no longer exists',\n  'Ally_not_exist' => 'Unfortunately there is no information about the Alliance',\n  'Ally_transfer' => 'Transfer Alliance',\n  'All_players' => 'All players',\n  'always_exist' => '%s already exists',\n  'Aplication_acepted' => 'You have taken',\n  'Aplication_hello' => 'Welcome<br>Alliance:',\n  'Aplication_rejected' => 'Your application for membership in the Alliance was rejected.<br>The Reason:<br>',\n  'apply_cantbeadded' => 'Request failed, try again!',\n  'apply_registered' => 'Your request has been sent.<br><br><a href=alliance.php>Back</a>',\n  'Back' => 'Back',\n  'Canceld_req_text' => 'You have cancelled for [%s]',\n  'Change' => 'Change',\n  'ch_allyname' => 'Change Alliance Name',\n  'ch_allytag' => 'Change Alliance Tag',\n  'Circular_message' => 'Circular Alliance',\n  'Circular_sended' => 'Message sent successfully',\n  'Clear' => 'Clear',\n  'Click_writerequest' => 'Click here to write an application',\n  'Continue' => 'Continue',\n  'Delete_apply' => 'Reject the application',\n  'Denied_access' => 'Access forbidden!',\n  'Destiny' => 'Recipient',\n  'Exit_of_this_alliance' => 'Exit Alliance',\n  'External_text' => 'External text',\n  'Founder' => 'Founder',\n  'Founder_name' => 'Founder Name',\n  'Function' => 'Function',\n  'Go_out_welldone' => 'You have successfully left the Alliance',\n  'have_not_name' => 'Type a name for the Alliance',\n  'have_not_tag' => 'Type the Tag of the Alliance',\n  'Help' => 'Assistance',\n  'Inactive' => 'Inactive',\n  'Inner_section' => 'Inner text',\n  'Internal_text' => 'Inner text',\n  'knowed_allys' => 'Current Alliances',\n  'laws_config' => 'Setting permissions',\n  'Main_Page' => 'Home page',\n  'make_alliance' => 'Establishment Of The Alliance',\n  'make_alliance_owner' => 'Create Alliance',\n  'max' => 'Max.',\n  'member' => 'Member',\n  'memberlist_view' => 'View a list of members',\n  'members' => 'Participants',\n  'members_admin' => 'Managing your members',\n  'Members_list' => 'List of members',\n  'members_who_recived_message' => 'The following members of the Alliance received a message:',\n  'Message' => 'Message',\n  'Motive_optional' => 'Reason (optional)',\n  'New_name' => 'New Alliance Name',\n  'New_tag' => 'New Tag',\n  'not_allow_request' => 'Reject the application',\n  'Novate' => 'Rookie',\n  'Number' => 'Number',\n  'Off' => 'Offline',\n  'Ok' => 'Ok',\n  'On' => 'Online',\n  'Online' => 'Status',\n  'Options' => 'Options',\n  'Position' => 'Status',\n  'Public_text_of_alliance' => 'External text',\n  'Range' => 'Rank',\n  'Reject_cand' => 'Reject',\n  'Reload' => 'Example',\n  'Repel' => 'Repel',\n  'requests_view' => 'Viewing applications',\n  'Request_answer' => 'Request rejected',\n  'Request_date' => 'Request date',\n  'Request_text' => 'Application text',\n  's' => '[N/A]',\n  'Search' => 'Search',\n  'searchd_ally_avail' => 'Found Alliances:',\n  'search_alliance' => 'Search',\n  'Send' => 'Send',\n  'Send_Apply' => 'To accept the application',\n  'Send_circular_mail' => 'Send a message to the entire Alliance',\n  'Set_range' => 'Change in rank',\n  'Show_of_request_text' => 'Application text',\n  'Texts' => 'Editing text',\n  'Text_mail' => 'Send a message to the entire Alliance',\n  'top10alliance' => 'Top 10 Alliances',\n  'transfer' => 'Transfer',\n  'transfer_ally' => 'Transfer Alliance',\n  'transfer_to' => 'Send Alliance player:',\n  'Want_go_out' => 'Are you sure you want to leave the Alliance?',\n  'write_apply' => 'Apply now',\n  'your_alliance' => 'Your Alliance',\n  'your_apply' => 'Your application',\n  'ali_info_leave_success' => '',\n\n\n  'opt_avatar' => 'Logo',\n  'opt_avatar_remove' => 'Remove logo',\n  'opt_avatar_search' => 'Search in Google',\n  'opt_upload' => 'Upload',\n\n  'opt_msg_avatar_removed' => 'Logo succesfully removed',\n  'opt_msg_avatar_uploaded' => 'Logo succesfully changed',\n  'opt_msg_avatar_error_delete' => 'Error deleting logo file. Please, contact server Administration',\n  'opt_msg_avatar_error_writing' => 'Error saving logo file. Please, contact server Administration',\n  'opt_msg_avatar_error_upload' => 'Error loading logo image %1. Please, contact server Administration',\n  'opt_msg_avatar_error_unsupported' => 'Uploaded image format not supported. Only supported JPG, GIF, PNG up to 200KB',\n\n  'ali_admin_mercenaries' => 'Alliance\\'s mercenaries',\n  'ali_admin_plans' => 'Alliance\\'s schematics',\n  'ali_admin_techs' => 'Alliance\\'s research',\n  'ali_admin_market_trader' => 'Black Market resource exchange',\n\n  'ali_res_player_bonus' => 'Member\\'s bonus',\n  'ali_res_transfer' => 'Transfer',\n  'ali_res_transfer_long' => 'Transfer to Alliance',\n  'ali_res_no_resources' => 'Alliance has no resources',\n  'ali_res_transfer_dm_log' => 'Member \\'%s\\' transferred %d DM to account of Alliance [%s]',\n\n  'ali_res_err_not_enough' => 'Ypu don\\'t have enough %s!',\n  'ali_res_err_wrong_unit' => 'You can only transfer resources to Ally!',\n\n  'ali_res_alliance_bonus' => 'Alliance\\'s Bonuses',\n  'ali_res_alliance_bonus_players' => 'Minimal members count to obtain bonus',\n\n  'ally_message_tag_exists' => 'There is already exists Alliance with tag [%1$s]',\n  'ally_message_name_exists' => 'There is already exists Alliance with name \"%1$s\"',\n\n  'ally_alliances_recommended' => 'Recommended Alliances',\n  'ally_recommended_diff' => 'Points diff',\n  'ally_recommended_rates' => 'Rate diff',\n  'ali_search_result_tip' => '\n    <li>Click on the name or tag of the Alliance that would see information about it</li>\n    <li>Click \"Enter\" to send a request to join</li>\n    <li>Колонка \"Разница в очках\" указывает на разницу между количеством очков у игрока и средним количеством очков на одного игрока в Альянсе. Если она отрицательная - средний игрок в Альянсе имеет больше очков, чем вы</li>\n    <li>Колонка \"Рейт\" указывает на соотношение между количеством очков у игрока и средним количеством очков на одного игрока в Альянсе. Если она меньше единицы - средний игрок в Альянсе слабее, чем вы</li>\n    <li>Рекомендуется по возможности выбирать Альянс с небольшой разницей в очках и рейте в пределах от 0,75 до 1,3. Однако, финальное решение всегда остаётся за игроком</li>\n   ',\n\n];\n"
  },
  {
    "path": "language/en/announce.mo.php",
    "content": "<?php\n\n/*\n#############################################################################\n#  Filename: announce.mo\n#  Project: SuperNova.WS\n#  Website: http://www.supernova.ws\n#  Description: Massive Multiplayer Online Browser Space Strategy Game\n#\n#  Copyright © 2009-2018 Gorlum for Project \"SuperNova.WS\"\n#  Copyright © 2008 Aleksandar Spasojevic <spalekg@gmail.com>\n#  Copyright © 2005 - 2008 KGsystem\n#############################################################################\n*/\n\n/**\n*\n* @package language\n* @system [English]\n* @version 43a16.13\n*\n*/\n\n/**\n* DO NOT CHANGE\n*/\n\nif (!defined('INSIDE')) die();\n\n$a_lang_array = (array(\n  'add_announce' => 'Add announcement',\n  'metal' => 'Metal',\n  'crystal' => 'Crystal',\n  'deuterium' => 'Deuterium',\n  'Resources_to_be_sold' => 'Proposed resources',\n  'Desired_resources' => 'Required resources',\n  'send' => 'Send',\n  'Your_announce_was_recorded' => 'Your ad successfully sent',\n  'return_to_announce' => 'Return to page ads',\n  'Classifieds' => 'Published ads',\n  'Action' => 'Action',\n  'Galaxy' => 'Galaxy',\n  'Solar_system' => 'The solar system',\n  'Infos_of_delivery' => 'Information dealer',\n  'Salesman' => 'Player',\n  'Delete' => 'Delete',\n  'announce_status' => 'Ad status',\n  'Your_announce_not_recorded' => 'Your ad was not sent',\n  'Your_announce_was_deleted' => 'Your announcement was successfully removed',\n));\n"
  },
  {
    "path": "language/en/artifacts.mo.php",
    "content": "<?php\n\n/*\n#############################################################################\n#  Filename: artifacts.mo\n#  Project: SuperNova.WS\n#  Website: http://www.supernova.ws\n#  Description: Massive Multiplayer Online Browser Space Strategy Game\n#\n#  Copyright © 2011-2018 Gorlum for Project \"SuperNova.WS\"\n#############################################################################\n*/\n\n/**\n*\n* @package language\n* @system [English]\n* @version 43a16.13\n*\n*/\n\n/**\n* DO NOT CHANGE\n*/\n\nif (!defined('INSIDE')) die();\n\n$a_lang_array = (array(\n  'art_use'             => 'Use artifact',\n\n  'art_lhc_from'        => 'Large Hadron Collider',\n  'art_lhc_subj'        => 'Creating moon...',\n  'art_moon_create'   => array(\n    ART_LHC => 'LHC\\'s gravity wave connects large pieces of debris resulting to creation new moon %s on coordinates %s!',\n    ART_HOOK_SMALL => 'Small Hook launches new moon %1$s diameter %3$s kilometers on coordinates %2$s!',\n    ART_HOOK_MEDIUM => 'Medium Hook launches new moon %1$s diameter %3$s kilometers on coordinates %2$s!',\n    ART_HOOK_LARGE => 'Large Hook launches new moon %1$s diameter %3$s kilometers on coordinates %2$s!',\n  ),\n  'art_moon_exists' => 'There is already moon on moon orbit on current coordinates',\n  'art_lhc_moon_fail'   => 'Unfortunatly LHC\\'s gravity wave was not enough to create a new moon',\n\n  'art_rcd_from'        => 'Rapid Colony Deployer',\n  'art_rcd_subj'        => 'Colony deployed',\n  'art_rcd_ok'          => '%1$s succesfully deployed colony on planet  %2$s coordinates %3$s',\n  'art_rcd_err_moon'    => 'RCD can be deployed on planet',\n  'art_rcd_err_no_sense'=> 'RCD detected that there will be no improvement to current buildings and aborted deployment',\n  'art_rcd_err_que'     => 'RCD can not be deployed on planet where building ongoing. Cancel all construction tasks and try to deploy RCD again',\n\n  'art_heurestic_chip_ok' => 'Decreased research time for %s level %d down by %s',\n  'art_heurestic_chip_subj' => 'Research speed up',\n  'art_heurestic_chip_no_research' => 'There is no research currently ongoing or there is less then 1 minute on current research',\n\n  'art_nano_builder_ok' => '%s time of \"%s\" level %d on planet %s %s decreased down by %s',\n  'art_nano_builder_build' => 'Building',\n  'art_nano_builder_destroy' => 'Destruction',\n  'art_nano_builder_subj' => 'Building operation speed up',\n  'art_nano_builder_no_que' => 'There is no construction operation performed on planet or current construction time is less then 1 minute',\n\n  'art_err_no_artifact' => 'You did not have this artifact',\n\n  'art_page_hint'       => '<ul>\n    <li>Artifacts are rare objects with unique properties</li>\n    <li>Artifacts are expendables i.e. after use Artifact disappears</li>\n    <li>Some Artifacts too powerfull that can exist only within limit numbers in one Empire</li>\n    <li>Usually Artifact effect extends only on planet where it used but some of them has Empire-wide effect.\n    Rarest and powerfullest Artifacts extends their effect to whole solar system, galaxy or even Universe!</li>\n  </ul>',\n));\n"
  },
  {
    "path": "language/en/buddy.mo.php",
    "content": "<?php\n\n/*\n#############################################################################\n#  Filename: buddy.mo\n#  Project: SuperNova.WS\n#  Website: http://www.supernova.ws\n#  Description: Massive Multiplayer Online Browser Space Strategy Game\n#\n#  Copyright © 2009-2018 Gorlum for Project \"SuperNova.WS\"\n#  Copyright © 2008 Aleksandar Spasojevic <spalekg@gmail.com>\n#  Copyright © 2005 - 2008 KGsystem\n#############################################################################\n*/\n\n/**\n*\n* @package language\n* @system [English]\n* @version 43a16.13\n*\n*/\n\n/**\n* DO NOT CHANGE\n*/\n\nif (!defined('INSIDE')) die();\n\n$a_lang_array = (array(\n  'buddy_buddies' => 'Friends',\n  'buddy_request_text' => 'Request text',\n  'buddy_request_text_default' => 'Please add me to your friendlist',\n  'buddy_request_none' => 'There are no friends and friendship requests',\n  'buddy_request_write_header' => 'New friendship request',\n  'buddy_request_player_name' => 'Player name',\n  'buddy_request_accept' => 'Add player to friendlist',\n\n  'buddy_status' => 'Status',\n  'buddy_status_active' => 'It is your mutuall friend',\n  'buddy_status_incoming_waiting' => 'Incoming friending request',\n  'buddy_status_incoming_denied' => 'You deny friending request',\n  'buddy_status_outcoming_waiting' => 'You request sent. Wait for answer',\n  'buddy_status_outcoming_denied' => 'You request denied',\n\n  // Result messages\n  'buddy_err_not_exist' => 'Request does not exists. Perhaps it was deleted or denied',\n\n  'buddy_err_accept_own' => 'You can not accept own request',\n  'buddy_err_accept_alien' => 'You can not accept request which wasn\\'t sent to you',\n  'buddy_err_accept_already' => 'You already accepted this request and already in friends with this player',\n  'buddy_err_accept_denied' => 'You already denied this request and can not accept it',\n  'buddy_err_accept_internal' => 'There is error while accepting request. Try again later. If error stil persists - please contact server administration',\n  'buddy_err_accept_none' => 'Friendship request granted',\n\n  'buddy_err_delete_alien' => 'This request was made not by you and not for you! Do not interfere in other people relations! Find you own friends!',\n  'buddy_err_unfriend_none' => 'You broke friendship',\n  'buddy_err_delete_own' => 'You request was deleted',\n\n  'buddy_err_deny_none' => 'You deny friendship from player. Why, o why?!',\n\n  'buddy_err_adding_exists' => 'You can not then request to this player - you already friends or there is exists some friendship request between yours',\n  'buddy_err_adding_none' => 'You friendship request was sent',\n  'buddy_err_adding_self' => 'You can not send friendship request to yourself',\n\n  // PM messages\n  'buddy_msg_accept_title' => 'You have new friend!',\n  'buddy_msg_accept_text' => 'Player %s added you to friendlist!',\n  'buddy_msg_unfriend_title' => 'You lost friend!',\n  'buddy_msg_unfriend_text' => 'Player %s broke friendship and removed you from friendlist. How sad...',\n  'buddy_msg_deny_title' => 'Unable to make a new friend',\n  'buddy_msg_deny_text' => 'Player %s denied your friendship offer',\n  'buddy_msg_adding_title' => 'Friendship offer',\n  'buddy_msg_adding_text' => 'Player %s offers you his friendship',\n\n  'buddy_hint' => '\n    <li>Send offer friendship through a menu item <a href=\"search.php\">Search</a>.</li>\n    <li>You can see the status of your friends online or offline. However, and your friends can see your status. Consider this fact before to accept a request on friendship.</li>\n    <li>If you denied friendship request you can not start any friendship relation with this player until he deletes his offer</li>',\n\n));\n"
  },
  {
    "path": "language/en/buildings.mo.php",
    "content": "<?php\n\n/*\n#############################################################################\n#  Filename: buildings.mo\n#  Project: SuperNova.WS\n#  Website: http://www.supernova.ws\n#  Description: Massive Multiplayer Online Browser Space Strategy Game\n#\n#  Copyright © 2009-2018 Gorlum for Project \"SuperNova.WS\"\n#  Copyright © 2008 Aleksandar Spasojevic <spalekg@gmail.com>\n#  Copyright © 2005 - 2008 KGsystem\n#############################################################################\n*/\n\n/**\n*\n* @package language\n* @system [English]\n* @version 45d0\n*\n*/\n\n/**\n* DO NOT CHANGE\n*/\n\nif (!defined('INSIDE')) die();\n\n\n$a_lang_array = array(\n  'built' => 'Built',\n  'Fleet' => 'Fleet',\n  'fleet' => 'Fleet',\n  'Defense' => 'Defence',\n  'defense' => 'Defence',\n  'Research' => 'Research',\n  'level' => 'Level',\n  'dispo' => 'Photos',\n  'load_det' => 'Click on image to view 3D model',\n  'off_det' => 'Press again to show off the 3D model',\n  'allowed_aya' => 'Available',\n  'allowed_ye' => 'Available',\n  'allowed_yi' => 'Available',\n  'mech_info' => 'Technical Specifications',\n  'fst_bld_load' => 'Order processing.<br>Please wait...',\n  'fst_bld' => 'Fast build :',\n  'price' => 'Price',\n  'builds' => 'Buildings',\n  'destroy_price' => 'Destroy Price',\n  'no_fields' => 'No fields on planet',\n  'can_build' => 'You can build: ',\n  'Requirements' => 'You Must: ',\n  'Requires' => 'Necessary Resources ',\n  'Rest_ress' => 'will resources ',\n  'Rest_ress_fleet' => 'The visiting fleet',\n  'Rechercher' => 'Explore',\n  'ConstructionTime' => 'Construction time ',\n  'DestructionTime' => 'Destruction time ',\n  'ResearchTime' => 'Research Time ',\n  'Construire' => 'Build',\n  'BuildFirstLevel' => 'Build',\n  'BuildNextLevel' => 'Build next level ',\n  'completed' => 'Completed',\n  'in_working' => 'Busy',\n  'work_todo' => 'Busy',\n  'total_left_time' => 'Remaining time',\n  'only_one' => 'You can only build one shield.',\n  'b_no_silo_space' => 'Missile silo is full.',\n  'Build_lab' => 'Building Error',\n  'NoMoreSpace' => 'Planet filled!',\n  'InBuildQueue' => 'Building Queue',\n  'bld_usedcells' => 'Built Fields',\n  'bld_theyare' => 'Left',\n  'bld_cellfree' => 'Free Fields',\n  'DelFromQueue' => 'Cancel',\n  'DelFirstQueue' => 'Suspend',\n  'cancel' => 'Cancel',\n  'continue' => 'Continue',\n  'ready' => 'Please wait',\n  'destroy' => 'Delete',\n  'on' => 'at',\n  'attention' => 'Attention! Attempted hacking! was fixed!',\n  'no_laboratory' => 'Research Lab is not built!',\n  'need_hangar' => 'Shipyard not built!',\n  'labo_on_update' => 'Lab busy!',\n  'fleet_on_update' => 'Shipyard busy!',\n  'Total_techs' => 'Total Techs',\n  'eco_bld_page_hint' => '<li>To view information about an individual Item, simply move your mouse over the picture\n  <li>Click on the image will select the unit. Repeated clicks on the same choice lift redeemed in units\n  <li>Detailed desription of the unit and its characteristics can be found by clicking on the blue icon &quot;i&quot; in the circle\n  <li>Build an defence, you can either click on the plus button in the upper-right corner or clicking &quot;Build&quot; in the description\n  <li>Destroy the building you can click on the minus sign in the upper left corner of the image, either on the appropriate link from the description',\n  'eco_price' => 'Price',\n  'eco_left' => 'Balance',\n\n  'eco_bld_resources_not_enough' => 'Lack the resources to build units ordered',\n  'eco_bld_msg_err_research_in_progress' => 'Scientists of empire already researching other technology',\n  'eco_bld_msg_err_not_research' => 'Can not research in laboratory other than technology',\n  'eco_bld_msg_err_requirements_not_meet' => 'Requirements for technology research not met',\n  'eco_bld_msg_err_laboratory_upgrading' => 'Laboratories currently upgrading and can not research anything<br><br>When you build/upgrade Laboratory or Nanolaboratory (even if they currently just in building que) on any of your planet you can not research Tech',\n\n  'eco_bld_unit_info_extra_show' => 'Показать дополнительную информацию',\n  'eco_bld_unit_info_extra_hide' => 'Спрятать дополнительную информацию',\n  'eco_bld_unit_info_extra_none' => 'Нет дополнительной информации',\n\n  'eco_bld_autoconvert' => 'Autoconvert',\n  'eco_bld_autoconvert_explain' => 'Недостающие на постройку/исследование ресурсы будут автоматически сконвертированы из наличных ресурсов (металл, кристалл, дейтерий), а затем постройка/исследование будет поставлена в очередь.\\r\\n\\r\\n',\n  'eco_bld_autoconvert_dark_matter_none' => 'Для постройки с автоконвертацией не хватает {0} Тёмной Материи.',\n  'eco_bld_autoconvert_confirm' => 'Эта операция будет стоить {0} Тёмной Материи.\\r\\n\\r\\nПродолжать?',\n\n  'eco_que_clear_dialog_title' => 'Подтвердите очистку очереди',\n  'eco_que_clear_dialog_text' => 'Продолжение данной операции приведет к очистке всей очереди!<br /><br />Все незавершенные постройки и исследования будут отменены, а время, потраченное на постройку текущего юнита - потеряно.<br />Ресурсы будут возвращены на планету.<br /><br />Вы уверены, что хотите продолжить?',\n\n  'eco_que_artifact_dialog_title' => 'Использовать {0}',\n  'eco_que_artifact_dialog_text' => \"Для ускорения постройки/исследования текущего юнита в очереди будет использован Артефакт \\\"{0}\\\".<br /><br />Если до окончания время постройки/исследования юнита осталось больше часа - время постройки уменьшится в два раза<br />Если меньше часа - постройка/исследование будет закончено моментально<br /><br />Артефакт нельзя использовать, если оставшееся время постройки/исследования - менее одной минуты\",\n\n  'eco_bld_research_page_name' => 'Technology Research',\n  'eco_bld_research_page_novapedia' => 'All technologies in Novapedia',\n\n);\n"
  },
  {
    "path": "language/en/chat_advanced.mo.php",
    "content": "<?php\n\n/*\n#############################################################################\n#  Filename: chat_advanced.mo.php\n#  Project: SuperNova.WS\n#  Website: http://supernova.ws\n#  Description: Massive Multiplayer Online Browser Space Strategy Game\n#\n#  Copyright © 2012-2018 Gorlum for Project \"SuperNova.WS\"\n#############################################################################\n*/\n\n/**\n*\n* @package language\n* @system [English]\n* @version 45d0\n*\n*/\n\n/**\n* DO NOT CHANGE\n*/\n\nif (!defined('INSIDE')) die();\n\n//$lang = array_merge($lang,\n//$lang->merge(\n$a_lang_array = (array(\n  'chat_advanced_chat_players' => 'Игроки в чате',\n  'chat_advanced_online_players' => 'Игроков в чате',\n  'chat_advanced_online_invisibles' => 'В том числе невидимых',\n  'chat_advanced_invisibility' => 'Невидимость',\n\n  'chat_advanced_frame_on' => 'Прикрепить',\n  'chat_advanced_frame_off' => 'Открепить',\n\n  'chat_advanced_smile_tooltip' => 'Кликните, что бы выбрать смайлик',\n\n  'chat_advanced_visible' => array(\n    0 => 'Вы видимы другим игрокам',\n    1 => 'Вы невидимы другим игрокам',\n  ),\n\n  'chat_advanced_help_description' => \"Используйте синтаксис \\\"/help <команда>\\\", что бы получить дополнительную информацию об определенной команде. Например, \\\"/help whisper\\\"\",\n  'chat_advanced_help_commands_accessible' => 'Вам доступны следующие команды чата:',\n  'chat_advanced_help_command' => 'Команда \"/%s\"',\n  'chat_advanced_help_command_aliases' => 'Псевдонимы данной команды: ',\n\n  'chat_advanced_whisper_recipient_prefix' => '',\n  'chat_advanced_whisper_recipient_midfix' => ' -> ',\n  'chat_advanced_whisper_recipient_suffix' => '> ',\n  'chat_advanced_whisper_sender_prefix' => '',\n  'chat_advanced_whisper_sender_midfix' => ' -> ',\n  'chat_advanced_whisper_sender_suffix' => '> ',\n\n  'chat_advanced_command_reason' => '. Причина: %s',\n  'chat_advanced_command_reason2' => 'Причина:',\n  'chat_advanced_command_mute' => 'Игрок \"%1$s\" лишен права писать в чат до %2$s по серверному времени%3$s',\n  'chat_advanced_command_unmute' => 'Игрок \"%s\" снова может писать в чат',\n  'chat_advanced_command_ban' => 'Игрок \"%1$s\" заблокирован с режимом отпуска до %2$s по серверному времени',\n  'chat_advanced_command_ban_no_vacancy' => 'Игрок \"%1$s\" заблокирован БЕЗ РЕЖИМА ОТПУСКА до %2$s по серверному времени',\n  'chat_advanced_command_unban' => 'Игрок \"%s\" разблокирован',\n\n  'chat_advanced_command_interval' => array(\n    '1h' => '1 час',\n    '3h' => '3 часа',\n    '6h' => '6 часов',\n    '12h' => '12 часов',\n    '1d' => '1 сутки',\n    '3d' => '3 суток',\n    '1w' => '1 неделя',\n    '2w' => '2 недели',\n    '1m' => '30 суток',\n    '2m' => '60 суток',\n    '3m' => '90 суток',\n    '10y' => 'Навсегда*',\n  ),\n  'chat_advanced_ban_vacancy' => 'Режим отпуска',\n\n  'chat_advanced_online_ban' => 'Заблокировать игрока \"%1$s\" на...',\n  'chat_advanced_online_mute' => 'Запретить игроку \"%1$s\" писать в чат на...',\n  'chat_advanced_online_unmute' => 'Разрешить игроку \"%1$s\" писать в чат',\n  'chat_advanced_online_invisible' => 'Невидимый игрок',\n  'chat_advanced_online_banned_via_chat' => 'Заблокирован из чата',\n\n  'chat_advanced_help' => array(\n    'help' => \"Команда '/help' позволяет получить подробную помощь по всем доступным вам командам чата\\r\\n\n               Формат команды: /help [<имя команды>]\\r\\n\n               <Имя команды> является необязательным параметром. Без него будет распечатан список доступных команд. Вместо имени команды можно использовать её псевдоним, например '/help w' выместо '/help whisper'\",\n    'whisper' => \"Команда '/whisper' позволяет отправить приватное сообщение определенному игроку. Приватное сообщение появляется во всех видах чата - общем и Альянсовском - и видно\n                  только вам и игроку, которому оно предназначено. Клик мышкой на имени в списке игроков онлайн добавит в строку сообщения соответствующую команду - вам останется только\n                  набрать сообщение и отправить его\\r\\nПриватные сообщения можно посылать невидимым игрокам, игрокам, отсуствующим в чате и игрокам оффлайн. В двух последних случаях они увидят ваши сообщения в своей истории\\r\\n\n                  Формат команды: /whisper <имя игрока> <сообщение>\\r\\n\n                  Если имя игрока содержит пробел, апостроф, прямой или обратный слеш - возьмите имя игрока в двойные ковычки. Например, так:\\r\\n\n                  /w \\\"имя с пробелом\\\" Привет!\",\n    'ban' => \"Команда '/ban' блокирует игроку доступ в игру на указанное время. Соответствующая иконка в списке игроков онлайн блокирует игрока на 1 неделю\\r\\n\n              Формат команды: /ban id <ИД игрока> <срок блокировки>[!] [<причина блокировки>]\\r\\n\n              ИД игрока можно узнать, наведясь на его ник в списке игроков онлайн\\r\\n\n              <Срок блокировки> имеет вид: <число>{y|m|w|d|h}, где h указывает срок блокировки в часах, d - в днях, w - в неделях, m - в месяцах, y - в годах\\r\\n\n              Если после срока блокировки поставить восклицательный знак, то пользователь будет заблокирован без режима отпуска\\r\\n\n              <Причина блокировки> - не обязательный параметр. Если указана - будет добавлена в сообщение в чате и в таблицу блокировок\",\n    'unban' => \"Команда '/unban' позволяет разблокировать ранее заблокированного игрока\\r\\n\n                Формат команды: /unban id <ИД игрока>\",\n    'mute'  => \"Команда '/mute' запрещает игроку писать в чат. Запрет касается всех каналов и так же рапространяется на приватные сообщения чата. Соответствующая иконка в списке игроков онлайн запрещает игроку писать в чат на 1 час\\r\\n\n                Формат команды: /mute id <ИД игрока> <длительность запрета> [<причина запрета>]\\r\\n\n                ИД игрока можно узнать, наведясь на его ник в списке игроков онлайн\\r\\n\n                <Длительность запрета> имеет вид: <число>{y|m|w|d|h}, где h указывает длительность в часах, d - в днях, w - в неделях, m - в месяцах, y - в годах\\r\\n\n                <Причина запрета> - не обязательный параметр. Если указана - будет добавлена в сообщение в чате\",\n    'unmute' =>  \"Команда '/unmute' позволяет вернуть право писать в чат игроку, который раньше был лишен этого права\\r\\n\n                  Формат команды: /unmute id <ИД игрока>\\r\\n\n                  ИД игрока можно узнать, наведясь на его ник в списке игроков онлайн\",\n    'invisible' => \"Команда '/invisible' позволяет сделать вас невидимым для других игроков в чате. Переключение галочки \\\"Невидимость\\\" дает такой же эффект.\\r\\n\n                    Невидимость является глобальной - т.е. распространяется и на общий чат, и на чат Альянса\\r\\n\n                    Формат команды: /invisible [on|off]\\r\\n\n                    /invisible on - Делает вас невидимым для других игроков в чате\\r\\n\n                    /invisible off - Делает вас видимым для других игроков в чате\\r\\n\n                    /invisible - Показывает ваш статус невидимости\",\n  ),\n\n  'chat_advanced_help_short' => array(\n    'help' => '/help',\n    'whisper' => '/whisper',\n    'ban' => '/ban',\n    'unban' => '/unban',\n    'mute' => '/mute',\n    'unmute' => '/unmute',\n    'invisible' => '/invisible',\n  ),\n\n\n\n  'chat_advanced_err_command_inacessible' => 'Эта команда чата вам недоступна. Используйте \"/help\", что бы увидеть список всех доступных вам команд',\n  'chat_advanced_err_command_unknown' => 'Неизвестная команда',\n  'chat_advanced_err_player_name_unknown' => 'Не существует игрока с таким именем',\n  'chat_advanced_err_message_empty' => 'Пустое сообщение не будет отправлено',\n  'chat_advanced_err_message_player_empty' => 'Укажите имя игрока, которому вы хотите отправить сообщение в чате',\n  'chat_advanced_err_player_id_need' => 'Для этой команды нужно имя игрока',\n  'chat_advanced_err_player_id_incorrect' => 'Неправильный идентификатор',\n  'chat_advanced_err_player_id_unknown' => 'Не существует игрока с таким ИД',\n  'chat_advanced_err_player_same' => 'Нельзя выполнить эту команду на самом себе',\n  'chat_advanced_err_player_higher' => 'Нельзя выполнить эту команду на игроке с большим или равным уровнем доступа',\n  'chat_advanced_err_term_need' => 'Для этой команды нужен срок действия',\n  'chat_advanced_err_term_wrong' => 'Указан неправильный срок действия команды',\n));\n"
  },
  {
    "path": "language/en/fleet.mo.php",
    "content": "<?php\n\n/*\n#############################################################################\n#  Filename: fleet.mo\n#  Project: SuperNova.WS\n#  Website: http://www.supernova.ws\n#  Description: Massive Multiplayer Online Browser Space Strategy Game\n#\n#  Copyright © 2009-2018 Gorlum for Project \"SuperNova.WS\"\n#  Copyright © 2008 Aleksandar Spasojevic <spalekg@gmail.com>\n#  Copyright © 2005 - 2008 KGsystem\n#############################################################################\n*/\n\n/**\n*\n* @package language\n* @system [English]\n* @version 44a35\n*\n*/\n\n/**\n* DO NOT CHANGE\n*/\n\nif (!defined('INSIDE')) die();\n\n$a_lang_array = (array(\n  'flt_page2_title' => 'Mission selection',\n  'fl_title' => 'Fleets',\n  'fl_expttl' => 'Expedition',\n  'fl_mission' => 'Mission',\n  'fl_count' => 'Number of',\n  'fl_count_short' => 'Quantity of',\n  'fl_where' => 'Where',\n  'fl_from' => 'From',\n  'fl_from_t' => 'Return',\n  'fl_start_t' => 'Time',\n  'fl_dest' => 'Where',\n  'fl_dest_t' => 'Arrival',\n  'fl_back_t' => 'Return',\n  'fl_back_in' => 'Left',\n  'fl_order' => 'Order',\n  'fl_get_to' => '(T)',\n  'fl_get_to_ttl' => 'Roundtrip',\n  'fl_back_to' => '(O)',\n  'fl_back_to_ttl' => 'Back',\n  'fl_associate' => 'Combat Union',\n  'fl_noslotfree' => 'No fleet slots!',\n  'fl_notback' => 'Fleet cannot return!',\n  'fl_onlyyours' => 'You can only return your fleets!',\n  'fl_isback' => 'Fleet returns',\n  'fl_sback' => 'Ago',\n  'fl_error' => 'Error',\n  'fl_new_miss' => 'New task: choose ships',\n  'fl_fleet_typ' => 'Ship type',\n  'fl_fleet_disp' => 'Number of',\n  'fl_noplanetrow' => 'System error is! ))',\n  'fl_fleetspeed' => 'Speed: ',\n  'fl_selmax' => 'Max.',\n  'fl_sur' => 'from',\n  'fl_continue' => 'Further',\n  'fl_noships' => 'No ships orbiting the planet',\n  'fl_unselectall' => 'unselect all',\n  'fl_selectall' => 'Select all',\n  'fl_orbiting' => 'In orbit',\n  'fl_to_fly' => 'Send',\n  'fl_no_flying_fleets' => 'No Fleets out',\n  'fl_floten1_ttl' => 'Fleet destination',\n  'fl_noenought' => 'Little ships!',\n  'fl_speed' => 'Speed',\n  'fl_planet' => 'Planet',\n  'fl_ruins' => 'Field debris',\n  'fl_moon' => 'Moon',\n  'fl_dist' => 'Distance',\n  'fl_fltime' => 'Duration(one way)',\n  'fl_time_go' => 'Sent (time)',\n  'fl_time_back' => 'Return (time)',\n  'fl_deute_need' => 'Fuel consumption',\n  'fl_speed_max' => 'Maximum speed',\n  'fl_shortcut' => 'Notes with coordinates',\n  'fl_shortlnk' => 'Edit shortcuts',\n  'fl_shrtcup1' => '(P)',\n  'fl_shrtcup2' => '(O)',\n  'fl_shrtcup3' => '(L)',\n  'fl_planettype1' => 'Planet',\n  'fl_planettype2' => 'Field debris',\n  'fl_planettype3' => 'Moon',\n  'fl_myplanets' => 'Planet(s)',\n  'fl_nocolonies' => 'No Planets',\n  'fl_noacss' => 'No Combat Unions',\n  'fl_grattack' => 'Military Alliances',\n  'fl_allressources' => 'All resource',\n  'fl_space_left' => 'Space left',\n  'fl_attack_error' => array(\n    ATTACK_ALLOWED => 'Fleet sent successfully',\n    ATTACK_NO_TARGET => 'The specified destination does not exist',\n    ATTACK_OWN => 'cannnot attack own planet',\n    ATTACK_WRONG_MISSION => 'The task cannot be performed in the specified destination',\n    ATTACK_NO_ALLY_DEPOSIT => 'No alliance depot on planet',\n    ATTACK_NO_DEBRIS => 'No debris field here',\n    ATTACK_VACATION => 'You cannot attack a player in Vacation Mode',\n    ATTACK_SAME_IP => 'Cannot trade with players with same IP!<br>Interaction with players with the same IP could be banned',\n    ATTACK_BUFFING => 'Injection - transfer of resources from weak to strong player - is forbidden by the rules',\n    ATTACK_ADMIN => 'You cannot attack the administrator',\n    ATTACK_NOOB => 'This player is too weak for you.',\n    ATTACK_OWN_VACATION => 'You are in Vacation Mode',\n    ATTACK_NO_SILO => 'Too low of missile silo',\n    ATTACK_NO_MISSILE => 'You cannot attack without missiles',\n    ATTACK_NO_FLEET => 'No fleet selected',\n    ATTACK_NO_SLOTS => 'No fleet slots',\n    ATTACK_NO_SHIPS => 'No ships',\n    ATTACK_NO_RECYCLERS => 'No recyclers',\n    ATTACK_NO_SPIES => 'No spy probes',\n    ATTACK_NO_COLONIZER => 'No colonyship',\n    ATTACK_MISSILE_TOO_FAR => 'Your missiles cannot reach this far',\n    ATTACK_WRONG_STRUCTURE => 'You can only attack defence structures',\n    ATTACK_NO_FUEL => 'Not enough fuel to launch fleet',\n    ATTACK_NO_RESOURCES => 'There is not enough resources for transport',\n    ATTACK_NO_ACS => 'No ACS',\n    ATTACK_ACS_MISSTARGET => 'Does not match the destination and the purpose of ACS',\n    ATTACK_WRONG_SPEED => 'Incorrect speed',\n    ATTACK_ACS_TOO_LATE => 'Fleet to slow - it will not catch with the Group ACS',\n    ATTACK_BASHING => 'Bashing protection. Number of allowed attacks per day per planet already reached',\n    ATTACK_BASHING_WAR_DELAY => 'Bashing protection. War to this Alliance already declared but did not started yet. Look at your Alliance page to see war begin date',\n    ATTACK_ACS_WRONG_TARGET => 'Fleet destination did not equal to ACS destination',\n    ATTACK_SAME => 'Source and destination planets are a same',\n    ATTACK_RESOURCE_FORBIDDEN => 'It is not allowed to send fleet with resources in this mission',\n    ATTACK_TRANSPORT_EMPTY => 'It is not allowed to send empty fleet to transport mission',\n    ATTACK_SPIES_LONLY => 'In this missions spies should be accompanied with other ships',\n    ATTACK_TOO_FAR => 'Your fleet cannot reach this far',\n    ATTACK_OVERLOADED => 'Your ships is overloaded. Reduce fleet cargo load or add more transports',\n    ATTACK_MISSION_ABSENT => 'There is no such mission type',\n    ATTACK_WRONG_UNIT => 'Wrong unit type',\n    ATTACK_ZERO_SPEED => 'There is orbital structure in the fleet',\n    ATTACK_SHIP_COUNT_WRONG => 'Ship amount can not be negative or zero',\n    ATTACK_RESOURCE_COUNT_WRONG => 'Resource amount can not be negative or zero',\n    ATTACK_MORATORIUM => 'There is moratorium on this mission type in progress',\n  ),\n\n  'fl_fleet_err' => 'Error!',\n  'fl_unknow_target' => '<li>The specified destination does not exist!</li>',\n  'fl_nodebris' => '<li>No Debris!</li>',\n  'fl_nomoon' => '<li>The moon does not exist!</li>',\n  'fl_vacation_ttl' => 'Vacation Mode',\n  'fl_vacation_pla' => 'Player is in Vacation!',\n  'fl_noob_title' => 'Newbie protection',\n  'fl_noob_mess_n' => 'The player is too low for you to attack!',\n  'fl_bad_planet01' => '<li>This planet is already colonized!</li>',\n  'fl_colonized' => '<li>Planet colonized by!</li>',\n  'fl_dont_stay_here' => 'You cannot land on the enemy planet!',\n  'fl_no_allydeposit' => 'No alliance depot on the planet!',\n  'fl_no_self_attack' => '<li>You cannot attack your own planet!</li>',\n  'fl_no_self_spy' => '<li>You cannot spy on your own planet!</li>',\n  'fl_only_stay_at_home' => '<li>You cannot relocate to another players planet!</li>',\n  'fl_cheat_speed' => 'Trying to cheat! Administration has been messaged!',\n  'fl_cheat_origine' => 'Trying to cheat! Administration has been messaged!',\n  'fl_limit_planet' => '<li>Bad planet !</li>',\n  'fl_limit_system' => '<li>Incorrect system !</li>',\n  'fl_limit_galaxy' => '<li>Irregular galaxy !</li>',\n  'fl_ownpl_err' => '<li>You cannot attack a planet!</li>',\n  'fl_no_planet_type' => '<li>Incorrect point designation!</li>',\n  'fl_fleet_err_pl' => '<li>Planets destination is buged!</li>',\n  'fl_bad_mission' => '<li>Setting is not specified or specified Mission is impossible!</li>',\n  'fl_no_fleetarray' => '<li>Something wrong with the fleet!</li>',\n  'fl_noenoughtgoods' => '<li>Attempt to send empty fleet on a mission &quot;Transport&quot;!</li>',\n  'fl_expe_notech' => '<li>Fleet was not sent, no Astrophysics Technology!</li>',\n  'fl_expe_max' => '<li>You cannot send another expedition. Develop Astrophysics technology</li>',\n  'fl_no_deuterium' => 'Not enough deuterium! ',\n  'fl_no_resources' => 'Not enough resources!',\n  'fl_nostoragespa' => 'Not enough storage space! ',\n  'fl_fleet_send' => 'Fleet sent',\n  'fl_expe_warning' => 'Attention, you may lose ships during the expedition!',\n  'fl_not_enough_fuel' => 'ERROR! You cargo bay is not enough for fuel! Add more ships with large cargo bays or reduce fleet speed to decrease consumption',\n  'fl_expe_staytime' => 'holding time',\n  'fl_expe_hours' => 'hours',\n  'fl_adm_attak' => 'You cannot attack the Administrator',\n  'fl_warning' => 'Warning',\n  'fl_page0_hint' => '<ul><li>To create, edit, and remove something  &quot;shortcut&quot; in the left menu<li>What would join the war with the alliance, please click on the title of any available you union</ul>',\n  'fl_page1_hint' => '<ul><li>Flight time includes the time for takeoff/landing fleet binding component of any flight,how near or far &quot;shortcut&quot; in the left menu <li>What would join the war with the alliance, please click on the title of any available you union</ul>',\n  'fl_page5_hint' => '<ul>  <li>Check in lines colonies types of resources you want to consolidate the current planet.  <li>A check mark in the title bar allows you to place or remove flags for the specified type resources on all colonies.  <li>Checkboxes in the column to select or clear check for all resources in the colony.  <li>Checkboxes in the column TOTAL for ease of interface and <span class=\"negative\">HAVE NO EFFECT</span> of the set of svozimyh resources  <li>Transportation resources are involved only transport ships: small transport, a large transport and Supertransport  <li>Vessels are loaded in descending size hold</ul>',\n  'fl_err_no_ships' => 'No ships In the fleet. Return to the previous page and select the ships for the fleet',\n  'fl_shrtcup' => array(\n    PT_PLANET => '(P)',\n    PT_DEBRIS => '(O)',\n    PT_MOON => '(L)',\n  ),\n\n  'fl_planettype' => array(\n    PT_PLANET => 'Planet',\n    PT_DEBRIS => 'Field debris',\n    PT_MOON => 'Moon',\n  ),\n\n  'fl_aks_invite_message_header' => 'Invitation to ACS',\n  'fl_aks_invite_message' => '<font color=\"red\">Player %s has invited you to join the ACS. you can join the ACS in the page &quot;Fleet&quot;.</font>',\n  'fl_aks_player_invited' => '<font color=\"lime\">Player %s was invited to joint attack.</font>',\n  'fl_aks_player_invited_already' => '<font color=\"lime\">Player %s already invited. Repeated invitations sent</font>',\n  'fl_aks_player_error' => '<font color=\"red\">Error. Player %s was not found.</font>',\n  'fl_aks_already_in_aks' => 'Fleet in the battle group!',\n  'fl_aks_adding_error' => 'Error adding party to fleet:<br>%s',\n  'fl_aks_hack_wrong_fleet' => 'Hacking attempt! Manipulation of the alien fleet Message sent to Administrator!',\n  'fl_aks_too_slow' => 'Fleet is too slow and could not join the war Union',\n  'fl_aks_too_power' => 'Adding this player will make ACS too strong to attack target',\n  'fl_aks_full' => 'Maximum fleet number in one ACS reached',\n  'fl_fleet_not_exists' => 'The fleet was not found',\n  'fl_multi_ip_protection' => 'Protection against multi-accounts!<br>Unable to send resources to the player with same IP!',\n  'fl_load_cargo' => 'Storage',\n  'fl_rest_on_planet' => 'Balance',\n  'fl_none_resources' => 'Reset',\n  'fl_planet_resources' => 'Resources on the planet',\n  'fl_fleet_data' => 'Current fleet',\n  'flt_gather_report' => 'Gathering Report',\n  'flt_report' => 'Report',\n  'flt_no_transports' => 'No transports',\n  'flt_no_fuel' => 'No fuel',\n  'fl_id' => '№',\n  'fl_ressources' => 'Resources',\n\n  'fl_fuel_on_planet' => 'Fuel on planet',\n\n  'flt_aks_players_in_aks' => 'Player in ACS',\n  'flt_aks_player_invite' => 'Invite player to ACS',\n  'flt_aks_player_invite_do' => 'Invite',\n  'flt_aks_player_same' => 'You can not invite to ACS player to whom attack is launched!',\n  'flt_aks_error_too_much_players' => 'You can invite to ACS 5 players maximum',\n\n  'flt_return_all' => 'Return selected',\n  'flt_return_fleet' => 'Return fleet',\n\n  'flt_acs_prefix' => 'ACS ',\n));\n"
  },
  {
    "path": "language/en/index.html",
    "content": ""
  },
  {
    "path": "language/en/infos.mo.php",
    "content": "<?php\n\n/*\n#############################################################################\n#  Filename: infos.mo\n#  Project: SuperNova.WS\n#  Website: http://www.supernova.ws\n#  Description: Massive Multiplayer Online Browser Space Strategy Game\n#\n#  Copyright © 2009-2018 Gorlum for Project \"SuperNova.WS\"\n#  Copyright © 2008 Aleksandar Spasojevic <spalekg@gmail.com>\n#  Copyright © 2005 - 2008 KGsystem\n#############################################################################\n*/\n\n/**\n*\n* @package language\n* @system [English]\n* @version 45d0\n*\n*/\n\n/**\n* DO NOT CHANGE\n*/\n\nif (!defined('INSIDE')) die();\n\n\n$a_lang_array = array(\n  'wiki_title' => 'Novapedia',\n\n  'wiki_char_nominal' => 'Nominal',\n  'wiki_char_actual' => 'Actual',\n\n  'wiki_ship_engine_header' => 'Engine characteristics',\n\n  'wiki_ship_header' => 'Flying characteristics',\n  'wiki_ship_speed' => 'Speed',\n  'wiki_ship_consumption' => 'Deuterium consumption',\n  'wiki_ship_capacity' => 'Cargo bay capacity',\n  'wiki_ship_hint' => '<li>Actual data calculated with applying all bonuses - techs, mercs etc</li>',\n\n  'wiki_combat_header' => 'Battle characteristics',\n  'wiki_combat_attack' => 'Attack power, hits',\n  'wiki_combat_shield' => 'Shield battery capacity, hits',\n  'wiki_combat_armor' => 'Durability, hits',\n\n  'wiki_combat_volley_header' => 'Volley',\n  'wiki_combat_volley_to' => 'Will destroy',\n  'wiki_combat_volley_from' => 'Will lost',\n\n  'info' => array(\n    STRUC_MINE_METAL => array(\n      'description' => 'The main supplier of raw materials for the construction of load-bearing structures of buildings and ships. Metal is the most inexpensive raw material, but takes more than everything else. For production of metal requires less total energy. Than mines more deeper. on most planets metal is at great depths, deeper mines you can obtain more metals, production increases. At the same time, larger mines require more energy.',\n      'description_short' => 'The main supplier of raw materials for the construction of load-bearing structures of buildings and ships.',\n    ),\n\n    STRUC_MINE_CRYSTAL => array(\n      'description' => 'For the synthesis of crystals requires approximately twicw more enery than for extraction of metal, so it therefore appreciated more. Crystals-main part of any modern computers and a key componet of warp drive-engines. Therefore, it is required for all ships and almost all buildings. Improving synth increases the number of produced crystals.',\n      'description_short' => 'The main supplier of raw materials for computer systems and warp-drives.',\n    ),\n\n    STRUC_MINE_DEUTERIUM => array(\n      'description' => 'Deuterium is heavy hydrogen. Because of this, as in the mains, more large stockpiles are at the bottom of the sea. Improving synth also contributes to development of these deep deposits of deuterium. Deuterium is needed as fuel for ships, nearly all studies, see the galaxies, and use sensor phalanx',\n      'description_short' => 'Extracts from the water on the planet, a small percentage of deuterium.',\n    ),\n\n    STRUC_MINE_SOLAR => array(\n      'description' => 'To ensure energy mines and synthesizers are huge solar power plants. The more built stations,the greater the surface is covered with solar panels that transform light energy into electricity. Solar power plants are the foundation of the energy world.',\n      'description_short' => 'Produces energy from sunlight. Energy is required for the majority of buildings.',\n    ),\n\n    STRUC_MINE_FUSION => array(\n      'description_short' => 'Extracts energy from education Atom helium two heavy hydrogen atoms.',\n      'description' => '',\n    ),\n\n    STRUC_FACTORY_ROBOT => array(\n      'description' => 'Provides a simple labor, which can be used in the construction of a global infrastructure. Each level increases the speed of factory building.',\n      'description_short' => 'Manufactures machines and mechanisms that are used in construction of a global infrastructure. Each level increases the speed of development of factory building.',\n    ),\n\n    STRUC_FACTORY_NANO => array(\n      'description' => 'nanobots are the final evolution of robotic factories. The only equipment-nanobots to manipulate individual molecules and even atoms of matter. Since their invention made possible the production of virtually any material with predefined properties. Moreover, thanks to nanobots you can quickly produce finished parts of any forms and configurations. But invention nanobots revoke conventional plants. Although nanobots can produce any design, many things still energetically more favorable to &quot;old fashioned&quot;. But even with such restrictions each level nanobots shortens the time of construction of any buildings, defences, and ships by half.',\n      'description_short' => 'Nanobots are specialized complexes to construct objects from individual molecules and atoms. Each level increases the speed of buildings, defences, and ships by twice.',\n      'effect' => '',\n    ),\n\n    STRUC_FACTORY_HANGAR => array(\n      'description' => 'Shipyard can produce all types of ships and defences. The faster you can build more complex and larger ships and defensive structures. By constructing factories nanites factory are simplified many chains that can dramatically improve the performance of the shipyard.',\n      'description_short' => 'Shipyard produces spaceships, orbital structures and defences.',\n    ),\n\n    STRUC_STORE_METAL => array(\n      'description' => 'Great location for extracted ores. The higher the level, the more metal you can store in it. If it is filled up, the extraction of metal ends.',\n      'description_short' => 'Storage for metal.',\n    ),\n\n    STRUC_STORE_CRYSTAL => array(\n      'description' => 'Great location for extracted ores. The higher the level, the more crystal you can store in it. If it is filled up, the extraction of crystal ends.',\n      'description_short' => 'Storage for crystal.',\n    ),\n\n    STRUC_STORE_DEUTERIUM => array(\n      'description' => 'Great location for extracted fuels. The higher the level, the more deuterium you can store in it. If it is filled up, the extraction of deuterium ends.',\n      'description_short' => 'Storage for deuterium.',\n    ),\n\n    STRUC_LABORATORY => array(\n      'description' => 'To study new technologies requires a working research station. Level of development research station is critical factor in how quickly could be developed new technologies. The higher the level of development research laboratory, the more can be researched new technologies. In order to complete as soon as the research work on the same planet, it sends all available scientists and then leave their planet. Once the technology is investigated, the scientists are returning to their home planet and carry with them knowledge about it. So new technologies can be applied on other planets.',\n      'description_short' => 'Laboratory is for researching new technologies.',\n    ),\n\n    STRUC_TERRAFORMER => array(\n      'description' => 'As more and more important building planets became the limited usable space. Traditional methods such as construction skyward and inside were insufficient. A small group of physicists and nanotehnikov found the solution-terraformer.<br><br>Spending large amounts of energy terraformer can convert vast areas and even the entire continents, making them suitable for construction. This structure is constantly produced special nanity, responsible for the constant quality of soil.',\n      'description_short' => 'Terraformer can transform a huge territory, increasing the number of construction fields.',\n    ),\n\n    STRUC_ALLY_DEPOSIT => array(\n      'description' => 'Alliance depot provides a way to ensure fuel friendly fleets that help with defense and are in orbit. The higher the level of development, the more deuterium can be sent to fleets in orbit.',\n      'description_short' => 'Alliance depot provides a way to ensure fuel to friendly fleets that help with defense and are in orbit.',\n    ),\n\n    STRUC_LABORATORY_NANO => array(\n      'description' => 'Reduce time to research stage doubled.',\n      'description_short' => 'Nanobots equipped with the latest technology. Heavy duty crystaline computers and super-precise nanosbots accelerated by any study by twice.',\n    ),\n\n    STRUC_MOON_STATION => array(\n      'description' => 'The moon has no atmosphere, therefore before the planned stay is required to build Lunar bases. It provides the necessary air, Gravitation and warmth. The higher the level of development of the lunar base, the more secure the biosphere area. Each level of the lunar base can build 3 sector, up to a maximum total square of the moon. It is 2 (diameter of the Moon/1000) ^ 2, each level lunar base itself occupies one field.',\n      'description_short' => 'The moon has no atmosphere, therefore before the planned stay is required to build lunar base.',\n    ),\n\n    STRUC_MOON_PHALANX => array(\n      'description' => 'High frequency sensors full browsing frequency spectrum by all the falangu radiations. Powerful computers combines tiny fluctuations in energy and thus gain information about the movements of ships at the distant planets. To view the Moon should be given the energy in the form of deuterium (per view 1000 * phalanx level). View is the transition from the Moon menu Galaxy and the title enemy planet located in range sensors (formula: (level phalanges) ^ 2-1).',\n      'description_short' => 'High frequency sensors full browsing frequency spectrum by all the ship fuel radiation.',\n    ),\n\n    STRUC_MOON_GATE => array(\n      'description' => 'Gate is a huge teleportery that can transmit between the fleets of all sizes without time-consuming. These teleportery do not require deuterium, but between two hops must undergo one hour, or gate recharge. Is also forwarding resources. The entire process requires very highly developed technology.',\n      'description_short' => 'Gate is a huge teleportery that can transmit between the fleets of all sizes without time-consuming.',\n    ),\n\n    STRUC_SILO => array(\n      'description' => 'Missile silo serve for storing missiles. With each level you can store four interplanetary or twelve interceptor missiles more. One interplanetary missiles require space triple the interceptor missile. May be any combination of different types of missiles.',\n      'description_short' => 'Missile silo allows firing rockets and missile plus storage.',\n    ),\n\n    TECH_SPY => array(\n      'description_short' => 'Using this technology produces data on other planets.',\n      'description' => '',\n    ),\n\n    TECH_COMPUTER => array(\n      'description' => 'Computer technology is designed to increase availability of computer power. As a result of the planet are more productive and efficient computer systems, increasing processing power and speed of computing processes. With the increasing power of computers you can command the entire of fleets. Each level of development of computer technology makes it possible to command + 1 fleet. The more sent fleets, the more you can do  and thus capture more raw materials. Naturally, this technology is useful and traders, as it enables them to simultaneously send larger merchant fleets. For this reason, you should constantly develop computer technology throughout the entire game.',\n      'description_short' => 'With the increasing power of computers you can command the entire many fleets. Each level of computer technology increases the maximum number of fleets by 1.',\n    ),\n\n    TECH_WEAPON => array(\n      'description' => 'Weapon technology focuses on the further development of weapons systems. Particular importance is attached to implementing existing systems more energy and more precisely this energy channel. The weapons systems become more efficient, and weapons causes more devastation. Each level increases the power of weapons technology weapons military units by 10%. Weapons technology is important in competitive content of parts. Why should constantly develop throughout the game.',\n      'description_short' => 'Weapon technology makes weapons systems more efficient. Each level increases power weapons military units at 10% of the basic.',\n    ),\n\n    TECH_SHIELD => array(\n      'description' => 'Development of this technology allows you to increase the supply of energy shields and shielding, which in turn increases their resilience and ability to absorb or reflect energy attacks of the enemy. Thanks to this with each passing level effectiveness of ship\\'s shields and stationary shield generators increases by 10% of the rated power.',\n      'description_short' => 'This technology examines more new features greater energy shields that make them more efficient. Thanks to this with each passing level effectiveness of shields is increased by 10%.',\n    ),\n\n    TECH_ARMOR => array(\n      'description' => 'Special alloys improve armor spacecraft. Once found very resistant alloy, special beams are changing the molecular structure of a spacecraft, and brings it to a known alloy. The sustainability of armor can increase with each level at 10%.',\n      'description_short' => 'Special alloys improve armor spacecraft. With each level strength armor increased by 10 % of the base value.',\n    ),\n\n    TECH_ENERGY => array(\n      'description' => 'Energy technology is a further development of transmission systems and energy storage that are required for many new technologies.',\n      'description_short' => 'Study of energy technology improves impact outcome of thermonuclear electrostations.',\n    ),\n\n    TECH_HYPERSPACE => array(\n      'description' => 'By Plexus 4th and 5th dimension has become possible to explore new, more economical and efficient engine.',\n      'description_short' => 'By Plexus 4th and 5th dimension has become possible to explore new, more economical and efficient engine.',\n    ),\n\n    TECH_ENGINE_CHEMICAL => array(\n      'description' => 'Jet engine is based on the principle of effectiveness. Fabric, hot to elevated temperatures, thrown in the opposite direction and gives faster ship. The effectiveness of these engines is small enough, but they are quite reliable, cheap production and services. Furthermore they take up much less space on the ship than the other engines, so they are still quite often can be found on small ships. Because the rocket engines are the foundation of any flight into space, should examine them as soon as possible. Further development of these engines makes the following ships with each level at 10% faster: small transports (until a researched pulse engine the 5th level), large transports, light fighters, recyclers and spy probes.',\n      'description_short' => 'Further development of these engines makes some ships faster, but each level increases the speed of only 10%.',\n    ),\n\n    TECH_ENGINE_ION => array(\n      'description' => 'Impulse engine is based on the principle of effectiveness, and warming up of matter are the nuclear reaction. You can also inject additional mass. Further development of these engines makes the following ships with each level to 20% faster: small transportation, bombers (until a researched hyperspace engine 8th level), cruisers, heavy fighter and colonizers. Each level increases the reach of interplanetary missiles.',\n      'description_short' => 'Impulse engine is based on the principle of effectiveness. Further development of these engines makes some ships faster, but with each level increases the speed of only 20%.',\n    ),\n\n    TECH_ENGINE_HYPER => array(\n      'description' => 'By spatio-temporal curvature in the immediate environment of spacecraft space shrinks, the faster the overcome long distances. The higher developed Hyperspace drive, the higher the compression space, which makes the following ships with each level of 30% faster: Hypertransport, Cruiser, Bomber (after research of 8th level), Battleship, Destroyer, Death Star and Supernova-class cruiser.',\n      'description_short' => 'By spatio-temporal curvature in the immediate environment of spacecraft space shrinks, the faster the overcome long distances. The higher the developed hyperspace drive, the higher the compression space, with each level of the speed of ships rises up 30%.',\n    ),\n\n    TECH_LASER => array(\n      'description' => 'Laser (light amplification using induced emission of radiation) produces rich energy beam of coherent light. These devices are used in all areas of optical computers before heavy lasers that freely cut armor spacecraft. Laser technology is an important element for the study of further weapons technology.',\n      'description_short' => 'Thanks to focus light rays that occurs when an object causes him injury.',\n    ),\n\n    TECH_ION => array(\n      'description' => 'Truly deadly beam of accelerated ions. In contact with an object they cause immense damage.',\n      'description_short' => 'Truly deadly beam of accelerated ions. In contact with an object they cause immense damage.',\n    ),\n\n    TECH_PLASMA => array(\n      'description' => 'Further development of the ion technology which accelerates the ions, and energetic plasma. She has had a devastating effect in contact with an object.',\n      'description_short' => '',\n    ),\n\n    TECH_RESEARCH => array(\n      'description' => 'This network enables communication scientists working in research laboratories from different planets. Each new level allows you to attach to the network for additional laboratory (primarily attached laboratory senior levels). Of all United in a network of laboratories in each study involved only those that are sufficient to conduct the study level. Speed study FY08 levels involved in it laboratories.',\n      'description_short' => 'This network enables communication scientists working in research laboratories from different planets. Each new level allows you to attach to the network for additional laboratory.',\n    ),\n\n    TECH_EXPEDITION => array(\n      'description' => 'Expedition technology encompasses various scanning technology and makes it possible to equip the ships of different classes of research module. It contains a database, a small mobile laboratory, as well as various biokletki and vessels for samples. For the safety of the ship when investigating hazardous objects research module is equipped with an autonomous energy and generator of energy field, which in extreme cases can surround a powerful energy field research module.',\n      'description_short' => 'Now you can equip ships providing research module processing the collected data in long flights.',\n    ),\n\n    TECH_COLONIZATION => array(\n      'description' => 'Ruler with many colonies, has more advantages over others.',\n      'description_short' => 'This technology is very important that you could build your Empire with many colonies.',\n    ),\n\n    TECH_ASTROTECH => array(\n      'description' => 'Астрокартография позволяет увеличить максимальное количество колоний и экспедиций, а так же максимальную длительность экспедиции',\n      'description_short' => 'Астрокартография позволяет увеличить максимальное количество колоний и экспедиций, а так же максимальную длительность экспедиции',\n    ),\n\n    TECH_GRAVITON => array(\n      'description' => 'Graviton is a particle that has neither mass nor charge and determines the force of attraction. By launching the concentrated charge gravitonov can create artificial gravitational field that, like a black hole, dragging a ton, so you can destroy ships or even the moon. To produce a sufficient quantity of gravitonov requires huge amounts of energy.',\n      'description_short' => 'Graviton is a particle that has neither mass nor charge and determines the force of attraction. By launching the concentrated charge gravitonov can create artificial gravitational field that, like a black hole, dragging a ton, so you can destroy ships or even the Moon.',\n    ),\n\n    SHIP_CARGO_SMALL => array(\n      'description' => 'Transports have approximately the same size as the fighter aircraft, but they don\\'t have powerful engines and onboard weapons to save space. Small transport accommodates 5,000 units of stuff. Because small firepower small transports are often accompanied by other ships when pulse engine researched up to 5th grade, small transport improves the speed and it is equipped with a base that type engine.',\n      'description_short' => 'Small transport is highly maneuverable craft, which can easily transport the raw material to other planets. After research impulse drive 5th level ships are being refurbished.',\n    ),\n\n    SHIP_CARGO_BIG => array(\n      'description' => 'Aboard this ship there is only weak armament and no serious technology ... For this reason, they should never be run unattended. Thanks to its advanced Jet engine large transportation serves as a rapid interplanetary dostavsika resources, as it accompanies the fleets to attack the enemy planet to grab as many resources as possible.',\n      'description_short' => 'Further development of small transports a ships with greater capacity and more developed an engine that can travel faster than light travel, until small transports not installed impulse engines 5th level.',\n    ),\n\n    SHIP_CARGO_SUPER => array(\n      'description' => 'The last word in technology transfer. Supertransport-giant transport ship equipped with impulse engines. Its speed is low and the fuel consumption is very high, but it completely pays off an extraordinary sitting capacity. Supertransport is equipped with a laser turrent, but has a powerful shield.',\n      'description_short' => 'A giant self-propelled barge equipped with impulse engines. Equipped with a powerful shield, but of weapons has only laser turrent.',\n    ),\n\n    SHIP_CARGO_HYPER => array(\n      'description' => 'If Supercargo was &quot;last word in transportation technology&quot; then Hypertransport would be a final point. &quot;Giant&quot; is too weak word to describe this ship. Sized as small moon this transport able to transfer enormeous amount of resources. Only hyperdrives can move fully loaded ship. They allow Hypertransport to slide through Universe with acceptable speed. But price is very high - cost of one ship is about ten times cost of Destructor. Moreover - fuel consumption of this transport will made almost any Emperor cry like a baby. Thou Hypertransport is a ship for a large and powerfull Empires which need to transfer several millions tons of resources at once',\n      'description_short' => 'Cargo ship sized as small moon. Equiped with hyperdrives and able to transfer million ton of resources at once',\n    ),\n\n    SHIP_SMALL_FIGHTER_LIGHT => array(\n      'description' => 'Lightweight fighter is a highly maneuverable craft, which can be found on almost every planet. Cost is not too big, but the shielding power and capacity are very small.',\n      'description_short' => 'Lightweight fighter is a highly maneuverable craft, which can be found on almost every planet. Cost is not too big, but the shielding power and capacity are very small.',\n    ),\n\n    SHIP_SMALL_FIGHTER_HEAVY => array(\n      'description' => 'In further development of the lightweight fighter scientists came to a point where it became clear that an ordinary engine lacks power. In order to optimally remove ship, was first used pulse engine. Though it has cost, but it also opened new possibilities. Thanks to this engine has more power for weapons and shields, moreover, for this kind of fighter aircraft were also used valuable materials. This has led to improved structural integrity and stronger firepower, melee weapons, it represents a greater threat than its predecessor. After these changes Beaufighter represents a new era of technology, shipbuilding basis technology.',\n      'description_short' => 'Further development of the lightweight fighter, it is better to secure and has a stronger attack.',\n    ),\n\n    SHIP_MEDIUM_DESTROYER => array(\n      'description' => 'With the development of heavy lasers and ion guns, more and more marginalized by heavy fighters. Despite the many improvements in firepower and armor cannot be so modified to efficiently counter these defensive guns. It was therefore decided to build a new class of ships, which would have more firepower and armor. So were the destroyers. Destroyers protected almost three times stronger than heavy fighters and more than twice the firepower. They are very quick. There is no better weapons against high defense. Nearly a century cruisers unlimited dominated in the universe. With the advent of Gauss guns and plasma guns their rule came to an end. However, today they are used against groups of fighters.',\n      'description_short' => 'Destroyers protected almost three times stronger than heavy fighters and firepower they surpass heavy fighters almost doubled. They are very fast.',\n    ),\n\n    SHIP_LARGE_CRUISER => array(\n      'description' => 'Cruisers tend to form the backbone of the fleet. Their heavy guns, high speed and large cargo make them serious opponents.',\n      'description_short' => 'Cruisers tend to form the backbone of the fleet. Their heavy guns, high speed and large cargo make them serious opponents.',\n    ),\n\n    SHIP_COLONIZER => array(\n      'description' => 'This well protected boat serves conquer new planets that need expanding Empire. It is used in the new colonies as the supplier of raw material and use it to take apart-its useful material for the development of the new world. Maximum number of colonies per Empire depends on Universe Settings.',\n      'description_short' => 'This ship can absorb planet.',\n    ),\n\n    SHIP_RECYCLER => array(\n      'description' => 'Space battles were all lost. Destroyed thousands of ships and encountered the wreckage seemed forever lost. Normal transports couldn\\'t come close to them without being badly corrupted small fragments. With the new opening in shield technology it has become possible to effectively address this problem, a new class of ship, similar large transport-processor. With its help you can reuse the seemingly lost resources. Due to the new boards are small fragments no longer represented danger. Unfortunately, these devices require space, so its cargo tonnage is limited to 20 000.',\n      'description_short' => 'Using the raw material is extracted from the wreckage of tire.',\n    ),\n\n    SHIP_SPY => array(\n      'description' => 'Spy probes are small mobile that the ships which deliver with distances of fleets and planets. Their high-performance engine allows them to travel long distances for a few seconds. Once it lands on the orbit of a planet, they are there for some time to build the data. At this time the enemy is relatively easy to detect them and attack. To save space not found no armor or shields, no guns, making the sounders in case light objectives.',\n      'description_short' => 'Spy probes are small mobile that the ships which deliver with distances of fleets and planets.',\n    ),\n\n    SHIP_LARGE_BOMBER => array(\n      'description' => 'Bomber was developed specifically to destroy the planetary protection. Using a laser sight it just resets plasma bombs on the surface of the planet and thus causes enormous damage to the defensive structures when hyperspace engine researched up to 8th grade, the bomber is a basic speed and it is equipped with this type of engine.',\n      'description_short' => 'Bomber was developed specifically to destroy the planetary protection.',\n    ),\n\n    SHIP_SATTELITE_SOLAR => array(\n      'description' => 'Solar satellites are launched into orbit of the planet. They collect solar energy and transmit it to the ground station. Efficiency solar satellites depends on the strength of solar radiation. In principle, the extraction of energy orbits more approximate to the Sun is higher than on the planets, remote from the Sun. Due to its price/quality ratio solar satellites solve energy problems of many worlds. But attention: solar satellites can be destroyed in battle.',\n      'description_short' => 'Solar satellites is a simple platform of solar cells, which are located on high orbit. They collect sunlight and expose it using laser ground station.',\n    ),\n\n    SHIP_LARGE_DESTRUCTOR => array(\n      'description' => 'Destructor - King of warships. The ion, plasma and gauss turrets can thanks to its advanced sensor to a 99% even high-speed mobile that the fighters. Since destroyers are very large, their agility is very limited, and in battle they resemble rather a battle station than combat ship. Deuterium consumption are also appreciated as their fighting power.',\n      'description_short' => 'Destructor - King among military ships.',\n    ),\n\n    SHIP_HUGE_DEATH_STAR => array(\n      'description' => 'Death star is equipped with a giant graviton gun that could destroy all types of ships and even the moon. To produce enough energy, celebrity deaths almost entirely consists of generators. Only huge stellar Empire have enough resources and workers for the construction of this huge ship.',\n      'description_short' => 'Death star is equipped with a giant graviton gun that can destroy ships and even the Moon. ',\n    ),\n\n    SHIP_LARGE_BATTLESHIP => array(\n      'description' => 'This high-tech ship brings death intruder fleets. Its advanced laser guns keep heavy enemy ships at a distance and can destroy several units one in one gulp. Due to its small size and incredibly powerful weapons, lenght linear Cruiser is very small, but at the expense of hyperspatial engine as little fuel consumption.',\n      'description_short' => 'Starcruiser specializes in capturing enemy fleets.',\n    ),\n\n    SHIP_HUGE_SUPERNOVA => array(\n      'description' => 'You are granted a ship from the emperor for your cruelty skills.',\n      'description_short' => 'The flagship of the fleet of the Empire. The huge build cost will be compensated with terrible firepower and advanced protection. One ship of this class is able to defeat an average fleet by itself.',\n    ),\n\n    UNIT_DEF_TURRET_MISSILE => array(\n      'description' => 'Launcher is a simple and inexpensive means of defence. Because this is the development of conventional ballistic artillery shells, it does not require further upgrading. The small cost of its production justifies its use against smaller fleets, but over time it loses value. Later it is used only for withdrawal of enemy shots. Defensive deactivates itself once they severely damaged. Recoverability of the fortifications after a battle with up to 70%.',\n      'description_short' => 'Launcher is a simple and inexpensive means of defence.',\n    ),\n\n    UNIT_DEF_TURRET_LASER_SMALL => array(\n      'description_short' => 'Using the concentrated attack targets photons can be achieved much great destruction than conventional ballistic weapons.',\n      'description' => '',\n    ),\n\n    UNIT_DEF_TURRET_LASER_BIG => array(\n      'description' => 'Heavy laser represents a further development of the laser light. Structure has been reinforced and improved with new materials. Wrapper could do much more resistant. At the same time has been improved and energy system and the target computer, so heavy laser can concentrate much more on target. Defensive deactivates itself once they severely damaged. Recoverability of the fortifications after a battle with up to 70%.',\n      'description_short' => 'Heavy laser represents a further development of laser light.',\n    ),\n\n    UNIT_DEF_TURRET_GAUSS => array(\n      'description_short' => 'Coilgun accelerates mnogotonnye charges with gigantic energy costs.',\n      'description' => '',\n    ),\n\n    UNIT_DEF_TURRET_ION => array(\n      'description_short' => 'Ion Cannon directs to the purpose of wave ion, which destabilizes the shields and damages the electronics.',\n      'description' => '',\n    ),\n\n    UNIT_DEF_TURRET_PLASMA => array(\n      'description' => 'Laser technology was brought to perfection, Ionic technology has reached the final stage and was thought to be virtually impossible, even qualitatively Cannon system to achieve even greater efficiency. But everything was to change when the idea to combine both systems. Using the technology of nuclear fusion, laser heat the substance (usually a deuterium) up to ultrahigh temperatures reaching millions of degrees. Ionic technology provides enrichment plasma electric charge, its stabilization and acceleration. Once electric charge sufficiently warmed, ionized and is under pressure, it produce using accelerators in the direction of the goal. Glowing bluish color plasma Bowl looks awesome, the only question is, how long they will enjoy the command ship-purpose if after a few seconds, the armor he explodes into pieces and Electronics will burn ... Plasma turret is generally the most frightening weapons, and this technology represents a trade-off. Defensive deactivates itself once they severely damaged. Recoverability of the fortifications after a battle with up to 70%.',\n      'description_short' => 'The last word in Planetary Defense technologies created a symbiosis of laser and Ion tech.',\n    ),\n\n    UNIT_DEF_SHIELD_SMALL => array(\n      'description' => 'Long before the shield generators become sufficiently small to be used on ships, there is a huge generators on the surface of planets. They obvolakivali an entire planet force field that can absorb the shock of the attack. Small fleets are constantly attacking on these billboards dome. Due to the growing technological development of these boards can be even greater. Later, you can build stronger large shield dome. On every planet you can build only one small shield dome.',\n      'description_short' => 'Small shield protects the planet and absorbs shock attack.',\n    ),\n\n    UNIT_DEF_SHIELD_BIG => array(\n      'description' => 'Further development of the small shield dome. It can deter even stronger attack to planet, consuming significantly more energy.',\n      'description_short' => 'Further development of the small shield dome. It can deter even stronger attack to planet, consuming significantly more energy.',\n    ),\n\n    UNIT_DEF_SHIELD_PLANET => array(\n      'description' => 'The best protection for your planets',\n      'description_short' => 'The best protection for your planets',\n    ),\n\n    UNIT_DEF_MISSILE_INTERCEPTOR => array(\n      'description' => 'Rocket interceptors destroy attacking interplanetary missiles. An interceptor missile destroys one interplanetary missile.',\n      'description_short' => 'Rocket interceptors destroy attacking interplanetary missiles',\n    ),\n\n    UNIT_DEF_MISSILE_INTERPLANET => array(\n      'description_short' => 'Interplanetary missiles destroy enemy defenses',\n      'description' => '',\n    ),\n\n    MRC_TECHNOLOGIST => array(\n      'description' => 'Technologist is a recognized expert in astromineralogy and crystalography. With his team of Metalurgists and chemists, he supported the interplanetary Government when developing new resources and optimize their refinery.',\n      'description_short' => 'Technologist is a recognized expert in astromineralogy and crystalography. With his team of Metalurgists and chemists, he supported the interplanetary Government when developing new resources and optimize their refinery.',\n      'effect' => 'per level to metal, crystal and deuterium production, to energy production on solar and fusion stations each level',\n    ),\n\n    MRC_ENGINEER => array(\n      'description' => 'Engineer is expert in structures and ship building',\n      'description_short' => 'Engineer is expert in structures and ship building',\n      'effect' => 'per level to construction speed for buildings and ships<br />+1 slot per level to planet structures and hangar queries',\n    ),\n\n    MRC_FORTIFIER => array(\n      'description' => 'Fortifier - Army Engineer. His in-depth knowledge of defensive systems allow you to shorten planet defense building time',\n      'description_short' => 'Fortifier - Army Engineer. His in-depth knowledge of defensive systems allow you to shorten planet defense building time',\n      'effect' => 'per level to construction speed of missiles and defense structures<br />+10% per level to attack, armor and shields when defending planet<br />+1 slot per level to defence structures and missile queries',\n    ),\n\n    MRC_STOCKMAN => array(\n      'description' => 'Cargo-master is a highly skilled specialist in storage. His genius allows you to get the most out of storage resources to increase their effective capacity beyond the builders.',\n      'description_short' => 'Cargo-master is a highly skilled specialist in storage. His genius allows you to get the most out of storage resources to increase their effective capacity beyond the builders.',\n      'effect' => 'size of warehouses for each level',\n    ),\n\n    MRC_SPY => array(\n      'description' => 'Spy-master person Empire. He had hundreds of thousands of individuals and a million ideas for mask works, defensive networks and fleets. Everyone who saw his real face, is now dead.',\n      'description_short' => 'Spy-master person Empire. He had hundreds of thousands of individuals and a million ideas for mask works, defensive networks and fleets. Everyone who saw his real face, is now dead.',\n      'effect' => 'level of spying for each level',\n    ),\n\n    MRC_ACADEMIC => array(\n      'description' => 'Academicians are actors Guild Technocrats. Their mind and scholars degree allow them Excel in their acts even constructors. They specialize in the field of technological progress.',\n      'description_short' => 'Academicians are actors Guild Technocrats. Their mind and scholars degree allow them Excel in their acts even constructors. They specialize in the field of technological progress.',\n      'effect' => 'per level to technology research speed',\n    ),\n\n//    MRC_DESTRUCTOR => array(\n//      'description' => 'Destroyer, a ruthless army officer. He suggests how the Empire\\'s planets brutal methods should be. The same Destroyer has developed technology and manufactures Deathstars.',\n//      'effect' => 'Allows to build Deathstars in the shipyard',\n//    ),\n\n    MRC_ADMIRAL => array(\n      'description' => 'Admiral is tried by war veteran and a brilliant strategist. Even in the hottest fights he doesn\\'t lose a review and maintains contact with commanders fleets. The wise ruler can rely on him in battle and thereby use to battle more ships.',\n      'description_short' => 'Admiral is tried by war veteran and a brilliant strategist. Even in the hottest fights he doesn\\'t lose a review and maintains contact with commanders fleets. The wise ruler can rely on him in battle and thereby use to battle more ships.',\n      'effect' => 'armor, shields and attack ships for each level',\n    ),\n\n    MRC_COORDINATOR => array(\n      'description' => 'The Coordinator is an expert in managing fleets. His knowledge can make the most of the fleet management system.',\n      'description_short' => 'The Coordinator is an expert in managing fleets. His knowledge can make the most of the fleet management system.',\n      'effect' => 'additional fleet for each level',\n    ),\n\n    MRC_NAVIGATOR => array(\n      'description' => 'Navigator-genius in calculating the trajectories of fleets. His knowledge of laws warp drive-space device jump-drive and technologies all existing types of engines can speed flying ships.',\n      'description_short' => 'Navigator-genius in calculating the trajectories of fleets. His knowledge of laws warp drive-space device jump-drive and technologies all existing types of engines can speed flying ships.',\n      'effect' => 'speed of ships for each level',\n    ),\n\n//    MRC_ASSASIN => array(\n//      'description' => 'Assasin is a trusted killer. But it\\'s not his only quality. Assassin has developed the new cruiser class Supernova. He is the only person who can manage this ship.',\n//      'effect' => 'Allows to build Supernova Cruisers in the shipyard',\n//    ),\n\n    MRC_EMPEROR => array(\n      'description' => 'Emperor - your personal assistant and Deputy. The accuracy of its reports and punctuality in everything-his best qualities, capable of total control over the Empire.',\n      'description_short' => 'Emperor - your personal assistant and Deputy. The accuracy of its reports and punctuality in everything-his best qualities, capable of total control over the Empire.',\n      'effect' => 'Allows you to change the characteristics of the Emperor',\n    ),\n\n    ART_LHC => array(\n      'description' => 'LHC creates waves of gravitons that forces debris to concentrate in one place<br /><span class=warning>WARNING! Using of LHC is not a guarantee to creation of new moon!</span>',\n      'effect' => 'Allows another chance to create moon<br />1% per 1.000.000 of debris but not more then 30%',\n    ),\n\n    ART_HOOK_SMALL => array(\n      'description' => 'Принцип действия этого Артефакта до конца не изучен что, впрочем, не мешает его использовать. Малый Крюк телепортирует на устойчивую орбиту планеты небольшой астероид. Таким образом у планеты получается луна минимального диаметра',\n      'effect' => 'Creates minimal size Moon',\n    ),\n\n    ART_HOOK_MEDIUM => array(\n      'description' => 'Принцип действия этого Артефакта до конца не изучен что, впрочем, не мешает его использовать. Средний Крюк телепортирует на устойчивую орбиту планеты астероид, создавая таким образом луну<br /><span class=warning>ВНИМАНИЕ! Размер луны СЛУЧАЕН!</span>',\n      'effect' => 'Creates random size Moon',\n    ),\n\n    ART_HOOK_LARGE => array(\n      'description' => 'Принцип действия этого Артефакта до конца не изучен что, впрочем, не мешает его использовать. Большой Крюк телепортирует на устойчивую орбиту планеты огромный астероид. Таким образом у планеты получается луна максимального диаметра',\n      'effect' => 'Creates maximum size Moon',\n    ),\n\n    ART_RCD_SMALL => array(\n      'description' => 'Small Rapid Colony Deployer (RCD) is a set of ready constructions and programs that allows deploy on planet basic colony in no time<br />\n        If there are buildings on planet they will be upgraded or left intact - if their level higher then RCD\\'s programming. RCD can be fully deployed on planet even when there is lack of free sectors. RCD can not be deployed on moon.<br />\n        Basic colony includes Metal Mine, Crystal Mine and Deuterium Synthetizer of level 10, Solar Plant of level 14 and Robotics Factory level 4',\n      'effect' => 'Instantly deploys basic colony on planet',\n    ),\n\n    ART_RCD_MEDIUM => array(\n      'description' => 'Medium Rapid Colony Deployer (RCD) is a set of ready constructions and programs that allows deploy on planet advanced colony in no time<br />\n        If there are buildings on planet they will be upgraded or left intact - if their level higher then RCD\\'s programming. RCD can be fully deployed on planet even when there is lack of free sectors. RCD can not be deployed on moon.<br />\n        Advanced colony includes Metal Mine, Crystal Mine and Deuterium Synthetizer of level 15, Solar Plant of level 20 and Robotics Factory level 8',\n      'effect' => 'Instantly deploys advanced colony on planet',\n    ),\n\n    ART_RCD_LARGE => array(\n      'description' => 'Large Rapid Colony Deployer (RCD) is a set of ready constructions and programs that allows deploy on planet improved colony in no time<br />\n        If there are buildings on planet they will be upgraded or left intact - if their level higher then RCD\\'s programming. RCD can be fully deployed on planet even when there is lack of free sectors. RCD can not be deployed on moon.<br />\n        Improved colony includes Metal Mine, Crystal Mine and Deuterium Synthetizer of level 20, Solar Plant of level 25, Robotics Factory level 10 and Nanite Factory level 1',\n      'effect' => 'Instantly deploys improved colony on planet',\n    ),\n\n    ART_HEURISTIC_CHIP => array(\n      'description' => 'Эвристический чип - уникальный преинсталлированный набор программ, записанных на кристаллический носитель. Подключаясь к исследовательской сети, алгоритмы чипа способны проанализировать текущее состояние исследования и выдать новые эффективные эвристики, таким образом значительно сокращая время исследования. Однажды активированный чип невозможно перенастроить на другое исследование. К сожалению, как и с любым другим кристаллическим чипом, декомпиляция &quot;зашитой&quot; программы принципиально невозможна, равно как и копирование сборщиками.',\n      'effect' => 'Уменьшает время текущего исследования в два раза (если до конца исследования осталось больше часа) или моментально заканчивает его (если до конца исследования осталось меньше 1 часа, но больше 1 минуты)', //. Если времени исследования осталось менее часа - остаток не переходит на следующий слот в очереди',\n    ),\n\n    ART_NANO_BUILDER => array(\n      'description' => 'Как известно, сборщики обычно не используются в строительстве крупных объектов типа зданий. Экономически целесообразней возводить строения методом традиционной &quot;блочной сборки&quot;, когда отдельные стандартизированные детали производятся на роботизированных фабриках. Однако специализированные наносборщики оказываются эффективнее традиционных методов. Эти крошечные роботы собраны в преконфигурированные пакеты, каждый из которых обладает своим собственным роевым суб-ИИ. Анализируя текущее состояние возводимого здания, наностроители безошибочно находят узкие места и вычисляют наиболее эффективные пути ускорения строительства. Пакет является одноразовым и после использования больше непригоден к работе. Вдобавок инициированный пакет уже невозможно перенастроить на интеграцию с другой стройкой. Хотя сборщики и способны воспроизвести отдельно взяты наностроитель, но без управляющего кристалла такая реплика является не более чем масштабной моделью...',\n      'effect' => 'Уменьшает время постройки/разрушения текущего здания на данной планете в два раза (если до конца процесса осталось больше часа) или моментально заканчивает его (если до конца процесса меньше 1 часа, но больше 1 минуты)', // . Если времени строительства осталось менее часа - остаток не переходит на следующий слот в очереди',\n    ),\n\n\n    UNIT_PLAN_STRUC_MINE_FUSION  => array(\n      'description' => 'Allows to build on planets sturcture &quot;Thermonuclear plant&quot;',\n      'effect' => 'Allows to build on planets sturcture &quot;Thermonuclear plant&quot;',\n    ),\n\n    UNIT_PLAN_SHIP_CARGO_SUPER  => array(\n      'description' => 'Allows to build on planet\\'s hangars &quot;Super Cargo&quot; ships',\n      'effect' => 'Allows to build on planet\\'s hangars &quot;Super Cargo&quot; ships',\n    ),\n\n    UNIT_PLAN_SHIP_CARGO_HYPER  => array(\n      'description' => 'Allows to build on planet\\'s hangars &quot;Hypercargo&quot; ships',\n      'effect' => 'Allows to build on planet\\'s hangars &quot;Hypercargo&quot; ships',\n    ),\n\n    UNIT_PLAN_SHIP_DEATH_STAR  => array(\n      'description' => 'Allows to build on planet\\'s hangars &quot;Death Star&quot; ships',\n      'effect' => 'Allows to build on planet\\'s hangars &quot;Death Star&quot; ships',\n    ),\n\n    UNIT_PLAN_SHIP_SUPERNOVA  => array(\n      'description' => 'Allows to build on planet\\'s hangars &quot;Supernova&quot;-class cruisers',\n      'effect' => 'Allows to build on planet\\'s hangars &quot;Supernova&quot;-class cruisers',\n    ),\n\n    UNIT_PLAN_DEF_SHIELD_PLANET  => array(\n      'description' => 'Allows to build on planets defense system &quot;Planet protector&quot;',\n      'effect' => 'Allows to build on planets defense system &quot;Planet protector&quot;',\n    ),\n\n\n    RES_METAL => array(\n      'description' => 'Metametallic iron-normed energy-neutraul compound (shortly &quot;metal&quot;) is a basic raw material from which nanobots produces all materials and details used in construction and research. Metal comes in ignots. Each ignot volume is 127 litres and weights 1 metric tonn including protective case. &quot;Iron-normed&quot; means that standard pack of nanobot will produce from 1 ignot 1 tonn of pure iron. &quot;Energy-neutral&quot; means that nanobots will use exact amount of energy which can be extracted from ignot. &quot;Metametallic compaund&quot; means that ignot can include simple and complex chemical substances. Composition of metal ignot can differ from planet to planet and from mine to mine - but their physical characteristics remain the same. Usually metal is slightly radioactive',\n      'effect' => '',\n    ),\n\n    RES_CRYSTAL => array(\n      'description' => 'Crystal is a complex termoplastic polymer which demonstrates Superlight Conduction Effect. SCE - increasing photon speed in a crystall above 300000 km/s. All modern computers uses crystall as material for processing and memory units. Residuals (&quot;anomal assemblies&quot; - i.e. polymers that has same formula but didn\\'t demonstrates SCE) used in solar panels whose efficiency is about 100%. Specially selected crystal is a main part of jump-drive - device which allow faster-then-light travels',\n      'effect' => '',\n    ),\n\n    RES_DEUTERIUM => array(\n      'description' => 'Deuterium, also called heavy hydrogen, is one of two stable isotopes of hydrogen. The nucleus of deuterium, called a deuteron, contains one proton and one neutron, whereas the far more common hydrogen isotope, protium, has no neutron in the nucleus. Deuterium used as fuel for termonuclear reactors and all types of ship engines. It is stored in liquefied form in a standard thermally insulated containers which also is a fuel cells for reactors and engines. Ship cargo bays with automatic feeders also serves as &quot;fuel tank&quot; for any ship',\n      'effect' => '',\n    ),\n\n    RES_ENERGY => array(\n      'description' => 'Electric energy - unified type of energy that used everywhere. On planets it\\'s usually produced on solar stations and solar sattelites. On space ships and some colonies too far from sun it\\'s produced on termonuclear reactors and plants - respectivly',\n      'effect' => '',\n    ),\n\n    RES_DARK_MATTER => array(\n      'description' => '<span class=\"dark_matter\">Dark Matter</span> is a matter that neither emits nor scatters light or other electromagnetic radiation, and so cannot be directly detected via optical or radio astronomy. From it we can get an incredible amount of energy. Because of this, and also because of the complexities associated with its production of <span class=\"dark_matter\">Dark Matter</span> is highly appreciated.',\n      'effect' => '',\n    ),\n\n    UNIT_PLANET_DENSITY => array(\n      'description' => 'Средняя планетарная плотность (далее - просто \"плотность\") характеризует химический состав геосферы планеты. В частности, она очень точно предсказывает соотношения добываемых полезных веществ.<br /><br />\n      В общем случае геосфера планеты делится на атмосферу (газовая оболочка), гидросферу (жидкая оболочка), литосферу (твердая оболочка из относительно легких веществ и элементов), мантию (промежуточная оболочка между литосферой и ядром) и ядро (самые тяжелые вещества и элементы, находящиеся в центре планеты под высоким давлением).<br /><br />\n      Основное влияние на плотность оказывает ядро. Именно оно содержит основную массу планеты, поскольку плотность химических соединений убывает по мере подъема от центра планеты к её поверхности.<br /><br />\n      Таким образом именно тип ядра полностью определяет виды и уровень добычи полезных ископаемых. Комбинации типов планетарных ядер с разными температурными режимами и размерами дают всё многообразие известных планет.<br /><br />\n      Для примера рассмотрим планету с ледяным ядром. Оно состоит из водяного льда с небольшой примесью других веществ, мантия - из твердого метана, а литосфера - из кристаллического водорода.<br />\n      В подавляющем большинстве такие планеты встречаются на периферии звездных систем. И выглядят они под стать своему названию: гигантские глыбы из смеси льда.<br />\n      Впрочем, иногда такая планета может оказаться и на низкой орбите вокруг звезды. Обычно это происходит после столкновения звездных систем. Так же звезда может захватить своим тяготением какую-нибудь блуждающую планету.<br />\n      Близость к звезде преображает планету. На ней образуется быстроулетучивающаяся атмосфера из легких газов (водород и гелий), а иногда даже и гидросфера из жидкого водорода и метана. При этом ядро планеты по-прежнему остается ледяным.<br />\n      Существование такой планеты чрезвычайно коротко во вселенских масштабах, но учитывая те же масштабы, обнаружить такую планету вполне возможно.\n      <br />\n      <br />\n\n      Тип ядра можно сменить за ТМ на странице <a href=\"overview.php?mode=manage\" class=\"link ok\">\"Управление планетой\"</a> (пункт меню \"Планета\", кнопка \"Управление\" на странице).\n      Стоимость смены типа ядра зависит от количества секторов самой планеты без учета секторов от Терраформеров, но учитывая сектора, купленные за ТМ. Стоимость нового типа ядра не зависит от текущего.\n      Добыча ресурсов в пределах одного класса приблизительно равны в пересчете на потраченную ТМ (как определяется добыча - см. ниже после таблицы).\n      Ядра более редкого класса стоят дороже, но и дают заметно больше ресурсов (см. выше).<br /><br />\n\n      Всего существует 10 типов ядер, рассортированных на четыре класса:\n      <table>\n        <tr class=\"c_c\">\n          <th rowspan=\"2\">Тип ядра</th>\n          <th colspan=\"2\">Плотность, кг/м&sup3;</th>\n          <th colspan=\"3\">Добыча</th>\n          <th rowspan=\"2\">Класс</th>\n        </tr>\n        <tr class=\"c_c\">\n          <th>Минимальная</th>\n          <th>Максимальная</th>\n          <th>Металл</th>\n          <th>Кристалл</th>\n          <th>Дейтерий</th>\n        </tr>\n\n        <tr class=\"c_c\">\n          <th>Водородный лёд</th>\n          <td>250</td>\n          <td>750</td>\n          <td class=\"error\">Очень плохая</td>\n          <td class=\"warning\">Плохая</td>\n          <td class=\"positive\">Отличная</td>\n          <td class=\"error\">Раритет</td>\n        </tr>\n\n        <tr class=\"c_c\">\n          <th>Метановый лёд</th>\n          <td>750</td>\n          <td>1250</td>\n          <td class=\"warning\">Плохая</td>\n          <td class=\"notice\">Неплохая</td>\n          <td class=\"positive\">Очень хорошая</td>\n          <td class=\"warning\">Редкий</td>\n        </tr>\n\n        <tr class=\"c_c\">\n          <th>Водный лёд</th>\n          <td>1250</td>\n          <td>2000</td>\n          <td class=\"notice\">Неплохая</td>\n          <td class=\"notice\">Неплохая</td>\n          <td class=\"positive\">Хорошая</td>\n          <td class=\"notice\">Продвинутый</td>\n        </tr>\n\n        <tr class=\"c_c\">\n          <th>Кристалл</th>\n          <td>2000</td>\n          <td>2500</td>\n          <td class=\"error\">Очень плохая</td>\n          <td class=\"positive\">Отличная</td>\n          <td class=\"warning\">Плохая</td>\n          <td class=\"error\">Раритет</td>\n        </tr>\n\n        <tr class=\"c_c\">\n          <th>Силикат</th>\n          <td>2500</td>\n          <td>3500</td>\n          <td class=\"warning\">Плохая</td>\n          <td class=\"positive\">Очень хорошая</td>\n          <td class=\"notice\">Неплохая</td>\n          <td class=\"warning\">Редкий</td>\n        </tr>\n\n        <tr class=\"c_c\">\n          <th>Камень</th>\n          <td>3500</td>\n          <td>4750</td>\n          <td class=\"notice\">Неплохая</td>\n          <td class=\"positive\">Хорошая</td>\n          <td class=\"notice\">Неплохая</td>\n          <td class=\"notice\">Продвинутый</td>\n        </tr>\n\n        <tr class=\"c_c\">\n          <th>Стандарт</th>\n          <td>4750</td>\n          <td>5750</td>\n          <td>Стандартная</td>\n          <td>Стандартная</td>\n          <td>Стандартная</td>\n          <td>Базовый</td>\n        </tr>\n\n        <tr class=\"c_c\">\n          <th>Руда</th>\n          <td>5750</td>\n          <td>7000</td>\n          <td class=\"positive\">Хорошая</td>\n          <td class=\"notice\">Неплохая</td>\n          <td class=\"notice\">Неплохая</td>\n          <td class=\"notice\">Продвинутый</td>\n        </tr>\n        <tr class=\"c_c\">\n          <th>Оливин</th>\n          <td>7000</td>\n          <td>8250</td>\n          <td class=\"positive\">Очень хорошая</td>\n          <td class=\"notice\">Неплохая</td>\n          <td class=\"warning\">Плохая</td>\n          <td class=\"warning\">Редкий</td>\n        </tr>\n        <tr class=\"c_c\">\n          <th>Металл</th>\n          <td>8250</td>\n          <td>9500</td>\n          <td class=\"positive\">Отличная</td>\n          <td class=\"error\">Очень плохая</td>\n          <td class=\"error\">Очень плохая</td>\n          <td class=\"error\">Раритет</td>\n        </tr>\n      </table><br />\n\n      Класс ядра, который можно найти в экспедиции, ограничен эффективным уровнем Астрокартографии (т.е. уровень с учётом всех бонусов: от родного мира, Премиум-аккаунта, внутриигровых акций итд).\n      Это сделано для облегчения игры для новичков и уравнивания стартовых условий\n      Например, если первая же найденная колония имеет тип ядра \"Водородный лёд\" - новый игрок получает сильное пенальти на развитие планеты (планеты \"Раритетного\" класса очень сложно застраивать сам-один и для нормальной застройки требуется транспорт других типов ресурсов с остальных планет).\n      <ul>\n        <li>Если эффективный уровень Астрокартографии меньше 6 - можно найти планеты с ядрами классов \"Стандарт\" и \"Продвинутый\";</li>\n        <li>6-10 - можно так же найти планеты с ядрами \"Редкого\" класса;</li>\n        <li>Более 11 - можно так же найти планеты с ядрами \"Раритетного\" класса.</li>\n      </ul>\n      <br />\n\n      Добыча считается в процентах от добычи указанного типа ресурсов на стандартном типе ядра в пересчете на металл приведенные к стоимости шахты:\n      <ul>\n        <li><span class=\"error\">Очень плохая</span> - менее 40%</li>\n        <li><span class=\"warning\">Плохая</span> - не меньше 40%, но менее 80% </li>\n        <li><span class=\"notice\">Неплохая</span> - не меньше 80%, меньше 100% </li>\n        <li>Неплохая - 100% базовой добычи</li>\n        <li><span class=\"ok\">Хорошая</span> - больше 100%, но меньше 300%</li>\n        <li><span class=\"ok\">Очень хорошая</span> - больше 300%, но меньше 400%</li>\n        <li><span class=\"ok\">Отличная</span> - больше 400%</li>\n      </ul><br/>\n      Класс типа ядра определяется в процентах от добычи всех ресурсов на стандартном типе ядра в пересчете на металл вычисленные для шахт эквивалентной стоимости предельно-достижимого уровня:\n      <ul>\n        <li>\"Базовый\" класс ядер - 100% от базовой добычи. Включает только ядра типа \"Стандарт\" - самый распространенный тип ядра планеты (примерно треть от всех планет)</li>\n        <li>\n          <span class=\"notice\">Продвинутый</span> - более 100%, но менее 150% от базовой добычи.\n            Включает в себя ядра типа \"Руда\", \"Камень\" и \"Водный лёд\".\n            Самый распространенный класс ядер - примерно половина планет содержат ядра этого класса;\n        </li>\n        <li><span class=\"warning\">Редкий</span> - не меньше 150%, но менее 250% от базовой добычи. Включает в себя ядра типа \"Оливин\", \"Силикат\" и \"Метановый лёд\". Как не сложно понять из названия класса - встречается редко;</li>\n        <li><span class=\"error\">Раритет</span> - не меньше 250% от базовой добычи. Включает в себя ядра типа \"Металл\", \"Кристалл\" и \"Водородный лёд\". Встречается ОЧЕНЬ редко - менее 5% планет содержат ядра этого класса.</li>\n      </ul><br />\n',\n    ),\n\n    UNIT_CAN_NOT_BE_BUILD => array(\n      'description'       => 'Этот юнит не может быть построен игроком в настоящее время. Нет, его нельзя купить. Возможно, его можно было купить/построить раньше. И нет, не спрашивайте можно ли будет его построить когда-либо или получить как-нибудь. Сие неизвестно есть<br/>Хотя... может быть его можно получить другими способами? Спросите у других игроков... У АД-ии спрашивать бесполезно',\n      'description_short' => 'В настоящее время этот юнит невозможно построить или купить',\n      'effect'            => 'Этот юнит невозможно построить или купить',\n    ),\n  )\n);\n"
  },
  {
    "path": "language/en/language.mo.php",
    "content": "<?php\n/*\n#############################################################################\n#  Filename: language.mo\n#  Create date: Sunday, March 30, 2008   19:56:23\n#  Project: SuperNova.WS\n#  Description: Massive Multiplayer Online Browser Space Strategy Game\n#\n#  Copyright © 2009-2018 Gorlum for Project \"SuperNova.WS\"\n#  Copyright © 2009 MSW\n#  Copyright © 2008 Aleksandar Spasojevic <spalekg@gmail.com>\n#  Copyright © 2005 - 2008 KGsystem\n#############################################################################\n*/\n\n/**\n*\n* system [English]\n*\n* @package language\n* @version $Id$\n*\n*/\n\n/**\n* DO NOT CHANGE\n*/\nif (!defined('INSIDE')) \n{\n\tdie('Hack attempt!');\n}\n\n$lang_info = array(\n  'LANG_VERSION'      => 'V26e20',\n  'LANG_DIRECTION'    => 'ltr',\n\n  'LANG_NAME_NATIVE'  => 'English',\n  'LANG_NAME_ENGLISH' => 'English',\n  'LANG_FLAG'         => 'en.png',\n  'LANG_FLAG_MEDIUM'  => 'en-UK_medium.png',\n  'LANG_NAME_ISO2'    => 'en',\n  'LANG_NAME_ISO3'    => 'eng',\n\n  'LANG_VARIANTS'     => array(\n    array(\n      'LANG_NAME_NATIVE'  => 'US English',\n      'LANG_NAME_ENGLISH' => 'US English',\n      'LANG_FLAG'         => 'en.png',\n      'LANG_FLAG_MEDIUM'  => 'en-US_medium.png',\n      'LANG_NAME_ISO2'    => 'en',\n      'LANG_NAME_ISO3'    => 'eng',\n    ),\n    array(\n      'LANG_NAME_NATIVE'  => 'English',\n      'LANG_NAME_ENGLISH' => 'English',\n      'LANG_FLAG'         => 'en.png',\n      'LANG_FLAG_MEDIUM'  => 'en-UK_medium.png',\n      'LANG_NAME_ISO2'    => 'en',\n      'LANG_NAME_ISO3'    => 'eng',\n    ),\n  ),\n\n  'LANG_MAINTAINER'   => 'madmax1991',\n  'LANG_HOMEPAGE'     => 'http://forum.supernova.ws/viewforum.php?f=73',\n\n  'LANG_COPYRIGHT'    => array(\n    'Copyright &copy; 2011 madmax1991 for Project &quot;SuperNova.WS&quot;',\n    'Copyright &copy; 2009 Gorlum for Project &quot;SuperNova.WS&quot;',\n    'Copyright &copy; 2008 Aleksandar Spasojevic &lt;spalekg@gmail.com&gt;',\n    'Copyright &copy; 2005 - 2008 KGsystem',\n  ),\n\n);\n"
  },
  {
    "path": "language/en/login.mo.php",
    "content": "<?php\n\n/*\n#############################################################################\n#  Filename: login.mo\n#  Project: SuperNova.WS\n#  Website: http://www.supernova.ws\n#  Description: Massive Multiplayer Online Browser Space Strategy Game\n#\n#  Copyright © 2009-2018 Gorlum for Project \"SuperNova.WS\"\n#  Copyright © 2008 Aleksandar Spasojevic <spalekg@gmail.com>\n#  Copyright © 2005 - 2008 KGsystem\n#############################################################################\n*/\n\n/**\n*\n* @package language\n* @system [English]\n* @version 45d0\n*\n*/\n\n/**\n* DO NOT CHANGE\n*/\n\nif (!defined('INSIDE')) die();\n\nglobal $config;\n\n$a_lang_array = (array(\n  'Login' => 'Login',\n  'User_name' => 'Username:',\n  'Authorization' => 'Authorization',\n  'Please_Login' => 'You are welcome <a href=\"login.php\" target=\"_main\">login...</a>',\n  'Please_Wait' => 'Please wait',\n  'Remember_me' => 'Remember me',\n  'Register' => 'Information about the error',\n  'Login_Error' => 'Login error',\n  'PleaseWait' => 'Please wait',\n  'PasswordLost' => 'Lost password?',\n  'Login_Ok' => 'Successfully connected, <a href=\"./\"><blink>redirection...</blink></a><br><center><img src=\"design/images/progressbar.gif\"></center>',\n  'Login_FailPassword' => 'Incorrect name and/or password<br /><a href=\"login.php\" target=\"_top\">Back</a>',\n  'Login_FailUser' => 'This player does not exist.<br><a href=login.php>Back</a>',\n  'log_univ' => 'Universe log!',\n  'log_reg' => 'Register',\n  'log_reg_main' => 'Rules!',\n  'log_menu' => 'Menu',\n  'log_stat_menu' => 'Statistics',\n  'log_enter' => 'Login',\n  'log_news' => 'Server announces',\n  'log_cred' => 'About server',\n  'log_faq' => 'FAQ',\n  'log_forums' => 'Forum',\n  'log_contacts' => 'Administration',\n  'log_desc' => '<strong>Supernova is a browser based online multiplayer space strategy.</strong> Thousands of players are simultaneously against one another. For the game you need only a browser.',\n  'log_toreg' => 'Sign up now!',\n  'log_online' => 'Players Online',\n  'log_lastreg' => 'Newbie',\n  'log_numbreg' => 'Total accounts',\n  'log_welcome' => 'Welcome to',\n  'vacation_mode' => 'Your in vacation Mode<br> You can turn off vacation mode ',\n  'hours' => ' Hours',\n  'vacations' => 'Vacation Mode',\n  'log_scr1' => 'Screenshot of shipyard, where ships are built and ordered on the current planet. Click image to enlarge.',\n  'log_scr2' => 'Screenshot of Statistics, there are shows your ranking among other players on various parameters. Click image to enlarge.',\n  'log_scr3' => 'Screenshot of the universe, here you can see your planet in the universe, and find the planets of other players. Click image to enlarge.',\n  'log_rules' => 'Rules of the game',\n  'log_banned' => 'List of currently banned',\n  'log_see_you' => 'Hope to see you again at the expanse of our universe. Good luck!<br><a href=\"login.php\">Go to the login page in the game</a>',\n  'log_session_closed' => 'Session closed.',\n  'registry' => 'Registration',\n  'form' => 'Registration form',\n  'Undefined' => '- undetermined -',\n  'Male' => 'Male',\n  'Female' => 'Female',\n  'Multiverse' => 'XNova',\n  'E-Mail' => 'E-Mail address',\n  'MainPlanet' => 'The name of your planet',\n  'GameName' => 'Game name',\n  'gender' => 'Gender',\n  'accept' => 'Accept',\n  'reg_i_agree' => 'I have read and agree with',\n  'reg_with_rules' => 'Rules of the game',\n  'signup' => 'Register',\n  'Languese' => 'Language',\n  'log_reg_text0' => 'Before registering please read',\n  'log_reg_text1' => 'Registration means that you have read and fully agree with all points of the rules. If you do not agree with any paragraph rules-please register.',\n  'thanksforregistry' => 'Congratulations on your successful registration! You will be redirected to the main page of your planet in 10 seconds, if it did not click on this <a href=overview.php><u>link!</u></a>!',\n  'welcome_to_universe' => 'Welcome to OGame!!!',\n  'please_click_url' => 'In order to use the account, you must activate it by clicking on this link',\n  'regards' => 'Good luck!',\n  'error_lang' => 'This language is not supported!<br />',\n  'error_mail' => 'Wrong E-Mail !<br />',\n  'error_planet' => 'Another planet has the same name !<br />',\n  'error_hplanetnum' => 'Name the planet must be written with Latin letters ONLY !<br />',\n  'error_character' => 'Incorrect name !<br />',\n  'error_charalpha' => 'You can use only letters !<br />',\n  'error_password' => 'Password must be at least 4 characters !<br />',\n  'error_rgt' => 'You must comply with the rules !<br />',\n  'error_userexist' => 'This name is already in use !<br />',\n  'error_emailexist' => 'This e-mail is already in use !<br />',\n  'error_sex' => 'Error in the choice of gender !<br />',\n  'error_mailsend' => 'Error in sending the email, your password: ',\n  'reg_welldone' => 'Registration complete! Your password was specified when registering the mailbox. Here it is again just in case',\n  'error_captcha' => 'The wrong graphic code !<br/>',\n  'error_v' => 'Try it again !<br />',\n  'log_login_page' => 'Enter the game',\n  'log_reg_already' => 'Already have a registration? ',\n  'log_reg_already_lost' => 'Forgot password?',\n\n  'log_lost_header' => 'Password recovery',\n  'log_lost_code' => 'Confirmation code',\n  'log_lost_description1' => 'Enter the email address you registered your account. We will send an email with a verification code to reset your password',\n\n  'log_lost_send_mail' => 'Send confirmation code',\n  'log_lost_description2' => 'If you have a confirmation code, please enter it below and click \"Reset password\". An e-mail will be sent an email with a new password<br /><br />\n    If you already asking for confirmation code but can not find email from us in your main folder - check your SPAM folder. Some mail servers can mark our letters as \"SPAM\"<br /><br />\n    If you absolutly sure that did not receive email from us - just write a email to Administration address <span class=\"ok\">' . $config->server_email . '</span>',\n  'log_lost_reset_pass' => 'Reset password',\n  'log_lost_sent_code' => 'Email sent to this email with further instructions on resetting your password',\n  'log_lost_sent_pass' => 'Just sent to your email message with the new password',\n  'log_lost_err_email' => 'This email is not registered in the database. This could mean one of the following:<br>You miss typed the email. Return to the previous page and try again<br>Your account has been deleted due to inactivity. Register a new<br>You are trying to enter the wrong Gaming Universe. Double check the name of the current Universe and on Error Go To correct Universe',\n  'log_lost_err_sending' => 'Error sending message to the specified by email. Notify Administrator of the error',\n  'log_lost_err_code' => 'The verification code is not registered in the database. This could mean one of the following:<br>You mistype the confirmation code. Return to the previous page and enter the code<br>You are trying to enter the confirmation code in the wrong Universe for which it was generated. Double check the name of the current Universe and on Error Go To correct Universe<br>Your account has been deleted due to inactivity. Register a new<br>Expired confirmation code. Check the expiration date of code in the letter. If it passed, request a new confirmation code',\n  'log_lost_err_admin' => 'The members server command (moderators, operators, administrators, etc) may not use the password reset function. Contact your server administrator to change the password',\n  'log_lost_err_change' => 'Error changing the password in the database. Notify Administrator of error',\n\n  'login_register_offer' => 'Click here to register',\n  'login_password_restore_offer' => 'Click here to reset password',\n\n  'login_register_email_hint' => 'Указывайте работающий e-mail - владельцем аккаунта считается владелец указанного e-mail<br />\n    Постарайтесь не использовать ящики на mail.ru',\n\n  'login_account_name_or_email' => 'E-mail',\n\n));\n"
  },
  {
    "path": "language/en/market.mo.php",
    "content": "<?php\n\n/*\n#############################################################################\n#  Filename: market.mo\n#  Project: SuperNova.WS\n#  Website: http://www.supernova.ws\n#  Description: Massive Multiplayer Online Browser Space Strategy Game\n#\n#  Copyright © 2009-2018 Gorlum for Project \"SuperNova.WS\"\n#############################################################################\n*/\n\n/**\n*\n* @package language\n* @system [English]\n* @version 43a16.13\n* @condition clear\n*\n*/\n\n/**\n* DO NOT CHANGE\n*/\n\nif (!defined('INSIDE')) die();\n\n\n$a_lang_array = (array(\n  'eco_mrk_title' => 'Market',\n  'eco_mrk_description' => 'Very strange but you can not recognize any mention of this menu item in documentation. Where it came from?',\n  'eco_mrk_service' => 'Service',\n  'eco_mrk_service_cost' => 'Serivce cost',\n\n  'eco_mrk_trader_do' => 'Exchange resources',\n  'eco_mrk_trader' => 'Exchange resources',\n  'eco_mrk_trader_cost' => 'Cost sharing resources',\n  'eco_mrk_trader_exchange' => 'Exchange',\n  'eco_mrk_trader_to' => 'Exchanged for',\n  'eco_mrk_trader_course' => 'Course',\n  'eco_mrk_trader_left' => 'Exchange result',\n  'eco_mrk_trader_resources_all' => 'All resources',\n  'eco_mrk_trader_exchange_dm_confirm' => 'Are you sure that you want to trade {0} Dark Matter for resources?',\n\n  'eco_mrk_scraper_do' => 'Scrap ships for resources',\n  'eco_mrk_scraper' => 'Scrap ships for resources',\n  'eco_mrk_scraper_price' => 'Scrap output',\n  'eco_mrk_scraper_perShip' => 'from ship',\n  'eco_mrk_scraper_total' => 'Total',\n  'eco_mrk_scraper_cost' => 'Sell ships for scrap costs',\n  'eco_mrk_scraper_onOrbit' => 'In orbit',\n  'eco_mrk_scraper_to' => 'Allow for scrapping',\n  'eco_mrk_scraper_res' => 'The following scrap:',\n  'eco_mrk_scraper_ships' => 'Put the following ships for scrap:',\n  'eco_mrk_scraper_noShip' => 'There spacecrafts in orbit',\n\n  'eco_mrk_stockman_do' => 'Buy s/h ships',\n  'eco_mrk_stockman' => 'Buy s/h ships',\n  'eco_mrk_stockman_price' => 'Price',\n  'eco_mrk_stockman_perShip' => 'Ship',\n  'eco_mrk_stockman_onStock' => 'From the seller',\n  'eco_mrk_stockman_buy' => 'Buy ships',\n  'eco_mrk_stockman_res' => 'Cost of purchased ships:',\n  'eco_mrk_stockman_ships' => 'Purchased the following ships:',\n  'eco_mrk_stockman_noShip' => 'The seller now has no ships for sale',\n\n  'eco_mrk_exchange' => 'Resource Exchange',\n  'eco_mrk_banker' => 'Banker',\n  'eco_mrk_pawnshop' => 'Pawnshop',\n\n  'eco_mrk_info_do' => 'Buy information',\n  'eco_mrk_info' => 'Infotrader',\n  'eco_mrk_info_description' => 'You discover in your inbox letter from unknown source. It says exactly:',\n  'eco_mrk_info_description_2' => 'I have access to plenty of interest information. I can share it with your... for a reward, of course. On request will cost you only',\n  'eco_mrk_info_buy' => 'Buy infopacket',\n\n  'eco_mrk_info_player' => 'Info about player',\n  'eco_mrk_info_player_description' => 'I can tell you which Mercenaries currently working under player\\'s rule',\n  'eco_mrk_info_player_message' => 'As far as I know list of player ID %1$d [%2$s] Mercenaries for now looks like this:',\n\n  'eco_mrk_info_not_hired' => 'not hired',\n\n  'eco_mrk_info_ally' => 'Info about Alliance',\n  'eco_mrk_info_online' => 'Current activity on Universe',\n\n  'eco_mrk_info_msg_from' => 'Untracible source',\n\n  'eco_mrk_error_title' => 'Market - Error',\n  'eco_mrk_errors' => array(\n    MARKET_RESOURCES => 'The operation was a success',\n    MARKET_SCRAPPER => 'Exchange of resources occurred successfully',\n    MARKET_NOT_A_SHIP => 'Do not try to sell anything other than a ship!',\n    MARKET_STOCKMAN => 'Dark matter is missing to complete the operation',\n    MARKET_NO_RESOURCES => 'Not enough resources to complete operation',\n    MARKET_PAWNSHOP => 'You are trying to send more ships for scrap than there are in orbit',\n    MARKET_NO_STOCK => 'You are trying to buy more ships than the seller. This might not have selected ships, someone else has already bought them',\n    MARKET_ZERO_DEAL => 'Do not specify the number of resources for sharing',\n    MARKET_NOTHING => 'Select ships for sale',\n    MARKET_ZERO_RES_STOCK => 'Select ships for purchase',\n    MARKET_NEGATIVE_SHIPS => 'Do not try to sell a negative number of ships!',\n\n    MARKET_NO_DM => 'There is not enough DM to complete operation',\n    MARKET_INFO_WRONG => 'I did not sell this kind of information',\n    MARKET_INFO_PLAYER => 'Information bought succesfully. Check your Personal Mail',\n    MARKET_INFO_PLAYER_WRONG => 'You should specify player name or ID',\n    MARKET_INFO_PLAYER_NOT_FOUND => 'Can not identify player. If player name consists from unreadable symbols or plain numbers - try to use player ID',\n    MARKET_INFO_PLAYER_SAME => 'Why would you like to pay for info about yourself?',\n  ),\n\n));\n"
  },
  {
    "path": "language/en/menu.mo.php",
    "content": "<?php\n\n/*\n#############################################################################\n#  Filename: leftmenu.mo\n#  Project: SuperNova.WS\n#  Website: http://www.supernova.ws\n#  Description: Massive Multiplayer Online Browser Space Strategy Game\n#\n#  Copyright © 2009-2018 Gorlum for Project \"SuperNova.WS\"\n#  Copyright © 2008 Aleksandar Spasojevic <spalekg@gmail.com>\n#  Copyright © 2005 - 2008 KGsystem\n#############################################################################\n*/\n\n/**\n*\n* @package language\n* @system [English]\n* @version 43a16.13\n*\n*/\n\n/**\n* DO NOT CHANGE\n*/\n\nif (!defined('INSIDE')) die();\n\n\n$a_lang_array = (array(\n  'Multiverse' => '<b>Sever</b> Uni',\n  'm_h_rules' => 'Rules',\n  'm_faq' => 'FAQ',\n  'm_faq_hint' => 'The most frequently asked customer questions and answers',\n  'm_h_control' => 'Office',\n  'm_forum' => 'Forum',\n  'm_others' => 'Miscellaneous',\n  'm_simulator' => 'Battle Simulator',\n  'm_communication' => 'Link & Communication',\n  'm_exchange' => 'Exchange resources',\n  'm_affilates' => 'Earn DM!',\n  'Overview' => 'Overview',\n  'Officiers' => 'Officers',\n  'Building' => 'Construction',\n  'Research' => 'Research',\n  'Shipyard' => 'Shipyard',\n  'Defense' => 'Defense',\n  'Resources' => 'Resources',\n  'Imperium' => 'Imperium',\n  'Marchand' => 'Trader',\n  'Annonces' => 'Ads',\n  'Technology' => 'Technology',\n  'Galaxy' => 'Galaxy',\n  'lm_fleet_orbiting' => 'Fleet in orbit',\n  'Alliance' => 'Alliance',\n  'Allianc' => 'Alliance',\n  'AllyChat' => 'Alliance Chat',\n  'Statistics' => 'Statistics',\n  'Search' => 'Search',\n  'Records' => 'Highscores',\n  'Messages' => 'Messages',\n  'Notes' => 'Notes',\n  'Buddylist' => 'Buddy List',\n  'Chat' => 'Chat',\n  'Contact' => 'Administration',\n  'Options' => 'Settings',\n  'Bug' => 'Bug',\n  'Logout' => 'Log out',\n  'Rules' => 'Rules',\n  'devlp' => 'Development',\n  'navig' => 'Information',\n  'Economy' => 'Economy',\n  'trade' => 'Trade',\n  'Society' => 'Society',\n  'rinok' => 'Market',\n  'observ' => 'Observatory',\n  'commun' => 'Administration',\n  'infog' => 'Information',\n  'lm_combat_reports' => 'Combat reports',\n  'adm_over' => 'Overview',\n  'adm_conf' => 'Settings',\n  'adm_reset' => 'Reset',\n  'adm_plrlst' => 'Players List',\n  'adm_panel' => 'Admin Panel',\n  'adm_plrsch' => 'Player Search',\n  'adm_addres' => 'Add Resources',\n  'adm_pltlst' => 'Planet List',\n  'adm_actplt' => 'Active Planets',\n  'adm_moonlst' => 'Moon List',\n  'adm_addmoon' => 'Add Moon',\n  'adm_fleet' => 'Fleets in Flight',\n  'adm_ban' => 'Ban',\n  'adm_unban' => 'UnBan',\n  'adm_chat' => 'Chat Editor',\n  'adm_updpt' => 'Update Stat',\n  'adm_msg' => 'Message List',\n  'adm_md5' => 'Encryption',\n  'adm_updrank' => 'Reset Databases',\n  'adm_log_main' => 'Log records',\n  'adm_help' => 'Developers Forum',\n  'adm_back' => 'Return',\n  'admin' => 'Administration',\n  'player' => 'Players',\n  'tool' => 'Utilities',\n  'lm_ifo_serv' => 'Raw Materials',\n  'lm_ifo_game' => 'Game',\n  'lm_ifo_fleet' => 'Fleet',\n  'lm_ifo_queue' => 'Queue',\n  'lm_shortcuts' => 'Notes/Shortcuts',\n  'lm_banned' => 'Ban List',\n  'lm_announce_fresh' => 'Update',\n  'lm_server_info' => 'Server',\n\n  'menu_quest_list' => 'Quest list',\n  'menu_universe_overview' => 'Universe overview',\n  'menu_stat_players' => 'Players statistics',\n  'menu_stat_records' => 'Universe records',\n  'menu_races' => 'Homeworlds',\n\n  'menu_hide' => '<<',\n  'menu_show' => '>>',\n\n  'menu_pin' => 'Pin menu',\n  'menu_unpin' => 'Unpin menu',\n\n  'menu_admin_modules' => 'Modules',\n));\n"
  },
  {
    "path": "language/en/messages.mo.php",
    "content": "<?php\n\n/*\n#############################################################################\n#  Filename: messages.mo\n#  Project: SuperNova.WS\n#  Website: http://www.supernova.ws\n#  Description: Massive Multiplayer Online Browser Space Strategy Game\n#\n#  Copyright © 2009-2018 Gorlum for Project \"SuperNova.WS\"\n#  Copyright © 2008 Aleksandar Spasojevic <spalekg@gmail.com>\n#  Copyright © 2005 - 2008 KGsystem\n#############################################################################\n*/\n\n/**\n*\n* @package language\n* @system [English]\n* @version 45d0\n*\n*/\n\n/**\n* DO NOT CHANGE\n*/\n\nif (!defined('INSIDE')) die();\n\n$a_lang_array = [\n  'msg_page_header' => 'Personal messages',\n  'msg_head_type' => 'Category',\n  'msg_head_count' => 'Unread',\n  'msg_head_total' => 'Total',\n  'msg_mark_select' => '-- SELECT RANGE --',\n  'msg_mark_checked' => 'Marked messages',\n  'msg_mark_unchecked' => 'Unmarked messages',\n  'msg_mark_class' => 'All messages in category',\n  'msg_mark_all' => 'ALL PERSONAL MESSAGES',\n  'msg_select_all' => 'Select All',\n  'msg_delete_checked' => 'Delete marked messages',\n  'msg_show_all' => 'Show all',\n  'msg_date' => 'Date',\n  'msg_from' => 'From',\n  'msg_recipient' => 'To',\n  'msg_subject' => 'Subject',\n  'msg_answer' => 'Answer',\n  'msg_answer_prefix' => 'RE:',\n  'msg_compose' => 'Write message',\n  'msg_text' => 'Message',\n  'msg_subject_default' => 'New message',\n  'msg_not_message_sent' => 'Message succesfully sent',\n  'msg_warn_no_messages' => 'No messages in this category',\n  'msg_err_player_not_found' => 'Player not found',\n  'msg_err_no_text' => 'You can not send empty message',\n  'msg_err_self_send' => 'You can not send message to yourself',\n  'msg_del_class' => 'Delete all messages in this category',\n  'msg_page_hint_class' =>\n    '<ul>\n      <li>Category \"Sent messages\" contains messages sent by you AND did not yet deleted by recipient. You can not delete messages from this category</li>\n      <li>To delete all messages of one category press delete icon in according row</li>\n      <li>Deleting messages from category \"All messages\" will lead to clear whole messagebox</li>\n      <li>Slow connection and/or large ammount of messages on one category can lead to unability to browse through messages. In such case you should clear according message category and/or clear whole messagebox</li>\n    </ul>',\n  'msg_header_dialog' => 'Dialog with',\n\n  'msg_ignore' => 'Игнорировать',\n  'msg_ignore_title' => \"Добавить игрока [PLAYER_NAME] в игнор-лист?\",\n  'msg_ignore_message' => \"Вы больше не увидите личных сообщений от игрока в игнор-листе.<br><br>Вы можете управлять своим игнор-листом на странице 'Настройки'.<br><br>Добавить игрока [PLAYER_NAME] в игнор-лист?\",\n  'msg_message' => 'Сообщение',\n  'msg_ignored_messages' => 'сообщений от пользователей в игнор-листе не показано',\n  'msg_ignore_control' => 'Вы можете управлять игнор-листом на странице \"Настройки\"',\n];\n"
  },
  {
    "path": "language/en/mrc_mercenary.mo.php",
    "content": "<?php\n\n/*\n#############################################################################\n#  Filename: mercenary.mo\n#  Project: SuperNova.WS\n#  Website: http://www.supernova.ws\n#  Description: Massive Multiplayer Online Browser Space Strategy Game\n#\n#  Copyright © 2009-2018 Gorlum for Project \"SuperNova.WS\"\n#############################################################################\n*/\n\n/**\n*\n* @package language\n* @system [English]\n* @version 43a16.13\n*\n* @clean - all constants is used\n*\n*/\n\n/**\n* DO NOT CHANGE\n*/\n\nif (!defined('INSIDE'))\n{\n  exit;\n}\n\n$a_lang_array = array(\n  'mrc_up_to' => 'up to',\n  'mrc_hire' => 'Hire',\n  'mrc_hire_for' => 'Hire for',\n  'mrc_allowed' => 'Allowed',\n  'mrc_msg_error_wrong_mercenary' => 'Wrong Mercenary ID',\n  'mrc_msg_error_wrong_level' => 'Wrong Mercenary level - too big or too small',\n  'mrc_msg_error_wrong_period' => 'Unacceptable hire period',\n  'mrc_msg_error_already_hired' => 'Mercenary already hired. Dismiss him or wait until hire period ends',\n  'mrc_msg_error_no_resource' => 'Not enough Dark Matter to hire Mercenary',\n  'mrc_msg_error_requirements' => 'Requirements not met',\n\n  'mrc_dismiss' => 'Dismiss',\n  'mrc_dismiss_confirm' => 'When you dismissing Mercenary you loose all DM that you spent for hiring this merc before! Are you sure that you want do dismiss Mercenary?',\n  'mrc_dismiss_before_hire' => 'To change level of recruited Mercenary you should before fire current one. You will lose all DM spent for current Mercenary!',\n\n  'mrc_mercenary_hired_log' => 'Hired Mercenary \"%1$s\" ID %2$d for %3$d DM for %4$d days',\n  'mrc_mercenary_dismissed_log' => 'LOST %7$d hire days and %8$d DM (current prices)! Dismissed Mercenary \"%1$s\" ID %2$d, hired for %4$d days (from %5$s to %6$s) which costs now %3$d DM',\n  'mrc_plan_bought_log' => 'Purchased Plan \"%1$s\" ID %2$d for %3$d DM',\n);\n"
  },
  {
    "path": "language/en/notes.mo.php",
    "content": "<?php\n\n/*\n#############################################################################\n#  Filename: notes.mo\n#  Project: SuperNova.WS\n#  Website: http://www.supernova.ws\n#  Description: Massive Multiplayer Online Browser Space Strategy Game\n#\n#  Copyright © 2009-2018 Gorlum for Project \"SuperNova.WS\"\n#############################################################################\n*/\n\n/**\n*\n* @package language\n* @system [English]\n* @version 43a16.13\n*\n*/\n\n/**\n* DO NOT CHANGE\n*/\n\n!defined('INSIDE') && die();\n\n$a_lang_array = array(\n  'note_page_header' => 'Notes',\n  'note_date' => 'Date',\n  'note_note' => 'Note',\n  'note_priority' => 'Priority',\n  'note_sticky' => 'Sticky',\n  'note_new_title' => 'New note title',\n  'note_new_text' => 'New note text',\n  'note_stick_it' => 'Stick this note under navbar on each page',\n\n  'note_err_none_added' => 'Note succesfully added',\n  'note_err_none_changed' => 'Note succesfully changed',\n  'note_err_note_not_found' => 'Note with this ID not found. Possibly it was already deleted',\n  'note_err_owner_wrong' => 'You are not owner of this note',\n  'note_err_note_empty' => 'You did not write anything in note - it will not be added',\n\n  'note_delete' => 'Delete notes',\n  'note_range_select' => '-- SELECT RANGE --',\n  'note_range_marked' => 'Marked notes',\n  'note_range_marked_not' => 'Not marked notes',\n  'note_range_all' => 'All notes',\n\n  'note_warn_no_range' => 'You did not select range - nothing to delete',\n  'note_err_none_selected' => 'There are no notes selected - nothing to delete. To delete all notes at once select range \"All notes\"',\n\n);\n"
  },
  {
    "path": "language/en/options.mo.php",
    "content": "<?php\n\n/*\n#############################################################################\n#  Filename: options.mo\n#  Project: SuperNova.WS\n#  Website: http://www.supernova.ws\n#  Description: Massive Multiplayer Online Browser Space Strategy Game\n#\n#  Copyright © 2009-2018 Gorlum for Project \"SuperNova.WS\"\n#  Copyright © 2008 Aleksandar Spasojevic <spalekg@gmail.com>\n#  Copyright © 2005 - 2008 KGsystem\n#############################################################################\n*/\n\n/**\n*\n* @package language\n* @system [English]\n* @version 45d0\n*\n*/\n\n/**\n* DO NOT CHANGE\n*/\n\nif (!defined('INSIDE')) die();\n\n$a_lang_array = [\n  'opt_account' => 'Account',\n  'opt_int_options' => 'Interface',\n  'opt_settings_statistics' => 'Player\\'s statistics',\n  'opt_settings_info' => 'Player\\'s info',\n  'opt_alerts' => 'Alerts',\n  'opt_common' => 'Common',\n  'opt_tutorial' => 'Tutorial',\n\n  'opt_birthday' => 'Birthday',\n\n  'opt_header' => 'User options',\n  'opt_messages' => 'Automatic alerts',\n  'opt_msg_saved' => 'Options succesfully saved',\n  'opt_msg_name_changed' => 'Username sucessfully changed',\n  'opt_msg_pass_changed' => 'Password sucessfully changed',\n  'opt_err_pass_wrong' => 'Wrong old password. Password was not changed',\n  'opt_err_pass_unmatched' => 'New password confirmation is not identical to new password. Password was not changed',\n\n  'opt_msg_name_change_err_used_name' => 'Someone else already owns this name',\n  'opt_msg_name_change_err_no_dm' => 'There is not enough DM to change name',\n\n  'username_old' => 'Current name',\n  'username_new' => 'New name',\n  'username_change_confirm' => 'Change name',\n  'username_change_confirm_payed' => 'for',\n\n  'changue_pass' => 'Change password',\n  'Download' => 'Download',\n  'userdata' => 'Information',\n  'username' => 'Username',\n  'lastpassword' => 'Old password',\n  'newpassword' => 'New password<br>(min. 8 characters)',\n  'newpasswordagain' => 'Repeat new password',\n  'emaildir' => 'E-mail address',\n  'emaildir_tip' => 'This address can be changed at any time. Address will be the main, if it has not been modified within 7 days.',\n  'permanentemaildir' => 'Main e-mail address',\n  'opt_planet_sort_title' => 'Order planets by',\n  'opt_planet_sort_options' => [\n    SORT_ID       => 'Colonization Time',\n    SORT_LOCATION => 'Coordinates',\n    SORT_NAME     => 'Name',\n    SORT_SIZE     => 'Size',\n  ],\n  'opt_planet_sort_ascending' => [\n    SORT_ASCENDING  => 'Ascending',\n    SORT_DESCENDING => 'Descending',\n  ],\n\n  'opt_navbar_title' => 'Navigation Panel',\n  'opt_navbar_description' => 'The navigation bar (or simply \"navbar\") is located at the very top of the screen. This section allows you to customize the look of the navbar.',\n  'opt_navbar_resourcebar_description' => 'Resourcebar - resource panel',\n  'opt_navbar_buttons_title' => 'Setup navbar buttons',\n  'opt_player_options' => [\n    PLAYER_OPTION_NAVBAR_PLANET_VERTICAL        => 'Vertical resourcebar',\n    PLAYER_OPTION_NAVBAR_PLANET_DISABLE_STORAGE => 'Disable storage capacity in resourcebar',\n    PLAYER_OPTION_NAVBAR_PLANET_OLD             => 'Use old tabled resource view',\n\n    PLAYER_OPTION_NAVBAR_RESEARCH_WIDE          => 'Wide Research button (old look)',\n    PLAYER_OPTION_NAVBAR_DISABLE_RESEARCH       => 'Disable Research button',\n    PLAYER_OPTION_NAVBAR_DISABLE_PLANET         => 'Disable Planet button',\n    PLAYER_OPTION_NAVBAR_DISABLE_HANGAR         => 'Disable Hangar button',\n    PLAYER_OPTION_NAVBAR_DISABLE_DEFENSE        => 'Disable Defense button',\n    PLAYER_OPTION_NAVBAR_DISABLE_EXPEDITIONS    => 'Disable Expeditions button',\n    PLAYER_OPTION_NAVBAR_DISABLE_FLYING_FLEETS  => 'Disable Flying Fleets button',\n    PLAYER_OPTION_NAVBAR_DISABLE_QUESTS         => 'Disable Quest button',\n    PLAYER_OPTION_NAVBAR_DISABLE_META_MATTER    => 'Disable MetaMatter button',\n\n    PLAYER_OPTION_UNIVERSE_OLD                  => 'Use the old view of the \"Survey of the Universe\"',\n    PLAYER_OPTION_UNIVERSE_DISABLE_COLONIZE     => 'Disable Colonization Button',\n    PLAYER_OPTION_DESIGN_DISABLE_BORDERS        => 'Disable table borders (if any)',\n    PLAYER_OPTION_TECH_TREE_TABLE               => 'View Technology Tree as table (old view)',\n    PLAYER_OPTION_FLEET_SHIP_SELECT_OLD         => 'Use old fleet selection view',\n    PLAYER_OPTION_FLEET_SHIP_HIDE_SPEED         => 'Do not show ship speed',\n    PLAYER_OPTION_FLEET_SHIP_HIDE_CAPACITY      => 'Do not show ship capacity',\n    PLAYER_OPTION_FLEET_SHIP_HIDE_CONSUMPTION   => 'Do not show ship fuel consumption',\n    PLAYER_OPTION_TUTORIAL_DISABLED             => 'Disable tutorial',\n    PLAYER_OPTION_TUTORIAL_WINDOWED             => 'Show tutorial in popup window',\n    PLAYER_OPTION_TUTORIAL_CURRENT              => 'Reset tutorial - tutorial will starts from begin',\n\n    PLAYER_OPTION_PLANET_SORT_INVERSE           => 'Reverse order',\n    PLAYER_OPTION_BUILD_AUTOCONVERT_HIDE        => 'Hide autoconvert button',\n\n    PLAYER_OPTION_SOUND_ENABLED                 => 'Enable game sounds',\n    PLAYER_OPTION_ANIMATION_DISABLED            => 'Disable animation effects',\n    PLAYER_OPTION_PROGRESS_BARS_DISABLED        => 'Disable progress bars',\n  ],\n\n  'opt_chk_skin' => 'Use skin',\n  'opt_adm_title' => 'Administration options',\n  'opt_adm_planet_prot' => 'Planetary protection',\n  'thanksforregistry' => 'Thanks for registering.<br />After a few minutes you will receive your message with a password.',\n  'general_settings' => 'General settings',\n  'skins_example' => 'Skin',\n\n  'opt_avatar' => 'Avatar',\n  'opt_avatar_remove' => 'Remove avatar',\n  'opt_avatar_search' => 'Search in Google',\n  'opt_upload' => 'Upload',\n\n  'opt_msg_avatar_removed' => 'Avatar succesfully removed',\n  'opt_msg_avatar_uploaded' => 'Avatar succesfully changed',\n  'opt_msg_avatar_error_delete' => 'Error deleting avatar file. Please, contact server Administration',\n  'opt_msg_avatar_error_writing' => 'Error saving avatar file. Please, contact server Administration',\n  'opt_msg_avatar_error_upload' => 'Error loading avatar image %1. Please, contact server Administration',\n  'opt_msg_avatar_error_unsupported' => 'Uploaded image format not supported. Only supported JPG, GIF, PNG up to 200KB',\n\n  'untoggleip' => 'Disable IP check',\n  'untoggleip_tip' => 'Check IP means that you will not be able to log in under his own name with two different IP. Testing gives you the advantage in security!',\n  'galaxyvision_options' => 'Universe',\n  'spy_cant' => 'Number of probes',\n  'spy_cant_tip' => 'Number of probes to be sent when you follow someone for.',\n  'tooltip_time' => 'Delay before show tooltip',\n  'mess_ammount_max' => 'The number of maximum fleet communications',\n  'seconds' => 'Second(s)',\n  'shortcut' => 'Quick access',\n  'show' => 'Show',\n  'write_a_messege' => 'Write a message',\n  'spy' => 'Espionage',\n  'add_to_buddylist' => 'Add as friend',\n  'attack_with_missile' => 'Missile attack',\n  'show_report' => 'View report',\n  'delete_vacations' => 'Account management',\n  'mode_vacations' => 'Turn vacation',\n  'vacations_tip' => 'Vacation mode is to protect the planet while you\\'re away.',\n  'deleteaccount' => 'Disable Account',\n  'deleteaccount_tip' => 'Account will be deleted after 45 days of no login.',\n  'deleteaccount_on' => 'If no activity this profile would be deleted on',\n  'save_settings' => 'Save the changes',\n  'exit_vacations' => 'Exit leave',\n  'Vaccation_mode' => 'Vacation mode is enabled. It runs until: ',\n  'You_cant_exit_vmode' => 'You can not exit leaves until time expires',\n  'Error' => 'Error',\n  'cans_resource' => 'Stop resource extraction on planets',\n  'cans_reseach' => 'Stop research on planets',\n  'cans_build' => 'Stop construction on the planets',\n  'cans_fleet_build' => 'Stop the construction of Ships and Defenses',\n  'cans_fly_fleet2' => 'Alien fleet approaches ... You can go on vacation',\n  'vacations_exit' => 'Vacation mode is disabled',\n  'select_skin_path' => 'SELECT',\n  'opt_language' => 'Interface language',\n  'opt_compatibility' => 'Compatibility - old interfaces',\n  'opt_compat_structures' => 'The old interface construction',\n  'opt_vacation_err_your_fleet' => 'Not to leave until the flight is at least one of your fleet',\n  'opt_vacation_err_building' => 'You are building or explore on %s and therefore you cannot leave on vacation',\n  'opt_vacation_err_research' => 'Your scientists do some research and therefore you cannot leave on vacation',\n  'opt_vacation_err_que' => 'There are research ongoing and/or some planet ques is not empty so you can not leave to vacation. Use Empire overview to find what happening',\n  'opt_vacation_err_timeout' => 'Vacancy timeout not reached',\n  'opt_vacation_next' => 'Next vacancy would be available after',\n  'opt_vacation_min' => 'a minimum of',\n  'succeful_changepass' => '',\n\n  'opt_time_diff_clear' => 'Measure difference between time on player side and time on server',\n  'opt_time_diff_manual' => 'Set time difference manually',\n  'opt_time_diff_explain' => 'When time difference set right clocks \"Time on player\" in navbar should click second to second with clocks on player\\'s device<br />\n  Usually game automatically detects time difference right. However when time zone is worng on player\\'s device or player used several devices for playing or when\n  internet connection is bad you should set time difference manually',\n\n  'opt_custom' => [\n    'opt_uni_avatar_user' => 'Show user avatar',\n    'opt_uni_avatar_ally' => 'Show Ally logo',\n    'opt_int_struc_vertical' => 'Vertical structures que',\n    'opt_int_navbar_resource_force' => 'Always show resourcebar',\n    'opt_int_overview_planet_columns' => 'Column count in planet list',\n    'opt_int_overview_planet_columns_hint' => '0 - calculate by maximum row count',\n    'opt_int_overview_planet_rows' => 'Maximum row count in planet list',\n    'opt_int_overview_planet_rows_hint' => 'Ignored if there is column count',\n  ],\n\n  'opt_mail_optional_description' => 'Personal messages from other players and notifications about internal game events (like combat reports, expedition reports etc) will be sent to this e-mail',\n  'opt_mail_permanent_description' => 'Your game account linked permanently to this e-mail. All system notices (like password change confirmation) will be sent to this address. You can enter this email only once',\n\n  'opt_account_name' => 'You login<br />Login is used to enter game. Usually this is email you entered on registration',\n  'opt_game_user_name' => 'Name in the game (nickname)<br />Other players in game will see your nickname - not your login',\n\n  'opt_universe_title' => 'Universe',\n\n  'option_fleets' => 'Fleets',\n  'option_fleet_send' => 'Fleet send',\n\n  'option_change_nick_disabled' => 'Player nickname change forbidden by server settings',\n\n  'opt_ignores' => 'Игнор-лист',\n  'opt_unignore_do' => 'Удалить из игнор-листа',\n  'opt_ignore_list_empty' => 'Ваш игнор-лист пуст',\n\n];\n"
  },
  {
    "path": "language/en/overview.mo.php",
    "content": "<?php\n\n/*\n#############################################################################\n#  Filename: overview.mo\n#  Project: SuperNova.WS\n#  Website: http://www.supernova.ws\n#  Description: Massive Multiplayer Online Browser Space Strategy Game\n#\n#  Copyright © 2009-2018 Gorlum for Project \"SuperNova.WS\"\n#  Copyright © 2008 Aleksandar Spasojevic <spalekg@gmail.com>\n#  Copyright © 2005 - 2008 KGsystem\n#############################################################################\n*/\n\n/**\n*\n* @package language\n* @system [English]\n* @version 43a16.13\n*\n*/\n\n/**\n* DO NOT CHANGE\n*/\n\nif (!defined('INSIDE')) die();\n\n\n$a_lang_array = (array(\n  'ov_hack_alert' => 'DATABASE hacking attempt!!!',\n  'ov_you_have' => 'You have',\n  'ov_new_message' => 'One new message',\n  'ov_new_messages' => 'New messages',\n  'cancel' => 'Cancel',\n  'Planet_menu' => 'Buildings on the planet',\n  'Planet' => 'Planet',\n  'Moon' => 'Moon',\n  'Have_new_level_mineur' => 'For achievements in economy you have won a point development officers!',\n  'Have_new_level_raid' => 'For successful attacks you have won a point development officers!',\n  'Server_time' => 'Time',\n  'Left_time' => 'Remaining Time',\n  'Now_build' => 'Now under construction',\n  'Events' => 'Events',\n  'Free' => 'Free',\n  'Diameter' => 'Diameter',\n  'fields' => 'Fields',\n  'Developed_fields' => 'Developed Fields',\n  'max_eveloped_fields' => 'maximum number of fields',\n  'Temperature' => 'Temperature',\n  'min_avg_max' => 'min/avg/max',\n  'approx' => 'Approx',\n  'to' => 'to',\n  'Centigrade' => 'C',\n  'Position' => 'Position',\n  'Fleet' => 'Fleet',\n  'Research' => 'Research',\n  'Total' => 'Total',\n  'Rank' => 'Rank',\n  'of' => 'Of',\n  'Miner' => 'Miner',\n  'Raider' => 'Raider',\n  'Debris_Field' => 'Debris Field',\n  'rename_and_abandon_planet' => 'Rename and abandon planet',\n  'functions' => 'Function',\n  'coords' => 'Coordinates',\n  'your_planet' => 'Your planet',\n  'colony_abandon' => 'Abandon Colony',\n  'deleteplanet' => 'Remove the planet!',\n  'security_query' => 'Security system',\n  'name' => 'Name',\n  'namer' => 'Change the name',\n  'confirm_planet_delete' => 'Confirm the deletion of the planet',\n  'confirmed_with_password' => 'Confirm the password',\n  'ov_delete_ok' => 'Colony successfully deleted',\n  'ov_delete_wrong_planet' => 'The planet cannot be left! You try to leave your planet or you changed the current planet in another browser window.',\n  'ov_delete_wrong_pass' => 'Wrong password!',\n  'MembersOnline' => 'Players',\n  'ov_fleet_list' => 'Schedule of fleets',\n  'ov_fleet' => 'Fleet',\n  'ov_destination' => 'Destination',\n  'ov_source' => 'From Where',\n  'event_time' => 'Time',\n  'ov_mission' => 'Mission',\n  'ov_event' => 'Event',\n  'ov_flying_fleets' => 'The fleets traveling at',\n  'ov_other_planets' => 'other planets',\n  'ov_fleet_arrive' => 'Arrive',\n  'ov_fleet_return' => 'Return',\n  'ov_fleet_hold' => 'End Mission',\n  'ov_fleet_rocket' => 'Missile strike',\n  'ov_fleet_exploration' => 'Exploration',\n  'ov_fleet_colonization' => 'Colonization',\n  'ov_fleet_no_flying' => 'No fleets in flight',\n  'ov_vennant' => ' posted ',\n  'ov_planet_to' => 'with the planet ',\n  'ov_moon_to' => 'from the Moon ',\n  'ov_atteint' => ' posted at ',\n  'ov_planet_to_target' => 'Planet ',\n  'ov_moon_to_target' => 'Moon ',\n  'ov_debris_to_target' => 'Field debris ',\n  'ov_explo_to_target' => 'position ',\n  'ov_explo_stay' => ' Exploreing ',\n  'ov_explo_mission' => '. Mission : ',\n  'ov_rentrant' => ' Returns ',\n  'ov_planet_from' => 'with the planet ',\n  'ov_moon_from' => 'from the Moon ',\n  'ov_debris_from' => 'from the wreckage ',\n  'ov_explo_from' => 'with position ',\n  'ov_back_planet' => ' on the planet ',\n  'ov_back_moon' => ' to the Moon ',\n  'ov_hostile' => ' Player ',\n  'ov_message' => 'Send a message',\n\n  'imp_user_points_struc' => 'For construction',\n  'imp_user_points_tech' => 'For research',\n  'imp_user_points_fleet' => 'For fleet',\n  'imp_user_points_def' => 'For defence',\n  'imp_user_points_res' => 'For resources',\n  'imp_user_points_all' => 'Total',\n  'imp_statistics' => 'Statistics',\n\n  'Points_1' => 'Field',\n  'km' => 'km',\n  'orb' => 'Debris in orbit',\n  'buildings_on_planet' => 'Building',\n  'NumberOfRaids' => 'Held',\n  'RaidsWin' => 'Won',\n  'RaidsLoose' => 'Lost',\n  'Economica' => 'Economy',\n  'Teching' => 'Research',\n  'ov_planet_details' => 'Planet Details',\n  'ov_building' => 'Building',\n  'ov_hangar' => 'Shipyard',\n  'ov_rank' => 'Rank',\n  'ov_rpg_new_level_miner' => 'For achievements in economy you get Dark Matter.',\n  'ov_rpg_new_level_raid' => 'For successful attacks you get Dark Matter.',\n  'ov_points' => 'Points',\n  'ov_raids' => 'Raids',\n  'ov_experience' => 'Experience',\n  'ov_player_rpg' => 'Player statistics',\n  'ov_banner' => 'Banner',\n  'ov_userbar' => 'Userbar',\n  'ov_banner_empty_id' => 'SuperNova - Join The Game!',\n  'ov_new' => 'New',\n  'ov_overview' => 'Overview',\n  'ov_manage' => 'Manage',\n  'ov_return' => 'Back to overview',\n  'ov_rename' => 'Rename',\n  'ov_new_name' => 'New name',\n  'cur_governor' => 'Current governor',\n  'ov_mrc_confirm_1' => 'Do you confirm replacement of governor ',\n  'ov_mrc_confirm_2' => 'level',\n  'ov_mrc_confirm_3' => 'to governor',\n  'ov_mrc_confirm_4' => 'first level? All your Dark Matter invested in current governor WILL BE LOST!',\n  'ov_manage_page_hint' => '  <li class=\"warning\">WARNING! When hiring different governor his starting level will become 1 WITHOUT COMPENSATION SPENT DARK MATTER for previous one!  It\\'s essential to plan your Empire, choosing right governor for each planet depending of it\\'s role!</li>  <li>Governors are mercenaries that manage one planet and granting certain bonuses to it</li>  <li>Click on governor\\'s image in list to see description and granted bonuses</li>  <li>To hire governor click on \"Hire\"</li>  <li>Hiring current governor will increase his level - and thus increase his bonuses on planet</li>  <li>Some governors has limit by level. Some - has not</li>\n  <li>Teleportatioin moves planet with orbiting fleet to new coordinates</li>\n  <li>If planet have a moon - it would be also teleported with orbiting fleet</li>\n  <li>Teleportation is not possible if there is any activity in planet range (i.e. some fleets have as source and/or destination current planet or their moon or debris field)</li>\n  <li>After teleportation you should wait some time - dimension metrics should normalize itself before next teleportation</li>\n  ',\n\n  'ov_gate_time_left' => 'Time to next jump',\n\n  'ov_teleport' => 'Teleport',\n  'ov_teleport_new_coordinates' => 'New coordinates',\n  'ov_teleport_err_none' => 'Planet succesfully teleported',\n  'ov_teleport_err_wrong_coordinates' => 'Wrong coordinates',\n  'ov_teleport_err_fleet' => 'There is some fleet activity in planet range',\n  'ov_teleport_err_destination_busy' => 'Destination is busy',\n  'ov_teleport_err_cooldown' => 'You can not teleport until dimension metrics is normalized. Please wait',\n  'ov_teleport_err_no_dark_matter' => 'There is not enough Dark Matter to teleport',\n  'ov_teleport_log_record' => 'Planet {%2$d} %1$s teleported from coordinates %3$s to coordinates %4$s',\n\n  'ov_capital' => 'Transfer Empire capital here',\n  'ov_capital_err_none' => 'This planet is now Empire\\'s capital ',\n  'ov_capital_err_capital_already' => 'This planet is already the capital',\n  'ov_capital_err_no_dark_matter' => 'There is not enough Dark Matter to transfer capital',\n  'ov_capital_err_not_a_planet' => 'Only a planet can be capital',\n\n  'read_all_news' => 'Read all news',\n\n  'imp_TBA' => 'To be announced...',\n  'imp_experience_current' => 'Current',\n  'imp_experience_to_level' => 'Level-up',\n\n  'ov_manage_special' => 'Special functions',\n  'ov_password' => 'Your password to confirm',\n\n  'ov_governor_purchase' => 'Игрок купил Губернатора %1$s ID %2$d уровня %3$d на планету %4$s',\n\n  'ov_planet_rename_dialog_title' => 'Rename planet/moon',\n\n));\n"
  },
  {
    "path": "language/en/payment.mo.php",
    "content": "<?php\n\n/*\n#############################################################################\n#  Filename: payment.mo.php\n#  Project: SuperNova.WS\n#  Website: http://www.supernova.ws\n#  Description: Massive Multiplayer Online Browser Space Strategy Game\n#\n#  Copyright © 2009-2018 Gorlum for Project \"SuperNova.WS\"\n#############################################################################\n*/\n\n/**\n*\n* @package language\n* @system [English]\n* @version 45d0\n*\n*/\n\n/**\n* DO NOT CHANGE\n*/\n\nif (!defined('INSIDE')) die();\n\n$a_lang_array = (array(\n  // Metamatter\n  'sys_metamatter_what_header' => 'What is <span class=\"metamatter\">Metamatter</span>',\n  'sys_metamatter_what_description' => '<span class=\"metamatter\">Metamatter</span> (shortly <span class=\"metamatter\">MM</span>) - это весьма условное название для особого состояния Вселенной. Фактически - это даже не материя, а факторизируемая вероятность.<br /><br />\n  У <span class=\"metamatter\">Метаматерии</span> нет состояния - и в то же время она находится во всех состояних. <span class=\"metamatter\">Метаматерия</span> нигде не находится - и в то же время находится везде. Потенциально метаматерия может стать чем угодно и где угодно - если правильно актуализировать вероятность.',\n  'sys_metamatter_what_purchase' => 'Basically <span class=\"metamatter\">Metamatter</span> is a \"Buyable <span class=\"dark_matter\">Dark Matter</span>\" - when there is lack of <span class=\"metamatter\">DM</span> for purchase of something <span class=\"metamatter\">MM</span> would be <span class=\"ok\">automatically converted</span> to necessary amount of <span class=\"dark_matter\">DM</span> as <span class=\"dark_matter\">1 DM</span> = <span class=\"metamatter\">1 MM</span>',\n\n  'pay_mm_convert_header' => 'Конвертация Метаматерии в Тёмную Материю',\n  'pay_mm_convert_text' => 'Метаматерия конвертируется в Тёмную Материю по курсу 1 к 1',\n  'pay_mm_convert_no_mm' => 'Нет Метаматерии - купите её сначала',\n  'pay_mm_convert_prefix' => 'Единицы Метаматерии',\n  'pay_mm_convert_suffix' => '',\n  'pay_mm_convert_do' => 'Сконвертировать в ТМ',\n\n  'pay_msg_mm_convert_wrong_amount' => 'Неправильное количество Метаматерии',\n  'pay_msg_mm_convert_not_enough' => 'Не хватает Метаматерии для конвертации в Тёмную Материю',\n  'pay_msg_mm_convert_mm_error' => 'Ошибка изменения количеста Метаматерии',\n  'pay_msg_mm_convert_dm_error' => 'Ошибка изменения количеста Тёмной Материи',\n\n  'pay_mm_buy' => 'Purchase <span class=\"metamatter\">Metamatter</span>',\n  'pay_mm_buy_text_cost' => 'Price for',\n  'pay_mm_buy_text_unit' => 'is',\n  'pay_mm_buy_url_description' => 'In addition you can purchase for real money',\n  'pay_mm_buy_url_get'  => 'Click here to read details',\n  'pay_mm_buy_url_none' => 'Contact with server Administration to get <span class=\"metamatter\">Metamatter</span>',\n\n  'pay_mm_bonus_header' => '<span class=\"metamatter\">Metamatter</span> cost and bonuses for bulk purchases',\n  'pay_mm_bonus' => 'When you purchasing large amounts of <span class=\"metamatter\">Metamatter</span> you recieve bonuses:',\n  'pay_mm_bonus_each' => 'from %s <span class=\"metamatter\">MM</span> - %d%% bonus to purchased <span class=\"metamatter\">MM</span> amount',\n  'pay_mm_bonus_text' => 'Bonus',\n\n  'pay_mm_buy_step1_text' => 'Select amount of <span class=\"metamatter\">MM</span> you wish to purchase, select payment system and confirm your selection',\n  'pay_mm_buy_metamatter_amount' => 'Select amount of <span class=\"metamatter\">Metamatter</span> from list',\n  'pay_mm_buy_metamatter_amount_enter' => '...or enter your desired amount of <span class=\"metamatter\">Metamatter</span>',\n  'pay_mm_buy_price_for' => 'Price for',\n  'pay_mm_buy_unit' => '<span class=\"metamatter\">Metamatter</span>',\n  'pay_mm_buy_select' => 'Select payment system',\n  'pay_mm_buy_method_detail' => 'Некоторые способы оплаты предлагают выбор разных платёжных систем. Если платёж не проходит через одну платёжную систему - попробуйте использовать тот же способ оплаты с другой платёжной системой',\n  'pay_mm_buy_confirm' => 'Confirm selection',\n  'pay_mm_buy_payment_selected' => 'Purchase would be made using payment system',\n  'pay_mm_buy_purchase' => 'Purchase',\n\n  'pay_mm_buy_payment_method_more' => 'Press \"Show\" button, to see more payment methods',\n\n  'pay_mm_buy_payment_method_select' => 'Select payment method',\n  'pay_mm_buy_payment_method_selected' => 'Your selected payment method is',\n\n  'pay_mm_buy_step2_text' => 'Calculated price DOES NOT includes additional commisions which payment system or payment aggregator may apply. Verify selected amount of <span class=\"metamatter\">Metamatter</span> and selected payment system. If everything is OK press button \"Purchase <span class=\"metamatter\">Metamatter</span>\". If there is any error - press button \"Discard and start again\"',\n  'pay_mm_buy_pay' => 'Purchase <span class=\"metamatter\">Metamatter</span>',\n  'pay_mm_buy_reset' => 'Discard and start again',\n  'pay_mm_buy_in_progress' => 'Payment in progress...',\n  'pay_mm_buy_conversion_cost' => 'Calculated cost of %1$s <span class=\"metamatter\">Metamatter</span> in payment system currency will be <span class=\"%4$s\">%2$s</span> %3$s',\n  'pay_mm_buy_cost_base' => 'Cost will be',\n  'pay_mm_buy_real_income' => 'Бонус за оптовую покупку составит %s%% и на ваш игровой счёт будет зачислено %s <span class=\"metamatter\">ММ</span>',\n\n  'pay_currency_name' => 'Currency',\n  'pay_currency_symbol' => 'Symbol',\n  'pay_currency_choose' => 'Choose payment currency',\n  'pay_currency_list' => array(\n    'RUB' => 'Russian ruble',\n    'USD' => 'Dollar USA',\n    'EUR' => 'Euro',\n    'UAH' => 'Ukrainian hryvna',\n//    'WMR' => 'WebMoney rouble',\n//    'WMZ' => 'WebMoney dollar',\n//    'WME' => 'WebMoney euro',\n//    'WMU' => 'WebMoney hryvna',\n//    'WMB' => 'WebMoney belorussian rouble',\n  ),\n\n  'pay_methods' => array(\n    PAYMENT_METHOD_EMONEY => 'Электронный кошелёк',\n    PAYMENT_METHOD_EMONEY_YANDEX => 'Яндекс.Деньги',\n    PAYMENT_METHOD_EMONEY_WEBMONEY_WMR => 'WebMoney WMR',\n    PAYMENT_METHOD_EMONEY_WEBMONEY_WMZ => 'WebMoney WMZ',\n    PAYMENT_METHOD_EMONEY_WEBMONEY_WMU => 'WebMoney WMU',\n    PAYMENT_METHOD_EMONEY_WEBMONEY_WME => 'WebMoney WME',\n    PAYMENT_METHOD_EMONEY_WEBMONEY_WMB => 'WebMoney WMB',\n    PAYMENT_METHOD_EMONEY_QIWI => 'QIWI Кошелек',\n    PAYMENT_METHOD_EMONEY_ELECSNET => 'Кошелек Элекснет',\n    PAYMENT_METHOD_EMONEY_MAILRU => 'Деньги@Mail.Ru',\n    PAYMENT_METHOD_EMONEY_EASYPAY => 'EasyPay',\n    PAYMENT_METHOD_EMONEY_RUR_W1R => 'RUR Единый Кошелек',\n    PAYMENT_METHOD_EMONEY_TELEMONEY => 'TeleMoney',\n\n    PAYMENT_METHOD_BANK_CARD => 'Платежная карта (VISA, MasterCard итд)',\n    PAYMENT_METHOD_BANK_CARD_STANDARD => 'Банковская карта',\n    PAYMENT_METHOD_BANK_CARD_LIQPAY => 'LiqPay',\n    PAYMENT_METHOD_BANK_CARD_EASYPAY => 'EasyPay',\n    PAYMENT_METHOD_BANK_CARD_AMERICAN_EXPRESS => 'American Express',\n    PAYMENT_METHOD_BANK_CARD_JCB => 'JCB',\n    PAYMENT_METHOD_BANK_CARD_UNIONPAY => 'UnionPay',\n\n    PAYMENT_METHOD_BANK_INTERNET => 'Через интернет-банк',\n    PAYMENT_METHOD_BANK_INTERNET_ALFA_BANK => 'Альфа-Клик',\n    PAYMENT_METHOD_BANK_INTERNET_RUSSKIY_STANDART => 'Банк Русский Стандарт',\n    PAYMENT_METHOD_BANK_INTERNET_PROSMVYAZBANK => 'Промсвязьбанк',\n    PAYMENT_METHOD_BANK_INTERNET_VTB24 => 'ВТБ24',\n    PAYMENT_METHOD_BANK_INTERNET_OCEAN_BANK => 'Океан Банк',\n    PAYMENT_METHOD_BANK_INTERNET_HANDY_BANK => 'HandyBank',\n    PAYMENT_METHOD_BANK_INTERNET_007 => 'Банк Богородский',\n    PAYMENT_METHOD_BANK_INTERNET_008 => 'Банк Образование',\n    PAYMENT_METHOD_BANK_INTERNET_009 => 'ФлексБанк',\n    PAYMENT_METHOD_BANK_INTERNET_010 => 'ФьючерБанк',\n    PAYMENT_METHOD_BANK_INTERNET_011 => 'КранБанк',\n    PAYMENT_METHOD_BANK_INTERNET_012 => 'Костромаселькомбанк',\n    PAYMENT_METHOD_BANK_INTERNET_013 => 'Липецкий областной банк',\n    PAYMENT_METHOD_BANK_INTERNET_014 => 'Независимый строительный банк',\n    PAYMENT_METHOD_BANK_INTERNET_015 => 'Русский Трастовый Банк',\n    PAYMENT_METHOD_BANK_INTERNET_016 => 'ВестИнтерБанк',\n    PAYMENT_METHOD_BANK_INTERNET_017 => 'Межтопэнергобанк',\n    PAYMENT_METHOD_BANK_INTERNET_018 => 'Московский Индустриальный Банк',\n    PAYMENT_METHOD_BANK_INTERNET_019 => 'Банк Интеза',\n    PAYMENT_METHOD_BANK_INTERNET_020 => 'Банк Город',\n    PAYMENT_METHOD_BANK_INTERNET_021 => 'Банк АВБ',\n    PAYMENT_METHOD_BANK_INTERNET_BANK24 => 'Банк24 Национальный кредит',\n    PAYMENT_METHOD_BANK_INTERNET_PRIVAT24 => \"Приват24\",\n    PAYMENT_METHOD_BANK_INTERNET_SBERBANK => \"Сбербанк Онлайн\",\n\n    PAYMENT_METHOD_BANK_TRANSFER => 'Банковский перевод',\n\n    PAYMENT_METHOD_MOBILE => 'С мобильного телефона',\n    PAYMENT_METHOD_MOBILE_SMS => 'SMS',\n//    PAYMENT_METHOD_MOBILE_XSOLLA => 'Со счёта мобильного',\n    PAYMENT_METHOD_MOBILE_PAYPAL_ZONG => 'Со счёта или SMS',\n    PAYMENT_METHOD_MOBILE_MEGAPHONE => 'Мегафон',\n    PAYMENT_METHOD_MOBILE_MTS => 'МТС',\n    PAYMENT_METHOD_MOBILE_KYIVSTAR => 'Киевстар',\n    PAYMENT_METHOD_MOBILE_BEELINE => 'Билайн',\n\n    PAYMENT_METHOD_TERMINAL => 'Терминал оплаты',\n    PAYMENT_METHOD_TERMINAL_QIWI => 'QIWI Кошелек',\n    PAYMENT_METHOD_TERMINAL_ELECSNET => 'Элекснет',\n    PAYMENT_METHOD_TERMINAL_ELEMENT => 'Мобил Элемент',\n    PAYMENT_METHOD_TERMINAL_KASSIRANET => 'Кассира.нет',\n    PAYMENT_METHOD_TERMINAL_IBOX => 'Ibox',\n    PAYMENT_METHOD_TERMINAL_UKRAINE => 'Терминалы Украины',\n    PAYMENT_METHOD_TERMINAL_RUSSIA => 'Терминалы России',\n    PAYMENT_METHOD_TERMINAL_EASYPAY => 'EasyPay',\n\n    PAYMENT_METHOD_OTHER => 'Другие способы',\n    PAYMENT_METHOD_OTHER_EVROSET => 'Евросеть',\n    PAYMENT_METHOD_OTHER_SVYAZNOY => 'Связной',\n    PAYMENT_METHOD_OTHER_ROBOKASSA_MOBILE => 'Мобильная ROBOKASSA',\n\n    PAYMENT_METHOD_GENERIC => 'Выше перечислены далеко не все возможнные способы оплаты. Если вы не нашли подходящего для себя способа - воспользуйтесь услугами агрегаторов',\n//    PAYMENT_METHOD_GENERIC_XSOLLA => 'xSolla',\n//    PAYMENT_METHOD_GENERIC_ROBOKASSA => 'RoboKassa',\n  ),\n\n  'pay_currency_exchange_title' => 'Internal currency exchange',\n  'pay_currency_exchange_rate' => 'Exchange rate',\n  'pay_currency_exchange_direct' => 'Direct',\n  'pay_currency_exchange_reverse' => 'Reverse',\n  'pay_currency_exchange_mm' => '<span class=\"metamatter\">MM</span> for 1 currency',\n  'pay_currency_exchange_note' => 'Internal exchange rates used to calculate payment amount in payment system currency. Exchange rates does not includes commission of payment system(s)',\n\n  'pay_msg_mm_purchase_complete'   => 'You succesfully paid for %d <span class=\"metamatter\">Metamatter</span> via %s. You gained %s <span class=\"metamatter\">Metamatter</span>',\n  'pay_msg_mm_purchase_incomplete' => 'You payment for %d <span class=\"metamatter\">Metamatter</span> via %s currently in progress. If you feel it wrong please contact Administration',\n  'pay_msg_mm_purchase_test'       => 'Really you did not gain anything! Because it was a test payment ha-ha-ha! If you feel it wrong - contact Administration',\n\n  'pay_msg_request_user_found' => 'User found',\n  'pay_msg_request_payment_complete' => 'Платёж завершен',\n  'pay_msg_request_payment_cancel_complete' => 'Платёж успешно отменён',\n\n  'pay_msg_request_unsupported' => 'Unsupported request',\n  'pay_msg_request_signature_invalid' => 'Wrong request signature',\n  'pay_msg_request_user_invalid' => 'User ID is invalid',\n  'pay_msg_request_server_wrong' => 'Wrong server',\n  'pay_msg_request_payment_amount_invalid' => 'Wrong payment amount',\n  'pay_msg_request_payment_id_invalid' => 'Wrong payment ID',\n  'pay_msg_request_payment_date_invalid' => 'Wrong payment date',\n  'pay_msg_request_internal_error' => 'Server internal error. Try again later',\n  'pay_msg_request_paylink_unsupported' => 'This type of paylink is not supported. It\\'s looks like you using outdated version of SuperNova which incompatible with selected payment module',\n  'pay_msg_request_payment_write_error' => 'Payment write error',\n  'pay_msg_request_payment_cancelled_already' => 'Платёж уже отменен',\n  'pay_msg_request_payment_cancel_not_complete' => 'Платёж еще не завершен и не может быть отменен',\n  'pay_msg_request_payment_cancelled' => '!!! Платёж отозван платёжной системой!!!',\n  'pay_msg_request_payment_not_found' => 'Платёж не найден',\n\n  'pay_msg_module_disabled' => 'Payment module disabled',\n\n  'pay_msg_mm_request_money_and_mm_mismatched' => 'Не совпадает сумма оплаты и количество покупаемой ММ',\n\n  'pay_msg_mm_request_amount_invalid' => 'Wrong <span class=\"metamatter\">Metamatter</span> amount',\n  'pay_msg_mm_request_config_invalid' => 'There is error in payment module configuration. Please contact server Administration',\n  'pay_msg_mm_request_mm_adjust_error' => 'Error adjusting <span class=\"metamatter\">Metamatter</span>',\n\n  'pay_msg_request_error_db_payment_create' => 'Ошибка создания платежа в БД',\n  'pay_msg_request_error_test_payment' => 'Статус платежа в БД не совпадает с информацией в запросе',\n  'pay_msg_error_internal_no_external_currency_set' => 'ВНУТРЕННЯЯ ОШИБКА или ОШИБКА КОНФИГУРАЦИИ ПЛАТЁЖНОГО МОДУЛЯ! Не установлена валюта платёжной системы! Пожалуйста, сообщите Администрации сервера!',\n\n));\n"
  },
  {
    "path": "language/en/quest.mo.php",
    "content": "<?php\n\n/*\n#############################################################################\n#  Filename: quest.mo\n#  Project: SuperNova.WS\n#  Website: http://www.supernova.ws\n#  Description: Massive Multiplayer Online Browser Space Strategy Game\n#\n#  Copyright © 2009-2018 Gorlum for Project \"SuperNova.WS\"\n#  Copyright © 2008 Aleksandar Spasojevic <spalekg@gmail.com>\n#  Copyright © 2005 - 2008 KGsystem\n#############################################################################\n*/\n\n/**\n*\n* @package language\n* @system [English]\n* @version 43a16.13\n*\n*/\n\n/**\n* DO NOT CHANGE\n*/\n\nif (!defined('INSIDE')) die();\n\n\n$a_lang_array = (array(\n  'qst_quest' => 'Quest',\n  'qst_quest_of' => 'quest',\n  'qst_name' => 'Name',\n  'qst_description' => 'Description',\n  'qst_adm_conditions' => 'Requirements',\n  'qst_conditions' => 'Need to build/research',\n  'qst_rewards' => 'Reward',\n  'qst_total' => 'Quests',\n  'qst_status' => 'Status',\n  'qst_status_list' => array(\n    QUEST_STATUS_ALL => '-- All quests --',\n    QUEST_STATUS_NOT_STARTED => 'Not&nbsp;started',\n    QUEST_STATUS_STARTED => 'Started',\n    QUEST_STATUS_EXCEPT_COMPLETE => 'All&nbsp;-&nbsp;except&nbsp;completed',\n    QUEST_STATUS_COMPLETE => 'Completed',\n  ),\n\n  'qst_filter_by_status' => 'Show quest by status',\n\n  'qst_add' => 'Add quest',\n  'qst_edit' => 'Edit quest',\n  'qst_copy' => 'Copy quest',\n  'qst_mode_add' => 'Add',\n  'qst_mode_edit' => 'Edit',\n  'qst_mode_copy' => 'Copy',\n  'qst_adm_err_unit_id' => 'Unsupported unit',\n  'qst_adm_err_unit_amount' => 'Wrong unit amount',\n  'qst_adm_err_reward_amount' => 'Wrong reward amount',\n  'qst_adm_err_reward_type' => 'Wrong reward type',\n  'qst_adm_err_reward_empty' => 'Quest reward empty',\n));\n"
  },
  {
    "path": "language/en/search.mo.php",
    "content": "<?php\n\n/*\n#############################################################################\n#  Filename: search.mo\n#  Project: SuperNova.WS\n#  Website: http://www.supernova.ws\n#  Description: Massive Multiplayer Online Browser Space Strategy Game\n#\n#  Copyright © 2009-2018 Gorlum for Project \"SuperNova.WS\"\n#  Copyright © 2008 Aleksandar Spasojevic <spalekg@gmail.com>\n#  Copyright © 2005 - 2008 KGsystem\n#############################################################################\n*/\n\n/**\n*\n* @package language\n* @system [English]\n* @version #43a16.13#\n*\n*/\n\n/**\n* DO NOT CHANGE\n*/\n\nif (!defined('INSIDE')) die();\n\n\n$a_lang_array = (array(\n  'srch_title' => 'Server search',\n  'srch_search_do' => 'Search',\n  'srch_result_none' => 'There is nothing found on your query',\n  'srch_player_name' => 'Player name',\n  'srch_ally_name' => 'Alliance name',\n  'srch_ally_members' => 'Members',\n  'srch_planet_name' => 'Planet name',\n  'srch_rank' => 'Rank',\n  'srch_action_pm' => 'Write PM',\n  'srch_action_buddy' => 'Request friendship',\n\n  'srch_aka' => 'AKA',\n\n  'srch_page_hint' => '<li>Only first 30 records appears</li>\n  <li>If player ever changed his name and one of his old names is a subject of search request then there will be additional line for old name.\n  In this case current name will be shown and old name also will apear - bracketed and highlighted by <span class=\"warning\">color</span></li>',\n\n));\n"
  },
  {
    "path": "language/en/stat.mo.php",
    "content": "<?php\n\n/*\n#############################################################################\n#  Filename: stat.mo\n#  Project: SuperNova.WS\n#  Website: http://www.supernova.ws\n#  Description: Massive Multiplayer Online Browser Space Strategy Game\n#\n#  Copyright © 2009-2018 Gorlum for Project \"SuperNova.WS\"\n#  Copyright © 2008 Aleksandar Spasojevic <spalekg@gmail.com>\n#  Copyright © 2005 - 2008 KGsystem\n#############################################################################\n*/\n\n/**\n*\n* @package language\n* @system [English]\n* @version 43a16.13\n*\n*/\n\n/**\n* DO NOT CHANGE\n*/\n\nif (!defined('INSIDE')) die();\n\n$a_lang_array = (array(\n  'stat_header' => 'Statistics',\n  'stat_refresh_last' => 'Last update on',\n  'stat_refresh_next' => 'Next update on',\n\n  'stat_rank' => 'Rank',\n  'stat_rank_diff' => 'Diff',\n  'stat_points' => 'Points',\n  'stat_per_member' => 'Per member',\n  'stat_members' => 'Members',\n  'stat_message_write' => 'Write message',\n\n  'stat_show' => 'Show',\n  'stat_type' => array(\n    STAT_TOTAL => 'total',\n    STAT_FLEET => 'fleet',\n    STAT_TECH => 'research',\n    STAT_BUILDING => 'building',\n    STAT_DEFENSE => 'defense',\n    STAT_RESOURCE => 'resource',\n    STAT_RAID_TOTAL => 'raid',\n    STAT_RAID_WON => 'raid won',\n    STAT_RAID_LOST => 'raid lost',\n    STAT_LVL_BUILDING => 'building level',\n    STAT_LVL_TECH => 'tech level',\n    STAT_LVL_RAID => 'raid level',\n  ),\n\n  'stat_by' => 'for',\n  'stat_player' => 'players',\n  'stat_allys' => 'alliances',\n  'stat_range' => 'ranks',\n\n  'stat_details' => 'Player info',\n\n));\n"
  },
  {
    "path": "language/en/system.mo.php",
    "content": "<?php\n\n/*\n#############################################################################\n#  Filename: system.mo\n#  Project: SuperNova.WS\n#  Website: http://www.supernova.ws\n#  Description: Massive Multiplayer Online Browser Space Strategy Game\n#\n#  Copyright © 2009-2018 Gorlum for Project \"SuperNova.WS\"\n#  Copyright В© 2008 Aleksandar Spasojevic <spalekg@gmail.com>\n#  Copyright В© 2005 - 2008 KGsystem\n#############################################################################\n*/\n\n/**\n*\n* @package language\n* @system [English]\n* @version 46d0\n*\n*/\n\n/**\n* DO NOT CHANGE\n*/\n\nuse Fleet\\Constants;\n\nif (!defined('INSIDE')) {\n\tdie('Hack attempt!');\n}\n\nglobal $config;\n\n$a_lang_array = (array(\n  'sys_administration' => 'SuperNova Administration',\n  'sys_birthday' => 'Birthday',\n  'sys_birthday_message' => '%1$s! SuperNova Administration warmly greats you with your birthday on %2$s and gives to you s small gift - %3$d %4$s!',\n\n  'adm_err_denied' => 'Access denied. You do not have enough rights to use this admin page',\n\n  'sys_empire'          => 'Empire',\n  'VacationMode'\t\t\t=> \"Your production stopped because you are on vacation\",\n  'sys_moon_destruction_report' => \"Report of destruction of the Moon\",\n  'sys_moon_destroyed' => \"Your Deathstar shot a powerful gravitational wave, which destroyed the Moon! \",\n  'sys_rips_destroyed' => \"Your Deathstar shot a  powerful gravitational wave, but it had not enough power to destroy the Moon due to its size. But the gravitational wave reflected from the lunar surface and ruined your fleet.\",\n  'sys_rips_come_back' => \"Your Deathstar did not have enough power to defeat this moon. Your fleet is not destroying the Moon.\",\n  'sys_chance_moon_destroy' => \"Chance of Moon destruction: \",\n  'sys_chance_rips_destroy' => \"Modify burst destruction: \",\n\n  'sys_impersonate' => 'Impersonate',\n  'sys_impersonate_done' => 'Unimpersonate',\n  'sys_impersonated_as' => 'WARNING! Currently you impersonating player %1$s. Don\\'t forget that you are really %2$s! To unimpersonate select appropriate menu item.',\n\n  'menu_admin_mining'          => 'Mining stats',\n  'menu_admin_units'          => 'Units',\n  'menu_admin_ube_balance'          => 'UBE Balance',\n\n  'sys_day' => \"Days\",\n  'sys_hrs' => \"Hours\",\n  'sys_min' => \"Minutes\",\n  'sys_sec' => \"Seconds\",\n  'sys_day_short' => \"D\",\n  'sys_hrs_short' => \"H\",\n  'sys_min_short' => \"M\",\n  'sys_sec_short' => \"S\",\n\n  'sys_ask_admin' => 'Questions and suggestions sent to',\n\n  'sys_wait' => 'The query is executed. Please wait.',\n\n  'sys_fleets'       => 'Fleets',\n  'sys_expeditions'  => 'Expeditions',\n  'sys_fleet'        => 'fleet',\n  'sys_expedition'   => 'expedition',\n  'sys_event_next'   => 'Next event:',\n  'sys_event_arrive' => 'will arrive',\n  'sys_event_stay'   => 'will end task',\n  'sys_event_return' => 'will return',\n\n  'sys_total'           => \"Total\",\n  'sys_need'\t\t\t\t=> 'Need',\n  'sys_register_date'   => 'Registration date',\n\n  'sys_attacker' \t\t=> \"Attacker\",\n  'sys_defender' \t\t=> \"Defender\",\n\n  'COE_combatSimulator' => \"Battle simulator\",\n  'COE_simulate'        => \"Run the Simulator\",\n  'COE_fleet'           => \"Fleet\",\n  'COE_defense'         => \"Defence\",\n  'sys_coe_combat_start'=> \"Combat begins\",\n  'sys_coe_combat_end'  => \"Combat outcome\",\n  'sys_coe_round'       => \"Round\",\n\n  'sys_coe_attacker_turn'=> 'Attacker make shots for %1$s. Defender\\'s shield absorbs %2$s<br />',\n  'sys_coe_defender_turn'=> 'Defender make shots for %1$s. Attacker\\'s shield absorbs %2$s<br /><br /><br />',\n  'sys_coe_outcome_win'  => 'Defender wons combat!<br />',\n  'sys_coe_outcome_loss' => 'Attacker wons combat!<br />',\n  'sys_coe_outcome_loot' => 'He\\'s lootin %1$s metal, %2$s crystal, %3$s deuterium<br />',\n  'sys_coe_outcome_draw' => 'Combat end with draw...<br />',\n  'sys_coe_attacker_lost'=> 'Attacker lost %1$s units<br />',\n  'sys_coe_defender_lost'=> 'Defender lost %1$s units<br />',\n  'sys_coe_debris_left'  => 'There is %1$s metal and %2$s crystal floating in debris around planet.<br /><br />',\n  'sys_coe_moon_chance'  => 'Moon creation chance is %1$s%%<br />',\n  'sys_coe_rw_time'      => 'Reprot generated in %1$s seconds<br />',\n\n  'sys_resources'       => \"Resources\",\n  'sys_ships'           => \"Ships\",\n  'sys_metal'          => \"Metal\",\n  'sys_metal_sh'       => \"M\",\n  'sys_crystal'        => \"Crystal\",\n  'sys_crystal_sh'     => \"C\",\n  'sys_deuterium'      => \"Deuterium\",\n  'sys_deuterium_sh'   => \"D\",\n  'sys_energy'         => \"Energy\",\n  'sys_energy_sh'      => \"E\",\n  'sys_dark_matter'    => \"Dark Matter\",\n  'sys_dark_matter_sh' => \"DM\",\n\n  'sys_reset'           => \"Reset\",\n  'sys_send'            => \"Send\",\n  'sys_characters'      => \"characters\",\n  'sys_back'            => \"Back\",\n  'sys_return'          => \"Return\",\n  'sys_delete'          => \"Delete\",\n  'sys_writeMessage'    => \"Write a message\",\n  'sys_hint'            => \"Tip\",\n\n  'sys_alliance'        => \"Alliance\",\n  'sys_player'          => \"Player\",\n  'sys_coordinates'     => \"Coordinates\",\n\n  'sys_online'          => \"Online\",\n  'sys_offline'         => \"Offline\",\n  'sys_status'          => \"Status\",\n\n  'sys_universe'        => \"Universe\",\n  'sys_goto'            => \"Go\",\n\n  'sys_time'            => \"Time\",\n  'sys_temperature'     => 'Temperature',\n\n  'sys_no_task'         => \"No task\",\n\n  'sys_affilates'       => \"Invited players\",\n\n  'sys_fleet_arrived'   => \"Fleet arrived\",\n\n  'sys_planet_type' => array(\n    PT_PLANET => 'Planet',\n    PT_DEBRIS => 'Debris Field',\n    PT_MOON   => 'Moon',\n  ),\n\n  'sys_planet_type_sh' => array(\n    PT_PLANET => '(P)',\n    PT_DEBRIS => '(D)',\n    PT_MOON   => '(M)',\n  ),\n\n  'sys_planet_expedition' => 'unexplored space',\n\n  'sys_capacity' \t\t\t=> 'Load Capacity',\n  'sys_cargo_bays' \t\t\t=> 'Holds',\n\n  'sys_supernova' \t\t\t=> 'Supernova',\n  'sys_server' \t\t\t=> 'Server',\n\n  'sys_unbanned'\t\t\t=> 'Unbanned',\n\n  'sys_date_time'\t\t\t=> 'Date and time',\n  'sys_from_person'\t   => 'From',\n  'sys_from_speed'\t   => 'from',\n\n  'sys_from'\t\t  => 'from',\n\n// Resource page\n  'res_planet_production' => 'Planet Production',\n  'res_basic_starting_resources' => 'Planet starting resources',\n  'res_basic_income' => 'Basic Income',\n  'res_basic_storage_size' => 'Planet storage size',\n  'res_total' => 'Total',\n  'res_calculate' => 'Calculate',\n  'res_hourly' => 'Hourly',\n  'res_daily' => 'Daily',\n  'res_weekly' => 'Weekly',\n  'res_monthly' => 'Monthly',\n  'res_storage_fill' => 'Storage occupancy',\n  'res_hint' => '<ul><li>Production resources <100% means a shortage of energy. Build more power stations or reduce production resources<li>If your production is 0% likely you came from vacation mode and you want to include all plants<li>What would make the extraction for all plants immediately use the drop-down in the resource table. Especially convenient to use it after the vacation mode</ul>',\n\n// Build page\n  'bld_destroy' => 'Destroy',\n  'bld_create'  => 'Build',\n  'bld_research' => 'Research',\n  'bld_hire' => 'Hire',\n\n// Imperium page\n  'imp_imperator' => \"Emperor\",\n  'imp_overview' => \"Empire Overview\",\n  'imp_fleets' => \"Fleets in flight\",\n  'imp_production' => \"Production\",\n  'imp_name' => \"Name\",\n  'imp_research' => \"Research\",\n  'imp_exploration' => \"Exploration\",\n  'imp_imperator_none' => \"There is no such Emperor in this Universe!\",\n  'sys_fields' => \"Fields\",\n\n// Cookies\n  'err_cookie' => \"Error! You cannot authenticate the user on information in a cookie.<br />Clear cookies in you browser then <a href='login\" . DOT_PHP_EX . \"'>log in</a> in a game or <a href='reg\" . DOT_PHP_EX . \"'>register new account again</a>.\",\n\n// Supported languages\n  'ru'              \t  => 'Russian',\n  'en'              \t  => 'English',\n\n  'sys_vacation'        => 'Your are on vacation until',\n  'sys_vacation_leave'  => 'I have got rest - break holiday!',\n  'sys_vacation_in'     => 'On vacation',\n  'sys_level'           => 'Level',\n  'sys_level_short'     => 'Lvl',\n  'sys_level_max'       => 'Max level',\n\n  'sys_yes'             => 'Yes',\n  'sys_no'              => 'No',\n\n  'sys_on'              => 'Enable',\n  'sys_off'             => 'Disable',\n\n  'sys_confirm'         => 'Confirm',\n  'sys_save'            => 'Save',\n  'sys_create'          => 'Create',\n  'sys_write_message'   => 'Write a message',\n\n// top bar\n  'top_of_year' => 'Year',\n  'top_online'\t\t\t=> 'Players online',\n\n  'sys_first_round_crash_1'\t=> 'Contact with the affected fleet lost.',\n  'sys_first_round_crash_2'\t=> 'This means that it was destroyed in the first round of the battle.',\n\n  'sys_ques' => array(\n    QUE_STRUCTURES => 'Building',\n    QUE_HANGAR     => 'Shipyard',\n    SUBQUE_DEFENSE => 'Defense',\n    QUE_RESEARCH   => 'Research',\n  ),\n\n  'navbar_button_expeditions_short' => 'Exp',\n  'navbar_button_fleets' => 'Fleets',\n  'navbar_button_quests' => 'Quests',\n  'navbar_font' => 'Font',\n  'navbar_font_normal' => 'Normal',\n  'sys_que_structures' => 'Buildings',\n  'sys_que_hangar' => 'Hangar',\n  'sys_que_defense' => 'Defense',\n  'sys_que_research' => 'Research',\n  'sys_que_research_short' => 'Science',\n\n  'eco_que'          => 'Queue',\n  'eco_que_empty'    => 'Queue is empty',\n  'eco_que_clear'    => 'Clear queue',\n  'eco_que_trim'     => 'Undo last queue',\n  'eco_que_artifact' => 'Use Artifact',\n\n  'sys_cancel' => 'Cancel',\n\n  'sys_overview'\t\t\t=> 'Overview',\n  'mod_marchand'\t\t\t=> 'Trader',\n  'sys_galaxy'\t\t\t=> 'Galaxy',\n  'sys_system'\t\t\t=> 'System',\n  'sys_planet'\t\t\t=> 'Planet',\n  'sys_planet_title'\t\t\t=> 'Planet Type',\n  'sys_planet_title_short'\t\t\t=> 'Type',\n  'sys_moon'\t\t\t=> 'Moon',\n  'sys_error'\t\t\t=> 'Error',\n  'sys_done'\t\t\t\t=> 'Finish',\n  'sys_no_vars'\t\t\t=> 'Initialization of variables, see the Administration!',\n  'sys_attacker_lostunits'\t\t=> 'Attacker lost %s units.',\n  'sys_defender_lostunits'\t\t=> 'Defender lost %s units.',\n  'sys_gcdrunits' \t\t\t=> 'Now at these coordinates are %s %s and %s %s.',\n  'sys_moonproba' \t\t\t=> 'Chance of Moon is: %d %% ',\n  'sys_moonbuilt' \t\t\t=> 'Thanks to the huge energy huge chunks of metal and Crystal are joined together and formed new moon %s %s!',\n  'sys_attack_title'    \t\t=> '%s. Battle occurred between the following fleets::',\n  'sys_attack_attacker_pos'      \t=> 'Attacker %s [%s:%s:%s]',\n  'sys_attack_techologies' \t=> 'Weapons: %d %% Shields: %d %% Armor: %d %% ',\n  'sys_attack_defender_pos' \t=> 'Defender %s [%s:%s:%s]',\n  'sys_ship_type' \t\t\t=> 'Type',\n  'sys_ship_count' \t\t=> 'Count',\n  'sys_ship_weapon' \t\t=> 'Weapon',\n  'sys_ship_shield' \t\t=> 'Shield',\n  'sys_ship_armour' \t\t=> 'Armor',\n  'sys_ship_speed' \t\t=> 'Speed',\n  'sys_ship_consumption' \t\t=> 'Consumption',\n  'sys_ship_capacity' \t\t=> 'Capacity/Tanks',\n  'sys_destroyed' \t\t\t=> 'destroyed',\n  'sys_attack_attack_wave' \t=> 'The Attacker is doing shots with a total capacity of %s on the defender. Shields absorb %s of the shots.',\n  'sys_attack_defend_wave'\t\t=> 'The Defender is doing shots with a total capacity of %s on the attacker. Shields absorb %s of the shots.',\n  'sys_attacker_won' \t\t=> 'The Attacker won the battle!',\n  'sys_defender_won' \t\t=> 'The Defender won the battle!',\n  'sys_both_won' \t\t\t=> 'The battle ended in a draw!',\n  'sys_stealed_ressources' \t=> 'The Attacker gets %s Metal %s %s Crystal %s and %s Deuterium.',\n  'sys_rapport_build_time' \t=> 'Report generation time %s seconds',\n  'sys_mess_tower' \t\t=> 'Transport',\n  'sys_coe_lost_contact' \t\t=> 'You lost contact with your fleet',\n  'sys_spy_activity' => 'There is some spy activity around your planets',\n  'sys_spy_materials' \t\t=> 'Raw material',\n  'sys_spy_fleet' \t\t\t=> 'Fleet',\n  'sys_spy_defenses' \t\t=> 'Defence',\n  'sys_mess_qg' \t\t\t=> 'Fleet command',\n  'sys_mess_spy_report' \t\t=> 'Spy Report',\n  'sys_mess_spy_lostproba' \t=> 'Accuracy of information received by the Spy probe %d %% ',\n  'sys_mess_spy_detect_chance' \t=> 'Detection chance %d%%',\n  'sys_mess_spy_detect_chance_no_percent' \t=> 'Detection chance',\n  'sys_mess_spy_control' \t\t=> 'Counter-intelligence',\n  'sys_mess_spy_activity' \t\t=> 'Spy activity',\n  'sys_mess_spy_enemy_fleet' \t=> 'Alien fleet with planet',\n  'sys_mess_spy_seen_at'\t\t=> 'was discovered near the planet',\n  'sys_mess_spy_destroyed'\t\t=> 'Spy fleet was destroyed',\n  'sys_mess_spy_destroyed_enemy'\t\t=> 'Enemy spy fleet was destroyed',\n  'sys_object_arrival'\t\t=> 'Arrived on the planet',\n  'sys_stay_mess_stay' => 'Leave Fleet',\n  'sys_stay_mess_start' \t\t=> 'Your fleet arrived at the planet',\n  'sys_stay_mess_back'\t\t=> 'Your fleet is back ',\n  'sys_stay_mess_end'\t\t=> ' and delivered:',\n  'sys_stay_mess_bend'\t\t=> ' and delivered the following resources:',\n  'sys_address_planet' \t\t=> '[%s:%s:%s]',\n  'sys_stay_mess_goods' \t\t=> '%s : %s, %s : %s, %s : %s',\n  'sys_colo_mess_from' \t\t=> 'Colonization',\n  'sys_colo_mess_report' \t\t=> 'Report about colonization',\n  'sys_colo_default_name' \t\t=> 'Colony',\n  'sys_colo_arrival' \t\t=> 'The fleet reaches the coordinates ',\n  'sys_colo_max_colo' \t\t=> ', but you cannot colonize the planet has reached the maximum number of colonies for your level of colonization',\n  'sys_colo_all_is_ok' \t\t=> ', and colonists are beginning to a new planet.',\n  'sys_colo_bad_pos'  \t\t\t=> ', and the colonists found little benefit for the environment of your Empire. The mission colonization back to planet submit.',\n  'sys_colo_not_free' \t\t\t=> ', the colonists did not find the planet in these coordinates. They have to pave the way back completely discouraged.',\n  'sys_colo_no_colonizer'     => 'In the fleet not colonizer',\n  'sys_colo_planet'  \t\t=> ' Planet colonized by!',\n  'sys_expe_report' \t\t=> 'Expedition Report',\n  'sys_recycler_report' \t\t=> 'Recycler information',\n  'sys_expe_blackholl_1' \t\t=> 'Your fleet hit the black hole and you lost part of your fleet!',\n  'sys_expe_blackholl_2' \t\t=> 'Your fleet hit the black hole and your fleet was completely sucked in!',\n  'sys_expe_nothing_1' \t\t=> 'Your researchers witnessed a Supernova! And your drives are able to take part of the absorption of energy.',\n  'sys_expe_nothing_2' \t\t=> 'Your researchers found nothing!',\n  'sys_expe_found_goods' \t\t=> 'Your researchers found a planet rich in raw materials!<br>You got %s %s, %s %s and %s %s',\n  'sys_expe_found_ships' \t\t=> 'Your researchers found flawlessly new fleet!<br>You got: ',\n  'sys_expe_back_home' \t\t=> 'Your fleet is back.',\n  'sys_mess_transport' \t\t=> 'Transport',\n  // 'sys_tran_mess_owner' \t\t=> 'One of your fleet reaches the planet %s %s and delivers %s %s, %s  %s and %s %s.',\n  'sys_tran_mess_user'  \t\t=> 'Your fleet sent to the planet %s %s arrived at %s %s and delivered %s %s, %s  %s and %s %s.',\n  'sys_relocate_mess_user'  \t\t=> 'Also following units where relocated:<br />',\n  'sys_mess_fleetback' \t\t=> 'Return',\n  'sys_tran_mess_back' \t\t=> 'One of your fleet returned to planet %s %s.',\n  'sys_recycler_gotten' \t\t=> 'One of your fleets, Nancy a %s %s and %s %s Return to planet.',\n  'sys_notenough_money' \t\t=> 'You do not have enough resources to build: %s. You now: %s %s , %s %s and %s %s. For construction: %s %s , %s %s and %s %s.',\n  'sys_nomore_level'\t\t=> 'You no longer can improve it. It reached Max. level ( %s ).',\n  'sys_buildlist' \t\t\t=> 'Building list',\n  'sys_buildlist_fail' \t\t=> 'no buildings',\n  'sys_gain' \t\t\t=> 'Extraction: ',\n  'sys_debris' \t\t\t=> 'Debris: ',\n  'sys_noaccess' \t\t\t=> 'Access Denied',\n  'sys_noalloaw' \t\t\t=> 'You have access to this zone!',\n  'sys_governor'        => 'Governor',\n\n  'flt_error_duration_wrong' => 'Невозможно отправить флот - нет доступных интервалов для задержки. Изучите еще уровни Астрокартографии',\n  'flt_stay_duration' => 'Time',\n\n  'flt_mission_expedition' => array(\n    'msg_sender' => 'Отчет экспедиции',\n    'msg_title' => 'Отчет экспедиции',\n\n    'found_dark_matter' => 'Получено %1$d единиц ТМ',\n    'found_resources' => \"Найдены ресурсы:\\r\\n\",\n    'found_fleet' => \"Найдены корабли:\\r\\n\",\n    'lost_fleet' => \"Потеряны следующие корабли:\\r\\n\",\n\n    'outcomes' => array(\n      Constants::OUTCOME_NONE => array(\n        'messages' => array(\n          'Ваши исследователи ничего не обнаружили',\n        ),\n      ),\n\n      Constants::EXPEDITION_OUTCOME_LOST_FLEET => array(\n        'messages' => array(\n          'Флот попал в черную дыру и частично утерян',\n        ),\n      ),\n\n      Constants::EXPEDITION_OUTCOME_LOST_FLEET_ALL => array(\n        'messages' => array(\n          'Если бы вы только это видели! Оно такое красивое... Оно зовёт к себе... (связь с флотом утеряна)',\n          // 'Отчёт флота %1$s. Мы завершили исследование сектора. Команда недовольна Эй, ты что делаешь на мостике?! (связь с флотом утеряна)',\n          'Отчёт флота %1$s. Всё спокойно (помехи) (связь с флотом утеряна)',\n          'АААААА! ЧТО ЭТО?! ОТКУДА ОНО ВЗЯ (связь с флотом утеряна)',\n          'Обнаружен неизвестный объект. Он не отвечает на запросы стандартных протоколов. Высылаем зонд для проведения исследований (связь с флотом утеряна)',\n        ),\n      ),\n\n      Constants::EXPEDITION_OUTCOME_FOUND_FLEET => [\n        'no_result' => 'К сожалению, совокупной мощности всех компьютеров флота не хватило даже на контроль самого мелкого корабля. Попробуйте отправлять больше кораблей и/или более крупные корабли',\n        'messages' => [\n          0 => [\n            'Вы нашли абсолютно новый флот',\n          ],\n          1 => [\n            'Вы нашли флот',\n          ],\n          2 => [\n            'Вы нашли б/у флот',\n          ],\n        ],\n      ],\n\n      Constants::EXPEDITION_OUTCOME_FOUND_RESOURCES => array(\n        'no_result' => 'Трюмы вашего флота оказались неспособны вместить хоть один контейнер с ресурсами. Попробуйте отправлять флот с большим количеством транспортников',\n        'messages' => array(\n          0 => array(\n            'Вы нашли пиратский клад с ресурсами. Сколько же кораблей было уничтожено, что бы собрать столько добра?',\n          ),\n          1 => array(\n            'Вы нашли заброшенную астероидную базу. Интересно, куда делись её обитатели? Исследовав руины, вы нашли несколько уцелевших хранилищ',\n          ),\n          2 => array(\n            'Вы наткнулись на уничтоженный транспортный конвой. Обыскав трюмы разбитых кораблей, вы обнаружили немного ресурсов',\n          ),\n        ),\n      ),\n\n      Constants::EXPEDITION_OUTCOME_FOUND_DM => array(\n        'no_result' => 'К сожалению, всех накопителей флота не хватило что бы собрать одну-единственую ТМ. Попробуйте отправлять флот побольше',\n        'messages' => 'Ваш флот стал свидетелем рождения СуперНовы',\n        /*\n        'messages' => array(\n          'Ваш флот стал свидетелем рождения СуперНовы 1',\n          'Ваш флот стал свидетелем рождения СуперНовы 2',\n          'Ваш флот стал свидетелем рождения СуперНовы 3',\n        ),\n        */\n      ),\n\n    ),\n  ),\n\n  // News page & a bit of imperator page\n  'news_fresh'      => 'Fresh news',\n  'news_all'        => 'All news',\n  'news_title'      => 'News',\n  'news_none'       => 'No news',\n  'news_new'        => 'New',\n  'news_future'     => 'Announcement',\n  'news_more'       => 'Read More...',\n  'news_hint'       => 'To close fresh news list - read them all by clicking on header \"[ News ]\"',\n\n  'news_date'       => 'Date',\n  'news_announce'   => 'Table of Contents',\n  'news_detail_url' => 'Link to more info',\n  'news_mass_mail'  => 'Send news to all players',\n\n  'news_total'      => 'Total news: ',\n\n  'news_add'        => 'Submit news',\n  'news_edit'       => 'Edit news',\n  'news_copy'       => 'Copy the news',\n  'news_mode_new'   => 'New',\n  'news_mode_edit'  => 'Editing',\n  'news_mode_copy'  => 'Copying',\n\n  'sys_administration' => 'Server Administration',\n\n  // Shortcuts\n  'shortcut_title'     => 'Shortcuts',\n  'shortcut_none'      => 'No shortcuts',\n  'shortcut_new'       => 'NEW',\n  'shortcut_text'      => 'Text',\n\n  'shortcut_add'       => 'Add shortcut',\n  'shortcut_edit'      => 'Edit shortcut',\n  'shortcut_copy'      => 'Copy shortcut',\n  'shortcut_mode_new'  => 'New',\n  'shortcut_mode_edit' => 'Editing',\n  'shortcut_mode_copy' => 'Copying',\n\n  // Missile-related\n  'mip_h_launched'\t\t\t=> 'Launch of interplanetary missiles',\n  'mip_launched'\t\t\t\t=> 'Launching interplanetary missiles: <b>%s</b>!',\n\n  'mip_no_silo'\t\t\t\t=> 'Insufficient level of silos on the planet <b>%s</b>.',\n  'mip_no_impulse'\t\t\t=> 'You want to investigate pulse motor.',\n  'mip_too_far'\t\t\t\t=> 'Rocket cannot fly that far.',\n  'mip_planet_error'\t\t\t=> 'Error - more than one planet one coordinate',\n  'mip_no_rocket'\t\t\t\t=> 'Not enough missiles in the shaft to carry out the attack.',\n  'mip_hack_attempt'\t\t\t=> ' You an hacker? Another joke and you will be banned. IP address and login Is recorded.',\n\n  'mip_all_destroyed' \t\t=> 'All interplanetary missiles were destroyed missile intercepted<br>',\n  'mip_destroyed'\t\t\t\t=> '%s interplanetary missiles were destroyed by intercept missiles.<br>',\n  'mip_defense_destroyed'\t=> 'Destroyed following defences:<br />',\n  'mip_recycled'\t\t\t\t=> 'Repaired from the debris of defence equipment: ',\n  'mip_no_defense'\t\t\t=> 'On an affected planet protection!',\n\n  'mip_sender_amd'\t\t\t=> 'Rocket and space forces',\n  'mip_subject_amd'\t\t\t=> 'Missile attack',\n  'mip_body_attack'\t\t\t=> 'Attack of the interplanetary missiles (%1$s PCs.) with the planet %2$s <a href=\"galaxy.php?mode=3&galaxy=%3$d&system=%4$d&planet=%5$d\">[%3$d:%4$d:%5$d]</a> on the planet %6$s <a href=\"galaxy.php?mode=3&galaxy=%7$d&system=%8$d&planet=%9$d\">[%7$d:%8$d:%9$d]</a><br><br>',\n\n  // Misc\n  'sys_game_rules' => 'Rules of the game',\n  'sys_game_documentation' => 'Documentation',\n  'sys_max' => 'Max',\n  'sys_banned_msg' => 'You are banned. For more information please visit <a href=\"banned.php\">here</a>. Time of account ban: ',\n  'sys_total_time' => 'Total time',\n  'sys_total_time_short' => 'Que time',\n\n  // Univers\n  'uni_moon_of_planet' => 'planet',\n\n  // Combat reports\n  'cr_view_title'  => \"View Combat Reports\",\n  'cr_view_button' => \"View Report\",\n  'cr_view_prompt' => \"Enter the code\",\n  'cr_view_my'     => \"My Combat Records\",\n  'cr_view_hint'   => \"This page allows you to view shared Combat Reports. All Combat Reports will have a code at the bottom. To share a Combat Report simply give them that code. Then they can enter it here and view your Combat Report.\",\n\n  // Fleet\n  'flt_gather_all'    => 'Gather resources',\n\n  // Ban system\n  'ban_title'      => 'Black list',\n  'ban_name'       => 'Name',\n  'ban_reason'     => 'The reason for the ban',\n  'ban_from'       => 'Ban data',\n  'ban_to'         => 'Term of Ban',\n  'ban_by'         => 'Issued',\n  'ban_no'         => 'No Banned players',\n  'ban_thereare'   => 'Total',\n  'ban_players'    => 'Banned',\n  'ban_banned'     => 'Players banned: ',\n\n  // Contacts\n  'ctc_title' => 'Administration',\n  'ctc_intro' => 'Here you will find the addresses of all administrators and operators of the games for feedback',\n  'ctc_name'  => 'Name',\n  'ctc_rank'  => 'Rank',\n  'ctc_mail'  => 'E-Mail',\n\n  // Records page\n  'rec_title'  => 'Universe Records',\n  'rec_build'  => 'Building',\n  'rec_specb'  => 'Special Building',\n  'rec_playe'  => 'Player',\n  'rec_defes'  => 'Defence',\n  'rec_fleet'  => 'Fleet',\n  'rec_techn'  => 'Technology',\n  'rec_level'  => 'Level',\n  'rec_nbre'   => 'Number',\n  'rec_rien'   => '-',\n\n  // Credits page\n  'cred_link'    => 'Internet',\n  'cred_site'    => 'Site',\n  'cred_forum'   => 'Forum',\n  'cred_credit'  => 'Authors',\n  'cred_creat'   => 'Director',\n  'cred_prog'    => 'Programmer',\n  'cred_master'  => 'Moderator',\n  'cred_design'  => 'DesignerСЂ',\n  'cred_web'     => 'Webmaster',\n  'cred_thx'     => 'Thanks',\n  'cred_based'   => 'Basis for establishing XNova',\n  'cred_start'   => 'Place debut XNova',\n\n  // Built-in chat\n  'chat_common'  => 'Common chat',\n  'chat_ally'    => 'Ally chat',\n  'chat_history' => 'History',\n  'chat_message' => 'Message',\n  'chat_send'    => 'Send',\n  'chat_page'    => 'Page',\n  'chat_timeout' => 'Chat is disabled from your inactivity. Refresh the page.',\n\n  // ----------------------------------------------------------------------------------------------------------\n  // Interface of Jump Gate\n  'gate_start_moon' => 'Home Moon',\n  'gate_dest_moon'  => 'Destination Moon',\n  'gate_use_gate'   => 'Use Gate',\n  'gate_ship_sel'   => 'Select ships',\n  'gate_ship_dispo' => 'photos',\n  'gate_jump_btn'   => 'jump!!',\n  'gate_jump_done'  => 'Gates are in the process of reloading!<br>Gates will be ready for use through: ',\n  'gate_wait_dest'  => 'points of destination Gate is in preparations! gate will be ready for use: ',\n  'gate_no_dest_g'  => 'The ultimate destination did not open the gate to move the fleet',\n  'gate_no_src_ga'  => 'There is no gates on current moon',\n  'gate_wait_star'  => 'Gates are in the process of reloading!<br>Gates will be ready for use: ',\n  'gate_wait_data'  => 'error, no data to make jump!',\n  'gate_vacation'   => 'Error, you cannot leap because you are in Vacation Mode!',\n  'gate_ready'      => 'Gate ready to jump',\n\n  // quests\n  'qst_quests'               => 'Quests',\n  'qst_msg_complete_subject' => 'You completed quest!',\n  'qst_msg_complete_body'    => 'You completed quest \"%s\".',\n  'qst_msg_your_reward'      => 'Your reward: ',\n\n  // Messages\n  'msg_from_admin' => 'Universe Administration',\n  'msg_class' => array(\n    MSG_TYPE_OUTBOX => 'Sent messages',\n    MSG_TYPE_SPY => 'Spy reports',\n    MSG_TYPE_PLAYER => 'Message by players',\n    MSG_TYPE_ALLIANCE => 'Alliance Communications',\n    MSG_TYPE_COMBAT => 'Military reports',\n    MSG_TYPE_RECYCLE => 'Records processing',\n    MSG_TYPE_TRANSPORT => 'The arrival of the fleet',\n    MSG_TYPE_ADMIN => 'Administrative messages',\n    MSG_TYPE_EXPLORE => 'Reports for expeditions',\n    MSG_TYPE_QUE => 'Message queue structures',\n    MSG_TYPE_NEW => 'All messages',\n  ),\n\n  'msg_que_research_from'    => 'Scientists',\n  'msg_que_research_subject' => 'Scientific discovery',\n  'msg_que_research_message' => 'New technology \"%s\" level %d was discovered',\n\n  'msg_que_planet_from'    => 'Governor',\n\n  'msg_que_hangar_subject' => 'Building on hangar complete',\n  'msg_que_hangar_message' => \"Hangar on %s complete his work\",\n\n  'msg_que_built_subject'   => 'Planetary build work complete',\n  'msg_que_built_message'   => \"Building of '%2\\$s' on %1\\$s complete. Levels built: %3\\$d\",\n  'msg_que_destroy_message' => \"Demolition of '%2\\$s' on %1\\$s complete. Levels demolished: %3\\$d\",\n\n  'msg_personal_messages' => 'Personal Messages',\n\n  'sys_opt_bash_info'    => 'Antibashing settings',\n  'sys_opt_bash_attacks' => 'Attacks per wave',\n  'sys_opt_bash_interval' => 'Interval between waves',\n  'sys_opt_bash_scope' => 'Bashing calculate period',\n  'sys_opt_bash_war_delay' => 'Moratory after declaring war',\n  'sys_opt_bash_waves' => 'Waves per period',\n  'sys_opt_bash_disabled'    => 'Antibashing system disabled',\n\n  'sys_id' => 'ID',\n  'sys_identifier' => 'Identifier',\n\n  'sys_email'   => 'E-Mail',\n  'sys_ip' => 'IP',\n\n  'sys_max' => 'Max',\n  'sys_maximum' => 'Maximum',\n  'sys_maximum_level' => 'Max level',\n\n  'sys_user_name' => 'User name',\n  'sys_player_name' => 'Player name',\n  'sys_user_name_short' => 'Name',\n\n  'sys_planets' => 'Planets',\n  'sys_moons' => 'Moons',\n\n  'sys_quantity' => 'Quantity',\n  'sys_quantity_maximum' => 'Maximum quantity',\n  'sys_qty' => 'Qty',\n  'sys_quantity_total' => 'Total',\n\n  'sys_buy_for' => 'Buy for',\n  'sys_buy' => 'Buy',\n\n  'sys_eco_lack_dark_matter' => 'Not enough Dark Matter',\n\n  'time_local' => 'Time on player',\n  'time_server' => 'Time on server',\n\n  'topnav_imp_attack' => 'Your Empire is attacked!',\n\n  'sys_result' => array(\n    'error_dark_matter_not_enough' => 'Не хватает Тёмной Материи для завершения операции',\n    'error_dark_matter_change' => 'Ошибка изменения количества Тёмной Материи! Повторите операцию еще раз. Если ошибка повторится - сообщите Администрации сервера',\n  ),\n\n  // Arrays\n  'sys_build_result' => array(\n    BUILD_ALLOWED => 'Can be built',\n    BUILD_REQUIRE_NOT_MEET => 'Requirements not met',\n    BUILD_AMOUNT_WRONG => 'Too much',\n    BUILD_QUE_WRONG => 'Queue not exists',\n    BUILD_QUE_UNIT_WRONG => 'Wrong queue',\n    BUILD_INDESTRUCTABLE => 'Can not be destroyed',\n    BUILD_NO_RESOURCES => 'Not enough resources',\n    BUILD_NO_UNITS => 'No units',\n    BUILD_UNIT_BUSY => array(\n      0 => 'Busy',\n      STRUC_LABORATORY => 'Research ongoing',\n      STRUC_LABORATORY_NANO => 'Research ongoing',\n    ),\n    BUILD_QUE_FULL => 'Que is full',\n    BUILD_SILO_FULL => 'Silo is full',\n    BUILD_MAX_REACHED => 'You already build and/or enqued maximum numbers of this type units',\n    BUILD_SECTORS_NONE => 'No free sectors',\n    BUILD_AUTOCONVERT_AVAILABLE => 'Autoconvert available',\n    BUILD_HIGHSPOT_NOT_ACTIVE => 'Festival highspot is not active',\n  ),\n\n  'sys_game_mode' => array(\n    GAME_SUPERNOVA => 'SuperNova',\n    GAME_OGAME     => 'oGame',\n    GAME_BLITZ     => 'Blitz',\n  ),\n\n  'months' => array(\n    '01'=>'January',\n    '02'=>'February',\n    '03'=>'March',\n    '04'=>'April',\n    '05'=>'May',\n    '06'=>'June',\n    '07'=>'July',\n    '08'=>'August',\n    '09'=>'September',\n    '10'=>'October',\n    '11'=>'November',\n    '12'=>'December'\n  ),\n\n  'weekdays' => array(\n    0 => 'Sunday',\n    1 => 'Monday',\n    2 => 'Tuesday',\n    3 => 'Wednesday',\n    4 => 'Thursday',\n    5 => 'Friday',\n    6 => 'Saturday'\n  ),\n\n  'user_level' => array(\n    0 => 'Player',\n    1 => 'Moderator',\n    2 => 'Operator',\n    3 => 'Administrator',\n    4 => 'Developer',\n  ),\n\n  'user_level_shortcut' => array(\n    0 => 'P',\n    1 => 'M',\n    2 => 'O',\n    3 => 'A',\n    4 => 'D',\n  ),\n\n  'sys_lessThen15min'   => '&lt; 15 min',\n\n  'sys_no_points'        => 'You do not have enough Dark Matter!',\n  'sys_dark_matter_obtain_header' => 'How to obtain Dark Matter',\n  'sys_dark_matter_desc' => 'Dark matter - using the standard methods of  fabric, which accounts for 23% mass of the universe. From there you can obtain an incredible amount of energy. Because of this, and because of the complexities associated with its extraction, Dark Matter is valued very highly.',\n  'sys_dark_matter_hint' => 'With the help of this substance you can hire officers and commanders.',\n\n  'sys_dark_matter_what_header' => 'What is <span class=\"dark_matter\">Dark Matter</span>',\n  'sys_dark_matter_description_header' => 'Why do you need <span class=\"dark_matter\">Dark Matter</span>',\n  'sys_dark_matter_description_text' => '<span class=\"dark_matter\">Dark Matter</span> is ingame currency, which in the game you can make a variety of operations:\n    <ul>\n      <li>Buy <a href=\"index.php?page=premium\"><span class=\"link\">Premium account</span></a></li>\n      <li>Recruit <a href=\"officer.php?mode=600\"><span class=\"link\">Mercenaries</span></a> for Empire</li>\n      <li>Hire Governors and but additional sectors <a href=\"overview.php?mode=manage\"><span class=\"link\">for planets</span></a></li>\n      <li>Buy <a href=\"officer.php?mode=1100\"><span class=\"link\">Schematics</span></a></li>\n      <li>Buy <a href=\"artifacts.php\"><span class=\"link\">Artefacts</span></a></li>\n      <li>Use <a href=\"market.php\"><span class=\"link\">Black Market</span></a>: exchange resources; sell ships; buy ships; buy intelligence etc</li>\n      <li>...and many other things</li>\n    </ul>',\n  'sys_dark_matter_obtain_text' => 'You acquring <span class=\"dark_matter\">Dark Matter</span> in game process: while gained levels for raids to enemy planets, researching technologies, building and destroying buildings.\n    Also sometimes expeditions can gain you some <span class=\"dark_matter\">DM</span>.',\n\n//  'sys_dark_matter_obtain_text_convert' => '<br />Besides, you can convert Metamatter to Dark Matter. <a href=\"metamatter.php\" class=\"link\">More about Metamatter</a>',\n  'sys_dark_matter_obtain_text_convert' => '<br />If you lack <span class=\"dark_matter\">Dark Matter</span> - purchase the <span class=\"metamatter\">Metamatter</span>. If you have not enough <span class=\"dark_matter\">DM</span> needed amount of <span class=\"metamatter\">Metamatter</span> would be used instead of <span class=\"dark_matter\">DM</span>',\n\n  'sys_msg_err_update_dm' => 'Error updating DM quantity!',\n\n  'sys_na' => 'Not available',\n  'sys_na_short' => 'N/A',\n\n  'sys_ali_res_title' => 'Alliance\\'s resources',\n\n  'sys_bonus' => 'Bonus',\n\n  'sys_of_ally' => 'of Alliance',\n\n  'sys_hint_player_name' => 'You can search player by his ID or name. If player name consists from strange symbols or only from numbers - you should use player ID for search.',\n  'sys_hint_ally_name' => 'You can search Alliance by his ID, tag or name. If Alliance\\'s tag or name consists from strange symbols or only from numbers - you should use ally ID for search.',\n\n  'sys_fleet_and' => '+ fleets',\n\n  'sys_on_planet' => 'On planet',\n  'fl_on_stores' => 'In stock',\n\n  'sys_ali_bonus_members' => 'Minimum Alliance size for Ally bonus ',\n\n  'sys_premium' => 'Premium account',\n\n  'mrc_period_list' => array(\n    PERIOD_MINUTE    => '1 minute',\n    PERIOD_MINUTE_3  => '3 minutes',\n    PERIOD_MINUTE_5  => '5 minutes',\n    PERIOD_MINUTE_10 => '10 minutes',\n    PERIOD_DAY       => '1 day',\n    PERIOD_DAY_3     => '3 days',\n    PERIOD_WEEK      => '1 week',\n    PERIOD_WEEK_2    => '2 weeks',\n    PERIOD_MONTH     => '30 days',\n    PERIOD_MONTH_2   => '60 days',\n    PERIOD_MONTH_3   => '90 days',\n  ),\n\n  'sys_sector_buy' => 'Buy 1 sector',\n\n  'sys_select_confirm' => 'Confirm selection',\n\n  'sys_capital' => 'Capital',\n\n  'sys_result_operation' => 'Outcome',\n\n  'sys_password' => 'Password',\n  'sys_password_length' => 'Password length',\n  'sys_password_seed' => 'Used characters',\n\n  'sys_msg_ube_report_err_not_found' => 'Battle report not found - check cypher key. It is possible that battle report was deleted as outdated',\n\n  'sys_mess_attack_report' \t=> 'Battle Report',\n  'sys_perte_attaquant' \t\t=> 'The Attacker lost',\n  'sys_perte_defenseur' \t\t=> 'The Defender lost',\n\n\n  'ube_report_info_page_header' => 'Battle report',\n  'ube_report_info_page_header_cypher' => 'Access code',\n  'ube_report_info_main' => 'Main battle info',\n  'ube_report_info_date' => 'Date and time',\n  'ube_report_info_location' => 'Location',\n  'ube_report_info_rounds_number' => 'Round number',\n  'ube_report_info_outcome' => 'Battle outcome',\n  'ube_report_info_outcome_win' => 'Attacker win',\n  'ube_report_info_outcome_loss' => 'Attacker lost',\n  'ube_report_info_outcome_draw' => 'Draw',\n  'ube_report_info_link' => 'Link to battle report',\n  'ube_report_info_bbcode' => 'BBCode for posting in chat',\n  'ube_report_info_sfr' => 'Battle finshed in one round by attacker loss<br />Possible SFR',\n  'ube_report_info_debris' => 'Debris on orbit',\n  'ube_report_info_debris_simulator' => '(does not counting moon)',\n  'ube_report_info_loot' => 'Loot',\n  'ube_report_info_loss' => 'Battle losses',\n  'ube_report_info_generate' => 'Page generation time',\n\n  'ube_report_moon_was' => 'This planet already had moon',\n  'ube_report_moon_chance' => 'Moon chance',\n  'ube_report_moon_created' => 'On planet orbit appears new moon diameter',\n\n  'ube_report_moon_reapers_none' => 'All ships with graviton engines was destroyed during fight',\n  'ube_report_moon_reapers_wave' => 'Attacker\\'s ship created focused gravitation wave',\n  'ube_report_moon_reapers_chance' => 'Moon destruction chance',\n  'ube_report_moon_reapers_success' => 'Moon destroyed',\n  'ube_report_moon_reapers_failure' => 'Graviton wave power was not enough to destroy moon',\n\n  'ube_report_moon_reapers_outcome' => 'Graviton engines self-desctruction chance',\n  'ube_report_moon_reapers_survive' => 'Graviton engines was succesfully synchronized and compensated graviton recoil',\n  'ube_report_moon_reapers_died' => 'Graviton engines self-destructs and destroy all your fleet',\n\n  'ube_report_side_attacker' => 'Attacker',\n  'ube_report_side_defender' => 'Defender',\n\n  'ube_report_round' => 'Round',\n  'ube_report_unit' => 'Unit',\n  'ube_report_attack' => 'Attack',\n  'ube_report_shields' => 'Shields',\n  'ube_report_shields_passed' => 'Puncture',\n  'ube_report_armor' => 'Armor',\n  'ube_report_damage' => 'Damage',\n  'ube_report_loss' => 'Losses',\n\n  'ube_report_info_restored' => 'Defense unit recovered',\n  'ube_report_info_loss_final' => 'Total unit loss',\n  'ube_report_info_loss_resources' => 'Loss in resources',\n  'ube_report_info_loss_dropped' => 'Resource loss due reduced cargo capacity',\n  'ube_report_info_loot_lost' => 'Resources looted from planet',\n  'ube_report_info_loss_gained' => 'Resource loss due planet loot',\n  'ube_report_info_loss_in_metal' => 'Total resources lost in metal',\n\n  'ube_report_msg_body_common' => 'Battle on %s on orbit of %s [%d:%d:%d] %s<br />%s<br /><br />',\n  'ube_report_msg_body_debris' => 'There are debris appears on planet orbit:<br />',\n  'ube_report_msg_body_sfr' => 'You lost contact with your fleet',\n\n  'ube_report_capture' => 'Planet capture',\n  'ube_report_capture_result' => array(\n    UBE_CAPTURE_DISABLED => 'Захват планет отключён',\n    UBE_CAPTURE_NON_PLANET => 'Захватывать можно только планеты',\n    UBE_CAPTURE_NOT_A_WIN_IN_1_ROUND => 'Для захвата планеты бой должен закончиться победой в первом раунде',\n    UBE_CAPTURE_TOO_MUCH_FLEETS => 'При захвате планеты в бою должен участвовать только флот-захватчик и планетарный флот',\n    UBE_CAPTURE_NO_ATTACKER_USER_ID => 'ВНУТРЕННЯЯ ОШИБКА - Нет ИД атакующего! Сообщите разработчику!',\n    UBE_CAPTURE_NO_DEFENDER_USER_ID => 'ВНУТРЕННЯЯ ОШИБКА - Нет ИД защитника! Сообщите разработчику!',\n    UBE_CAPTURE_CAPITAL => 'Нельзя захватывать столицу',\n    UBE_CAPTURE_TOO_LOW_POINTS => 'Можно захватывать планеты только у игроков, чье общее количество очков не менее чем в 2 раза больше количество очков у атакующего',\n    UBE_CAPTURE_NOT_ENOUGH_SLOTS => 'Больше нет слотов захвата планеты',\n    UBE_CAPTURE_SUCCESSFUL => 'Планета захвачена атакующим игроком',\n  ),\n\n  'sys_kilometers_short' => 'km',\n\n  'ube_simulation' => 'Simulation',\n\n  'sys_hire_do' => 'Hire',\n\n  'sys_captains' => 'Captains',\n\n  'sys_fleet_composition' => 'Fleet composition',\n\n  'sys_continue' => 'Continue',\n\n  'uni_planet_density_types' => array(\n    PLANET_DENSITY_NONE => 'Never happens',\n    PLANET_DENSITY_ICE_HYDROGEN => 'Hydrogen ice',\n    PLANET_DENSITY_ICE_METHANE => 'Methane ice',\n    PLANET_DENSITY_ICE_WATER => 'Water ice',\n    PLANET_DENSITY_CRYSTAL_RAW => 'Crystal',\n    PLANET_DENSITY_CRYSTAL_SILICATE => 'Silicate',\n    PLANET_DENSITY_CRYSTAL_STONE => 'Stone',\n    PLANET_DENSITY_STANDARD => 'Standard',\n    PLANET_DENSITY_METAL_ORE => 'Ore',\n    PLANET_DENSITY_METAL_PERIDOT => 'Peridot',\n    PLANET_DENSITY_METAL_RAW => 'Metal',\n  ),\n\n  'sys_planet_density' => 'Density',\n  'sys_planet_density_units' => 'kg/m&sup3;',\n  'sys_planet_density_core' => 'Core type',\n\n  'sys_change' => 'Change',\n  'sys_show' => 'Show',\n  'sys_hide' => 'Hide',\n  'sys_close' => 'Close',\n  'sys_unlimited' => 'Unlimited',\n\n  'ov_core_type_current' => 'Current core type',\n  'ov_core_change_to' => 'Change to',\n  'ov_core_err_none' => 'Planet core type succesfully changed from \"%s\" to \"%s\".<br />New average planet density %d kg/m3',\n  'ov_core_err_not_a_planet' => 'Only planet core type could be changed',\n  'ov_core_err_denisty_type_wrong' => 'Wrong core type',\n  'ov_core_err_same_density' => 'New core type does not differ from current one - nothing to change',\n  'ov_core_err_no_dark_matter' => 'There are not enough Dark Matter to change core type',\n\n  'sys_color'  => \"Color\",\n  'topnav_imp_attack' => 'Your Empire is attacked',\n  'topnav_user_rank' => 'Your current place in statistic',\n  'topnav_users' => 'Number of registered players',\n  'topnav_users_online' => 'Now online',\n\n  'topnav_refresh_page' => 'Reload page',\n\n  'sys_colonies' => 'Colonies',\n  'sys_radio' => '\"Space\" radio',\n\n  'sys_auth_provider_list' => array(\n    ACCOUNT_PROVIDER_NONE => 'Таблица USERS',\n    ACCOUNT_PROVIDER_LOCAL => 'Таблица ACCOUNT',\n    ACCOUNT_PROVIDER_CENTRAL => 'Центральная таблица ACCOUNT',\n  ),\n\n  'sys_login_messages' => array(\n    LOGIN_UNDEFINED => 'Login process does not started',\n    LOGIN_SUCCESS => 'Login succesfull',\n    LOGIN_ERROR_USERNAME_EMPTY => 'Имя игрока не может быть пустым',\n    LOGIN_ERROR_USERNAME_RESTRICTED_CHARACTERS => 'В имени игрока и логине не допускаются символы ',\n    LOGIN_ERROR_USERNAME => 'There is no player with such name',\n    LOGIN_ERROR_USERNAME_ALLY_OR_BOT => 'Это имя принадлежит Альянсу или боту. Под ним нельзя логиниться... по крайней мере пока',\n    LOGIN_ERROR_PASSWORD_EMPTY => 'Пароль не может быть пустым',\n    LOGIN_ERROR_PASSWORD_TRIMMED => 'Пароль не может начинаться или заканчиваться пробелом, табуляцией или символом перевода строки',\n    LOGIN_ERROR_PASSWORD => 'Wrong password',\n    //    LOGIN_ERROR_COOKIE => '',\n\n    REGISTER_SUCCESS => 'Registration succesfully complete',\n    REGISTER_ERROR_BLITZ_MODE => 'Регистрация новых игроков в режиме Блиц-сервера отключена',\n    REGISTER_ERROR_USERNAME_WRONG => 'Wrong player name',\n    REGISTER_ERROR_ACCOUNT_NAME_EXISTS => 'Account name already registered',\n    REGISTER_ERROR_PASSWORD_INSECURE => 'Insecure or wrong password. Password should be at least ' . PASSWORD_LENGTH_MIN . ' characters long and cannot start or end with spaces',\n    REGISTER_ERROR_USERNAME_SHORT => 'Слишком короткое имя. Имя должно состоять минимум из ' . LOGIN_LENGTH_MIN. ' символов',\n    REGISTER_ERROR_PASSWORD_DIFFERENT => 'Password does not match confirmation password',\n    REGISTER_ERROR_EMAIL_EMPTY => 'Е-Мейл не может быть пустым',\n    REGISTER_ERROR_EMAIL_WRONG => 'Введенный Е-Мейл не является адресом электронной почты',\n    REGISTER_ERROR_EMAIL_EXISTS => 'This email already registered. If you already registered try password reset option. Otherwise use other email address',\n\n    PASSWORD_RESTORE_ERROR_EMAIL_NOT_EXISTS => 'There is no player with such base email',\n    PASSWORD_RESTORE_ERROR_TOO_OFTEN => 'You can request password restoration code only once per 10 minutes. Check your SPAM folder for restoration code or contact server administration via email <span class=\"ok\">' . $config->server_email . '</span> from your main email (email which you used for registration)',\n    PASSWORD_RESTORE_ERROR_SENDING => 'There is error sending email with restore code. Contact server administration via email <span class=\"ok\">' . $config->server_email . '</span>',\n    PASSWORD_RESTORE_SUCCESS_CODE_SENT => 'Restoration code successfully sent',\n\n    PASSWORD_RESTORE_ERROR_CODE_EMPTY => 'Restoration code can not be empty',\n    PASSWORD_RESTORE_ERROR_CODE_WRONG => 'Wrong restoration code',\n    PASSWORD_RESTORE_ERROR_CODE_TOO_OLD => 'Restoration code is too old. Get new one',\n    PASSWORD_RESTORE_ERROR_CODE_OK_BUT_NO_ACCOUNT_FOR_EMAIL => 'Код восстановления указан верно, однако не найдено ни одного аккаунта с таким емейлом. Возможно, он был удалён или произошла внутренняя ошибка. Обратитесь к Администрации сервера',\n    PASSWORD_RESTORE_SUCCESS_PASSWORD_SENT => 'Email with new password successfully sent to your email',\n    PASSWORD_RESTORE_SUCCESS_PASSWORD_SEND_ERROR => 'Error sending new password. Get new restoration code and try again',\n\n\n    REGISTER_ERROR_PLAYER_NAME_TRIMMED => 'Player name can not starts or ends with \"empty characters\" (characters like \"Space\", \"Tabulation\", \"New line\" etc)',\n    REGISTER_ERROR_PLAYER_NAME_EMPTY => 'Player name can not be empty',\n    REGISTER_ERROR_PLAYER_NAME_RESTRICTED_CHARACTERS => 'Player name contains forbidden characters',\n    REGISTER_ERROR_PLAYER_NAME_SHORT => 'Player name should have ' . LOGIN_LENGTH_MIN . ' characters at least',\n    REGISTER_ERROR_PLAYER_NAME_EXISTS => 'This player name is already owned by someone. Please choose another name',\n\n\n    // Внутренние ошибки\n    AUTH_ERROR_INTERNAL_PASSWORD_CHANGE_ON_RESTORE => 'Password change error. Contact server administration',\n    PASSWORD_RESTORE_ERROR_ADMIN_ACCOUNT => 'Forbidden to restore password for member of Server Team. Contact Administrator directly',\n    REGISTER_ERROR_ACCOUNT_CREATE => 'Error creating account! Please, message Administration about this error!',\n    LOGIN_ERROR_SYSTEM_ACCOUNT_TRANSLATION => 'СИСТЕМНАЯ ОШИБКА - СБОЙ В ТАБЛИЦЕ ТРАНСЛЯЦИИ ПРОВАЙДЕРОВ! Сообщите администрации сервера!',\n    PASSWORD_RESTORE_ERROR_ACCOUNT_NOT_EXISTS => 'Account not found! Contact server administration!',\n    AUTH_PASSWORD_RESET_INSIDE_ERROR_NO_ACCOUNT_FOR_CONFIRMATION => 'INTERNAL ERROR! No account to change password on confirmation code. Please, report to Universe Administration!',\n    LOGIN_ERROR_NO_ACCOUNT_FOR_COOKIE_SET => 'INTERNAL ERROR! No account for cookie_set()! Please, report to Universe Administration!',\n  ),\n\n  'log_reg_email_title' => \"Your registration on SuperNova game server %1\\$s\",\n  'log_reg_email_text' => \"Registration confirmation for account %3\\$s\\r\\n\\r\\n\n  This letter contains registration data on SuperNova game server %1\\$s\n  Store this data in safe place\\r\\n\\r\\n\n  Server address: %2\\$s\\r\\n\n  Your login: %3\\$s\\r\\n\n  Your password: %4\\$s\\r\\n\\r\\n\n  Thank you for registering on our server! We wish you luck in game!\\r\\n\n  Server administration %1\\$s %2\\$s\\r\\n\\r\\n\n  Powered by OpenSource engine 'Project SuperNova.WS'. Light your SuperNova http://supernova.ws/\",\n\n  'log_lost_email_title' => 'Supernova, Universe %s: Password reset',\n  'log_lost_email_code' => \"Someone (possibly you) has requested a reset password on SuperNova Universe %4\\$s . If you did not request reset password-then just ignore this email.\\r\\n\\r\\nFor password reset, go to the address \\r\\n%1\\$s?password_reset_confirm=1&password_reset_code=%2\\$s#tab_password_reset\\r\\nor enter the confirmation code \\\"%2\\$s\\\" (WITHOUT THE DOUBLE QUOTES!) on the page %1\\$s#tab_password_reset This code will be valid up to %3\\$s. After the password reset you will need to request a new confirmation code\",\n  'log_lost_email_pass' => \"You changed your password on the SuperNova Universe %1\\$s.\\r\\n\\r\\nYou login:\\r\\n%2\\$s\\r\\n\\r\\nYour new password:\\r\\n%3\\$s\\r\\n\\r\\nRemember it!\\r\\n\\r\\nYou can enter into game following link \" . SN_ROOT_VIRTUAL . \"login.php and using provided login and password\",\n\n  'sys_password_reset_message_body' => \"Your password to accessing this Universe was reset.\\r\\n\\r\\nYour new password is:\\r\\n\\r\\n%1\\$s\\r\\n\\r\\nRemember your password!\\r\\n\\r\\nYou can change it any time to one that suits you on 'Settings' menu\",\n\n  'sys_login_password_show' => 'Show password',\n  'sys_login_password_hide' => 'Hide password',\n  'sys_password_repeat' => 'Confirm password',\n\n  'sys_game_disable_reason' => array(\n    GAME_DISABLE_NONE => 'Game is enabled',\n    GAME_DISABLE_REASON => 'Game is disabled. Players will see reason',\n    GAME_DISABLE_UPDATE => 'Game is updating',\n    GAME_DISABLE_STAT => 'Statistics update in progress',\n    GAME_DISABLE_INSTALL => 'Game is not configured yet',\n    GAME_DISABLE_EVENT_BLACK_MOON => 'Black Moon Rising!',\n  ),\n\n  'sys_sector_purchase_log' => 'User {%2$d} {%1$s} purchased 1 sector on planet {%5$d} {%3$s} type \"%4$s\" for %6$d DM',\n\n  'sys_notes' => 'Notes',\n  'sys_notes_priorities' => array(\n    0 => 'Low priority',\n    1 => 'Below normal',\n    2 => 'Normal',\n    3 => 'Important',\n    4 => 'Very important',\n  ),\n\n  'sys_milliseconds' => 'milliseconds',\n\n  'sys_gender' => 'Gender',\n  'sys_gender_list' => array(\n    GENDER_UNKNOWN => 'Will decide - when grew up',\n    GENDER_MALE => 'Male',\n    GENDER_FEMALE => 'Female',\n  ),\n\n  'imp_stat_header' => 'График изменений',\n  'imp_stat_types' => array(\n    'TOTAL_RANK' => 'Место в общей статистике',\n    'TOTAL_POINTS' => 'Общее количество очков',\n    // 'TOTAL_COUNT' => 'Общее количество ресурсов',\n    'TECH_RANK' => 'Место в статистике по Исследованиям',\n    'TECH_POINTS' => 'Количество очков за Исследования',\n    // 'TECH_COUNT' => 'Количество уровней',\n    'BUILD_RANK' => 'Место в статистике по Постройкам',\n    'BUILD_POINTS' => 'Количество очков за Постройки',\n    // 'BUILD_COUNT' => '',\n    'DEFS_RANK' => 'Место в статистике по Обороне',\n    'DEFS_POINTS' => 'Количество очков за Оборону',\n    //'DEFS_COUNT' => '',\n    'FLEET_RANK' => 'Место в статистике по Кораблям',\n    'FLEET_POINTS' => 'Количество очков за Корабли',\n    //'FLEET_COUNT' => '',\n    'RES_RANK' => 'Место в статистике по свободным ресурсам',\n    'RES_POINTS' => 'Количество очков за свободные ресурсы',\n    //'RES_COUNT' => '',\n  ),\n\n  'sys_date' => 'Date',\n\n  'sys_blitz_global_button' => 'Блиц-сервер',\n  'sys_blitz_page_disabled' => 'В режиме Блиц-сервера эта страница недоступна',\n  'sys_blitz_registration_disabled' => 'Регистрация на игру в Блиц-сервер отключена',\n  'sys_blitz_registration_no_users' => 'Нет зарегестрированных игроков',\n  'sys_blitz_registration_player_register' => 'Зарегестрироваться для игры',\n  'sys_blitz_registration_player_register_un' => 'Отозвать регистрацию',\n  'sys_blitz_registration_closed' => 'Регистрация пока закрыта. Попробуйте зайти позже',\n  'sys_blitz_registration_player_generate' => 'Сгенерировать логины и пароли',\n  'sys_blitz_registration_player_import_generated' => 'Импортировать сгенерированную строку',\n  'sys_blitz_registration_player_name' => 'Ваш логин для Блиц-сервера:',\n  'sys_blitz_registration_player_password' => 'Ваш пароль для Блиц-сервера:',\n  'sys_blitz_registration_server_link' => 'Ссылка на Блиц-сервер',\n  'sys_blitz_registration_player_blitz_name' => 'Имя на Блиц-сервере',\n  'sys_blitz_registration_price' => 'Стоимость подачи заявки',\n  'sys_blitz_registration_mode_list' => array(\n    BLITZ_REGISTER_DISABLED => 'Регистрация отключена',\n    BLITZ_REGISTER_OPEN => 'Регистрация открыта',\n    BLITZ_REGISTER_CLOSED => 'Регистрация закрыта',\n    BLITZ_REGISTER_SHOW_LOGIN => 'Открыты логины и пароли',\n    BLITZ_REGISTER_DISCLOSURE_NAMES => 'Подведение итогов',\n  ),\n\n  'survey' => 'Опрос',\n  'survey_questions' => 'Варианты для выбора',\n  'survey_questions_hint' => '1 вариант на строку',\n  'survey_questions_hint_edit' => 'Редактированние опроса обнулит его результаты',\n  'survey_until' => 'Длительность опроса (1 сутки по умолчанию)',\n\n  'survey_votes_total_none' => 'Еще никто не проголосовал... Проголосуй первым!',\n  'survey_votes_total_voted' => 'Уже проголосовало:',\n  'survey_votes_total_voted_join' => 'Голосуй - или проиграешь!',\n  'survey_votes_total_voted_has_answer' => 'Вы уже проголосовали. Вместе с вами проголосовавших',\n\n  'survey_lasts_until' => 'Опрос продлится до',\n\n  'survey_select_one' => 'Выберите один вариант ответа и нажмите',\n  'survey_confirm' => 'Проголосовать!',\n  'survey_result_sent' => 'Ваш голос учтен. Обновите страницу или воспользуйтесь ссылкой <a class=\"link\" href=\"announce.php\">Новости</a> что бы увидеть текущие результаты опроса',\n  'survey_complete' => 'Опрос завершен',\n\n  'player_option_fleet_ship_sort' => array(\n    PLAYER_OPTION_SORT_DEFAULT => 'Standard',\n    PLAYER_OPTION_SORT_NAME => 'By name',\n    PLAYER_OPTION_SORT_SPEED => 'By ship speed',\n    PLAYER_OPTION_SORT_COUNT => 'By ship quantity',\n    PLAYER_OPTION_SORT_ID => 'By ID',\n  ),\n\n  'player_option_building_sort' => array(\n    PLAYER_OPTION_SORT_DEFAULT => 'Standard',\n    PLAYER_OPTION_SORT_NAME => 'By name',\n    PLAYER_OPTION_SORT_ID => 'By ID',\n    PLAYER_OPTION_SORT_CREATE_TIME_LENGTH => 'By building time',\n  ),\n\n  'sys_sort' => 'Sort',\n  'sys_sort_inverse' => 'Reverse order',\n\n  'sys_blitz_reward_log_message' => 'Блиц-сервер %1$d призовое место блиц-имя \"%2$s\"',\n  'sys_blitz_registration_view_stat' => 'Посмотреть статистику Блиц-сервера',\n\n  'sys_login_register_message_title' => \"Ваше имя и пароль для входа в игру\",\n  'sys_login_register_message_body' => \"Ваше имя для входа в игру (логин)\\r\\n%1\\$s\\r\\n\\r\\nВаш пароль для входа в игру\\r\\n%2\\$s\\r\\n\\r\\nЗапишите или запомните эти данные!\",\n\n  'auth_provider_list' => array(\n    ACCOUNT_PROVIDER_NONE => 'Таблица users',\n    ACCOUNT_PROVIDER_LOCAL => 'Таблица account',\n    ACCOUNT_PROVIDER_CENTRAL => 'Центральное хранилище',\n  ),\n\n  'bld_autoconvert' => 'Автоматическая конвертация при создании юнита {%1$d} \"%4$s\" в количесте %2$d на планете %3$s',\n\n  'news_show_rest' => 'Показать текст новости',\n\n  'wiki_requrements' => 'Requires',\n  'wiki_grants' => 'Grants',\n\n  'que_slot_length' => 'Slots',\n\n  'sys_confirm_action_title' => 'Подтвердите ваше действие',\n  'sys_confirm_action' => 'Вы действительно хотите сделать это?',\n\n  'sys_system_speed_original' => 'Original speed',\n  'sys_system_speed_for_action' => 'Speed while action/event',\n\n  'menu_info_best_battles' => 'Best battles',\n\n  'sys_cost' => 'Cost',\n  'sys_price' => 'Price',\n\n  'sys_governor_none' => 'Governor not hired',\n  'sys_governor_hire' => 'Hire Governor',\n  'sys_governor_upgrade_or_change' => 'Upgrade or change Governor',\n\n  'tutorial_prev' => '<< Next',\n  'tutorial_next' => 'Prev >>',\n  'tutorial_finish' => 'Finish',\n  'tutorial_window' => 'Open in window',\n  'tutorial_window_off' => 'Return to page',\n\n  'tutorial_error_load' => \"Error loading tutorial - try again. If error persists - please contact game Administration\",\n  'tutorial_error_next' => \"Error - there is no next page in tutorial - please contact game Administration\",\n  'tutorial_error_prev' => \"Error - there is no previous page in tutorial - please contact game Administration\",\n\n  'sys_click_here_to_continue' => 'Click here to continue',\n\n  'sys_module_error_not_found' => 'Award module \"%1$s\" not found or disabled!',\n\n  'rank_page_title' => 'Ranks list',\n  'rank' => 'Military Rank',\n  'ranks' => [\n    0  => 'Cadet',\n    1  => 'Recruit',\n    2  => 'Private',\n    3  => 'Lance Corporal',\n    4  => 'Corporal',\n    5  => 'Sergeant',\n    6  => 'Sergeant Major',\n    7  => 'Midshipman',\n    8  => 'Warrant Officer',\n    9  => 'Ensign',\n    10 => 'Lieutenant',\n    11 => 'Captain',\n    12 => 'Major',\n    13 => 'Lieutenant Colonel',\n    14 => 'Colonel',\n    15 => 'Rear Admiral',\n    16 => 'Vice Admiral',\n    17 => 'Admiral',\n    18 => 'Fleet Admiral',\n    19 => 'Marshal',\n    20 => 'Generalissimo',\n  ],\n\n));\n"
  },
  {
    "path": "language/en/tech.mo.php",
    "content": "<?php\n\n/*\n#############################################################################\n#  Filename: tech.mo\n#  Project: SuperNova.WS\n#  Website: http://www.supernova.ws\n#  Description: Massive Multiplayer Online Browser Space Strategy Game\n#\n#  Copyright © 2009-2018 Gorlum for Project \"SuperNova.WS\"\n#  Copyright © 2008 Aleksandar Spasojevic <spalekg@gmail.com>\n#  Copyright © 2005 - 2008 KGsystem\n#############################################################################\n*/\n\n/**\n*\n* @package language\n* @system [English]\n* @version 46d0\n*\n*/\n\n/**\n* DO NOT CHANGE\n*/\n\nif (!defined('INSIDE')) die();\n\n$a_lang_array = (array(\n  'tech_storage_max' => 'Max. storage capacity',\n  'tech_storage' => 'In storage',\n  'tech_storage_energy' => 'Consumption',\n  'tech_storage_energy_max' => 'Production',\n  'tech_storage_energy_fullness' => 'Load',\n  'Tech' => 'Technology',\n  'Requirements' => 'Requirements',\n  'Metal' => 'Metal',\n  'Crystal' => 'Crystal',\n  'Deuterium' => 'Deuterium',\n  'Energy' => 'Energy',\n  'dark_matter' => 'Dark matter',\n  'ds' => 'Messages',\n  'Message' => 'Messages',\n  'level' => 'Level',\n  'treeinfo' => '[i]',\n  'comingsoon' => 'Coming Soon',\n  'te_dt_tx_pre' => 'Weak production',\n  'tech_fullness' => 'Fullness',\n\n  'type_mission' => array(\n    MT_NONE => '-- unknown --',\n    MT_ATTACK => 'Attack',\n    MT_AKS => 'Joint Attack',\n    MT_TRANSPORT => 'Transport',\n    MT_RELOCATE => 'Deployment',\n    MT_HOLD => 'Retention',\n    MT_SPY => 'Espionage',\n    MT_COLONIZE => 'Colonization',\n    MT_RECYCLE => 'Rework',\n    MT_DESTROY => 'Destruction',\n    MT_MISSILE => 'Missile attack',\n    MT_EXPLORE => 'Expedition',\n  ),\n  'fleet_events' => array(\n    EVENT_FLEET_NONE   => 'No event',\n    EVENT_FLEET_ARRIVE => 'Arriving',\n    EVENT_FLEET_STAY   => 'Hold/Expedition',\n    EVENT_FLEET_RETURN => 'Return',\n  ),\n\n  'tech' => array(\n    UNIT_STRUCTURES => 'Buildings',\n    STRUC_MINE_METAL => 'Metal mine',\n    STRUC_MINE_CRYSTAL => 'Crystal Mine',\n    STRUC_MINE_DEUTERIUM => 'Deuterium Synthesizer',\n    STRUC_MINE_SOLAR => 'Solar Plant',\n    STRUC_MINE_FUSION => 'Fusion Reactor',\n    STRUC_FACTORY_ROBOT => 'Robotics Factory',\n    STRUC_FACTORY_NANO => 'Nanite Factory',\n    STRUC_FACTORY_HANGAR => 'Shipyard',\n    STRUC_STORE_METAL => 'Metal Storage',\n    STRUC_STORE_CRYSTAL => 'Crystal Storage',\n    STRUC_STORE_DEUTERIUM => 'Deuterium Tank',\n    STRUC_LABORATORY => 'Research Lab',\n    STRUC_TERRAFORMER => 'Terraformer',\n    STRUC_ALLY_DEPOSIT => 'Alliance Depot',\n    STRUC_LABORATORY_NANO => 'Special Building',\n\n    UNIT_STRUCTURES_SPECIAL => 'Moon Buildings',\n    STRUC_MOON_STATION => 'Moon Base',\n    STRUC_MOON_PHALANX => 'Sensor Phalanx',\n    STRUC_MOON_GATE => 'Stargate',\n    STRUC_SILO => 'Missile Silo',\n\n    UNIT_TECHNOLOGIES => 'Technologies',\n    TECH_ENERGY => 'Energy Technology',\n    TECH_COMPUTER => 'Computer Technology',\n    TECH_SPY => 'Espionage Technology',\n    TECH_ARMOR => 'Armor Technology',\n    TECH_WEAPON => 'Weapons Technology',\n    TECH_SHIELD => 'Shielding Technology',\n    TECH_ENGINE_CHEMICAL => 'Rocket Engine',\n    TECH_ENGINE_ION => 'Impulse Engine',\n    TECH_ENGINE_HYPER => 'Hyperspace Drive',\n    TECH_LASER => 'Laser Technology',\n    TECH_ION => 'Ion Technology',\n    TECH_PLASMA => 'Plasma Technology',\n    TECH_HYPERSPACE => 'Hyperspace Technology',\n    TECH_EXPEDITION => 'Expedition Technology',\n    TECH_COLONIZATION => 'Colonization Technology',\n    TECH_ASTROTECH => 'Astrophysics Technology',\n    TECH_GRAVITON => 'Graviton Technology',\n    TECH_RESEARCH => 'Intergalactic Research Network',\n\n    UNIT_SHIPS               => 'Ships',\n    SHIP_SATTELITE_SOLAR     => 'Solar Satellite',\n    SHIP_SPY                 => 'Spy Probe',\n    SHIP_CARGO_SMALL         => 'Small Cargo',\n    SHIP_CARGO_BIG           => 'Large Cargo',\n    SHIP_CARGO_SUPER         => 'Super Cargo',\n    SHIP_CARGO_HYPER         => 'Hypercargo',\n    SHIP_RECYCLER            => 'Recycler',\n    SHIP_COLONIZER           => 'Colony Ship',\n    SHIP_SMALL_FIGHTER_LIGHT => 'Light Fighter',\n    SHIP_SMALL_FIGHTER_HEAVY => 'Heavy Fighter',\n    SHIP_MEDIUM_DESTROYER    => 'Destroyer',\n    SHIP_LARGE_CRUISER       => 'Cruiser',\n    SHIP_LARGE_BOMBER        => 'Bomber',\n    SHIP_LARGE_BATTLESHIP    => 'Battleship',\n    SHIP_LARGE_DESTRUCTOR    => 'Destructor',\n    SHIP_HUGE_DEATH_STAR     => 'Deathstar',\n    SHIP_HUGE_SUPERNOVA      => '&quot;Supernova&quot; Class Cruiser',\n\n    UNIT_DEFENCE                 => 'Defence',\n    UNIT_DEF_TURRET_MISSILE      => 'RoLa',\n    UNIT_DEF_TURRET_LASER_SMALL  => 'LaserL',\n    UNIT_DEF_TURRET_LASER_BIG    => 'LaserH',\n    UNIT_DEF_TURRET_GAUSS        => 'GausCa',\n    UNIT_DEF_TURRET_ION          => 'IonCa',\n    UNIT_DEF_TURRET_PLASMA       => 'Plasma',\n    UNIT_DEF_SHIELD_SMALL        => 'SmShD',\n    UNIT_DEF_SHIELD_BIG          => 'LaShD',\n    UNIT_DEF_SHIELD_PLANET       => 'PlaPro',\n    UNIT_DEF_MISSILE_INTERCEPTOR => 'IntMis',\n    UNIT_DEF_MISSILE_INTERPLANET => 'PlanMis',\n\n    UNIT_MERCENARIES => 'Mercenaries',\n    MRC_STOCKMAN => 'Cargo Master',\n    MRC_SPY => 'Spy',\n    MRC_ACADEMIC => 'Academician',\n//    MRC_DESTRUCTOR => 'Destroyer',\n    MRC_ADMIRAL => 'Admiral',\n    MRC_COORDINATOR => 'Coordinator',\n    MRC_NAVIGATOR => 'Navigator',\n//    MRC_ASSASIN => 'Assassin',\n\n    UNIT_GOVERNORS => 'Governors',\n    MRC_TECHNOLOGIST => 'Technologist',\n    MRC_ENGINEER => 'Engineer',\n    MRC_FORTIFIER => 'Fortifier',\n\n    UNIT_RESOURCES => 'Resources',\n    RES_METAL => 'Metal',\n    RES_CRYSTAL => 'Crystal',\n    RES_DEUTERIUM => 'Deuterium',\n    RES_ENERGY => 'Energy',\n    RES_DARK_MATTER => 'Dark Matter',\n\n    UNIT_ARTIFACTS => 'Artifacts',\n    ART_LHC => 'Large Hadron Collider',\n    ART_HOOK_SMALL => 'Small Hook',\n    ART_HOOK_MEDIUM => 'Medium Hook',\n    ART_HOOK_LARGE => 'Large Hook',\n    ART_RCD_SMALL => 'Small RCD',\n    ART_RCD_MEDIUM => 'Medium RCD',\n    ART_RCD_LARGE => 'Large RCD',\n    ART_HEURISTIC_CHIP => 'Heuristic chip',\n    ART_NANO_BUILDER   => 'Nanobuilder',\n\n    UNIT_PLANS => 'Schematics',\n    UNIT_PLAN_STRUC_MINE_FUSION => 'Schematic &quot;Thermonuclear plant&quot;',\n    UNIT_PLAN_SHIP_CARGO_SUPER => 'Schematic &quot;Supertransport&quot;',\n    UNIT_PLAN_SHIP_CARGO_HYPER => 'Schematic &quot;Hyperstransport&quot;',\n    UNIT_PLAN_SHIP_DEATH_STAR => 'Schematic &quot;Death Star&quot;',\n    UNIT_PLAN_SHIP_SUPERNOVA => 'Schematic &quot;Supernova class cruiser&quot;',\n    UNIT_PLAN_DEF_SHIELD_PLANET => 'Schematic &quot;Planet defense&quot;',\n\n    UNIT_PREMIUM => 'Premium',\n    UNIT_CAPTAIN => 'Captain',\n\n    UNIT_PLANET_DENSITY => 'Density',\n\n    UNIT_CAN_NOT_BE_BUILD => 'Unit can not be built by player',\n  ),\n\n  'tech_short' => array(\n//    UNIT_STRUCTURES => 'Buildings',\n//    STRUC_MINE_METAL => 'Metal mine',\n//    STRUC_MINE_CRYSTAL => 'Crystal Mine',\n//    STRUC_MINE_DEUTERIUM => 'Deuterium Synthesizer',\n//    STRUC_MINE_SOLAR => 'Solar Plant',\n//    STRUC_MINE_FUSION => 'Fusion Reactor',\n//    STRUC_FACTORY_ROBOT => 'Robotics Factory',\n//    STRUC_FACTORY_NANO => 'Nanite Factory',\n//    STRUC_FACTORY_HANGAR => 'Shipyard',\n//    STRUC_STORE_METAL => 'Metal Storage',\n//    STRUC_STORE_CRYSTAL => 'Crystal Storage',\n//    STRUC_STORE_DEUTERIUM => 'Deuterium Tank',\n//    STRUC_LABORATORY => 'Research Lab',\n//    STRUC_TERRAFORMER => 'Terraformer',\n//    STRUC_ALLY_DEPOSIT => 'Alliance Depot',\n//    STRUC_LABORATORY_NANO => 'Special Building',\n//\n//    UNIT_STRUCTURES_SPECIAL => 'Moon Buildings',\n//    STRUC_MOON_STATION => 'Moon Base',\n//    STRUC_MOON_PHALANX => 'Sensor Phalanx',\n//    STRUC_MOON_GATE => 'Stargate',\n//    STRUC_SILO => 'Missile Silo',\n//\n//    UNIT_TECHNOLOGIES => 'Technologies',\n//    TECH_ENERGY => 'Energy Technology',\n//    TECH_COMPUTER => 'Computer Technology',\n//    TECH_SPY => 'Espionage Technology',\n//    TECH_ARMOR => 'Armor Technology',\n//    TECH_WEAPON => 'Weapons Technology',\n//    TECH_SHIELD => 'Shielding Technology',\n//    TECH_ENGINE_CHEMICAL => 'Rocket Engine',\n//    TECH_ENGINE_ION => 'Impulse Engine',\n//    TECH_ENGINE_HYPER => 'Hyperspace Drive',\n//    TECH_LASER => 'Laser Technology',\n//    TECH_ION => 'Ion Technology',\n//    TECH_PLASMA => 'Plasma Technology',\n//    TECH_HYPERSPACE => 'Hyperspace Technology',\n//    TECH_EXPEDITION => 'Expedition Technology',\n//    TECH_COLONIZATION => 'Colonization Technology',\n//    TECH_ASTROTECH => 'Astrophysics Technology',\n//    TECH_GRAVITON => 'Graviton Technology',\n//    TECH_RESEARCH => 'Intergalactic Research Network',\n\n    UNIT_SHIPS               => 'Ships',\n    SHIP_SATTELITE_SOLAR     => 'SolSat',\n    SHIP_SPY                 => 'SpyProb',\n    SHIP_CARGO_SMALL         => 'SmCargo',\n    SHIP_CARGO_BIG           => 'LgCargo',\n    SHIP_CARGO_SUPER         => 'SuCargo',\n    SHIP_CARGO_HYPER         => 'HyCargo',\n    SHIP_RECYCLER            => 'Recycl',\n    SHIP_COLONIZER           => 'Colony',\n    SHIP_SMALL_FIGHTER_LIGHT => 'LtFight',\n    SHIP_SMALL_FIGHTER_HEAVY => 'HvFight',\n    SHIP_MEDIUM_DESTROYER    => 'Destr',\n    SHIP_LARGE_CRUISER       => 'Cruiser',\n    SHIP_LARGE_BOMBER        => 'Bomber',\n    SHIP_LARGE_BATTLESHIP    => 'BtlShip',\n    SHIP_LARGE_DESTRUCTOR    => 'Destr',\n    SHIP_HUGE_DEATH_STAR     => 'DthStar',\n    SHIP_HUGE_SUPERNOVA      => 'SN',\n\n//    UNIT_DEFENCE => 'Defences',\n//    UNIT_DEF_TURRET_MISSILE => 'Rocket Launcher',\n//    UNIT_DEF_TURRET_LASER_SMALL => 'Light Laser',\n//    UNIT_DEF_TURRET_LASER_BIG => 'Heavy Laser',\n//    UNIT_DEF_TURRET_GAUSS => 'Gauss Cannon',\n//    UNIT_DEF_TURRET_ION => 'Ion Cannon',\n//    UNIT_DEF_TURRET_PLASMA => 'Plasma Turrent',\n//    UNIT_DEF_SHIELD_SMALL => 'Small Shield Dome',\n//    UNIT_DEF_SHIELD_BIG => 'Large Shield Dome',\n//    UNIT_DEF_SHIELD_PLANET => 'Planetary Protection',\n//    UNIT_DEF_MISSILE_INTERCEPTOR => 'Interceptor Missiles',\n//    UNIT_DEF_MISSILE_INTERPLANET => 'Interplanetary Missiles',\n//\n//    UNIT_MERCENARIES => 'Mercenaries',\n//    MRC_STOCKMAN => 'Cargo Master',\n//    MRC_SPY => 'Spy',\n//    MRC_ACADEMIC => 'Academician',\n////    MRC_DESTRUCTOR => 'Destroyer',\n//    MRC_ADMIRAL => 'Admiral',\n//    MRC_COORDINATOR => 'Coordinator',\n//    MRC_NAVIGATOR => 'Navigator',\n////    MRC_ASSASIN => 'Assassin',\n//\n//    UNIT_GOVERNORS => 'Governors',\n//    MRC_TECHNOLOGIST => 'Technologist',\n//    MRC_ENGINEER => 'Engineer',\n//    MRC_FORTIFIER => 'Fortifier',\n//\n    UNIT_RESOURCES => 'Res',\n    RES_METAL => 'Met',\n    RES_CRYSTAL => 'Crys',\n    RES_DEUTERIUM => 'Deut',\n    RES_ENERGY => 'Ener',\n    RES_DARK_MATTER => 'DM',\n    RES_METAMATTER => 'MM',\n//\n//    UNIT_ARTIFACTS => 'Artifacts',\n//    ART_LHC => 'Large Hadron Collider',\n//    ART_HOOK_SMALL => 'Small Hook',\n//    ART_HOOK_MEDIUM => 'Medium Hook',\n//    ART_HOOK_LARGE => 'Large Hook',\n//    ART_RCD_SMALL => 'Small RCD',\n//    ART_RCD_MEDIUM => 'Medium RCD',\n//    ART_RCD_LARGE => 'Large RCD',\n//    ART_HEURISTIC_CHIP => 'Heuristic chip',\n//    ART_NANO_BUILDER   => 'Nanobuilder',\n//\n//    UNIT_PLANS => 'Schematics',\n//    UNIT_PLAN_STRUC_MINE_FUSION => 'Schematic &quot;Thermonuclear plant&quot;',\n//    UNIT_PLAN_SHIP_CARGO_SUPER => 'Schematic &quot;Supertransport&quot;',\n//    UNIT_PLAN_SHIP_CARGO_HYPER => 'Schematic &quot;Hyperstransport&quot;',\n//    UNIT_PLAN_SHIP_DEATH_STAR => 'Schematic &quot;Death Star&quot;',\n//    UNIT_PLAN_SHIP_SUPERNOVA => 'Schematic &quot;Supernova class cruiser&quot;',\n//    UNIT_PLAN_DEF_SHIELD_PLANET => 'Schematic &quot;Planet defense&quot;',\n//\n//    UNIT_PREMIUM => 'Premium',\n//    UNIT_CAPTAIN => 'Captain',\n//\n//    UNIT_PLANET_DENSITY => 'Density',\n\n    UNIT_CAN_NOT_BE_BUILD => 'Unit non-buildable',\n  ),\n\n));\n"
  },
  {
    "path": "language/en/universe.mo.php",
    "content": "<?php\n\n/*\n#############################################################################\n#  Filename: universe.mo\n#  Project: SuperNova.WS\n#  Website: http://www.supernova.ws\n#  Description: Massive Multiplayer Online Browser Space Strategy Game\n#\n#  Copyright © 2009-2018 Gorlum for Project \"SuperNova.WS\"\n#  Copyright © 2008 Aleksandar Spasojevic <spalekg@gmail.com>\n#  Copyright © 2005 - 2008 KGsystem\n#############################################################################\n*/\n\n/**\n*\n* @package language\n* @system [English]\n* @version 43a16.13\n*\n*/\n\n/**\n* DO NOT CHANGE\n*/\n\nif (!defined('INSIDE')) die();\n\n\n$a_lang_array = array(\n  'Galaxy' => 'Galaxy',\n  'Solar_system' => 'Solar System',\n  'Show' => 'Show',\n  'vacation_shortcut' => 'V',\n  'banned_shortcut' => 'B',\n  'active_shortcut' => '*',\n  'inactif_7_shortcut' => 'i',\n  'inactif_28_shortcut' => 'I',\n  'strong_player_shortcut' => 'S',\n  'weak_player_shortcut' => 'N',\n  'uni_protected_player_shortcut' => 'P',\n  'Legend' => 'Legend',\n  'Strong_player' => 'Strong Player',\n  'Weak_player' => 'Weak Player',\n  'Way_vacation' => 'Vacation Mode',\n  'Pendent_user' => 'Banned Player',\n  'Active' => 'Active Player',\n  'Inactive_7_days' => 'Inactive for 7 days',\n  'Inactive_28_days' => 'Inactive for 28 days',\n  'uni_protected_player' => 'Protected Players',\n  'uni_legend_myplanet' => 'My planet',\n  'uni_legend_allyplanet' => 'Alliance planet',\n  'Solar_system_at' => 'Solar System %g:%s',\n  'Activity' => 'Activity',\n  'Planet_info' => 'Planet Information',\n  'Moon_info' => 'Moon Information',\n  'Available' => 'Available',\n  'type1' => 'Planet',\n  'type3' => 'Moon',\n  'Pos' => 'Pos',\n  'Planet' => 'View',\n  'Name' => 'Name',\n  'Moon' => 'Moon',\n  'Debris' => 'Field Debris',\n  'caracters' => 'Characteristic',\n  'diameter' => 'Diameter',\n  'temperature' => 'Temperature',\n  'Place' => 'Place',\n  'unPlace' => '(Not Ranked)',\n  'State' => 'Status',\n  'Alliance' => 'Alliance',\n  'Actions' => 'Actions',\n  'aava' => 'Avatar',\n  'Player' => 'Player (status)',\n  'AllyInfoText' => 'Alliance %n on the spot %r contains %m Members',\n  'Sending_fleet' => 'Sending Fleet...',\n  'Sent_fleet' => 'Sent Fleet...',\n  'Obtaining_data' => 'Checking',\n  'Planet_info_tip' => 'For information about the planet, and to perform a variety of actions, move your mouse over the planet that interests you',\n  'an_error_has_happened_while_it_was_sent' => 'Error, has happened while it was sent',\n  'error_there_is_no_moon' => 'Error, there is no Moon',\n  'error_the_player_is_under_the_protection_of_beginners' => 'Error, the player is under protection',\n  'error_the_player_is_too_strong' => 'Error, the player is too strong',\n  'error_the_player_is_in_way_vacation' => 'Error, the player is in Vacation Mode',\n  'error_only_x_available_probes_sending' => 'Error, only available \"+retVals[1]+\" probes to send',\n  'error_there_are_no_available_probes_of_spying' => 'Error, No available probes for espionage',\n  'error_you_cannot_send_any_more_fleets' => 'Error, you cannot send any more fleets',\n  'error_you_do_not_have_sufficient_deuterium' => 'Error, not enough deuterium',\n  'There_is_not_planet' => 'There is no planet',\n  'error_there_is_no_sufficient_fuel' => 'Error, not enough fuel',\n  'multialarm' => 'Multi-Alarm',\n  'gm_all' => 'All',\n  'gm_launch' => 'Start a missle attack',\n  'gm_send' => 'Send',\n  'gm_target' => 'Target:',\n  'gal_mis_toLaunch' => 'Number of missles',\n  'gal_mis_rest' => 'Missles: ',\n  'gal_mis_launch' => 'Launch',\n  'gl_espionner' => 'Espionage',\n  'gl_mipattack' => 'Interplanetary Attack',\n  'gl_shortcut' => 'Add to shortcut',\n  'gl_with' => 'with',\n  'gl_membre' => 'Members',\n  'gl_ally_internal' => 'Alliance&nbsp;Info',\n  'gl_ally_web' => 'Alliance Site',\n  'gl_sendmess' => 'Send a message',\n  'gl_buddy' => 'Buddy list',\n  'gl_buddyreq' => 'Add as buddy',\n  'gl_stats' => 'Statistics',\n  'gl_planet' => 'Planet',\n  'gl_destroyedplanet' => 'Planet destroyed',\n  'gl_phalanx' => 'Phalanx',\n  'gl_ressource' => 'Resources',\n  'gl_action' => 'Mission',\n  'gs_c00' => 'Error, mission is unaccessible from Universe interface',\n  'gs_c01' => 'Error, planet was not found.',\n  'gs_c02' => 'Error, wrong coordinates in Universe',\n  'gs_c03' => 'Error, the player is too weak.',\n  'gs_c04' => 'Error, the player is too strong.',\n  'gs_c04k' => ' You do not have any spy probes!',\n  'gs_c04r' => ' You do not have processors!',\n  'gs_c05' => 'Error, player is in Vacation Mode',\n  'gs_c610' => 'Error, not enough spy probes',\n  'gs_c610a' => 'Bug, with ',\n  'gs_c610b' => 'Error, no espionage.',\n  'gs_c611' => 'Error, not enough ships.',\n  'gs_c612' => 'Error: not enough computer power.',\n  'gs_c613' => 'Error: not enough deterium',\n  'gs_c616' => 'Error, Alarm!',\n  'gs_c618' => 'Error, trying to attack yourself!',\n  'gs_c619' => 'Error',\n  'gs_c620' => 'Error, you leave!',\n  'gs_sending' => 'Sent',\n  'gs_to' => 'to coordinates',\n  'Sending' => 'Send',\n  'gal_planets' => 'Colonized Planets: ',\n  'gal_planetNone' => 'No Colonized Planets',\n  'gf_cntmone' => 'Sat-planet',\n  'gf_cntmnone' => 'No sat data planet.',\n  'gf_cntmsome' => 'Sat-planet.',\n  'gf_mi_title' => 'interplanetary missiles',\n  'gf_fleetslt' => 'Fleets',\n  'gf_rc_title' => 'Recycler',\n  'gf_sp_title' => 'espionage probes',\n  'gf_unknowsp' => 'Explore space - send expedition',\n  'phalanx_header' => 'Fleets in flight',\n  'phalanx_noflotes' => 'No fleets in flight',\n  'phalanx_nodeuterium' => 'Lack of Deuterium for Sensory phalanx.',\n  'phalanx_onlyformoons' => 'This development is only available for the Moon.',\n  'phalanx_nosensoravailable' => 'You do not have a Sensor Phalanx.',\n  'phalanx_rangeerror' => 'Lacks level Sensor Phalanx!',\n  'phalanx_planet_destroyed' => 'You can not scan destroyed planet!',\n  'phalanx_planet_not_exists' => 'This planet does not exists!',\n  'gal_sys_members' => 'Members:&nbsp;',\n  'gal_sys_hint' => 'Position the mouse pointer to the picture of the planet, moon, wrecks or the user name, the name of the Alliance that would get a popup menu with additional options',\n  'uni_debris' => 'Debris',\n  'uni_need' => 'Need',\n  'uni_flying' => 'Flying',\n  'uni_incoming_fleets' => 'Incoming Fleet',\n  'Planets_count' => '',\n\n  'uni_rename' => 'Rename',\n  'uni_name' => 'Name',\n  'uni_to_name' => 'Name it!',\n  'uni_naming' => 'Naming',\n  'uni_for' => 'for',\n  'uni_msg_error_wrong_galaxy' => 'Wrong galaxy number',\n  'uni_msg_error_wrong_system' => 'Wrong system number',\n  'uni_msg_error_low_price' => 'Too low naming price',\n  'uni_msg_error_no_dm' => 'Not enough Dark Matter for naming',\n  'uni_name_page_hint' => \"\n    <li>You can name galaxy or system with chosen name</li>\n    <li>Chosen name would be seen by all players on page \\\"Universe\\\"</li>\n    <li>Naming unnamed galaxy or system has it's base price which could be seen on page \\\"Universe Constants\\\"</li>\n    <li>When naming galaxy or system you can choose naming price. Naming price can't be less than proposed</li>\n    <li>Why you can choose to increase naming price? Next player that wish to rename galaxy or system after you should pay your naming price plus base price for galaxy or system. So paying higher price will protect (in some degree) galaxy or system from further renaming</li>\n  \",\n\n  'uni_galaxy_of' => 'galaxy',\n  'uni_system_of' => 'system',\n  'uni_msg_admin_rename' => 'Player ID %d [%s] for %d DM gives to %s at [%s%s] new name: %s',\n\n  'uni_colonize' => 'Send colonizer ship to make a Colony on position',\n\n  'uni_scan_start' => 'Start scanning mode',\n  'uni_scan_stop' => 'Exit scanning mode',\n\n);\n"
  },
  {
    "path": "language/es/admin.mo.php",
    "content": "<?php\n\n/*\n#############################################################################\n# Nombre del archivo: admin.mo\n# Proyecto: SuperNova.WS\n# Sitio web: http://www.supernova.ws\n# Descripción: Juego masivo multijugador en línea en el navegador, estrategia espacial\n#\n# Derechos de autor © 2009-2025 Gorlum para el proyecto \"SuperNova.WS\" + DeepSeec, GhatGpt,YandexGPT 5 Pro\n\n#############################################################################\n*/\n\n/**\n*\n* Paquete: idioma\n* Sistema: Español\n* Versión: 45a13\n*\n*/\n\n/**\n* NO CAMBIE\n*/\n\nif (!defined('INSIDE')) die();\n\n$a_lang_array = array(\n  'menu_admin_ally' => 'Alianzas',\n\n  'adm_tool_md5_header' => 'Generación y cifrado de contraseña (MD5)',\n  'adm_tool_md5_hash' => 'Hash MD5',\n  'adm_tool_md5_encode' => 'Cifrar',\n  'adm_tool_md5_generate' => 'Generar',\n\n  'adm_tool_sql_page_header' => 'Parámetros del servidor SQL',\n\n  'adm_tool_sql_server_version' => 'Versión del servidor',\n  'adm_tool_sql_client_version' => 'Versión de la biblioteca',\n  'adm_tool_sql_host_info' => 'Método de conexión',\n\n  'adm_confirm_do' => 'Confirmar',\n\n  'adm_tool_sql_table' => array(\n    'server' => array(\n      'TABLE_HEADER' => 'Servidor SQL',\n      'COLUMN_NAME_1' => 'Parámetro',\n      'COLUMN_NAME_2' => 'Valor',\n      // 'TABLE_FOOTER' => '',\n      // 'TABLE_EMPTY' => '',\n    ),\n\n    'status' => array(\n      'TABLE_HEADER' => 'Estado del servidor SQL',\n      'COLUMN_NAME_1' => 'Parámetro',\n      'COLUMN_NAME_2' => 'Valor',\n      // 'TABLE_FOOTER' => '',\n    ),\n\n    'params' => array(\n      'TABLE_HEADER' => 'Configuración del servidor SQL',\n      'COLUMN_NAME_1' => 'Parámetro',\n      'COLUMN_NAME_2' => 'Valor',\n      // 'TABLE_FOOTER' => '',\n    ),\n  ),\n\n  'adm_pl_image' => 'Imagen del planeta',\n  'adm_pl_fields_max' => 'Máximo de sectores',\n  'adm_pl_temp_min' => 'Temperatura mínima',\n  'adm_pl_temp_max' => 'Temperatura máxima',\n  'adm_pl_fields_busy' => 'Sectores ocupados',\n  'adm_pl_governor' => 'Gobernador',\n  'adm_pl_debris_metal' => 'Escombros, metal',\n  'adm_pl_debris_crystal' => 'Escombros, cristal',\n\n  'adm_opt_user_settings' => 'Configuraciones de los jugadores',\n  'adm_opt_user_birthday_gift' => 'Regalo al jugador por su cumpleaños',\n  'adm_opt_user_birthday_gift_disable' => '0 - deshabilitar regalos',\n  'adm_opt_user_birthday_range' => 'Rango retroactivo de cumpleaños, en días',\n  'adm_opt_user_birthday_range_hint' => 'Qué tan lejos en el pasado puede estar el cumpleaños para que el jugador reciba un regalo. Obviamente, no tiene sentido práctico establecer este valor más de 364 días.',\n\n  'adm_ul_title' => 'Lista de jugadores',\n  'adm_ul_title_online' => 'Jugadores en línea',\n  'adm_ul_time_registered' => 'Fecha de registro',\n  'adm_ul_time_played' => 'Último inicio de sesión',\n  'adm_ul_time_banned' => 'Término de bloqueo',\n  'adm_ul_delete_confirm' => 'Confirme la eliminación del usuario',\n  'adm_ul_referral' => 'Recomendaciones',\n  'adm_ul_players' => 'Jugadores',\n  'adm_ul_dms' => 'TM',\n  'adm_sys_actions' => 'Acciones',\n  'adm_sys_write_message' => 'Escribir un mensaje privado',\n  'adm_sys_delete_user' => 'Eliminar jugador',\n  'adm_done' => 'Se ha completado con éxito',\n  'adm_inactive_removed' => '<li>Se eliminaron registros inactivos de jugadores: %d</li>',\n  'adm_stat_title' => 'Actualización de estadísticas',\n  'adm_maintenance_title' => 'Mantenimiento de la BD',\n  'adm_records' => 'registros procesados',\n  'adm_cleaner_title' => 'Limpieza de la cola de construcciones',\n  'adm_cleaned' => 'Cantidad de tareas eliminadas: ',\n  'adm_schedule_none' => 'No hay tareas en el horario actual',\n\n\n\n  'Fix' => 'Actualizado',\n  'Welcome_to_Fix_section' => 'sección de patches',\n  'There_is_not_need_fix' => '¡No se necesita fix!',\n  'Fix_welldone' => 'Hecho!',\n  'adm_ov_title' => 'Resumen',\n  'adm_ov_infos' => 'Información',\n  'adm_ov_yourv' => 'Versión actual',\n  'adm_ov_lastv' => 'Versión disponible',\n  'adm_ov_here' => 'aquí',\n  'adm_ov_onlin' => 'En línea',\n  'adm_ov_ally' => 'Alianza',\n  'adm_ov_point' => 'Puntos',\n  'adm_ov_activ' => 'Activo',\n  'adm_ov_count' => 'Jugadores en línea',\n  'adm_ov_wrtpm' => 'Escribir en MP',\n  'adm_ov_altpm' => '[MP]',\n  'adm_ov_hint' => '<ul><li>La tabla de usuarios en línea puede ser ordenada por las columnas \"ID\", \"Nombre del jugador\", \"Alianza\", \"Puntos\" y \"Actividad\". Para ordenar por una columna específica, haga clic en su encabezado</li></ul>',\n\n\n\n\n  'adm_ul_ttle2' => 'Jugadores listados',\n  'adm_ul_id' => 'ID',\n  'adm_ul_name' => 'Nombre del jugador',\n  'adm_ul_mail' => 'Correo electrónico',\n  'adm_ul_adip' => 'IP',\n  'adm_ul_regd' => 'Registrado desde',\n  'adm_ul_lconn' => 'Último inicio de sesión',\n  'adm_ul_bana' => 'Ban',\n  'adm_ul_detai' => 'Detalles',\n  'adm_ul_actio' => 'Acciones',\n  'adm_ul_playe' => 'jugadores',\n  'adm_ul_yes' => 'Sí',\n  'adm_ul_no' => 'No',\n  'adm_pl_title' => 'Planetas activos',\n  'adm_pl_activ' => 'Planetas activos',\n  'adm_pl_name' => 'Nombre del planeta',\n  'adm_pl_posit' => 'Coordenadas',\n  'adm_pl_point' => 'Valor',\n  'adm_pl_since' => 'Activo',\n  'adm_pl_they' => 'Total',\n  'adm_pl_apla' => 'planeta(s)',\n  'adm_am_plid' => 'ID del planeta',\n  'adm_am_done' => 'La adición se completó con éxito',\n  'adm_am_ttle' => 'Añadir recursos',\n  'adm_am_add' => 'Confirmar',\n  'adm_am_form' => 'Formulario de adición de recursos',\n  'adm_ban_title' => 'Bloquear jugador',\n  'adm_bn_plto' => 'Bloquear jugador',\n  'adm_bn_name' => 'Nombre del jugador',\n  'adm_bn_reas' => 'Razón del bloqueo',\n  'adm_bn_isvc' => 'Con modo de vacaciones',\n  'adm_bn_time' => 'Duración del bloqueo',\n  'adm_bn_days' => 'Días',\n  'adm_bn_hour' => 'Horas',\n  'adm_bn_mins' => 'Minutos',\n  'adm_bn_secs' => 'Segundos',\n  'adm_bn_bnbt' => 'Bloquear',\n  'adm_bn_thpl' => 'Jugador',\n  'adm_bn_isbn' => 'bloqueado con éxito!',\n  'adm_bn_vctn' => 'Modo de vacaciones activado.',\n  'adm_bn_errr' => 'Error al bloquear al jugador! Es posible que el apodo %s no se encuentre.',\n  'adm_bn_err2' => 'Error al desactivar la producción en los planetas!',\n  'adm_bn_plnt' => 'Producción en los planetas desactivada.',\n  'adm_ban_msg_issued_date' => 'bloqueó al jugador',\n  'adm_unbn_ttle' => 'Desbloqueo',\n  'adm_unbn_plto' => 'Desbloquear jugador',\n  'adm_unbn_name' => 'Nombre',\n  'adm_unbn_bnbt' => 'Desbloquear',\n  'adm_unbn_thpl' => 'Jugador',\n  'adm_unbn_isbn' => 'desbloqueado!',\n  'adm_rz_ttle' => 'Reinicio de la universo',\n  'adm_rz_done' => 'Usuario(s) de transferencia(s)',\n  'adm_rz_conf' => 'Confirmación',\n  'adm_rz_text' => 'Al presionar el botón (reiniciar) eliminará todos los datos de la base de datos. ¿Hizo una copia de seguridad??? ¡Los cuentas no serán eliminadas!..',\n  'adm_rz_doit' => 'Reiniciar',\n  'adm_ch_ttle' => 'Administración del chat',\n  'adm_ch_list' => 'Lista de mensajes',\n  'adm_ch_clear' => 'Borrar',\n  'adm_ch_idmsg' => 'ID',\n  'adm_ch_delet' => 'eliminar',\n  'adm_ch_play' => 'Jugador',\n  'adm_ch_time' => 'Fecha',\n  'adm_ch_chat' => 'Réplica',\n  'adm_ch_nbs' => 'mensajes en total...',\n  'adm_er_ttle' => 'Entradas del sistema de registros',\n  'adm_er_clear' => 'Borrar',\n  'adm_er_idmsg' => 'ID',\n  'adm_er_type' => '[Código] Título',\n  'adm_er_play' => 'Jugador',\n  'adm_er_time' => 'Fecha',\n  'adm_er_page' => 'Dirección de la página',\n  'adm_er_nbs' => 'Entradas en el registro:',\n  'adm_er_text' => 'Entrada del registro',\n  'adm_er_bktr' => 'Información de depuración',\n  'adm_dm_title' => 'Cambio de la cantidad de Materia Oscura',\n  'adm_dm_planet' => 'ID, coordenadas o nombre del planeta',\n  'adm_dm_oruser' => 'O',\n  'adm_dm_user' => 'ID o nombre del jugador de la lista de jugadores',\n  'adm_or_caption' => 'O',\n  'adm_dm_no_quant' => 'Indique la cantidad de TM (positiva - para acreditación, negativa - para descontar)',\n  'adm_dm_no_dest' => 'Indique el ID o el nombre del jugador para cambiar TM',\n  'adm_dm_add_err' => 'Parece que hubo un error al acreditar TM',\n  'adm_dm_user_none' => 'Error: no se encontró jugador con ID o nombre \"%s\"',\n  'adm_dm_user_added' => 'La cantidad de TM del jugador [%2$d] \"%1$s\" se cambió exitosamente a %3$s TM',\n  'adm_dm_user_conflict' => 'Error: parece que en la BD hay un jugador tanto con ese nombre como con ese ID',\n  'adm_dm_planet_none' => 'Error al buscar el planeta: no se encontró planeta con ID, coordenadas o nombre %s',\n  'adm_dm_planet_added' => 'La cantidad de TM del jugador ID %1$d (dueño del planeta %4$s %2$s ID %3$d) se cambió exitosamente a %5$d TM.',\n  'adm_dm_planet_conflict' => 'Datos no únicos para buscar el planeta.<br>Esto significa que en la BD existen al mismo tiempo',\n  'adm_dm_planet_conflict_id' => 'un planeta con nombre \"%1$s\" y un planeta con ID %1$s .<br>Intente utilizar las coordenadas del planeta.',\n  'adm_dm_planet_conflict_name' => 'varios planetas con nombre \"%1$s\".<br>Intente utilizar coordenadas o ID del planeta.',\n  'adm_dm_planet_conflict_coords' => 'un planeta con nombre \"%1$s\" y un planeta con coordenadas %1$s.<br>Intente utilizar el ID del planeta.',\n  'adm_apply' => 'Aplicar',\n  'adm_maint' => 'Mantenimiento',\n  'adm_backup' => 'Copia de seguridad',\n  'adm_tools' => 'Herramientas',\n  'adm_tools_reloadConfig' => 'Recalcular la configuración',\n  'adm_reason' => 'Razón',\n  'adm_opt_title' => 'Configuración del Universo',\n  'adm_opt_game_settings' => 'Parámetros del juego',\n'adm_opt_game_name' => 'Nombre del Universo',\n'adm_opt_multiaccount_enabled' => 'Permitir la interacción de cuentas desde 1 IP',\n'adm_opt_speed' => 'Velocidad',\n'adm_opt_game_gspeed' => 'Juegos',\n'adm_opt_game_fspeed' => 'Flotas',\n'adm_opt_game_pspeed' => 'Recolección de recursos',\n'adm_opt_colonies_not_counted' => '(sin contar la Capital)',\n'adm_opt_colonies_no_restrictions' => '(-1 - sin restricciones)',\n'adm_opt_game_speed_normal' => '(1&nbsp;-&nbsp;normal)',\n'adm_opt_game_faq' => 'Enlace a Preguntas Frecuentes',\n'adm_opt_game_forum' => 'Dirección del foro',\n'adm_opt_game_metamatter' => 'Enlace \"Comprar Metamateria\"',\n'adm_opt_game_copyrigh' => 'Copyright',\n'adm_opt_game_online' => 'Desactivar el juego. Los usuarios verán el siguiente mensaje:',\n'adm_opt_game_offreaso' => 'Mensaje',\n'adm_opt_plan_settings' => 'Parámetros de los planetas',\n'adm_opt_plan_initial' => 'Tamaño del planeta inicial',\n'adm_opt_plan_base_inc' => 'Producción base',\n'adm_opt_game_debugmod' => 'Activar modo de depuración',\n'adm_opt_geoip_whois_url' => 'URL del servicio WHOIS',\n'adm_opt_geoip_whois_url_example' => '(por ejemplo \"http://1whois.ru/?ip=\")',\n'adm_opt_game_counter' => 'Activar contador de visitas',\n'adm_opt_game_oth_info' => 'Otros parámetros',\n'adm_opt_int_news_count' => 'Número de noticias',\n'adm_opt_int_page_imperor' => 'En la página \"Emperador\"',\n'adm_opt_game_zero_disable' => '(0&nbsp;-&nbsp;desactivar)',\n'adm_opt_game_advertise' => 'Bloques de publicidad',\n'adm_opt_game_oth_adds' => 'Activar bloque de publicidad en el menú izquierdo. Código del banner:',\n'adm_opt_game_oth_gala' => 'Galaxia',\n'adm_opt_game_oth_syst' => 'Sistema',\n'adm_opt_game_oth_plan' => 'Planeta',\n'adm_opt_btn_save' => 'Guardar',\n'adm_opt_vacation_mode' => 'Desactivar modo de vacaciones',\n'adm_opt_sectors' => 'sectores',\n'adm_opt_per_hour' => 'por hora',\n'adm_opt_saved' => 'Las configuraciones del juego se han guardado correctamente',\n'adm_opt_players_online' => 'Jugadores en el servidor',\n'adm_opt_vacation_mode_is' => 'Modo de vacaciones',\n'adm_opt_game_status' => 'Estado del juego',\n'adm_opt_links' => 'Enlaces y banners',\n'adm_opt_universe_size' => 'Tamaño del Universo',\n'adm_opt_galaxies' => 'Galaxias',\n'adm_opt_systems' => 'Sistemas',\n'adm_opt_planets' => 'Planetas',\n'adm_opt_build_on_research' => 'Construir laboratorio durante la investigación',\n'adm_opt_eco_scale_storage' => 'Escalado de almacenes según la velocidad de recolección',\n'adm_opt_game_rules' => 'Enlace a las reglas',\n'adm_opt_max_colonies' => 'Número de colonias',\n'adm_opt_exchange' => 'Tasa de cambio de recursos',\n'adm_opt_game_mode' => 'Tipo de Universo',\n'adm_opt_chat' => 'Configuración del chat',\n'adm_opt_chat_timeout' => 'Tiempo de espera por inactividad',\n'adm_opt_allow_buffing' => 'Permitir el buffing',\n'adm_opt_ally_help_weak' => 'Permitir el mantenimiento en un aliado débil',\n'adm_opt_email_pm' => 'Permitir el envío de MP a correo electrónico',\n'adm_opt_player_defaults' => 'Configuración predeterminada del jugador',\n'adm_opt_game_default_language' => 'Idioma de la interfaz',\n'adm_opt_game_default_skin' => 'Diseño/Piel',\n'adm_opt_game_default_template' => 'Plantilla',\n'adm_opt_player_change_name' => 'Cambio de nombre del jugador',\n'adm_opt_player_change_name_options' => [\n    SERVER_PLAYER_NAME_CHANGE_NONE => 'El cambio de nombre está prohibido',\n    SERVER_PLAYER_NAME_CHANGE_FREE => 'Cambio de nombre gratuito',\n    SERVER_PLAYER_NAME_CHANGE_PAY => 'Cambio de nombre por TM',\n],\n'adm_opt_player_change_name_cost' => 'Costo en TM por cambio de nombre',\n'adm_opt_empire_mercenary_temporary' => 'Mercenarios temporales',\n'adm_opt_empire_mercenary_temporary_base' => 'Tiempo base para el reclutamiento, en segundos',\n'adm_opt_empire_mercenary_temporary_hint' => 'Al activar la opción, todos los mercenarios se convertirán en temporales con un plazo base. Al desactivar la opción, todos los mercenarios se convertirán en permanentes. Los mercenarios reclutados que no cumplan los requisitos de reclutamiento no podrán ser actualizados, pero seguirán activos.',\n'adm_opt_experimental' => 'OPCIONES EXPERIMENTALES! USAR CON CUIDADO!',\n'adm_opt_tpl_minifier' => 'Minificador de plantillas',\n'adm_opt_tpl_minifier_hint' => 'El minificador comprimirá las plantillas, reemplazando varios caracteres \"vacíos\" consecutivos (salto de línea, tabulación, espacio) por un solo espacio. Para más información sobre el funcionamiento del minificador, consulte /docs/changelog.txt',\n'adm_lm_compensate' => 'Compensar',\n'adm_pl_comp_title' => 'Compensación del planeta destruido',\n'adm_pl_comp_src' => 'Destruir el planeta',\n'adm_pl_comp_dst' => 'Acreditar recursos al planeta',\n'adm_pl_comp_bonus' => 'Bonificación del jugador',\n'adm_pl_comp_check' => 'Comprobar',\n'adm_pl_comp_confirm' => 'Confirmar',\n'adm_pl_comp_done' => 'Listo',\n'adm_pl_comp_price' => 'Costo de las construcciones',\n'adm_pl_comp_got' => 'Se acreditará',\n'adm_pl_com_of_plr' => 'del jugador',\n'adm_pl_comp_will_be' => 'será',\n'adm_pl_comp_destr' => 'destruido.',\n'adm_pl_comp_recieve' => 'La cantidad de recursos indicada',\n'adm_pl_comp_recieve2' => 'acreditada al planeta',\n'adm_pl_comp_err_0' => 'No se encontró el planeta a destruir',\n'adm_pl_comp_err_1' => 'El planeta ya ha sido destruido',\n  'adm_pl_comp_err_2' => 'No se encontró planeta para acreditar recursos',\n  'adm_pl_comp_err_3' => 'Los planetas especificados tienen dueños diferentes. Solo se pueden acreditar recursos en un planeta del mismo jugador',\n  'adm_pl_comp_err_4' => 'El planeta no pertenece al jugador especificado',\n  'adm_pl_comp_err_5' => 'Los planetas para destruir y acreditar recursos coinciden',\n  'adm_ver_versions' => 'Versiones de componentes del servidor',\n  'adm_ver_version_sn' => 'Versión del motor',\n  'adm_ver_version_db' => 'Versiones de la base de datos',\n  'adm_update_force' => 'Forzar actualización desde cero',\n  'adm_update_repeat' => 'Repetir actualización anterior',\n  'adm_ptl_test' => 'Prueba de phpBB Template Engine',\n  'adm_counter_recalc' => 'Recalcular tabla `counter`',\n  'adm_lm_planet_edit' => 'Editar',\n  'adm_planet_edit' => 'Edición de planeta',\n  'adm_planet_id' => 'ID del planeta',\n  'adm_name' => 'Nombre',\n  'adm_planet_change' => 'Cambio',\n  'adm_planet_parent' => 'Planeta principal',\n  'adm_planet_active' => 'Planetas activos',\n  'adm_planet_edit_hint' => '<ul>    <li>Si introduces un ID de planeta en una página vacía y haces clic en \"Confirmar\", el motor intentará mostrar información sobre ese planeta: tipo, nombre y coordenadas, así como la cantidad actual de unidades/recursos del tipo seleccionado en el planeta</li>    <li>Para eliminar una cierta cantidad de unidades/recursos del planeta, introduce un número negativo</li>  </ul>',\n  'adm_planet_list_title' => 'Lista de planetas',\n  'adm_sys_owner' => 'Dueño',\n  'adm_sys_owner_id' => 'ID del dueño',\n  'addm_title' => 'Añadir luna',\n  'addm_addform' => 'Formulario de nueva luna',\n  'addm_playerid' => 'ID del planeta anfitrión',\n  'addm_moonname' => 'Nombre de la luna',\n  'addm_moongala' => 'Especifica galaxia',\n  'addm_moonsyst' => 'Especifica sistema',\n  'addm_moonplan' => 'Especifica posición',\n  'addm_moondoit' => 'Añadir',\n  'addm_done' => 'Luna creada',\n  'adm_usr_level' => array(\n    '0' => 'Jugador',\n    '1' => 'Operador',\n    '2' => 'Moderador',\n    '3' => 'Administrador',\n  ),\n\n  'adm_usr_genre' => array(\n    GENDER_UNKNOWN => 'No especificado',\n    GENDER_MALE => 'Hombre',\n    GENDER_FEMALE => 'Mujer',\n  ),\n\n  'panel_mainttl' => 'Panel de administración',\n  'adm_panel_mnu' => 'Buscar jugador',\n  'adm_panel_ttl' => 'Tipo de búsqueda',\n  'adm_search_pl' => 'Buscar por nombre',\n  'adm_search_ip' => 'Buscar por IP',\n  'adm_stat_play' => 'Estadísticas del jugador',\n  'adm_mod_level' => 'Nivel de acceso',\n  'adm_player_nm' => 'Nombre del jugador',\n  'adm_ip' => 'IP',\n  'adm_plyer_wip' => 'Jugadores con IP',\n  'adm_frm1_id' => 'ID',\n  'adm_frm1_name' => 'Nombre',\n  'adm_frm1_ip' => 'IP',\n  'adm_frm1_mail' => 'e-Mail',\n  'adm_frm1_acc' => 'Rango',\n  'adm_frm1_gen' => 'Género',\n  'adm_frm1_main' => 'ID del planeta',\n  'adm_frm1_gpos' => 'Coordenadas',\n  'adm_mess_lvl1' => 'Nivel de acceso',\n  'adm_mess_lvl2' => '\"ahora\" ',\n  'adm_colony' => 'Colonias',\n  'adm_planet' => 'Planeta',\n  'adm_moon' => 'Luna',\n  'adm_technos' => 'Tecnologías',\n  'adm_bt_search' => 'Buscar',\n  'adm_bt_change' => 'Cambiar',\n  'flt_id' => 'ID',\n  'flt_fleet' => 'Flota',\n  'flt_ships' => 'Composición',\n  'flt_mission' => 'Misión',\n  'flt_here' => 'Regreso',\n  'flt_there' => 'Ida',\n  'flt_here_there' => 'Ida/Regreso',\n  'flt_departure' => 'Punto de partida',\n  'flt_owner' => 'Dueño',\n  'flt_planet' => 'Planeta',\n  'flt_time_return' => 'Regreso',\n  'flt_e_owner' => 'Destino',\n  'flt_time_arrive' => 'Llegada',\n  'flt_staying' => 'Tiempo de espera',\n  'flt_action' => 'Acción',\n  'flt_title' => 'Flotas en vuelo',\n  'flt_no_fleet' => 'Actualmente no hay flotas en vuelo',\n  'mlst_title' => 'Lista de mensajes',\n  'mlst_mess_del' => 'Eliminación de mensajes',\n  'mlst_hdr_page' => 'Pág.',\n  'mlst_hdr_title' => ' ) mensajes :',\n  'mlst_hdr_prev' => '[ &lt;- ]',\n  'mlst_hdr_next' => '[ -&gt; ]',\n  'mlst_hdr_id' => 'ID',\n  'mlst_hdr_type' => 'Tipo de mensajes',\n  'mlst_hdr_time' => 'Hora de envío',\n  'mlst_hdr_from' => 'De',\n  'mlst_hdr_to' => 'Para',\n  'mlst_hdr_text' => 'Contenido',\n  'mlst_hdr_action' => 'Marcar',\n  'mlst_del_mess' => 'Eliminar',\n  'mlst_bt_delsel' => 'Eliminar mensajes seleccionados',\n  'mlst_bt_deldate' => 'Eliminar',\n  'mlst_hdr_delfrom' => 'Eliminar mensajes de este tipo anteriores a',\n  'mlst_no_messages' => 'No hay mensajes',\n  'mlst_messages_deleted' => 'Mensajes con ID %s eliminados',\n  'mlst_messages_deleted_date' => 'Mensajes tipo \"%s\" hasta %s eliminados (sin incluir mensajes en la fecha especificada)',\n\n  'adm_lng_title' => 'Localización',\n  'adm_lng_warning' => '¡ADVERTENCIA! ¡Esta es una versión alpha del editor de localizaciones! ¡Úsalo bajo tu propio riesgo!',\n  'adm_lng_domain' => 'Dominio',\n  'adm_lng_string_name' => 'Nombre de la cadena',\n  'adm_lng_string_add' => 'Añadir cadena',\n  'adm_uni_price_galaxy' => 'Costo base para renombrar galaxia',\n  'adm_uni_price_system' => 'Costo base para renombrar sistema',\n\n  'adm_opt_ver_check' => 'Verificación de versión',\n  'adm_opt_ver_check_hint' => 'En cualquier tipo de verificación de versión solo se envían datos anónimos: versión actual de la BD, número de release y versión del juego. Puedes verificar la versión \"manualmente\" haciendo clic en \"Verificar versión\".',\n  'adm_opt_ver_check_do' => 'Verificar versión',\n  'adm_opt_ver_check_last' => 'Última verificación de versión realizada',\n  'adm_opt_ver_check_auto' => 'Verificación automática de versión',\n  'adm_opt_ver_check_auto_hint' => 'Puedes activar la verificación automática de versión del juego. La verificación se realizará automáticamente cada cierto tiempo (por defecto, una vez al día). Más detalles en la documentación',\n\n  'adm_opt_ver_response' => array(\n    SNC_VER_NEVER => 'No se ha realizado verificación de versión',\n\n    SNC_VER_ERROR_CONNECT => 'Error de verificación de versión. El juego no pudo contactar con el servidor de actualizaciones. Asegúrate de que tienes instalado y activado CURL en PHP o que en la configuración de PHP está permitido el acceso a servidores remotos',\n    SNC_VER_ERROR_SERVER => 'Error del servidor de actualizaciones. Comprueba si ha salido una versión más nueva del motor con mejor soporte para el servidor de actualizaciones. En caso contrario, ¡notifica urgentemente al desarrollador!',\n\n    SNC_VER_EXACT => 'Tienes instalada la última versión alpha del próximo release. ¡Gracias por participar en las pruebas!',\n    SNC_VER_LESS => 'Estás usando una versión alpha del próximo release. ¡Pero ya hay una alpha más nueva! Actualiza si quieres obtener correcciones de errores y probar nuevas características.',\n    SNC_VER_FUTURE => '¡Tienes una versión del juego del futuro! ¡Contacta urgentemente con el desarrollador y pásale esta versión! También prepárate para la visita de la Milicia Temporal por violación del continuo espacio-temporal...',\n\n    SNC_VER_RELEASE_EXACT => 'Tienes la versión más reciente del último release del juego',\n    SNC_VER_RELEASE_MINOR => 'Tienes una versión desactualizada del juego - ya hay una actualización del release actual. Probablemente corrige algunos errores de tu versión. Se recomienda actualizar.',\n    SNC_VER_RELEASE_MAJOR => 'Tienes una versión muy desactualizada - ya ha salido un nuevo release. Corrección de errores, nuevas características - ¡debes actualizar!',\n    SNC_VER_RELEASE_ALPHA => 'Tienes la versión más reciente del release del juego. Pero ya hay una alpha del próximo release. ¿Quizás quieras participar en sus pruebas?',\n\n    SNC_VER_MAINTENANCE => 'El servidor de actualizaciones está en mantenimiento. Inténtalo más tarde',\n    SNC_VER_UNKNOWN_RESPONSE => 'El servidor de actualizaciones devolvió una respuesta desconocida. Probablemente significa que hay una versión más nueva del motor con capacidades más avanzadas de actualización',\n    SNC_VER_INVALID => 'No puedo entender qué versión tan extraña tienes. Contacta con el desarrollador para diagnosticar el problema.',\n    SNC_VER_STRANGE => 'No deberías ver este mensaje. Si lo ves, algo ha ido mal. Contacta con el desarrollador para diagnosticar el problema.',\n\n    SNC_VER_REGISTER_UNREGISTERED => 'Tu servidor no está registrado',\n    SNC_VER_REGISTER_ERROR_MULTISERVER => 'Error - ¡tu servidor está registrado múltiples veces! Contacta con el desarrollador para diagnosticar el problema.',\n    SNC_VER_REGISTER_ERROR_REGISTERED => 'Error - ¡tu servidor ya está registrado! Comprueba que la clave única y el identificador en la configuración del servidor son correctos.',\n    SNC_VER_REGISTER_ERROR_NO_NAME => 'Error - ¡falta el nombre del servidor! Debes asignar un nombre al servidor.',\n    SNC_VER_REGISTER_ERROR_WRONG_URL => 'Error - ¡URL incorrecta! La cadena proporcionada no es una URL válida. Quizás intentaste registrar un servidor ejecutándose en localhost - el servidor de actualizaciones no trabaja con esos servidores.',\n    SNC_VER_REGISTER_REGISTERED => 'Tu sitio se ha registrado con éxito',\n\n    SNC_VER_ERROR_INCOMPLETE_REQUEST => 'Error - ¡clave o ID de sitio incorrectos! Comprueba que la clave y el ID en la configuración del servidor son correctos.',\n    SNC_VER_ERROR_UNKNOWN_KEY => 'Error - ¡clave desconocida! La clave proporcionada no se encuentra en la BD del servidor de actualizaciones. Comprueba que la clave en la configuración del servidor es correcta.',\n    SNC_VER_ERROR_MISSMATCH_KEY_ID => 'Error - ¡la clave proporcionada no coincide con el ID proporcionado! Comprueba que la clave y el ID en la configuración del servidor son correctos.',\n  ),\n\n  'adm_opt_ver_response_short' => array(\n    SNC_VER_NEVER => 'No realizada',\n\n    SNC_VER_ERROR_CONNECT => 'Error de conexión',\n    SNC_VER_ERROR_SERVER => 'Error del servidor',\n\n    SNC_VER_EXACT => 'Última alpha',\n    SNC_VER_LESS => 'Alpha antigua',\n    SNC_VER_FUTURE => 'Alpha del futuro',\n\n    SNC_VER_RELEASE_EXACT => 'Versión actualizada',\n    SNC_VER_RELEASE_MINOR => 'Actualización recomendada',\n    SNC_VER_RELEASE_MAJOR => 'Actualización necesaria',\n    SNC_VER_RELEASE_ALPHA => 'Release actualizado',\n\n    SNC_VER_MAINTENANCE => 'Mantenimiento',\n    SNC_VER_UNKNOWN_RESPONSE => 'Respuesta desconocida',\n    SNC_VER_INVALID => 'Error de versión',\n    SNC_VER_STRANGE => 'Problema inesperado',\n\n    SNC_VER_REGISTER_UNREGISTERED => 'No registrado',\n    SNC_VER_REGISTER_ERROR_MULTISERVER => 'Multi-registro',\n    SNC_VER_REGISTER_ERROR_REGISTERED => 'Error de clave',\n    SNC_VER_REGISTER_ERROR_NO_NAME => 'Error de nombre',\n    SNC_VER_REGISTER_REGISTERED => 'Registrado',\n\n    SNC_VER_ERROR_INCOMPLETE_REQUEST => 'Error de clave o ID',\n    SNC_VER_ERROR_UNKNOWN_KEY => 'Clave desconocida',\n    SNC_VER_ERROR_MISSMATCH_KEY_ID => 'La clave no coincide con el ID',\n  ),\n\n  'adm_upd_register' => 'Registro del servidor',\n\n  'adm_upd_register_hint' => '\n    El registro del servidor es necesario para ciertas solicitudes al servidor de actualizaciones. Durante el registro se envía la mínima información necesaria para identificar el servidor:\n    <ul>\n      <li>URL completo del servidor - es decir, dirección HTTP y subdirectorio del servidor. Ejemplo: http://miservidor.com/micarpeta/. Esto es necesario para la identificación primaria del servidor. La ruta completa es necesaria para distinguir múltiples copias de SuperNova instaladas en el mismo IP o dominio.</li>\n      <li>Nombre interno del servidor. Se usa para incluir en mensajes.</li>\n    </ul>\n    ¿Por qué registrar tu servidor? En el futuro se planean varias funciones que solo estarán disponibles para servidores registrados. Entre ellas (ordenadas por plazo estimado de implementación):\n    <ul>\n      <li>Obtención automática del registro de cambios</li>\n      <li>Actualización automatizada del motor</li>\n      <li>Participación en el ranking de servidores</li>\n      <li>Reportes de errores de administradores</li>\n      <li>Chat para administradores de servidores</li>\n      <li>Por solicitud - diagnóstico remoto del servidor</li>\n      <li>...y mucho más</li>\n    </ul>\n    ¿Por qué registrar tu servidor ahora?\n    <ul>\n      <li>Las solicitudes de servidores registrados tienen mayor prioridad en diagnóstico de problemas y procesamiento de reportes.</li>\n      <li>Durante el registro, además de una clave única, el servidor recibe un número de identificación único que se usará para ordenar servidores. Cuanto antes registres tu servidor, más arriba aparecerá en el listado general...</li>\n    </ul>\n  ',\n\n  'adm_upd_register_do' => 'Registrar servidor',\n  'adm_upd_register_already' => 'Ya estás registrado en el servidor de actualizaciones. ¡Asegúrate de guardar el ID y clave única de tu servidor!',\n  'adm_upd_register_id' => 'Número de registro',\n  'adm_upd_register_key' => 'Clave de registro',\n\n  'adm_opt_stats_and_records' => 'Estadísticas y récords',\n  'adm_opt_stats_hide_admins' => 'Ocultar administradores',\n  'adm_opt_stats_hide_admins_detail' => 'Se ocultarán todas las cuentas con authlevel > 0',\n  'adm_opt_stats_hide_player_list' => 'Ocultar jugadores',\n  'adm_opt_stats_hide_player_list_detail' => 'Lista de IDs de jugadores a ocultar, separados por comas',\n  'adm_opt_stats_schedule' => 'Programación de actualización de estadísticas',\n  'adm_opt_stats_schedule_detail' => 'Formato: \"[AAAA:[MM:[DD:[HH:[MM:[SS]]]]]][,(...)]\"<br />\n    Los parámetros cero a la izquierda son opcionales<br />\n    Los parámetros vacíos a la derecha se consideran cero<br />\n    Ejemplos:<br />\n     - \"00:00:27:00\" significa \"ejecución a los 27 minutos de cada hora\";<br />\n     - \"04::\" - \"ejecución a las 4 AM cada día\";<br />\n     - \"02::,17:00\" - \"ejecución a las 2 AM cada día y a los 17 minutos de cada hora\";<br />\n     - \"1:4:30:00\" - \"Ejecución el día 1 de cada mes a las 04:30 AM\" etc.',\n  'adm_opt_stats_hide_pm_link' => 'Ocultar enlaces a mensajes privados',\n\n  'adm_pay' => 'Pagos',\n  'adm_pay_stats' => 'Estadísticas de pagos',\n  'adm_pay_th_payer' => 'Pagador',\n  'adm_pay_th_payer_id' => 'ID',\n  'adm_pay_th_payer_name' => 'Nombre',\n  'adm_pay_th_payment' => 'Pago',\n  'adm_pay_th_payment_id' => 'ID',\n  'adm_pay_th_payment_date' => 'Fecha',\n  'adm_pay_th_payment_status' => 'Estado',\n  'adm_pay_th_payment_amount' => 'Monto',\n  'adm_pay_th_payment_currency' => 'Moneda',\n  'adm_pay_th_mm_paid' => 'Pagado',\n  'adm_pay_th_mm_gained' => 'Acreditado',\n  'adm_pay_th_module' => 'Sistema de pago',\n  'adm_pay_th_module_name' => 'Tipo',\n\n  'adm_pay_filter_all' => '-- Todos --',\n  'adm_pay_filter_status' => array(\n    PAYMENT_STATUS_ALL => '-- Todos --',\n    PAYMENT_STATUS_NONE => 'No completado',\n    PAYMENT_STATUS_COMPLETE => 'Completado',\n  ),\n  'adm_pay_filter_test' => array(\n    PAYMENT_TEST_ALL => '-- Todos --',\n    PAYMENT_TEST_REAL => 'Real',\n    PAYMENT_TEST_PROBE => 'Prueba',\n  ),\n  'adm_pay_filter_stat' => array(\n    PAYMENT_FILTER_STAT_NORMAL => '-- Ninguno --',\n    PAYMENT_FILTER_STAT_MONTH => 'Por meses',\n    PAYMENT_FILTER_STAT_YEAR => 'Por años',\n    PAYMENT_FILTER_STAT_ALL => 'Todo el tiempo',\n  ),\n  'adm_pay_filter_stat_name' => 'Estadísticas',\n\n  'adm_user_stat' => 'Estadísticas de usuarios',\n  'adm_user_online' => 'En línea desde %s hasta %s',\n\n  'adm_ban_unban' => 'Ban/Desban',\n  'adm_metametter_payment' => 'MM & Pagos',\n\n  'adm_stat_already_started' => 'Las estadísticas ya se están actualizando ahora mismo',\n\n  'adm_dm_change_hint' => 'La búsqueda se realiza primero por ID de jugador, y si no se encuentra, por nombre',\n\n  'adm_matter_change_log_record' => 'A través del panel de administración por el usuario [%3$s] \"%4$s\" para la cuenta [%1$d] \"%2$s\" por la razón \"%5$s\"',\n\n  'adm_game_status' => 'Estado actual del juego',\n\n  'adm_log_delete_update_info' => 'Eliminar información sobre mantenimiento de BD, actualizaciones de estadísticas y motor',\n\n  'admin_tab_status' => 'Estado',\n  'admin_tab_game' => 'Juego',\n  'admin_tab_universe' => 'Universo',\n  'admin_tab_planets' => 'Planetas',\n  'admin_tab_stats_and_records' => 'Estadísticas',\n  'admin_tab_urls' => 'Enlaces',\n  'admin_tab_players' => 'Jugadores',\n  'admin_tab_UBE' => 'Batalla',\n  'admin_tab_advertise' => 'Publicidad',\n\n  'admin_tab_universe_main' => 'Universo',\n\n  'admin_ptl_test_la_' => \"Single'Double\\\"Zero\\0End\",\n\n  'admin_title_access_denied' => 'Acceso denegado',\n\n  'menu_admin_modules' => 'Módulos',\n\n  'adm_player' => 'Jugador',\n  'adm_planets' => 'Planetas',\n\n  // ------------------ NO LOCALIZADO -------------------------------\n  'adm_mm_title'                        => 'Cambiar cantidad de Metamateria',\n  'adm_mm_account'                      => 'Cuenta: ID, nombre o email de registro',\n  'adm_mm_account_hint'                 => 'La búsqueda de cuenta primero es por ID, luego por nombre, luego por email de registro',\n  'adm_mm_player'                       => 'Jugador: ID o nombre de la lista de jugadores',\n  'adm_mm_player_hint'                  => 'La búsqueda de jugador primero es por ID, luego por nombre',\n  'adm_mm_err_points_empty'             => 'Especifica cantidad de MM (positivo - añadir, negativo - quitar)',\n  'adm_mm_err_account_not_found'        => 'Error: no puedo encontrar cuenta con ID, nombre o email \"%1$s\"',\n  'adm_mm_err_player_not_found'         => 'Error: no se encontró jugador con ID o nombre \"%1$s\"',\n  'adm_mm_err_player_no_account'        => 'Error: no puedo encontrar cuenta para el jugador \"%1$s\"',\n  'adm_mm_err_account_and_player_empty' => 'Error: no se especificó ni cuenta ni jugador para cambiar MM',\n  'adm_mm_err_mm_change_failed'         => 'Error: error interno al cambiar MM. Contacta al desarrollador',\n  'adm_mm_msg_mm_changed'               => 'La cuenta [%2$d] \"%1$s\" (jugador [%4$s] \"%5$s\") tuvo su MM cambiada exitosamente a <span class=\"metamatter\">%3$s MM</span>',\n  'adm_mm_msg_confirm_mm_change'        => 'Confirma cambiar MM de la cuenta [%2$d] \"%1$s\" (jugador [%4$s] \"%5$s\") a <span class=\"metamatter\">%3$s MM</span>',\n  'adm_mm_msg_change_mm_log_record'     => 'Admin [%6$s] \"%7$s\" (jugador [%3$s] \"%4$s\") razón \"%5$s\" para [%1$d] \"%2$s\" (jugador [%8$d] \"%9$s\")',\n\n  'admin_ally_list' => 'Lista de Alianzas',\n);\n"
  },
  {
    "path": "language/es/affilates.mo.php",
    "content": "<?php\n\n/*\n#############################################################################\n# Nombre del archivo: affilates.mo\n# Proyecto: SuperNova.WS\n# Sitio web: http://www.supernova.ws\n# Descripción: Juego masivo multijugador en línea en el navegador, estrategia espacial\n#\n# Derechos de autor © 2009 Gorlum para el proyecto \"SuperNova.WS\"\n# Derechos de autor © 2009 MSW\n#############################################################################\n*/\n\n/**\n*\n* Paquete: idioma\n* Sistema: Español\n* Versión: 39b3.0\n*\n*/\n\n/**\n* NO CAMBIE\n*/\n\n!defined('INSIDE') && die();\n\n//$lang = array_merge($lang,\n//$lang->merge(\n$a_lang_array = (array(\n  'aff_title' => 'Programa de afiliados',\n  'aff_text1' => 'Publique su enlace personal, banner o barra de usuario en el foro o sitio web y cada visitante que llegue por el enlace será su Invitado. Recibirá 1 TM por cada',\n  'aff_text2' => 'TM ganados por el invitado.',\n  'aff_text3' => 'El cobro de bonos comienza después de que su Invitado gane',\n  'aff_link' => 'Enlace personal en el programa de afiliados',\n  'aff_link_direct' => 'Enlace directo',\n  'aff_link_bb' => 'BBCode para publicar el enlace personal en el foro',\n  'aff_link_html' => 'Código HTML para publicar el enlace personal en una página web',\n  'aff_banner' => 'Banner de 416x58',\n  'aff_banner_bb' => 'BBCode para publicar el banner en el foro',\n  'aff_banner_html' => 'Código HTML para publicar el banner en una página web',\n  'aff_userbar' => 'Barra de usuario de 350x19',\n  'aff_userbar_bb' => 'BBCode para publicar la barra de usuario en el foro',\n  'aff_userbar_html' => 'Código HTML para publicar la barra de usuario en una página web',\n  'aff_list' => 'Lista de invitados',\n  'aff_none' => 'No hay invitados',\n  'aff_gained' => 'TM ganados por el jugador',\n  'aff_your_bonus' => 'Su bono',\n));\n"
  },
  {
    "path": "language/es/alliance.mo.php",
    "content": "<?php\n\n/*\n#############################################################################\n#  Filename: alliance.mo\n#  Project: SuperNova.WS\n#  Website: http://www.supernova.ws\n#  Description: Juego de estrategia espacial masivo multijugador en línea\n#\n#  Copyright © 2009-2018 Gorlum for Project \"SuperNova.WS\"\n#############################################################################\n*/\n\n/**\n*\n* @package language\n* @system [Spanish]\n* @version 46d0\n*\n*/\n\n/**\n* DO NOT CHANGE\n*/\n\nif (!defined('INSIDE')) die();\n\n\n$a_lang_array = [\n  'ali_dip_title' => 'Diplomacia',\n  'ali_dip_negotiate' => 'Negociaciones',\n  'ali_adm_msg_subject' => 'Mensaje de la Alianza',\n  'ali_dip_offers_your' => 'Tus ofertas',\n  'ali_dip_offers_to_you' => 'Ofertas para ti',\n  'ali_dip_offer_none' => 'No hay ofertas',\n  'ali_dip_offer' => 'Oferta',\n  'ali_dip_offers' => 'Ofertas',\n  'ali_dip_offer_new' => 'Iniciar negociaciones',\n  'ali_dip_offer_to_ally' => 'Ofrecer a la Alianza',\n  'ali_dip_offer_make' => 'Comenzar negociaciones',\n  'ali_dip_offer_answer' => 'La Alianza ha rechazado tu oferta',\n  'ali_dip_offer_deny_reason' => 'Has rechazado la oferta',\n  'ali_dip_offer_to' => 'A la Alianza',\n  'ali_dip_offer_from' => 'De la Alianza',\n  'ali_dip_offer_deny' => 'Rechazar oferta',\n  'ali_dip_offer_accept' => 'Aceptar oferta',\n  'ali_dip_offer_delete' => 'Retirar oferta',\n  'ali_dip_err_no_ally' => 'No existe tal Alianza',\n  'ali_dip_err_same_ally' => 'No puedes negociar con tu propia Alianza',\n  'ali_dip_err_wrong_offer' => 'No puedes hacer una oferta así',\n  'ali_dip_err_offer_none' => 'No existe tal oferta',\n  'ali_dip_err_offer_same' => 'Ya tienes una relación \"%s\" con esta Alianza',\n  'ali_dip_err_offer_alien' => '¡Esta oferta no fue hecha para ti!',\n  'ali_dip_err_offer_accept_own' => '¡No puedes aceptar tu propia oferta!',\n  'ali_dip_err_offer_empty' => 'No se especificó la oferta',\n  'ali_dip_relation_none' => 'Sin relación',\n  'ali_dip_relation_change_auto_accept' => 'La Alianza \"%1$s\" ha cambiado su relación con la Alianza \"%2$s\" a \"%3$s\"',\n  'ali_dip_relation_change_own' => 'Hemos aceptado la oferta de la Alianza \"%2$s\" para cambiar la relación a \"%3$s\"',\n  'ali_dip_relation_change_other' => 'La Alianza \"%1$s\" ha aceptado nuestra oferta para cambiar la relación a \"%3$s\"',\n  'ali_dip_relations' => [\n    ALLY_DIPLOMACY_NEUTRAL => 'Neutralidad',\n    ALLY_DIPLOMACY_WAR => 'Guerra',\n    ALLY_DIPLOMACY_PEACE => 'Paz',\n    ALLY_DIPLOMACY_CONFEDERATION => 'Confederación',\n    ALLY_DIPLOMACY_FEDERATION => 'Federación',\n    ALLY_DIPLOMACY_UNION => 'Unión',\n    ALLY_DIPLOMACY_MASTER => 'Líder',\n    ALLY_DIPLOMACY_SLAVE => 'Subordinado',\n  ],\n\n  'ali_lessThen15min' => '&lt; 15 m',\n  'ali_confirm' => 'Confirmar',\n  'ali_confirmation' => 'Confirmación',\n  'ali_adm_disband' => 'Disolver la Alianza',\n  'ali_adm_options' => 'Opciones de la Alianza',\n  'ali_adm_transfer' => 'Transferir la Alianza a un jugador',\n  'ali_adm_return' => 'Volver a la gestión de la Alianza',\n  'ali_adm_kick' => 'Expulsar jugador de la Alianza',\n  'ali_adm_kick_confirm' => '¿Estás seguro de que deseas expulsar al jugador de la Alianza?',\n  'ali_adm_requests' => 'Solicitudes de ingreso',\n  'ali_adm_newLeader' => 'SELECCIONA JUGADOR',\n  'ali_adm_lastRank' => '¡No se puede eliminar el único rango!',\n  'ali_adm_rights_title' => 'Configuración de permisos',\n  'ali_adm_rights_rank_new' => 'Nuevo rango',\n  'ali_adm_rights_rank_delete' => 'Eliminar rango',\n  'ali_adm_rights_rank_none' => 'No hay rangos',\n  'ali_adm_rights_rank_name' => 'Rango',\n  'ali_adm_rights_mass_mail' => 'Mensaje a toda la Alianza',\n  'ali_adm_rights_view_online' => 'Ver estado en línea de los miembros',\n  'ali_adm_rights_helper' => 'Asistente del líder (Se necesita rango de fundador para transferir)',\n  'ali_adm_rights_legend' => 'Permisos de la Alianza',\n  'ali_leaderRank' => 'Líder de la Alianza',\n  'ali_defaultRankName' => 'Novato',\n  'ali_make_title' => 'Creación de Alianza',\n  'ali_make_tag_length' => '(de 3 a 8 caracteres)',\n  'ali_make_name_length' => '(hasta 35 caracteres)',\n  'ali_make_confirm' => 'Crear Alianza',\n  'ali_req_cancel' => 'Eliminar solicitud',\n  'ali_req_candidate' => 'Candidato',\n  'ali_req_characters' => 'caracteres',\n  'ali_req_date' => 'Fecha de solicitud',\n  'ali_req_deny_msg' => 'Tu solicitud para unirte a la Alianza [%s] ha sido rechazada.<br>Razón: \"%s\".<br>Puedes eliminar la solicitud y volver a intentarlo más tarde o unirte a otra Alianza.',\n  'ali_req_deny_admin' => '<font color=red>Solicitud ya rechazada</font>. Sin embargo, mientras el usuario no elimine la solicitud, puedes cambiar tu decisión',\n  'ali_req_deny_reason' => 'Tu solicitud de ingreso ha sido rechazada',\n  'ali_req_emptyList' => 'No hay solicitudes para revisar',\n  'ali_req_inAlly' => 'Ya eres miembro de una Alianza.',\n  'ali_req_make' => 'Enviar solicitud',\n  'ali_req_not_allowed' => 'NO SE ACEPTAN',\n  'ali_req_otherRequest' => 'Ya has enviado una solicitud a otra Alianza.',\n  'ali_req_template' => 'Solicito unirme a su Alianza',\n  'ali_req_text' => 'Texto de la solicitud',\n  'ali_req_title' => 'Enviar solicitud a la Alianza',\n  'ali_req_waiting' => 'Tu solicitud para unirte a la Alianza [%s] será revisada por el líder.<br>Se te notificará la decisión.',\n  'ali_req_check' => 'Gestión de solicitudes',\n  'ali_req_requestCount' => 'Solicitudes pendientes',\n  'ali_req_admin_title' => 'Revisión de solicitudes',\n  'ali_req_accept' => 'Aceptar solicitud',\n  'ali_req_deny' => 'Rechazar solicitud',\n  'ali_search_title' => 'Buscar Alianza',\n  'ali_search_action' => 'Buscar',\n  'ali_search_tip' => 'Puedes buscar por parte del nombre o etiqueta de la Alianza',\n  'ali_search_result_none' => 'No se encontraron Alianzas que coincidan con tu búsqueda.',\n  'ali_search_show_all' => 'Lista y estadísticas de todas las Alianzas',\n  'ali_sys_name' => 'Nombre',\n  'ali_sys_tag' => 'Etiqueta',\n  'ali_sys_members' => 'Miembros',\n  'ali_sys_notFound' => 'Esta Alianza no existe',\n  'ali_sys_memberName' => 'Nombre',\n  'ali_sys_points' => 'Puntos',\n  'ali_sys_lastActive' => 'Actividad',\n  'ali_sys_totalMembers' => 'Total',\n  'ali_sys_clear' => 'Limpiar',\n  'ali_sys_main_page' => 'Volver a la página principal de la Alianza',\n  'ali_sys_joined' => 'Fecha de ingreso',\n  'ali_frm_write' => 'Escribir en el foro',\n  'ali_info_title' => 'Información de la Alianza',\n  'ali_info_internal' => 'Información interna',\n  'ali_info_leave' => 'Abandonar la Alianza',\n  'ali_info_bonus_rate' => 'Tasa de bonificación',\n  'Name' => 'Nombre',\n  'Tag' => 'Etiqueta',\n  'Members' => 'Miembros',\n  'Accept_cand' => 'Aceptar',\n  'alliance' => 'Alianza',\n  'alliances' => 'Alianzas',\n  'Alliance_information' => 'Información de la Alianza',\n  'Alliance_logo' => 'Logo de la Alianza',\n  'alliance_tag' => 'Etiqueta de la Alianza',\n  'Allow_request' => 'Aceptar solicitudes',\n  'allyance_name' => 'Nombre de la Alianza',\n  'ally_admin' => 'Gestión de la Alianza',\n  'ally_been_maked' => 'La Alianza %s se ha creado con éxito',\n  'ally_description' => 'Descripción de la Alianza',\n  'ally_dissolve' => 'Eliminar Alianza',\n  'Ally_info_1' => 'Información de la Alianza',\n  'ally_maked' => '%s creada',\n  'Ally_nodescription' => 'La Alianza no tiene descripción',\n  'ally_notexist' => 'La Alianza ya no existe',\n  'Ally_not_exist' => 'No hay información disponible sobre esta Alianza',\n  'Ally_transfer' => 'Transferir Alianza',\n  'All_players' => 'Todos los jugadores',\n  'always_exist' => '%s ya existe',\n  'Aplication_acepted' => 'Has sido aceptado',\n  'Aplication_hello' => 'Saludos<br>Alianza:',\n  'Aplication_rejected' => 'Tu solicitud para unirte a la Alianza ha sido rechazada.<br>Razón:<br>',\n  'apply_cantbeadded' => 'Error al enviar la solicitud. Por favor, inténtalo de nuevo.',\n  'apply_registered' => 'Tu solicitud ha sido enviada.<br><br><a href=alliance.php>Volver</a>',\n  'Back' => 'Volver',\n  'Canceld_req_text' => 'Has cancelado tu solicitud para unirte a [%s]',\n  'Change' => 'Cambiar',\n  'ch_allyname' => 'Cambiar nombre de la Alianza',\n  'ch_allytag' => 'Cambiar etiqueta de la Alianza',\n  'Circular_message' => 'Mensaje a la Alianza',\n  'Circular_sended' => 'Mensaje enviado con éxito',\n  'Clear' => 'Limpiar',\n  'Click_writerequest' => 'Haz clic aquí para escribir una solicitud',\n  'Continue' => 'Continuar',\n  'Delete_apply' => 'Rechazar solicitud',\n  'Denied_access' => '¡Acceso denegado!',\n  'Destiny' => 'Destinatario',\n  'Exit_of_this_alliance' => 'Salir de la Alianza',\n  'External_text' => 'Texto externo',\n  'Founder' => 'Fundador',\n  'Founder_name' => 'Rango de fundador',\n  'Function' => 'Función',\n  'Go_out_welldone' => 'Has abandonado la Alianza con éxito',\n  'have_not_name' => 'Ingresa un nombre para la Alianza',\n  'have_not_tag' => 'Ingresa una etiqueta para la Alianza',\n  'Help' => 'Ayuda',\n  'Inactive' => 'Inactivo',\n  'Inner_section' => 'Texto interno',\n  'Internal_text' => 'Texto interno',\n  'knowed_allys' => 'Alianzas existentes',\n  'laws_config' => 'Configuración de permisos',\n  'Main_Page' => 'Página principal',\n  'make_alliance' => 'Crear Alianza',\n  'make_alliance_owner' => 'Crear Alianza',\n  'max' => 'máx.',\n  'member' => 'Miembro',\n  'memberlist_view' => 'Ver lista de miembros',\n  'members' => 'Miembros',\n  'members_admin' => 'Gestión de miembros',\n  'Members_list' => 'Lista de miembros',\n  'members_who_recived_message' => 'Los siguientes miembros han recibido el mensaje:',\n  'Message' => 'Mensaje',\n  'Motive_optional' => 'Motivo (opcional)',\n  'New_name' => 'Nuevo nombre',\n  'New_tag' => 'Nueva etiqueta',\n  'not_allow_request' => 'Rechazar solicitudes',\n  'Novate' => 'Novato',\n  'Number' => 'Nº',\n  'Off' => 'Off-line',\n  'Ok' => 'Aceptar',\n  'On' => 'On-line',\n  'Online' => 'Estado',\n  'Position' => 'Posición',\n  'Public_text_of_alliance' => 'Texto público',\n  'Range' => 'Rango',\n  'Reject_cand' => 'Rechazar',\n  'Reload' => 'Ejemplo',\n  'Repel' => 'Repeler',\n  'requests_view' => 'Ver solicitudes',\n  'Request_answer' => 'Solicitud rechazada',\n  'Request_date' => 'Fecha de solicitud',\n  'Request_text' => 'Texto de la solicitud',\n  's' => '[N/A]',\n  'Search' => 'Buscar',\n  'searchd_ally_avail' => 'Alianzas encontradas:',\n  'search_alliance' => 'Buscar',\n  'Send' => 'Enviar',\n  'Send_Apply' => 'Aceptar solicitud',\n  'Send_circular_mail' => 'Enviar mensaje a toda la Alianza',\n  'Set_range' => 'Cambiar rango',\n  'Show_of_request_text' => 'Texto de la solicitud',\n  'Texts' => 'Editar texto',\n  'Text_mail' => 'Enviar mensaje a toda la Alianza',\n  'top10alliance' => 'Top 10 Alianzas',\n  'transfer' => 'Transferir',\n  'transfer_ally' => 'Transferir Alianza',\n  'transfer_to' => 'Transferir Alianza a:',\n  'Want_go_out' => '¿Estás seguro de que deseas abandonar la Alianza?',\n  'write_apply' => 'Enviar solicitud',\n  'your_alliance' => 'Tu Alianza',\n  'your_apply' => 'Tu solicitud',\n  'ali_info_leave_success' => 'Has abandonado la Alianza [%s].<br />Ahora puedes crear tu propia Alianza o enviar una solicitud a otra Alianza<br />',\n\n  'opt_avatar' => 'Logo de la Alianza',\n  'opt_avatar_search' => 'Buscar en Google',\n  'opt_avatar_remove' => 'Eliminar logo',\n  'opt_upload' => 'Subir',\n\n  'opt_msg_avatar_removed' => 'Logo eliminado',\n  'opt_msg_avatar_uploaded' => 'Logo actualizado con éxito',\n  'opt_msg_avatar_error_delete' => 'Error al eliminar el logo. Contacta al administrador del servidor',\n  'opt_msg_avatar_error_writing' => 'Error al guardar el logo. Contacta al administrador del servidor',\n  'opt_msg_avatar_error_upload' => 'Error al subir la imagen %1. Contacta al administrador del servidor',\n  'opt_msg_avatar_error_unsupported' => 'Formato de imagen no soportado. Solo se permiten archivos JPG, GIF o PNG de hasta 200KB',\n\n  'ali_admin_mercenaries' => 'Mercenarios de la Alianza',\n  'ali_admin_plans' => 'Planos de la Alianza',\n  'ali_admin_techs' => 'Investigaciones de la Alianza',\n  'ali_admin_market_trader' => 'Intercambio de recursos en el mercado negro',\n\n  'ali_res_player_bonus' => 'Bonificación de miembro',\n  'ali_res_transfer' => 'Transferir',\n  'ali_res_transfer_long' => 'Transferir a la cuenta de la Alianza',\n  'ali_res_no_resources' => 'La Alianza no tiene recursos',\n  'ali_res_transfer_dm_log' => 'El miembro \"%s\" ha transferido %d DM a la cuenta de la Alianza [%s]',\n\n  'ali_res_err_not_enough' => '¡No hay suficiente %s!',\n  'ali_res_err_wrong_unit' => '¡Solo puedes transferir recursos a la cuenta de la Alianza!',\n\n  'ali_res_alliance_bonus' => 'Bonificaciones de la Alianza',\n  'ali_res_alliance_bonus_players' => 'Número de miembros para recibir bonificación',\n\n  'ally_message_tag_exists' => 'Ya existe una Alianza con la etiqueta [%1$s]',\n  'ally_message_name_exists' => 'Ya existe una Alianza con el nombre \"%1$s\"',\n\n  'ally_alliances_recommended' => 'Alianzas recomendadas',\n  'ally_recommended_diff' => 'Diferencia de puntos',\n  'ally_recommended_rates' => 'Ratio',\n  'ali_search_result_tip' => '\n    <li>Haz clic en el nombre o etiqueta de la Alianza para ver su información</li>\n    <li>Haz clic en \"Enviar solicitud\" para solicitar unirte</li>\n    <li>La columna \"Diferencia de puntos\" muestra la diferencia entre tus puntos y el promedio de puntos por miembro en la Alianza. Si es negativo, el miembro promedio tiene más puntos que tú</li>\n    <li>La columna \"Ratio\" muestra la relación entre tus puntos y el promedio de la Alianza. Si es menor que 1, el miembro promedio es más débil que tú</li>\n    <li>Se recomienda elegir una Alianza con una diferencia pequeña y un ratio entre 0.75 y 1.3. Sin embargo, la decisión final es tuya</li>\n   ',\n];"
  },
  {
    "path": "language/es/announce.mo.php",
    "content": "<?php\n\n/*\n#############################################################################\n#  Filename: announce.mo\n#  Project: SuperNova.WS\n#  Website: http://www.supernova.ws\n#  Description: Juego de estrategia espacial masivo multijugador en línea\n#\n#  Copyright © 2009-2018 Gorlum for Project \"SuperNova.WS\"\n#############################################################################\n*/\n\n/**\n*\n* @package language\n* @system [Spanish]\n* @version 46d0\n*\n*/\n\n/**\n* DO NOT CHANGE\n*/\n\nif (!defined('INSIDE')) die();\n\n\n$a_lang_array = array(\n  'add_announce' => 'Añadir anuncio',\n  'metal' => 'Metal',\n  'crystal' => 'Cristal',\n  'deuterium' => 'Deuterio',\n  'Resources_to_be_sold' => 'Recursos ofrecidos',\n  'Desired_resources' => 'Recursos solicitados',\n  'send' => 'Enviar',\n  'Your_announce_was_recorded' => 'Tu anuncio se ha publicado correctamente',\n  'return_to_announce' => 'Volver a la página de anuncios',\n  'Classifieds' => 'Anuncios publicados',\n  'Action' => 'Acción',\n  'Galaxy' => 'Galaxia',\n  'Solar_system' => 'Sistema solar',\n  'Infos_of_delivery' => 'Información del comerciante',\n  'Salesman' => 'Jugador',\n  'Delete' => 'Eliminar',\n  'announce_status' => 'Estado del anuncio',\n  'Your_announce_not_recorded' => 'Tu anuncio no se ha publicado',\n  'Your_announce_was_deleted' => 'Tu anuncio se ha eliminado correctamente',\n);"
  },
  {
    "path": "language/es/artifacts.mo.php",
    "content": "<?php\n\n/*\n#############################################################################\n#  Filename: artifacts.mo\n#  Project: SuperNova.WS\n#  Website: http://www.supernova.ws\n#  Description: Juego de estrategia espacial masivo multijugador en línea\n#\n#  Copyright © 2009-2018 Gorlum for Project \"SuperNova.WS\"\n#############################################################################\n*/\n\n/**\n*\n* @package language\n* @system [Spanish]\n* @version 46d0\n*\n*/\n\n/**\n* DO NOT CHANGE\n*/\n\nif (!defined('INSIDE')) die();\n\n$a_lang_array = array(\n  'art_use'             => 'Usar artefacto',\n\n  'art_lhc_from'          => 'Gran Colisionador de Hadrones',\n  'art_lhc_subj'          => 'Intento de creación de luna',\n  'art_moon_create'   => array(\n    ART_LHC => '¡La onda gravitacional del GCH fusionó fragmentos de metal y cristal en órbita, creando una nueva luna %s en las coordenadas %s!',\n    ART_HOOK_SMALL => '¡El Gancho Pequeño lanzó la luna %1$s (diámetro: %3$s km) en %2$s!',\n    ART_HOOK_MEDIUM => '¡El Gancho Mediano lanzó la luna %1$s (diámetro: %3$s km) en %2$s!',\n    ART_HOOK_LARGE => '¡El Gancho Grande lanzó la luna %1$s (diámetro: %3$s km) en %2$s!',\n  ),\n  'art_moon_exists'   => 'Ya existe una luna en estas coordenadas',\n  'art_lhc_moon_fail'     => 'La onda gravitacional del GCH no fue suficiente para formar una luna',\n\n  'art_rcd_from'          => 'Complejo de Colonización Autónoma',\n  'art_rcd_subj'          => 'Colonia desplegada',\n  'art_rcd_ok'            => '%1$s desplegó con éxito una colonia en el planeta %2$s (coordenadas: %3$s)',\n  'art_rcd_err_moon'      => 'El CCA solo puede desplegarse en planetas',\n  'art_rcd_err_no_sense'  => 'El CCA detectó que ninguna mejora era posible y canceló el despliegue',\n  'art_rcd_err_que'       => 'El CCA no puede desplegarse en planetas con construcciones en curso. Cancela todas las órdenes e inténtalo de nuevo',\n\n  'art_heurestic_chip_ok' => 'Tiempo de investigación de \"%s\" (nivel %d) reducido en %s',\n  'art_heurestic_chip_subj' => 'Aceleración de investigación',\n  'art_heurestic_chip_no_research' => 'No hay investigaciones en curso o el tiempo restante es menor a 1 minuto',\n\n  'art_nano_builder_ok' => 'Tiempo de %s del edificio \"%s\" (nivel %d) en el planeta %s %s reducido en %s',\n  'art_nano_builder_build' => 'construcción',\n  'art_nano_builder_destroy' => 'demolición',\n  'art_nano_builder_subj' => 'Aceleración de operación',\n  'art_nano_builder_no_que' => 'No hay operaciones en curso o el tiempo restante es menor a 1 minuto',\n\n  'art_err_no_artifact'  => 'No tienes este artefacto',\n\n  'art_page_hint'        => '<ul>\n    <li>Los artefactos son objetos raros con efectos únicos</li>\n    <li>Son de un solo uso: desaparecen después de activarse</li>\n    <li>Algunos son tan poderosos que su cantidad por Imperio está limitada</li>\n    <li>La mayoría afecta solo al planeta de uso, pero los más raros pueden influir en sistemas solares, galaxias o ¡todo el universo!</li>\n  </ul>',\n);"
  },
  {
    "path": "language/es/buddy.mo.php",
    "content": "<?php\n\n/*\n#############################################################################\n#  Filename: buddy.mo\n#  Project: SuperNova.WS\n#  Website: http://www.supernova.ws\n#  Description: Juego de estrategia espacial masivo multijugador en línea\n#\n#  Copyright © 2009-2018 Gorlum for Project \"SuperNova.WS\"\n#############################################################################\n*/\n\n/**\n*\n* @package language\n* @system [Spanish]\n* @version 46d0\n*\n*/\n\n/**\n* DO NOT CHANGE\n*/\n\nif (!defined('INSIDE')) die();\n\n\n$a_lang_array = array(\n  'buddy_buddies' => 'Amigos',\n  'buddy_request_text' => 'Texto de solicitud',\n  'buddy_request_text_default' => 'Por favor, añádeme a tu lista de amigos',\n  'buddy_request_none' => 'No tienes amigos ni solicitudes pendientes',\n  'buddy_request_write_header' => 'Enviar solicitud de amistad',\n  'buddy_request_player_name' => 'Nombre del jugador',\n  'buddy_request_accept' => 'Añadir a la lista de amigos',\n\n  'buddy_status' => 'Estado',\n  'buddy_status_active' => 'Amistad mutua',\n  'buddy_status_incoming_waiting' => 'Has recibido una solicitud de amistad',\n  'buddy_status_incoming_denied' => 'Has rechazado la solicitud',\n  'buddy_status_outcoming_waiting' => 'Solicitud enviada. Esperando respuesta',\n  'buddy_status_outcoming_denied' => 'Tu solicitud ha sido rechazada',\n\n  // Mensajes de resultado\n  'buddy_err_not_exist' => 'La solicitud no existe. Puede que la hayas eliminado, rechazado o el autor la haya cancelado',\n\n  'buddy_err_accept_own' => 'No puedes aceptar tu propia solicitud',\n  'buddy_err_accept_alien' => 'No puedes aceptar una solicitud que no está dirigida a ti',\n  'buddy_err_accept_already' => 'Ya has aceptado esta solicitud y eres amigo de este jugador',\n  'buddy_err_accept_denied' => 'Ya has rechazado esta solicitud y no puedes aceptarla ahora',\n  'buddy_err_accept_internal' => 'Error al procesar la solicitud. Inténtalo más tarde o contacta con los administradores',\n  'buddy_err_accept_none' => 'Solicitud aceptada correctamente',\n\n  'buddy_err_delete_alien' => '¡Esta solicitud no es tuya! No interfieras en las relaciones de otros. ¡Busca tus propios amigos!',\n  'buddy_err_unfriend_none' => 'Has terminado la amistad',\n  'buddy_err_delete_own' => 'Tu solicitud ha sido eliminada',\n  'buddy_err_deny_none' => 'Has rechazado la amistad con este jugador',\n\n  'buddy_err_adding_exists' => 'No puedes enviar solicitud: ya son amigos o existe una solicitud pendiente',\n  'buddy_err_adding_none' => 'Solicitud de amistad enviada',\n  'buddy_err_adding_self' => 'No puedes añadirte a ti mismo como amigo',\n\n  // Mensajes privados\n  'buddy_msg_accept_title' => '¡Tienes un nuevo amigo!',\n  'buddy_msg_accept_text' => '¡El jugador %s te ha añadido a su lista de amigos!',\n  'buddy_msg_unfriend_title' => '¡Has perdido un amigo!',\n  'buddy_msg_unfriend_text' => 'El jugador %s ha roto su amistad contigo. ¡Qué triste!',\n  'buddy_msg_deny_title' => 'Solicitud rechazada',\n  'buddy_msg_deny_text' => 'El jugador %s ha rechazado tu solicitud de amistad',\n  'buddy_msg_adding_title' => 'Solicitud de amistad',\n  'buddy_msg_adding_text' => 'El jugador %s quiere ser tu amigo',\n\n  'buddy_hint' => '\n    <li>Envía solicitudes de amistad desde el menú <a href=\"search.php\">Búsqueda</a></li>\n    <li>Puedes ver el estado de tus amigos (online/offline), pero ellos también verán el tuyo. Tenlo en cuenta al aceptar solicitudes</li>\n    <li>Si rechazas una solicitud, no podrás iniciar amistad hasta que el jugador elimine su petición</li>',\n);"
  },
  {
    "path": "language/es/buildings.mo.php",
    "content": "<?php\n\n/*\n#############################################################################\n#  Filename: buildings.mo\n#  Project: SuperNova.WS\n#  Website: http://www.supernova.ws\n#  Description: Juego de estrategia espacial masivo multijugador en línea\n#\n#  Copyright © 2009-2018 Gorlum for Project \"SuperNova.WS\"\n#############################################################################\n*/\n\n/**\n*\n* @package language\n* @system [Spanish]\n* @version 46d0\n*\n*/\n\n/**\n* DO NOT CHANGE\n*/\n\nif (!defined('INSIDE')) die();\n\n$a_lang_array = array(\n  'built' => 'Construido',\n  'Fleet' => 'Flota',\n  'fleet' => 'flota',\n  'Defense' => 'Defensa',\n  'defense' => 'defensa',\n  'Research' => 'Investigación',\n  'level' => 'Nivel',\n  'dispo' => 'Disponible',\n  'load_det' => 'Haz clic en la imagen para ver el modelo 3D',\n  'off_det' => 'Haz clic nuevamente para ocultar el modelo 3D',\n  'allowed_aya' => 'Disponible',\n  'allowed_ye' => 'Disponibles',\n  'allowed_yi' => 'Disponible',\n  'mech_info' => 'Especificaciones técnicas',\n  'fst_bld_load' => 'Procesando pedido.<br>Por favor espera...',\n  'fst_bld' => 'Pedido rápido:',\n  'price' => 'Costo',\n  'builds' => 'Construcciones',\n  'destroy_price' => 'Costo de demolición',\n  'no_fields' => 'No hay campos disponibles en el planeta',\n  'can_build' => 'Se puede construir: ',\n  'Requirements' => 'Requisitos: ',\n  'Requires' => 'Recursos necesarios ',\n  'Rest_ress' => 'Recursos restantes ',\n  'Rest_ress_fleet' => 'Incluyendo flotas en camino',\n  'Rechercher' => 'Investigar',\n  'ConstructionTime' => 'Tiempo de construcción ',\n  'DestructionTime' => 'Tiempo de demolición ',\n  'ResearchTime' => 'Tiempo de investigación ',\n  'Construire' => 'Construir',\n  'BuildFirstLevel' => 'Construir',\n  'BuildNextLevel' => 'Construir siguiente nivel ',\n  'completed' => 'Completado',\n  'in_working' => 'Ocupado',\n  'work_todo' => 'Ocupado',\n  'total_left_time' => 'Tiempo restante',\n  'only_one' => 'Solo puedes construir un escudo.',\n  'b_no_silo_space' => 'El silo de misiles está lleno.',\n  'que_full' => '¡La cola de construcción está llena!',\n  'Build_lab' => 'Error de construcción',\n  'NoMoreSpace' => '¡Planeta lleno!',\n  'InBuildQueue' => 'En cola de construcción',\n  'bld_usedcells' => 'Campos ocupados',\n  'bld_theyare' => 'Quedan',\n  'bld_cellfree' => 'campos libres',\n  'DelFromQueue' => 'cancelar',\n  'DelFirstQueue' => 'Pausar',\n  'cancel' => 'Cancelar',\n  'continue' => 'Continuar',\n  'ready' => 'Esperar',\n  'destroy' => 'Demoler',\n  'on' => 'en',\n  'attention' => '¡Atención! Se detectó un intento de hackeo. ¡La acción ha sido registrada!',\n  'no_laboratory' => '¡Laboratorio de investigación no construido!',\n  'need_hangar' => '¡Astillero no construido!',\n  'labo_on_update' => '¡Laboratorio en actualización!',\n  'fleet_on_update' => '¡Astillero en modernización!',\n  'Total_techs' => 'Total de investigaciones',\n  'eco_bld_page_hint' => '<ul><li>Pasa el cursor sobre la imagen para ver información de la unidad</li>\n  <li>Haz clic para seleccionar la unidad. Otro clic en la misma unidad la deseleccionará</li>\n  <li>Haz clic en el icono azul \"i\" para ver características detalladas</li>\n  <li>Construye haciendo clic en el \"+\" (esquina superior derecha) o en el enlace \"Construir\"</li>\n  <li>Demuele haciendo clic en el \"-\" (esquina superior izquierda) o en el enlace correspondiente</li></ul>',\n  'eco_price' => 'Precio',\n  'eco_left' => 'Restante',\n  'eco_bld_resources_not_enough' => 'No hay suficientes recursos para construir las unidades pedidas',\n\n  'eco_bld_msg_err_research_in_progress' => 'Los científicos del Imperio ya están investigando',\n  'eco_bld_msg_err_not_research' => 'Solo se pueden investigar tecnologías en los laboratorios',\n  'eco_bld_msg_err_requirements_not_meet' => 'No se cumplen los requisitos para la investigación',\n  'eco_bld_msg_err_laboratory_upgrading' => 'Los laboratorios están siendo modificados y no pueden investigar.<br/><br/>Durante la construcción o modificación de Laboratorios o Nano-laboratorios en cualquier planeta del Imperio (incluso si están en cola), la investigación no está disponible<br/><br/>Para iniciar una investigación, elimina todos los Laboratorios y Nano-laboratorios de todas las colas de construcción en todos los planetas',\n\n  'eco_bld_unit_info_extra_show' => 'Mostrar información adicional',\n  'eco_bld_unit_info_extra_hide' => 'Ocultar información adicional',\n  'eco_bld_unit_info_extra_none' => 'Sin información adicional',\n\n  'eco_bld_autoconvert' => 'Autoconversión',\n  'eco_bld_autoconvert_explain' => 'Los recursos faltantes para construcción/investigación se convertirán automáticamente de los recursos disponibles (metal, cristal, deuterio), y luego se añadirán a la cola.\\r\\n\\r\\n',\n  'eco_bld_autoconvert_dark_matter_none' => 'Faltan {0} de Materia Oscura para construcción con autoconversión.',\n  'eco_bld_autoconvert_confirm' => 'Esta operación costará {0} de Materia Oscura.\\r\\n\\r\\n¿Continuar?',\n\n  'eco_que_clear_dialog_title' => 'Confirmar limpieza de cola',\n  'eco_que_clear_dialog_text' => '¡Esta acción borrará toda la cola!<br /><br />Todas las construcciones/investigaciones no completadas se cancelarán y el tiempo invertido se perderá.<br />Los recursos serán devueltos al planeta.<br /><br />¿Estás seguro de continuar?',\n\n  'eco_que_artifact_dialog_title' => 'Usar {0}',\n  'eco_que_artifact_dialog_text' => \"Se usará el Artefacto \\\"{0}\\\" para acelerar la construcción/investigación actual.<br /><br />Si queda más de una hora: el tiempo se reducirá a la mitad<br />Si queda menos de una hora: se completará inmediatamente<br /><br />No se puede usar si queda menos de un minuto\",\n\n  'eco_bld_research_page_name' => 'Investigación de tecnologías',\n  'eco_bld_research_page_novapedia' => 'Lista de tecnologías en Novapedia',\n);"
  },
  {
    "path": "language/es/chat_advanced.mo.php",
    "content": "<?php\n\n/*\n#############################################################################\n#  Filename: chat_advanced.mo\n#  Project: SuperNova.WS\n#  Website: http://www.supernova.ws\n#  Description: Juego de estrategia espacial masivo multijugador en línea\n#\n#  Copyright © 2012-2018 Gorlum for Project \"SuperNova.WS\"\n#############################################################################\n*/\n\n/**\n*\n* @package language\n* @system [Spanish]\n* @version 46d0\n*\n*/\n\n/**\n* DO NOT CHANGE\n*/\n\nif (!defined('INSIDE')) die();\n\n$a_lang_array = array(\n  'chat_advanced_chat_players' => 'Jugadores en el chat',\n  'chat_advanced_online_players' => 'Jugadores en línea',\n  'chat_advanced_online_invisibles' => 'Incluyendo invisibles',\n  'chat_advanced_invisibility' => 'Invisibilidad',\n\n  'chat_advanced_frame_on' => 'Fijar',\n  'chat_advanced_frame_off' => 'Desfijar',\n\n  'chat_advanced_smile_tooltip' => 'Haz clic para seleccionar un emoticono',\n\n  'chat_advanced_visible' => array(\n    0 => 'Eres visible para otros jugadores',\n    1 => 'Eres invisible para otros jugadores',\n  ),\n\n  'chat_advanced_help_description' => \"Usa el comando \\\"/help <comando>\\\" para obtener información detallada. Ejemplo: \\\"/help whisper\\\"\",\n  'chat_advanced_help_commands_accessible' => 'Comandos de chat disponibles:',\n  'chat_advanced_help_command' => 'Comando \"/%s\"',\n  'chat_advanced_help_command_aliases' => 'Alias: ',\n\n  'chat_advanced_whisper_recipient_prefix' => '',\n  'chat_advanced_whisper_recipient_midfix' => ' -> ',\n  'chat_advanced_whisper_recipient_suffix' => '> ',\n  'chat_advanced_whisper_sender_prefix' => '',\n  'chat_advanced_whisper_sender_midfix' => ' -> ',\n  'chat_advanced_whisper_sender_suffix' => '> ',\n\n  'chat_advanced_command_reason' => '. Razón: %s',\n  'chat_advanced_command_reason2' => 'Razón:',\n  'chat_advanced_command_mute' => 'Jugador \"%1$s\" silenciado hasta %2$s (hora del servidor)%3$s',\n  'chat_advanced_command_unmute' => 'Jugador \"%s\" puede escribir nuevamente',\n  'chat_advanced_command_ban' => 'Jugador \"%1$s\" baneado (modo vacaciones) hasta %2$s',\n  'chat_advanced_command_ban_no_vacancy' => 'Jugador \"%1$s\" baneado SIN MODO VACACIONES hasta %2$s',\n  'chat_advanced_command_unban' => 'Jugador \"%s\" desbaneado',\n\n  'chat_advanced_command_interval' => array(\n    '1h' => '1 hora',\n    '3h' => '3 horas',\n    '6h' => '6 horas',\n    '12h' => '12 horas',\n    '1d' => '1 día',\n    '3d' => '3 días',\n    '1w' => '1 semana',\n    '2w' => '2 semanas',\n    '1m' => '30 días',\n    '2m' => '60 días',\n    '3m' => '90 días',\n    '10y' => 'Permanentemente*',\n  ),\n  'chat_advanced_ban_vacancy' => 'Modo vacaciones',\n\n  'chat_advanced_online_ban' => 'Banear a \"%1$s\" por...',\n  'chat_advanced_online_mute' => 'Silenciar a \"%1$s\" por...',\n  'chat_advanced_online_unmute' => 'Permitir que \"%1$s\" escriba',\n  'chat_advanced_online_invisible' => 'Jugador invisible',\n  'chat_advanced_online_banned_via_chat' => 'Baneado desde el chat',\n\n  'chat_advanced_help' => array(\n    'help' => \"El comando '/help' muestra información sobre comandos disponibles\\r\\n\n               Uso: /help [<comando>]\\r\\n\n               Sin parámetros muestra la lista completa. Ejemplo: '/help w' equivale a '/help whisper'\",\n    'whisper' => \"El comando '/whisper' envía mensajes privados\\r\\n\n                  Visibles solo para el remitente y destinatario, incluso si están offline\\r\\n\n                  Uso: /whisper <nombre> <mensaje>\\r\\n\n                  Nombres con espacios: /w \\\"nombre largo\\\" Hola!\",\n    'ban' => \"El comando '/ban' restringe el acceso al juego\\r\\n\n              Uso: /ban id <ID> <tiempo>[!] [razón]\\r\\n\n              <tiempo> formato: <número>{y|m|w|d|h}\\r\\n\n              ¡Sin modo vacaciones!\",\n    'unban' => \"El comando '/unban' elimina restricciones\\r\\n\n                Uso: /unban id <ID>\",\n    'mute'  => \"El comando '/mute' prohíbe escribir en el chat\\r\\n\n                Uso: /mute id <ID> <tiempo> [razón]\\r\\n\n                <tiempo> formato: <número>{y|m|w|d|h}\",\n    'unmute' =>  \"El comando '/unmute' restaura permisos de chat\\r\\n\n                  Uso: /unmute id <ID>\",\n    'invisible' => \"El comando '/invisible' controla tu visibilidad\\r\\n\n                    Uso: /invisible [on|off]\\r\\n\n                    Sin parámetros muestra el estado actual\",\n  ),\n\n  'chat_advanced_help_short' => array(\n    'help' => '/help',\n    'whisper' => '/whisper',\n    'ban' => '/ban',\n    'unban' => '/unban',\n    'mute' => '/mute',\n    'unmute' => '/unmute',\n    'invisible' => '/invisible',\n  ),\n\n  'chat_advanced_err_command_inacessible' => 'Comando no disponible. Usa \"/help\" para ver la lista',\n  'chat_advanced_err_command_unknown' => 'Comando desconocido',\n  'chat_advanced_err_player_name_unknown' => 'Jugador no encontrado',\n  'chat_advanced_err_message_empty' => 'No se pueden enviar mensajes vacíos',\n  'chat_advanced_err_message_player_empty' => 'Especifica el nombre del destinatario',\n  'chat_advanced_err_player_id_need' => 'Se requiere ID de jugador',\n  'chat_advanced_err_player_id_incorrect' => 'ID incorrecto',\n  'chat_advanced_err_player_id_unknown' => 'ID no existe',\n  'chat_advanced_err_player_same' => 'No puedes usarlo contigo mismo',\n  'chat_advanced_err_player_higher' => 'No puedes usarlo con jugadores de mayor nivel',\n  'chat_advanced_err_term_need' => 'Se requiere duración',\n  'chat_advanced_err_term_wrong' => 'Duración incorrecta',\n);"
  },
  {
    "path": "language/es/fleet.mo.php",
    "content": "<?php\n\n/*\n#############################################################################\n#  Filename: fleet.mo\n#  Project: SuperNova.WS\n#  Website: http://www.supernova.ws\n#  Description: Juego de estrategia espacial masivo multijugador en línea\n#\n#  Copyright © 2009-2018 Gorlum for Project \"SuperNova.WS\"\n#############################################################################\n*/\n\n/**\n*\n* @package language\n* @system [Spanish]\n* @version 46d0\n*\n*/\n\n/**\n* DO NOT CHANGE\n*/\n\nif (!defined('INSIDE')) die();\n\n$a_lang_array = array(\n  'flt_page2_title' => 'Seleccionar misión',\n  'fl_title' => 'Flotas',\n  'fl_expttl' => 'Expediciones',\n  'fl_mission' => 'Misión',\n  'fl_count' => 'Cantidad',\n  'fl_count_short' => 'Cant.',\n  'fl_where' => 'Ubicación',\n  'fl_from' => 'Origen',\n  'fl_from_t' => 'Regreso',\n  'fl_start_t' => 'Hora',\n  'fl_dest' => 'Destino',\n  'fl_dest_t' => 'Llegada',\n  'fl_back_t' => 'Retorno',\n  'fl_back_in' => 'Tiempo restante',\n  'fl_order' => 'Orden',\n  'fl_get_to' => '(I)',\n  'fl_get_to_ttl' => 'Ida',\n  'fl_back_to' => '(V)',\n  'fl_back_to_ttl' => 'Vuelta',\n  'fl_associate' => 'Alianza de combate',\n  'fl_noslotfree' => '¡No hay comandantes disponibles!',\n  'fl_notback' => '¡La flota no puede regresar!',\n  'fl_onlyyours' => 'Solo puedes retirar tus propias flotas',\n  'fl_isback' => 'Flota en retorno',\n  'fl_sback' => 'Retirar',\n  'fl_error' => 'Error',\n  'fl_new_miss' => 'Nueva misión: seleccionar naves',\n  'fl_fleet_typ' => 'Tipo de nave',\n  'fl_fleet_disp' => 'Cantidad',\n  'fl_noplanetrow' => '¡Error del sistema!',\n  'fl_fleetspeed' => 'Velocidad: ',\n  'fl_selmax' => 'máx.',\n  'fl_sur' => 'de',\n  'fl_continue' => 'Continuar',\n  'fl_noships' => 'No hay naves en órbita',\n  'fl_unselectall' => 'Deseleccionar',\n  'fl_selectall' => 'Todas las naves',\n  'fl_orbiting' => 'En órbita',\n  'fl_to_fly' => 'Enviar',\n  'fl_no_flying_fleets' => 'No hay flotas en vuelo',\n  'fl_floten1_ttl' => 'Seleccionar destino',\n  'fl_noenought' => '¡Naves insuficientes!',\n  'fl_speed' => 'Velocidad',\n  'fl_planet' => 'Planeta',\n  'fl_ruins' => 'Escombros',\n  'fl_moon' => 'Luna',\n  'fl_dist' => 'Distancia',\n  'fl_fltime' => 'Duración (solo ida)',\n  'fl_time_go' => 'Llegada al destino',\n  'fl_time_back' => 'Regreso al origen',\n  'fl_deute_need' => 'Consumo de combustible',\n  'fl_speed_max' => 'Velocidad máxima',\n  'fl_shortcut' => 'Marcadores',\n  'fl_shortlnk' => 'Editar marcadores',\n  'fl_shrtcup1' => '(P)',\n  'fl_shrtcup2' => '(E)',\n  'fl_shrtcup3' => '(L)',\n  'fl_planettype1' => 'Planeta',\n  'fl_planettype2' => 'Escombros',\n  'fl_planettype3' => 'Luna',\n  'fl_myplanets' => 'Mis planetas',\n  'fl_nocolonies' => 'sin colonias',\n  'fl_noacss' => 'No hay invitaciones de alianzas',\n  'fl_grattack' => 'Alianzas de combate',\n  'fl_allressources' => 'Todos los recursos',\n  'fl_space_left' => 'Capacidad de carga',\n  'fl_attack_error' => array(\n    ATTACK_ALLOWED => 'Flota enviada',\n    ATTACK_NO_TARGET => 'Destino no existe',\n    ATTACK_OWN => 'No puedes atacar tus planetas',\n    ATTACK_WRONG_MISSION => 'Misión no válida para este destino',\n    ATTACK_NO_ALLY_DEPOSIT => 'No hay depósito de alianza',\n    ATTACK_NO_DEBRIS => 'No hay campo de escombros',\n    ATTACK_VACATION => 'Jugador en modo vacaciones',\n    ATTACK_SAME_IP => '¡PROTECCIÓN MULTICUENTA!<br>No puedes interactuar con tu misma IP',\n    ATTACK_BUFFING => '¡Transferencia de recursos prohibida!',\n    ATTACK_ADMIN => 'No puedes atacar administradores',\n    ATTACK_NOOB => 'Este jugador está bajo protección',\n    ATTACK_OWN_VACATION => 'Estás en modo vacaciones',\n    ATTACK_NO_SILO => 'Silo de misiles insuficiente',\n    ATTACK_NO_MISSILE => 'No hay misiles',\n    ATTACK_NO_FLEET => 'Flota vacía',\n    ATTACK_NO_SLOTS => 'Sin comandantes',\n    // ATTACK_NO_SHIP => 'Naves insuficientes',\n    ATTACK_NO_RECYCLERS => 'Sin recicladores',\n    ATTACK_NO_SPIES => 'Sin sondas de espionaje',\n    ATTACK_NO_COLONIZER => 'Sin colonizador',\n    ATTACK_MISSILE_TOO_FAR => 'Rango de misiles excedido',\n    ATTACK_WRONG_STRUCTURE => 'Objetivo no válido para misiles',\n    ATTACK_NO_FUEL => 'Combustible insuficiente',\n    ATTACK_NO_RESOURCES => 'Recursos insuficientes',\n    ATTACK_NO_ACS => 'Alianza no existe',\n    ATTACK_ACS_MISSTARGET => 'Destino no coincide',\n    ATTACK_WRONG_SPEED => 'Velocidad incorrecta',\n    ATTACK_ACS_TOO_LATE => 'Flota demasiado lenta',\n    ATTACK_BASHING => 'Límite de ataques diarios alcanzado',\n    ATTACK_BASHING_WAR_DELAY => 'Guerra pendiente con esta alianza',\n    ATTACK_ACS_WRONG_TARGET => 'Destino no coincide',\n    ATTACK_SAME => 'Origen y destino iguales',\n    ATTACK_RESOURCE_FORBIDDEN => 'Recursos no permitidos',\n    ATTACK_TRANSPORT_EMPTY => 'Flota de transporte vacía',\n    ATTACK_SPIES_LONLY => 'Solo sondas de espionaje',\n    ATTACK_TOO_FAR => 'Distancia excedida',\n    ATTACK_OVERLOADED => 'Sobrecarga de carga',\n    ATTACK_MISSION_ABSENT => 'Misión no existe',\n    ATTACK_WRONG_UNIT => 'Unidad incorrecta',\n    ATTACK_ZERO_SPEED => 'Estructura orbital no voladora',\n    ATTACK_SHIP_COUNT_WRONG => 'Cantidad de naves inválida',\n    ATTACK_RESOURCE_COUNT_WRONG => 'Cantidad de recursos inválida',\n    ATTACK_MORATORIUM => 'Moratorio activo',\n    ATTACK_CHILD_PROTECTION => '¡Día de Protección Infantil! No puedes atacar jugadores más débiles',\n    ATTACK_ACS_MAX_FLEETS => 'Límite de flotas en alianza alcanzado',\n  ),\n\n  'fl_fleet_err' => '¡Error!',\n  'fl_unknow_target' => '<li>¡Destino desconocido!</li>',\n  'fl_nodebris' => '<li>¡No hay escombros!</li>',\n  'fl_nomoon' => '<li>¡No hay luna!</li>',\n  'fl_vacation_ttl' => 'Modo vacaciones',\n  'fl_vacation_pla' => '¡Jugador en vacaciones!',\n  'fl_noob_title' => 'Protección de novatos',\n  'fl_noob_mess_n' => '¡Jugador bajo protección!</li>',\n  'fl_bad_planet01' => '<li>¡Planeta habitado!</li>',\n  'fl_colonized' => '<li>¡Planeta ya colonizado!</li>',\n  'fl_dont_stay_here' => '¡No puedes aterrizar en planeta enemigo!',\n  'fl_no_allydeposit' => '¡No hay depósito de alianza!',\n  'fl_no_self_attack' => '<li>¡No puedes atacarte a ti mismo!</li>',\n  'fl_no_self_spy' => '<li>¡No puedes espiarte a ti mismo!</li>',\n  'fl_only_stay_at_home' => '<li>¡No puedes estacionar flotas enemigas!</li>',\n  'fl_cheat_speed' => '¡Intento de hackeo reportado!',\n  'fl_cheat_origine' => '¡Intento de hackeo reportado!',\n  'fl_limit_planet' => '<li>¡Planeta incorrecto!</li>',\n  'fl_limit_system' => '<li>¡Sistema incorrecto!</li>',\n  'fl_limit_galaxy' => '<li>¡Galaxia incorrecta!</li>',\n  'fl_ownpl_err' => '<li>¡No puedes atacar tu planeta!</li>',\n  'fl_no_planet_type' => '<li>¡Destino no válido!</li>',\n  'fl_fleet_err_pl' => '<li>¡Error en destino!</li>',\n  'fl_bad_mission' => '<li>¡Misión no válida!</li>',\n  'fl_no_fleetarray' => '<li>¡Error en flota!</li>',\n  'fl_noenoughtgoods' => '<li>¡Flota de transporte vacía!</li>',\n  'fl_expe_notech' => '<li>¡Necesitas Astrocartografía!</li>',\n  'fl_expe_max' => '<li>¡Límite de expediciones alcanzado!</li>',\n  'fl_no_deuterium' => 'Combustible insuficiente. Faltan: ',\n  'fl_no_resources' => 'Recursos insuficientes para carga.',\n  'fl_nostoragespa' => '¡Capacidad de carga insuficiente! Faltan: ',\n  'fl_fleet_send' => 'Flota enviada',\n  'fl_expe_warning' => '¡Advertencia! Puedes perder naves en expediciones.',\n  'fl_not_enough_fuel' => '¡ERROR! Capacidad insuficiente incluso para combustible. Añade naves de carga o reduce velocidad.',\n  'fl_expe_staytime' => 'tiempo de espera',\n  'fl_expe_hours' => 'horas',\n  'fl_adm_attak' => 'No puedes atacar administradores',\n  'fl_warning' => 'Advertencia',\n  'fl_page0_hint' => '<ul><li>Usa \"Marcadores\" en el menú para gestionarlos<li>Haz clic en nombres de alianzas para unirte</ul>',\n  'fl_page1_hint' => '<ul><li>El tiempo de vuelo incluye despegue/aterrizaje<li>Usa \"Marcadores\" para gestionar favoritos<li>Haz clic en alianzas para unirte</ul>',\n  'fl_page5_hint' => '<ul><li>Marca recursos a transportar<li>Las casillas superiores seleccionan todos los recursos<li>Solo naves de carga participan<li>Las naves se cargan por capacidad descendente<li>La capacidad total se muestra al final</ul>',\n  'fl_err_no_ships' => 'No hay naves seleccionadas. Regresa y selecciona naves.',\n  'fl_shrtcup' => array(\n    PT_PLANET => '(P)',\n    PT_DEBRIS => '(E)',\n    PT_MOON => '(L)',\n  ),\n\n  'fl_planettype' => array(\n    PT_PLANET => 'Planeta',\n    PT_DEBRIS => 'Escombros',\n    PT_MOON => 'Luna',\n  ),\n\n  'fl_aks_invite_message_header' => 'Invitación ACS',\n  'fl_aks_invite_message' => '<span class=\"error\">%s te invitó a un ACS. Únete en \"Flotas\".</span>',\n  'fl_aks_player_invited' => '<span class=\"success\">%s fue invitado.</span>',\n  'fl_aks_player_invited_already' => '<span class=\"success\">%s ya estaba invitado.</font>',\n  'fl_aks_player_error' => '<span class=\"error\">Error. %s no encontrado.</span>',\n  'fl_aks_already_in_aks' => '¡Flota ya en ACS!',\n  'fl_aks_adding_error' => 'Error al añadir jugador:<br>%s',\n  'fl_aks_hack_wrong_fleet' => '¡Intento de hackeo reportado!',\n  'fl_aks_too_slow' => 'Flota demasiado lenta para ACS',\n  'fl_aks_too_power' => 'ACS sería demasiado poderoso',\n  'fl_aks_full' => 'Límite de flotas en ACS alcanzado',\n  'fl_fleet_not_exists' => 'Flota no encontrada',\n  'fl_multi_ip_protection' => '¡PROTECCIÓN MULTICUENTA!<br>No puedes enviar recursos a tu misma IP',\n  'fl_load_cargo' => 'Cargado',\n  'fl_rest_on_planet' => 'Restante',\n  'fl_none_resources' => 'Limpiar',\n  'fl_planet_resources' => 'Recursos planetarios',\n  'fl_fleet_data' => 'Datos de flota',\n  'flt_gather_report' => 'Informe de misión',\n  'flt_report' => 'Informe',\n  'flt_no_transports' => 'sin transportes',\n  'flt_no_fuel' => 'sin combustible',\n  'fl_id' => 'ID',\n  'fl_ressources' => 'Recursos',\n\n  'fl_fuel_on_planet' => 'Combustible disponible',\n\n  'flt_aks_players_in_aks' => 'Jugadores en ACS',\n  'flt_aks_player_invite' => 'Invitar a ACS',\n  'flt_aks_player_invite_do' => 'Invitar',\n  'flt_aks_player_same' => 'No puedes invitar al objetivo',\n  'flt_aks_error_too_much_players' => 'Límite de 5 jugadores en ACS',\n\n  'flt_return_all' => 'Retornar seleccionadas',\n  'flt_return_fleet' => 'Retornar flota',\n\n  'flt_acs_prefix' => 'ACS ',\n\n  'ship_consumption_short' => 'Consumo',\n  'ship_capacity_short' => 'Carga',\n\n  'ship_is_satellite' => 'Satélite',\n);"
  },
  {
    "path": "language/es/infos.mo.php",
    "content": "<?php\n\n/*\n#############################################################################\n#  Filename: infos.mo\n#  Project: SuperNova.WS\n#  Website: http://www.supernova.ws\n#  Description: Juego de estrategia espacial masivo multijugador en línea\n#\n#  Copyright © 2009-2018 Gorlum for Project \"SuperNova.WS\"\n#############################################################################\n*/\n\n/**\n*\n* @package language\n* @system [Spanish]\n* @version 46d0\n*\n*/\n\n/**\n* NO CAMBIAR\n*/\n\nif (!defined('INSIDE')) die();\n\n$a_lang_array = array(\n  'wiki_title' => 'NovaPedia',\n\n  'wiki_char_nominal' => 'Nominales',\n  'wiki_char_actual' => 'Reales',\n\n  'wiki_ship_engine_header' => 'Características de los motores',\n\n  'wiki_ship_header' => 'Características de transporte',\n  'wiki_ship_speed' => 'Velocidad',\n  'wiki_ship_consumption' => 'Consumo de deuterio',\n  'wiki_ship_capacity' => 'Capacidad de carga',\n  'wiki_ship_hint' => '<li>La velocidad y consumo reales se muestran considerando todos los bonos: tecnologías, mercenarios, etc.</li>',\n\n  'wiki_combat_header' => 'Características de combate',\n  'wiki_combat_attack' => 'Potencia de disparo, puntos',\n  'wiki_combat_shield' => 'Capacidad de escudos, puntos',\n  'wiki_combat_armor' => 'Integridad estructural, puntos',\n\n  'wiki_combat_volley_header' => 'Fuego de salva',\n  'wiki_combat_volley_to' => 'Unidades impactadas',\n  'wiki_combat_volley_from' => 'Unidades perdidas',\n  'info' => array(\n    STRUC_MINE_METAL => array(\n      'description' => 'Principal proveedor de materias primas para la construcción de estructuras portantes de edificios y naves. El metal es la materia prima más barata, pero se requiere en mayores cantidades que otros recursos. Su producción consume menos energía. A mayor nivel de las minas, más profundas son. En la mayoría de los planetas, el metal se encuentra a grandes profundidades, lo que permite extraer más en minas más profundas. Sin embargo, minas más grandes requieren más energía.',\n      'description_short' => 'Principal proveedor de materias primas para estructuras portantes de edificios y naves.',\n    ),\n\n    STRUC_MINE_CRYSTAL => array(\n      'description' => 'La síntesis de cristales requiere aproximadamente el doble de energía que la extracción de una cantidad equivalente de metal, por lo que es más valioso. Los cristales son componentes esenciales en computadoras modernas y motores de curvatura. Se necesitan para casi todas las naves y edificios. Mejorar el sintetizador aumenta la producción de cristales.',\n      'description_short' => 'Principal proveedor de materias primas para sistemas informáticos y motores de curvatura.',\n    ),\n\n    STRUC_MINE_DEUTERIUM => array(\n      'description' => 'El deuterio es hidrógeno pesado. Al igual que en las minas, los depósitos más grandes se encuentran en las profundidades del mar. Mejorar el sintetizador permite explotar estos depósitos profundos. El deuterio se usa como combustible para naves, en la mayoría de las investigaciones, para escanear galaxias y en la falange sensorial.',\n      'description_short' => 'Extrae una pequeña fracción de deuterio del agua del planeta.',\n    ),\n\n    STRUC_MINE_SOLAR => array(\n      'description' => 'Para alimentar minas y sintetizadores, se necesitan enormes plantas de energía solar. A mayor número de plantas, más superficie se cubre con paneles solares que convierten la luz en electricidad. Las plantas solares son la base del suministro energético del planeta.',\n      'description_short' => 'Genera energía a partir de la luz solar. La energía es necesaria para la mayoría de los edificios.',\n    ),\n\n    STRUC_MINE_FUSION => array(\n      'description_short' => 'Genera energía fusionando dos átomos de hidrógeno pesado para formar helio',\n      'description' => 'Las plantas de fusión nuclear combinan dos átomos de hidrógeno pesado bajo alta presión y temperatura para formar helio, liberando energía (41,32*10^-13 J por reacción). A mayor nivel de la planta, más complejos son los procesos de fusión, generando más energía.<br><br>Fórmula de producción:<br>30 * [nivel PF] * (1,05 + [nivel de tecnología energética] * 0,01) ^ [nivel PF]<br>La producción también puede aumentarse mejorando la tecnología energética.',\n    ),\n\n    STRUC_FACTORY_ROBOT => array(\n      'description' => 'Proporciona mano de obra básica para la construcción de infraestructura planetaria. Cada nivel de la fábrica aumenta la velocidad de construcción de edificios.',\n      'description_short' => 'Fabrica máquinas y mecanismos para la construcción de infraestructura planetaria. Cada nivel aumenta la velocidad de construcción.',\n    ),\n\n    STRUC_FACTORY_NANO => array(\n      'description' => 'Las nanofábricas representan la evolución final de las fábricas robóticas. Su único equipo son los nanoensambladores, que manipulan moléculas y átomos individuales, permitiendo crear materiales con propiedades personalizadas. Aunque pueden producir cualquier estructura, muchos objetos siguen siendo más económicos de fabricar de manera tradicional. Cada nivel de nanofábrica reduce a la mitad el tiempo de construcción de edificios, defensas y naves.',\n      'description_short' => 'Equipada con nanoensambladores especializados que construyen objetos a nivel molecular.',\n      'effect' => 'Cada nivel de nanofábrica duplica la velocidad de construcción.',\n    ),\n\n    STRUC_FACTORY_HANGAR => array(\n      'description' => 'En los astilleros se producen todo tipo de naves y defensas. A mayor nivel, más rápida es la construcción de naves complejas. La integración de fábricas de nanitos simplifica las cadenas de producción, mejorando drásticamente la productividad.',\n      'description_short' => 'Produce naves espaciales, estructuras orbitales y defensas.',\n    ),\n\n    STRUC_STORE_METAL => array(\n      'description' => 'Almacén para el mineral extraído. Cuanto más grande es, más mineral puede almacenar. Si el almacén está lleno, la extracción de metal se detiene. El almacén se usa EXCLUSIVAMENTE para la producción: la cantidad total de metal en el planeta (por ejemplo, al transportar recursos desde otro planeta) puede exceder la capacidad máxima del almacén.',\n      'description_short' => 'Almacén para el mineral antes de su posterior procesamiento.',\n    ),\n\n    STRUC_STORE_CRYSTAL => array(\n      'description' => 'Este almacén guarda materiales semielaborados para la síntesis de cristales. Cuanto más grande es, más material puede almacenar. Si el almacén está lleno, la síntesis de cristales se detiene. El almacén se usa EXCLUSIVAMENTE para la producción: la cantidad total de cristal en el planeta puede exceder la capacidad máxima del almacén.',\n      'description_short' => 'Almacén para materiales semielaborados de cristal antes de su procesamiento final.',\n    ),\n\n    STRUC_STORE_DEUTERIUM => array(\n      'description' => 'Tanques especiales para almacenar agua pesada, generalmente ubicados cerca de los puertos espaciales. Cuanto más grandes son, más agua pesada pueden contener. Si están llenos, la extracción de deuterio se detiene. Los tanques se usan EXCLUSIVAMENTE para la producción: la cantidad total de deuterio en el planeta puede exceder su capacidad máxima.',\n      'description_short' => 'Tanques para almacenar agua pesada antes de la extracción de deuterio.',\n    ),\n\n    STRUC_LABORATORY => array(\n      'description' => 'Para investigar nuevas tecnologías, se necesita una estación de investigación. El nivel del laboratorio determina la velocidad de desarrollo tecnológico. Cuanto más avanzado es el laboratorio, más tecnologías pueden investigarse. Todos los científicos disponibles se concentran en un planeta durante la investigación y luego difunden los conocimientos adquiridos.',\n      'description_short' => 'Aquí se investigan nuevas tecnologías.',\n    ),\n\n    STRUC_TERRAFORMER => array(\n      'description' => 'A medida que los planetas se urbanizan, el espacio útil se vuelve escaso. El terraformador soluciona esto transformando territorios enteros en áreas habitables, consumiendo enormes cantidades de energía. Genera nanitos especializados que optimizan constantemente el suelo.',\n      'description_short' => 'Transforma territorios inútiles en zonas edificables.',\n    ),\n\n    STRUC_ALLY_DEPOSIT => array(\n      'description' => 'El almacén de la alianza suministra combustible a flotas aliadas que ayudan en la defensa orbital. A mayor nivel, más deuterio puede enviarse a las flotas.',\n      'description_short' => 'Proporciona combustible a flotas aliadas en órbita.',\n    ),\n\n    STRUC_LABORATORY_NANO => array(\n      'description' => 'Reduce a la mitad el tiempo de investigación.',\n      'description_short' => 'Equipado con computadoras cuánticas y nanoensambladores de última generación que aceleran cualquier investigación.',\n    ),\n\n    STRUC_MOON_STATION => array(\n      'description' => 'La Luna carece de atmósfera, por lo que requiere una base lunar para su colonización. Cada nivel aumenta el área habitable (3 sectores por nivel, hasta cubrir toda la Luna). La base ocupa 1 sector.',\n      'description_short' => 'Proporciona atmósfera, gravedad y calor en la Luna.',\n    ),\n\n    STRUC_MOON_PHALANX => array(\n      'description' => 'Sensores de alta frecuencia que escanean todo el espectro electromagnético. Detectan movimientos de flotas en planetas distantes (alcance: (nivel de falange)^2 -1 sistemas). Consume deuterio (1000*nivel por escaneo).',\n      'description_short' => 'Sensores avanzados para rastrear flotas enemigas.',\n    ),\n\n    STRUC_MOON_GATE => array(\n      'description' => 'Portal de teletransportación masiva que mueve flotas enteras sin demoras. Requiere 1 hora entre saltos para evitar sobrecalentamiento. No transporta recursos.',\n      'description_short' => 'Teletransporta flotas entre lunas instantáneamente.',\n    ),\n\n    STRUC_SILO => array(\n      'description' => 'Silos para misiles. Cada nivel permite almacenar +4 misiles interplanetarios o +12 interceptores (1 misil interplanetario = 3 espacios de interceptor).',\n      'description_short' => 'Almacena y lanza misiles defensivos/ofensivos.',\n    ),\n\n    TECH_SPY => array(\n      'description_short' => 'Esta tecnología obtiene datos de otros planetas.',\n      'description' => 'Mejora los sensores de espionaje. A mayor nivel, más información se obtiene sobre objetivos. La diferencia de niveles con el enemigo es crucial: ventaja en espionaje revela más datos y reduce la probabilidad de ser detectado. También mejora el rastreo de flotas enemigas.',\n    ),\n\n        TECH_COMPUTER => array(\n      'description' => 'Amplía la capacidad computacional para gestionar más flotas simultáneamente. Cada nivel permite controlar +1 flota adicional. Esencial para comerciantes y atacantes.',\n      'description_short' => 'Cada nivel permite comandar +1 flota simultánea.',\n    ),\n\n    TECH_WEAPON => array(\n      'description' => 'Mejora los sistemas de armamento. Cada nivel aumenta un 10% el daño de las unidades militares.',\n      'description_short' => '+10% de potencia de fuego por nivel.',\n    ),\n\n    TECH_SHIELD => array(\n      'description' => 'Optimiza generadores de escudos. Cada nivel mejora un 10% la absorción de daño.',\n      'description_short' => '+10% de eficiencia en escudos por nivel.',\n    ),\n\n    TECH_ARMOR => array(\n      'description' => 'Desarrolla aleaciones avanzadas para blindaje. Cada nivel aumenta un 10% la resistencia estructural.',\n      'description_short' => '+10% de blindaje por nivel.',\n    ),\n\n    TECH_ENERGY => array(\n      'description' => 'Investiga sistemas de transmisión y almacenamiento energético para otras tecnologías.',\n      'description_short' => 'Mejora la producción en plantas de fusión.',\n    ),\n\n    TECH_HYPERSPACE => array(\n      'description' => 'Permite desarrollar motores hiperespaciales más eficientes.',\n      'description_short' => 'Base para motores de hiperpropulsión.',\n    ),\n\n    // Sección de motores\n    TECH_ENGINE_CHEMICAL => array(\n      'description' => 'Motores básicos que usan reacciones químicas. Económicos pero poco eficientes. Cada nivel aumenta un 10% la velocidad de: Transporte Pequeño (hasta nivel 5 de motores iónicos), Transporte Grande, Reciclador, Sonda Espía y Cazador Ligero.',\n      'description_short' => '+10% de velocidad en naves básicas por nivel.',\n    ),\n\n    TECH_ENGINE_ION => array(\n      'description' => 'Motores que aceleran iones con campos electromagnéticos. Consumen más deuterio pero son más rápidos. Cada nivel otorga +20% de velocidad a: Transporte Pequeño (nivel 5+), Supertransporte, Colonizador, Cazador Pesado, Destructor y Bombardero (hasta nivel 8 de hiperpropulsión).',\n      'description_short' => '+20% de velocidad en naves intermedias por nivel.',\n    ),\n\n    TECH_ENGINE_HYPER => array(\n      'description' => 'Comprime el espacio-tiempo alrededor de la nave para viajes FTL. Cada nivel da +30% de velocidad a: Hipertransporte, Crucero, Bombardero (nivel 8+), Acorazado, Estrella de la Muerte y Crucero \"Supernova\".',\n      'description_short' => '+30% de velocidad en naves avanzadas por nivel.',\n    ),\n\n    // Tecnologías de armas\n    TECH_LASER => array(\n      'description' => 'Desarrolla láseres coherentes para cortar blindajes. Requisito para otras tecnologías armamentísticas.',\n      'description_short' => 'Armas de energía focalizada.',\n    ),\n\n    TECH_ION => array(\n      'description' => 'Haces de iones acelerados que dañan sistemas electrónicos y escudos.',\n      'description_short' => 'Armas EMP avanzadas.',\n    ),\n\n    TECH_PLASMA => array(\n      'description' => 'Evolución de la tecnología iónica que dispara plasma supercaliente. Devastador contra estructuras.',\n      'description_short' => 'El armamento más destructivo.',\n    ),\n\n    // Sección de naves\n    SHIP_CARGO_SMALL => array(\n      'description' => 'Nave ágil con capacidad para 5,000 unidades. Poca defensa. Al alcanzar Motores Iónicos nivel 5, se actualiza su sistema de propulsión.',\n      'description_short' => 'Transportador rápido para recursos.',\n    ),\n\n    SHIP_CARGO_BIG => array(\n      'description' => 'Versión ampliada del transporte pequeño. Ideal para saqueos pero vulnerable sin escolta.',\n      'description_short' => 'Mayor capacidad que el transporte pequeño.',\n    ),\n\n    SHIP_CARGO_SUPER => array(\n      'description' => 'Gigante con motores iónicos. Lento y costoso pero con escudos potentes y capacidad masiva.',\n      'description_short' => 'Barcaza de carga pesada con escudos.',\n    ),\n\n    SHIP_CARGO_HYPER => array(\n      'description' => 'Colosal nave del tamaño de una luna pequeña. Solo sus motores hiperespaciales pueden moverla cuando está cargada. Consume cantidades astronómicas de deuterio.',\n      'description_short' => 'Transporte masivo para imperios avanzados.',\n    ),\n\n    SHIP_SMALL_FIGHTER_LIGHT => array(\n      'description' => 'Nave rápida y económica con escudos y capacidad de carga limitados. Presente en casi todos los planetas.',\n      'description_short' => 'Unidad básica de combate.',\n    ),\n\n    SHIP_SMALL_FIGHTER_HEAVY => array(\n      'description' => 'Evolución del cazador ligero con motores iónicos, mejor blindaje y mayor poder de fuego. Precursor de la tecnología de cruceros.',\n      'description_short' => 'Versión mejorada del cazador ligero.',\n    ),\n\n    SHIP_MEDIUM_DESTROYER => array(\n      'description' => 'Especializado en destruir defensas medianas. 3 veces más resistente que un cazador pesado y el doble de daño.',\n      'description_short' => 'Dominó el campo de batalla durante siglos.',\n    ),\n\n    SHIP_LARGE_CRUISER => array(\n      'description' => 'Columna vertebral de las flotas. Combina potencia de fuego, velocidad y capacidad de carga.',\n      'description_short' => 'Nave versátil para múltiples roles.',\n    ),\n\n    SHIP_COLONIZER => array(\n      'description' => 'Nave fortificada que se desmonta al colonizar nuevos planetas. El límite de colonias depende de la configuración del universo.',\n      'description_short' => 'Para establecer nuevas colonias.',\n    ),\n\n    SHIP_RECYCLER => array(\n      'description' => 'Equipado con escudos especiales para recolectar escombros espaciales. Capacidad limitada a 20,000 unidades.',\n      'description_short' => 'Recolecta recursos de campos de escombros.',\n    ),\n\n    SHIP_SPY => array(\n      'description' => 'Pequeña y veloz, pero sin armadura ni escudos. Fácil de destruir si es detectada.',\n      'description_short' => 'Obtiene información de planetas enemigos.',\n    ),\n\n    SHIP_LARGE_BOMBER => array(\n      'description' => 'Diseñado específicamente para destruir defensas planetarias con bombas de plasma. Mejora sus motores al investigar Hiperpropulsión nivel 8.',\n      'description_short' => 'Asedia estructuras defensivas.',\n    ),\n\n    SHIP_SATTELITE_SOLAR => array(\n      'description' => 'Plataformas orbitales que transmiten energía solar a la superficie. Vulnerables en combate.',\n      'description_short' => 'Generadores de energía en órbita.',\n    ),\n\n    SHIP_LARGE_DESTRUCTOR => array(\n      'description' => 'El rey de las naves militares. Sus torretas multiflango tienen 99% de precisión. Movilidad limitada y alto consumo de deuterio.',\n      'description_short' => 'La máxima expresión de poderío militar.',\n    ),\n\n    SHIP_HUGE_DEATH_STAR => array(\n      'description' => 'Armada con un cañón de gravitones capaz de destruir lunas. Requiere recursos astronómicos para su construcción.',\n      'description_short' => 'Arma definitiva de destrucción masiva.',\n    ),\n\n    SHIP_LARGE_BATTLESHIP => array(\n      'description' => 'Crucero tecnológicamente avanzado con armas láser de largo alcance. Bajo consumo de combustible.',\n      'description_short' => 'Especialista en interceptar flotas.',\n    ),\n\n    SHIP_HUGE_SUPERNOVA => array(\n      'description_short' => 'El buque insignia de la flota imperial.',\n      'description' => 'El Crucero \"Supernova\" justifica su enorme costo con un poder de fuego devastador y defensas avanzadas. Puede destruir flotas enteras solo.',\n    ),\n\n    // Defensas\n    UNIT_DEF_TURRET_MISSILE => array(\n      'description' => 'Defensa básica y económica. Efectiva contra flotas pequeñas. Se autorepara hasta 70% después de batallas.',\n      'description_short' => 'Torreta de misiles balísticos.',\n    ),\n\n    UNIT_DEF_TURRET_LASER_SMALL => array(\n      'description_short' => 'Láseres focales que causan más daño que armas balísticas.',\n      'description' => 'Solución contra naves mejor blindadas. Mejor relación costo-beneficio que las torretas de misiles. Autoreparación hasta 70%.',\n    ),\n\n    UNIT_DEF_TURRET_LASER_BIG => array(\n      'description' => 'Versión mejorada del láser pequeño con materiales avanzados y sistemas de focalización. Autoreparación hasta 70%.',\n      'description_short' => 'Láser pesado de alta energía.',\n    ),\n\n    UNIT_DEF_TURRET_GAUSS => array(\n      'description_short' => 'Acelera proyectiles de varias toneladas con electromagnetismo.',\n      'description' => 'Tecnología antigua readaptada. Los proyectiles pueden atravesar blindajes modernos. Autoreparación hasta 70%.',\n    ),\n\n    UNIT_DEF_TURRET_ION => array(\n      'description_short' => 'Desestabiliza escudos y daña sistemas electrónicos.',\n      'description' => 'Evolución de las armas EMP. Inútil contra blancos sin electrónica. Alto consumo energético. Autoreparación hasta 70%.',\n    ),\n\n    UNIT_DEF_TURRET_PLASMA => array(\n      'description' => 'Fusión de tecnología láser e iónica. Dispara bolas de plasma a millones de grados. El arma defensiva más poderosa. Autoreparación hasta 70%.',\n      'description_short' => 'Sintetiza lo mejor de ambas tecnologías.',\n    ),\n\n    // Escudos planetarios\n    UNIT_DEF_SHIELD_SMALL => array(\n      'description' => 'Genera un campo de fuerza alrededor del planeta. Solo se puede construir un escudo pequeño por planeta.',\n      'description_short' => 'Protección básica contra ataques.',\n    ),\n\n    UNIT_DEF_SHIELD_BIG => array(\n      'description' => 'Escudo mejorado que absorbe más energía que la versión pequeña.',\n      'description_short' => 'Versión reforzada del escudo pequeño.',\n    ),\n\n    UNIT_DEF_SHIELD_PLANET => array(\n      'description' => 'La mejor protección disponible para tus planetas.',\n      'description_short' => 'Defensa planetaria definitiva.',\n    ),\n\n    // Misiles\n    UNIT_DEF_MISSILE_INTERCEPTOR => array(\n      'description' => 'Destruye misiles interplanetarios enemigos. 1 interceptor = 1 misil enemigo.',\n      'description_short' => 'Defensa antimisiles.',\n    ),\n\n    UNIT_DEF_MISSILE_INTERPLANET => array(\n      'description_short' => 'Destruye estructuras defensivas enemigas.',\n      'description' => 'Aniquila defensas planetarias. Las estructuras destruidas no se autoreparan.',\n    ),\n\n    // Mercenarios\n    MRC_TECHNOLOGIST => array(\n      'description' => 'Experto en optimización de recursos. Trabaja con metalúrgicos, químicos y energéticos para mejorar la producción.',\n      'description_short' => 'Mejora la eficiencia de producción.',\n      'effect' => 'Bonus a minería, síntesis y generación de energía por nivel.',\n    ),\n\n    MRC_ENGINEER => array(\n      'description' => 'Constructor genéticamente mejorado con fuerza sobrehumana y mente brillante. Puede edificar ciudades enteras solo.',\n      'description_short' => 'Maestro de la construcción.',\n      'effect' => 'Acelera construcción de edificios/naves + slots adicionales por nivel.',\n    ),\n\n\n    MRC_FORTIFIER => array(\n      'description' => 'Ingeniero militar especializado en sistemas defensivos. Acelera la construcción de estructuras de defensa planetaria.',\n      'description_short' => 'Experto en fortificaciones planetarias.',\n      'effect' => 'Aumenta velocidad de construcción de defensas +10% ataque/defensa por nivel. +1 slot de cola defensiva por nivel.',\n    ),\n\n    MRC_STOCKMAN => array(\n      'description' => 'Especialista en almacenamiento que optimiza la capacidad de los depósitos más allá de sus límites teóricos.',\n      'description_short' => 'Maestro de la gestión de almacenes.',\n      'effect' => 'Aumenta capacidad de almacenamiento por nivel.',\n    ),\n\n    MRC_SPY => array(\n      'description' => 'Operativo encubierto con identidades múltiples. Experto en camuflar instalaciones y flotas.',\n      'description_short' => 'Espía maestro del engaño.',\n      'effect' => 'Mejora nivel de espionaje por nivel.',\n    ),\n\n    MRC_ACADEMIC => array(\n      'description' => 'Miembro del Gremio Tecnocrático. Supera incluso a los Constructores en avances científicos.',\n      'description_short' => 'Investigador de élite.',\n      'effect' => 'Acelera velocidad de investigación por nivel.',\n    ),\n\n    MRC_ADMIRAL => array(\n      'description' => 'Veterano de guerra y estratega brillante. Coordina flotas complejas con eficiencia letal.',\n      'description_short' => 'Comandante supremo de flotas.',\n      'effect' => 'Mejora armadura, escudos y ataque de naves por nivel.',\n    ),\n\n    MRC_COORDINATOR => array(\n      'description' => 'Experto en sistemas de control de flotas. Maximiza la eficiencia operativa.',\n      'description_short' => 'Optimizador de operaciones navales.',\n      'effect' => '+1 flota adicional por nivel.',\n    ),\n\n    MRC_NAVIGATOR => array(\n      'description' => 'Genio de la astro-navegación. Domina las leyes del espacio warp y todos los sistemas de propulsión.',\n      'description_short' => 'Navegante de precisión absoluta.',\n      'effect' => 'Aumenta velocidad de las naves por nivel.',\n    ),\n\n    MRC_EMPEROR => array(\n      'description' => 'Tu asistente personal. Su precisión y meticulosidad permiten un control total sobre el imperio.',\n      'description_short' => 'Máxima autoridad ejecutiva.',\n      'effect' => 'Permite modificar las características del Emperador.',\n    ),\n\n    // Artefactos\n    ART_LHC => array(\n      'description' => 'El Colisionador de Hadrones genera un flujo de radiación de gravitones que atrae escombros orbitales.<br /><span class=warning>¡ATENCIÓN! No garantiza la creación de lunas.</span>',\n      'effect' => 'Permite reintentar creación de luna<br />1% por millón de escombros (máx. 30%)',\n    ),\n\n    ART_HOOK_SMALL => array(\n      'description' => 'Teleporta un asteroide pequeño a órbita estable, creando una luna de tamaño mínimo.',\n      'effect' => 'Crea luna de tamaño mínimo',\n    ),\n\n    ART_HOOK_MEDIUM => array(\n      'description' => 'Teleporta un asteroide mediano a órbita estable.<br /><span class=warning>¡El tamaño final es aleatorio!</span>',\n      'effect' => 'Crea luna de tamaño aleatorio',\n    ),\n\n    ART_HOOK_LARGE => array(\n      'description' => 'Teleporta un asteroide masivo a órbita estable, creando una luna de tamaño máximo.',\n      'effect' => 'Crea luna de tamaño máximo',\n    ),\n\n    ART_RCD_SMALL => array(\n      'description' => 'Kit de colonización autónoma básico. Incluye: Mina de Metal 10, Sintetizador de Cristal 10, Sintetizador de Deuterio 10, Planta Solar 14 y Fábrica de Robots 4.',\n      'effect' => 'Despliega colonia básica instantáneamente',\n    ),\n\n    ART_RCD_MEDIUM => array(\n      'description' => 'Kit de colonización intermedio. Incluye: Mina de Metal 15, Sintetizador de Cristal 15, Sintetizador de Deuterio 15, Planta Solar 20 y Fábrica de Robots 8.',\n      'effect' => 'Despliega colonia intermedia instantáneamente',\n    ),\n\n    ART_RCD_LARGE => array(\n      'description' => 'Kit de colonización avanzado. Incluye: Mina de Metal 20, Sintetizador de Cristal 20, Sintetizador de Deuterio 20, Planta Solar 25, Fábrica de Robots 10 y Nanofábrica 1.',\n      'effect' => 'Despliega colonia avanzada instantáneamente',\n    ),\n\n    ART_HEURISTIC_CHIP => array(\n      'description' => 'Cristal programable con algoritmos de investigación. No puede reutilizarse ni copiarse.',\n      'effect' => 'Reduce tiempo de investigación actual a la mitad (>1h) o completa instantáneamente (<1h)',\n    ),\n\n    ART_NANO_BUILDER => array(\n      'description' => 'Enjambre de nano-robots de construcción única. Optimizan procesos de edificación en tiempo real.',\n      'effect' => 'Reduce tiempo de construcción actual a la mitad (>1h) o completa instantáneamente (<1h)',\n    ),\n\n    // Planos\n    UNIT_PLAN_STRUC_MINE_FUSION => array(\n      'description' => 'Planos para construir Plantas de Fusión Nuclear.',\n      'effect' => 'Permite construir Plantas de Fusión',\n    ),\n\n    UNIT_PLAN_SHIP_CARGO_SUPER => array(\n      'description' => 'Planos del Supertransporte.',\n      'effect' => 'Permite construir Supertransportes',\n    ),\n\n    UNIT_PLAN_SHIP_CARGO_HYPER => array(\n      'description' => 'Planos del Hipertransporte.',\n      'effect' => 'Permite construir Hipertransportes',\n    ),\n\n    UNIT_PLAN_SHIP_DEATH_STAR => array(\n      'description' => 'Planos de la Estrella de la Muerte.',\n      'effect' => 'Permite construir Estrellas de la Muerte',\n    ),\n\n    UNIT_PLAN_SHIP_SUPERNOVA => array(\n      'description' => 'Planos del Crucero \"Supernova\".',\n      'effect' => 'Permite construir Cruceros \"Supernova\"',\n    ),\n\n    UNIT_PLAN_DEF_SHIELD_PLANET => array(\n      'description' => 'Planos del Escudo Planetario Definitivo.',\n      'effect' => 'Permite construir Escudos Planetarios',\n    ),\n\n    // Recursos\n    RES_METAL => array(\n      'description' => 'Compuesto hierro-normalizado energéticamente neutro. Material base para construcción. Se presenta en lingotes de 127L (1 tonelada).',\n      'effect' => '',\n    ),\n\n    RES_CRYSTAL => array(\n      'description' => 'Polímero termoplástico con Efecto de Conductividad Superlumínica (ECS). Esencial para computación y motores warp.',\n      'effect' => '',\n    ),\n\n    RES_DEUTERIUM => array(\n      'description' => 'Isótopo estable de hidrógeno (protón+neutrón). Combustible para reactores de fusión y motores. Almacenado en contenedores criogénicos.',\n      'effect' => '',\n    ),\n\n    RES_ENERGY => array(\n      'description' => 'Energía universal generada por plantas solares o reactores de deuterio.',\n      'effect' => '',\n    ),\n\n    RES_DARK_MATTER => array(\n      'description' => '<span class=\"dark_matter\">Materia Oscura</span> - 23% de la masa universal. Fuente energética extremadamente valiosa.',\n      'effect' => '',\n    ),\n\n    RES_METAMATTER => array(\n      'description' => 'Nuevo material con propiedades extraordinarias (descripción pendiente).',\n      'effect' => '',\n    ),    \n\n    UNIT_PLANET_DENSITY => array(\n      'description' => 'La densidad planetaria media (simplemente \"densidad\") caracteriza la composición química de la geosfera del planeta. En particular, predice con precisión las proporciones de recursos extraíbles.<br /><br />\n      \n      La geosfera planetaria se divide en:\n      <ul>\n        <li>Atmósfera (envoltura gaseosa)</li>\n        <li>Hidrosfera (envoltura líquida)</li>\n        <li>Litosfera (envoltura sólida de sustancias/elementos relativamente ligeros)</li>\n        <li>Manto (capa intermedia entre litosfera y núcleo)</li>\n        <li>Núcleo (elementos más pesados en el centro bajo alta presión)</li>\n      </ul>\n\n      El núcleo determina principalmente la densidad, conteniendo la mayor masa planetaria. La densidad de los compuestos químicos disminuye desde el centro hacia la superficie.<br /><br />\n\n      Por ejemplo, un planeta con núcleo helado:\n      <ul>\n        <li>Núcleo: Hielo de agua con trazas de otros elementos</li>\n        <li>Manto: Metano sólido</li>\n        <li>Litosfera: Hidrógeno cristalino</li>\n      </ul>\n      \n      Estos planetas suelen encontrarse en la periferia de los sistemas estelares. En raras ocasiones pueden orbitar cerca de una estrella, desarrollando una atmósfera temporal de hidrógeno/helio.<br /><br />\n\n      Puedes cambiar el tipo de núcleo por Materia Oscura en <a href=\"overview.php?mode=manage\" class=\"link ok\">\"Administrar planeta\"</a>. El coste depende del número de sectores planetarios (excluyendo los de Terraformadores pero incluyendo los comprados con MO).<br /><br />\n\n      Tipos de núcleos disponibles:\n      <table>\n        <tr class=\"c_c\">\n          <th rowspan=\"2\">Tipo de núcleo</th>\n          <th colspan=\"2\">Densidad (kg/m³)</th>\n          <th colspan=\"3\">Producción</th>\n          <th rowspan=\"2\">Clase</th>\n        </tr>\n        <tr class=\"c_c\">\n          <th>Mínima</th>\n          <th>Máxima</th>\n          <th>Metal</th>\n          <th>Cristal</th>\n          <th>Deuterio</th>\n        </tr>\n\n        <tr class=\"c_c\">\n          <th>Hielo de hidrógeno</th>\n          <td>250</td>\n          <td>750</td>\n          <td class=\"error\">Muy pobre</td>\n          <td class=\"warning\">Pobre</td>\n          <td class=\"positive\">Excelente</td>\n          <td class=\"error\">Excepcional</td>\n        </tr>\n\n        <tr class=\"c_c\">\n          <th>Hielo de metano</th>\n          <td>750</td>\n          <td>1250</td>\n          <td class=\"warning\">Pobre</td>\n          <td class=\"notice\">Aceptable</td>\n          <td class=\"positive\">Muy buena</td>\n          <td class=\"warning\">Raro</td>\n        </tr>\n\n        <tr class=\"c_c\">\n          <th>Hielo acuoso</th>\n          <td>1250</td>\n          <td>2000</td>\n          <td class=\"notice\">Aceptable</td>\n          <td class=\"notice\">Aceptable</td>\n          <td class=\"positive\">Buena</td>\n          <td class=\"notice\">Avanzado</td>\n        </tr>\n\n        <tr class=\"c_c\">\n          <th>Cristalino</th>\n          <td>2000</td>\n          <td>2500</td>\n          <td class=\"error\">Muy pobre</td>\n          <td class=\"positive\">Excelente</td>\n          <td class=\"warning\">Pobre</td>\n          <td class=\"error\">Excepcional</td>\n        </tr>\n\n        <tr class=\"c_c\">\n          <th>Silíceo</th>\n          <td>2500</td>\n          <td>3500</td>\n          <td class=\"warning\">Pobre</td>\n          <td class=\"positive\">Muy buena</td>\n          <td class=\"notice\">Aceptable</td>\n          <td class=\"warning\">Raro</td>\n        </tr>\n\n        <tr class=\"c_c\">\n          <th>Rocoso</th>\n          <td>3500</td>\n          <td>4750</td>\n          <td class=\"notice\">Aceptable</td>\n          <td class=\"positive\">Buena</td>\n          <td class=\"notice\">Aceptable</td>\n          <td class=\"notice\">Avanzado</td>\n        </tr>\n\n        <tr class=\"c_c\">\n          <th>Estándar</th>\n          <td>4750</td>\n          <td>5750</td>\n          <td>Estándar</td>\n          <td>Estándar</td>\n          <td>Estándar</td>\n          <td>Básico</td>\n        </tr>\n\n        <tr class=\"c_c\">\n          <th>Mineral</th>\n          <td>5750</td>\n          <td>7000</td>\n          <td class=\"positive\">Buena</td>\n          <td class=\"notice\">Aceptable</td>\n          <td class=\"notice\">Aceptable</td>\n          <td class=\"notice\">Avanzado</td>\n        </tr>\n\n        <tr class=\"c_c\">\n          <th>Olivino</th>\n          <td>7000</td>\n          <td>8250</td>\n          <td class=\"positive\">Muy buena</td>\n          <td class=\"notice\">Aceptable</td>\n          <td class=\"warning\">Pobre</td>\n          <td class=\"warning\">Raro</td>\n        </tr>\n\n        <tr class=\"c_c\">\n          <th>Metálico</th>\n          <td>8250</td>\n          <td>9500</td>\n          <td class=\"positive\">Excelente</td>\n          <td class=\"error\">Muy pobre</td>\n          <td class=\"error\">Muy pobre</td>\n          <td class=\"error\">Excepcional</td>\n        </tr>\n      </table><br />\n\n      La clase de núcleo que puedes encontrar en expediciones depende de tu nivel efectivo de Astrocartografía (incluyendo bonificaciones). Esto ayuda a equilibrar el juego para nuevos jugadores.<br /><br />\n\n      Niveles de Astrocartografía:\n      <ul>\n        <li>Nivel &lt;6: Planetas con núcleos \"Básico\" y \"Avanzado\"</li>\n        <li>Nivel 6-10: También núcleos \"Raros\"</li>\n        <li>Nivel ≥11: También núcleos \"Excepcionales\"</li>\n      </ul><br />\n\n      Eficiencia de producción (en % respecto al núcleo estándar):\n      <ul>\n        <li><span class=\"error\">Muy pobre</span>: &lt;40%</li>\n        <li><span class=\"warning\">Pobre</span>: 40-80%</li>\n        <li><span class=\"notice\">Aceptable</span>: 80-100%</li>\n        <li>Estándar: 100%</li>\n        <li><span class=\"ok\">Buena</span>: 100-300%</li>\n        <li><span class=\"ok\">Muy buena</span>: 300-400%</li>\n        <li><span class=\"ok\">Excelente</span>: &gt;400%</li>\n      </ul><br />\n\n\n',\n/*\n      '<ul>\n        <li>\n          La clase básica incluye un tipo de núcleo. Los planetas con núcleos de esta clase son <span class=\"ok\">muy comunes</span> - casi un tercio de los planetas habitables tienen este tipo de núcleo\n          <ul>\n            <li>\n              Densidad del núcleo \"Estándar\": <span class=\"zero\">más de 4750 pero menos de 5750</span> kg/m³\n            </li>\n            <li>\n              En composición química, estos planetas son muy similares a la Tierra\n            </li>\n            <li>\n              Recibieron este nombre porque los minerales están distribuidos de manera estándar - producción normal de metales, producción normal de cristales y producción normal de deuterio.\n            </li>\n          </ul>\n          . Todos los planetas iniciales tienen núcleos estándar.\n          (densidad )\n          <br />\n          Debido a la abundancia de hielo de agua-metano y grandes cantidades de hidrógeno en varios estados, los planetas helados tienen <span class=\"ok\">una producción de deuterio muy alta</span>.\n          Hay un inconveniente - debido a la pequeña cantidad de materia más densa, tienen <span class=\"error\">una producción de cristales muy baja</span> y <span class=\"error\">una producción de metal muy baja</span>. Los planetas helados son <span class=\"error\">muy raros</span>.\n        </li>\n        <li>\n          <span class=\"ok\">Planetas silicatos</span> (densidad <span class=\"zero\">más de 2000 pero menos de 3250</span> kg/m³) son <span class=\"warning\">poco comunes</span>. En ellos\n          <span class=\"error\">la producción de metales es muy baja</span>, <span class=\"ok\">la producción de cristales es muy alta</span> y aunque un poco más baja de lo normal, todavía tienen <span class=\"zero\">buena producción de deuterio</span>.\n        </li>\n        <li>\n          <span class=\"ok\">Planetas rocosos</span> (densidad <span class=\"zero\">más de 3250 pero menos de 4500</span> kg/m³) son <span class=\"zero\">comunes</span>. En ellos\n          hay una producción ligeramente reducida pero <span class=\"zero\">buena de metales</span>, <span class=\"ok\">alta producción de cristales</span> y <span class=\"warning\">producción reducida de deuterio</span>.\n        </li>\n        <li>\n          <span class=\"ok\">Planetas estándar</span> (densidad <span class=\"zero\">más de 4500 pero menos de 5750</span> kg/m³) son <span class=\"ok\">muy comunes</span>.\n\n        </li>\n        <li>\n          <span class=\"ok\">Planetas ferrosos</span> (densidad <span class=\"zero\">más de 5750 pero menos de 7000</span> kg/m³) son <span class=\"zero\">comunes</span>.\n          En ellos hay <span class=\"ok\">una producción muy buena de metales</span>, <span class=\"warning\">producción reducida de cristales</span> y <span class=\"zero\">producción reducida de deuterio</span>.\n        </li>\n        <li>\n          <span class=\"ok\">Planetas metálicos</span> (densidad <span class=\"zero\">más de 7000 pero menos de 8250</span> kg/m³) son <span class=\"warning\">poco comunes</span>.\n          En ellos hay <span class=\"ok\">producción excelente de metales</span>, <span class=\"warning\">baja producción de cristales</span> y <span class=\"zero\">baja producción de deuterio</span>.\n        </li>\n        <li>\n          <span class=\"ok\">Planetas de metales pesados</span> (densidad <span class=\"zero\">más de 8250</span> kg/m³) son <span class=\"error\">muy raros</span>.\n          En ellos hay <span class=\"ok\">producción magnífica de metales</span>, <span class=\"error\">producción de cristales muy baja</span> y <span class=\"error\">producción de deuterio muy baja</span>.\n        </li>\n      </ul>\n\n      <ul>\n        <li>\n          <span class=\"ok\">Planetas helados</span> (densidad <span class=\"zero\">menos de 2000</span> kg/m³).\n          Debido a la abundancia de hielo de agua-metano y grandes cantidades de hidrógeno en varios estados, tienen <span class=\"ok\">producción de deuterio muy alta</span>.\n          El inconveniente es que por la poca cantidad de materia más densa, tienen <span class=\"error\">producción de cristales muy baja</span> y <span class=\"error\">producción de metal muy baja</span>. Son <span class=\"error\">muy raros</span>.\n        </li>\n        <li>\n          <span class=\"ok\">Planetas silicatos</span> (densidad <span class=\"zero\">más de 2000 pero menos de 3250</span> kg/m³) son <span class=\"warning\">poco comunes</span>. En ellos\n          <span class=\"error\">la producción de metales es muy baja</span>, <span class=\"ok\">la producción de cristales es muy alta</span> y aunque un poco más baja de lo normal, todavía tienen <span class=\"zero\">buena producción de deuterio</span>.\n        </li>\n        <li>\n          <span class=\"ok\">Planetas rocosos</span> (densidad <span class=\"zero\">más de 3250 pero menos de 4500</span> kg/m³) son <span class=\"zero\">comunes</span>. En ellos\n          hay una producción ligeramente reducida pero <span class=\"zero\">buena de metales</span>, <span class=\"ok\">alta producción de cristales</span> y <span class=\"warning\">producción reducida de deuterio</span>.\n        </li>\n        <li>\n          <span class=\"ok\">Planetas estándar</span> (densidad <span class=\"zero\">más de 4500 pero menos de 5750</span> kg/m³) son <span class=\"ok\">muy comunes</span>. En composición química son muy similares a la Tierra.\n          Los minerales están distribuidos de manera estándar - <span class=\"zero\">buena producción de metales</span>, <span class=\"zero\">buena producción de cristales</span> y <span class=\"zero\">buena producción de deuterio</span>.\n        </li>\n        <li>\n          <span class=\"ok\">Planetas ferrosos</span> (densidad <span class=\"zero\">más de 5750 pero menos de 7000</span> kg/m³) son <span class=\"zero\">comunes</span>.\n          En ellos hay <span class=\"ok\">producción muy buena de metales</span>, <span class=\"warning\">producción reducida de cristales</span> y <span class=\"zero\">producción reducida de deuterio</span>.\n        </li>\n        <li>\n          <span class=\"ok\">Planetas metálicos</span> (densidad <span class=\"zero\">más de 7000 pero menos de 8250</span> kg/m³) son <span class=\"warning\">poco comunes</span>.\n          En ellos hay <span class=\"ok\">producción excelente de metales</span>, <span class=\"warning\">baja producción de cristales</span> y <span class=\"zero\">baja producción de deuterio</span>.\n        </li>\n        <li>\n          <span class=\"ok\">Planetas de metales pesados</span> (densidad <span class=\"zero\">más de 8250</span> kg/m³) son <span class=\"error\">muy raros</span>.\n          En ellos hay <span class=\"ok\">producción magnífica de metales</span>, <span class=\"error\">producción de cristales muy baja</span> y <span class=\"error\">producción de deuterio muy baja</span>.\n        </li>\n      </ul>',\n*/\n    ),\n\n    UNIT_CAN_NOT_BE_BUILD => array(\n      'description'       => 'Esta unidad no puede ser construida por jugadores actualmente. No, no se puede comprar. Quizás pudo ser obtenida/construida anteriormente. Y no, no preguntes si podrá ser construida en el futuro u obtenida de alguna manera. Eso se desconoce<br/>Aunque... tal vez pueda obtenerse por otros métodos? Pregunta a otros jugadores... Preguntar a los administradores es inútil',\n      'description_short' => 'Actualmente esta unidad no puede ser construida o comprada',\n      'effect'            => 'Esta unidad no puede ser construida o comprada',\n    ),\n  )\n);\n"
  },
  {
    "path": "language/es/language.mo.php",
    "content": "<?php\n/*\n#############################################################################\n#  Nombre del archivo: language.mo\n#  Fecha de creación: Domingo, 30 de Marzo de 2008   19:56:23\n#  Proyecto: SuperNova.WS\n#  Descripción: Juego de estrategia espacial multijugador masivo en línea\n#\n#  Copyright © 2011-2018 Gorlum para el Proyecto \"SuperNova.WS\"\n#############################################################################\n*/\n\n/**\n*\n* system [Español]\n*\n* @package language\n* @version $Id$\n*\n*/\n\n/**\n* NO MODIFICAR\n*/\nif (!defined('INSIDE'))\n{\n  exit;\n}\n\n$lang_info = array(\n  'LANG_VERSION'      => 'V26e20',\n\n  'LANG_NAME_NATIVE'  => 'Español',\n  'LANG_NAME_ENGLISH' => 'Spanish',\n  'LANG_FLAG'         => 'es.png',\n  'LANG_FLAG_MEDIUM'  => 'es_medium.png',\n  'LANG_NAME_ISO2'    => 'es',\n  'LANG_NAME_ISO3'    => 'spa',\n  'LANG_DIRECTION'    => 'ltr',\n\n  'LANG_MAINTAINER'   => 'Gorlum',\n  'LANG_HOMEPAGE'     => 'https://forum.supernova.ws/viewtopic.php?t=2083',\n\n  'LANG_COPYRIGHT'    => array(\n    'Copyright &copy; 2009 Gorlum para el Proyecto &quot;SuperNova.WS&quot;',\n    'Copyright &copy; 2009 MSW',\n  ),\n);"
  },
  {
    "path": "language/es/login.mo.php",
    "content": "<?php\n\n/*\n#############################################################################\n#  Filename: login.mo\n#  Project: SuperNova.WS\n#  Website: http://www.supernova.ws\n#  Description: Massive Multiplayer Online Browser Space Strategy Game\n#\n#  Copyright © 2009-2018 Gorlum for Project \"SuperNova.WS\"\n#############################################################################\n*/\n\n/**\n*\n* @package language\n* @system [Russian]\n* @version 46d0\n*\n*/\n\n/**\n* DO NOT CHANGE\n*/\n\nif (!defined('INSIDE')) die();\n\nglobal $config;\n\n$a_lang_array = array(\n  'Login' => 'Iniciar sesión',\n  'User_name' => 'Nombre:',\n  'Authorization' => 'Autorización',\n  'Please_Login' => 'Por favor <a href=\"login.php\" target=\"_main\">ingrese...</a>',\n  'Please_Wait' => 'Espere',\n  'Remember_me' => 'Recordarme',\n  'Register' => 'Información de error',\n  'Login_Error' => 'Error',\n  'PleaseWait' => 'Espere',\n  'PasswordLost' => 'Recuperar contraseña',\n  'Login_Ok' => 'Conexión exitosa, <a href=\"./\"><blink>redireccionando...</blink></a><br><center><img src=\"design/images/progressbar.gif\"></center>',\n  'Login_FailPassword' => 'Nombre y/o contraseña incorrectos<br /><a href=\"login.php\" target=\"_top\">Volver</a>',\n  'Login_FailUser' => 'El jugador no existe.<br><a href=login.php>Volver</a>',\n  'log_univ' => '¡Bienvenido a nuestro Universo!',\n  'log_reg' => 'Registrarse',\n  'log_reg_main' => 'Registrarse',\n  'log_menu' => 'Menú',\n  'log_stat_menu' => 'Estadísticas',\n  'log_enter' => 'Entrar',\n  'log_news' => 'Noticias del servidor',\n  'log_cred' => 'Acerca del servidor',\n  'log_faq' => 'FAQ del juego',\n  'log_forums' => 'Foro',\n  'log_contacts' => 'Administración',\n  'log_desc' => '<strong>SuperNova es una estrategia espacial multijugador en línea.</strong> Miles de jugadores compiten simultáneamente. Solo necesitas un navegador web.',\n  'log_toreg' => '¡Regístrate ahora!',\n  'log_online' => 'Jugadores en línea',\n  'log_lastreg' => 'Nuevo jugador',\n  'log_numbreg' => 'Cuentas totales',\n  'log_welcome' => 'Bienvenido a',\n  'vacation_mode' => 'Estás en modo vacaciones<br>puedes desactivarlo en ',\n  'hours' => ' horas',\n  'vacations' => 'Modo vacaciones',\n  'log_scr1' => 'Captura de pantalla del astillero, donde se construyen y ordenan naves en el planeta actual. Haz clic para ampliar.',\n  'log_scr2' => 'Captura de estadísticas, muestra tu ranking entre otros jugadores. Haz clic para ampliar.',\n  'log_scr3' => 'Captura del universo, muestra tu planeta en el universo. Haz clic para ampliar.',\n  'log_rules' => 'Reglas del juego',\n  'log_banned' => 'Lista de baneados',\n  'log_see_you' => 'Esperamos verte de nuevo en nuestro Universo. ¡Buena suerte!<br><a href=\"login.php\">Ir a la página de inicio</a>',\n  'log_session_closed' => 'Sesión cerrada.',\n  'registry' => 'Registro',\n  'form' => 'Formulario de registro',\n  'Undefined' => '- indefinido -',\n  'Male' => 'Masculino',\n  'Female' => 'Femenino',\n  'Multiverse' => 'XNova',\n  'E-Mail' => 'Correo electrónico',\n  'MainPlanet' => 'Nombre del planeta principal',\n  'GameName' => 'Nombre',\n  'gender' => 'Género',\n  'accept' => 'Acepto las reglas',\n  'reg_i_agree' => 'He leído y acepto los',\n  'reg_with_rules' => 'términos del juego',\n  'signup' => 'Registrarse',\n  'Languese' => 'Idioma',\n  'log_reg_text0' => 'Antes de registrarte, lee las',\n  'log_reg_text1' => 'El registro implica que has leído y aceptado todas las reglas. Si no estás de acuerdo con algún punto, por favor no te registres.',\n  'thanksforregistry' => '¡Felicidades por tu registro! Serás redirigido a tu planeta en 10 segundos. Si no ocurre, haz clic <a href=overview.php><u>aquí</u></a>',\n  'welcome_to_universe' => '¡Bienvenido a OGame!!!',\n  'please_click_url' => 'Para activar tu cuenta, haz clic en este enlace',\n  'regards' => '¡Buena suerte!',\n  'error_lang' => '¡Este idioma no es soportado!<br />',\n  'error_mail' => '¡Correo electrónico inválido!<br />',\n  'error_planet' => '¡Otro planeta ya tiene ese nombre!<br />',\n  'error_hplanetnum' => '¡El nombre del planeta solo puede contener letras latinas!<br />',\n  'error_character' => '¡Nombre inválido!<br />',\n  'error_charalpha' => '¡Solo puedes usar letras latinas!<br />',\n  'error_password' => '¡La contraseña debe tener al menos 4 caracteres!<br />',\n  'error_rgt' => '¡Debes aceptar las reglas!<br />',\n  'error_userexist' => '¡Ese nombre ya está en uso!<br />',\n  'error_emailexist' => '¡Ese correo ya está registrado!<br />',\n  'error_sex' => '¡Error en la selección de género!<br />',\n  'error_mailsend' => 'Error al enviar el correo, tu contraseña: ',\n  'reg_welldone' => '¡Registro completado! Tu contraseña ha sido enviada a tu correo. Aquí está de nuevo por si acaso:<br>',\n  'error_captcha' => '¡Código gráfico incorrecto!<br/>',\n  'error_v' => '¡Inténtalo de nuevo!<br />',\n  'log_login_page' => 'Ingresar al juego',\n  'log_reg_already' => '¿Ya tienes cuenta? Haz clic ',\n  'log_reg_already_lost' => '¿Olvidaste tu contraseña? Haz clic ',\n\n  'log_lost_header' => 'Restablecer contraseña',\n  'log_lost_code' => 'Código de verificación',\n  'log_lost_description2' => 'Si tienes un código de verificación, ingrésalo y haz clic en \"Restablecer contraseña\". Recibirás un correo con tu nueva contraseña.<br /><br />\n    Si no ves el correo, revisa tu carpeta de spam. Nuestro mensaje pudo ser marcado como no deseado.<br /><span style=\"color: red;\">¡ATENCIÓN! mail.ru bloquea nuestros correos. Contacta al administrador.</span><br /><br />\n    Si aún no lo encuentras, escribe al administrador: <span class=\"ok\">' . $config->server_email . '</span>',\n  'log_lost_reset_pass' => 'Restablecer contraseña',\n  'log_lost_send_mail' => 'Enviar código de verificación',\n  'log_lost_sent_code' => 'Se ha enviado un correo con instrucciones para restablecer tu contraseña',\n  'log_lost_sent_pass' => 'También se ha enviado un correo con tu nueva contraseña',\n\n  'log_lost_err_email' => 'El correo no está registrado. Posibles causas:<br>Error al escribir el correo. Intenta nuevamente<br>Tu cuenta fue eliminada por inactividad. Regístrate de nuevo<br>Estás intentando acceder al universo incorrecto',\n  'log_lost_err_sending' => 'Error al enviar el correo. Contacta al administrador',\n  'log_lost_err_code' => 'Código de verificación inválido. Posibles causas:<br>Error al ingresar el código<br>Estás usando el código en el universo equivocado<br>Tu cuenta fue eliminada<br>El código ha expirado',\n  'log_lost_err_admin' => 'El equipo del servidor no puede usar esta función. Contacta al administrador',\n  'log_lost_err_change' => 'Error al cambiar la contraseña. Contacta al administrador',\n\n  'log_lost_description1' => 'Ingresa el correo electrónico asociado a tu cuenta para recibir un código de verificación',\n  'login_register_offer' => 'Haz clic aquí para registrarte',\n  'login_password_restore_offer' => 'Haz clic aquí para restablecer tu contraseña',\n\n  'login_register_email_hint' => 'Usa un correo válido - el dueño del correo es considerado dueño de la cuenta<br />\n    <span style=\"color: red;\">¡ATENCIÓN! No uses correos de @mail.ru - no recibirás nuestros mensajes!</span>',\n\n  'login_account_name_or_email' => 'Е-мейл',\n\n);\n"
  },
  {
    "path": "language/es/market.mo.php",
    "content": "<?php\n\n/*\n#############################################################################\n#  Filename: market.mo\n#  Project: SuperNova.WS\n#  Website: http://www.supernova.ws\n#  Description: Massive Multiplayer Online Browser Space Strategy Game\n#\n#  Copyright © 2009-2018 Gorlum for Project \"SuperNova.WS\"\n#############################################################################\n*/\n\n/**\n*\n* @package language\n* @system [Spanish]\n* @version 46d0\n* @condition clear\n*\n*/\n\n/**\n* DO NOT CHANGE\n*/\n\nif (!defined('INSIDE')) die();\n\n$a_lang_array = (array(\n  'eco_mrk_title' => 'Mercado Negro',\n  'eco_mrk_description' => 'Curiosamente, en la descripción de la interfaz de gestión del Imperio no había tal punto... ¿Me pregunto de dónde salió?',\n  'eco_mrk_service' => 'Servicio',\n  'eco_mrk_service_cost' => 'Costo del servicio',\n\n  'eco_mrk_trader_do' => 'Intercambiar recursos',\n  'eco_mrk_trader' => 'Intercambio de recursos',\n  'eco_mrk_trader_cost' => 'Costo del intercambio de recursos',\n  'eco_mrk_trader_exchange' => 'Cantidad de recursos a intercambiar',\n  'eco_mrk_trader_to' => 'Intercambiar por',\n  'eco_mrk_trader_course' => 'Tasa',\n  'eco_mrk_trader_left' => 'Resultado de la operación',\n  'eco_mrk_trader_resources_all' => 'Todos los recursos',\n  'eco_mrk_trader_exchange_dm_confirm' => '¿Estás seguro de que deseas intercambiar {0} Materia Oscura por recursos?',\n\n  'eco_mrk_scraper_do' => 'Vender naves al desguazador',\n  'eco_mrk_scraper' => 'Desguace de naves',\n  'eco_mrk_scraper_price' => 'Rendimiento de chatarra',\n  'eco_mrk_scraper_perShip' => 'por nave',\n  'eco_mrk_scraper_total' => 'Total',\n  'eco_mrk_scraper_cost' => 'Vender naves como chatarra cuesta',\n  'eco_mrk_scraper_onOrbit' => 'En órbita',\n  'eco_mrk_scraper_to' => 'Enviar al desguace',\n  'eco_mrk_scraper_res' => 'Se obtuvo la siguiente chatarra:',\n  'eco_mrk_scraper_ships' => 'Las siguientes naves fueron desguazadas:',\n  'eco_mrk_scraper_noShip' => 'No hay naves en órbita',\n\n  'eco_mrk_stockman_do' => 'Comprar naves al desguazador',\n  'eco_mrk_stockman' => 'Vendedor de naves usadas',\n  'eco_mrk_stockman_price' => 'Precio',\n  'eco_mrk_stockman_perShip' => 'por nave',\n  'eco_mrk_stockman_onStock' => 'En stock',\n  'eco_mrk_stockman_buy' => 'Comprar naves',\n  'eco_mrk_stockman_res' => 'Costo de las naves compradas:',\n  'eco_mrk_stockman_ships' => 'Se compraron las siguientes naves:',\n  'eco_mrk_stockman_noShip' => 'El vendedor no tiene naves disponibles en este momento',\n\n  'eco_mrk_exchange' => 'Bolsa de intercambio de recursos',\n  'eco_mrk_banker'   => 'Banquero',\n  'eco_mrk_pawnshop' => 'Casa de empeño',\n\n  'eco_mrk_info_do' => 'Comprar información',\n  'eco_mrk_info' => 'Vendedor de información',\n  'eco_mrk_info_description' => 'En el buzón de entrada se encontró un mensaje con el siguiente contenido:',\n  'eco_mrk_info_description_2' => 'Tengo acceso a mucha información interesante. Puedo compartirla contigo... por una modesta recompensa. Por una consulta, solo',\n  'eco_mrk_info_buy' => 'Comprar información',\n\n  'eco_mrk_info_player' => 'Información sobre el jugador',\n  'eco_mrk_info_player_description' => 'Puedo averiguar qué mercenarios están trabajando actualmente para el jugador',\n  'eco_mrk_info_player_message' => 'Según mis fuentes confiables, la lista de mercenarios del jugador ID %1$d [%2$s] es la siguiente:',\n\n  'eco_mrk_info_not_hired' => 'no contratado',\n\n  'eco_mrk_info_ally' => 'Información sobre la Alianza',\n  'eco_mrk_info_online' => 'Actividad actual en el universo',\n\n  'eco_mrk_info_msg_from' => 'Fuente no rastreable',\n\n  'eco_mrk_error_title' => 'Mercado Negro - Error',\n  'eco_mrk_errors' => array(\n    MARKET_RESOURCES => 'Operación completada con éxito',\n    MARKET_SCRAPPER => 'Intercambio de recursos realizado con éxito',\n    MARKET_NOT_A_SHIP => '¡No intentes vender algo que no sea una nave!',\n    MARKET_STOCKMAN => 'No hay suficiente Materia Oscura para completar la operación',\n    MARKET_NO_RESOURCES => 'No hay suficientes recursos para completar la operación',\n    MARKET_PAWNSHOP => 'Intentas desguazar más naves de las que hay en órbita',\n    MARKET_NO_STOCK => 'Intentas comprar más naves de las que tiene el vendedor. Quizás, mientras seleccionabas, alguien más ya las compró',\n    MARKET_ZERO_DEAL => 'No se especificó la cantidad de recursos para intercambiar',\n    MARKET_NOTHING => 'Debes seleccionar naves para vender',\n    MARKET_ZERO_RES_STOCK => 'Debes seleccionar naves para comprar',\n    MARKET_NEGATIVE_SHIPS => '¡No intentes vender una cantidad negativa de naves!',\n\n    MARKET_NO_DM => 'No hay suficiente Materia Oscura para completar la operación',\n    MARKET_INFO_WRONG => 'No existe tal información',\n    MARKET_INFO_PLAYER => 'Información comprada con éxito. Revisa tu buzón',\n    MARKET_INFO_PLAYER_WRONG => 'Debes especificar el ID o nombre del jugador',\n    MARKET_INFO_PLAYER_NOT_FOUND => 'No puedo identificar al jugador. Si el nombre del jugador consiste en números o es ilegible, intenta usar su ID',\n    MARKET_INFO_PLAYER_SAME => '¿Para qué quieres información sobre ti mismo?',\n  ),\n));"
  },
  {
    "path": "language/es/menu.mo.php",
    "content": "<?php\n\n/*\n#############################################################################\n#  Filename: leftmenu.mo\n#  Project: SuperNova.WS\n#  Website: http://www.supernova.ws\n#  Description: Massive Multiplayer Online Browser Space Strategy Game\n#\n#  Copyright © 2009-2018 Gorlum for Project \"SuperNova.WS\"\n#############################################################################\n*/\n\n/**\n*\n* @package language\n* @system [Spanish]\n* @version 46d0\n*\n*/\n\n/**\n* DO NOT CHANGE\n*/\n\nif (!defined('INSIDE')) die();\n\n$a_lang_array = (array(\n  'Multiverse' => '<b>Servidor</b> Uni',\n  'm_h_rules' => 'Reglas',\n  'm_faq' => '¿Cómo jugar?',\n  'm_faq_hint' => 'Preguntas más frecuentes de los usuarios y sus respuestas',\n  'm_h_control' => 'Control',\n  'm_forum' => 'Foro',\n  'm_others' => 'Otros',\n  'm_simulator' => 'Simulador de batalla',\n  'm_communication' => 'Comunicaciones',\n  'm_exchange' => 'Bolsa de recursos',\n  'm_affilates' => '¡GANA TM!',\n  'Overview' => 'Vista del planeta',\n  'Officiers' => 'Oficiales',\n//  'Buildings' => 'Edificios',\n  'Building' => 'Construcción',\n  'Research' => 'Investigación',\n  'Shipyard' => 'Astillero',\n  'Defense' => 'Defensa',\n  'Resources' => 'Recursos',\n  'Imperium' => 'Imperio',\n  'Marchand' => 'Comerciante',\n  'Annonces' => 'Anuncios',\n  'Technology' => 'Tecnologías',\n  'Galaxy' => 'Galaxia',\n  'lm_fleet_orbiting' => 'Flota en órbita',\n  'Alliance' => 'Tu Alianza',\n  'Allianc' => 'Alianza',\n  'AllyChat' => 'Chat de la Alianza',\n  'Statistics' => 'Estadísticas',\n  'Search' => 'Buscar',\n  'Records' => 'Récords',\n  'Messages' => 'Mensajes',\n  'Notes' => 'Notas',\n  'Buddylist' => 'Amigos',\n  'Chat' => 'Chat',\n  'Contact' => 'Administración',\n  'Options' => 'Ajustes',\n  'Bug' => 'Soporte técnico',\n  'Logout' => 'Salir',\n  'Rules' => 'Reglas',\n  'devlp' => 'Desarrollo',\n  'navig' => 'Información',\n  'Economy' => 'Economía',\n  'trade' => 'Comercio',\n  'Society' => 'Sociedad',\n  'rinok' => 'Mercado Negro',\n  'observ' => 'Observatorio',\n  'commun' => 'Administración',\n  'infog' => 'Información',\n  'lm_combat_reports' => 'Informes de combate',\n  'adm_over' => 'Resumen',\n  'adm_conf' => 'Configuración',\n  'adm_reset' => 'Reinicio',\n  'adm_plrlst' => 'Lista de jugadores',\n  'adm_panel' => 'Panel de administrador',\n  'adm_plrsch' => 'Buscar jugador',\n  'adm_addres' => 'Añadir recursos',\n  'adm_pltlst' => 'Lista de planetas',\n  'adm_actplt' => 'Planetas activos',\n  'adm_moonlst' => 'Lista de lunas',\n  'adm_addmoon' => 'Añadir luna',\n  'adm_fleet' => 'Flotas en vuelo',\n  'adm_ban' => 'Banear',\n  'adm_unban' => 'Desbanear',\n  'adm_chat' => 'Editor de chat',\n  'adm_updpt' => 'Actualizar estadísticas',\n  'adm_msg' => 'Lista de mensajes',\n  'adm_md5' => 'Encriptación',\n  'adm_updrank' => 'Reiniciar base',\n  'adm_log_main' => 'Registros de logs',\n  'adm_help' => 'Foro de desarrolladores',\n  'adm_back' => 'Volver',\n  'admin' => 'Administración',\n  'player' => 'Jugadores',\n  'tool' => 'Utilidades',\n  'lm_ifo_serv' => 'Materias primas',\n  'lm_ifo_game' => 'Juego',\n  'lm_ifo_fleet' => 'Flota',\n  'lm_ifo_queue' => 'Cola',\n  'lm_shortcuts' => 'Marcadores',\n  'lm_banned' => 'Lista de baneados',\n  'lm_announce_fresh' => 'NUEVO',\n  'lm_server_info' => 'Sobre el servidor',\n\n  'menu_quest_list' => 'Lista de misiones',\n  'menu_universe_overview' => 'Resumen del Universo',\n  'menu_stat_players' => 'Estadísticas',\n  'menu_stat_records' => 'Récords',\n  'menu_races' => 'Mundos natales',\n  'menu_metamatter' => 'Metamateria',\n\n  'menu_hide' => '<<',\n  'menu_show' => '>>',\n\n  'menu_pin' => 'Fijar menú',\n  'menu_unpin' => 'Desfijar menú',\n\n  'matter_analyze' => 'Balance de materia',\n));"
  },
  {
    "path": "language/es/messages.mo.php",
    "content": "<?php\n\n/*\n#############################################################################\n#  Filename: messages.mo\n#  Project: SuperNova.WS\n#  Website: http://www.supernova.ws\n#  Description: Massive Multiplayer Online Browser Space Strategy Game\n#\n#  Copyright © 2009-2018 Gorlum for Project \"SuperNova.WS\"\n#############################################################################\n*/\n\n/**\n*\n* @package language\n* @system [Spanish]\n* @version 46d0\n*\n*/\n\n/**\n* DO NOT CHANGE\n*/\n\nif (!defined('INSIDE')) die();\n\n$a_lang_array = [\n  'msg_page_header' => 'Mensajes privados',\n  'msg_head_type' => 'Categorías',\n  'msg_head_count' => 'No leídos',\n  'msg_head_total' => 'Total',\n  'msg_mark_select' => '-- SELECCIONE RANGO --',\n  'msg_mark_checked' => 'Mensajes marcados',\n  'msg_mark_unchecked' => 'Mensajes no marcados',\n  'msg_mark_class' => 'Todos los mensajes de la categoría',\n  'msg_mark_all' => 'TODOS los mensajes privados',\n  'msg_select_all' => 'Seleccionar todos',\n  'msg_delete_checked' => 'Eliminar mensajes marcados',\n  'msg_show_all' => 'Mostrar todos',\n  'msg_date' => 'Fecha',\n  'msg_from' => 'De',\n  'msg_recipient' => 'Para',\n  'msg_subject' => 'Asunto del mensaje',\n  'msg_answer' => 'Responder',\n  'msg_answer_prefix' => 'RE:',\n  'msg_compose' => 'Escribir mensaje',\n  'msg_text' => 'Texto del mensaje',\n  'msg_subject_default' => 'Nuevo mensaje',\n  'msg_not_message_sent' => 'Mensaje enviado',\n  'msg_warn_no_messages' => 'No hay mensajes en esta categoría',\n  'msg_err_player_not_found' => 'Jugador no encontrado',\n  'msg_err_no_text' => 'No se puede enviar un mensaje vacío',\n  'msg_err_self_send' => 'No puedes enviarte un mensaje a ti mismo',\n  'msg_del_class' => 'Eliminar todos los mensajes de esta categoría',\n  'msg_page_hint_class' =>\n    '<ul>\n      <li>La categoría \"Mensajes enviados\" contiene la lista de mensajes que has enviado y que aún no han sido eliminados por el destinatario. No puedes eliminar mensajes de esta categoría</li>\n      <li>Para eliminar todos los mensajes de una categoría específica, haz clic en el icono de eliminación en la fila correspondiente</li>\n      <li>Eliminar mensajes de la categoría \"Todos los mensajes\" limpiará tu bandeja de entrada</li>\n      <li>Una conexión lenta y/o una gran cantidad de mensajes en una categoría puede hacer imposible ver los mensajes. En ese caso, debes limpiar tu bandeja de entrada por completo o eliminar todos los mensajes de la categoría problemática</li>\n    </ul>',\n  'msg_header_dialog' => 'Diálogo con',\n\n  'msg_ignore' => 'Ignorar',\n  'msg_ignore_title' => \"¿Añadir al jugador [PLAYER_NAME] a la lista de ignorados?\",\n  'msg_ignore_message' => \"Ya no verás mensajes privados de jugadores en tu lista de ignorados.<br><br>Puedes gestionar tu lista de ignorados en la página 'Ajustes'.<br><br>¿Añadir al jugador [PLAYER_NAME] a la lista de ignorados?\",\n  'msg_message' => 'Mensaje',\n  'msg_ignored_messages' => 'mensajes de usuarios en la lista de ignorados no mostrados',\n  'msg_ignore_control' => 'Puedes gestionar tu lista de ignorados en la página \"Ajustes\"',\n];"
  },
  {
    "path": "language/es/mrc_mercenary.mo.php",
    "content": "<?php\n\n/*\n#############################################################################\n#  Filename: mercenary.mo\n#  Project: SuperNova.WS\n#  Website: http://www.supernova.ws\n#  Description: Massive Multiplayer Online Browser Space Strategy Game\n#\n#  Copyright © 2009-2018 Gorlum for Project \"SuperNova.WS\"\n#############################################################################\n*/\n\n/**\n*\n* @package language\n* @system [Spanish]\n* @version 46d0\n*\n* @clean - all constants is used\n*\n*/\n\n/**\n* DO NOT CHANGE\n*/\n\nif (!defined('INSIDE'))\n{\n  exit;\n}\n\n$a_lang_array = (array(\n  'mrc_up_to' => 'hasta',\n  'mrc_hire' => 'Contratar',\n  'mrc_hire_for' => 'Contratar por',\n  'mrc_allowed' => 'Disponibles',\n  'mrc_msg_error_wrong_mercenary' => 'ID de Mercenario incorrecto',\n  'mrc_msg_error_wrong_level' => 'Nivel de Mercenario incorrecto',\n  'mrc_msg_error_wrong_period' => 'Período de contratación no válido',\n  'mrc_msg_error_already_hired' => 'El Mercenario ya está contratado. Despídelo o espera a que termine su contrato',\n  'mrc_msg_error_no_resource' => 'No hay suficiente Materia Oscura para contratar',\n  'mrc_msg_error_requirements' => 'No se cumplen los requisitos para contratar',\n\n  'mrc_dismiss' => 'Despedir',\n  'mrc_dismiss_confirm' => '¡Al despedir al Mercenario se perderá toda la Materia Oscura gastada en su contratación! ¿Estás seguro de que quieres despedir al Mercenario?',\n  'mrc_dismiss_before_hire' => 'Para cambiar el nivel de un Mercenario contratado, primero debes despedir al actual - con pérdida de la Materia Oscura gastada',\n\n  'mrc_mercenary_hired_log' => 'Mercenario \"%1$s\" ID %2$d contratado por %3$d de Materia Oscura por %4$d días',\n  'mrc_mercenary_dismissed_log' => '¡PERDIDOS %7$d días de contratación y %8$d de Materia Oscura al precio actual! Mercenario \"%1$s\" ID %2$d despedido, contratado por %4$d días (desde %5$s hasta %6$s) y que actualmente cuesta %3$d de Materia Oscura',\n  'mrc_plan_bought_log' => 'Comprado \"%1$s\" ID %2$d por %3$d de Materia Oscura',\n));"
  },
  {
    "path": "language/es/notes.mo.php",
    "content": "<?php\n\n/*\n#############################################################################\n#  Filename: notes.mo\n#  Project: SuperNova.WS\n#  Website: http://www.supernova.ws\n#  Description: Massive Multiplayer Online Browser Space Strategy Game\n#\n#  Copyright © 2009-2018 Gorlum for Project \"SuperNova.WS\"\n#############################################################################\n*/\n\n/**\n*\n* @package language\n* @system [Spanish]\n* @version 46d0\n*\n*/\n\n/**\n* DO NOT CHANGE\n*/\n\n!defined('INSIDE') && die();\n\n$a_lang_array = (array(\n  'note_page_header' => 'Notas',\n  'note_date' => 'Fecha',\n  'note_note' => 'Nota',\n  'note_priority' => 'Importancia',\n  'note_sticky' => 'Fijada',\n  'note_new_title' => 'Título de la nueva nota',\n  'note_new_text' => 'Texto de la nueva nota',\n  'note_stick_it' => 'Fijar la nota bajo la barra de navegación en cada página',\n\n  'note_err_none_added' => 'Nota añadida correctamente',\n  'note_err_none_changed' => 'Nota modificada correctamente',\n  'note_err_note_not_found' => 'No se encontró la nota con este ID. Posiblemente fue eliminada en otra ventana',\n  'note_err_owner_wrong' => 'No eres el propietario de esta nota',\n  'note_err_note_empty' => 'No has escrito nada en la nota - no se añadirá',\n\n  'note_delete' => 'Eliminar notas',\n  'note_range_select' => '-- SELECCIONE RANGO --',\n  'note_range_marked' => 'Notas marcadas',\n  'note_range_marked_not' => 'Notas no marcadas',\n  'note_range_all' => 'Todas las notas',\n\n  'note_warn_no_range' => 'No has seleccionado un rango. Nada que eliminar',\n  'note_err_none_selected' => 'No se seleccionó ninguna nota - no se realiza la eliminación. Para eliminar todas las notas, selecciona el rango \"Todas las notas\"',\n));"
  },
  {
    "path": "language/es/options.mo.php",
    "content": "<?php\n/** @noinspection HtmlUnknownTarget */\n\n/*\n#############################################################################\n#  Filename: options.mo\n#  Project: SuperNova.WS\n#  Website: http://www.supernova.ws\n#  Description: Massive Multiplayer Online Browser Space Strategy Game\n#\n#  Copyright © 2009-2018 Gorlum for Project \"SuperNova.WS\"\n#############################################################################\n*/\n\n/**\n*\n* @package language\n* @system [Spanish]\n* @version 46d0\n*\n*/\n\n/**\n* DO NOT CHANGE\n*/\n\nif (!defined('INSIDE')) die();\n\n$a_lang_array = [\n  'opt_account' => 'Perfil',\n  'opt_int_options' => 'Interfaz',\n  'opt_settings_statistics' => 'Estadísticas del jugador',\n  'opt_settings_info' => 'Información del jugador',\n  'opt_alerts' => 'Notificaciones',\n  'opt_common' => 'General',\n  'opt_tutorial' => 'Tutorial',\n\n  'opt_birthday' => 'Cumpleaños',\n\n  'opt_header' => 'Configuración de usuario',\n  'opt_messages' => 'Notificaciones automáticas',\n  'opt_msg_saved' => 'Configuración guardada correctamente',\n  'opt_msg_name_changed' => 'Nombre de usuario cambiado correctamente',\n  'opt_msg_name_change_err_used_name' => 'Este nombre pertenece a otro usuario',\n  'opt_msg_name_change_err_no_dm' => 'No hay suficiente Materia Oscura para cambiar el nombre',\n\n  'username_old' => 'Nombre actual',\n  'username_new' => 'Nuevo nombre',\n  'username_change_confirm' => 'Cambiar nombre',\n  'username_change_confirm_payed' => 'por',\n\n  'opt_msg_pass_changed' => 'Contraseña cambiada correctamente',\n  'opt_err_pass_wrong' => 'Contraseña actual incorrecta. La contraseña no se cambió',\n  'opt_err_pass_unmatched' => 'Las contraseñas no coinciden. La contraseña no se cambió',\n  'changue_pass' => 'Cambiar contraseña',\n  'Download' => 'Descargar',\n  'userdata' => 'Información',\n  'username' => 'Nombre',\n  'lastpassword' => 'Contraseña anterior',\n  'newpassword' => 'Nueva contraseña<br>(mín. 8 caracteres)',\n  'newpasswordagain' => 'Repetir nueva contraseña',\n  'emaildir' => 'Dirección de email',\n  'emaildir_tip' => 'Esta dirección puede cambiarse en cualquier momento. Se convertirá en la principal si no se modifica durante 7 días.',\n  'permanentemaildir' => 'Dirección de email principal',\n  'opt_planet_sort_title' => 'Ordenar planetas por',\n  'opt_planet_sort_options' => [\n    SORT_ID       => 'Fecha de colonización',\n    SORT_LOCATION => 'Coordenadas',\n    SORT_NAME     => 'Orden alfabético',\n    SORT_SIZE     => 'Número de campos',\n  ],\n  'opt_planet_sort_ascending' => [\n    SORT_ASCENDING  => 'Ascendente',\n    SORT_DESCENDING => 'Descendente',\n  ],\n\n  'opt_navbar_title' => 'Barra de navegación',\n  'opt_navbar_description' => 'La barra de navegación (o simplemente \"navbar\") se encuentra en la parte superior de la pantalla. Esta sección permite configurar su apariencia',\n  'opt_navbar_resourcebar_description' => 'Barra de recursos',\n  'opt_navbar_buttons_title' => 'Configuración de botones del navbar',\n  'opt_player_options' => [\n    PLAYER_OPTION_NAVBAR_PLANET_VERTICAL        => 'Barra de recursos vertical',\n    PLAYER_OPTION_NAVBAR_PLANET_DISABLE_STORAGE => 'Ocultar capacidad de almacenamiento en la barra de recursos',\n    PLAYER_OPTION_NAVBAR_PLANET_OLD             => 'Usar visualización antigua de recursos en tabla',\n\n    PLAYER_OPTION_NAVBAR_RESEARCH_WIDE          => 'Botón de investigación ancho (visualización antigua)',\n    PLAYER_OPTION_NAVBAR_DISABLE_RESEARCH       => 'Desactivar botón de investigación',\n    PLAYER_OPTION_NAVBAR_DISABLE_PLANET         => 'Desactivar botón de planeta',\n    PLAYER_OPTION_NAVBAR_DISABLE_HANGAR         => 'Desactivar botón de astillero',\n    PLAYER_OPTION_NAVBAR_DISABLE_DEFENSE        => 'Desactivar botón de defensa',\n    PLAYER_OPTION_NAVBAR_DISABLE_EXPEDITIONS    => 'Desactivar botón de expediciones',\n    PLAYER_OPTION_NAVBAR_DISABLE_FLYING_FLEETS  => 'Desactivar botón de flotas en vuelo',\n    PLAYER_OPTION_NAVBAR_DISABLE_QUESTS         => 'Desactivar botón de misiones',\n    PLAYER_OPTION_NAVBAR_DISABLE_META_MATTER    => 'Desactivar botón de MetaMateria',\n\n    PLAYER_OPTION_UNIVERSE_OLD                  => 'Usar visualización antigua del \"Universo\"',\n    PLAYER_OPTION_UNIVERSE_DISABLE_COLONIZE     => 'Desactivar botón de colonización',\n    PLAYER_OPTION_DESIGN_DISABLE_BORDERS        => 'Desactivar bordes de imágenes en tablas',\n    PLAYER_OPTION_TECH_TREE_TABLE               => 'Página de Tecnologías en formato de tabla (visualización antigua)',\n    PLAYER_OPTION_FLEET_SHIP_SELECT_OLD         => 'Cantidad de naves en columna separada (visualización antigua)',\n    PLAYER_OPTION_FLEET_SHIP_HIDE_SPEED         => 'No mostrar velocidad de la nave',\n    PLAYER_OPTION_FLEET_SHIP_HIDE_CAPACITY      => 'No mostrar capacidad de carga de la nave',\n    PLAYER_OPTION_FLEET_SHIP_HIDE_CONSUMPTION   => 'No mostrar consumo de combustible de la nave',\n    PLAYER_OPTION_TUTORIAL_DISABLED             => 'Desactivar completamente el tutorial',\n    PLAYER_OPTION_TUTORIAL_WINDOWED             => 'Mostrar texto tutorial en ventana emergente (popup)',\n    PLAYER_OPTION_TUTORIAL_CURRENT              => 'Reiniciar tutorial - comenzará desde el principio',\n\n    PLAYER_OPTION_PLANET_SORT_INVERSE           => 'En orden inverso',\n    PLAYER_OPTION_BUILD_AUTOCONVERT_HIDE        => 'Ocultar botón de autoconversión',\n\n    PLAYER_OPTION_SOUND_ENABLED                 => 'Activar sonidos en el juego',\n    PLAYER_OPTION_ANIMATION_DISABLED            => 'Desactivar efectos de animación',\n    PLAYER_OPTION_PROGRESS_BARS_DISABLED        => 'Desactivar barras de progreso',\n  ],\n\n  'opt_chk_skin' => 'Usar tema visual',\n  'opt_adm_title' => 'Opciones de administración',\n  'opt_adm_planet_prot' => 'Protección de planetas',\n  'thanksforregistry' => 'Gracias por registrarte.<br />Recibirás un mensaje con tu contraseña en unos minutos.',\n  'general_settings' => 'Configuración general',\n  'skins_example' => 'Tema visual',\n\n  'opt_avatar' => 'Avatar',\n  'opt_avatar_search' => 'Buscar en Google',\n  'opt_avatar_remove' => 'Eliminar avatar',\n  'opt_upload' => 'Subir',\n\n  'opt_msg_avatar_removed' => 'Avatar eliminado',\n  'opt_msg_avatar_uploaded' => 'Avatar cambiado correctamente',\n  'opt_msg_avatar_error_delete' => 'Error al eliminar el archivo de avatar. Contacta con la Administración del servidor',\n  'opt_msg_avatar_error_writing' => 'Error al guardar el archivo de avatar. Contacta con la Administración del servidor',\n  'opt_msg_avatar_error_upload' => 'Error al subir la imagen %1. Contacta con la Administración del servidor',\n  'opt_msg_avatar_error_unsupported' => 'Formato de imagen no soportado. Solo se admiten archivos JPG, GIF, PNG de hasta 200KB',\n\n  'untoggleip' => 'Desactivar verificación por IP',\n  'untoggleip_tip' => 'La verificación por IP significa que no podrás iniciar sesión con tu nombre desde dos IP diferentes. ¡La verificación mejora tu seguridad!',\n  'galaxyvision_options' => 'Universo',\n  'spy_cant' => 'Cantidad de sondas',\n  'spy_cant_tip' => 'Cantidad de sondas que se enviarán cuando espíes a alguien.',\n  'tooltip_time' => 'Retraso antes de mostrar tooltips',\n  'mess_ammount_max' => 'Cantidad máxima de mensajes de flota',\n  'seconds' => 'Segundo(s)',\n  'shortcut' => 'Acceso rápido',\n  'show' => 'Mostrar',\n  'write_a_messege' => 'Escribir mensaje',\n  'spy' => 'Espionaje',\n  'add_to_buddylist' => 'Añadir a amigos',\n  'attack_with_missile' => 'Ataque con misiles',\n  'show_report' => 'Ver informe',\n  'delete_vacations' => 'Gestionar perfil',\n  'mode_vacations' => 'Activar modo vacaciones',\n  'vacations_tip' => 'El modo vacaciones protege tus planetas durante tu ausencia.',\n  'deleteaccount' => 'Desactivar perfil',\n  'deleteaccount_tip' => 'El perfil se eliminará después de 45 días de inactividad.',\n  'deleteaccount_on' => 'La eliminación de la cuenta ocurrirá tras inactividad',\n  'save_settings' => 'Guardar cambios',\n  'exit_vacations' => 'Salir del modo vacaciones',\n  'Vaccation_mode' => 'Modo vacaciones activado. Durará hasta: ',\n  'You_cant_exit_vmode' => 'No puedes salir del modo vacaciones hasta que pase el tiempo mínimo',\n  'Error' => 'Error',\n  'cans_resource' => 'Detener producción de recursos en planetas',\n  'cans_reseach' => 'Detener investigaciones en planetas',\n  'cans_build' => 'Detener construcciones en planetas',\n  'cans_fleet_build' => 'Detener construcción de flota y defensas',\n  'cans_fly_fleet2' => 'Flota enemiga acercándose... no puedes irte de vacaciones',\n  'vacations_exit' => 'Modo vacaciones desactivado... Vuelve a iniciar sesión',\n  'select_skin_path' => 'SELECCIONAR',\n  'opt_language' => 'Idioma de interfaz',\n  'opt_compatibility' => 'Compatibilidad - interfaces antiguos',\n  'opt_compat_structures' => 'Interfaz antiguo de construcción de edificios',\n  'opt_vacation_err_your_fleet' => 'No puedes irte de vacaciones mientras tengas al menos una flota en vuelo',\n  'opt_vacation_err_building' => 'Estás construyendo o investigando en %s y por eso no puedes irte de vacaciones',\n  'opt_vacation_err_research' => 'Tus científicos están investigando tecnología y por eso no puedes irte de vacaciones',\n  'opt_vacation_err_que' => 'Tienes investigaciones en curso o construcciones en algún planeta y por eso no puedes irte de vacaciones. Usa el enlace \"Imperio\" para ver las colas de construcción',\n  'opt_vacation_err_timeout' => 'Aún no has acumulado suficiente tiempo para vacaciones - el tiempo de espera no ha terminado',\n  'opt_vacation_next' => 'Podrás irte de vacaciones después de',\n  'opt_vacation_min' => 'mínimo hasta',\n  'succeful_changepass' => 'Contraseña cambiada correctamente.<br /><a href=\"login.php\" target=\"_top\">Volver</a>',\n\n  'opt_time_diff_clear' => 'Medir diferencia entre el tiempo del jugador y el del servidor',\n  'opt_time_diff_manual' => 'Establecer diferencia de tiempo manualmente',\n  'opt_time_diff_explain' => 'Con la diferencia de tiempo correctamente configurada, el reloj \"Tiempo del jugador\" en el navbar debería estar sincronizado segundo a segundo con el reloj del dispositivo del jugador<br />\n  Normalmente el juego establece automáticamente la diferencia correcta. Sin embargo, con una zona horaria incorrecta en el dispositivo del jugador, al jugar desde múltiples dispositivos, o con una conexión muy lenta, a veces es necesario establecer la diferencia manualmente',\n\n  'opt_custom' => [\n    'opt_uni_avatar_user' => 'Mostrar avatar del usuario',\n    'opt_uni_avatar_ally' => 'Mostrar logo de la Alianza',\n    'opt_int_struc_vertical' => 'Cola de construcciones vertical',\n    'opt_int_navbar_resource_force' => 'Mostrar siempre la barra de recursos',\n    'opt_int_overview_planet_columns' => 'Número de columnas en la lista de planetas',\n    'opt_int_overview_planet_columns_hint' => '0 - calcular según el número máximo de filas',\n    'opt_int_overview_planet_rows' => 'Número máximo de filas en la lista de planetas',\n    'opt_int_overview_planet_rows_hint' => 'Se ignora si se especifica el número de columnas',\n  ],\n\n  'opt_mail_optional_description' => 'A esta dirección se envían mensajes privados de otros jugadores y notificaciones de eventos del juego (como informes de expediciones y espionaje)',\n  'opt_mail_permanent_description' => 'Esta dirección de email está vinculada a tu cuenta. Solo puede establecerse una vez. Todas las notificaciones del sistema (como cambios de contraseña) se envían aquí',\n\n  'opt_account_name' => 'Tu nombre de usuario<br />Este nombre se usa para iniciar sesión',\n  'opt_game_user_name' => 'Nombre en el juego (nick)<br />Con este nombre te verán otros jugadores',\n\n  'opt_universe_title' => 'Universo',\n\n  'option_fleets' => 'Flotas',\n  'option_fleet_send' => 'Envío de flota',\n\n  'option_change_nick_disabled' => 'El cambio de nick está desactivado en la configuración del servidor',\n\n  'opt_ignores' => 'Lista de ignorados',\n  'opt_unignore_do' => 'Eliminar de lista de ignorados',\n  'opt_ignore_list_empty' => 'Tu lista de ignorados está vacía',\n];"
  },
  {
    "path": "language/es/overview.mo.php",
    "content": "<?php\n\n/*\n#############################################################################\n#  Filename: overview.mo\n#  Project: SuperNova.WS\n#  Website: http://www.supernova.ws\n#  Description: Massive Multiplayer Online Browser Space Strategy Game\n#\n#  Copyright © 2009-2018 Gorlum for Project \"SuperNova.WS\"\n#############################################################################\n*/\n\n/**\n*\n* @package language\n* @system [Spanish]\n* @version 46d0\n*\n*/\n\n/**\n* DO NOT CHANGE\n*/\n\nif (!defined('INSIDE')) die();\n\n\n$a_lang_array = (array(\n  'ov_hack_alert' => '¡Intento de hackeo de la base de datos!!!',\n  'ov_you_have' => 'Tienes',\n  'ov_new_message' => 'un nuevo mensaje',\n  'ov_new_messages' => 'nuevos mensajes',\n  'cancel' => 'Cancelar',\n  'Planet_menu' => 'Construcción en el planeta',\n  'Planet' => 'Planeta',\n  'Moon' => 'Luna',\n  'Have_new_level_mineur' => '¡Por tus logros económicos has ganado un punto de desarrollo para oficiales!',\n  'Have_new_level_raid' => '¡Por tus ataques exitosos has ganado un punto de desarrollo para oficiales!',\n  'Server_time' => 'Hora',\n  'Left_time' => 'Tiempo restante',\n  'Now_build' => 'En construcción',\n  'Events' => 'Eventos',\n  'Free' => 'sin tareas',\n  'Diameter' => 'Diámetro',\n  'fields' => 'Sectores',\n  'Developed_fields' => 'Sectores ocupados',\n  'max_eveloped_fields' => 'Número máximo de sectores',\n  'Temperature' => 'Temperatura',\n  'min_avg_max' => 'min/med/máx',\n  'approx' => 'aproximadamente',\n  'to' => 'a',\n  'Centigrade' => '°C',\n  'Position' => 'Coordenadas',\n  'Fleet' => 'Flota',\n  'Research' => 'Investigación',\n  'Total' => 'Total',\n  'Rank' => 'Posición',\n  'of' => 'de',\n  'Miner' => 'Geólogo',\n  'Raider' => 'Asaltante',\n  'Debris_Field' => 'Campo de escombros',\n  'rename_and_abandon_planet' => 'Gestión del planeta',\n  'functions' => 'Funciones',\n  'coords' => 'Coordenadas',\n  'your_planet' => 'Tu planeta',\n  'colony_abandon' => 'Abandonar colonia',\n  'deleteplanet' => '¡Eliminar planeta!',\n  'security_query' => 'Sistema de seguridad',\n  'name' => 'Nombre',\n  'namer' => 'Cambiar nombre',\n  'confirm_planet_delete' => 'Confirmar eliminación del planeta',\n  'confirmed_with_password' => 'Confirmar con contraseña',\n  'ov_delete_ok' => 'Colonia eliminada con éxito',\n  'ov_delete_wrong_planet' => '¡El planeta no puede ser abandonado! Estás intentando abandonar tu planeta principal o has cambiado el planeta actual en otra ventana del navegador.',\n  'ov_delete_wrong_pass' => '¡Contraseña incorrecta!',\n  'MembersOnline' => 'Jugadores',\n  'ov_fleet_list' => 'Programa de movimientos de flota',\n  'ov_fleet' => 'Flota',\n  'ov_destination' => 'Destino',\n  'ov_source' => 'Origen',\n  'event_time' => 'Hora',\n  'ov_mission' => 'Misión',\n  'ov_event' => 'Evento',\n  'ov_flying_fleets' => 'Flotas en camino a',\n  'ov_other_planets' => 'otros planetas',\n  'ov_fleet_arrive' => 'Llegada',\n  'ov_fleet_return' => 'Regreso',\n  'ov_fleet_hold' => 'fin de misión',\n  'ov_fleet_rocket' => 'Ataque con misiles',\n  'ov_fleet_exploration' => 'Exploración',\n  'ov_fleet_colonization' => 'Colonización de planeta',\n  'ov_fleet_no_flying' => 'No hay flotas en vuelo',\n  'ov_vennant' => ' enviado ',\n  'ov_planet_to' => 'desde el planeta ',\n  'ov_moon_to' => 'desde la luna ',\n  'ov_atteint' => ' enviado a ',\n  'ov_planet_to_target' => 'planeta ',\n  'ov_moon_to_target' => 'luna ',\n  'ov_debris_to_target' => 'campo de escombros ',\n  'ov_explo_to_target' => 'posición ',\n  'ov_explo_stay' => ' en exploración ',\n  'ov_explo_mission' => '. Misión: ',\n  'ov_rentrant' => ' regresando ',\n  'ov_planet_from' => 'desde el planeta ',\n  'ov_moon_from' => 'desde la luna ',\n  'ov_debris_from' => 'desde el campo de escombros ',\n  'ov_explo_from' => 'desde la posición ',\n  'ov_back_planet' => ' al planeta ',\n  'ov_back_moon' => ' a la luna ',\n  'ov_hostile' => ' del jugador ',\n  'ov_message' => 'Enviar mensaje',\n\n  'imp_user_points_struc' => 'Por construcciones',\n  'imp_user_points_tech' => 'Por investigaciones',\n  'imp_user_points_fleet' => 'Por flota',\n  'imp_user_points_def' => 'Por defensas',\n  'imp_user_points_res' => 'Por recursos',\n  'imp_user_points_all' => 'Total',\n  'NumberOfRaids' => 'Realizadas',\n  'RaidsWin' => 'Ganadas',\n  'RaidsLoose' => 'Perdidas',\n  'Economica' => 'Construcciones',\n  'Teching' => 'Investigaciones',\n  'imp_statistics' => 'Estadísticas',\n\n  'Points_1' => 'sectores',\n  'km' => 'km',\n  'orb' => 'Escombros en órbita',\n  'buildings_on_planet' => 'Construcciones',\n  'ov_planet_details' => 'Detalles del planeta',\n  'ov_building' => 'Edificios',\n  'ov_hangar' => 'Astilleros',\n  'ov_rank' => 'Rango',\n  'ov_rpg_new_level_miner' => 'Por tus logros económicos recibes Materia Oscura.',\n  'ov_rpg_new_level_raid' => 'Por tus ataques exitosos recibes Materia Oscura.',\n  'ov_points' => 'Puntos',\n  'ov_raids' => 'Incursiones',\n  'ov_experience' => 'Experiencia',\n  'ov_player_rpg' => 'Estadísticas del jugador',\n  'ov_banner' => 'Banner',\n  'ov_userbar' => 'Barra de usuario',\n  'ov_banner_empty_id' => 'SuperNova - ¡Únete al juego!',\n  'ov_new' => 'NUEVO',\n  'ov_overview' => 'Resumen',\n  'ov_manage' => 'Gestión',\n  'ov_return' => 'Volver al resumen',\n  'ov_rename' => 'Renombrar',\n  'ov_planet_rename_dialog_title' => 'Renombrar planeta/luna',\n  'ov_new_name' => 'Nuevo nombre',\n  'cur_governor' => 'Gobernador actual',\n  'ov_mrc_confirm_1' => '¿Estás seguro de que quieres reemplazar al gobernador',\n  'ov_mrc_confirm_2' => 'nivel',\n  'ov_mrc_confirm_3' => 'por el gobernador',\n  'ov_mrc_confirm_4' => 'de primer nivel? ¡Toda la Materia Oscura invertida en el gobernador actual SE PERDERÁ!',\n  'ov_manage_page_hint' => '<li class=\"warning\">¡ATENCIÓN! Al contratar un gobernador diferente al actual, el gobernador anterior es despedido SIN COMPENSACIÓN DE MATERIA OSCURA, y el nivel del nuevo gobernador será uno. ¡Planifica el desarrollo de tu Imperio seleccionando previamente el gobernador adecuado para cada planeta según su función planeada!</li>\n  <li>Los gobernadores son mercenarios que administran un planeta específico y otorgan ciertos bonos a ese planeta</li>\n  <li>Haz clic en la imagen del gobernador en la lista para ver su descripción y los bonos que proporciona</li>\n  <li>Para contratar un gobernador específico, haz clic en \"Contratar\"</li>\n  <li>Contratar nuevamente al gobernador actual aumenta su nivel y, por lo tanto, su efecto planetario</li>\n  <li>Algunos gobernadores tienen límites de nivel. Otros no</li>\n  <li>La teleportación mueve el planeta a nuevas coordenadas junto con las flotas que orbitan el planeta</li>\n  <li>Si el planeta tiene una luna, también se mueve a las nuevas coordenadas junto con las flotas</li>\n  <li>La teleportación no es posible si hay actividad de flotas cerca del planeta (es decir, hay flotas que tienen como punto de origen o destino el planeta, la luna o el campo de escombros)</li>\n  <li>Después de la teleportación, debes esperar un tiempo antes de la siguiente teleportación - la métrica espacial alterada alrededor del planeta debe normalizarse</li>',\n\n  'ov_gate_time_left' => 'Tiempo hasta el próximo salto',\n\n  'ov_teleport' => 'Teleportar',\n  'ov_teleport_new_coordinates' => 'Nuevas coordenadas',\n  'ov_teleport_err_none' => 'Planeta teleportado con éxito',\n  'ov_teleport_err_wrong_coordinates' => 'Coordenadas incorrectas',\n  'ov_teleport_err_fleet' => 'Hay actividad de flotas cerca del planeta',\n  'ov_teleport_err_destination_busy' => 'El destino está ocupado',\n  'ov_teleport_err_cooldown' => 'No puedes teleportarte dos veces seguidas. Espera a que se normalice la métrica espacial',\n  'ov_teleport_err_no_dark_matter' => 'No hay suficiente Materia Oscura para teleportar',\n  'ov_teleport_log_record' => 'Planeta {%2$d} %1$s teleportado desde coordenadas %3$s a coordenadas %4$s',\n\n  'ov_capital' => 'Hacer capital del Imperio',\n  'ov_capital_err_none' => 'El planeta es ahora la capital del Imperio',\n  'ov_capital_err_capital_already' => 'Este planeta ya es la capital del Imperio',\n  'ov_capital_err_no_dark_matter' => 'No hay suficiente Materia Oscura para trasladar la capital del Imperio',\n  'ov_capital_err_not_a_planet' => 'Solo un planeta puede ser la capital del Imperio',\n\n  'read_all_news' => 'Marcar todo como leído',\n\n  'imp_TBA' => 'Algo vendrá...',\n  'imp_experience_current' => 'Actual',\n  'imp_experience_to_level' => 'Siguiente nivel',\n\n  'ov_manage_special' => 'Funciones especiales',\n  'ov_password' => 'Tu contraseña para ingresar al juego',\n\n  'ov_governor_purchase' => 'El jugador compró al Gobernador %1$s ID %2$d nivel %3$d para el planeta %4$s',\n));"
  },
  {
    "path": "language/es/payment.mo.php",
    "content": "<?php\n\n/*\n#############################################################################\n#  Filename: payment.mo.php\n#  Project: SuperNova.WS\n#  Website: http://www.supernova.ws\n#  Description: Massive Multiplayer Online Browser Space Strategy Game\n#\n#  Copyright © 2009-2018 Gorlum for Project \"SuperNova.WS\"\n#############################################################################\n*/\n\n/**\n*\n* @package language\n* @system [Spanish]\n* @version 46d0\n*\n*/\n\n/**\n* DO NOT CHANGE\n*/\n\nif (!defined('INSIDE')) die();\n\n$a_lang_array = (array(\n  // Metamateria\n  'sys_metamatter_what_header' => 'Qué es la <span class=\"metamatter\">Metamateria</span>',\n  'sys_metamatter_what_description' => 'La <span class=\"metamatter\">Metamateria</span> (abreviado <span class=\"metamatter\">MM</span>) es un nombre convencional para un estado especial del Universo. En realidad, ni siquiera es materia, sino una probabilidad factorizable.<br /><br />\n  La <span class=\"metamatter\">Metamateria</span> no tiene estado, y al mismo tiempo está en todos los estados. No está en ningún lugar, y al mismo tiempo está en todas partes. Potencialmente puede convertirse en cualquier cosa y aparecer en cualquier lugar, si se actualiza correctamente su probabilidad.',\n  'sys_metamatter_what_purchase' => 'La <span class=\"metamatter\">Metamateria</span> puede considerarse como \"Materia Oscura comprable\". Si falta <span class=\"dark_matter\">Materia Oscura</span> para adquirir algo, la <span class=\"metamatter\">MM</span> se <span class=\"ok\">convertirá automáticamente</span> en la cantidad faltante de <span class=\"dark_matter\">MO</span> a una tasa de <span class=\"dark_matter\">1 MO</span> = <span class=\"metamatter\">1 MM</span>',\n\n  'pay_mm_convert_header' => 'Conversión de Metamateria a Materia Oscura',\n  'pay_mm_convert_text' => 'La Metamateria se convierte a Materia Oscura a una tasa de 1 a 1',\n  'pay_mm_convert_no_mm' => 'No tienes Metamateria - cómprala primero',\n  'pay_mm_convert_prefix' => 'Unidades de Metamateria',\n  'pay_mm_convert_suffix' => '',\n  'pay_mm_convert_do' => 'Convertir a MO',\n\n  'pay_msg_mm_convert_wrong_amount' => 'Cantidad incorrecta de Metamateria',\n  'pay_msg_mm_convert_not_enough' => 'No hay suficiente Metamateria para convertir a Materia Oscura',\n  'pay_msg_mm_convert_mm_error' => 'Error al modificar la cantidad de Metamateria',\n  'pay_msg_mm_convert_dm_error' => 'Error al modificar la cantidad de Materia Oscura',\n\n  'pay_mm_buy' => 'Comprar <span class=\"metamatter\">Metamateria</span>',\n  'pay_mm_buy_text_cost' => 'Costo',\n  'pay_mm_buy_text_unit' => 'es',\n  'pay_mm_buy_url_description' => 'La <span class=\"metamatter\">MM</span> solo puede adquirirse con dinero real',\n  'pay_mm_buy_url_get'  => 'Abre este enlace para más detalles',\n  'pay_mm_buy_url_none' => 'Contacta a la Administración del servidor para obtener <span class=\"metamatter\">Metamateria</span>',\n\n  'pay_mm_bonus_header' => 'Costo de la <span class=\"metamatter\">Metamateria</span> y bonos por compra al por mayor',\n  'pay_mm_bonus' => 'Al comprar <span class=\"metamatter\">MM</span> al por mayor se otorgan bonos',\n  'pay_mm_bonus_each' => 'desde %s <span class=\"metamatter\">MM</span> - bono del %d%% en cantidad de <span class=\"metamatter\">MM</span>',\n  'pay_mm_bonus_text' => 'Bono',\n\n  'pay_mm_buy_step1_text' => 'Selecciona la cantidad de <span class=\"metamatter\">MM</span>, método de pago y confirma tu elección',\n  'pay_mm_buy_metamatter_amount' => 'Selecciona la cantidad de <span class=\"metamatter\">Metamateria</span> de la lista',\n  'pay_mm_buy_metamatter_amount_enter' => '...o ingresa otra cantidad de <span class=\"metamatter\">Metamateria</span>',\n  'pay_mm_buy_price_for' => 'Precio por',\n  'pay_mm_buy_unit' => '<span class=\"metamatter\">Metamateria</span>',\n  'pay_mm_buy_select' => 'Selecciona el sistema de pago',\n  'pay_mm_buy_method_detail' => 'Algunos métodos de pago ofrecen diferentes sistemas de pago. Si un pago no se procesa a través de un sistema, intenta usar el mismo método de pago con otro sistema',\n  'pay_mm_buy_confirm' => 'Confirmar selección',\n  'pay_mm_buy_payment_selected' => 'El pago se realizará usando el sistema de pago',\n  'pay_mm_buy_purchase' => 'Compra',\n\n  'pay_mm_buy_payment_method_more' => 'Haz clic aquí para ver más métodos de pago',\n\n  'pay_mm_buy_payment_method_select' => 'Selecciona el método de pago',\n  'pay_mm_buy_payment_method_selected' => 'Has seleccionado el método de pago',\n\n  'pay_mm_buy_step2_text' => 'El costo estimado no incluye comisiones adicionales que puedan cobrar los sistemas de pago y/o intermediarios. Verifica la cantidad seleccionada de <span class=\"metamatter\">Metamateria</span> y el método de pago. Si todo es correcto, haz clic en \"Pagar <span class=\"metamatter\">Metamateria</span>\". Si cometiste un error, haz clic en \"Comenzar de nuevo\"',\n  'pay_mm_buy_pay' => 'Pagar <span class=\"metamatter\">Metamateria</span>',\n  'pay_mm_buy_reset' => 'Comenzar de nuevo',\n  'pay_mm_buy_in_progress' => 'Procesando pago...',\n  'pay_mm_buy_conversion_cost' => 'El costo estimado de %1$s unidades de <span class=\"metamatter\">Metamateria</span> en la moneda del sistema de pago será <span class=\"%4$s\">%2$s</span> %3$s',\n  'pay_mm_buy_cost_base' => 'El costo será',\n  'pay_mm_buy_real_income' => 'El bono por compra al por mayor será %s%% y se acreditarán %s <span class=\"metamatter\">MM</span> en tu cuenta de juego',\n  'pay_mm_buy_approximate_cost' => 'El costo aproximado de MM en el sistema de pago es <span class=\"notice\">%1$s %2$s</span> (el costo es APROXIMADO. El monto final en el sistema de pago puede variar)',\n\n  'pay_currency_name' => 'Moneda',\n  'pay_currency_symbol' => 'Símbolo',\n  'pay_currency_choose' => 'Selecciona la moneda de pago',\n  'pay_currency_list' => array(\n    'RUB' => 'Rublo ruso',\n    'USD' => 'Dólar estadounidense',\n    'EUR' => 'Euro',\n    'UAH' => 'Grivna ucraniana',\n  ),\n\n  'pay_methods' => array(\n    PAYMENT_METHOD_EMONEY => 'Monedero electrónico',\n    PAYMENT_METHOD_EMONEY_YANDEX => 'Yandex.Money',\n    PAYMENT_METHOD_EMONEY_WEBMONEY_WMR => 'WebMoney WMR',\n    PAYMENT_METHOD_EMONEY_WEBMONEY_WMZ => 'WebMoney WMZ',\n    PAYMENT_METHOD_EMONEY_WEBMONEY_WMU => 'WebMoney WMU',\n    PAYMENT_METHOD_EMONEY_WEBMONEY_WME => 'WebMoney WME',\n    PAYMENT_METHOD_EMONEY_WEBMONEY_WMB => 'WebMoney WMB',\n    PAYMENT_METHOD_EMONEY_QIWI => 'QIWI Wallet',\n    PAYMENT_METHOD_EMONEY_ELECSNET => 'Elecsnet Wallet',\n    PAYMENT_METHOD_EMONEY_MAILRU => 'Dinero@Mail.Ru',\n    PAYMENT_METHOD_EMONEY_EASYPAY => 'EasyPay',\n    PAYMENT_METHOD_EMONEY_RUR_W1R => 'RUR Unified Wallet',\n    PAYMENT_METHOD_EMONEY_TELEMONEY => 'TeleMoney',\n\n    PAYMENT_METHOD_BANK_CARD => 'Tarjeta de pago (VISA, MasterCard, etc)',\n    PAYMENT_METHOD_BANK_CARD_STANDARD => 'Tarjeta bancaria',\n    PAYMENT_METHOD_BANK_CARD_LIQPAY => 'LiqPay',\n    PAYMENT_METHOD_BANK_CARD_EASYPAY => 'EasyPay',\n    PAYMENT_METHOD_BANK_CARD_AMERICAN_EXPRESS => 'American Express',\n    PAYMENT_METHOD_BANK_CARD_JCB => 'JCB',\n    PAYMENT_METHOD_BANK_CARD_UNIONPAY => 'UnionPay',\n\n    PAYMENT_METHOD_BANK_INTERNET => 'A través de banca por internet',\n    PAYMENT_METHOD_BANK_INTERNET_ALFA_BANK => 'Alfa-Click',\n    PAYMENT_METHOD_BANK_INTERNET_RUSSKIY_STANDART => 'Banco Russian Standard',\n    PAYMENT_METHOD_BANK_INTERNET_PROSMVYAZBANK => 'Promsvyazbank',\n    PAYMENT_METHOD_BANK_INTERNET_VTB24 => 'VTB24',\n    PAYMENT_METHOD_BANK_INTERNET_OCEAN_BANK => 'Ocean Bank',\n    PAYMENT_METHOD_BANK_INTERNET_HANDY_BANK => 'HandyBank',\n    PAYMENT_METHOD_BANK_INTERNET_007 => 'Banco Bogorodsky',\n    PAYMENT_METHOD_BANK_INTERNET_008 => 'Banco Obrazovanie',\n    PAYMENT_METHOD_BANK_INTERNET_009 => 'FlexBank',\n    PAYMENT_METHOD_BANK_INTERNET_010 => 'FutureBank',\n    PAYMENT_METHOD_BANK_INTERNET_011 => 'KranBank',\n    PAYMENT_METHOD_BANK_INTERNET_012 => 'Kostromaselkombank',\n    PAYMENT_METHOD_BANK_INTERNET_013 => 'Banco Regional de Lipetsk',\n    PAYMENT_METHOD_BANK_INTERNET_014 => 'Banco Independiente de Construcción',\n    PAYMENT_METHOD_BANK_INTERNET_015 => 'Russian Trust Bank',\n    PAYMENT_METHOD_BANK_INTERNET_016 => 'WestInterBank',\n    PAYMENT_METHOD_BANK_INTERNET_017 => 'MezhTopEnergoBank',\n    PAYMENT_METHOD_BANK_INTERNET_018 => 'Banco Industrial de Moscú',\n    PAYMENT_METHOD_BANK_INTERNET_019 => 'Banco Intesa',\n    PAYMENT_METHOD_BANK_INTERNET_020 => 'Banco Ciudad',\n    PAYMENT_METHOD_BANK_INTERNET_021 => 'Banco AVB',\n    PAYMENT_METHOD_BANK_INTERNET_BANK24 => 'Bank24 Crédito Nacional',\n    PAYMENT_METHOD_BANK_INTERNET_PRIVAT24 => \"Privat24\",\n    PAYMENT_METHOD_BANK_INTERNET_SBERBANK => \"Sberbank Online\",\n\n    PAYMENT_METHOD_BANK_TRANSFER => 'Transferencia bancaria',\n\n    PAYMENT_METHOD_MOBILE => 'Desde teléfono móvil',\n    PAYMENT_METHOD_MOBILE_SMS => 'SMS',\n    PAYMENT_METHOD_MOBILE_PAYPAL_ZONG => 'Desde cuenta o SMS',\n    PAYMENT_METHOD_MOBILE_MEGAPHONE => 'Megafon',\n    PAYMENT_METHOD_MOBILE_MTS => 'MTS',\n    PAYMENT_METHOD_MOBILE_KYIVSTAR => 'Kyivstar',\n    PAYMENT_METHOD_MOBILE_BEELINE => 'Beeline',\n\n    PAYMENT_METHOD_TERMINAL => 'Terminal de pago',\n    PAYMENT_METHOD_TERMINAL_QIWI => 'QIWI Wallet',\n    PAYMENT_METHOD_TERMINAL_ELECSNET => 'Elecsnet',\n    PAYMENT_METHOD_TERMINAL_ELEMENT => 'Mobil Element',\n    PAYMENT_METHOD_TERMINAL_KASSIRANET => 'Kassira.net',\n    PAYMENT_METHOD_TERMINAL_IBOX => 'Ibox',\n    PAYMENT_METHOD_TERMINAL_UKRAINE => 'Terminales de Ucrania',\n    PAYMENT_METHOD_TERMINAL_RUSSIA => 'Terminales de Rusia',\n    PAYMENT_METHOD_TERMINAL_EASYPAY => 'EasyPay',\n\n    PAYMENT_METHOD_OTHER => 'Otros métodos',\n    PAYMENT_METHOD_OTHER_EVROSET => 'Euroset',\n    PAYMENT_METHOD_OTHER_SVYAZNOY => 'Svyaznoy',\n    PAYMENT_METHOD_OTHER_ROBOKASSA_MOBILE => 'ROBOKASSA Móvil',\n\n    PAYMENT_METHOD_GENERIC => 'No se enumeran todos los métodos de pago posibles. Si no encuentras un método adecuado, utiliza los servicios de agregadores',\n  ),\n\n  'pay_currency_exchange_title' => 'Tipos de cambio internos',\n  'pay_currency_exchange_rate' => 'Tipo de cambio',\n  'pay_currency_exchange_direct' => 'Directo',\n  'pay_currency_exchange_reverse' => 'Inverso',\n  'pay_currency_exchange_mm' => '<span class=\"metamatter\">MM</span> por 1 u.m.',\n  'pay_currency_exchange_note' => 'El tipo de cambio interno se utiliza para convertir de la moneda principal del servidor a la moneda del sistema de pago. El tipo de cambio no incluye comisiones de intermediarios y/o sistemas de pago',\n\n  'pay_msg_mm_purchase_complete'   => 'Has pagado exitosamente por %d unidades de Metamateria a través del servicio %s. Se te han acreditado %s unidades de <span class=\"metamatter\">Metamateria</span>',\n  'pay_msg_mm_purchase_incomplete' => 'Tu pago por %d unidades de <span class=\"metamatter\">Metamateria</span> a través del servicio %s no se ha completado. Si crees que hubo un error, contacta a la Administración del servidor',\n  'pay_msg_mm_purchase_test'       => 'En realidad, es una broma. ¡El pago fue de prueba, así que no recibiste nada, ja ja ja! Si crees que es un error, contacta a la Administración del servidor',\n\n  'pay_msg_request_user_found' => 'Usuario encontrado',\n  'pay_msg_request_payment_complete' => 'Pago completado',\n  'pay_msg_request_payment_cancel_complete' => 'Pago cancelado exitosamente',\n\n  'pay_msg_request_unsupported' => 'Este tipo de solicitud no es compatible',\n  'pay_msg_request_signature_invalid' => 'Firma de solicitud incorrecta',\n  'pay_msg_request_user_invalid' => 'ID de usuario incorrecto',\n  'pay_msg_request_server_wrong' => 'Servidor incorrecto',\n  'pay_msg_request_payment_amount_invalid' => 'Monto de pago incorrecto',\n  'pay_msg_request_payment_id_invalid' => 'ID de pago incorrecto',\n  'pay_msg_request_payment_date_invalid' => 'Fecha de pago incorrecta',\n  'pay_msg_request_internal_error' => 'Error interno del servidor. Intenta realizar el pago más tarde',\n  'pay_msg_request_paylink_unsupported' => 'Este tipo de enlace de pago no es compatible. Posiblemente se esté usando una versión obsoleta de SN, incompatible con este módulo de pago',\n  'pay_msg_request_payment_write_error' => 'Error al registrar el pago',\n  'pay_msg_request_payment_cancelled_already' => 'El pago ya fue cancelado',\n  'pay_msg_request_payment_cancel_not_complete' => 'El pago aún no se ha completado y no puede cancelarse',\n  'pay_msg_request_payment_cancelled' => '!!! ¡El pago fue revocado por el sistema de pago!',\n  'pay_msg_request_payment_not_found' => 'Pago no encontrado',\n\n  'pay_msg_module_disabled' => 'Módulo de pago desactivado',\n\n  'pay_msg_mm_request_money_and_mm_mismatched' => 'No coincide el monto del pago con la cantidad de MM comprada',\n\n  'pay_msg_mm_request_amount_invalid' => 'Cantidad incorrecta de <span class=\"metamatter\">Metamateria</span>',\n  'pay_msg_mm_request_config_invalid' => 'Error en la configuración del módulo de pago. Contacta a la Administración del servidor',\n  'pay_msg_mm_request_mm_adjust_error' => 'Error al acreditar <span class=\"metamatter\">Metamateria</span>',\n\n  'pay_msg_request_error_db_payment_create' => 'Error al crear el pago en la base de datos',\n  'pay_msg_request_error_test_payment' => 'El estado del pago en la base de datos no coincide con la información en la solicitud',\n  'pay_error_internal_no_external_currency_set' => '¡ERROR INTERNO o ERROR DE CONFIGURACIÓN DEL MÓDULO DE PAGO! ¡No se ha establecido la moneda del sistema de pago! Por favor, informa a la Administración del servidor!',\n));"
  },
  {
    "path": "language/es/quest.mo.php",
    "content": "<?php\n\n/*\n#############################################################################\n#  Filename: quest.mo.php\n#  Project: SuperNova.WS\n#  Website: http://www.supernova.ws\n#  Description: Massive Multiplayer Online Browser Space Strategy Game\n#\n#  Copyright © 2009-2018 Gorlum for Project \"SuperNova.WS\"\n#############################################################################\n*/\n\n/**\n*\n* @package language\n* @system [Spanish]\n* @version 46d0\n*\n*/\n\n/**\n* DO NOT CHANGE\n*/\n\nif (!defined('INSIDE')) die();\n\n\n$a_lang_array = (array(\n  'qst_quest' => 'Misión',\n  'qst_quest_of' => 'de la misión',\n  'qst_name' => 'Nombre',\n  'qst_description' => 'Descripción',\n  'qst_adm_conditions' => 'Requisitos',\n  'qst_conditions' => 'Necesitas construir/investigar',\n  'qst_rewards' => 'Recompensa por completar la misión',\n  'qst_total' => 'Misiones',\n  'qst_status' => 'Estado',\n  'qst_status_list' => array(\n    QUEST_STATUS_ALL => '-- Todas las misiones --',\n    QUEST_STATUS_NOT_STARTED => 'No&nbsp;iniciada',\n    QUEST_STATUS_STARTED => 'Iniciada',\n    QUEST_STATUS_EXCEPT_COMPLETE => 'Todas,&nbsp;excepto&nbsp;completadas',\n    QUEST_STATUS_COMPLETE => 'Completada',\n  ),\n\n  'qst_filter_by_status' => 'Mostrar misiones con estado',\n\n  'qst_add' => 'Añadir misión',\n  'qst_edit' => 'Editar misión',\n  'qst_copy' => 'Copiar misión',\n  'qst_mode_add' => 'Añadir',\n  'qst_mode_edit' => 'Editar',\n  'qst_mode_copy' => 'Copiar',\n  'qst_adm_err_unit_id' => 'Unidad incorrecta',\n  'qst_adm_err_unit_amount' => 'Cantidad de unidades incorrecta',\n  'qst_adm_err_reward_amount' => 'Cantidad de recompensa incorrecta',\n  'qst_adm_err_reward_type' => 'Tipo de recompensa incorrecto',\n  'qst_adm_err_reward_empty' => 'Recompensa de misión vacía',\n));"
  },
  {
    "path": "language/es/search.mo.php",
    "content": "<?php\n\n/*\n#############################################################################\n#  Filename: search.mo.php\n#  Project: SuperNova.WS\n#  Website: http://www.supernova.ws\n#  Description: Massive Multiplayer Online Browser Space Strategy Game\n#\n#  Copyright © 2009-2018 Gorlum for Project \"SuperNova.WS\"\n#############################################################################\n*/\n\n/**\n*\n* @package language\n* @system [Spanish]\n* @version #46d0#\n*\n*/\n\n/**\n* DO NOT CHANGE\n*/\n\nif (!defined('INSIDE')) die();\n\n$a_lang_array = (array(\n  'srch_title' => 'Búsqueda en el servidor',\n  'srch_search_do' => 'Buscar',\n  'srch_result_none' => 'No se encontraron resultados para esta búsqueda',\n  'srch_player_name' => 'Nombre del jugador',\n  'srch_ally_name' => 'Nombre de la Alianza',\n  'srch_ally_members' => 'Miembros',\n  'srch_planet_name' => 'Nombre del planeta',\n  'srch_rank' => 'Posición',\n  'srch_action_pm' => 'Enviar mensaje',\n  'srch_action_buddy' => 'Añadir a amigos',\n\n  'srch_aka' => 'También conocido como',\n\n  'srch_page_hint' => '<li>Solo se muestran los primeros 30 resultados</li>\n  <li>Si un jugador ha cambiado su nombre y uno de sus nombres anteriores coincide con la búsqueda, también se mostrará en una línea separada.\n  En este caso, se mostrará el nombre actual del jugador, y entre paréntesis <span class=\"warning\">en color</span> se indicará el nombre anterior</li>',\n));"
  },
  {
    "path": "language/es/stat.mo.php",
    "content": "<?php\n\n/*\n#############################################################################\n#  Filename: stat.mo.php\n#  Project: SuperNova.WS\n#  Website: http://www.supernova.ws\n#  Description: Massive Multiplayer Online Browser Space Strategy Game\n#\n#  Copyright © 2009-2018 Gorlum for Project \"SuperNova.WS\"\n#############################################################################\n*/\n\n/**\n*\n* @package language\n* @system [Spanish]\n* @version 46d0\n*\n*/\n\n/**\n* DO NOT CHANGE\n*/\n\nif (!defined('INSIDE')) die();\n\n$a_lang_array = (array(\n  'stat_header' => 'Estadísticas',\n  'stat_refresh_last' => 'Última actualización:',\n  'stat_refresh_next' => 'Próxima actualización:',\n\n  'stat_rank' => 'Posición',\n  'stat_rank_diff' => 'Cambio',\n  'stat_points' => 'Puntos',\n  'stat_per_member' => 'Por miembro',\n  'stat_members' => 'Miembros',\n  'stat_message_write' => 'Escribir mensaje',\n\n  'stat_show' => 'Mostrar estadísticas',\n  'stat_type' => array(\n    STAT_TOTAL => 'general',\n    STAT_FLEET => 'flotas',\n    STAT_TECH => 'investigaciones',\n    STAT_BUILDING => 'construcciones',\n    STAT_DEFENSE => 'defensas',\n    STAT_RESOURCE => 'recursos',\n    STAT_RAID_TOTAL => 'batallas realizadas',\n    STAT_RAID_WON => 'batallas ganadas',\n    STAT_RAID_LOST => 'batallas perdidas',\n    STAT_LVL_BUILDING => 'niveles de construcción',\n    STAT_LVL_TECH => 'niveles de investigación',\n    STAT_LVL_RAID => 'niveles de incursión',\n  ),\n\n  'stat_by' => 'para',\n  'stat_player' => 'Jugadores',\n  'stat_allys' => 'Alianzas',\n  'stat_range' => 'posiciones',\n\n  'stat_details' => 'Información del jugador',\n));"
  },
  {
    "path": "language/es/system.mo.php",
    "content": "<?php\n\n/*\n#############################################################################\n#  Filename: system.mo.php\n#  Project: SuperNova.WS\n#  Website: http://www.supernova.ws\n#  Description: Massive Multiplayer Online Browser Space Strategy Game\n#\n#  Copyright © 2009 Gorlum for Project \"SuperNova.WS\"\n#  Copyright © 2009 MSW\n#############################################################################\n*/\n\n/**\n*\n* @package language\n* @system [Spanish]\n* @version #46d0#\n*\n*/\n\n/**\n* DO NOT CHANGE\n*/\n\nuse Fleet\\Constants;\n\nif (!defined('INSIDE'))\n{\n  exit;\n}\n\n// System-wide localization\n\nglobal $config;\n\n$a_lang_array = [\n  'sys_birthday' => 'Cumpleaños',\n  'sys_birthday_message' => '¡%1$s! La administración de SuperNova te felicita cordialmente por tu cumpleaños, que cayó en %2$s y te regala %3$d %4$s. ¡Te deseamos mucho éxito en el juego y altos rangos en las estadísticas! Puede que este saludo llegue tarde, pero mejor tarde que nunca.',\n\n  'adm_err_denied' => 'Acceso denegado. No tienes los permisos necesarios para usar esta página de la interfaz de administración del servidor',\n\n  'sys_empire'          => 'Imperio',\n  'VacationMode'\t\t\t=> \"Tu producción está cerrada porque estás de vacaciones\",\n  'sys_moon_destruction_report' => \"Informe de destrucción de luna\",\n  'sys_moon_destroyed' => \"¡Tus Estrellas de la Muerte han producido una poderosa onda gravitacional que ha destruido la luna!\",\n  'sys_rips_destroyed' => \"Tus Estrellas de la Muerte han producido una poderosa onda gravitacional, pero no fue suficiente para destruir una luna de este tamaño. Sin embargo, la onda gravitacional rebotó en la superficie lunar y destruyó tu flota.\",\n  'sys_rips_come_back' => \"Tus Estrellas de la Muerte no tienen suficiente energía para dañar esta luna. Tu flota regresa sin destruir la luna.\",\n  'sys_chance_moon_destroy' => \"Probabilidad de destrucción lunar: \",\n  'sys_chance_rips_destroy' => \"Probabilidad de destrucción por onda gravitacional: \",\n\n  'sys_impersonate' => 'Personificar',\n  'sys_impersonate_done' => 'Dejar de personificar',\n  'sys_impersonated_as' => '¡ATENCIÓN! Actualmente estás personificando al jugador %1$s. No olvides que en realidad eres %2$s. Puedes dejar de personificar seleccionando la opción correspondiente en el menú.',\n\n  'menu_admin_mining'          => 'Minería de jugadores',\n  'menu_admin_units'          => 'Unidades',\n  'menu_admin_ube_balance'          => 'Balance UBE',\n\n  'sys_day' => \"días\",\n  'sys_hrs' => \"horas\",\n  'sys_min' => \"minutos\",\n  'sys_sec' => \"segundos\",\n  'sys_day_short' => \"d\",\n  'sys_hrs_short' => \"h\",\n  'sys_min_short' => \"m\",\n  'sys_sec_short' => \"s\",\n\n  'sys_ask_admin' => 'Enviar preguntas y sugerencias a',\n\n  'sys_wait'      => 'Solicitud en proceso. Por favor, espera.',\n\n  'sys_fleets'       => 'Flotas',\n  'sys_expeditions'  => 'Expediciones',\n  'sys_fleet'        => 'Flota',\n  'sys_expedition'   => 'Expedición',\n  'sys_event_next'   => 'Próximo evento:',\n  'sys_event_arrive' => 'llegará',\n  'sys_event_stay'   => 'terminará la misión',\n  'sys_event_return' => 'regresará',\n\n  'sys_total'           => \"TOTAL\",\n  'sys_need'\t\t\t\t=> 'Necesita',\n  'sys_register_date'   => 'Fecha de registro',\n\n  'sys_attacker' \t\t=> \"Atacante\",\n  'sys_defender' \t\t=> \"Defensor\",\n\n  'COE_combatSimulator' => \"Simulador de combate\",\n  'COE_simulate'        => \"Ejecutar simulador\",\n  'COE_fleet'           => \"Flota\",\n  'COE_defense'         => \"Defensa\",\n  'sys_coe_combat_start'=> \"Las flotas rivales se han encontrado\",\n  'sys_coe_combat_end'  => \"Resultados del combate\",\n  'sys_coe_round'       => \"Ronda\",\n\n  'sys_coe_attacker_turn'=> 'El atacante dispara con una potencia total de %1$s. Los escudos del defensor absorben %2$s disparos<br />',\n  'sys_coe_defender_turn'=> 'El defensor dispara con una potencia total de %1$s. Los escudos del atacante absorben %2$s disparos<br /><br /><br />',\n  'sys_coe_outcome_win'  => '¡El defensor ha ganado la batalla!<br />',\n  'sys_coe_outcome_loss' => '¡El atacante ha ganado la batalla!<br />',\n  'sys_coe_outcome_loot' => 'Obtiene %1$s de metal, %2$s de cristal, %3$s de deuterio<br />',\n  'sys_coe_outcome_draw' => 'La batalla terminó en empate.<br />',\n  'sys_coe_attacker_lost'=> 'El atacante perdió %1$s unidades.<br />',\n  'sys_coe_defender_lost'=> 'El defensor perdió %1$s unidades.<br />',\n  'sys_coe_debris_left'  => 'Ahora en estas coordenadas espaciales hay %1$s de metal y %2$s de cristal.<br /><br />',\n  'sys_coe_moon_chance'  => 'La probabilidad de que aparezca una luna es del %1$s%%<br />',\n  'sys_coe_rw_time'      => 'Tiempo de generación de la página: %1$s segundos<br />',\n\n  'sys_resources'       => \"Recursos\",\n  'sys_ships'           => \"Naves\",\n  'sys_metal'          => \"Metal\",\n  'sys_metal_sh'       => \"M\",\n  'sys_crystal'        => \"Cristal\",\n  'sys_crystal_sh'     => \"C\",\n  'sys_deuterium'      => \"Deuterio\",\n  'sys_deuterium_sh'   => \"D\",\n  'sys_energy'         => \"Energía\",\n  'sys_energy_sh'      => \"E\",\n  'sys_dark_matter'    => \"Materia Oscura\",\n  'sys_dark_matter_sh' => \"MO\",\n  'sys_metamatter'     => \"Metamateria\",\n  'sys_metamatter_sh'  => \"MM\",\n\n  'sys_reset'           => \"Reiniciar\",\n  'sys_send'            => \"Enviar\",\n  'sys_characters'      => \"caracteres\",\n  'sys_back'            => \"Atrás\",\n  'sys_return'          => \"Volver\",\n  'sys_delete'          => \"Eliminar\",\n  'sys_writeMessage'    => \"Escribir mensaje\",\n  'sys_hint'            => \"Sugerencia\",\n\n  'sys_alliance'        => \"Alianza\",\n  'sys_player'          => \"Jugador\",\n  'sys_coordinates'     => \"Coordenadas\",\n\n  'sys_online'          => \"En línea\",\n  'sys_offline'         => \"Desconectado\",\n  'sys_status'          => \"Estado\",\n\n  'sys_universe'        => \"Universo\",\n  'sys_goto'            => \"Ir a\",\n\n  'sys_time'            => \"Tiempo\",\n  'sys_temperature'\t\t=> 'Temperatura',\n\n  'sys_no_task'         => \"sin tarea\",\n\n  'sys_affilates'       => \"Jugadores invitados\",\n\n  'sys_fleet_arrived'   => \"Flota llegada\",\n\n  'sys_planet_type' => [\n    PT_PLANET => 'Planeta',\n    PT_DEBRIS => 'Campo de escombros',\n    PT_MOON   => 'Luna',\n  ],\n\n  'sys_planet_type_sh' => [\n    PT_PLANET => '(P)',\n    PT_DEBRIS => '(E)',\n    PT_MOON   => '(L)',\n  ],\n\n  'sys_planet_expedition' => 'espacio inexplorado',\n\n  'sys_capacity' \t\t\t=> 'Capacidad de carga',\n  'sys_cargo_bays' \t\t=> 'Bodegas',\n\n  'sys_supernova' \t\t=> 'SuperNova',\n  'sys_server' \t\t\t=> 'Servidor',\n\n  'sys_unbanned'\t\t\t=> 'Desbloqueado',\n\n  'sys_date_time'\t\t\t=> 'Fecha y hora',\n  'sys_from_person'\t   => 'De',\n  'sys_from_speed'\t   => 'desde',\n\n  'sys_from'\t\t  => 'de',\n  'tp_on'            => 'en',\n\n// Resource page\n  'res_planet_production' => 'Producción de recursos en el planeta',\n  'res_basic_starting_resources' => 'Recursos iniciales en el planeta',\n  'res_basic_income' => 'Producción natural',\n  'res_basic_storage_size' => 'Tamaño de los almacenes',\n  'res_total' => 'TOTAL',\n  'res_calculate' => 'Calcular',\n  'res_hourly' => 'Por hora',\n  'res_daily' => 'Por día',\n  'res_weekly' => 'Por semana',\n  'res_monthly' => 'Por mes',\n  'res_storage_fill' => 'Llenado de almacenes',\n  'res_hint' => '<ul><li>Una producción de recursos <100% significa falta de energía. Construye plantas de energía adicionales o reduce la producción de recursos<li>Si tu producción es 0%, probablemente acabas de salir de vacaciones y necesitas activar todas las fábricas<li>Para ajustar la producción de todas las fábricas a la vez, usa el menú desplegable en el encabezado de la tabla. Es especialmente útil después de salir de vacaciones</ul>',\n\n// Build page\n  'bld_destroy' => 'Destruir',\n  'bld_create'  => 'Construir',\n  'bld_research' => 'Investigar',\n  'bld_hire' => 'Contratar',\n\n// Imperium page\n  'imp_imperator' => \"Emperador\",\n  'imp_overview' => \"Resumen del Imperio\",\n  'imp_fleets' => \"Flotas en vuelo\",\n  'imp_production' => \"Producción\",\n  'imp_name' => \"Nombre\",\n  'imp_research' => \"Investigaciones\",\n  'imp_exploration' => \"Expediciones\",\n  'imp_imperator_none' => \"¡No existe tal Emperador en el Universo!\",\n  'sys_fields' => \"Sectores\",\n\n// Cookies\n  'err_cookie' => \"¡Error! No se puede autenticar al usuario con la información de la cookie.<br />Borra las cookies del navegador y luego intenta <a href='login\" . DOT_PHP_EX . \"'>iniciar sesión</a> en el juego o <a href='reg\" . DOT_PHP_EX . \"'>registrarte</a>.\",\n\n// Supported languages\n  'ru'              \t  => 'Ruso',\n  'en'              \t  => 'Inglés',\n\n  'sys_vacation'        => 'Estás de vacaciones hasta',\n  'sys_vacation_leave'  => '¡Ya he descansado - salir de vacaciones!',\n  'sys_vacation_in'     => 'De vacaciones',\n  'sys_level'           => 'Nivel',\n  'sys_level_short'     => 'Nv',\n  'sys_level_max'       => 'Nivel máximo',\n\n  'sys_yes'             => 'Sí',\n  'sys_no'              => 'No',\n\n  'sys_on'              => 'Activado',\n  'sys_off'             => 'Desactivado',\n\n  'sys_confirm'         => 'Confirmar',\n  'sys_save'            => 'Guardar',\n  'sys_create'          => 'Crear',\n  'sys_write_message'   => 'Escribir mensaje',\n\n// top bar\n  'top_of_year' => 'año',\n  'top_online'\t\t\t=> 'Jugadores',\n\n  'sys_first_round_crash_1'\t=> 'Se perdió el contacto con la flota atacada.',\n  'sys_first_round_crash_2'\t=> 'Esto significa que fue destruida en la primera ronda de batalla.',\n\n  'sys_ques' => [\n    QUE_STRUCTURES => 'Edificios',\n    QUE_HANGAR     => 'Astillero',\n    SUBQUE_DEFENSE => 'Defensa',\n    QUE_RESEARCH   => 'Investigaciones',\n  ],\n\n  'navbar_button_expeditions_short' => 'Exped',\n  'navbar_button_fleets' => 'Flotas',\n  'navbar_button_quests' => 'Misiones',\n  'navbar_font' => 'Fuente',\n  'navbar_font_normal' => 'Normal',\n  'sys_que_structures' => 'Edificios',\n  'sys_que_hangar' => 'Astillero',\n  'sys_que_defense' => 'Defensa',\n  'sys_que_research' => 'Investigaciones',\n  'sys_que_research_short' => 'Ciencia',\n\n  'eco_que' => 'Cola',\n  'eco_que_empty' => 'La cola está vacía',\n  'eco_que_clear' => 'Limpiar cola',\n  'eco_que_trim'  => 'Cancelar último',\n  'eco_que_artifact'  => 'Usar Artefacto',\n\n  'sys_cancel' => 'Cancelar',\n\n  'sys_overview'\t\t\t=> 'Resumen',\n  'mod_marchand'\t\t\t=> 'Mercader',\n  'sys_galaxy'\t\t\t=> 'Galaxia',\n  'sys_system'\t\t\t=> 'Sistema',\n  'sys_planet'\t\t\t=> 'Planeta',\n  'sys_planet_title'\t\t\t=> 'Tipo de planeta',\n  'sys_planet_title_short'\t\t\t=> 'Tipo',\n  'sys_moon'\t\t\t=> 'Luna',\n  'sys_error'\t\t\t=> 'Error',\n  'sys_done'\t\t\t\t=> 'Hecho',\n  'sys_no_vars'\t\t\t=> 'Error al inicializar variables, ¡contacta con la administración!',\n  'sys_attacker_lostunits'\t\t=> 'El atacante perdió %s unidades.',\n  'sys_defender_lostunits'\t\t=> 'El defensor perdió %s unidades.',\n  'sys_gcdrunits' \t\t\t=> 'Ahora en estas coordenadas espaciales hay %s %s y %s %s.',\n  'sys_moonproba' \t\t\t=> 'La probabilidad de que aparezca una luna es: %d %% ',\n  'sys_moonbuilt' \t\t\t=> '¡Gracias a la enorme energía, grandes trozos de metal y cristal se unen y forman una nueva luna %s %s!',\n  'sys_attack_title'    \t\t=> '%s. Ocurrió una batalla entre las siguientes flotas:',\n  'sys_attack_attacker_pos'      \t=> 'Atacante %s [%s:%s:%s]',\n  'sys_attack_techologies' \t=> 'Armamento: %d %% Escudos: %d %% Blindaje: %d %% ',\n  'sys_attack_defender_pos' \t=> 'Defensor %s [%s:%s:%s]',\n  'sys_ship_type' \t\t\t=> 'Tipo',\n  'sys_ship_count' \t\t=> 'Cantidad',\n  'sys_ship_weapon' \t\t=> 'Armamento',\n  'sys_ship_shield' \t\t=> 'Escudos',\n  'sys_ship_armour' \t\t=> 'Blindaje',\n  'sys_ship_speed' \t\t=> 'Velocidad',\n  'sys_ship_consumption' \t\t=> 'Consumo',\n  'sys_ship_capacity' \t\t=> 'Bodega/Tanque',\n  'sys_destroyed' \t\t\t=> 'destruido',\n  'sys_attack_attack_wave' \t=> 'El atacante dispara con una potencia total de %s al defensor. Los escudos del defensor absorben %s disparos.',\n  'sys_attack_defend_wave'\t\t=> 'El defensor dispara con una potencia total de %s al atacante. Los escudos del atacante absorben %s disparos.',\n  'sys_attacker_won' \t\t=> '¡El atacante ha ganado la batalla!',\n  'sys_defender_won' \t\t=> '¡El defensor ha ganado la batalla!',\n  'sys_both_won' \t\t\t=> '¡La batalla terminó en empate!',\n  'sys_stealed_ressources' \t=> 'Obtiene %s de metal %s %s de cristal %s y %s de deuterio.',\n  'sys_rapport_build_time' \t=> 'Tiempo de generación de la página: %s segundos',\n  'sys_mess_tower' \t\t=> 'Transporte',\n  'sys_coe_lost_contact' \t\t=> 'Se perdió el contacto con tu flota',\n  'sys_spy_activity' => 'Se observa actividad de espionaje cerca de tus planetas',\n  'sys_spy_materials' \t\t=> 'Materiales en',\n  'sys_spy_fleet' \t\t\t=> 'Flota',\n  'sys_spy_defenses' \t\t=> 'Defensa',\n  'sys_mess_qg' \t\t\t=> 'Comando de flota',\n  'sys_mess_spy_report' \t\t=> 'Informe de espionaje',\n  'sys_mess_spy_lostproba' \t=> 'Margen de error de la información obtenida por el satélite %d %% ',\n  'sys_mess_spy_detect_chance' \t=> 'Probabilidad de detección de tu flota de reconocimiento: %d%%',\n  'sys_mess_spy_detect_chance_no_percent' \t=> 'Probabilidad de detección de tu flota de reconocimiento',\n  'sys_mess_spy_control' \t\t=> 'Contraespionaje',\n  'sys_mess_spy_activity' \t\t=> 'Actividad de espionaje',\n  'sys_mess_spy_enemy_fleet' \t=> 'Flota enemiga del planeta',\n  'sys_mess_spy_seen_at'\t\t=> 'fue detectada cerca del planeta',\n  'sys_mess_spy_destroyed'\t\t=> 'La flota de reconocimiento fue destruida',\n  'sys_mess_spy_destroyed_enemy'\t\t=> 'Flota espía enemiga destruida',\n  'sys_object_arrival'\t\t=> 'Llegó al planeta',\n  'sys_stay_mess_stay' => 'Reubicación de flota',\n  'sys_stay_mess_start' \t\t=> 'Tu flota ha llegado al planeta',\n  'sys_stay_mess_back'\t\t=> 'Tu flota ha regresado ',\n  'sys_stay_mess_end'\t\t=> ' y ha entregado:',\n  'sys_stay_mess_bend'\t\t=> ' y ha entregado los siguientes recursos:',\n  'sys_adress_planet' \t\t=> '[%s:%s:%s]',\n  'sys_stay_mess_goods' \t\t=> '%s : %s, %s : %s, %s : %s',\n  'sys_colo_mess_from' \t\t=> 'Colonización',\n  'sys_colo_mess_report' \t\t=> 'Informe de colonización',\n  'sys_colo_defaultname' \t\t=> 'Colonia',\n  'sys_colo_arrival' \t\t=> 'La flota alcanza las coordenadas ',\n  'sys_colo_maxcolo' \t\t=> ', pero no se puede colonizar el planeta, se ha alcanzado el número máximo de colonias para tu nivel de colonización',\n  'sys_colo_allisok' \t\t=> ', y los colonos comienzan a establecerse en el nuevo planeta.',\n  'sys_colo_badpos'  \t\t\t=> ', y los colonos encontraron el entorno poco favorable para tu imperio. La misión de colonización regresa al planeta de origen.',\n  'sys_colo_notfree' \t\t\t=> ', y los colonos no encontraron un planeta en esas coordenadas. Se ven obligados a regresar completamente desconcertados.',\n  'sys_colo_no_colonizer'     => 'No hay colonizador en la flota',\n  'sys_colo_planet'  \t\t=> ' ¡Planeta colonizado!',\n  'sys_expe_report' \t\t=> 'Informe de expedición',\n  'sys_recy_report' \t\t=> 'Información del sistema',\n  'sys_expe_blackholl_1' \t\t=> '¡Tu flota entró en un agujero negro y se perdió parcialmente!',\n  'sys_expe_blackholl_2' \t\t=> '¡Tu flota entró en un agujero negro y se perdió por completo!',\n  'sys_expe_nothing_1' \t\t=> '¡Tus investigadores fueron testigos de una SuperNova! Y tus acumuladores lograron capturar parte de la energía liberada.',\n  'sys_expe_nothing_2' \t\t=> '¡Tus investigadores no encontraron nada!',\n  'sys_expe_found_goods' \t\t=> '¡Tus investigadores encontraron un planeta rico en recursos!<br>Obtuviste %s %s, %s %s y %s %s',\n  'sys_expe_found_ships' \t\t=> '¡Tus investigadores encontraron una flota en perfecto estado!<br>Obtuviste: ',\n  'sys_expe_back_home' \t\t=> 'Tu flota regresa.',\n  'sys_mess_transport' \t\t=> 'Transporte',\n//  'sys_tran_mess_owner' \t\t=> 'Una de tus flotas llega al planeta %s %s y entrega %s %s, %s  %s y %s %s.',\n  'sys_tran_mess_user'  \t\t=> 'Una flota del planeta %s %s llegó a %s %s y entregó %s %s, %s %s y %s %s.',\n  'sys_relocate_mess_user'  \t\t=> 'También se reubicaron las siguientes unidades de combate en el planeta:<br />',\n  'sys_mess_fleetback' \t\t=> 'Regreso',\n  'sys_tran_mess_back' \t\t=> 'Una de tus flotas regresa al planeta %s %s.',\n  'sys_recy_gotten' \t\t=> 'Una de tus flotas ha recolectado %s %s y %s %s. Regresa al planeta.',\n  'sys_notenough_money' \t\t=> 'No tienes suficientes recursos para construir: %s. Actualmente tienes: %s %s , %s %s y %s %s. Para construir necesitas: %s %s , %s %s y %s %s.',\n  'sys_nomore_level'\t\t=> 'No puedes mejorar esto más. Ha alcanzado el nivel máximo ( %s ).',\n  'sys_buildlist' \t\t\t=> 'Lista de construcciones',\n  'sys_buildlist_fail' \t\t=> 'sin construcciones',\n  'sys_gain' \t\t\t=> 'Recolección: ',\n  'sys_debris' \t\t\t=> 'Escombros: ',\n  'sys_noaccess' \t\t\t=> 'Acceso denegado',\n  'sys_noalloaw' \t\t\t=> '¡No tienes acceso a esta zona!',\n  'sys_governor'        => 'Gobernador',\n\n  'flt_error_duration_wrong' => 'No se puede enviar la flota - no hay intervalos disponibles para el retraso. Estudia más niveles de Astrocartografía',\n  'flt_stay_duration' => 'Tiempo',\n\n  'flt_mission_expedition' => [\n    'msg_sender' => 'Informe de expedición',\n    'msg_title' => 'Informe de expedición',\n\n    'found_dark_matter' => 'Obtenidas %1$d unidades de MO',\n    'found_resources' => \"Recursos encontrados:\\r\\n\",\n    'found_fleet' => \"Naves encontradas:\\r\\n\",\n    'lost_fleet' => \"Se perdieron las siguientes naves:\\r\\n\",\n\n    'outcomes' => [\n      Constants::OUTCOME_NONE => [\n        'messages' => [\n          'Tus investigadores no encontraron nada',\n        ],\n      ],\n\n      Constants::EXPEDITION_OUTCOME_LOST_FLEET => [\n        'messages' => [\n          'La flota entró en un agujero negro y se perdió parcialmente',\n        ],\n      ],\n\n      Constants::EXPEDITION_OUTCOME_LOST_FLEET_ALL => [\n        'messages' => [\n          'Si solo lo hubieras visto... ¡Es tan hermoso... Te llama... (se perdió el contacto con la flota)',\n          // 'Informe de la flota %1$s. Hemos completado la exploración del sector. La tripulación está descontenta ¡Eh, ¿qué haces en el puente?! (se perdió el contacto con la flota)',\n          'Informe de la flota %1$s. Todo tranquilo (interferencias) (se perdió el contacto con la flota)',\n          '¡AAAAAA! ¿QUÉ ES ESO? ¿DE DÓNDE SALIÓ (se perdió el contacto con la flota)',\n          'Objeto desconocido detectado. No responde a los protocolos estándar. Enviamos una sonda para investigar (se perdió el contacto con la flota)',\n        ],\n      ],\n\n      Constants::EXPEDITION_OUTCOME_FOUND_FLEET => [\n        'no_result' => 'Desafortunadamente, la potencia combinada de todas las computadoras de la flota no fue suficiente ni para controlar la nave más pequeña. Intenta enviar más naves y/o naves más grandes',\n        'messages' => [\n          0 => [\n            'Encontraste una flota completamente nueva',\n          ],\n          1 => [\n            'Encontraste una flota',\n          ],\n          2 => [\n            'Encontraste una flota usada',\n          ],\n        ],\n      ],\n\n      Constants::EXPEDITION_OUTCOME_FOUND_RESOURCES => [\n        'no_result' => 'Las bodegas de tu flota no pudieron contener ni un solo contenedor de recursos. Intenta enviar una flota con más transportes',\n        'messages' => [\n          0 => [\n            'Encontraste un tesoro pirata con recursos. ¿Cuántas naves fueron destruidas para acumular tanto botín?',\n          ],\n          1 => [\n            'Encontraste una base abandonada en un asteroide. ¿I wonder, ¿adónde fueron sus habitantes? Al investigar las ruinas, encontraste algunos almacenes intactos con recursos',\n          ],\n          2 => [\n            'Te topaste con un convoy de transporte destruido. Al revisar las bodegas de las naves destruidas, encontraste algunos recursos',\n          ],\n        ],\n      ],\n\n      Constants::EXPEDITION_OUTCOME_FOUND_DM => [\n        'no_result' => 'Desafortunadamente, todos los acumuladores de la flota no fueron suficientes para recolectar ni una sola MO. Intenta enviar una flota más grande',\n        'messages' => 'Tu flota fue testigo del nacimiento de una SuperNova',\n        // 'messages' => array(\n        //   'Tu flota fue testigo del nacimiento de una SuperNova 1',\n        //   'Tu flota fue testigo del nacimiento de una SuperNova 2',\n        //   'Tu flota fue testigo del nacimiento de una SuperNova 3',\n        // ),\n      ],\n\n    ],\n  ],\n\n  // News page & a bit of imperator page\n  'news_fresh'      => 'Noticias recientes',\n  'news_all'        => 'Todas las noticias',\n  'news_title'      => 'Noticias',\n  'news_none'       => 'No hay noticias',\n  'news_new'        => 'NUEVO',\n  'news_future'     => 'AVANCE',\n  'news_more'       => 'Más...',\n  'news_hint'       => 'Para ocultar la lista de noticias recientes, léelas todas haciendo clic en el título \"[ Noticias ]\"',\n\n  'news_date'       => 'Fecha',\n  'news_announce'   => 'Contenido',\n  'news_detail_url' => 'Enlace a detalles',\n  'news_mass_mail'  => 'Enviar noticia a todos los jugadores',\n\n  'news_total'      => 'Total de noticias: ',\n\n  'news_add'        => 'Agregar noticia',\n  'news_edit'       => 'Editar noticia',\n  'news_copy'       => 'Copiar noticia',\n  'news_mode_new'   => 'Nueva',\n  'news_mode_edit'  => 'Edición',\n  'news_mode_copy'  => 'Copia',\n\n  'sys_administration' => 'Administración del servidor',\n\n  'note_add'        => 'Agregar nota',\n  'note_del'        => 'Eliminar nota',\n  'note_edit'        => 'Editar nota',\n\n  // Shortcuts\n  'shortcut_title'     => 'Marcadores',\n  'shortcut_none'      => 'No hay marcadores',\n  'shortcut_new'       => 'NUEVO',\n  'shortcut_text'      => 'Texto',\n\n  'shortcut_add'       => 'Agregar marcador',\n  'shortcut_edit'      => 'Editar marcador',\n  'shortcut_copy'      => 'Copiar marcador',\n  'shortcut_mode_new'  => 'Nuevo',\n  'shortcut_mode_edit' => 'Edición',\n  'shortcut_mode_copy' => 'Copia',\n\n  // Missile-related\n  'mip_h_launched'\t\t\t=> 'Lanzamiento de misiles interplanetarios',\n  'mip_launched'\t\t\t\t=> '¡Misiles interplanetarios lanzados: <b>%s</b>!',\n\n  'mip_no_silo'\t\t\t\t=> 'Nivel insuficiente de silos de misiles en el planeta <b>%s</b>.',\n  'mip_no_impulse'\t\t\t=> 'Se requiere investigación del motor de impulso.',\n  'mip_too_far'\t\t\t\t=> 'El misil no puede volar tan lejos.',\n  'mip_planet_error'\t\t\t=> 'Error - más de un planeta en una coordenada',\n  'mip_no_rocket'\t\t\t\t=> 'No hay suficientes misiles en el silo para realizar el ataque.',\n  'mip_hack_attempt'\t\t\t=> ' ¿Eres un hacker? Un truco más como este y serás baneado. He registrado tu dirección IP y nombre de usuario.',\n\n  'mip_all_destroyed' \t\t=> 'Todos los misiles interplanetarios fueron destruidos por misiles interceptores<br>',\n  'mip_destroyed'\t\t\t\t=> '%s misiles interplanetarios fueron destruidos por misiles interceptores.<br>',\n  'mip_defense_destroyed'\t=> 'Se destruyeron las siguientes defensas:<br />',\n  'mip_recycled'\t\t\t\t=> 'Reciclado de escombros de defensa: ',\n  'mip_no_defense'\t\t\t=> '¡No había defensas en el planeta atacado!',\n\n  'mip_sender_amd'\t\t\t=> 'Tropas de misiles espaciales',\n  'mip_subject_amd'\t\t\t=> 'Ataque con misiles',\n  'mip_body_attack'\t\t\t=> 'Ataque con misiles interplanetarios (%1$s unidades) desde el planeta %2$s <a href=\"galaxy.php?mode=3&galaxy=%3$d&system=%4$d&planet=%5$d\">[%3$d:%4$d:%5$d]</a> al planeta %6$s <a href=\"galaxy.php?mode=3&galaxy=%7$d&system=%8$d&planet=%9$d\">[%7$d:%8$d:%9$d]</a><br><br>',\n\n  // Misc\n  'sys_game_rules' => 'Reglas del juego',\n  'sys_game_documentation' => 'Descripción del juego',\n  'sys_banned_msg' => 'Estás baneado. Para obtener información, visita <a href=\"banned.php\">aquí</a>. Fecha de finalización del bloqueo de la cuenta: ',\n  'sys_total_time' => 'Tiempo total',\n  'sys_total_time_short' => 'Cola',\n  'eco_que_finish' => 'Finalización',\n\n  // Universe\n  'uni_moon_of_planet' => 'del planeta',\n\n  // Combat reports\n  'cr_view_title'  => \"Ver informes de combate\",\n  'cr_view_button' => \"Ver informe\",\n  'cr_view_prompt' => \"Ingresa el código\",\n  'cr_view_my'     => \"Mis informes de combate\",\n  'cr_view_hint'   => '<ul><li>Puedes ver tus informes de combate haciendo clic en el enlace \"Mis informes de combate\" en el encabezado</li><li>El código del informe de combate se encuentra en su última línea y es una secuencia de 32 dígitos y caracteres del alfabeto latino</li></ul>',\n\n  // Fleet\n  'flt_gather_all'    => 'Recoger recursos',\n\n  // Ban system\n  'ban_title'      => 'Lista negra',\n  'ban_name'       => 'Nombre',\n  'ban_reason'     => 'Razón del bloqueo',\n  'ban_from'       => 'Fecha de bloqueo',\n  'ban_to'         => 'Duración del bloqueo',\n  'ban_by'         => 'Emitido por',\n  'ban_no'         => 'No hay jugadores bloqueados',\n  'ban_thereare'   => 'Total',\n  'ban_players'    => 'bloqueados',\n  'ban_banned'     => 'Jugadores bloqueados: ',\n\n\n  // Records page\n  'rec_title'  => 'Récords del Universo',\n  'rec_build'  => 'Edificios',\n  'rec_specb'  => 'Edificios especiales',\n  'rec_playe'  => 'Jugador',\n  'rec_defes'  => 'Defensa',\n  'rec_fleet'  => 'Flota',\n  'rec_techn'  => 'Tecnologías',\n  'rec_level'  => 'Nivel',\n  'rec_nbre'   => 'Cantidad',\n  'rec_rien'   => '-',\n\n  // Credits page\n  'cred_link'    => 'Internet',\n  'cred_site'    => 'Sitio web',\n  'cred_forum'   => 'Foro',\n  'cred_credit'  => 'Autores',\n  'cred_creat'   => 'Director',\n  'cred_prog'    => 'Programador',\n  'cred_master'  => 'Líder',\n  'cred_design'  => 'Diseñador',\n  'cred_web'     => 'Webmaster',\n  'cred_thx'     => 'Agradecimientos',\n  'cred_based'   => 'Base para la creación de XNova',\n  'cred_start'   => 'Lugar de debut de XNova',\n\n  // Built-in chat\n  'chat_common'   => 'Chat general',\n  'chat_ally'     => 'Chat de Alianza',\n  'chat_history'  => 'Historial del chat',\n  'chat_message'  => 'Mensaje',\n  'chat_send'     => 'Enviar',\n  'chat_page'     => 'Página',\n  'chat_timeout'  => 'Chat desactivado por inactividad. Actualiza la página.',\n\n  // Interface of Jump Gate\n  'gate_start_moon' => 'Luna de inicio',\n  'gate_dest_moon'  => 'Luna de destino',\n  'gate_use_gate'   => 'Usar puerta',\n  'gate_ship_sel'   => 'Seleccionar naves',\n  'gate_ship_dispo' => 'disponible',\n  'gate_jump_btn'   => '¡Realizar salto!',\n  'gate_jump_done'  => '¡Las puertas están en proceso de recarga!<br>Las puertas estarán listas para usar en: ',\n  'gate_wait_dest'  => '¡El destino de las puertas está en preparación! Las puertas estarán listas para usar en: ',\n  'gate_no_dest_g'  => 'No se encontraron puertas de salto en el destino',\n  'gate_no_src_ga'  => 'No hay puertas de salto disponibles',\n  'gate_wait_star'  => '¡Las puertas están en proceso de recarga!<br>Las puertas estarán listas para usar en: ',\n  'gate_wait_data'  => 'Error: no hay datos para el salto',\n  'gate_vacation'   => 'Error: no puedes realizar un salto porque estás en modo vacaciones',\n  'gate_ready'      => 'Puertas listas para saltar',\n\n  // quests\n  'qst_quests'               => 'Misiones',\n  'qst_msg_complete_subject' => 'Misión completada',\n  'qst_msg_complete_body'    => 'Has completado la misión \"%s\".',\n  'qst_msg_your_reward'      => 'Tu recompensa:',\n\n  // Messages\n  'msg_from_admin' => 'Administración del Universo',\n  'msg_class' => [\n    MSG_TYPE_OUTBOX => 'Mensajes enviados',\n    MSG_TYPE_SPY => 'Informes de espionaje',\n    MSG_TYPE_PLAYER => 'Mensajes de jugadores',\n    MSG_TYPE_ALLIANCE => 'Mensajes de Alianza',\n    MSG_TYPE_COMBAT => 'Informes de combate',\n    MSG_TYPE_RECYCLE => 'Informes de reciclaje',\n    MSG_TYPE_TRANSPORT => 'Llegada de flota',\n    MSG_TYPE_ADMIN => 'Mensajes de Administración',\n    MSG_TYPE_EXPLORE => 'Informes de expediciones',\n    MSG_TYPE_QUE => 'Mensajes de cola de construcción',\n    MSG_TYPE_NEW => 'Todos los mensajes',\n  ],\n\n  'msg_que_research_from'    => 'Instituto de investigación',\n  'msg_que_research_subject' => 'Nueva tecnología',\n  'msg_que_research_message' => 'Se ha investigado una nueva tecnología \\'%s\\'. Nuevo nivel - %d',\n\n  'msg_que_planet_from'    => 'Gobernador',\n\n  'msg_que_hangar_subject' => 'Trabajo en el astillero completado',\n  'msg_que_hangar_message' => \"El astillero en %s ha completado su trabajo\",\n\n  'msg_que_built_subject'   => 'Trabajos planetarios completados',\n  'msg_que_built_message'   => \"Se ha completado la construcción del edificio '%2\\$s' en %1\\$s. Niveles construidos: %3\\$d\",\n  'msg_que_destroy_message' => \"Se ha completado la demolición del edificio '%2\\$s' en %1\\$s. Niveles demolidos: %3\\$d\",\n\n  'msg_personal_messages' => 'Mensajes personales',\n\n  'sys_opt_bash_info'    => 'Configuración del sistema anti-bashing',\n  'sys_opt_bash_attacks' => 'Número de ataques en una oleada',\n  'sys_opt_bash_interval' => 'Intervalo entre oleadas',\n  'sys_opt_bash_scope' => 'Período de cálculo del bashing',\n  'sys_opt_bash_war_delay' => 'Moratoria después de declarar la guerra',\n  'sys_opt_bash_waves' => 'Número de oleadas por período',\n  'sys_opt_bash_disabled'    => 'Sistema anti-bashing desactivado',\n\n  'sys_id' => 'ID',\n  'sys_identifier' => 'Identificador',\n\n  'sys_email'   => 'Correo electrónico',\n  'sys_ip' => 'IP',\n\n  'sys_max' => 'Máx',\n  'sys_maximum' => 'Máximo',\n  'sys_maximum_level' => 'Nivel máximo',\n\n  'sys_user_name' => 'Nombre de usuario',\n  'sys_player_name' => 'Nombre del jugador',\n  'sys_user_name_short' => 'Nombre',\n\n  'sys_planets' => 'Planetas',\n  'sys_moons' => 'Lunas',\n\n  'sys_quantity' => 'Cantidad',\n  'sys_quantity_maximum' => 'Cantidad máxima',\n  'sys_qty' => 'Cant',\n  'sys_quantity_total' => 'Cantidad total',\n\n  'sys_buy_for' => 'Comprar por',\n  'sys_buy' => 'Comprar',\n  'sys_for' => 'por',\n\n  'sys_eco_lack_dark_matter' => 'Falta Materia Oscura',\n\n  'time_local' => 'Hora del jugador',\n  'time_server' => 'Hora del servidor',\n\n  'sys_result' => [\n    'error_dark_matter_not_enough' => 'No hay suficiente Materia Oscura para completar la operación',\n    'error_dark_matter_change' => 'Error al cambiar la cantidad de Materia Oscura. Intenta de nuevo. Si el error persiste, informa a la Administración del servidor',\n  ],\n\n  // Arrays\n  'sys_build_result' => [\n    BUILD_ALLOWED => 'Se puede construir',\n    BUILD_REQUIRE_NOT_MEET => 'Requisitos no cumplidos',\n    BUILD_AMOUNT_WRONG => 'Demasiados',\n    BUILD_QUE_WRONG => 'Cola inexistente',\n    BUILD_QUE_UNIT_WRONG => 'Cola incorrecta',\n    BUILD_INDESTRUCTABLE => 'No se puede destruir',\n    BUILD_NO_RESOURCES => 'Faltan recursos',\n    BUILD_NO_UNITS => 'No hay unidades',\n    BUILD_UNIT_BUSY => [\n      0 => 'Edificio ocupado',\n      STRUC_LABORATORY => 'Investigación en curso',\n      STRUC_LABORATORY_NANO => 'Investigación en curso',\n    ],\n    BUILD_QUE_FULL => 'Cola llena',\n    BUILD_SILO_FULL => 'Silo de misiles lleno',\n    BUILD_MAX_REACHED => 'Ya has construido y/o puesto en cola la cantidad máxima de unidades de este tipo',\n    BUILD_SECTORS_NONE => 'No hay sectores libres',\n    BUILD_AUTOCONVERT_AVAILABLE => 'Autoconversión disponible',\n    BUILD_HIGHSPOT_NOT_ACTIVE => 'Evento no activo',\n  ],\n\n  'sys_game_mode' => [\n    GAME_SUPERNOVA => 'SuperNova',\n    GAME_OGAME     => 'oGame',\n    GAME_BLITZ     => 'Servidor Blitz',\n  ],\n\n  'months' => [\n     1 =>'enero',\n     2 =>'febrero',\n     3 =>'marzo',\n     4 =>'abril',\n     5 =>'mayo',\n     6 =>'junio',\n     7 =>'julio',\n     8 =>'agosto',\n     9 =>'septiembre',\n    10 =>'octubre',\n    11 =>'noviembre',\n    12 =>'diciembre'\n  ],\n\n  'weekdays' => [\n    0 => 'Domingo',\n    1 => 'Lunes',\n    2 => 'Martes',\n    3 => 'Miércoles',\n    4 => 'Jueves',\n    5 => 'Viernes',\n    6 => 'Sábado'\n  ],\n\n  'user_level' => [\n    0 => 'Jugador',\n    1 => 'Moderador',\n    2 => 'Operador',\n    3 => 'Administrador',\n    4 => 'Desarrollador',\n  ],\n\n  'user_level_shortcut' => [\n    0 => 'J',\n    1 => 'M',\n    2 => 'O',\n    3 => 'A',\n    4 => 'D',\n  ],\n\n  'sys_lessThen15min'   => '&lt; 15 min',\n\n  'sys_no_points'         => '¡No tienes suficiente <span class=\"dark_matter\">Materia Oscura</span>!',\n  'sys_dark_matter_obtain_header' => 'Cómo obtener <span class=\"dark_matter\">Materia Oscura</span>',\n  'sys_dark_matter_desc' => 'La Materia Oscura es una materia no bariónica indetectable por métodos estándar, que constituye el 23% de la masa del Universo. Se puede extraer una cantidad increíble de energía de ella. Debido a esto, y a las dificultades asociadas con su extracción, la Materia Oscura es muy valiosa.',\n  'sys_dark_matter_hint' => 'Con esta sustancia puedes contratar oficiales y comandantes.',\n\n  'sys_dark_matter_what_why_how' => 'Qué es <span class=\"dark_matter\">Materia Oscura</span> y <span class=\"metamatter\">Metamateria</span>',\n  'sys_dark_matter_what_header' => 'Qué es <span class=\"dark_matter\">Materia Oscura</span>',\n  'sys_dark_matter_description_header' => 'Para qué sirve <span class=\"dark_matter\">Materia Oscura</span>',\n  'sys_dark_matter_description_text' => '<span class=\"dark_matter\">Materia Oscura</span> es un recurso dentro del juego que te permite realizar diversas operaciones:\n    <ul>\n      <li>Comprar <a href=\"index.php?page=premium\"><span class=\"link\">Cuenta Premium</span></a></li>\n      <li>Reclutar <a href=\"officer.php?mode=600\"><span class=\"link\">Mercenarios</span></a> en el Imperio </li>\n      <li>Contratar Gobernadores y comprar sectores adicionales <a href=\"overview.php?mode=manage\"><span class=\"link\">en los planetas</span></a></li>\n      <li>Comprar <a href=\"officer.php?mode=1100\"><span class=\"link\">Planos</span></a></li>\n      <li>Comprar <a href=\"artifacts.php\"><span class=\"link\">Artefactos</span></a></li>\n      <li>Usar <a href=\"market.php\"><span class=\"link\">Mercado Negro</span></a>: Intercambiar un tipo de recurso por otro; vender naves; comprar naves usadas, etc.</li>\n      <li>...y mucho más</li>\n    </ul>',\n  'sys_dark_matter_obtain_text' => 'Obtienes <span class=\"metamatter\">Materia Oscura</span> durante el juego: ganando experiencia por incursiones exitosas en otros planetas, investigando nuevas tecnologías, y también por construir y destruir edificios.\n    A veces, las expediciones de investigación también pueden traer <span class=\"metamatter\">MO</span>.',\n\n  'sys_dark_matter_obtain_text_convert' => '<br />Si no tienes suficiente <span class=\"dark_matter\">Materia Oscura</span>, adquiere <span class=\"metamatter\">Metamateria</span>. En caso de falta de <span class=\"dark_matter\">MO</span>, se usará la cantidad necesaria de <span class=\"metamatter\">Metamateria</span> en su lugar',\n\n  'sys_msg_err_update_dm' => 'Error al actualizar la cantidad de MO!',\n\n  'sys_na' => 'No disponible',\n  'sys_na_short' => 'N/D',\n\n  'sys_ali_res_title' => 'Recursos de la Alianza',\n\n  'sys_bonus' => 'Bonus',\n\n  'sys_of_ally' => 'de la Alianza',\n\n  'sys_hint_player_name' => 'La búsqueda de jugadores se puede realizar por ID o nombre. Si el nombre del jugador contiene caracteres no legibles o solo números, se debe usar el ID para la búsqueda',\n  'sys_hint_ally_name' => 'La búsqueda de Alianzas se puede realizar por ID, etiqueta o nombre. Si la etiqueta o el nombre de la Alianza contienen caracteres no legibles o solo números, se debe usar el ID para la búsqueda',\n\n  'sys_fleet_and' => '+ flotas',\n\n  'sys_on_planet' => 'En el planeta',\n  'fl_on_stores' => 'En almacén',\n\n  'sys_ali_bonus_members' => 'Tamaño mínimo de la Alianza para obtener el bonus',\n\n  'sys_premium' => 'Premium',\n\n  'mrc_period_list' => [\n    PERIOD_MINUTE    => '1 minuto',\n    PERIOD_MINUTE_3  => '3 minutos',\n    PERIOD_MINUTE_5  => '5 minutos',\n    PERIOD_MINUTE_10 => '10 minutos',\n    PERIOD_DAY       => '1 día',\n    PERIOD_DAY_3     => '3 días',\n    PERIOD_WEEK      => '1 semana',\n    PERIOD_WEEK_2    => '2 semanas',\n    PERIOD_MONTH     => '30 días',\n    PERIOD_MONTH_2   => '60 días',\n    PERIOD_MONTH_3   => '90 días',\n  ],\n\n  'sys_sector_buy' => 'Comprar 1 sector',\n\n  'sys_select_confirm' => 'Confirmar selección',\n\n  'sys_capital' => 'Capital',\n\n  'sys_result_operation' => 'Mensajes',\n\n  'sys_password' => 'Contraseña',\n  'sys_password_length' => 'Longitud de la contraseña',\n  'sys_password_seed' => 'Caracteres utilizados',\n\n  'sys_msg_ube_report_err_not_found' => 'Informe de combate no encontrado. Verifica la clave. También es posible que el informe haya sido eliminado por antigüedad',\n\n  'sys_mess_attack_report' \t=> 'Informe de combate',\n  'sys_perte_attaquant' \t\t=> 'El atacante perdió',\n  'sys_perte_defenseur' \t\t=> 'El defensor perdió',\n\n  'ube_report_info_page_header' => 'Informe de combate',\n  'ube_report_info_page_header_cypher' => 'Código de acceso',\n  'ube_report_info_main' => 'Información principal del combate',\n  'ube_report_info_date' => 'Fecha y hora',\n  'ube_report_info_location' => 'Ubicación',\n  'ube_report_info_rounds_number' => 'Número de rondas',\n  'ube_report_info_outcome' => 'Resultado del combate',\n  'ube_report_info_outcome_win' => 'El atacante ganó el combate',\n  'ube_report_info_outcome_loss' => 'El atacante perdió el combate',\n  'ube_report_info_outcome_draw' => 'El combate terminó en empate',\n  'ube_report_info_link' => 'Enlace al informe de combate',\n  'ube_report_info_bbcode' => 'BBCode para insertar en el chat',\n  'ube_report_info_sfr' => 'El combate terminó en una ronda con la derrota del atacante<br />Posible FMR',\n  'ube_report_info_debris' => 'Escombros en órbita',\n  'ube_report_info_debris_simulator' => '(sin contar la creación de la Luna)',\n  'ube_report_info_loot' => 'Botín',\n  'ube_report_info_loss' => 'Pérdidas de combate',\n  'ube_report_info_generate' => 'Tiempo de generación de la página',\n\n  'ube_report_moon_was' => 'Este planeta ya tenía una luna',\n  'ube_report_moon_chance' => 'Probabilidad de formación de luna',\n  'ube_report_moon_created' => 'En la órbita del planeta se formó una luna con un diámetro de',\n\n  'ube_report_moon_reapers_none' => 'Todas las naves con motores gravitacionales fueron destruidas durante el combate',\n  'ube_report_moon_reapers_wave' => 'Las naves del atacante crearon una onda gravitacional enfocada',\n  'ube_report_moon_reapers_chance' => 'Probabilidad de destrucción de la luna',\n  'ube_report_moon_reapers_success' => 'Luna destruida',\n  'ube_report_moon_reapers_failure' => 'La potencia de la onda no fue suficiente para destruir la luna',\n\n  'ube_report_moon_reapers_outcome' => 'Probabilidad de explosión de motores',\n  'ube_report_moon_reapers_survive' => 'La compensación precisa de los campos gravitacionales del sistema permitió disipar el retroceso de la destrucción de la luna',\n  'ube_report_moon_reapers_died' => 'Al no poder compensar los campos gravitacionales adicionales del sistema, la flota fue destruida',\n\n  'ube_report_side_attacker' => 'Atacante',\n  'ube_report_side_defender' => 'Defensor',\n\n  'ube_report_round' => 'Ronda',\n  'ube_report_unit' => 'Unidad de combate',\n  'ube_report_attack' => 'Ataque',\n  'ube_report_shields' => 'Escudos',\n  'ube_report_shields_passed' => 'Penetración',\n  'ube_report_armor' => 'Armadura',\n  'ube_report_damage' => 'Daño',\n  'ube_report_loss' => 'Pérdidas',\n\n\n  'ube_report_info_restored' => 'Estructuras defensivas restauradas',\n  'ube_report_info_loss_final' => 'Pérdidas finales de unidades',\n  'ube_report_info_loss_resources' => 'Pérdidas en recursos',\n  'ube_report_info_loss_dropped' => 'Pérdidas de recursos por reducción de capacidad de almacenamiento',\n  'ube_report_info_loot_lost' => 'Recursos saqueados del planeta',\n  'ube_report_info_loss_gained' => 'Pérdidas por saqueo de recursos',\n  'ube_report_info_loss_in_metal' => 'Pérdidas totales en metal',\n\n  'ube_report_msg_body_common' => 'La batalla tuvo lugar %s en la órbita %s [%d:%d:%d] %s<br />%s<br /><br />',\n  'ube_report_msg_body_debris' => 'Como resultado de la batalla, se generaron escombros en la órbita del planeta:<br />',\n  'ube_report_msg_body_sfr' => 'Se perdió contacto con la flota',\n\n  'ube_report_capture' => 'Captura de planeta',\n  'ube_report_capture_result' => [\n    UBE_CAPTURE_DISABLED => 'La captura de planetas está desactivada',\n    UBE_CAPTURE_NON_PLANET => 'Solo se pueden capturar planetas',\n    UBE_CAPTURE_NOT_A_WIN_IN_1_ROUND => 'Para capturar un planeta, la batalla debe terminar en victoria en el primer asalto',\n    UBE_CAPTURE_TOO_MUCH_FLEETS => 'Solo puede participar una flota de captura y la flota planetaria',\n    UBE_CAPTURE_NO_ATTACKER_USER_ID => 'ERROR INTERNO - ¡No hay ID de atacante! ¡Informa a los desarrolladores!',\n    UBE_CAPTURE_NO_DEFENDER_USER_ID => 'ERROR INTERNO - ¡No hay ID de defensor! ¡Informa a los desarrolladores!',\n    UBE_CAPTURE_CAPITAL => 'No se puede capturar la capital',\n    UBE_CAPTURE_TOO_LOW_POINTS => 'Solo puedes capturar planetas de jugadores cuyo total de puntos sea al menos el doble que el tuyo',\n    UBE_CAPTURE_NOT_ENOUGH_SLOTS => 'No hay más espacios para capturar planetas',\n    UBE_CAPTURE_SUCCESSFUL => 'El planeta ha sido capturado por el atacante',\n  ],\n\n  'sys_kilometers_short' => 'km',\n\n  'ube_simulation' => 'Simulación',\n\n  'sys_hire_do' => 'Contratar',\n\n  'sys_captains' => 'Capitanes',\n\n  'sys_fleet_composition' => 'Composición de la flota',\n\n  'sys_continue' => 'Continuar',\n\n  'uni_planet_density_types' => [\n    PLANET_DENSITY_NONE => 'No existe',\n    PLANET_DENSITY_ICE_HYDROGEN => 'Hielo de hidrógeno',\n    PLANET_DENSITY_ICE_METHANE => 'Hielo de metano',\n    PLANET_DENSITY_ICE_WATER => 'Hielo de agua',\n    PLANET_DENSITY_CRYSTAL_RAW => 'Cristal crudo',\n    PLANET_DENSITY_CRYSTAL_SILICATE => 'Silicato',\n    PLANET_DENSITY_CRYSTAL_STONE => 'Piedra',\n    PLANET_DENSITY_STANDARD => 'Estándar',\n    PLANET_DENSITY_METAL_ORE => 'Mineral',\n    PLANET_DENSITY_METAL_PERIDOT => 'Olivino',\n    PLANET_DENSITY_METAL_RAW => 'Metal crudo',\n  ],\n\n  'sys_planet_density' => 'Densidad',\n  'sys_planet_density_units' => 'kg/m&sup3;',\n  'sys_planet_density_core' => 'Tipo de núcleo',\n\n  'sys_change' => 'Cambiar',\n  'sys_show' => 'Mostrar',\n  'sys_hide' => 'Ocultar',\n  'sys_close' => 'Cerrar',\n  'sys_unlimited' => 'Sin límites',\n\n  'ov_core_type_current' => 'Tipo de núcleo actual',\n  'ov_core_change_to' => 'Cambiar a',\n  'ov_core_err_none' => 'El tipo de núcleo del planeta ha sido cambiado de \"%s\" a \"%s\".<br />Nueva densidad del planeta: %d kg/m3',\n  'ov_core_err_not_a_planet' => 'Solo puedes cambiar la densidad del núcleo en un planeta',\n  'ov_core_err_denisty_type_wrong' => 'Tipo de núcleo incorrecto',\n  'ov_core_err_same_density' => 'El nuevo tipo de núcleo no es diferente al actual - no hay cambios',\n  'ov_core_err_no_dark_matter' => 'No tienes suficiente Materia Oscura para cambiar el tipo de núcleo',\n\n  'sys_color'    => \"Color\",\n\n  'topnav_imp_attack' => '¡Tu Imperio está bajo ataque!',\n  'topnav_user_rank' => 'Tu posición actual en las estadísticas',\n  'topnav_users' => 'Total de jugadores registrados',\n  'topnav_users_online' => 'Jugadores en línea actualmente',\n\n  'topnav_refresh_page' => 'Recargar página',\n\n  'sys_colonies' => 'Colonias',\n  'sys_radio' => 'Radio \"Espacio\"',\n\n  'sys_auth_provider_list' => [\n    ACCOUNT_PROVIDER_NONE => 'Tabla USERS',\n    ACCOUNT_PROVIDER_LOCAL => 'Tabla ACCOUNT',\n    ACCOUNT_PROVIDER_CENTRAL => 'Tabla central ACCOUNT',\n  ],\n\n  'sys_login_messages' => [\n    LOGIN_UNDEFINED => 'El proceso de inicio de sesión no ha comenzado',\n    LOGIN_SUCCESS => 'Inicio de sesión exitoso',\n    LOGIN_ERROR_USERNAME_EMPTY => 'El nombre de usuario no puede estar vacío',\n    LOGIN_ERROR_USERNAME_RESTRICTED_CHARACTERS => 'El nombre de usuario no puede contener caracteres restringidos: ',\n    LOGIN_ERROR_USERNAME => 'No se encontró ningún jugador con ese nombre',\n    LOGIN_ERROR_USERNAME_ALLY_OR_BOT => 'Este nombre pertenece a una Alianza o bot. No puedes iniciar sesión con él... al menos por ahora',\n    LOGIN_ERROR_PASSWORD_EMPTY => 'La contraseña no puede estar vacía',\n    LOGIN_ERROR_PASSWORD_TRIMMED => 'La contraseña no puede comenzar o terminar con espacios, tabulaciones o saltos de línea',\n    LOGIN_ERROR_PASSWORD => 'Contraseña incorrecta',\n    //    LOGIN_ERROR_COOKIE => '',\n\n    REGISTER_SUCCESS => 'Registro completado con éxito',\n    REGISTER_ERROR_BLITZ_MODE => 'El registro de nuevos jugadores está desactivado en el modo Blitz',\n    REGISTER_ERROR_USERNAME_WRONG => 'Nombre de usuario incorrecto',\n    REGISTER_ERROR_ACCOUNT_NAME_EXISTS => 'El nombre de cuenta ya está en uso. Intenta iniciar sesión con este nombre y tu contraseña o restablecer tu contraseña',\n    REGISTER_ERROR_PASSWORD_INSECURE => 'Contraseña incorrecta. La contraseña debe tener al menos ' . PASSWORD_LENGTH_MIN . ' caracteres',\n    REGISTER_ERROR_USERNAME_SHORT => 'Nombre demasiado corto. Debe tener al menos ' . LOGIN_LENGTH_MIN. ' caracteres',\n    REGISTER_ERROR_PASSWORD_DIFFERENT => 'La contraseña y la confirmación no coinciden. Verifica los datos',\n    REGISTER_ERROR_EMAIL_EMPTY => 'El correo electrónico no puede estar vacío',\n    REGISTER_ERROR_EMAIL_WRONG => 'El correo electrónico ingresado no es válido. Verifica la dirección o usa otra',\n    REGISTER_ERROR_EMAIL_EXISTS => 'Este correo electrónico ya está registrado. Si ya te registraste en el juego, intenta restablecer tu contraseña. De lo contrario, usa otro correo',\n\n    PASSWORD_RESTORE_ERROR_EMAIL_NOT_EXISTS => 'No hay ningún jugador con este correo electrónico principal',\n    PASSWORD_RESTORE_ERROR_TOO_OFTEN => 'Solo puedes solicitar un código de recuperación cada 10 minutos. Si no recibiste el correo, revisa la carpeta de SPAM o contacta a la Administración del servidor al correo <span class=\"ok\">' . $config->server_email . '</span> desde la dirección que usaste al registrarte',\n    PASSWORD_RESTORE_ERROR_SENDING => 'Error al enviar el correo. Contacta a la Administración del servidor al correo <span class=\"ok\">' . $config->server_email . '</span>',\n    PASSWORD_RESTORE_SUCCESS_CODE_SENT => 'Correo con código de recuperación enviado con éxito',\n\n    PASSWORD_RESTORE_ERROR_CODE_EMPTY => 'El código de recuperación no puede estar vacío',\n    PASSWORD_RESTORE_ERROR_CODE_WRONG => 'Código de recuperación incorrecto',\n    PASSWORD_RESTORE_ERROR_CODE_TOO_OLD => 'El código de recuperación ha expirado. Solicita uno nuevo',\n    PASSWORD_RESTORE_ERROR_CODE_OK_BUT_NO_ACCOUNT_FOR_EMAIL => 'El código de recuperación es correcto, pero no se encontró ninguna cuenta con este correo. Puede que haya sido eliminada o hubo un error interno. Contacta a la Administración del servidor',\n    PASSWORD_RESTORE_SUCCESS_PASSWORD_SENT => 'Contraseña restablecida con éxito. Se te ha enviado un correo con la nueva contraseña',\n    PASSWORD_RESTORE_SUCCESS_PASSWORD_SEND_ERROR => 'Error al enviar el correo con la nueva contraseña. Solicita un nuevo código de recuperación e inténtalo de nuevo',\n\n    REGISTER_ERROR_PLAYER_NAME_TRIMMED => 'El nombre del jugador no puede comenzar o terminar con espacios, tabulaciones o saltos de línea',\n    REGISTER_ERROR_PLAYER_NAME_EMPTY => 'El nombre del jugador no puede estar vacío',\n    REGISTER_ERROR_PLAYER_NAME_RESTRICTED_CHARACTERS => 'El nombre del jugador contiene caracteres no permitidos',\n    REGISTER_ERROR_PLAYER_NAME_SHORT => 'El nombre del jugador no puede tener menos de ' . LOGIN_LENGTH_MIN . ' caracteres',\n    REGISTER_ERROR_PLAYER_NAME_EXISTS => 'Este nombre de jugador ya está en uso. Por favor, elige otro',\n\n    // Errores internos\n    AUTH_ERROR_INTERNAL_PASSWORD_CHANGE_ON_RESTORE => '¡ERROR INTERNO! ¡INFORMA A LA ADMINISTRACIÓN! Error al cambiar la contraseña. Por favor, informa a la Administración del Universo sobre este error',\n    PASSWORD_RESTORE_ERROR_ADMIN_ACCOUNT => 'No se permite restablecer la contraseña para el Equipo del servidor. Contacta al Administrador',\n    REGISTER_ERROR_ACCOUNT_CREATE => '¡Error al crear la cuenta! Por favor, informa a la Administración',\n    LOGIN_ERROR_SYSTEM_ACCOUNT_TRANSLATION => 'ERROR DEL SISTEMA - FALLO EN LA TABLA DE TRADUCCIÓN DE PROVEEDORES! ¡Informa a la Administración del servidor!',\n    PASSWORD_RESTORE_ERROR_ACCOUNT_NOT_EXISTS => 'Error interno - no se encontró la cuenta al cambiar la contraseña! ¡Informa a la Administración!',\n    AUTH_PASSWORD_RESET_INSIDE_ERROR_NO_ACCOUNT_FOR_CONFIRMATION => '¡ERROR INTERNO! No hay cuentas para restablecer la contraseña con el código de confirmación correcto! Por favor, informa a la Administración del Universo sobre este error',\n    LOGIN_ERROR_NO_ACCOUNT_FOR_COOKIE_SET => '¡ERROR INTERNO! ¡INFORMA A LA ADMINISTRACIÓN! No se estableció la cuenta en cookie_set()! Por favor, informa a la Administración del Universo sobre este error',\n  ],\n\n  'log_reg_email_title' => \"Tu registro en el servidor %1\\$s del juego SuperNova\",\n  'log_reg_email_text' => \"Confirmación de registro para %3\\$s\\r\\n\\r\\n\n  Este correo contiene tus datos de registro en el servidor %1\\$s del juego SuperNova\\r\\n\n  Guarda estos datos en un lugar seguro\\r\\n\\r\\n\n  Dirección del servidor: %2\\$s\\r\\n\n  Tu nombre de usuario: %3\\$s\\r\\n\n  Tu contraseña: %4\\$s\\r\\n\\r\\n\n  ¡Gracias por registrarte en nuestro servidor! ¡Te deseamos mucha suerte en el juego!\\r\\n\n  Administración del servidor %1\\$s %2\\$s\\r\\n\\r\\n\n  El servidor funciona con el motor libre 'Project SuperNova.WS'. Enciende tu SuperNova http://supernova.ws/\",\n\n  'log_lost_email_title' => 'SuperNova, Universo %s: Restablecimiento de contraseña',\n  'log_lost_email_code' => \"Alguien (puede que tú) ha solicitado restablecer la contraseña en el Universo %1\\$4 del juego SuperNova. Si no solicitaste esto, ignora este correo.\\r\\n\\r\\nPara restablecer la contraseña, visita \\r\\n%1\\$s?password_reset_confirm=1&password_reset_code=%2\\$s#tab_password_reset\\r\\n o ingresa el código de confirmación \\\"%2\\$s\\\" (¡SIN COMILLAS!) en la página %1\\$s#tab_password_reset\\r\\n\\r\\nEste código será válido hasta %3\\$s. Después de esta fecha, deberás solicitar un nuevo código\",\n  'log_lost_email_pass' => \"Has restablecido tu contraseña en el servidor %1\\$s del juego 'SuperNova'.\\r\\n\\r\\nTu nombre de usuario:\\r\\n%2\\$s\\r\\n\\r\\nTu nueva contraseña:\\r\\n%3\\$s\\r\\n\\r\\n¡Memorízala!\\r\\n\\r\\nPuedes iniciar sesión en el juego en \" . SN_ROOT_VIRTUAL . \"login.php usando el nombre y contraseña anteriores\",\n\n  'login_player_register_player_name' => 'Nombre del jugador',\n  'login_player_register_description' => '¡Solo un paso más! Elige un nombre de jugador - el nombre que otros jugadores verán en este Universo',\n  'login_player_register_do' => 'Elegir nombre',\n  'login_player_register_logout' => 'Iniciar sesión con otra cuenta',\n  'login_player_register_logout_description' => 'Si deseas iniciar sesión con otra cuenta, haz clic en el botón',\n\n  'sys_password_reset_message_body' => \"Has restablecido tu contraseña para acceder al juego en este Universo.\\r\\n\\r\\nTu nueva contraseña:\\r\\n\\r\\n%1\\$s\\r\\n\\r\\n¡Memorízala!\\r\\n\\r\\nPuedes cambiar tu contraseña en cualquier momento en el menú 'Configuración'.\",\n\n  'sys_login_password_show' => 'Mostrar contraseña',\n  'sys_login_password_hide' => 'Ocultar contraseña',\n  'sys_password_repeat' => 'Repite la contraseña',\n\n  'sys_game_disable_reason' => [\n    GAME_DISABLE_NONE => 'Juego activado',\n    GAME_DISABLE_REASON => 'Juego desactivado. Los jugadores verán un mensaje',\n    GAME_DISABLE_UPDATE => 'El juego se está actualizando',\n    GAME_DISABLE_STAT => 'Se está recalculando la estadística',\n    GAME_DISABLE_INSTALL => 'El juego aún no está configurado',\n    GAME_DISABLE_MAINTENANCE => 'Mantenimiento de la base de datos del servidor',\n    GAME_DISABLE_EVENT_BLACK_MOON => '¡Luna Negra!',\n    GAME_DISABLE_EVENT_OIS => 'Objetos en el espacio',\n  ],\n\n  'sys_sector_purchase_log' => 'El usuario {%2$d} {%1$s} compró 1 sector en el planeta {%5$d} {%3$s} tipo \"%4$s\" por %6$d TM',\n\n  'sys_notes' => 'Notas',\n  'sys_notes_priorities' => [\n    0 => 'No importante',\n    1 => 'Poco importante',\n    2 => 'Normal',\n    3 => 'Importante',\n    4 => 'Muy importante',\n  ],\n\n  'sys_milliseconds' => 'milisegundos',\n\n  'sys_gender' => 'Género',\n  'sys_gender_list' => [\n    GENDER_UNKNOWN => 'Decidirá cuando crezca',\n    GENDER_MALE => 'Masculino',\n    GENDER_FEMALE => 'Femenino',\n  ],\n\n  'imp_stat_header' => 'Gráfico de cambios en las estadísticas',\n  'imp_stat_types' => [\n    'TOTAL_RANK' => 'Posición en la estadística general',\n    'TOTAL_POINTS' => 'Puntos totales',\n    // 'TOTAL_COUNT' => 'Recursos totales',\n    'TECH_RANK' => 'Posición en Investigación',\n    'TECH_POINTS' => 'Puntos de Investigación',\n    // 'TECH_COUNT' => 'Niveles',\n    'BUILD_RANK' => 'Posición en Construcciones',\n    'BUILD_POINTS' => 'Puntos de Construcciones',\n    // 'BUILD_COUNT' => '',\n    'DEFS_RANK' => 'Posición en Defensa',\n    'DEFS_POINTS' => 'Puntos de Defensa',\n    //'DEFS_COUNT' => '',\n    'FLEET_RANK' => 'Posición en Flotas',\n    'FLEET_POINTS' => 'Puntos de Flotas',\n    //'FLEET_COUNT' => '',\n    'RES_RANK' => 'Posición en Recursos libres',\n    'RES_POINTS' => 'Puntos de Recursos libres',\n    //'RES_COUNT' => '',\n  ],\n\n  'sys_date' => 'Fecha',\n\n  'sys_blitz_global_button' => 'Servidor Blitz',\n  'sys_blitz_page_disabled' => 'Esta página no está disponible en el modo Blitz',\n  'sys_blitz_registration_disabled' => 'El registro en el servidor Blitz está desactivado',\n  'sys_blitz_registration_no_users' => 'No hay jugadores registrados',\n  'sys_blitz_registration_player_register' => 'Registrarse para jugar',\n  'sys_blitz_registration_player_register_un' => 'Cancelar registro',\n  'sys_blitz_registration_closed' => 'El registro está cerrado. Inténtalo más tarde',\n  'sys_blitz_registration_player_generate' => 'Generar nombres y contraseñas',\n  'sys_blitz_registration_player_import_generated' => 'Importar cadena generada',\n  'sys_blitz_registration_player_name' => 'Tu nombre de usuario para Blitz:',\n  'sys_blitz_registration_player_password' => 'Tu contraseña para Blitz:',\n  'sys_blitz_registration_server_link' => 'Enlace al servidor Blitz',\n  'sys_blitz_registration_player_blitz_name' => 'Nombre en Blitz',\n  'sys_blitz_registration_price' => 'Costo de la solicitud',\n  'sys_blitz_registration_mode_list' => [\n    BLITZ_REGISTER_DISABLED => 'Registro desactivado',\n    BLITZ_REGISTER_OPEN => 'Registro abierto',\n    BLITZ_REGISTER_CLOSED => 'Registro cerrado',\n    BLITZ_REGISTER_SHOW_LOGIN => 'Nombres y contraseñas visibles',\n    BLITZ_REGISTER_DISCLOSURE_NAMES => 'Resultados finales',\n  ],\n\n  'survey' => 'Encuesta',\n  'survey_questions' => 'Opciones',\n  'survey_questions_hint' => '1 opción por línea',\n  'survey_questions_hint_edit' => 'Editar la encuesta reiniciará los resultados',\n  'survey_until' => 'Duración de la encuesta (1 día por defecto)',\n\n  'survey_votes_total_none' => 'Nadie ha votado aún... ¡Sé el primero!',\n  'survey_votes_total_voted' => 'Votos totales:',\n  'survey_votes_total_voted_join' => '¡Vota o pierde!',\n  'survey_votes_total_voted_has_answer' => 'Ya has votado. Votos totales:',\n\n  'survey_lasts_until' => 'La encuesta estará activa hasta',\n\n  'survey_select_one' => 'Elige una opción y haz clic en',\n  'survey_confirm' => '¡Votar!',\n  'survey_result_sent' => 'Tu voto ha sido registrado. Actualiza la página o usa el enlace <a class=\"link\" href=\"announce.php\">Noticias</a> para ver los resultados',\n  'survey_complete' => 'Encuesta completada',\n\n  'player_option_fleet_ship_sort' => [\n    PLAYER_OPTION_SORT_DEFAULT => 'Estándar',\n    PLAYER_OPTION_SORT_NAME => 'Por nombre',\n    PLAYER_OPTION_SORT_ID => 'Por ID',\n    PLAYER_OPTION_SORT_SPEED => 'Por velocidad',\n    PLAYER_OPTION_SORT_COUNT => 'Por cantidad',\n  ],\n\n  'player_option_building_sort' => [\n    PLAYER_OPTION_SORT_DEFAULT => 'Estándar',\n    PLAYER_OPTION_SORT_NAME => 'Por nombre',\n    PLAYER_OPTION_SORT_ID => 'Por ID',\n    PLAYER_OPTION_SORT_CREATE_TIME_LENGTH => 'Por tiempo de construcción',\n  ],\n\n  'sys_sort' => 'Ordenar',\n  'sys_sort_inverse' => 'En orden inverso',\n\n  'sys_blitz_reward_log_message' => 'Servidor Blitz %1$d lugar \"%2$s\"',\n  'sys_blitz_registration_view_stat' => 'Ver estadísticas del servidor Blitz',\n\n  'sys_login_register_message_title' => \"Tu nombre y contraseña para iniciar sesión\",\n  'sys_login_register_message_body' => \"Tu nombre de usuario (login)\\r\\n%1\\$s\\r\\n\\r\\nTu contraseña\\r\\n%2\\$s\\r\\n\\r\\n¡Guarda o memoriza estos datos!\",\n\n  'auth_provider_list' => [\n    ACCOUNT_PROVIDER_NONE => 'Tabla users',\n    ACCOUNT_PROVIDER_LOCAL => 'Tabla account',\n    ACCOUNT_PROVIDER_CENTRAL => 'Almacenamiento central',\n  ],\n\n  'bld_autoconvert' => 'Conversión automática al crear en el planeta %3$s la unidad {%1$d} \"%4$s\" en cantidad %2$d con costo \"%5$s\". Debug: $resource_got = \"%6$s\", $exchange = %7$s\"\"',\n\n  'news_show_rest' => 'Mostrar texto completo',\n\n  'wiki_requrements' => 'Requisitos',\n  'wiki_grants' => 'Proporciona',\n\n  'que_slot_length' => 'Espacios',\n  'que_slot_length_long' => 'Espacios en cola',\n\n  'sys_buy_doing' => 'Estás comprando',\n  'sys_planet_sector' => 'sector',\n  'sys_planet_on' => 'en',\n\n  'sys_purchase_confirm' => 'Confirmar compra',\n\n  'sys_confirm_action_title' => 'Confirma tu acción',\n  'sys_confirm_action' => '¿Realmente deseas hacer esto?',\n\n  'sys_system_speed_original' => 'Velocidad original',\n  'sys_system_speed_for_action' => 'Como parte de la promoción',\n\n  'menu_info_best_battles' => 'Mejores batallas',\n\n  'sys_cost' => 'Costo',\n  'sys_price' => 'Precio',\n\n  'sys_governor_none' => 'Gobernador no contratado',\n  'sys_governor_hire' => 'Contratar Gobernador',\n  'sys_governor_upgrade_or_change' => 'Mejorar o cambiar Gobernador',\n\n  'tutorial_prev' => '<< Anterior',\n  'tutorial_next' => 'Siguiente >>',\n  'tutorial_finish' => 'Finalizar',\n  'tutorial_window' => 'Abrir en ventana',\n  'tutorial_window_off' => 'Volver a la página',\n\n  'tutorial_error_load' => \"Error al cargar el tutorial - ¡inténtalo de nuevo! Si persiste, informa a la Administración\",\n  'tutorial_error_next' => \"Error: No existe la siguiente página del tutorial - informa a la Administración\",\n  'tutorial_error_prev' => \"Error: No existe la página anterior del tutorial - informa a la Administración\",\n\n  'sys_click_here_to_continue' => 'Haz clic aquí para continuar',\n\n  'sys_module_error_not_found' => '¡El módulo de recompensas \"%1$s\" no se encuentra o está desactivado!',\n\n  'rank_page_title' => 'Rangos militares',\n  'rank' => 'Rango',\n  'ranks' => [\n    0  => 'Cadete',\n    1  => 'Recluta',\n    2  => 'Soldado',\n    3  => 'Cabo',\n    4  => 'Cabo Mayor',\n    5  => 'Sargento',\n    6  => 'Sargento Mayor',\n    7  => 'Guardiamarina',\n    8  => 'Alférez',\n    9  => 'Subteniente',\n    10 => 'Teniente',\n    11 => 'Capitán',\n    12 => 'Mayor',\n    13 => 'Teniente Coronel',\n    14 => 'Coronel',\n    15 => 'Contralmirante',\n    16 => 'Vicealmirante',\n    17 => 'Almirante',\n    18 => 'Almirante de Flota',\n    19 => 'Mariscal',\n    20 => 'Generalísimo',\n  ],\n\n];"
  },
  {
    "path": "language/es/tech.mo.php",
    "content": "<?php\n\n/*\n#############################################################################\n#  Filename: tech.mo\n#  Project: SuperNova.WS\n#  Website: http://www.supernova.ws\n#  Description: Massive Multiplayer Online Browser Space Strategy Game\n#\n#  Copyright © 2009-2018 Gorlum for Project \"SuperNova.WS\"\n#############################################################################\n*/\n\n/**\n*\n* @package language\n* @system [Spanish]\n* @version 46d0\n*\n*/\n\n/**\n* DO NOT CHANGE\n*/\n\nif (!defined('INSIDE')) die();\n\n\n$a_lang_array = (array(\n  'tech_storage_max' => 'Capacidad de almacenamiento',\n  'tech_storage' => 'En almacén',\n  'tech_storage_energy' => 'Consumo',\n  'tech_storage_energy_max' => 'Producción',\n  'tech_storage_energy_fullness' => 'Carga',\n  'Tech' => 'Tecnología',\n  'Requirements' => 'Requisitos',\n  'No_requirements' => 'Sin requisitos',\n  'Metal' => 'Metal',\n  'Crystal' => 'Cristal',\n  'Deuterium' => 'Deuterio',\n  'Energy' => 'Energía',\n  'dark_matter' => 'Materia Oscura',\n  'ds' => 'Mensajes',\n  'Message' => 'Mensajes',\n  'level' => 'Nivel',\n  'treeinfo' => '[i]',\n  'comingsoon' => 'Próximamente',\n  'te_dt_tx_pre' => 'Producción baja',\n  'tech_fullness' => 'Llenado',\n\n  'type_mission' => array(\n    MT_ATTACK => 'Ataque',\n    MT_AKS => 'Ataque conjunto',\n    MT_TRANSPORT => 'Transporte',\n    MT_RELOCATE => 'Reubicación',\n    MT_HOLD => 'Mantenimiento',\n    MT_SPY => 'Espionaje',\n    MT_COLONIZE => 'Colonización',\n    MT_RECYCLE => 'Reciclaje',\n    MT_DESTROY => 'Destrucción',\n    MT_MISSILE => 'Ataque con misiles',\n    MT_EXPLORE => 'Expedición',\n  ),\n  'fleet_events' => array(\n    EVENT_FLEET_NONE   => 'Sin evento',\n    EVENT_FLEET_ARRIVE => 'Llegada',\n    EVENT_FLEET_STAY   => 'Mantenimiento/Expedición',\n    EVENT_FLEET_RETURN => 'Retorno',\n  ),\n\n  'tech' => array(\n    UNIT_STRUCTURES => 'Estructuras',\n    STRUC_MINE_METAL => 'Mina de metal',\n    STRUC_MINE_CRYSTAL => 'Sintetizador de cristal',\n    STRUC_MINE_DEUTERIUM => 'Sintetizador de deuterio',\n    STRUC_MINE_SOLAR => 'Planta de energía solar',\n    STRUC_MINE_FUSION => 'Planta de energía de fusión',\n    STRUC_FACTORY_ROBOT => 'Fábrica de robots',\n    STRUC_FACTORY_NANO => 'Nanofábrica',\n    STRUC_FACTORY_HANGAR => 'Astillero',\n    STRUC_STORE_METAL => 'Almacén de metal',\n    STRUC_STORE_CRYSTAL => 'Almacén de cristal',\n    STRUC_STORE_DEUTERIUM => 'Contenedor de deuterio',\n    STRUC_LABORATORY => 'Laboratorio',\n    STRUC_TERRAFORMER => 'Terraformador',\n    STRUC_ALLY_DEPOSIT => 'Depósito de alianza',\n    STRUC_LABORATORY_NANO => 'Nanolaboratorio',\n\n    UNIT_STRUCTURES_SPECIAL => 'Estructuras lunares',\n    STRUC_MOON_STATION => 'Base lunar',\n    STRUC_MOON_PHALANX => 'Falange sensora',\n    STRUC_MOON_GATE => 'Portal intergaláctico',\n    STRUC_SILO => 'Silo de misiles',\n\n    UNIT_TECHNOLOGIES => 'Tecnologías',\n    TECH_ENERGY => 'Tecnología energética',\n    TECH_COMPUTER => 'Tecnología informática',\n    TECH_ARMOR => 'Blindaje de naves',\n    TECH_WEAPON => 'Tecnología de armas',\n    TECH_SHIELD => 'Tecnología de escudos',\n    TECH_ENGINE_CHEMICAL => 'Motor de combustión',\n    TECH_ENGINE_ION => 'Motor iónico',\n    TECH_ENGINE_HYPER => 'Motor hiperespacial',\n    TECH_LASER => 'Tecnología láser',\n    TECH_ION => 'Tecnología iónica',\n    TECH_PLASMA => 'Tecnología de plasma',\n    TECH_HYPERSPACE => 'Tecnología hiperespacial',\n    TECH_SPY => 'Tecnología de espionaje',\n    TECH_EXPEDITION => 'Tecnología de expedición',\n    TECH_COLONIZATION => 'Tecnología de colonización',\n    TECH_ASTROTECH => 'Astrocartografía',\n    TECH_GRAVITON => 'Tecnología gravitatoria',\n    TECH_RESEARCH => 'Red de investigación',\n\n    UNIT_SHIPS               => 'Flota',\n    SHIP_SATTELITE_SOLAR     => 'Satélite solar',\n    SHIP_SPY                 => 'Sonda espía',\n    SHIP_CARGO_SMALL         => 'Transporte pequeño',\n    SHIP_CARGO_BIG           => 'Transporte grande',\n    SHIP_CARGO_SUPER         => 'Supertransporte',\n    SHIP_CARGO_HYPER         => 'Hipertransporte',\n    SHIP_RECYCLER            => 'Reciclador',\n    SHIP_COLONIZER           => 'Colonizador',\n    SHIP_SMALL_FIGHTER_LIGHT => 'Caza ligero',\n    SHIP_SMALL_FIGHTER_HEAVY => 'Caza pesado',\n    SHIP_MEDIUM_DESTROYER    => 'Destructor',\n    SHIP_LARGE_CRUISER       => 'Crucero',\n    SHIP_LARGE_BOMBER        => 'Bombardero',\n    SHIP_LARGE_BATTLESHIP    => 'Acorazado',\n    SHIP_LARGE_DESTRUCTOR    => 'Destructor pesado',\n    SHIP_HUGE_DEATH_STAR     => 'Estrella de la muerte',\n    SHIP_HUGE_SUPERNOVA      => 'Crucero clase &quot;SuperNova&quot;',\n\n    UNIT_DEFENCE => 'Defensa',\n    UNIT_DEF_TURRET_MISSILE => 'Lanzamisiles',\n    UNIT_DEF_TURRET_LASER_SMALL => 'Láser pequeño',\n    UNIT_DEF_TURRET_LASER_BIG => 'Láser pesado',\n    UNIT_DEF_TURRET_GAUSS => 'Cañón Gauss',\n    UNIT_DEF_TURRET_ION => 'Cañón iónico',\n    UNIT_DEF_TURRET_PLASMA => 'Cañón de plasma',\n    UNIT_DEF_SHIELD_SMALL => 'Cúpula pequeña',\n    UNIT_DEF_SHIELD_BIG => 'Cúpula grande',\n    UNIT_DEF_SHIELD_PLANET => 'Protección planetaria',\n    UNIT_DEF_MISSILE_INTERCEPTOR => 'Misil interceptor',\n    UNIT_DEF_MISSILE_INTERPLANET => 'Misil interplanetario',\n\n    UNIT_MERCENARIES => 'Mercenarios',\n    MRC_STOCKMAN => 'Maestro de carga',\n    MRC_SPY => 'Espía',\n    MRC_ACADEMIC => 'Académico',\n    MRC_ADMIRAL => 'Almirante',\n    MRC_COORDINATOR => 'Coordinador',\n    MRC_NAVIGATOR => 'Navegante',\n\n    UNIT_GOVERNORS => 'Gobernadores',\n    MRC_TECHNOLOGIST => 'Tecnólogo',\n    MRC_ENGINEER => 'Ingeniero',\n    MRC_FORTIFIER => 'Fortificador',\n\n    UNIT_RESOURCES => 'Recursos',\n    RES_METAL => 'Metal',\n    RES_CRYSTAL => 'Cristal',\n    RES_DEUTERIUM => 'Deuterio',\n    RES_ENERGY => 'Energía',\n    RES_DARK_MATTER => 'Materia Oscura',\n    RES_METAMATTER => 'Metamateria',\n\n    UNIT_ARTIFACTS     => 'Artefactos',\n    ART_LHC            => 'Gran Colisionador de Hadrones',\n    ART_HOOK_SMALL     => 'Gancho pequeño',\n    ART_HOOK_MEDIUM    => 'Gancho mediano',\n    ART_HOOK_LARGE     => 'Gancho grande',\n    ART_RCD_SMALL      => 'ACD pequeño',\n    ART_RCD_MEDIUM     => 'ACD mediano',\n    ART_RCD_LARGE      => 'ACD grande',\n    ART_HEURISTIC_CHIP => 'Chip heurístico',\n    ART_NANO_BUILDER   => 'Nanoconstructor',\n    ART_DENSITY_CHANGER => 'Matriz de transmutación',\n\n    UNIT_PLANS => 'Planos',\n    UNIT_PLAN_STRUC_MINE_FUSION => 'Plano &quot;Planta de fusión&quot;',\n    UNIT_PLAN_SHIP_CARGO_SUPER => 'Plano &quot;Supertransporte&quot;',\n    UNIT_PLAN_SHIP_CARGO_HYPER => 'Plano &quot;Hipertransporte&quot;',\n    UNIT_PLAN_SHIP_DEATH_STAR => 'Plano &quot;Estrella de la muerte&quot;',\n    UNIT_PLAN_SHIP_SUPERNOVA => 'Plano crucero &quot;SuperNova&quot;',\n    UNIT_PLAN_DEF_SHIELD_PLANET => 'Plano &quot;Protección planetaria&quot;',\n\n    UNIT_PREMIUM => 'Premium',\n    UNIT_CAPTAIN => 'Capitán',\n\n    UNIT_PLANET_DENSITY => 'Densidad',\n\n    UNIT_CAN_NOT_BE_BUILD => 'No se puede construir',\n  ),\n\n  'tech_short' => array(\n    UNIT_STRUCTURES          => 'Estructuras',\n    STRUC_MINE_METAL         => 'MinaMet',\n    STRUC_MINE_CRYSTAL       => 'SintCris',\n    STRUC_MINE_DEUTERIUM     => 'SintDeut',\n    STRUC_MINE_SOLAR         => 'PlantaSol',\n    STRUC_MINE_FUSION        => 'PlantaFus',\n    STRUC_FACTORY_ROBOT      => 'FabRobots',\n    STRUC_FACTORY_NANO       => 'NanoFab',\n    STRUC_FACTORY_HANGAR     => 'Astillero',\n    STRUC_STORE_METAL        => 'AlmMet',\n    STRUC_STORE_CRYSTAL      => 'AlmCris',\n    STRUC_STORE_DEUTERIUM    => 'AlmDeut',\n    STRUC_LABORATORY         => 'Lab',\n    STRUC_TERRAFORMER       => 'TerraF',\n    STRUC_ALLY_DEPOSIT       => 'DepAlianza',\n    STRUC_LABORATORY_NANO    => 'NanoLab',\n    UNIT_STRUCTURES_SPECIAL  => 'Estructuras especiales',\n    STRUC_MOON_STATION       => 'BaseLunar',\n    STRUC_MOON_PHALANX       => 'Falange',\n    STRUC_MOON_GATE          => 'Portal',\n    STRUC_SILO               => 'SiloMis',\n    UNIT_TECHNOLOGIES        => 'Tecnologías',\n    TECH_ENERGY              => 'TecEner',\n    TECH_COMPUTER            => 'TecComp',\n    TECH_ARMOR               => 'Blindaje',\n    TECH_WEAPON              => 'Armas',\n    TECH_SHIELD              => 'Escudos',\n    TECH_ENGINE_CHEMICAL     => 'MotorQuim',\n    TECH_ENGINE_ION          => 'MotorIon',\n    TECH_ENGINE_HYPER        => 'MotorHiper',\n    TECH_LASER               => 'TecLaser',\n    TECH_ION                 => 'TecIon',\n    TECH_PLASMA              => 'TecPlasma',\n    TECH_HYPERSPACE          => 'TecHiper',\n    TECH_SPY                 => 'Espionaje',\n    TECH_ASTROTECH           => 'AstroTec',\n    TECH_GRAVITON            => 'GravTec',\n    TECH_RESEARCH            => 'RedInv',\n\n    UNIT_SHIPS               => 'Flota',\n    SHIP_SATTELITE_SOLAR     => 'SatSol',\n    SHIP_SPY                 => 'Espía',\n    SHIP_CARGO_SMALL         => 'TransPeq',\n    SHIP_CARGO_BIG           => 'TransGran',\n    SHIP_CARGO_SUPER         => 'SuperTrans',\n    SHIP_CARGO_HYPER         => 'HiperTrans',\n    SHIP_RECYCLER            => 'Reciclador',\n    SHIP_COLONIZER           => 'Coloniz',\n    SHIP_SMALL_FIGHTER_LIGHT => 'CazaLig',\n    SHIP_SMALL_FIGHTER_HEAVY => 'CazaPes',\n    SHIP_MEDIUM_DESTROYER    => 'Destructor',\n    SHIP_LARGE_CRUISER       => 'Crucero',\n    SHIP_LARGE_BOMBER        => 'Bombardero',\n    SHIP_LARGE_BATTLESHIP    => 'Acorazado',\n    SHIP_LARGE_DESTRUCTOR    => 'Destruct',\n    SHIP_HUGE_DEATH_STAR     => 'EDM',\n    SHIP_HUGE_SUPERNOVA      => 'SN',\n\n    UNIT_DEFENCE                 => 'Defensa',\n    UNIT_DEF_TURRET_MISSILE      => 'Misiles',\n    UNIT_DEF_TURRET_LASER_SMALL  => 'LaserPeq',\n    UNIT_DEF_TURRET_LASER_BIG    => 'LaserGran',\n    UNIT_DEF_TURRET_GAUSS        => 'Gauss',\n    UNIT_DEF_TURRET_ION          => 'Ion',\n    UNIT_DEF_TURRET_PLASMA       => 'Plasma',\n    UNIT_DEF_SHIELD_SMALL        => 'CúpulaPeq',\n    UNIT_DEF_SHIELD_BIG          => 'CúpulaGran',\n    UNIT_DEF_SHIELD_PLANET       => 'ProtPlanet',\n    UNIT_DEF_MISSILE_INTERCEPTOR => 'Intercept',\n    UNIT_DEF_MISSILE_INTERPLANET => 'MIP',\n\n    UNIT_RESOURCES => 'Recursos',\n    RES_METAL => 'Metal',\n    RES_CRYSTAL => 'Cris',\n    RES_DEUTERIUM => 'Deut',\n    RES_ENERGY => 'Ener',\n    RES_DARK_MATTER => 'MO',\n    RES_METAMATTER => 'MM',\n\n    UNIT_CAN_NOT_BE_BUILD => 'No construible',\n  ),\n\n));"
  },
  {
    "path": "language/es/universe.mo.php",
    "content": "<?php\n\n/*\n#############################################################################\n#  Filename: universe.mo\n#  Project: SuperNova.WS\n#  Website: http://www.supernova.ws\n#  Description: Massive Multiplayer Online Browser Space Strategy Game\n#\n#  Copyright © 2009-2018 Gorlum for Project \"SuperNova.WS\"\n#############################################################################\n*/\n\n/**\n*\n* @package language\n* @system [Spanish]\n* @version 46d0\n*\n*/\n\n/**\n* DO NOT CHANGE\n*/\n\nif (!defined('INSIDE')) die();\n\n\n$a_lang_array = (array(\n  'Galaxy' => 'Galaxia',\n  'Solar_system' => 'Sistema solar',\n  'Show' => 'Mostrar',\n  'vacation_shortcut' => 'V',\n  'banned_shortcut' => 'B',\n  'active_shortcut' => '*',\n  'inactif_7_shortcut' => 'i',\n  'inactif_28_shortcut' => 'I',\n  'strong_player_shortcut' => 'F',\n  'weak_player_shortcut' => 'D',\n  'uni_protected_player_shortcut' => 'n',\n  'Legend' => 'Leyenda',\n  'Strong_player' => 'Jugador fuerte',\n  'Weak_player' => 'Jugador débil',\n  'Way_vacation' => 'Modo vacaciones',\n  'Pendent_user' => 'Bloqueado',\n  'Active' => 'Jugador activo',\n  'Inactive_7_days' => 'Inactivo más de 7 días',\n  'Inactive_28_days' => 'Inactivo más de 28 días',\n  'uni_protected_player' => 'Jugador novato',\n  'uni_legend_myplanet' => 'Mis planetas',\n  'uni_legend_allyplanet' => 'Planetas de aliados',\n  'Solar_system_at' => 'Sistema solar %g:%s',\n  'Activity' => 'Actividad',\n  'Planet_info' => 'Información del planeta',\n  'Moon_info' => 'Información de la luna',\n  'Available' => 'Disponible',\n  'type1' => 'planeta',\n  'type3' => 'luna',\n  'Pos' => 'Pos',\n  'Planet' => 'Tipo',\n  'Name' => 'Nombre',\n  'Moon' => 'Luna',\n  'Debris' => 'Campo de escombros',\n  'caracters' => 'Características',\n  'diameter' => 'Diámetro',\n  'temperature' => 'Temperatura',\n  'Place' => 'Posición',\n  'unPlace' => '(fuera de estadísticas)',\n  'State' => 'Estado',\n  'Alliance' => 'Alianza',\n  'Actions' => 'Acciones',\n  'aava' => 'Avatar',\n  'Player' => 'Jugador (estado)',\n  'AllyInfoText' => 'Alianza %n en posición %r con %m miembros',\n  'Sending_fleet' => 'Enviando flota...',\n  'Sent_fleet' => 'Enviar flota...',\n  'Obtaining_data' => 'Verificando',\n  'Planet_info_tip' => 'Para obtener información sobre el planeta y realizar diversas acciones, coloca el cursor sobre el planeta de interés',\n  'an_error_has_happened_while_it_was_sent' => 'ocurrió un error durante el envío',\n  'error_there_is_no_moon' => 'error, esto no es una luna',\n  'error_the_player_is_under_the_protection_of_beginners' => 'error, jugador bajo protección para novatos',\n  'error_the_player_is_too_strong' => 'error, jugador demasiado fuerte',\n  'error_the_player_is_in_way_vacation' => 'error, jugador en modo vacaciones',\n  'error_only_x_available_probes_sending' => 'error, solo hay \"+retVals[1]+\" sondas disponibles para enviar',\n  'error_there_are_no_available_probes_of_spying' => 'error, no hay sondas de espionaje disponibles',\n  'error_you_cannot_send_any_more_fleets' => 'error, no puedes enviar más flotas',\n  'error_you_do_not_have_sufficient_deuterium' => 'error, deuterio insuficiente',\n  'There_is_not_planet' => 'Esto no es un planeta',\n  'error_there_is_no_sufficient_fuel' => 'error, combustible insuficiente',\n  'multialarm' => 'multialarma',\n  'gm_all' => 'Todo',\n  'gm_launch' => 'Lanzar ataque con misiles a',\n  'gm_send' => 'Enviar',\n  'gm_target' => 'Objetivo:',\n  'gal_mis_toLaunch' => 'Misiles a lanzar ',\n  'gal_mis_rest' => 'Misiles: ',\n  'gal_mis_launch' => 'LANZAR',\n  'gl_espionner' => 'Espionaje',\n  'gl_mipattack' => 'Ataque interplanetario',\n  'gl_shortcut' => 'Añadir a favoritos',\n  'gl_with' => 'con',\n  'gl_membre' => 'miembros',\n  'gl_ally_internal' => 'Info&nbsp;alianza',\n  'gl_ally_web' => 'Sitio de la alianza',\n  'gl_sendmess' => 'Enviar mensaje',\n  'gl_buddy' => 'Lista de amigos',\n  'gl_buddyreq' => 'Añadir amigo',\n  'gl_stats' => 'Estadísticas',\n  'gl_planet' => 'Planeta',\n  'gl_destroyedplanet' => 'Planeta destruido',\n  'gl_phalanx' => 'Falange',\n  'gl_ressource' => 'Recursos',\n  'gl_action' => 'Misión',\n  'gs_c00' => 'Error, misión no disponible desde la interfaz de Universo',\n  'gs_c01' => 'Error, planeta no encontrado.',\n  'gs_c02' => 'Error, coordenadas incorrectas en el Universo',\n  'gs_c03' => 'Error, jugador demasiado débil.',\n  'gs_c04' => 'Error, jugador demasiado fuerte.',\n  'gs_c04k' => ' ¡No tienes sondas de espionaje!',\n  'gs_c04r' => ' ¡No tienes recicladores!',\n  'gs_c05' => 'Error, jugador en modo vacaciones',\n  'gs_c610' => 'Error, no hay suficientes drones espía',\n  'gs_c610a' => 'Error, con ',\n  'gs_c610b' => 'Error, no hay suficientes sondas de espionaje.',\n  'gs_c611' => 'Error, no hay suficientes naves.',\n  'gs_c612' => 'Error: potencia de computadoras insuficiente.',\n  'gs_c613' => 'Error: deuterio insuficiente',\n  'gs_c616' => 'Error, ¡Alarma!',\n  'gs_c618' => 'Error, ¡estás intentando atacarte a ti mismo!',\n  'gs_c619' => 'Error',\n  'gs_c620' => 'Error, ¡estás de vacaciones!',\n  'gs_sending' => 'enviado',\n  'gs_to' => 'a coordenadas',\n  'Sending' => 'Enviar',\n  'gal_planets' => 'Planetas colonizados: ',\n  'gal_planetNone' => 'No hay planetas colonizados',\n  'gf_cntmone' => 'planetas colonizados',\n  'gf_cntmnone' => 'No hay planetas colonizados.',\n  'gf_cntmsome' => 'planetas colonizados.',\n  'gf_mi_title' => 'misiles interplanetarios',\n  'gf_fleetslt' => 'Flotas',\n  'gf_rc_title' => 'recicladores',\n  'gf_sp_title' => 'sondas de espionaje',\n  'gf_unknowsp' => 'Espacio desconocido - enviar Expedición',\n  'phalanx_header' => 'Flotas en vuelo',\n  'phalanx_noflotes' => 'No hay flotas en vuelo',\n  'phalanx_nodeuterium' => 'Falta deuterio para operar la Falange sensora.',\n  'phalanx_onlyformoons' => 'Esta estructura solo está disponible para lunas.',\n  'phalanx_nosensoravailable' => 'No tienes Falange sensora.',\n  'phalanx_rangeerror' => '¡Nivel de Falange sensora insuficiente!',\n  'phalanx_planet_destroyed' => '¡No se puede escanear un planeta destruido!',\n  'phalanx_planet_not_exists' => '¡Este planeta no existe!',\n  'gal_sys_members' => 'Miembros:&nbsp;',\n  'gal_sys_hint' => '<ul><li>Coloca el cursor sobre \"Leyenda\" en la esquina superior derecha de la tabla del sistema para ver una ventana emergente con la explicación de los colores, símbolos e iconos en la tabla del sistema</li><li>Coloca el cursor sobre la imagen del planeta, luna, escombros o sobre el nombre de usuario, nombre de alianza para ver un menú emergente con opciones adicionales</li></ul>',\n  'uni_debris' => 'Escombros',\n  'uni_need' => 'Necesita',\n  'uni_flying' => 'En vuelo',\n  'uni_incoming_fleets' => 'Composición de flotas entrantes',\n  'Planets_count' => '( %n planeta(s) colonizado(s) )',\n\n  'uni_rename' => 'Renombrar',\n  'uni_name' => 'Nombre',\n  'uni_to_name' => 'Nombrar',\n  'uni_naming' => 'Nombramiento',\n  'uni_for' => 'por',\n  'uni_msg_error_wrong_galaxy' => 'Número de galaxia no válido',\n  'uni_msg_error_wrong_system' => 'Número de sistema no válido',\n  'uni_msg_error_low_price' => 'Precio de nombramiento demasiado bajo',\n  'uni_msg_error_no_dm' => 'No hay suficiente MO para el nombramiento',\n  'uni_name_page_hint' => \"\n    <li>Puedes nombrar una galaxia o sistema con el nombre que elijas</li>\n    <li>El nombre elegido será visible para todos los jugadores en la página \\\"Universo\\\"</li>\n    <li>Nombrar una galaxia o sistema sin nombre tiene un precio base que puedes ver en la página \\\"Constantes del juego\\\"</li>\n    <li>Al nombrar una galaxia o sistema puedes elegir el precio de nombramiento. El precio no puede ser menor al sugerido</li>\n    <li>¿Por qué aumentar el precio de nombramiento? El siguiente jugador que quiera renombrar la galaxia o sistema después de ti deberá pagar tu precio de nombramiento más el precio base. Así, comprar el nombre de una galaxia o universo por un precio más alto que el sugerido protege en cierta medida el objeto de ser renombrado</li>\n  \",\n\n  'uni_galaxy_of' => 'galaxia',\n  'uni_system_of' => 'sistema',\n  'uni_msg_admin_rename' => 'Jugador ID %d [%s] por %d MO renombró %s [%s%s] como: %s',\n\n  'uni_debris_recyclable' => 'Para&nbsp;reciclar',\n  'uni_debris_incoming_recyclers' => 'En&nbsp;vuelo',\n  'uni_debris_on_planet' => 'En&nbsp;órbita',\n  'uni_recyclers_send' => 'Enviar recicladores',\n\n  'uni_colonize' => 'Enviar colonizador para fundar colonia en posición número',\n\n  'uni_scan_start' => 'Activar modo escaneo',\n  'uni_scan_stop' => 'Salir del modo escaneo',\n\n));"
  },
  {
    "path": "language/ru/admin.mo.php",
    "content": "<?php\n\n/*\n#############################################################################\n#  Filename: admin.mo\n#  Project: SuperNova.WS\n#  Website: http://www.supernova.ws\n#  Description: Massive Multiplayer Online Browser Space Strategy Game\n#\n#  Copyright © 2009-2018 Gorlum for Project \"SuperNova.WS\"\n#############################################################################\n*/\n\n/**\n*\n* @package language\n* @system [Russian]\n* @version 45d0\n*\n*/\n\n/**\n* DO NOT CHANGE\n*/\n\nif (!defined('INSIDE')) die();\n\n\n$a_lang_array = array(\n  'menu_admin_ally' => 'Альянсы',\n\n  'adm_tool_md5_header' => 'Генерация и шифрование пароля (MD5)',\n  'adm_tool_md5_hash' => 'Хэш MD5',\n  'adm_tool_md5_encode' => '[ Зашифровать ]',\n  'adm_tool_md5_generate' => '[ Сгенерировать ]',\n\n  'adm_tool_sql_page_header' => 'Параметры сервера SQL',\n\n  'adm_tool_sql_server_version' => 'Версия сервера',\n  'adm_tool_sql_client_version' => 'Версия библиотеки',\n  'adm_tool_sql_host_info' => 'Метод соединения',\n\n  'adm_confirm_do' => 'Подтвердить',\n\n  'adm_tool_sql_table' => array(\n    'server' => array(\n      'TABLE_HEADER'  => 'Сервер SQL',\n      'COLUMN_NAME_1' => 'Параметр',\n      'COLUMN_NAME_2' => 'Значение',\n//      'TABLE_FOOTER'  => '',\n//      'TABLE_EMPTY'   => '',\n    ),\n\n    'status' => array(\n      'TABLE_HEADER'  => 'Статус сервера SQL',\n      'COLUMN_NAME_1' => 'Параметр',\n      'COLUMN_NAME_2' => 'Значение',\n//      'TABLE_FOOTER'  => '',\n    ),\n\n    'params' => array(\n      'TABLE_HEADER'  => 'Настройки сервера SQL',\n      'COLUMN_NAME_1' => 'Параметр',\n      'COLUMN_NAME_2' => 'Значение',\n//      'TABLE_FOOTER'  => '',\n    ),\n  ),\n\n  'adm_pl_image' => 'Изображение планеты',\n  'adm_pl_fields_max' => 'Максимум секторов',\n  'adm_pl_temp_min' => 'Минимальная&nbsp;температура',\n  'adm_pl_temp_max' => 'Максимальная&nbsp;температура',\n  'adm_pl_fields_busy' => 'Занято секторов',\n  'adm_pl_governor' => 'Губернатор',\n  'adm_pl_debris_metal' => 'Обломки, металл',\n  'adm_pl_debris_crystal' => 'Обломки, кристалл',\n\n  'adm_opt_user_settings' => 'Настройки игроков',\n  'adm_opt_user_birthday_gift' => 'Подарок игроку на день рождения',\n  'adm_opt_user_birthday_gift_disable' => '0 - отключить подарки',\n  'adm_opt_user_birthday_range' => 'Ретро-рождение, в днях',\n  'adm_opt_user_birthday_range_hint' => 'Как далеко в прошлом может располагаться день рождения для получения игроком подарка. Очевидно, не имеет практического смысла устанавливать это значение более чем в 364 дня',\n\n  'adm_ul_title' => 'Список игроков',\n  'adm_ul_title_online' => 'Игроки онлайн',\n  'adm_ul_time_registered' => 'Дата регистрации',\n  'adm_ul_time_played' => 'Последний логин',\n  'adm_ul_time_banned' => 'Срок блокировки',\n  'adm_ul_delete_confirm' => 'Подвтердите удаление пользователя ',\n  'adm_ul_referral' => 'Рефералы',\n  'adm_ul_players' => 'Игроки',\n  'adm_ul_dms' => 'ТМ',\n  'adm_sys_actions' => 'Действия',\n  'adm_sys_write_message' => 'Написать личное сообщение',\n  'adm_sys_delete_user' => 'Удалить игрока',\n\n\n\n  'adm_done' => 'Успешно выполнено',\n  'adm_inactive_removed' => '<li>Удалено неактивных записей игроков: %d</li>',\n  'adm_stat_title' => 'Обновление статистики',\n  'adm_maintenance_title' => 'Обслуживание БД',\n  'adm_records' => 'записей обработано',\n  'adm_cleaner_title' => 'Чистка очереди построек',\n  'adm_cleaned' => 'Кол-во удаленных задач: ',\n  'adm_schedule_none' => 'В расписании нет задач на сейчас',\n\n  'Fix' => 'Обновлено',\n  'Welcome_to_Fix_section' => 'секция патчей',\n  'There_is_not_need_fix' => 'Фикс ненужен!',\n  'Fix_welldone' => 'Сделано!',\n  'adm_ov_title' => 'Обзор',\n  'adm_ov_infos' => 'Информация',\n  'adm_ov_yourv' => 'Текущая версия',\n  'adm_ov_lastv' => 'Доступная версия',\n  'adm_ov_here' => 'здесь',\n  'adm_ov_onlin' => 'Онлайн',\n  'adm_ov_ally' => 'Альянс',\n  'adm_ov_point' => 'Очки',\n  'adm_ov_activ' => 'Активен',\n  'adm_ov_count' => 'Онлайн игроки',\n  'adm_ov_wrtpm' => 'Написать в личку',\n  'adm_ov_altpm' => '[ЛП]',\n  'adm_ov_hint' => '<ul><li>Таблица пользователей онлайн может быть отсортирована по колонкам \"ID\", \"Имя игрока\", \"Альянс\", \"Очки\" и \"Активность\". Для сортировки по определенной колонке кликните на её заголовке</li></ul>',\n\n\n  'adm_ul_ttle2' => 'Players listed',\n  'adm_ul_id' => 'ID',\n  'adm_ul_name' => 'Имя игрока',\n  'adm_ul_mail' => 'E-mail',\n  'adm_ul_adip' => 'IP',\n  'adm_ul_regd' => 'Registred from',\n  'adm_ul_lconn' => 'Последний логин',\n  'adm_ul_bana' => 'Ban',\n  'adm_ul_detai' => 'Детали',\n  'adm_ul_actio' => 'Действия',\n  'adm_ul_playe' => ' игроков',\n  'adm_ul_yes' => 'Да',\n  'adm_ul_no' => 'Нет',\n  'adm_pl_title' => 'Активные планеты',\n  'adm_pl_activ' => 'Активные планеты',\n  'adm_pl_name' => 'Имя планеты',\n  'adm_pl_posit' => 'Координаты',\n  'adm_pl_point' => 'Значение',\n  'adm_pl_since' => 'Активна',\n  'adm_pl_they' => 'Всего',\n  'adm_pl_apla' => 'планет(а/ы)',\n  'adm_am_plid' => 'ID планеты',\n  'adm_am_done' => 'Добавление прошло успешно',\n  'adm_am_ttle' => 'Добавить ресурсы',\n  'adm_am_add' => 'Подтвердить',\n  'adm_am_form' => 'Форма добавления ресурсов',\n  'adm_ban_title' => 'Забанить игрока',\n  'adm_bn_plto' => 'Забанить игрока',\n  'adm_bn_name' => 'Имя игрока',\n  'adm_bn_reas' => 'Причина бана',\n  'adm_bn_isvc' => 'С режимом отпуска',\n  'adm_bn_time' => 'Длительность бана',\n  'adm_bn_days' => 'Дни',\n  'adm_bn_hour' => 'Часы',\n  'adm_bn_mins' => 'Минуты',\n  'adm_bn_secs' => 'Секунды',\n  'adm_bn_bnbt' => 'Забанить',\n  'adm_bn_thpl' => 'Игрок',\n  'adm_bn_isbn' => 'успешно заблокирован!',\n  'adm_bn_vctn' => ' Включен режим отпуска.',\n  'adm_bn_errr' => 'Ошибка блокировки игрока! Возможно ник %s не найден.',\n  'adm_bn_err2' => 'Ошибка отключения производства на планетах!',\n  'adm_bn_plnt' => 'Производство на планетах отключено.',\n  'adm_ban_msg_issued_date' => 'заблокировал игрока',\n  'adm_unbn_ttle' => 'Анбан',\n  'adm_unbn_plto' => 'Разбанить игрока',\n  'adm_unbn_name' => 'Имя',\n  'adm_unbn_bnbt' => 'Разбанить',\n  'adm_unbn_thpl' => 'Игрок',\n  'adm_unbn_isbn' => 'разбанен!',\n  'adm_rz_ttle' => 'Обнуление вселенной',\n  'adm_rz_done' => 'User(s) of transfer(s)',\n  'adm_rz_conf' => 'Подтверждение',\n  'adm_rz_text' => 'Нажимая кнопку (обнулить) вы уничтожите все данные базы. Вы сделали резервную копию??? Аккаунты удалены не будут...',\n  'adm_rz_doit' => 'Обнулить',\n  'adm_ch_ttle' => 'Администрирование чата',\n  'adm_ch_list' => 'Список сообщений',\n  'adm_ch_clear' => 'Очистить',\n  'adm_ch_idmsg' => 'ID',\n  'adm_ch_delet' => 'удалить',\n  'adm_ch_play' => 'Игрок',\n  'adm_ch_time' => 'Дата',\n  'adm_ch_chat' => 'Реплика',\n  'adm_ch_nbs' => 'сообщений всего...',\n  'adm_er_ttle' => 'Записи системы логов',\n  'adm_er_clear' => 'Очистить',\n  'adm_er_idmsg' => 'ID',\n  'adm_er_type' => '[Код] Заголовок',\n  'adm_er_play' => 'Игрок',\n  'adm_er_time' => 'Дата',\n  'adm_er_page' => 'Адрес страницы',\n  'adm_er_nbs' => 'Записей в логе:',\n  'adm_er_text' => 'Запись лога',\n  'adm_er_bktr' => 'Отладочная информация',\n  'adm_dm_title' => 'Изменение количества Тёмной Материи',\n  'adm_dm_planet' => 'ID, координаты или название планеты',\n  'adm_dm_oruser' => 'ИЛИ',\n  'adm_dm_user' => 'ID или имя игрока из списка игроков',\n  'adm_or_caption' => 'ИЛИ',\n  'adm_dm_no_quant' => 'Укажите количество ТМ (положительное - для начисления, отрицательное - для снятия)',\n  'adm_dm_no_dest' => 'Укажите ID или имя игрока для изменения ТМ',\n  'adm_dm_add_err' => 'Похоже, во время начисления ТМ произошла ошибка',\n  'adm_dm_user_none' => 'Ошибка: не найден игрок с ID или именем \"%s\"',\n  'adm_dm_user_added' => 'Количество ТМ у игрока [%2$d] \"%1$s\" успешно изменено на %3$s ТМ',\n  'adm_dm_user_conflict' => 'Ошибка: похоже, в БД есть игрока и с таким именем, и с таким ID',\n  'adm_dm_planet_none' => 'Ошибка при поиске планеты: не найдено планеты с ID, координатами или именем %s',\n  'adm_dm_planet_added' => 'Количество ТМ у игрока ID %1$d (владельца планеты %4$s %2$s ID %3$d) успешно изменено на %5$d ТМ.',\n  'adm_dm_planet_conflict' => 'Неуникальные данные для поиска планеты.<br>Это означает, что в БД одновременно существует ',\n  'adm_dm_planet_conflict_id' => 'планета с именем \"%1$s\" и планета с ID %1$s .<br>Попробуйте использовать координаты планеты.',\n  'adm_dm_planet_conflict_name' => 'несколько планет с именем \"%1$s\".<br>Попробуйте использовать координаты или ID планеты.',\n  'adm_dm_planet_conflict_coords' => 'планета с именем \"%1$s\" и планета с координатами %1$s.<br>Попробуйте использовать ID планеты.',\n  'adm_apply' => 'Применить',\n  'adm_maint' => 'Обслуживание',\n  'adm_backup' => 'Резервная копия',\n  'adm_tools' => 'Утилиты',\n  'adm_tools_reloadConfig' => 'Пересчитать конфигурацию',\n  'adm_reason' => 'Причина',\n  'adm_opt_title' => 'Настройки Вселенной',\n  'adm_opt_game_settings' => 'Параметры игры',\n  'adm_opt_game_name' => 'Название Вселенной',\n  'adm_opt_multiaccount_enabled' => 'Разрешить взаимодействие аккаунтов с 1 IP',\n  'adm_opt_speed' => 'Скорость',\n  'adm_opt_game_gspeed' => 'Игры',\n  'adm_opt_game_fspeed' => 'Флота',\n  'adm_opt_game_pspeed' => 'Добычи ресурсов',\n  'adm_opt_colonies_not_counted' => '(без учёта Столицы)',\n  'adm_opt_colonies_no_restrictions' => '(-1 - нет ограничений)',\n  'adm_opt_game_speed_normal' => '(1&nbsp;-&nbsp;нормальная)',\n  'adm_opt_game_faq' => 'Ссылка на ЧаВо',\n  'adm_opt_game_forum' => 'Адрес форума',\n  'adm_opt_game_metamatter' => 'Ссылка &quot;Приобрести Метаматерию&quot;',\n  'adm_opt_game_copyrigh' => 'Copyright',\n  'adm_opt_game_online' => 'Отключить игру. Пользователи увидят следующее сообщение:',\n  'adm_opt_game_offreaso' => 'Сообщение',\n  'adm_opt_plan_settings' => 'Параметры планет',\n  'adm_opt_plan_initial' => 'Размер стартовой планеты',\n  'adm_opt_plan_base_inc' => 'Базовая добыча',\n  'adm_opt_game_debugmod' => 'Включить режим отладки',\n  'adm_opt_geoip_whois_url' => 'URL сервиса WHOIS',\n  'adm_opt_geoip_whois_url_example' => '(например \"http://1whois.ru/?ip=\")',\n  'adm_opt_game_counter' => 'Включить счетчик посещений',\n  'adm_opt_game_oth_info' => 'Прочие параметры',\n  'adm_opt_int_news_count' => 'Количество новостей',\n  'adm_opt_int_page_imperor' => 'На странице &quot;Император&quot;',\n  'adm_opt_game_zero_disable' => '(0&nbsp;-&nbsp;отключить)',\n  'adm_opt_game_advertise' => 'Рекламные блоки',\n  'adm_opt_game_oth_adds' => 'Включить рекламный блок в левом меню. Код баннера:',\n  'adm_opt_game_oth_gala' => 'Галактика',\n  'adm_opt_game_oth_syst' => 'Система',\n  'adm_opt_game_oth_plan' => 'Планета',\n  'adm_opt_btn_save' => 'Сохранить',\n  'adm_opt_vacation_mode' => 'Отключить режим отпуска',\n  'adm_opt_sectors' => 'секторов',\n  'adm_opt_per_hour' => 'в час',\n  'adm_opt_saved' => 'Настройки игры сохранены успешно',\n  'adm_opt_players_online' => 'Игроков на сервере',\n  'adm_opt_vacation_mode_is' => 'Режим отпуска',\n  'adm_opt_game_status' => 'Состояние игры',\n  'adm_opt_links' => 'Ссылки и баннеры',\n  'adm_opt_universe_size' => 'Размер Вселенной',\n  'adm_opt_galaxies' => 'Галактик',\n  'adm_opt_systems' => 'Систем',\n  'adm_opt_planets' => 'Планет',\n  'adm_opt_build_on_research' => 'Строить лабораторию во время исследования',\n  'adm_opt_eco_scale_storage' => 'Масштабировать склады от скорости добычи',\n  'adm_opt_game_rules' => 'Ссылка на правила',\n  'adm_opt_max_colonies' => 'Количество колоний',\n  'adm_opt_exchange' => 'Курс обмена ресурсов',\n  'adm_opt_game_mode' => 'Тип Вселенной',\n  'adm_opt_chat' => 'Настройки чата',\n  'adm_opt_chat_timeout' => 'Таймаут по неактивности',\n  'adm_opt_allow_buffing' => 'Разрешить прокачку',\n  'adm_opt_ally_help_weak' => 'Разрешить удержание на слабом соаловце',\n  'adm_opt_email_pm' => 'Разрешить пересылку ЛС на e-mail',\n  'adm_opt_player_defaults' => 'Настройки игрока по умолчанию',\n  'adm_opt_game_default_language' => 'Язык интерфейса',\n  'adm_opt_game_default_skin' => 'Оформление/Шкурка',\n  'adm_opt_game_default_template' => 'Шаблон',\n  'adm_opt_player_change_name' => 'Смена ника игроком',\n  'adm_opt_player_change_name_options' => [\n    SERVER_PLAYER_NAME_CHANGE_NONE => 'Смена ника запрещена',\n    SERVER_PLAYER_NAME_CHANGE_FREE => 'Бесплатная смена ника',\n    SERVER_PLAYER_NAME_CHANGE_PAY => 'Смена ника за ТМ',\n  ],\n  'adm_opt_player_change_name_cost' => 'Стоимость в ТМ за смену ника',\n  'adm_opt_empire_mercenary_temporary' => 'Временные наемники',\n  'adm_opt_empire_mercenary_temporary_base' => 'Базовое время для найма, секунд',\n  'adm_opt_empire_mercenary_temporary_hint' => 'При включении опции все наемники будут преобразованы во временные с базовым сроком действия<br />При отключении опции все наемники будут преобразованы в постоянные. При этом рекрутированные наемники, не доступные по требованиям найма, хотя и не смогут быть проапгрейжены, но все равно будут активны - т.е. будут оказывать влияние на игру',\n  'adm_opt_experimental' => 'ЭКСПЕРИМЕНТАЛЬНЫЕ ОПЦИИ! ИСПОЛЬЗУЙТЕ С ОСТОРОЖНОСТЬЮ!',\n  'adm_opt_tpl_minifier' => 'Минификатор темплейтов',\n  'adm_opt_tpl_minifier_hint' => 'Минификатор сжимает темплейты, заменяя несколько идущих подряд \"пустых\" символов (перевод строки, табуляция, пробел) одним пробелом. Подробнее о работе минификатора можно прочитать в /docs/changelog.txt',\n  'adm_lm_compensate' => 'Компенсировать',\n  'adm_pl_comp_title' => 'Компенсация уничтоженной планеты',\n  'adm_pl_comp_src' => 'Уничтожить планету',\n  'adm_pl_comp_dst' => 'Зачислить ресурсы на планету',\n  'adm_pl_comp_bonus' => 'Бонус игрока',\n  'adm_pl_comp_check' => 'Проверить',\n  'adm_pl_comp_confirm' => 'Подтвердить',\n  'adm_pl_comp_done' => 'Готово',\n  'adm_pl_comp_price' => 'Стоимость построек',\n  'adm_pl_comp_got' => 'Будет зачислено',\n  'adm_pl_com_of_plr' => 'игрока',\n  'adm_pl_comp_will_be' => 'будет',\n  'adm_pl_comp_destr' => 'уничтожена.',\n  'adm_pl_comp_recieve' => 'Указанное количество ресурсов',\n  'adm_pl_comp_recieve2' => 'зачислено на планету',\n  'adm_pl_comp_err_0' => 'Не найдена уничтожаемая планета',\n  'adm_pl_comp_err_1' => 'Планета уже уничтожена',\n  'adm_pl_comp_err_2' => 'Не найдена планета, на которую нужно зачислить ресурсы',\n  'adm_pl_comp_err_3' => 'У указанных планет разные владельцы. Зачислить ресурсы можно только на планету того же игрока',\n  'adm_pl_comp_err_4' => 'Планета не пренадлежит указанному игроку',\n  'adm_pl_comp_err_5' => 'Планеты для уничтжения и для зачисления ресурсов совпадают',\n  'adm_ver_versions' => 'Версии компонентов сервера',\n  'adm_ver_version_sn' => 'Версия движка',\n  'adm_ver_version_db' => 'Версии базы данных',\n  'adm_update_force' => 'Форсировать обновление с нуля',\n  'adm_update_repeat' => 'Повторить предыдущее обновление',\n  'adm_ptl_test' => 'Тест работы phpBB Template Engine',\n  'adm_counter_recalc' => 'Пересчитать таблицу `counter`',\n  'adm_lm_planet_edit' => 'Редактировать',\n  'adm_planet_edit' => 'Редактирование планеты',\n  'adm_planet_id' => 'Идентификатор планеты',\n  'adm_name' => 'Название',\n  'adm_planet_change' => 'Изменение',\n  'adm_planet_parent' => 'Родительская планета',\n  'adm_planet_active' => 'Активные планеты',\n  'adm_planet_edit_hint' => '<ul>    <li>Если на пустой странице ввести идентификатор планеты и нажать кнопку \"Подтвердить\" движок попытается вывести информацию по планете с таким ИД: тип, название и координаты,    а так же текущее количество юнитов/ресурсов выбранного типа на планете</li>    <li>Что бы убрать с планеты определенное количество юнитов/ресурсов нужно вводить отрицательное число</li>  </ul>',\n  'adm_planet_list_title' => 'Список планет',\n  'adm_sys_owner' => 'Владелец',\n  'adm_sys_owner_id' => 'ИД владельца',\n  'addm_title' => 'Добавить луну',\n  'addm_addform' => 'Формуляр новой луны',\n  'addm_playerid' => 'ID планеты размещения',\n  'addm_moonname' => 'Название луны',\n  'addm_moongala' => 'Укажите галактику',\n  'addm_moonsyst' => 'Укажите систему',\n  'addm_moonplan' => 'Укажите позицию',\n  'addm_moondoit' => 'Добавить',\n  'addm_done' => 'Луна создана',\n  'adm_usr_level' => array(\n    '0' => 'Игрок',\n    '1' => 'Оператор',\n    '2' => 'Модератор',\n    '3' => 'Администратор',\n  ),\n\n  'adm_usr_genre' => array(\n    GENDER_UNKNOWN => 'Не выбран',\n    GENDER_MALE => 'Мужчина',\n    GENDER_FEMALE => 'Женщина',\n  ),\n\n  'panel_mainttl' => 'Панель администратора',\n  'adm_panel_mnu' => 'Поиск игрока',\n  'adm_panel_ttl' => 'Вид поиска',\n  'adm_search_pl' => 'Поиск по имени',\n  'adm_search_ip' => 'Поиск по IP',\n  'adm_stat_play' => 'Статистика игрока',\n  'adm_mod_level' => 'Уровень доступа',\n  'adm_player_nm' => 'Имя игрока',\n  'adm_ip' => 'IP',\n  'adm_plyer_wip' => 'Игроки с IP',\n  'adm_frm1_id' => 'ID',\n  'adm_frm1_name' => 'Имя',\n  'adm_frm1_ip' => 'IP',\n  'adm_frm1_mail' => 'e-Mail',\n  'adm_frm1_acc' => 'Звание',\n  'adm_frm1_gen' => 'Пол',\n  'adm_frm1_main' => 'ID планеты',\n  'adm_frm1_gpos' => 'Координаты',\n  'adm_mess_lvl1' => 'Уровень доступа',\n  'adm_mess_lvl2' => '&quot;теперь&quot; ',\n  'adm_colony' => 'Колонии',\n  'adm_planet' => 'Планета',\n  'adm_moon' => 'Луна',\n  'adm_technos' => 'Технологии',\n  'adm_bt_search' => 'Искать',\n  'adm_bt_change' => 'Изменить',\n  'flt_id' => 'ID',\n  'flt_fleet' => 'Флот',\n  'flt_ships' => 'Состав',\n  'flt_mission' => 'Задание',\n  'flt_here' => 'Обратно',\n  'flt_there' => 'Туда',\n  'flt_here_there' => 'Туда/Обратно',\n  'flt_departure' => 'Пункт отправления',\n  'flt_owner' => 'Владелец',\n  'flt_planet' => 'Планета',\n  'flt_time_return' => 'Возвращение',\n  'flt_e_owner' => 'Пункт назначения',\n  'flt_time_arrive' => 'Прибытие',\n  'flt_staying' => 'Время ожидания',\n  'flt_action' => 'Действие',\n  'flt_title' => 'Флоты в полёте',\n  'flt_no_fleet' => 'Сейчас в полете нет ни одного флота',\n  'mlst_title' => 'Список сообщений',\n  'mlst_mess_del' => 'Удаление сообщений',\n  'mlst_hdr_page' => 'Стр.',\n  'mlst_hdr_title' => ' ) сообщений :',\n  'mlst_hdr_prev' => '[ &lt;- ]',\n  'mlst_hdr_next' => '[ -&gt; ]',\n  'mlst_hdr_id' => 'ID',\n  'mlst_hdr_type' => 'Тип сообщений',\n  'mlst_hdr_time' => 'Время отправки',\n  'mlst_hdr_from' => 'От кого',\n  'mlst_hdr_to' => 'Кому',\n  'mlst_hdr_text' => 'Contenu',\n  'mlst_hdr_action' => 'Отм.',\n  'mlst_del_mess' => 'Удалить',\n  'mlst_bt_delsel' => 'Удалить выделенные сообщения',\n  'mlst_bt_deldate' => 'Удалить',\n  'mlst_hdr_delfrom' => 'Удалить сообщения текущего типа ранее даты',\n  'mlst_no_messages' => 'Нет сообщений',\n  'mlst_messages_deleted' => 'Удалены сообщения с ID %s',\n  'mlst_messages_deleted_date' => 'Удалены сообщения типа \"%s\" по дату %s (не включая сообщения на указанную дату)',\n\n  'adm_lng_title' => 'Локализация',\n  'adm_lng_warning' => 'ВНИМАНИЕ! Это alpha-версия редактор локализаций! Используйте его на свой страх и риск!',\n  'adm_lng_domain' => 'Домен',\n  'adm_lng_string_name' => 'Имя строки',\n  'adm_lng_string_add' => 'Добавить строку',\n  'adm_uni_price_galaxy' => 'Базовая стоимость переименования галактики',\n  'adm_uni_price_system' => 'Базовая стоимость переименования системы',\n\n  'adm_opt_ver_check' => 'Проверка версии',\n  'adm_opt_ver_check_hint' => 'При любом виде проверки версии передаются только анонимные данные - текущая версия БД, номер релиза и версия игры. Вы можете проверить версию \"вручную\" - нажав кнопку \"Проверить версию\".',\n  'adm_opt_ver_check_do' => 'Проверить версию',\n  'adm_opt_ver_check_last' => 'Последняя проверка версии производилась',\n  'adm_opt_ver_check_auto' => 'Автоматическая проверка версии',\n  'adm_opt_ver_check_auto_hint' => 'Вы можете включить автоматическую проверку версии игры. При этом проверка будет выполняться автоматически раз в заданный период времени (по умолчанию - раз в сутки). Подробнее см. документацию',\n\n  'adm_opt_ver_response' => array(\n    SNC_VER_NEVER => 'Проверка версии не производилась',\n\n    SNC_VER_ERROR_CONNECT => 'Ошибка проверки версии. Игра не смогла свзаться с сервером обновлений. Убедитесь, что у вас установлен и активизорван в PHP CURL или что в настройках PHP разрешен доступ к удаленным серверам',\n    SNC_VER_ERROR_SERVER => 'Ошибка сервера обновлений. Проверьте - не вышла ли более новая версия движка с более продвинутой подержкой сервера обновлений. В противном случае срочно уведомите разработчика!',\n\n    SNC_VER_EXACT => 'У вас установлена самая последняя альфа-версия будущего релиза. Спасибо за участие в тестировании!',\n    SNC_VER_LESS => 'Вы используете альфа-версию будущего релиза. Однако есть уже более свежая альфа! Обновитесь, если хотите получить исправления ошибок текущих версий и поучаствовать в тестировании новых возможностей игры.',\n    SNC_VER_FUTURE => 'У вас версия игры из будущего! Срочно свяжитесь с разработчиком и передайте ему эту версию! Так же подготовьтесь к визиту из Темпоральной Милиции по поводу нарушения пространственно-временного континуума и причинно-следственных связей...',\n\n    SNC_VER_RELEASE_EXACT => 'У вас самая свежая версия самого последнего релиза игры',\n    SNC_VER_RELEASE_MINOR => 'У вас устаревшая версия игры - уже вышло обновление текущего релиза. В нем, скорее всего, устранены некоторые ошибки вашей версии. Желательно обновить игру.',\n    SNC_VER_RELEASE_MAJOR => 'У вас сильно устаревшая версия игры - уже вышел новый релиз. Устранение ошибок, новые возможности - обязательно обновитесь!',\n    SNC_VER_RELEASE_ALPHA => 'У вас самая свежая версия релиза игры. Однако уже есть альфа-версия следующего релиза. Может быть вы захотите поучаствовать в её тестировании?',\n\n    SNC_VER_MAINTENANCE => 'Сервер обновлений отключен для техобслуживания. Повторите попытку позже',\n    SNC_VER_UNKNOWN_RESPONSE => 'Сервер обновлений выдал неизвестный ответ. Скорее всего это означает, что вышла более новая версия движка обладающая более продвинутыми возможностями работы с обновлениями',\n    SNC_VER_INVALID => 'Не могу понять, что у вас за странная и непонятная версия. Свяжитесь с разработчиком для диагностики проблемы.',\n    SNC_VER_STRANGE => 'Вы не должны видеть это сообщение. Если вы его увидели - что-то пошло не так. Свяжитесь с разработчиком для диагностики проблемы.',\n\n    SNC_VER_REGISTER_UNREGISTERED => 'Ваш сервер еще не зарегистрирован',\n    SNC_VER_REGISTER_ERROR_MULTISERVER => 'Ошибка - ваш сервер зарегестрирован несколько раз! Обратитесь к разработчику для диагностики проблемы.',\n    SNC_VER_REGISTER_ERROR_REGISTERED => 'Ошибка - ваш сервер уже зарегестрирован! Проверьте наличие уникального ключа и идентификатора в конфигурации сервера!',\n    SNC_VER_REGISTER_ERROR_NO_NAME => 'Ошибка - нет имени сервера! Нужно назначить имя сервера!',\n    SNC_VER_REGISTER_ERROR_WRONG_URL => 'Ошибка - неправильный URL! Переданная строка не является корректным URLом. Возможно вы попытались зарегестрировать сервер, запущенный на localhost - сервер обновлений не работает с такими серверами.',\n    SNC_VER_REGISTER_REGISTERED => 'Ваш сайт успешно зарегистрирован',\n\n    SNC_VER_ERROR_INCOMPLETE_REQUEST => 'Ошибка - некорректные ключ или ИД сайта! Проверьте корректность ключа и ИД в конфигурации сервера.',\n    SNC_VER_ERROR_UNKNOWN_KEY => 'Ошибка - неизвестный ключ! Переданный ключ не найден в БД сервера обновлений! Проверьте корректность ключа в конфигурации сервера.',\n    SNC_VER_ERROR_MISSMATCH_KEY_ID => 'Ошибка - переданный ключ не соответствует переданному ИД! Проверьте корректность ключа и ИД в конфигурации сервера.',\n  ),\n\n  'adm_opt_ver_response_short' => array(\n    SNC_VER_NEVER => 'Не производилась',\n\n    SNC_VER_ERROR_CONNECT => 'Ошибка соединения',\n    SNC_VER_ERROR_SERVER => 'Ошибка сервера',\n\n    SNC_VER_EXACT => 'Последняя альфа',\n    SNC_VER_LESS => 'Старая альфа',\n    SNC_VER_FUTURE => 'Альфа из будущего',\n\n    SNC_VER_RELEASE_EXACT => 'Свежая версия',\n    SNC_VER_RELEASE_MINOR => 'Рекомендовано обновление',\n    SNC_VER_RELEASE_MAJOR => 'Необходимо обновление',\n    SNC_VER_RELEASE_ALPHA => 'Свежий релиз',\n\n    SNC_VER_MAINTENANCE => 'Техобслуживание',\n    SNC_VER_UNKNOWN_RESPONSE => 'Неизвестный ответ',\n    SNC_VER_INVALID => 'Ошибка версии',\n    SNC_VER_STRANGE => 'Непредвиденная фигня',\n\n    SNC_VER_REGISTER_UNREGISTERED => 'Незарегистрирован',\n    SNC_VER_REGISTER_ERROR_MULTISERVER => 'Мультирегистрация',\n    SNC_VER_REGISTER_ERROR_REGISTERED => 'Ошибка ключа',\n    SNC_VER_REGISTER_ERROR_NO_NAME => 'Ошибка имени',\n    SNC_VER_REGISTER_REGISTERED => 'Зарегистрирован',\n\n    SNC_VER_ERROR_INCOMPLETE_REQUEST => 'Ошибка ключа или ИД',\n    SNC_VER_ERROR_UNKNOWN_KEY => 'Неизвестный ключ',\n    SNC_VER_ERROR_MISSMATCH_KEY_ID => 'Ключ не соответствует ИД',\n  ),\n\n  'adm_upd_register' => 'Регистрация сервера',\n\n  'adm_upd_register_hint' => '\n    Регистрация сервера нужна для ряда запросов к серверу обновлений. При регистрации передается минимум информации, необходимой для идентификации сервера:\n    <ul>\n      <li>Полный URL сервера - т.е. HTTP-адрес и подкаталог сервера. Например: http://myserver.com/myfolder/. Это необходимо для первичной идентификации сервера. Полный путь необходим для того, что бы различать несколько копий СуперНовы, установленных на одном IP или домене.</li>\n      <li>Внутреннее название сервера. Используется для подстановки в сообщения.</li>\n    </ul>\n    Зачем вообще регистрировать свой сервер? В будущем планируется ряд возможностей, которые буду доступны только зарегистрированным серверам. В их число входит (отсортированы по запланированным срокам реализации):\n    <ul>\n      <li>Автоматическое получение чейнджлога</li>\n      <li>Автоматизированное обновление движка</li>\n      <li>Участие в рейтинге серверов</li>\n      <li>Багрепорты от администраторов серверов</li>\n      <li>Чат для администраторов серверов</li>\n      <li>По запросу - удаленная диагностика сервера</li>\n      <li>...и многое, многое другое</li>\n    </ul>\n    Зачем регистрировать свой сервер прямо сейчас?\n    <ul>\n      <li>Запросы от администраторов зарегестрированных серверов имеют больший приоритет при диагностике проблем и обработке багрепортов.</li>\n      <li>При регистрации кроме индивидуального ключа серверу выдается уникальный идентификационный номер, который будет использоваться при первичной сортировке серверов. Чем раньше будет зарегистрирован сервер - тем, например, выше он будет в общем каталоге серверов...</li>\n    </ul>\n  ',\n\n  'adm_upd_register_do' => 'Зарегистрировать сервер',\n  'adm_upd_register_already' => 'Вы уже зарегистрированы на сервере обновлений. Обязательно сохраните ИД и уникальный ключ вашего сервера!',\n  'adm_upd_register_id' => 'Регистрационный номер',\n  'adm_upd_register_key' => 'Регистрационный ключ',\n\n  'adm_opt_stats_and_records' => 'Статистика и рекорды',\n  'adm_opt_stats_hide_admins' => 'Прятать админов',\n  'adm_opt_stats_hide_admins_detail' => 'Будут скрыты все аккунты с authlevel > 0',\n  'adm_opt_stats_hide_player_list' => 'Прятать игроков',\n  'adm_opt_stats_hide_player_list_detail' => 'Список ID скрываемых игроков через запятую',\n  'adm_opt_stats_schedule' => 'Расписание обновления статистики',\n  'adm_opt_stats_schedule_detail' => 'Формат: \"[ГГГГ:[ММ:[ДД:[ЧЧ:[ММ:[СС]]]]]][,(...)]\"<br />\n    Нулевые параметры слева - необязательны<br />\n    Пустые параметры справа приравниваются к нулю<br />\n    Примеры:<br />\n     - \"00:00:27:00\" означает \"запуск в 27 минут каждого часа\";<br />\n     - \"04::\" - \"запуск в 4 утра каждого дня\";<br />\n     - \"02::,17:00\" - \"запуск в 2 утра каждого дня и в 17 минут каждого часа\";<br />\n     - \"1:4:30:00\" - \"Запуск 1 числа каждого месяца в 04:30 утра\" итд ',\n  'adm_opt_stats_hide_pm_link' => 'Скрывать ссылки на ЛС',\n\n  'adm_pay' => 'Платежи',\n  'adm_pay_stats' => 'Статистика платежей',\n  'adm_pay_th_payer' => 'Плательщик',\n  'adm_pay_th_payer_id' => 'ID',\n  'adm_pay_th_payer_name' => 'Имя',\n  'adm_pay_th_payment' => 'Платёж',\n  'adm_pay_th_payment_id' => 'ID',\n  'adm_pay_th_payment_date' => 'Дата',\n  'adm_pay_th_payment_status' => 'Статус',\n  'adm_pay_th_payment_amount' => 'Сумма',\n  'adm_pay_th_payment_currency' => 'Валюта',\n  'adm_pay_th_mm_paid' => 'Оплачено',\n  'adm_pay_th_mm_gained' => 'Начислено',\n  'adm_pay_th_module' => 'Платёжная система',\n  'adm_pay_th_module_name' => 'Тип',\n\n  'adm_pay_filter_all' => '-- Все --',\n  'adm_pay_filter_status' => array(\n    PAYMENT_STATUS_ALL => '-- Все --',\n    PAYMENT_STATUS_NONE => 'Не завершен',\n    PAYMENT_STATUS_COMPLETE => 'Завершен',\n  ),\n  'adm_pay_filter_test' => array(\n    PAYMENT_TEST_ALL => '-- Все --',\n    PAYMENT_TEST_REAL => 'Реальный',\n    PAYMENT_TEST_PROBE => 'Тестовый',\n  ),\n  'adm_pay_filter_stat' => array(\n    PAYMENT_FILTER_STAT_NORMAL => '-- Нет --',\n    PAYMENT_FILTER_STAT_MONTH => 'По месяцам',\n    PAYMENT_FILTER_STAT_YEAR => 'По годам',\n    PAYMENT_FILTER_STAT_ALL => 'За всё время',\n  ),\n  'adm_pay_filter_stat_name' => 'Статистика',\n\n  'adm_user_stat' => 'Статистика пользователей',\n  'adm_user_online' => 'Онлайн с %s по %s',\n\n  'adm_ban_unban' => 'Бан/Разбан',\n  'adm_metametter_payment' => 'ММ & Платежи',\n\n  'adm_stat_already_started' => 'Статистика уже обновляется прямо сейчас',\n\n  'adm_dm_change_hint' => 'Поиск осуществляется сначала по ID игрока, а если не найден - по имени',\n\n  'adm_matter_change_log_record' => 'Через админку пользователем [%3$s] \"%4$s\" для аккаунта [%1$d] \"%2$s\" по причине \"%5$s\"',\n\n  'adm_game_status' => 'Текущее состояние игры',\n\n  'adm_log_delete_update_info' => 'Удалить информацию об обслуживании БД, обновлениях статистики и движка',\n\n  'admin_tab_status' => 'Статус',\n  'admin_tab_game' => 'Игра',\n  'admin_tab_universe' => 'Вселенная',\n  'admin_tab_planets' => 'Планеты',\n  'admin_tab_stats_and_records' => 'Статистика',\n  'admin_tab_urls' => 'Ссылки',\n  'admin_tab_players' => 'Игроки',\n  'admin_tab_UBE' => 'Бой',\n  'admin_tab_advertise' => 'Реклама',\n\n  'admin_tab_universe_main' => 'Вселенная',\n\n  'admin_ptl_test_la_' => \"Single'Double\\\"Zero\\0End\",\n\n  'admin_title_access_denied' => 'Доступ запрещен',\n\n  'menu_admin_modules' => 'Модули',\n\n  'adm_player' => 'Игрок',\n  'adm_planets' => 'Планеты',\n\n  // ------------------ NOT LOCALIZED -------------------------------\n  'adm_mm_title'                        => 'Изменение количества Метаматерии',\n  'adm_mm_account'                      => 'Аккаунт: ID, имя или емейл регистрации',\n  'adm_mm_account_hint'                 => 'Поиск аккаунта сначала идёт по ID, затем - по имени, затем - по емейлу регистрации',\n  'adm_mm_player'                       => 'Игрок: ID или имя из списка игроков',\n  'adm_mm_player_hint'                  => 'Поиск игрока сначала идёт по ID, затем - по имени',\n  'adm_mm_err_points_empty'             => 'Укажите количество ММ (положительное - для начисления, отрицательное - для снятия)',\n  'adm_mm_err_account_not_found'        => 'Ошибка: не могу найти аккаунт c ID, именем или емейлом \"%1$s\"',\n  'adm_mm_err_player_not_found'         => 'Ошибка: не найден игрок с ID или именем \"%1$s\"',\n  'adm_mm_err_player_no_account'        => 'Ошибка: не могу найти аккаунт для игрока \"%1$s\"',\n  'adm_mm_err_account_and_player_empty' => 'Ошибка: не указан ни аккаунт, ни игрок для изменения ММ',\n  'adm_mm_err_mm_change_failed'         => 'Ошибка: внутренняя ошибка начисления ММ. Свяжитесь с разработчиком',\n  'adm_mm_msg_mm_changed'               => 'У аккаунта [%2$d] \"%1$s\" (игрок [%4$s] \"%5$s\") количество ММ успешно изменено на <span class=\"metamatter\">%3$s ММ</span>',\n  'adm_mm_msg_confirm_mm_change'        => 'Подтвердите изменение ММ у аккаунт [%2$d] \"%1$s\" (игрок [%4$s] \"%5$s\") на <span class=\"metamatter\">%3$s ММ</span>',\n  'adm_mm_msg_change_mm_log_record'     => 'Админ [%6$s] \"%7$s\" (игрок [%3$s] \"%4$s\") причина \"%5$s\" для [%1$d] \"%2$s\" (игрок [%8$d] \"%9$s\")',\n\n  'admin_ally_list' => 'Список Альянсов',\n);\n"
  },
  {
    "path": "language/ru/affilates.mo.php",
    "content": "<?php\n\n/*\n#############################################################################\n#  Filename: affilates.mo\n#  Project: SuperNova.WS\n#  Website: http://www.supernova.ws\n#  Description: Massive Multiplayer Online Browser Space Strategy Game\n#\n#  Copyright © 2009-2018 Gorlum for Project \"SuperNova.WS\"\n#############################################################################\n*/\n\n/**\n*\n* @package language\n* @system [Russian]\n* @version 43a16.13\n*\n*/\n\n/**\n* DO NOT CHANGE\n*/\n\n!defined('INSIDE') && die();\n\n$a_lang_array = (array(\n  'aff_title' => 'Партнерская программа',\n  'aff_text1' => 'Разместите личную ссылку, баннер или юзербар на форуме или сайте и каждый пришедший по ссылке станет вашим Приглашенным. Вы будете получать по 1 ТМ за каждые',\n  'aff_text2' => 'ТМ, заработанных приглашенным.',\n  'aff_text3' => 'Начисление бонусов начинается после того, как ваш Приглашенный заработает',\n  'aff_link' => 'Личная ссылка в партнерской программе',\n  'aff_link_direct' => 'Прямая ссылка',\n  'aff_link_bb' => 'BBCode для размещения личной ссылки на форуме',\n  'aff_link_html' => 'HTML-код для размещения личной ссылки на веб-странице',\n  'aff_banner' => 'Баннер 416x58',\n  'aff_banner_bb' => 'BBCode для размещения баннера на форуме',\n  'aff_banner_html' => 'HTML-код для размещения баннера на веб-странице',\n  'aff_userbar' => 'Юзербар 350х19',\n  'aff_userbar_bb' => 'BBCode для размещения юзербара на форуме',\n  'aff_userbar_html' => 'HTML-код для размещения юзербара на веб-странице',\n  'aff_list' => 'Список Приглашенных',\n  'aff_none' => 'Нет Приглашенных',\n  'aff_gained' => 'ТМ заработано игроком',\n  'aff_your_bonus' => 'Ваш бонус',\n));\n"
  },
  {
    "path": "language/ru/alliance.mo.php",
    "content": "<?php\n\n/*\n#############################################################################\n#  Filename: alliance.mo\n#  Project: SuperNova.WS\n#  Website: http://www.supernova.ws\n#  Description: Massive Multiplayer Online Browser Space Strategy Game\n#\n#  Copyright © 2009-2018 Gorlum for Project \"SuperNova.WS\"\n#############################################################################\n*/\n\n/**\n*\n* @package language\n* @system [Russian]\n* @version 45d0\n*\n*/\n\n/**\n* DO NOT CHANGE\n*/\n\nif (!defined('INSIDE')) die();\n\n\n$a_lang_array = [\n  'ali_dip_title' => 'Дипломатия',\n  'ali_dip_negotiate' => 'Переговоры',\n  'ali_adm_msg_subject' => 'Рассылка Альянса',\n  'ali_dip_offers_your' => 'Ваши предложения',\n  'ali_dip_offers_to_you' => 'Предложения вам',\n  'ali_dip_offer_none' => 'Нет предложений',\n  'ali_dip_offer' => 'Предложение',\n  'ali_dip_offers' => 'Предложения',\n  'ali_dip_offer_new' => 'Вступить в переговоры',\n  'ali_dip_offer_to_ally' => 'Предложить Альянсу',\n  'ali_dip_offer_make' => 'Начать переговоры',\n  'ali_dip_offer_answer' => 'Альянс отклонил ваше предложение',\n  'ali_dip_offer_deny_reason' => 'Вы отклонили предложение',\n  'ali_dip_offer_to' => 'Альянсу',\n  'ali_dip_offer_from' => 'От Альянса',\n  'ali_dip_offer_deny' => 'Отклонить предложение',\n  'ali_dip_offer_accept' => 'Принять предложение',\n  'ali_dip_offer_delete' => 'Отозвать предложение',\n  'ali_dip_err_no_ally' => 'Нет такого Альянса',\n  'ali_dip_err_same_ally' => 'Нельзя проводить переговоры со своим же Альянсом',\n  'ali_dip_err_wrong_offer' => 'Нельзя сделать ТАКОЕ предложение',\n  'ali_dip_err_offer_none' => 'Нет такого предложения',\n  'ali_dip_err_offer_same' => 'Вы уже находитесь с этим Альянсом в отношениях %s',\n  'ali_dip_err_offer_alien' => 'Это предложение делали не вам!',\n  'ali_dip_err_offer_accept_own' => 'Нельзя принять за другого свое предложение!',\n  'ali_dip_err_offer_empty' => 'Не указано предложение',\n  'ali_dip_relation_none' => 'Нет отношений',\n  'ali_dip_relation_change_auto_accept' => 'Альянс \"%1$s\" изменил отношение к Альянсу \"%2$s\" на \"%3$s\"',\n  'ali_dip_relation_change_own' => 'Мы приняли предложение Альянса \"%2$s\" изменить отношения на \"%3$s\"',\n  'ali_dip_relation_change_other' => 'Альянс \"%1$s\" принял наше предложение изменить отношения на \"%3$s\"',\n  'ali_dip_relations' => [\n    ALLY_DIPLOMACY_NEUTRAL => 'Нейтралитет',\n    ALLY_DIPLOMACY_WAR => 'Война',\n    ALLY_DIPLOMACY_PEACE => 'Мир',\n    ALLY_DIPLOMACY_CONFEDERATION => 'Конфедерация',\n    ALLY_DIPLOMACY_FEDERATION => 'Федерация',\n    ALLY_DIPLOMACY_UNION => 'Союз',\n    ALLY_DIPLOMACY_MASTER => 'Ведущий',\n    ALLY_DIPLOMACY_SLAVE => 'Ведомый',\n  ],\n\n  'ali_lessThen15min' => '&lt; 15 м',\n  'ali_confirm' => 'Подтвердить',\n  'ali_confirmation' => 'Подтверждение',\n  'ali_adm_disband' => 'Распустить Альянс',\n  'ali_adm_options' => 'Настройки Альянса',\n  'ali_adm_transfer' => 'Передать Альянс игроку',\n  'ali_adm_return' => 'Вернуться к управлению Альянсом',\n  'ali_adm_kick' => 'Исключить игрока из Альянса',\n  'ali_adm_kick_confirm' => 'Вы уверенны что хотите исключить игрока из Альнса?',\n  'ali_adm_requests' => 'Заявки в альянс',\n  'ali_adm_newLeader' => 'ВЫБЕРИТЕ ИГРОКА',\n  'ali_adm_lastRank' => 'Нельзя удалить единственное звание!',\n  'ali_adm_rights_title' => 'Настройка прав доступа',\n  'ali_adm_rights_rank_new' => 'Новое звание',\n  'ali_adm_rights_rank_delete' => 'Удалить звание',\n  'ali_adm_rights_rank_none' => 'Нет званий',\n  'ali_adm_rights_rank_name' => 'Звание',\n  'ali_adm_rights_mass_mail' => 'Сообщение всему Альянсу',\n  'ali_adm_rights_view_online' => 'Просмотр on-line статуса участников',\n  'ali_adm_rights_helper' => 'Помощник главы (Для передачи необходим ранг основателя)',\n  'ali_adm_rights_legend' => 'Права Альянса',\n  'ali_leaderRank' => 'Глава Альянса',\n  'ali_defaultRankName' => 'Новичок',\n  'ali_make_title' => 'Создание Альянса',\n  'ali_make_tag_length' => '(от 3 до 8 символов)',\n  'ali_make_name_length' => '(до 35 символов)',\n  'ali_make_confirm' => 'Создать Альянс',\n  'ali_req_cancel' => 'Удалить заявку',\n  'ali_req_candidate' => 'Кандидат',\n  'ali_req_characters' => 'символов',\n  'ali_req_date' => 'Дата подачи заявки',\n  'ali_req_deny_msg' => 'Ваша заявка на вступление в Альянс [%s] была отклонена.<br>Причина отказа: \"%s\".<br>Вы можете удалить заявку и попробовать позже или вступить в другой Альянс.',\n  'ali_req_deny_admin' => '<font color=red>Запрос уже отклонен</font>. Однако, пока пользователь не удалил запрос на вступление, вы можете изменить свое решение',\n  'ali_req_deny_reason' => 'Ваш запрос на вступление отклонен',\n  'ali_req_emptyList' => 'Нет заявок для рассмотрения',\n  'ali_req_inAlly' => 'Вы уже являетесь участником Альянса.',\n  'ali_req_make' => 'Подать заявку',\n  'ali_req_not_allowed' => 'НЕТ ПРИЕМА',\n  'ali_req_otherRequest' => 'Вы уже подали заявку в другой Альянс.',\n  'ali_req_template' => 'Прошу принять меня в ваш Альянс',\n  'ali_req_text' => 'Текст заявки',\n  'ali_req_title' => 'Подача заявки в Альянс',\n  'ali_req_waiting' => 'Ваша заявка на вступление в Альянс [%s] будет расмотрена главой Альянса.<br>Вас оповестят о принятом решении.',\n  'ali_req_check' => 'Управление заявками',\n  'ali_req_requestCount' => 'Заявок в альянс',\n  'ali_req_admin_title' => 'Обзор заявок',\n  'ali_req_accept' => 'Принять заявку',\n  'ali_req_deny' => 'Отклонить заявку',\n  'ali_search_title' => 'Поиск Альянса',\n  'ali_search_action' => 'Искать',\n  'ali_search_tip' => 'Поиск можно производить по части имени или обозначения Альянса',\n  'ali_search_result_none' => 'Не найдено Альянсов, соответствующих вашему запросу.',\n  'ali_search_show_all' => 'Список и статистика всех Альянсов',\n  'ali_sys_name' => 'Название',\n  'ali_sys_tag' => 'Обозначение',\n  'ali_sys_members' => 'Участники',\n  'ali_sys_notFound' => 'Такой Альянс не существует',\n  'ali_sys_memberName' => 'Имя',\n  'ali_sys_points' => 'Очки',\n  'ali_sys_lastActive' => 'Активность',\n  'ali_sys_totalMembers' => 'Всего',\n  'ali_sys_clear' => 'Сбросить',\n  'ali_sys_main_page' => 'Вернуться на главную страницу Альянса',\n  'ali_sys_joined' => 'Дата вступления',\n  'ali_frm_write' => 'Писать на форум',\n  'ali_info_title' => 'Информация об Альянсе',\n  'ali_info_internal' => 'Внутрення информация',\n  'ali_info_leave' => 'Покинуть Альянс',\n  'ali_info_bonus_rate' => 'Коэфициент бонуса',\n  'Name' => 'Название',\n  'Tag' => 'Обозначение',\n  'Members' => 'Участники',\n  'Accept_cand' => 'Принять',\n  'alliance' => 'Альянс',\n  'alliances' => 'Альянсы',\n  'Alliance_information' => 'Информация об Альянсе',\n  'Alliance_logo' => 'Логотип Альянса',\n  'alliance_tag' => 'Обозначение Альянса',\n  'Allow_request' => 'Принимать заявки',\n  'allyance_name' => 'Имя Альянса',\n  'ally_admin' => 'Управление Альянсом',\n  'ally_been_maked' => 'Альянс %s успешно создан',\n  'ally_description' => 'Описание Альянса',\n  'ally_dissolve' => 'Удаление Альянса',\n  'Ally_info_1' => 'Информация об Альянсе',\n  'ally_maked' => '%s создан',\n  'Ally_nodescription' => 'У Альянса нет описания',\n  'ally_notexist' => 'Альянс больше не существует',\n  'Ally_not_exist' => 'К сожалению нет никакой информации о этом Альянсе',\n  'Ally_transfer' => 'Передать Альянс',\n  'All_players' => 'Все игроки',\n  'always_exist' => '%s уже существует',\n  'Aplication_acepted' => 'Вы приняты',\n  'Aplication_hello' => 'Приветствую<br>Альянс :',\n  'Aplication_rejected' => 'Ваша заявка на вступление в альянс была отклонена.<br>Причина:<br>',\n  'apply_cantbeadded' => 'Запрос не удался, попробуйте ещё раз!',\n  'apply_registered' => 'Ваша заявка была отправлена.<br><br><a href=alliance.php>Назад</a>',\n  'Back' => 'Назад',\n  'Canceld_req_text' => 'Вы отменили заявку на вступление в [%s]',\n  'Change' => 'Изменить',\n  'ch_allyname' => 'Изменить имя Альянса',\n  'ch_allytag' => 'Изменить обозначение Альянса',\n  'Circular_message' => 'Сообщение Альянсу',\n  'Circular_sended' => 'Сообщение успешно отправлено',\n  'Clear' => 'Очистить',\n  'Click_writerequest' => 'Нажмите здесь чтобы написать заявку',\n  'Continue' => 'продолжить',\n  'Delete_apply' => 'Отклонить заявку',\n  'Denied_access' => 'Доступ запрещён!',\n  'Destiny' => 'Получатель',\n  'Exit_of_this_alliance' => 'Выйти из Альянса',\n  'External_text' => 'Внешний текст',\n  'Founder' => 'Создатель',\n  'Founder_name' => 'Звание основателя',\n  'Function' => 'Функция',\n  'Go_out_welldone' => 'Вы успешно покинули Альянс',\n  'have_not_name' => 'Введите имя Альянса',\n  'have_not_tag' => 'Введите обозначение Альянса',\n  'Help' => 'Помощь',\n  'Inactive' => 'Неактивный',\n  'Inner_section' => 'Внутренний текст',\n  'Internal_text' => 'Внутренний текст',\n  'knowed_allys' => 'Существующие Альянсы',\n  'laws_config' => 'Настройка прав доступа',\n  'Main_Page' => 'Домашняя страница',\n  'make_alliance' => 'Создание Альянса',\n  'make_alliance_owner' => 'Создать Альянс',\n  'max' => 'макс.',\n  'member' => 'Участник',\n  'memberlist_view' => 'Просмотр списка участников',\n  'members' => 'Участники',\n  'members_admin' => 'Управление участниками',\n  'Members_list' => 'Список участников',\n  'members_who_recived_message' => 'Следующие члены Альянса получили сообщение:',\n  'Message' => 'Сообщение',\n  'Motive_optional' => 'Причина (опционально)',\n  'New_name' => 'Новое обозначение',\n  'New_tag' => 'Новый Тэг',\n  'not_allow_request' => 'Отклонять заявки',\n  'Novate' => 'Новичок',\n  'Number' => '№',\n  'Off' => 'Off-line',\n  'Ok' => 'Ок',\n  'On' => 'On-line',\n  'Online' => 'Статус',\n  'Position' => 'Статус',\n  'Public_text_of_alliance' => 'Внешний текст',\n  'Range' => 'Звание',\n  'Reject_cand' => 'Отклонить',\n  'Reload' => 'Пример',\n  'Repel' => 'Repel',\n  'requests_view' => 'Просмотр заявок',\n  'Request_answer' => 'Запрос отклонен',\n  'Request_date' => 'Дата подачи заявки',\n  'Request_text' => 'Текст заявки',\n  's' => '[N/A]',\n  'Search' => 'Поиск',\n  'searchd_ally_avail' => 'Найдены Альянсы:',\n  'search_alliance' => 'Поиск',\n  'Send' => 'Отправить',\n  'Send_Apply' => 'Принять заявку',\n  'Send_circular_mail' => 'Послать сообщение всему Альянсу',\n  'Set_range' => 'Изменение ранга',\n  'Show_of_request_text' => 'Текст заявки',\n  'Texts' => 'Редактирование текста',\n  'Text_mail' => 'Отправка сообщения всему Альянсу',\n  'top10alliance' => 'Топ 10 Альянсов',\n  'transfer' => 'Передача',\n  'transfer_ally' => 'Передача альянса',\n  'transfer_to' => 'Передать альянс игроку:',\n  'Want_go_out' => 'Вы действительно хотите покинуть Альянс ?',\n  'write_apply' => 'Подать заявку',\n  'your_alliance' => 'Ваш Альянс',\n  'your_apply' => 'Ваша заявка',\n  'ali_info_leave_success' => 'Вы покинули Альянс [%s].<br />Теперь вы можете создать свой собственный Альянс или подать заявку на вступление в другой Альянс<br />',\n\n  'opt_avatar' => 'Логотип Альянса',\n  'opt_avatar_search' => 'Искать в Google',\n  'opt_avatar_remove' => 'Удалить логотип',\n  'opt_upload' => 'Загрузить',\n\n  'opt_msg_avatar_removed' => 'Логотип удален',\n  'opt_msg_avatar_uploaded' => 'Логотип изменен успешно',\n  'opt_msg_avatar_error_delete' => 'Ошибка удаления файла логотипа. Обратитесь к Администрации сервера',\n  'opt_msg_avatar_error_writing' => 'Ошибка сохранения файла логотипа. Обратитесь к Администрации сервера',\n  'opt_msg_avatar_error_upload' => 'Ошибка загрузки изображения %1. Обратитесь к Администрации сервера',\n  'opt_msg_avatar_error_unsupported' => 'Формат загруженного изображения не поддерживается. Поддерживаются только файлы JPG, GIF, PNG размером до 200КБ',\n\n  'ali_admin_mercenaries' => 'Наемники Альянса',\n  'ali_admin_plans' => 'Чертежи Альянса',\n  'ali_admin_techs' => 'Исследования Альянса',\n  'ali_admin_market_trader' => 'Обмен ресурсов на чёрном рынке',\n\n  'ali_res_player_bonus' => 'Бонус участника',\n  'ali_res_transfer' => 'Перевести',\n  'ali_res_transfer_long' => 'Перевести на счет Альянса',\n  'ali_res_no_resources' => 'У Альянса нет ресурсов',\n  'ali_res_transfer_dm_log' => 'Участник \\'%s\\' перевел %d ТМ на счет Альянса [%s]',\n\n  'ali_res_err_not_enough' => 'Не хватает ресурса %s!',\n  'ali_res_err_wrong_unit' => 'На счет Альянса можно передавать только ресурсы!',\n\n  'ali_res_alliance_bonus' => 'Бонусы Альянса',\n  'ali_res_alliance_bonus_players' => 'Количество участников для получения бонуса',\n\n  'ally_message_tag_exists' => 'Альянс с тэгом [%1$s] уже существует',\n  'ally_message_name_exists' => 'Альянс с именем \"%1$s\" уже существует',\n\n  'ally_alliances_recommended' => 'Рекомендуемые Альянсы',\n  'ally_recommended_diff' => 'Разница в очках',\n  'ally_recommended_rates' => 'Рейт',\n  'ali_search_result_tip' => '\n    <li>Кликните на имени или обозначении Альянса, что бы посмотреть информацию о нем</li>\n    <li>Кликните \"Послать заявку\", что бы послать запрос о вступлении</li>\n    <li>Колонка \"Разница в очках\" указывает на разницу между количеством очков у игрока и средним количеством очков на одного игрока в Альянсе. Если она отрицательная - средний игрок в Альянсе имеет больше очков, чем вы</li>\n    <li>Колонка \"Рейт\" указывает на соотношение между количеством очков у игрока и средним количеством очков на одного игрока в Альянсе. Если она меньше единицы - средний игрок в Альянсе слабее, чем вы</li>\n    <li>Рекомендуется по возможности выбирать Альянс с небольшой разницей в очках и рейте в пределах от 0,75 до 1,3. Однако, финальное решение всегда остаётся за игроком</li>\n   ',\n\n];\n"
  },
  {
    "path": "language/ru/announce.mo.php",
    "content": "<?php\n\n/*\n#############################################################################\n#  Filename: announce.mo\n#  Project: SuperNova.WS\n#  Website: http://www.supernova.ws\n#  Description: Massive Multiplayer Online Browser Space Strategy Game\n#\n#  Copyright © 2009-2018 Gorlum for Project \"SuperNova.WS\"\n#############################################################################\n*/\n\n/**\n*\n* @package language\n* @system [Russian]\n* @version 43a16.13\n*\n*/\n\n/**\n* DO NOT CHANGE\n*/\n\nif (!defined('INSIDE')) die();\n\n\n$a_lang_array = (array(\n  'add_announce' => 'Добавить объявление',\n  'metal' => 'Металл',\n  'crystal' => 'Кристалл',\n  'deuterium' => 'Дейтерий',\n  'Resources_to_be_sold' => 'Предлагаемые ресурсы',\n  'Desired_resources' => 'Требующиеся ресурсы',\n  'send' => 'Отправить',\n  'Your_announce_was_recorded' => 'Ваше объявление успешно отправлено',\n  'return_to_announce' => 'Вернутся на страницу объявлений',\n  'Classifieds' => 'Опубликованные объявления',\n  'Action' => 'Действие',\n  'Galaxy' => 'Галактика',\n  'Solar_system' => 'Солнечная система',\n  'Infos_of_delivery' => 'Информация торговца',\n  'Salesman' => 'Игрок',\n  'Delete' => 'Удалить',\n  'announce_status' => 'Статус объявления',\n  'Your_announce_not_recorded' => 'Ваше объявление не отправлено',\n  'Your_announce_was_deleted' => 'Ваше объявление успешно удалено',\n));\n"
  },
  {
    "path": "language/ru/artifacts.mo.php",
    "content": "<?php\n\n/*\n#############################################################################\n#  Filename: artifacts.mo\n#  Project: SuperNova.WS\n#  Website: http://www.supernova.ws\n#  Description: Massive Multiplayer Online Browser Space Strategy Game\n#\n#  Copyright © 2009-2018 Gorlum for Project \"SuperNova.WS\"\n#############################################################################\n*/\n\n/**\n*\n* @package language\n* @system [Russian]\n* @version 43a16.13\n*\n*/\n\n/**\n* DO NOT CHANGE\n*/\n\nif (!defined('INSIDE')) die();\n\n$a_lang_array = (array(\n  'art_use'             => 'Использовать артефакт',\n\n  'art_lhc_from'          => 'Большой Адронный Коллайдер',\n  'art_lhc_subj'          => 'Попытка создания луны',\n  'art_moon_create'   => array(\n    ART_LHC => 'Гравитационная волна, запущенная БАК, соединила огромные куски металла и кристалла на орбите, в результате чего по образовалась новая луна %s по координатам %s!',\n    ART_HOOK_SMALL => 'Малый Крюк запустил луну %1$s диаметром %3$s километров по координатам %2$s!',\n    ART_HOOK_MEDIUM => 'Средний Крюк запустил луну %1$s диаметром %3$s километров по координатам %2$s!',\n    ART_HOOK_LARGE => 'Большой Крюк запустил луну %1$s диаметром %3$s километров по координатам %2$s!',\n  ),\n  'art_moon_exists'   => 'На лунной орбите по текущим координатам уже находится луна',\n  'art_lhc_moon_fail'     => 'Гравитационой волны БАК оказалось недостаточно для образования новой луны',\n\n  'art_rcd_from'          => 'Автономный Колонизационный Комплекc',\n  'art_rcd_subj'          => 'Колония развернута',\n  'art_rcd_ok'            => '%1$s успешно развернул колонию на планете %2$s по координатам %3$s',\n  'art_rcd_err_moon'      => 'АКК может быть развернут только на планете',\n  'art_rcd_err_no_sense'  => 'АКК определил, что ни одно из зданий не будет усовершенствовано и прекратил развертывание',\n  'art_rcd_err_que'       => 'АКК не может быть развернут на планете, где ведется строительство. Отмените всё строительство на планете и попробуйте развернуть АКК еще раз',\n\n  'art_heurestic_chip_ok' => 'Время исследования технологии \"%s\" (уровень %d) уменьшено на %s',\n  'art_heurestic_chip_subj' => 'Ускорение времени исследования',\n  'art_heurestic_chip_no_research' => 'В настоящее время не ведется исследований или текущее время исследования менее 1 минуты',\n\n  'art_nano_builder_ok' => 'Время %s здания \"%s\" (уровень %d) на планете %s %s уменьшено на %s',\n  'art_nano_builder_build' => 'строительства',\n  'art_nano_builder_destroy' => 'сноса',\n  'art_nano_builder_subj' => 'Ускорение строительной операции',\n  'art_nano_builder_no_que' => 'В настоящее время на планете не производится строительных операций или текущее время операции менее 1 минуты',\n\n  'art_err_no_artifact'  => 'У вас нет нужного артефакта',\n\n  'art_page_hint'        => '<ul>\n    <li>Артефакты - редкие объекты с уникальными свойствами</li>\n    <li>Артефакты являются одноразовыми - после использования Артефакт исчезает</li>\n    <li>Некоторые Артефакты настолько мощные, что их количество в одной Империи не может быть больше опредленного числа</li>\n    <li>Обычно эффект от использования Артефакта распространяется на планету применения, но некоторые Артефакты имеют всеимперский эффект.\n    Самые редкие и дорогие Артефакты могут действовать на всю солнечную систему, галактику или даже на всю Вселенную!</li>\n  </ul>',\n));\n"
  },
  {
    "path": "language/ru/buddy.mo.php",
    "content": "<?php\n\n/*\n#############################################################################\n#  Filename: buddy.mo\n#  Project: SuperNova.WS\n#  Website: http://www.supernova.ws\n#  Description: Massive Multiplayer Online Browser Space Strategy Game\n#\n#  Copyright © 2009-2018 Gorlum for Project \"SuperNova.WS\"\n#############################################################################\n*/\n\n/**\n*\n* @package language\n* @system [Russian]\n* @version 43a16.13\n*\n*/\n\n/**\n* DO NOT CHANGE\n*/\n\nif (!defined('INSIDE')) die();\n\n\n$a_lang_array = (array(\n  'buddy_buddies' => 'Друзья',\n  'buddy_request_text' => 'Текст запроса',\n  'buddy_request_text_default' => 'Прошу добавить меня в список друзей',\n  'buddy_request_none' => 'Нет ни друзей, ни заявок на дружбу',\n  'buddy_request_write_header' => 'Отправить запрос на добавление в друзья',\n  'buddy_request_player_name' => 'Имя игрока',\n  'buddy_request_accept' => 'Добавить игрока в список друзей',\n\n  'buddy_status' => 'Статус',\n  'buddy_status_active' => 'Это ваш взаимный друг',\n  'buddy_status_incoming_waiting' => 'Вам пришел запрос на добавление вас в друзья',\n  'buddy_status_incoming_denied' => 'Вы отклонили предложение дружбы',\n  'buddy_status_outcoming_waiting' => 'Ваш запрос отправлен. Ждите ответа',\n  'buddy_status_outcoming_denied' => 'Ваш запрос отклонен',\n\n  // Result messages\n  'buddy_err_not_exist' => 'Указанная заявка не существует. Возможно, вы её удалили или отвергли, либо она была отозвана её автором',\n\n  'buddy_err_accept_own' => 'Вы не можете принять свою же заявку',\n  'buddy_err_accept_alien' => 'Вы не можете принять заявку, которая направлена не вам',\n  'buddy_err_accept_already' => 'Вы уже приняли эту заявку раньше и являетесь другом этого игрока',\n  'buddy_err_accept_denied' => 'Вы уже отклонили эту заявку и теперь не можете её принять',\n  'buddy_err_accept_internal' => 'Во время принятия заявки возникла ошибка. Попробуйте еще раз через некоторое время. Если ошибка не пропала - обратитесь к администрации сервера',\n  'buddy_err_accept_none' => 'Заявка успешно принята',\n\n  'buddy_err_delete_alien' => 'Эта заявка создана не вами и не для вас! Не стоит вмешиваться в отошения других людей! Поищите лучше себе друзей!',\n  'buddy_err_unfriend_none' => 'Вы разорвали дружеские отношения',\n  'buddy_err_delete_own' => 'Ваша заявка успешно удалена',\n\n  'buddy_err_deny_none' => 'Вы отказались дружить с другим игроком. Почему?',\n\n  'buddy_err_adding_exists' => 'Нельзя отправить запрос этому игроку - вы уже являетесь друзьями или существуют какие-то предложения дружбы между вами',\n  'buddy_err_adding_none' => 'Ваше предложение дружбы отправлено',\n  'buddy_err_adding_self' => 'Нельзя добавить себя в друзья',\n\n  // PM messages\n  'buddy_msg_accept_title' => 'У вас появился новый друг!',\n  'buddy_msg_accept_text' => 'Игрок %s добавил вас в свой список друзей!',\n  'buddy_msg_unfriend_title' => 'Вы потеряли друга!',\n  'buddy_msg_unfriend_text' => 'Игрок %s разорвал с вами дружеские отношения и вычеркнул вас из списка друзей. Как это грустно...',\n  'buddy_msg_deny_title' => 'Не удалось завести нового друга',\n  'buddy_msg_deny_text' => 'Игрок %s не захотел с вами дружить',\n  'buddy_msg_adding_title' => 'Предложение дружбы',\n  'buddy_msg_adding_text' => 'Игрок %s предлагает вам дружить',\n\n  'buddy_hint' => '\n    <li>Послать предложение дружбы можно через пункт меню <a href=\"search.php\">Поиск</a></li>\n    <li>Вы можете видеть статус ваших друзей - находятся ли они онлайн или оффлайн. Однако и друзья могут видеть ваш статус. Учитывайте данный факт перед принятием предложения о дружбе.</li>\n    <li>Если вы отклонили предложение дружбы, то вы не сможете начать дружеские отношения с этим игроком, пока он не удалит свой запрос</li>',\n\n));\n"
  },
  {
    "path": "language/ru/buildings.mo.php",
    "content": "<?php\n\n/*\n#############################################################################\n#  Filename: buildings.mo\n#  Project: SuperNova.WS\n#  Website: http://www.supernova.ws\n#  Description: Massive Multiplayer Online Browser Space Strategy Game\n#\n#  Copyright © 2009-2018 Gorlum for Project \"SuperNova.WS\"\n#############################################################################\n*/\n\n/**\n*\n* @package language\n* @system [Russian]\n* @version 45d0\n*\n*/\n\n/**\n* DO NOT CHANGE\n*/\n\nif (!defined('INSIDE')) die();\n\n$a_lang_array = array(\n  'built' => 'Построено',\n  'Fleet' => 'Флот',\n  'fleet' => 'Флот',\n  'Defense' => 'Оборона',\n  'defense' => 'Оборона',\n  'Research' => 'Исследования',\n  'level' => 'Уровень',\n  'dispo' => 'Доступно',\n  'load_det' => 'Нажмите на изображение для просмотра 3D модели',\n  'off_det' => 'Повторное нажатие отключает просмотр 3D модели',\n  'allowed_aya' => 'Доступная',\n  'allowed_ye' => 'Доступные',\n  'allowed_yi' => 'Доступный',\n  'mech_info' => 'Технические характеристики',\n  'fst_bld_load' => 'Обработка заказа.<br>Пожалуйста, подождите...',\n  'fst_bld' => 'Быстрый заказ :',\n  'price' => 'Стоимость',\n  'builds' => 'Постройки',\n  'destroy_price' => 'Стоимость Уничтожения',\n  'no_fields' => 'Нету полей на планете',\n  'can_build' => 'Можно построить: ',\n  'Requirements' => 'Необходимо: ',\n  'Requires' => 'Необходимые ресурсы ',\n  'Rest_ress' => 'Останется ресурсов ',\n  'Rest_ress_fleet' => 'С учетом прибывающих флотов',\n  'Rechercher' => 'Исследовать',\n  'ConstructionTime' => 'Время строительства ',\n  'DestructionTime' => 'Время уничтожения ',\n  'ResearchTime' => 'Время исследования ',\n  'Construire' => 'Построить',\n  'BuildFirstLevel' => 'Построить',\n  'BuildNextLevel' => 'Построить следующий уровень ',\n  'completed' => 'Завершено',\n  'in_working' => 'Занято',\n  'work_todo' => 'Занято',\n  'total_left_time' => 'Оставшееся время',\n  'only_one' => 'Вы можете построить только один щит.',\n  'b_no_silo_space' => 'Ракетная шахта заполнена.',\n  'que_full' => 'Очередь построек заполнена!',\n  'Build_lab' => 'Ошибка Строительства',\n  'NoMoreSpace' => 'Планета заполнена!',\n  'InBuildQueue' => 'В очередь построек',\n  'bld_usedcells' => 'Занятость полей',\n  'bld_theyare' => 'Осталось',\n  'bld_cellfree' => 'свободных полей',\n  'DelFromQueue' => 'отменить',\n  'DelFirstQueue' => 'Приостановить',\n  'cancel' => 'Отменить',\n  'continue' => 'Продолжить',\n  'ready' => 'Подождите',\n  'destroy' => 'Удалить',\n  'on' => 'на',\n  'attention' => 'Внимание! Произошла попытка взлома! Действите было зафиксировано!',\n  'no_laboratory' => 'Исследовательская лаборатория не построена!',\n  'need_hangar' => 'Верфь не построена!',\n  'labo_on_update' => 'Идёт обновление лаборатории!',\n  'fleet_on_update' => 'Идет модернизация верфи!',\n  'Total_techs' => 'Общее количество исследований',\n  'eco_bld_page_hint' => '<ul><li>Чтобы посмотреть информацию об отдельном юните достаточно навести на картинку курсор мышки</li>\n  <li>Клик на картинке выберет юнит. Повторный клик по тому же юниту отменит выбор</li>\n  <li>Подробное описание юнита и его характеристики можно узнать, кликнув на синюю иконку &quot;i&quot; с кругом</li>\n  <li>Построить здание можно либо кликнув на плюсик в правом верхнем углу картинки, либо нажав ссылку &quot;Построить&quot; в описании</li>\n  <li>Разрушить здание можно кликнув на минус в левом верхнем углу картинки, либо по соответствующей ссылке из описания</li></ul>',\n  'eco_price' => 'Цена',\n  'eco_left' => 'Остаток',\n  'eco_bld_resources_not_enough' => 'Не хватает ресурсов для постройки заказанных юнитов',\n\n  'eco_bld_msg_err_research_in_progress' => 'Ученные Империи уже ведут исследование',\n  'eco_bld_msg_err_not_research' => 'В лабораториях могут быть исследованы только технологии',\n  'eco_bld_msg_err_requirements_not_meet' => 'Требования для проведения исследований не удовлетворены',\n  'eco_bld_msg_err_laboratory_upgrading' => 'Исследовательские лаборатории находятся в процессе модификации и не могут производить исследования.<br/><br/>Во время постройки или модификации Лаборатории или Нанолаборатории на любой из планет Империи (даже если они просто стоят в очереди построек) исследование технологий не доступно<br/><br/>Что бы запустить Исследование, уберите все Лаборатории и Нанолаборатории из всех очередей построек на всех планетах',\n\n  'eco_bld_unit_info_extra_show' => 'Показать дополнительную информацию',\n  'eco_bld_unit_info_extra_hide' => 'Спрятать дополнительную информацию',\n  'eco_bld_unit_info_extra_none' => 'Нет дополнительной информации',\n\n  'eco_bld_autoconvert' => 'Автоконвертация',\n  'eco_bld_autoconvert_explain' => 'Недостающие на постройку/исследование ресурсы будут автоматически сконвертированы из наличных ресурсов (металл, кристалл, дейтерий), а затем постройка/исследование будет поставлена в очередь.\\r\\n\\r\\n',\n  'eco_bld_autoconvert_dark_matter_none' => 'Для постройки с автоконвертацией не хватает {0} Тёмной Материи.',\n  'eco_bld_autoconvert_confirm' => 'Эта операция будет стоить {0} Тёмной Материи.\\r\\n\\r\\nПродолжать?',\n\n  'eco_que_clear_dialog_title' => 'Подтвердите очистку очереди',\n  'eco_que_clear_dialog_text' => 'Продолжение данной операции приведет к очистке всей очереди!<br /><br />Все незавершенные постройки и исследования будут отменены, а время, потраченное на постройку текущего юнита - потеряно.<br />Ресурсы будут возвращены на планету.<br /><br />Вы уверены, что хотите продолжить?',\n\n  'eco_que_artifact_dialog_title' => 'Использовать {0}',\n  'eco_que_artifact_dialog_text' => \"Для ускорения постройки/исследования текущего юнита в очереди будет использован Артефакт \\\"{0}\\\".<br /><br />Если до окончания время постройки/исследования юнита осталось больше часа - время постройки уменьшится в два раза<br />Если меньше часа - постройка/исследование будет закончено моментально<br /><br />Артефакт нельзя использовать, если оставшееся время постройки/исследования - менее одной минуты\",\n\n  'eco_bld_research_page_name' => 'Исследование технологий',\n  'eco_bld_research_page_novapedia' => 'Список технологий в Новапедии',\n\n);\n"
  },
  {
    "path": "language/ru/chat_advanced.mo.php",
    "content": "<?php\n\n/*\n#############################################################################\n#  Filename: player_premium_ru.mo.php\n#  Project: SuperNova.WS\n#  Website: http://www.supernova.ws\n#  Description: Massive Multiplayer Online Browser Space Strategy Game\n#\n#  Copyright © 2012-2018 Gorlum for Project \"SuperNova.WS\"\n#############################################################################\n*/\n\n/**\n*\n* @package language\n* @system [Russian]\n* @version 45d0\n*\n*/\n\n/**\n* DO NOT CHANGE\n*/\n\nif (!defined('INSIDE')) die();\n\n//$lang = array_merge($lang,\n//$lang->merge(\n$a_lang_array = (array(\n  'chat_advanced_chat_players' => 'Игроки в чате',\n  'chat_advanced_online_players' => 'Игроков в чате',\n  'chat_advanced_online_invisibles' => 'В том числе невидимых',\n  'chat_advanced_invisibility' => 'Невидимость',\n\n  'chat_advanced_frame_on' => 'Прикрепить',\n  'chat_advanced_frame_off' => 'Открепить',\n\n  'chat_advanced_smile_tooltip' => 'Кликните, что бы выбрать смайлик',\n\n  'chat_advanced_visible' => array(\n    0 => 'Вы видимы другим игрокам',\n    1 => 'Вы невидимы другим игрокам',\n  ),\n\n  'chat_advanced_help_description' => \"Используйте синтаксис \\\"/help <команда>\\\", что бы получить дополнительную информацию об определенной команде. Например, \\\"/help whisper\\\"\",\n  'chat_advanced_help_commands_accessible' => 'Вам доступны следующие команды чата:',\n  'chat_advanced_help_command' => 'Команда \"/%s\"',\n  'chat_advanced_help_command_aliases' => 'Псевдонимы данной команды: ',\n\n  'chat_advanced_whisper_recipient_prefix' => '',\n  'chat_advanced_whisper_recipient_midfix' => ' -> ',\n  'chat_advanced_whisper_recipient_suffix' => '> ',\n  'chat_advanced_whisper_sender_prefix' => '',\n  'chat_advanced_whisper_sender_midfix' => ' -> ',\n  'chat_advanced_whisper_sender_suffix' => '> ',\n\n  'chat_advanced_command_reason' => '. Причина: %s',\n  'chat_advanced_command_reason2' => 'Причина:',\n  'chat_advanced_command_mute' => 'Игрок \"%1$s\" лишен права писать в чат до %2$s по серверному времени%3$s',\n  'chat_advanced_command_unmute' => 'Игрок \"%s\" снова может писать в чат',\n  'chat_advanced_command_ban' => 'Игрок \"%1$s\" заблокирован с режимом отпуска до %2$s по серверному времени',\n  'chat_advanced_command_ban_no_vacancy' => 'Игрок \"%1$s\" заблокирован БЕЗ РЕЖИМА ОТПУСКА до %2$s по серверному времени',\n  'chat_advanced_command_unban' => 'Игрок \"%s\" разблокирован',\n\n  'chat_advanced_command_interval' => array(\n    '1h' => '1 час',\n    '3h' => '3 часа',\n    '6h' => '6 часов',\n    '12h' => '12 часов',\n    '1d' => '1 сутки',\n    '3d' => '3 суток',\n    '1w' => '1 неделя',\n    '2w' => '2 недели',\n    '1m' => '30 суток',\n    '2m' => '60 суток',\n    '3m' => '90 суток',\n    '10y' => 'Навсегда*',\n  ),\n  'chat_advanced_ban_vacancy' => 'Режим отпуска',\n\n  'chat_advanced_online_ban' => 'Заблокировать игрока \"%1$s\" на...',\n  'chat_advanced_online_mute' => 'Запретить игроку \"%1$s\" писать в чат на...',\n  'chat_advanced_online_unmute' => 'Разрешить игроку \"%1$s\" писать в чат',\n  'chat_advanced_online_invisible' => 'Невидимый игрок',\n  'chat_advanced_online_banned_via_chat' => 'Заблокирован из чата',\n\n  'chat_advanced_help' => array(\n    'help' => \"Команда '/help' позволяет получить подробную помощь по всем доступным вам командам чата\\r\\n\n               Формат команды: /help [<имя команды>]\\r\\n\n               <Имя команды> является необязательным параметром. Без него будет распечатан список доступных команд. Вместо имени команды можно использовать её псевдоним, например '/help w' выместо '/help whisper'\",\n    'whisper' => \"Команда '/whisper' позволяет отправить приватное сообщение определенному игроку. Приватное сообщение появляется во всех видах чата - общем и Альянсовском - и видно\n                  только вам и игроку, которому оно предназначено. Клик мышкой на имени в списке игроков онлайн добавит в строку сообщения соответствующую команду - вам останется только\n                  набрать сообщение и отправить его\\r\\nПриватные сообщения можно посылать невидимым игрокам, игрокам, отсуствующим в чате и игрокам оффлайн. В двух последних случаях они увидят ваши сообщения в своей истории\\r\\n\n                  Формат команды: /whisper <имя игрока> <сообщение>\\r\\n\n                  Если имя игрока содержит пробел, апостроф, прямой или обратный слеш - возьмите имя игрока в двойные ковычки. Например, так:\\r\\n\n                  /w \\\"имя с пробелом\\\" Привет!\",\n    'ban' => \"Команда '/ban' блокирует игроку доступ в игру на указанное время. Соответствующая иконка в списке игроков онлайн блокирует игрока на 1 неделю\\r\\n\n              Формат команды: /ban id <ИД игрока> <срок блокировки>[!] [<причина блокировки>]\\r\\n\n              ИД игрока можно узнать, наведясь на его ник в списке игроков онлайн\\r\\n\n              <Срок блокировки> имеет вид: <число>{y|m|w|d|h}, где h указывает срок блокировки в часах, d - в днях, w - в неделях, m - в месяцах, y - в годах\\r\\n\n              Если после срока блокировки поставить восклицательный знак, то пользователь будет заблокирован без режима отпуска\\r\\n\n              <Причина блокировки> - не обязательный параметр. Если указана - будет добавлена в сообщение в чате и в таблицу блокировок\",\n    'unban' => \"Команда '/unban' позволяет разблокировать ранее заблокированного игрока\\r\\n\n                Формат команды: /unban id <ИД игрока>\",\n    'mute'  => \"Команда '/mute' запрещает игроку писать в чат. Запрет касается всех каналов и так же рапространяется на приватные сообщения чата. Соответствующая иконка в списке игроков онлайн запрещает игроку писать в чат на 1 час\\r\\n\n                Формат команды: /mute id <ИД игрока> <длительность запрета> [<причина запрета>]\\r\\n\n                ИД игрока можно узнать, наведясь на его ник в списке игроков онлайн\\r\\n\n                <Длительность запрета> имеет вид: <число>{y|m|w|d|h}, где h указывает длительность в часах, d - в днях, w - в неделях, m - в месяцах, y - в годах\\r\\n\n                <Причина запрета> - не обязательный параметр. Если указана - будет добавлена в сообщение в чате\",\n    'unmute' =>  \"Команда '/unmute' позволяет вернуть право писать в чат игроку, который раньше был лишен этого права\\r\\n\n                  Формат команды: /unmute id <ИД игрока>\\r\\n\n                  ИД игрока можно узнать, наведясь на его ник в списке игроков онлайн\",\n    'invisible' => \"Команда '/invisible' позволяет сделать вас невидимым для других игроков в чате. Переключение галочки \\\"Невидимость\\\" дает такой же эффект.\\r\\n\n                    Невидимость является глобальной - т.е. распространяется и на общий чат, и на чат Альянса\\r\\n\n                    Формат команды: /invisible [on|off]\\r\\n\n                    /invisible on - Делает вас невидимым для других игроков в чате\\r\\n\n                    /invisible off - Делает вас видимым для других игроков в чате\\r\\n\n                    /invisible - Показывает ваш статус невидимости\",\n  ),\n\n  'chat_advanced_help_short' => array(\n    'help' => '/help',\n    'whisper' => '/whisper',\n    'ban' => '/ban',\n    'unban' => '/unban',\n    'mute' => '/mute',\n    'unmute' => '/unmute',\n    'invisible' => '/invisible',\n  ),\n\n\n\n  'chat_advanced_err_command_inacessible' => 'Эта команда чата вам недоступна. Используйте \"/help\", что бы увидеть список всех доступных вам команд',\n  'chat_advanced_err_command_unknown' => 'Неизвестная команда',\n  'chat_advanced_err_player_name_unknown' => 'Не существует игрока с таким именем',\n  'chat_advanced_err_message_empty' => 'Пустое сообщение не будет отправлено',\n  'chat_advanced_err_message_player_empty' => 'Укажите имя игрока, которому вы хотите отправить сообщение в чате',\n  'chat_advanced_err_player_id_need' => 'Для этой команды нужно имя игрока',\n  'chat_advanced_err_player_id_incorrect' => 'Неправильный идентификатор',\n  'chat_advanced_err_player_id_unknown' => 'Не существует игрока с таким ИД',\n  'chat_advanced_err_player_same' => 'Нельзя выполнить эту команду на самом себе',\n  'chat_advanced_err_player_higher' => 'Нельзя выполнить эту команду на игроке с большим или равным уровнем доступа',\n  'chat_advanced_err_term_need' => 'Для этой команды нужен срок действия',\n  'chat_advanced_err_term_wrong' => 'Указан неправильный срок действия команды',\n));\n"
  },
  {
    "path": "language/ru/fleet.mo.php",
    "content": "<?php\n\n/*\n#############################################################################\n#  Filename: fleet.mo\n#  Project: SuperNova.WS\n#  Website: http://www.supernova.ws\n#  Description: Massive Multiplayer Online Browser Space Strategy Game\n#\n#  Copyright © 2009-2018 Gorlum for Project \"SuperNova.WS\"\n#############################################################################\n*/\n\n/**\n*\n* @package language\n* @system [Russian]\n* @version 44a35\n*\n*/\n\n/**\n* DO NOT CHANGE\n*/\n\nif (!defined('INSIDE')) die();\n\n$a_lang_array = (array(\n  'flt_page2_title' => 'Выбор задания',\n  'fl_title' => 'Флоты',\n  'fl_expttl' => 'Экспедиции',\n  'fl_mission' => 'Задание',\n  'fl_count' => 'Количество',\n  'fl_count_short' => 'К-во',\n  'fl_where' => 'Где',\n  'fl_from' => 'Откуда',\n  'fl_from_t' => 'Возвращение',\n  'fl_start_t' => 'Время',\n  'fl_dest' => 'Куда',\n  'fl_dest_t' => 'Прибытие',\n  'fl_back_t' => 'Возвращение',\n  'fl_back_in' => 'Осталось',\n  'fl_order' => 'Приказ',\n  'fl_get_to' => '(Т)',\n  'fl_get_to_ttl' => 'Туда',\n  'fl_back_to' => '(О)',\n  'fl_back_to_ttl' => 'Обратно',\n  'fl_associate' => 'Боевой союз',\n  'fl_noslotfree' => 'Некому командовать флотом!',\n  'fl_notback' => 'Флот не может вернуться!',\n  'fl_onlyyours' => 'Вы можете возвращать только свой флот!',\n  'fl_isback' => 'Флот возвращается',\n  'fl_sback' => 'Назад',\n  'fl_error' => 'Ошибка',\n  'fl_new_miss' => 'Новое задание: выбрать корабли',\n  'fl_fleet_typ' => 'Тип корабля',\n  'fl_fleet_disp' => 'Количество',\n  'fl_noplanetrow' => 'Системная ошибка получилась! ))',\n  'fl_fleetspeed' => 'Скорость: ',\n  'fl_selmax' => 'макс.',\n  'fl_sur' => 'из',\n  'fl_continue' => 'Далее',\n  'fl_noships' => 'На орбите планеты нет кораблей',\n  'fl_unselectall' => 'Обнулить',\n  'fl_selectall' => 'Все корабли',\n  'fl_orbiting' => 'На орбите',\n  'fl_to_fly' => 'Отправить',\n  'fl_no_flying_fleets' => 'Нет флотов в полете',\n  'fl_floten1_ttl' => 'Выбор точки назначения',\n  'fl_noenought' => 'Недостаточно кораблей!',\n  'fl_speed' => 'Скорость',\n  'fl_planet' => 'Планета',\n  'fl_ruins' => 'Поле обломков',\n  'fl_moon' => 'Луна',\n  'fl_dist' => 'Расстояние',\n  'fl_fltime' => 'Продолжительность (в одну сторону)',\n  'fl_time_go' => 'Прибытие в точку назначения',\n  'fl_time_back' => 'Возвращение в точку старта',\n  'fl_deute_need' => 'Потребление топлива',\n  'fl_speed_max' => 'Максимальная скорость',\n  'fl_shortcut' => 'Заметки с координатами',\n  'fl_shortlnk' => 'Редактировать закладки',\n  'fl_shrtcup1' => '(П)',\n  'fl_shrtcup2' => '(О)',\n  'fl_shrtcup3' => '(Л)',\n  'fl_planettype1' => 'Планета',\n  'fl_planettype2' => 'Поле обломков',\n  'fl_planettype3' => 'Луна',\n  'fl_myplanets' => 'Планеты',\n  'fl_nocolonies' => 'нет планет',\n  'fl_noacss' => 'Нет приглашений в боевые союзы',\n  'fl_grattack' => 'Боевые союзы',\n  'fl_allressources' => 'Все ресурсы',\n  'fl_space_left' => 'Место в трюме',\n  'fl_attack_error' => array(\n    ATTACK_ALLOWED => 'Флот успешно отправлен',\n    ATTACK_NO_TARGET => 'Указанный пункт назначения не существует',\n    ATTACK_OWN => 'Нельзя атаковать собственные планеты',\n    ATTACK_WRONG_MISSION => 'Это задание не может быть выполнено в указанной точке назначения',\n    ATTACK_NO_ALLY_DEPOSIT => 'На планете нет склада Альянса',\n    ATTACK_NO_DEBRIS => 'Поле обломков не существует',\n    ATTACK_VACATION => 'Нельзя атаковать игрока, находящегося в режие отпуска',\n    ATTACK_SAME_IP => 'ЗАЩИТА ОТ МУЛЬТИАККАУНТОВ!<br>Взаимодействие с игроком с таким же IP невозможно',\n    ATTACK_BUFFING => 'Прокачка - передача ресурсов от слабого игрока к сильному - запрещена правилами',\n    ATTACK_ADMIN => 'Вы не можете атаковать Администратора',\n    ATTACK_NOOB => 'Этот игрок слишком слабый для вас',\n    ATTACK_OWN_VACATION => 'Вы же в отпуске',\n    ATTACK_NO_SILO => 'Слишком низкий уровень ракетной шахты',\n    ATTACK_NO_MISSILE => 'Нельзя начать ракетную атаку без ракет',\n    ATTACK_NO_FLEET => 'Нельзя отправлять на задание пустой флот',\n    ATTACK_NO_SLOTS => 'Некому командовать флотом',\n    ATTACK_NO_SHIPS => 'Не хватает боевых единиц или заявленного количества ресурсов для перевозки',\n    ATTACK_NO_RECYCLERS => 'Нельзя отправить на переработку флот без переработчиков',\n    ATTACK_NO_SPIES => 'Нельзя отправить шпионить флот без шпионских зондов',\n    ATTACK_NO_COLONIZER => 'Нельзя колнизировать планету без колонизатора',\n    ATTACK_MISSILE_TOO_FAR => 'Ракеты нельзя запустить так далеко',\n    ATTACK_WRONG_STRUCTURE => 'Нельзя атаковать ракетами что-то кроме защитных сооружений',\n    ATTACK_NO_FUEL => 'Не хватает дейтерия для запуска флота',\n    ATTACK_NO_RESOURCES => 'Нет указанного количества ресурсов для транспортировки',\n    ATTACK_NO_ACS => 'Указанная группа САБ не существует',\n    ATTACK_ACS_MISSTARGET => 'Не совпадает точка назначения и цель группы САБ',\n    ATTACK_WRONG_SPEED => 'Неправильная скорость полета',\n    ATTACK_ACS_TOO_LATE => 'Флот слишком медленный - он не успеет догнать группу САБ',\n    ATTACK_BASHING => 'Защита от башинга. Уже произведено разрешенное количество атак в сутки на одну планету',\n    ATTACK_BASHING_WAR_DELAY => 'Защита от башинга. Война этому Альянсу уже объявлена, но еще не началась. Посмотрите на страницу информации вашего Альянса, что бы увидеть дату начала войны',\n    ATTACK_ACS_WRONG_TARGET => 'Точка назначения флота не совпадает с целью для САБа',\n    ATTACK_SAME => 'Исходная планета совпадает с пунктом назначения флота',\n    ATTACK_RESOURCE_FORBIDDEN => 'Нельзя отправить флот с ресурсами в эту миссию',\n    ATTACK_TRANSPORT_EMPTY => 'Нельзя отправить флот без ресурсов в транспортную миссию',\n    ATTACK_SPIES_LONLY => 'Нельзя отправить в эту миссию флот только из шпионских зондов',\n    ATTACK_TOO_FAR => 'Ваш флот не может лететь так далеко',\n    ATTACK_OVERLOADED => 'Ваши корабли перегружены. Уменьшите загрузку трюмов или добавьте транспортных кораблей',\n    ATTACK_MISSION_ABSENT => 'Не существует такого типа миссии',\n    ATTACK_WRONG_UNIT => 'Неправильный тип юнита',\n    ATTACK_ZERO_SPEED => 'Во флоте находится нелетающая орбитальная структура',\n    ATTACK_SHIP_COUNT_WRONG => 'Количество кораблей во флоте не может быть негативным',\n    ATTACK_RESOURCE_COUNT_WRONG => 'Количество ресурсов во флоте не может быть негативным',\n    ATTACK_MORATORIUM => 'На запуск флота в эту миссию наложен мораторий',\n    ATTACK_CHILD_PROTECTION => 'Сегодня - День Защиты Детей! Нельзя атаковать игрока с меньшим количеством очков!',\n    ATTACK_ACS_MAX_FLEETS => 'Достигнуто максимальное количество флотов в САБе',\n  ),\n\n  'fl_fleet_err' => 'Ошибка!',\n  'fl_unknow_target' => '<li>Указанный пункт назначения не существует!</li>',\n  'fl_nodebris' => '<li>Поле астероидов не существует!</li>',\n  'fl_nomoon' => '<li>Луна не существует!</li>',\n  'fl_vacation_ttl' => 'Режим отпуска',\n  'fl_vacation_pla' => 'Игрок находится в режие отпуска!',\n  'fl_noob_title' => 'Защита новичков',\n  'fl_noob_mess_n' => 'Игрок находится под защитой новичков!',\n  'fl_bad_planet01' => '<li>На планете есть жизнь!</li>',\n  'fl_colonized' => '<li>Планета уже колонизирована!</li>',\n  'fl_dont_stay_here' => 'Вы не можете приземляться на вражеской планете!',\n  'fl_no_allydeposit' => 'На планете нет склада Альянса!',\n  'fl_no_self_attack' => '<li>Нельзя атаковать собственные планеты!</li>',\n  'fl_no_self_spy' => '<li>Нельзя отправлять шпионов на собственные планеты!</li>',\n  'fl_only_stay_at_home' => '<li>Нельзя передислоцировать флот на чужую планету!</li>',\n  'fl_cheat_speed' => 'Попытка багоюзерства! Сообщение администрации отправлено!',\n  'fl_cheat_origine' => 'Попытка багоюзерства! Сообщение администрации отправлено!',\n  'fl_limit_planet' => '<li>Неправильная планета !</li>',\n  'fl_limit_system' => '<li>Неправильная система !</li>',\n  'fl_limit_galaxy' => '<li>Неправильная галактика !</li>',\n  'fl_ownpl_err' => '<li>Нельзя нападать на свою планету!</li>',\n  'fl_no_planet_type' => '<li>Неправильная точка назначение!</li>',\n  'fl_fleet_err_pl' => '<li>Ошибка планеты назначения!</li>',\n  'fl_bad_mission' => '<li>Задание не указано или указано невозможное задание!</li>',\n  'fl_no_fleetarray' => '<li>Что-то с флотом не так!</li>',\n  'fl_noenoughtgoods' => '<li>Попытка отправить пустой флот с заданием &quot;Транспорт&quot;!</li>',\n  'fl_expe_notech' => '<li>Перед отправкой экспедиции нужно исследовать Астрокартографию!</li>',\n  'fl_expe_max' => '<li>Вы не можете отправить ещё одну экспедицию. Развивайте Астрокартографию.</li>',\n  'fl_no_deuterium' => 'Не хватает дейтерия что бы загрузить трюмы и обеспечить полет. Нехватка: ',\n  'fl_no_resources' => 'Не хватает ресурсов, что бы загрузить трюмы флота.',\n  'fl_nostoragespa' => 'Не хватает места в трюме для перевозки ресурсов! Нехватка: ',\n  'fl_fleet_send' => 'Флот отправлен',\n  'fl_expe_warning' => 'Внимание, вы можете потерять корабли во время экспедиции!',\n  'fl_not_enough_fuel' => 'ОШИБКА! Ёмкость трюмов недостаточна даже что бы вместить топливо на полёт! Добавьте еще кораблей с ёмкими трюмами или попробуйте снизить скорость полёта, что бы уменьшить потребление топлива',\n  'fl_expe_staytime' => 'время удержания',\n  'fl_expe_hours' => 'часов',\n  'fl_adm_attak' => 'Вы не можете атаковать Администратора',\n  'fl_warning' => 'Предупреждение',\n  'fl_page0_hint' => '<ul><li>Для создания, редактирования и удаления закладкок используйте пункт &quot;Закладки&quot; в левом меню<li>Что бы присоединится к боевому союзу кликните на названии любого доступного вам союза</ul>',\n  'fl_page1_hint' => '<ul><li>Продолжительность полета включает время на взлет/посадку флота - обязательную компоненту любого рейса, как далеко или близко он бы не совершался<li>Для создания, редактирования и удаления закладкок используйте пункт &quot;Закладки&quot; в левом меню<li>Что бы присоединится к боевому союзу кликните на названии любого доступного вам союза</ul>',\n  'fl_page5_hint' => '<ul><li>Отметьте галочками в строчках колоний виды ресурсов, которые хотите свезти на текущую планету  <li>Галочка в заголовке окна позволяет поставить или снять отметки для указанного типа ресурсов сразу на всех колониях  <li>Галочки в столбце ВСЕГО позволяют отметить или снять отметки для всех ресурсов на указанной колонии  <li>Галочки в столбце ВСЕГО предназначены исключительно для облегчения работы с интерфейсом и <span class=\"negative\">НЕ ВЛИЯЮТ</span> на набор свозимых ресурсов  <li>В перевозке ресурсов участвуют только транспортные корабли: Малый транспорт, Большой транспорт, Супертранспорт и Гипертранспорт<li>Корабли загружаются в порядке убывания емкости трюма<li>В расчете строки ИТОГО учитывается ёмкость трюмов флота</ul>',\n  'fl_err_no_ships' => 'Во флоте нет ни одного корабля. Вернитесь на предыдущую страницу и выберите корабли для отправки флота',\n  'fl_shrtcup' => array(\n    PT_PLANET => '(П)',\n    PT_DEBRIS => '(О)',\n    PT_MOON => '(Л)',\n  ),\n\n  'fl_planettype' => array(\n    PT_PLANET => 'Планета',\n    PT_DEBRIS => 'Поле обломков',\n    PT_MOON => 'Луна',\n  ),\n\n  'fl_aks_invite_message_header' => 'Приглашение к САБ',\n  'fl_aks_invite_message' => '<font color=\"red\">Игрок %s пригласил Вас присоединяться к САБ. Вы можете присоединиться к САБ на странице &quot;Флот&quot;.</font>',\n  'fl_aks_player_invited' => '<font color=\"lime\">Игрок %s был приглашен для совместного нападения.</font>',\n  'fl_aks_player_invited_already' => '<font color=\"lime\">Игрок %s уже приглашен. Повторное приглашение отослано.</font>',\n  'fl_aks_player_error' => '<font color=\"red\">Ошибка. Игрок %s не найден.</font>',\n  'fl_aks_already_in_aks' => 'Флот уже в боевой группе!',\n  'fl_aks_adding_error' => 'Ошибка добавления участника к флоту:<br>%s',\n  'fl_aks_hack_wrong_fleet' => 'Попытка взлома! Манипулирование чужим флотом! Сообщение отправлено Администратору!',\n  'fl_aks_too_slow' => 'Флот слишком медленный и не может присоединиться к боевому союзу',\n  'fl_aks_too_power' => 'Добавление этого игрока сделает САБ слишком сильным для атаки на цель',\n  'fl_aks_full' => 'Достигнуто максимальное количество флотов в одном САБе',\n  'fl_fleet_not_exists' => 'Указанный флот не найден',\n  'fl_multi_ip_protection' => 'ЗАЩИТА ОТ МУЛЬИАККАУНТОВ!<br>Невозможно отправить ресурсы игроку с таким же IP!',\n  'fl_load_cargo' => 'В трюме',\n  'fl_rest_on_planet' => 'Остаток',\n  'fl_none_resources' => 'Сбросить',\n  'fl_planet_resources' => 'Ресурсы на планете',\n  'fl_fleet_data' => 'Текущие параметры флота',\n  'flt_gather_report' => 'Отчет о выполнении',\n  'flt_report' => 'Отчет',\n  'flt_no_transports' => 'нет транспорта',\n  'flt_no_fuel' => 'нет топлива',\n  'fl_id' => '№',\n  'fl_ressources' => 'Сырьё',\n\n  'fl_fuel_on_planet' => 'Топливо на складе',\n\n  'flt_aks_players_in_aks' => 'Игроки в САБ',\n  'flt_aks_player_invite' => 'Пригласить игрока в САБ',\n  'flt_aks_player_invite_do' => 'Пригласить',\n  'flt_aks_player_same' => 'Нельзя присоединить к САБу пользователя, на которого совершается нападение!',\n  'flt_aks_error_too_much_players' => 'Нельзя пригласить в САБ больше 5 игроков',\n\n  'flt_return_all' => 'Вернуть отмеченные',\n  'flt_return_fleet' => 'Вернуть флот',\n\n  'flt_acs_prefix' => 'САБ ',\n\n  'ship_consumption_short' => 'Расход',\n  'ship_capacity_short' => 'Трюм',\n\n  'ship_is_satellite' => 'Спутник',\n\n));\n"
  },
  {
    "path": "language/ru/index.html",
    "content": ""
  },
  {
    "path": "language/ru/infos.mo.php",
    "content": "<?php\n\n/*\n#############################################################################\n#  Filename: infos.mo\n#  Project: SuperNova.WS\n#  Website: http://www.supernova.ws\n#  Description: Massive Multiplayer Online Browser Space Strategy Game\n#\n#  Copyright © 2009-2018 Gorlum for Project \"SuperNova.WS\"\n#############################################################################\n*/\n\n/**\n*\n* @package language\n* @system [Russian]\n* @version 45d0\n*\n*/\n\n/**\n* DO NOT CHANGE\n*/\n\nif (!defined('INSIDE')) die();\n\n$a_lang_array = array(\n  'wiki_title' => 'Новапедия',\n\n  'wiki_char_nominal' => 'Паспортные',\n  'wiki_char_actual' => 'Актуальные',\n\n  'wiki_ship_engine_header' => 'Характеристики двигателей',\n\n  'wiki_ship_header' => 'Транспортные характеристики',\n  'wiki_ship_speed' => 'Скорость',\n  'wiki_ship_consumption' => 'Потребление дейтерия',\n  'wiki_ship_capacity' => 'Емкость трюма',\n  'wiki_ship_hint' => '<li>Актуальные скорость и потребление приводятся с учетом всех бонусов - технологий, наемников и т.д.</li>',\n\n  'wiki_combat_header' => 'Боевые характеристики',\n  'wiki_combat_attack' => 'Мощность залпа, хитов',\n  'wiki_combat_shield' => 'Емкость щитовых батарей, хитов',\n  'wiki_combat_armor' => 'Структурная целостность, хитов',\n\n  'wiki_combat_volley_header' => 'Залповый огонь',\n  'wiki_combat_volley_to' => 'Поражает единиц',\n  'wiki_combat_volley_from' => 'Теряет единиц',\n  'info' => array(\n    STRUC_MINE_METAL => array(\n      'description' => 'Основной поставщик сырья для строительства несущих структур построек и кораблей. Металл - самое дешёвое сырьё, но зато его требуется больше, чем всего остального. Для производства металла требуется меньше всего энергии. Чем рудники больше, тем они глубже. На большинстве планет металл находится на больших глубинах, в этих более глубоких рудниках можно добывать больше металлов, производство растёт. В тоже время более крупные рудники требуют больше энергии.',\n      'description_short' => 'Основной поставщик сырья для строительства несущих структур построек и кораблей.',\n    ),\n\n    STRUC_MINE_CRYSTAL => array(\n      'description' => 'Для синтеза кристаллов требуется примерно вдвое больше энергии, чем для добычи равного количества металла, поэтому он, соответственно, ценится больше. Кристаллы - основная составляющяя любой современных компьютеров и ключевой компонент варп-двигателей. Поэтому он требуется для всех кораблей и почти для всех зданий. Усовершенствование синтезатора увеличивает количество производимых кристаллов.',\n      'description_short' => 'Основной поставщик сырья для компьютерных систем и варп-приводов.',\n    ),\n\n    STRUC_MINE_DEUTERIUM => array(\n      'description' => 'Дейтерий - это тяжёлый водород. Из-за этого, как и на рудниках, более крупные запасы находятся на дне моря. Усовершенствование синтезатора также способствует освоению этих глубинных залежей дейтерия. Дейтерий необходим в качестве топлива для кораблей, почти для всех исследований, просмотра галактик, а также для использования сенсорной фаланги.',\n      'description_short' => 'Извлекает из воды на планете незначительную долю дейтерия.',\n    ),\n\n    STRUC_MINE_SOLAR => array(\n      'description' => 'Для обеспечения энергией рудников и синтезаторов необходимы огромные солнечные электростанции. Чем больше построено станций, тем больше поверхности покрыто солнечными батареями, которые перерабатывают световую энергию в электричество. Солнечные электростанции представляют собой основу энергообеспечения планеты.',\n      'description_short' => 'Производит энергию из солнечных лучей. Энергия требуется для работы большинства построек.',\n    ),\n\n    STRUC_MINE_FUSION => array(\n      'description_short' => 'Добывает энергию из процесса образования атома гелия двумя атомами тяжёлого водорода',\n      'description' => 'На термоядерных электростанциях при помощи термоядерного синтеза под огромным давлением и при высокой температуре 2 атома тяжёлого водорода объединяются в один атом гелия. При этом, при образовании ядра гелия, вырабатывается энергия в 41,32*10^-13 Дж в виде излучения (т.о. при сгорании 1 г водорода вырабатывается 172 МВт*ч энергии). Чем больше термоядерный реактор, тем сложнее процессы синтезирования, реактор производит больше энергии.<br><br>Формула добычи:<br>30 * [уровень ТЭ] * (1,05 + [уровень энергетической технологии] * 0,01) ^ [уровень ТЭ]<br>Добыча энергии также может быть повышена за счёт развития энергетической технологии.',\n    ),\n\n    STRUC_FACTORY_ROBOT => array(\n      'description' => 'Предоставляет простую рабочую силу, которую можно применять при строительстве планетарной инфраструктуры. Каждый уровень развития фабрики повышает скорость строительства зданий.',\n      'description_short' => 'Изготавливает машины и механизмы, которые применяются при строительстве планетарной инфраструктуры. Каждый уровень развития фабрики повышает скорость строительства зданий.',\n    ),\n\n    STRUC_FACTORY_NANO => array(\n      'description' => 'Нанофабрики являются финалом эволюции роботизированных заводов. Единственное их оборудование - наносборщики, позволяющие манипулировать отдельными молекулами и даже атомами вещества. С их изобретением стало возможным производства практически любых материалов с заранее заданными свойствами. Более того - благодаря наносборщикам можно сразу производить готовые детали любых форм и конфигураций. Однако изобретение наносборщиков не упразднило обычные заводы. Хотя нанофабрика может произвести любую конструкцию, многие вещи по-прежнему энергетически выгоднее производить &quot;по старинке&quot;. Но даже с такими ограничениями каждый уровень нанофабрики сокращает время строительства любых зданий, оборонных сооружений и кораблей вдвое.',\n      'description_short' => 'Единственным оборудованиеем этой фабрики являются наносборщики - специализированные комплексы, позволяющие конструировать объекты из отдельных молекул и атомов.',\n      'effect' => 'Каждый уровень нанофабрики повышает скорость любого строительства в два раза.',\n    ),\n\n    STRUC_FACTORY_HANGAR => array(\n      'description' => 'В строительной верфи производятся все виды кораблей и оборонительных сооружений. Чем она больше, тем быстрее можно строить более сложные и более крупные корабли и оборонительные сооружения. Посредством строительства фабрики нанитов упрощаются множество технологических цепочек, что позволяет кардинально улучшить производительность верфи.',\n      'description_short' => 'Верфи производят космические корабли, орбитальные структуры и оборонительные сооружения.',\n    ),\n\n    STRUC_STORE_METAL => array(\n      'description' => 'Склад для добытой руды. Чем он больше, тем больше руды можно в нём хранить. Если склад заполнен, то добыча металла прекращается. Склад используется ИСКЛЮЧИТЕЛЬНО при добыче - общее количество металла на планете (например - при транспортировке ресурса с другой планеты) может привышать максимальную ёмкость склада',\n      'description_short' => 'Склад для добытой руды перед дальнейшей переработкой',\n    ),\n\n    STRUC_STORE_CRYSTAL => array(\n      'description' => 'На этом складе хранятся полуфабрикаты для синтеза кристаллов. Чем он больше, тем больше материала можно хранить. Если склад заполнен, то синтез кристалла прекращается. Склад используется ИСКЛЮЧИТЕЛЬНО при добыче - общее количество кристалла на планете (например - при транспортировке ресурса с другой планеты) может привышать максимальную ёмкость склада',\n      'description_short' => 'Склад для хранения полуфабрикатов кристаллов перед их дальнейшей переработкой',\n    ),\n\n    STRUC_STORE_DEUTERIUM => array(\n      'description' => 'Специальные цистерны для хранения тяжёлой воды. Они обычно находятся вблизи космических портов. Чем они больше, тем больше тяжёлой воды в них может сберегаться. Если они заполнены, то добыча дейтерия прекращается. Ёмкости используется ИСКЛЮЧИТЕЛЬНО при добыче - общее количество дейтерия на планете (например - при транспортировке ресурса с другой планеты) может привышать их максимальную вместимость',\n      'description_short' => 'Цистерны для хранения тяжёлой воды перед выделением из неё дейтерия',\n    ),\n\n    STRUC_LABORATORY => array(\n      'description' => 'Для исследования новых технологий необходима работа исследовательской станции. Уровень развития исследовательской станции является решающим фактором того, как быстро могут быть освоены новые технологии. Чем выше уровень развития исследовательской лаборатории, тем больше может быть исследовано новых технологий. Для того, чтобы как можно быстрее завершить исследовательские работы на одной планете, на неё посылаются все имеющиеся в наличии учёные и, таким образом, покидают свои планеты. Как только технология исследована, учёные возвращаются на свои родные планеты и переносят с собой знания о ней. Так новые технологии можно применять и на других планетах.',\n      'description_short' => 'В лаборатории производятся исследования новых технологий.',\n    ),\n\n    STRUC_TERRAFORMER => array(\n      'description' => 'По мере застройки планет всё более важным становился вопрос об ограниченности пригодных для использования площадей. Такие традиционные методы, как строительство ввысь и вглубь, оказались недостаточными. Маленькая группа физиков и нанотехников нашла решение - терраформер.<br><br>Затрачивая огромное количество энергии терраформер может преобразовывать огромные территории и даже целые континенты, делая их пригодными для застройки. В этом строении беспрерывно производятся специальные наниты, отвечающие за постоянное качество почвы.',\n      'description_short' => 'Терраформер может преобразовывать огромные территории, увеличивая количество доступных для застройки секторов.',\n    ),\n\n    STRUC_ALLY_DEPOSIT => array(\n      'description' => 'Склад альянса предоставляет возможность обеспечения топливом дружественных флотов, которые помогают при обороне и находятся на орбите. Чем выше уровень развития, тем больше дейтерия можно отправлять флотам на орбиту.',\n      'description_short' => 'Склад альянса предоставляет возможность обеспечения топливом дружественных флотов, которые помогают при обороне и находятся на орбите.',\n    ),\n\n    STRUC_LABORATORY_NANO => array(\n      'description' => 'Сокращает время на исследовательской стадии вдвое.',\n      'description_short' => 'Нанолаборатории оснащены по последнему слову техники. Сверхмощные кристаллические компьютеры и сверхточные наносборщики позволяют ускорить любое исследование в два раза.',\n    ),\n\n    STRUC_MOON_STATION => array(\n      'description' => 'Луна не располагает атмосферой, поэтому перед заселением требуется соорудить лунную базу. Она обеспечивает необходимые воздух, гравитацию и тепло. Чем выше уровень развития лунной базы, тем больше обеспеченная биосферой площадь. Каждый уровень лунной базы может застроить 3 сектора, максимум до площади всей луны. Она составляет 2 (диаметр луны/1000)^2, причём каждый уровень лунной базы сам занимает одно поле.',\n      'description_short' => 'Луна не располагает атмосферой, поэтому перед заселением требуется соорудить лунную базу.',\n    ),\n\n    STRUC_MOON_PHALANX => array(\n      'description' => 'Высокочастотные сенсоры полностью просматривают спектр частот всех попадающих на фалангу излучений. Мощные компьютеры комбинируют мельчайшие колебания энергии и таким образом получают информацию о передвижениях кораблей на отдалённых планетах. Для просмотра на луне должна быть предоставлена энергия в форме дейтерия (1000 * уровень фаланги за просмотр). Просмотр осуществляется переходом с луны в меню Галактика и на название вражеской планеты, находящейся в радиусе действия сенсоров (формула: (уровень фаланги)^2-1 систем).',\n      'description_short' => 'Высокочастотные сенсоры полностью просматривают спектр частот всех попадающих на фалангу излучений.',\n    ),\n\n    STRUC_MOON_GATE => array(\n      'description' => 'Ворота - это огромные телепортеры, которые могут пересылать между собой флоты любых размеров без временных затрат. Эти телепортеры не требуют дейтерия, однако между двумя прыжками должен пройти один час, иначе ворота перегреются. Невозможна также пересылка ресурсов. Весь процесс требует крайне высоко развитой технологии.',\n      'description_short' => 'Ворота - это огромные телепортеры, которые могут пересылать между собой флоты любых размеров без временных затрат.',\n    ),\n\n    STRUC_SILO => array(\n      'description' => 'Ракетные шахты служат для хранения ракет. С каждым уровнем можно хранить на четыре межпланетных или двенадцать ракет-перехватчиков больше. Одна межпланетная ракета требует места в три раза больше, чем ракета-перехватчик. Возможно любое комбинирование различных типов ракет.',\n      'description_short' => 'Ракетная шахта позволяет производить запуски ракет и одновременно является ракетным хранилищем.',\n    ),\n\n    TECH_SPY => array(\n      'description_short' => 'С помощью этой технологии добываются данные о других планетах.',\n      'description' => 'Шпионаж предназначен для исследования новых и более эффективных сенсоров. Чем выше развита эта технология, тем больше информации имеет игрок о событиях в своём окружении. Разница в уровнях шпионажа с противником играет решающую роль - чем больше исследована собственная шпионская технология, тем больше информации содержится в разведданных и тем меньше шанс быть обнаруженным. Чем больше послано зондов, тем больше собирается подробностей о противнике, но при этом растёт опасность быть обнаруженным. Также шпионаж совершенствует определение местонахождения чужих флотов. При этом также важен уровень развития собственного шпионажа. Начиная со второго уровня его развития при атаке на Вас кроме сообщения о нападении показывается также и общая численность нападающих кораблей. С четвёртого уровня распознаётся вид нападающих кораблей, равно как и их общая численность, а с восьмого - точная численность каждого типа кораблей. Для налётчиков эта технология очень важна, так как она предоставляет информацию о том, выставила ли жертва флот и/или защиту или нет, поэтому следует исследовать её как можно раньше. Лучше всего - сразу же после исследования малых транспортов.',\n    ),\n\n    TECH_COMPUTER => array(\n      'description' => 'Компьютерная технология предназначена для расширения имеющихся в наличии компьютерных мощностей. В результате на планете развиваются более продуктивные и эффективные компьютерные системы, возрастает вычислительная мощность и скорость протекания вычислительных процессов. С повышением мощности компьютеров можно одновременно командовать всё бoльшим количеством флотов. Каждый уровень развития компьютерной технологии даёт возможность командовать +1 флотом. Чем больше рассылается флотов, тем больше можно совершать налётов и тем самым захватывать больше сырья. Естественно, что эта технология полезна и торговцам, так как она позволяет им одновременно рассылать больше торговых флотов. По этой причине следует постоянно развивать компьютерную технологию на протяжении всей игры.',\n      'description_short' => 'С увеличением мощности компьютеров можно командовать всё большим количеством флотов. Каждый уровень компьютерной технологии увеличивает максимальное количество флотов на один.',\n    ),\n\n    TECH_WEAPON => array(\n      'description' => 'Оружейная технология занимается прежде всего дальнейшим развитием имеющихся в наличии систем вооружения. При этом особое значение придаётся тому, чтобы снабжать имеющиеся в наличии системы большей энергией и более точно эту энергию направлять. Благодаря этому системы вооружения становятся эффективней, а оружие вызывает больше разрушений. Каждый уровень оружейной технологии увеличивает мощность вооружения войсковых частей на 10%. Оружейная технология важна для конкурентоспособного содержания частей. Поэтому следовало бы её постоянно развивать в течение всей игры.',\n      'description_short' => 'Оружейная технология делает системы вооружения эффективней. Каждый уровень увеличивает мощность вооружения войсковых частей на 10% от базовой.',\n    ),\n\n    TECH_SHIELD => array(\n      'description' => 'Развитие этой технологии позволяет увеличивать снабжение энергией щитов и защитных экранов, что в свою очередь повышает их устойчивость и способность поглощать или отражать энергию атак противника. Благодаря этому с каждым изученным уровнем эффективность корабельных щитов и стационарных генераторов энергополей повышается на 10% от номинальной мощности.',\n      'description_short' => 'Эта технология занимается изучением более новых возможностей большего энергоснабжения щитов, что делает их эффективней и устойчивей. Благодаря этому с каждым изученным уровнем эффективность щитов повышается на 10%.',\n    ),\n\n    TECH_ARMOR => array(\n      'description' => 'Специальные сплавы улучшают броню космических кораблей. Как только найден очень стойкий сплав, специальные лучи изменяют молекулярную структуру космического корабля, и доводит её до состояния изученного сплава. Так, устойчивость брони может увеличиваться с каждым уровнем на 10%.',\n      'description_short' => 'Специальные сплавы улучшают броню космических кораблей. С каждым уровнем прочность брони увеличиваться на 10% от базовой.',\n    ),\n\n    TECH_ENERGY => array(\n      'description' => 'Энергетическая технология занимается дальнейшим развитием систем передачи и хранения энергии, которые необходимы для многих новых технологий.',\n      'description_short' => 'Исследование энергетических технологий позволяет улучшить отдачу термоядерных электростанций.',\n    ),\n\n    TECH_HYPERSPACE => array(\n      'description' => 'Путём сплетения 4-го и 5-го измерения стало возможным исследовать новый более экономный и эффективный двигатель. ',\n      'description_short' => 'Путём сплетения 4-го и 5-го измерения стало возможным исследовать новый более экономный и эффективный двигатель.',\n    ),\n\n    TECH_ENGINE_CHEMICAL => array(\n      'description' => 'Химический ракетный двигатель - самый простой вид двигателя. В них используются экзотермические химические реакции горючего и окислителя (вместе именуемые топливом), в результате которых продукты сгорания нагреваются в камере сгорания до высоких температур, расширяясь, разгоняются в сверхзвуковом сопле и истекают из двигателя. Топливо химического ракетного двигателя является источником как тепловой энергии, так и газообразного рабочего тела, при расширении которого его внутренняя энергия преобразуется в кинетическую энергию реактивной струи. Эффективность этих двигателей относительно мала, однако они они весьма надёжны, дёшевы в производстве и неприхотливы в обслуживании. Кроме того они занимают гораздо меньше места на корабле по сравнению с остальными двигателями, поэтому их можно встретить на маленьких кораблях. Так как химические двигатели являются основой любого полёта в космос, следует исследовать их как можно раньше. Каждый уровень развития этих двигателей делает на 10% быстрее следующие корабли: Малый Транспорт (до тех пор пока не будет исследован Ионный Двигатель 5-го уровня), Большой Транспорт, Переработчик, Шпионский Зонд и Легкий Истребитель',\n      'description_short' => 'Дальнейшее развитие этих двигателей делает некоторые корабли быстрее. Каждый уровень повышает скорость корабля на 10% от базовой.',\n    ),\n\n    TECH_ENGINE_ION => array(\n      'description' => 'Принцип работы ионного двигателя заключается в ионизации газа и его разгоне электростатическим полем. При этом, благодаря высокому отношению заряда к массе, становится возможным разогнать ионы до очень высоких скоростей (вплоть до 210 км/с по сравнению с 3—4,5 км/с у химических ракетных двигателей). Таким образом, в ионном двигателе можно достичь очень большого удельного импульса. Это позволяет значительно уменьшить расход реактивной массы ионизированного газа по сравнению с расходом реактивной массы в химических ракетах, но требует больших затрат энергии. Поэтому все корабли на ионных двигателях несут на борту дополнительный термоядерный реактор, работающий исключительно на двигательную установку и отличаются повышенным расходом дейтерия. Наиболее рационально устанавливать двигатели данного типа либо на легкие корабли, которые должны разгонятся быстро, либо на тяжелые корабли, ускорение которых не имеет значение. Именно поэтому номенклатура кораблей с данной установкой включает Малый Транспорт (после разработки 5-го уровня двигателей), Супертранспорт, Колонизатор, Тяжелый Истребитель, Эсминец и Бомбардировщик (до разработки технологии Гипердвигателя 8-го уровня). Так же эти двигатели устанавливаются на межпланетные ракеты - в их случае реактор настраивается так, что остатки топлива являются вспомогательным ВВ',\n      'description_short' => 'Ионный двигатель работает на принципе ионизации газа и его разгоне электростатическим полем. Каждый уровень развития этих двигателей делает оснащенные им корабли быстрее на 20% базовой скорости.',\n    ),\n\n    TECH_ENGINE_HYPER => array(\n      'description' => 'Благодаря пространственно-временному изгибу в непосредственном окружении корабля пространство сжимается, чем быстрее преодолеваются далёкие расстояния. Чем выше развит гиперпространственный привод, тем выше сжатие пространства, благодаря чему делает следующие корабли с каждым уровнем на 30% быстрее: Гипертранспорт, Крейсер, Бомбардировщик (после разработки 8-го уровня двигателей), Линейный Крейсер, Уничтожитель, Звезда Смерти и крейсер класса &quot;СуперНова&quot;',\n      'description_short' => 'Двигатель сжимает пространство-время вокруг корабля, разгоняя его до сверхсветовых скоростей. С каждым уровнем скорость кораблей повышается на 30%.',\n    ),\n\n    TECH_LASER => array(\n      'description' => 'Лазеры (усиление света при помощи индуцированного выброса излучения) производят насыщенный энергетический луч когерентного света. Эти приборы находят применение во всевозможных областях, от оптических компьютеров до тяжёлых лазеров, которые свободно режут броню космических кораблей. Лазерная технология является важным элементом для исследования дальнейших оружейных технологий.',\n      'description_short' => 'Благодаря фокусированию света возникает луч, который при попадании на объект наносит ему повреждения.',\n    ),\n\n    TECH_ION => array(\n      'description' => 'Поистине смертоносный наводимый луч из ускоренных ионов. При попадании на какой-либо объект они наносят огромный ущерб. ',\n      'description_short' => 'Поистине смертоносный наводимый луч из ускоренных ионов. При попадании на какой-либо объект они наносят огромный ущерб.',\n    ),\n\n    TECH_PLASMA => array(\n      'description' => 'Дальнейшее развитие ионной технологии, которая ускоряет не ионы, а высокоэнергетическую плазму. Она оказывает опустошительное действие при попадании на какой-либо объект. ',\n      'description_short' => 'Дальнейшее развитие ионной технологии, которая ускоряет не ионы, а высокоэнергетическую плазму. Она оказывает опустошительное действие при попадании на какой-либо объект.',\n    ),\n\n    TECH_RESEARCH => array(\n      'description' => 'Эта сеть делает возможным общение учёных, работающих в исследовательских лабораториях разных планет. Каждый новый уровень позволяет присоединить к сети дополнительную лабораторию (в первую очередь присоединяются лаборатории старших уровней). Из всех объединённых в сеть лабораторий, в каждом исследовании принимают участие только те, которые имеют достаточный для проведения данного исследования уровень. Скорость исследования соответствует сумме уровней участвующих в нём лабораторий.',\n      'description_short' => 'Эта сеть делает возможным общение учёных, работающих в исследовательских лабораториях разных планет. Каждый новый уровень позволяет присоединить к сети дополнительную лабораторию.',\n    ),\n\n    TECH_EXPEDITION => array(\n      'description' => 'Экспедиционная технология охватывает различные технологии сканирования и даёт возможность оснащать корабли различных классов исследовательским модулем. Он содержит базу данных, маленькую передвижную лабораторию, а также различные биоклетки и сосуды для проб. Для безопасности корабля при исследовании опасных объектов исследовательский модуль оснащён автономным энергообеспечением и генератором энергетического поля, который в экстремальных ситуациях может окружать исследовательский модуль мощным энергетическим полем.',\n      'description_short' => 'Теперь корабли можно оснащать исследовательским модулем, обеспечивающим обработку собранных данных в условиях длительных полётов.',\n    ),\n\n    TECH_COLONIZATION => array(\n      'description' => 'Властитель, имеющий много колоний во вселеной, имеет больше преимущества перед другими.',\n      'description_short' => 'Эта технология очень важна, чтобы ты мог основать империю с множеством колоний.',\n    ),\n\n    TECH_ASTROTECH => array(\n      'description' => 'Астрокартография позволяет увеличить максимальное количество колоний и экспедиций, а так же максимальную длительность экспедиции',\n      'description_short' => 'Астрокартография позволяет увеличить максимальное количество колоний и экспедиций, а так же максимальную длительность экспедиции',\n    ),\n\n    TECH_GRAVITON => array(\n      'description' => 'Гравитон - это частица, которая не обладает ни массой ни зарядом и определяет силу притяжения. Путём запуска концентрированного заряда гравитонов можно создавать искусственное гравитационное поле, которое, подобно чёрной дыре, втягивает в себя массу, благодаря чему можно уничтожать корабли или даже луны. Чтобы произвести достаточное количество гравитонов, требуются огромные количества энергии.',\n      'description_short' => 'Путём запуска концентрированного заряда гравитонов можно создавать искусственное гравитационное поле, которое, подобно чёрной дыре, втягивает в себя массу, благодаря чему можно уничтожать корабли или даже луны.',\n    ),\n\n    SHIP_CARGO_SMALL => array(\n      'description' => 'Транспорты имеют примерно такой же размер, что и истребители, но они не обладают мощными двигателями и бортовым вооружением ради экономии места. Малый транспорт вмещает 5000 единиц сырья. По причине малой огневой мощи малые транспорты часто сопровождаются другими кораблями. Когда ионный двигатель исследован до 5-й ступени, у малого транспорта повышается базовая скорость и он оснащается этим типом двигателя.',\n      'description_short' => 'Малый транспорт - это манёвренный корабль, который может быстро транспортировать сырьё на другие планеты. После исследования ионного двигателя 5го уровня, корабли переоснащаются.',\n    ),\n\n    SHIP_CARGO_BIG => array(\n      'description' => 'На борту этого корабля есть лишь слабое вооружение и нет серьёзных технологий... По этой причине их никогда не следует запускать без сопровождения. Благодаря своему высокоразвитому химическому двигателю большой транспорт служит в качестве быстрого межпланетного доставщика ресурсов, также он сопровождает флоты при нападениях на вражеские планеты, чтобы захватить как можно больше ресурсов.',\n      'description_short' => 'Большой транспорт обладает большей вместительностью, чем малый транспорт. Скорость БТ так же выше, но лишь до тех пор, пока на малых транспортах не устанавливаются ионные двигатели 5-го уровня',\n    ),\n\n    SHIP_CARGO_SUPER => array(\n      'description' => 'Последнее слово в технологиях транспортировки. Супертранспорт - гигантский транспортный корабль, оснащенный ионными двигателями. Его скорость невелика, а расход топлива весьма большой, однако это полностью окупается экстраординарной вместительностью. Супертранспорт оснащен лишь лазерными противометеоритными туррелями, но имеет мощный щит.',\n      'description_short' => 'Гигантская самоходная баржа, оснащенная ионными двигателями. Оборудован мощным щитом, но из оружия имеет только лазерные противометеоритные туррели.',\n    ),\n\n    SHIP_CARGO_HYPER => array(\n      'description' => 'Если считать Супетранспорт последним словом в технологии транспортировки - то Гипертранспорт является финальной точкой. &quot;Гигантский&quot; - слишком слабое слово для описания этого корабля. Размером с небольшую луну, этот транспорт способен перевозить неимоверное количество ресурсов. Сдвинуть с места полностью загруженный корабль способны только гипердвигатели. Они позволяют Гипертранспорту передвигаться по Вселенной с приемлемой скоростью. Но цена велика - стоимость транспорта приближается к стоимости десятка Уничтожителей. А расход топлива способен заставить плакать не одного Императора. Поэтому Гипертранспорт - корабль для огромных и мощных Империй, которым нужно перевозить за раз десятки или сотни миллионов тон ресурсов',\n      'description_short' => 'Транспортный корабль размером с небольшую луну. Оснащен гипердвигателями и способен перевозить миллион тон ресурсов за один рейс',\n    ),\n\n    SHIP_SMALL_FIGHTER_LIGHT => array(\n      'description' => 'Лёгкий истребитель - это манёвренный корабль, который можно найти почти на каждой планете. Затраты на него не особо велики, однако щитовая мощность и вместимость очень малы. ',\n      'description_short' => 'Лёгкий истребитель - это манёвренный корабль, который можно найти почти на каждой планете. Затраты на него не особо велики, однако щитовая мощность и вместимость очень малы.',\n    ),\n\n    SHIP_SMALL_FIGHTER_HEAVY => array(\n      'description' => 'При дальнейшем развитии лёгкого истребителя учёные дошли до момента, когда стало ясно, что обыкновенный двигатель не обладает необходимой мощью. Для того, чтобы оптимально передвигать корабль, был впервые использован ионный двигатель. Хоть он и повысил стоимость, однако он также открыл новые возможности. Благодаря применению этого двигателя осталось больше энергии для вооружения и щитов, кроме того, для этого вида истребителей также использовались ценные материалы. Это привело к улучшенной структурной целостности и более сильной огневой мощи, благодаря чему в бою он представляет бoльшую угрозу, чем его предшественник. После этих изменений тяжёлый истребитель представляет собой новую эру технологии кораблестроения, основу технологии крейсеростроения.',\n      'description_short' => 'Дальнейшее развитие лёгкого истребителя, он лучше защищён и обладает большей силой атаки.',\n    ),\n\n    SHIP_MEDIUM_DESTROYER => array(\n      'description' => 'С развитием тяжёлых лазеров и ионных пушек тяжёлые истребители всё больше вытеснялись. Несмотря на многочисленные усовершенствования огневая мощь и бронирование не могли быть настолько изменены, чтобы действенно противостоять этим оборонительным орудиям. Поэтому было решено построить новый класс кораблей, который объединял бы в себе больше бронирования и огневой мощи. Так появились эсминцы. Эсминцы почти втрое сильней защищены, чем тяжёлые истребители и обладают более чем удвоенной огневой мощью. К тому же они очень быстры. Нет лучшего оружия против средней защиты. Почти столетие крейсеры неограниченно господствовали во вселенной. С появлением орудий Гаусса и плазменных орудий их господство закончилось. Однако и сегодня их охотно применяют против групп истребителей.',\n      'description_short' => 'Эсминцы почти втрое сильней защищены, чем тяжёлые истребители, а по огневой мощи они превосходят тяжёлые истребители почти в два раза. К тому же они очень быстры.',\n    ),\n\n    SHIP_LARGE_CRUISER => array(\n      'description' => 'Крейсера как правило составляют основу флота. Их тяжёлые орудия, высокая скорость и большой грузовой тоннаж делают их серьёзными противниками. ',\n      'description_short' => 'Крейсера как правило составляют основу флота. Их тяжёлые орудия, высокая скорость и большой грузовой тоннаж делают их серьёзными противниками.',\n    ),\n\n    SHIP_COLONIZER => array(\n      'description' => 'Этот хорошо защищённый корабль служит покорению новых планет, что необходимо развивающейся империи. Он используется в новой колонии в качестве поставщика сырья - его разбирают и используют весь полезный материал для освоения нового света. Максимальное количество колоний зависит от настроек Вселенной, которые можно посмотреть через пункт меню \\'Мировые константы\\'',\n      'description_short' => 'При помощи этого корабля можно осваивать незаселённые планеты.',\n    ),\n\n    SHIP_RECYCLER => array(\n      'description' => 'Космические бои принимали всё бoльшие масштабы. Уничтожались тысячи кораблей и возникавшие при этом обломки казались навсегда потерянными. Нормальные транспорты не могли близко к ним приблизиться, не будучи сильно повреждёнными маленькими обломками. С новым открытием в области щитовой технологии стало возможно эффективно устранять эту проблему, возник новый класс корабля, подобный большому транспорту - переработчик. С его помощью можно было заново использовать казавшиеся потерянными ресурсы. Из-за новых щитов маленькие обломки больше не представляли собой опасности. К сожалению, эти устройства требуют пространства, поэтому его грузовой тоннаж ограничен до 20 000.',\n      'description_short' => 'С помощью переработчика добывается сырьё из обломков.',\n    ),\n\n    SHIP_SPY => array(\n      'description' => 'Шпионские зонды - это маленькие манёвренные корабли, которые доставляют с больших расстояний данные о флотах и планетах. Их высокомощный двигатель позволяет им преодолевать большие расстояния за несколько секунд. Однажды попав на орбиту какой-нибудь планеты, они пребывают там некоторое время для сборки данных. В это время враг может их относительно легко обнаружить и атаковать. Для экономии места не было установлено ни брони, ни щитов, ни орудий, что делает зонды в случае обнаружения лёгкими целями.',\n      'description_short' => 'Шпионские зонды - это маленькие манёвренные корабли, которые доставляют с больших расстояний данные о флотах и планетах.',\n    ),\n\n    SHIP_LARGE_BOMBER => array(\n      'description' => 'Бомбардировщик был разработан специально для того, чтобы уничтожать планетарную защиту. С помощью лазерного прицела он точно сбрасывает плазменные бомбы на поверхность планеты и таким образом наносит огромные повреждения оборонительным сооружениям.Когда гиперпространственный двигатель исследован до 8-й ступени, у бомбардировщика повышается базовая скорость и он оснащается этим типом двигателя.',\n      'description_short' => 'Бомбардировщик был разработан специально для того, чтобы уничтожать планетарную защиту.',\n    ),\n\n    SHIP_SATTELITE_SOLAR => array(\n      'description' => 'Солнечные спутники запускаются на орбиту планеты. Они собирают солнечную энергию и передают её на наземную станцию. Эффективность солнечных спутников зависит от мощи солнечного излучения. В принципе, добыча энергии на орбитах, более приближённых к солнцу, выше, чем на планетах, удалённых от солнца. Из-за своего соотношения цены и качества солнечные спутники решают энергетические проблемы многих миров. Но внимание: солнечные спутники могут быть уничтожены в бою.',\n      'description_short' => 'Солнечные спутники - это простые платформы из солнечных батарей, которые находятся на высокой орбите. Они собирают солнечный свет и передают его с помощью лазера на наземную станцию.',\n    ),\n\n    SHIP_LARGE_DESTRUCTOR => array(\n      'description' => 'Уничтожитель - король среди военных кораблей. Его мультифланговые ионные, плазменные и гауссовые орудийные башни могут благодаря своим усовершенствованным пеленгационным сенсорам поражать с точностью до 99% даже скоростные манёвренные истребители. Так как уничтожители очень велики, их манёвренность очень ограничена, и в бою они подобны скорее боевой станции, чем боевому кораблю. Потребление дейтерия у них так же высоко, как и их боевая мощь.',\n      'description_short' => 'Уничтожитель - король среди военных кораблей.',\n    ),\n\n    SHIP_HUGE_DEATH_STAR => array(\n      'description' => 'Звезда Смерти оснащена гравитонной пушкой, которая может уничтожать все типы кораблей и даже луны. Для производства достаточного количества энергии, звезда смерти практически полностью состоит из генераторов. Только огромные звёздные империи имеют достаточно ресурсов и работников для строительства этого огромного корабля.',\n      'description_short' => 'Звезда смерти оснащена гигантской гравитонной пушкой, которая может уничтожать корабли и даже луны. ',\n    ),\n\n    SHIP_LARGE_BATTLESHIP => array(\n      'description' => 'Этот высокотехнологичный корабль несёт смерть атакующим флотам. Его усовершенствованные лазерные орудия удерживают тяжёлые корабли противника на расстоянии и могут уничтожать несколько единиц одним залпом. За счёт его малых размеров и невероятно мощного вооружения, грузоподъёмность линейного крейсера очень мала, но за счёт гиперпространственного двигателя также мало потребление топлива.',\n      'description_short' => 'Линейный крейсер специализируется на перехвате вражеских флотов.',\n    ),\n\n    SHIP_HUGE_SUPERNOVA => array(\n      'description_short' => 'Крейсер класса &quot;СуперНова&quot; - флагман космического флота Империи',\n      'description' => 'Крейсер класса &quot;СуперНова&quot; - флагман космического флота Империи. Огромная стоимость постройки с лихвой компенсируется ужасающей огневой мощью и продвинутой защитой. Один корабль такого класса способен в одиночку разгромить средний флот.',\n    ),\n\n    UNIT_DEF_TURRET_MISSILE => array(\n      'description' => 'Ракетная установка - простое и дешёвое средство обороны. Так как это развитие обычных баллистических орудий, то ему не требуется дальнейшей модернизации. Малые затраты на его производство оправдывают его применение против более маленьких флотов, но со временем он теряет значение. Позднее его используют лишь для отвода вражеских выстрелов. Оборонительные сооружения деактивируются сами по себе, как только они сильно повреждаются. Возможность восстановления оборонительных сооружений после боя составляет до 70%.',\n      'description_short' => 'Ракетная установка - простое и дешёвое средство обороны.',\n    ),\n\n    UNIT_DEF_TURRET_LASER_SMALL => array(\n      'description_short' => 'При помощи концентрированного обстрела цели фотонами можно достичь значительно бoльших разрушений, чем при применении обычного баллистического вооружения.',\n      'description' => 'Для компенсации чрезмерных успехов в области технологии космических кораблей, учёные должны были создать оборонительное сооружение, справляющееся с более крупными и лучше вооружёнными флотами. Это привело к появлению лёгкого лазера. При помощи концентрированного обстрела цели фотонами можно достичь значительно больших разрушений, чем при применении обычного баллистического вооружения. Для противостояния более сильной огневой мощи новых типов кораблей он также оснащён усовершенствованными щитами. Однако, чтобы стоимость производства оставалась низкой, структура дальше не усиливалась. Лёгкий лазер обладает наилучшим соотношением цены и качества, поэтому он также интересен и для более развитых цивилизаций. Оборонительные сооружения деактивируются сами по себе, как только они сильно повреждаются. Возможность восстановления оборонительных сооружений после боя составляет до 70%.',\n    ),\n\n    UNIT_DEF_TURRET_LASER_BIG => array(\n      'description' => 'Тяжёлый лазер представляет собой дальнейшее развитие лёгкого лазера. Структура была усилена и усовершенствована новыми материалами. Оболочку смогли сделать значительно более стойкой. Одновременно была улучшена и энергетическая система и целевой компьютер, так что тяжёлый лазер может концентрировать значительно больше энергии на цели. Оборонительные сооружения деактивируются сами по себе, как только они сильно повреждаются. Возможность восстановления оборонительных сооружений после боя составляет до 70%.',\n      'description_short' => 'Тяжёлый лазер представляет собой дальнейшее развитие лёгкого лазера.',\n    ),\n\n    UNIT_DEF_TURRET_GAUSS => array(\n      'description_short' => 'Пушка Гаусса ускоряет многотонные заряды с гигантскими затратами энергии.',\n      'description' => 'Долгое время артиллерийские орудия считались устаревшими на фоне развития современной лазерной и ионной техники и постоянно улучшающейся защиты, пока новые исследования в области энергетической технологии не позволили поднять артиллерию на качественно новый уровень. Вообще-то принципы электромагнитных ускорителей масс были известны на Земле уже с 20-го столетия. Один из них - пушка Гаусса. С решением технических трудностей использование ее в качестве оружия стало реальностью. Многотонные снаряды ускоряются магнитным полем при огромных затратах энергии и имеют такую выходную скорость, что частички пыли в воздухе вокруг снаряда сгорают, а звуковая волна сотрясает землю. Даже современная броня и щиты с трудом могут противостоять пробивной силе пушки Гаусса, и нередко случается так, что цель просто простреливается насквозь. Оборонительные сооружения деактивируются сами по себе, как только они сильно повреждаются. Возможность восстановления оборонительных сооружений после боя составляет до 70%.',\n    ),\n\n    UNIT_DEF_TURRET_ION => array(\n      'description_short' => 'Ионное орудие направляет на цель волну ионов, которая дестабилизирует щиты и повреждает электронику.',\n      'description' => 'В 21-м столетии на Земле уже существовало то, что общеизвестно как ЭМИ. ЭМИ означает электромагнитный импульс, который обладает способностью индуцировать дополнительные напряжения в схемы и тем самым причинять массовые помехи, которые могут уничтожить все чувствительные приборы. Тогда ЭМИ-орудия были в основном на базе ракет и бомб, также в комбинации с ядерными орудиями. Между тем ЭМИ-орудия постоянно развивались, так как в них видели большой потенциал не уничтожать цели, а делать их неспособными к бою и манёвренности и, тем самым, упрощать их захват. Пока что наивысшая форма ЭМИ-орудий представлена ионным орудием. Оно направляет на цель волну ионов (электрически заряженных частиц), которая дестабилизирует щиты и повреждает электронику, только если она не очень хорошо защищена, что иногда подобно полному уничтожению. Кинетической пробивной силой можно пренебречь. Ионная техника используется только на крейсерах, так как потребление энергии ионными орудиями огромно, и в бою часто приходится уничтожать, а не парализовать цель. Оборонительные сооружения деактивируются сами по себе, как только они сильно повреждаются. Возможность восстановления оборонительных сооружений после боя составляет до 70%.',\n    ),\n\n    UNIT_DEF_TURRET_PLASMA => array(\n      'description' => 'Лазерная технология была доведена до совершенства, ионная техника достигла конечной стадии и считалось, что практически невозможно, даже с качественной точки зрения орудийной системы, достичь ещё большей эффективности. Но всё должно было измениться, когда появилась идея объединить обе системы. Используя технологии термоядерного синтеза, лазерами нагревают вещество (обычно дейтерий) до сверхвысоких температур, достигающих миллионов градусов. Ионная техника обеспечивает обогащение плазмы электрическим зарядом, её стабилизацию и ускорение. Как только заряд достаточно нагрет, ионизирован и находится под давлением, то его выпускают при помощи ускорителей в направлении цели. Светящийся голубоватым цветом плазменный шар выглядит внушительно, только спрашивается, долго ли им будет наслаждаться команда корабля-цели, если через несколько секунд броня разорвётся на куски, а электроника сгорит... Плазменное орудие считается вообще самым страшным оружием, и у этой техники есть своя цена. Оборонительные сооружения деактивируются сами по себе, как только они сильно повреждаются. Возможность восстановления оборонительных сооружений после боя составляет до 70%.',\n      'description_short' => 'Последнее слово в планетарных оборонных технологиях, порожденный симбиозом лазерной и ионной техник.',\n    ),\n\n    UNIT_DEF_SHIELD_SMALL => array(\n      'description' => 'Задолго до того, как щитовые генераторы стали достаточно малы для того, чтобы найти применение на кораблях, уже существовали огромные генераторы на поверхности планет. Они обволакивали целую планету силовым полем, которое могло поглощать удары атаки. Малые атакующие флоты постоянно разбиваются об эти щитовые купола. Благодаря растущему технологическому развитию эти щиты можно ещё усилить. Позже можно строить более сильный большой щитовой купол. На каждой планете можно построить только один малый щитовой купол.',\n      'description_short' => 'Малый щитовой защищает планету и поглощает удары атаки.',\n    ),\n\n    UNIT_DEF_SHIELD_BIG => array(\n      'description' => 'Дальнейшее развитие малого щитового купола. Он может сдерживать ещё более сильные атаки на планету, поглощая значительно большее количество энергии. ',\n      'description_short' => 'Дальнейшее развитие малого щитового купола. Он может сдерживать ещё более сильные атаки на планету, поглощая значительно большее количество энергии.',\n    ),\n\n    UNIT_DEF_SHIELD_PLANET => array(\n      'description' => 'Лучшая защита для ваших планет ',\n      'description_short' => 'Лучшая защита для ваших планет',\n    ),\n\n    UNIT_DEF_MISSILE_INTERCEPTOR => array(\n      'description' => 'Ракеты-перехватчики уничтожают атакующие межпланетные ракеты. Одна ракета-перехватчик уничтожает одну межпланетную ракету.',\n      'description_short' => 'Ракеты-перехватчики уничтожают атакующие межпланетные ракеты',\n    ),\n\n    UNIT_DEF_MISSILE_INTERPLANET => array(\n      'description_short' => 'Межпланетные ракеты уничтожают вражескую оборону',\n      'description' => 'Межпланетные ракеты уничтожают защиту противника. Уничтоженные межпланетными ракетами оборонительные сооружения больше не восстанавливаются.',\n    ),\n\n    MRC_TECHNOLOGIST => array(\n      'description' => 'Технолог - признанный эксперт в оптимизации добычи и обработки ресурсов. Со своей командой металлургов, химиков и энергетиков он поддерживает планетарные правительства при разработке новых источников ресурсов и оптимизирует их очистку',\n      'description_short' => 'Технолог - признанный эксперт в оптимизации добычи и обработки ресурсов. Со своей командой металлургов, химиков и энергетиков он поддерживает планетарные правительства при разработке новых источников ресурсов и оптимизирует их очистку',\n      'effect' => 'к добыче металла, кристаллов и дейтерия, к выработке электроэнергии на солнечной и термоядерной станциях за каждый уровень.',\n    ),\n\n    MRC_ENGINEER => array(\n      'description' => 'Инженер - современнейший строитель Империи. Его ДНК была подвергнута мутациям, что обеспечило ему экстраординарный склад ума и сверхчеловеческую силу. Инженер в одиночку может спроектировать и построить целый город.',\n      'description_short' => 'Инженер - современнейший строитель Империи. Его ДНК была подвергнута мутациям, что обеспечило ему экстраординарный склад ума и сверхчеловеческую силу. Инженер в одиночку может спроектировать и построить целый город.',\n      'effect' => 'к скорости постройки зданий и кораблей за каждый уровень<br />+1 слот к очередям построек и кораблей за каждый уровень',\n    ),\n\n    MRC_FORTIFIER => array(\n      'description' => 'Фортификатор - армейский инженер-специалист. Его глубокие познания в области оборонительных систем позволяют сократить сроки строения планетарных систем обороны',\n      'description_short' => 'Фортификатор - армейский инженер-специалист. Его глубокие познания в области оборонительных систем позволяют сократить сроки строения планетарных систем обороны',\n      'effect' => 'к скорости постройки защитных сооружений и ракет за каждый уровень.<br />+10% за каждый уровень к атаке, броне и щитам хозяина планеты при обороне<br />+1 слот к очереди оборонных сооружений и ракет за каждый уровень',\n    ),\n\n    MRC_STOCKMAN => array(\n      'description' => 'Карго-мастер является высококлассным специалистом по складированию. Его гений позволяет извлечь максимум из хранилищ ресурсов, увеличивая их эффективную емкость за пределы, предусмотренные строителями.',\n      'description_short' => 'Карго-мастер является высококлассным специалистом по складированию. Его гений позволяет извлечь максимум из хранилищ ресурсов, увеличивая их эффективную емкость за пределы, предусмотренные строителями.',\n      'effect' => 'к размеру складов за каждый уровень.',\n    ),\n\n    MRC_SPY => array(\n      'description' => 'Шпион - загадочнейшая персона Империи. У него сотни лиц, тысячи личностей и миллион идей, касающихся маскировки сооружений, оборонительных сетей и флота. Все, кто видел его настоящее лицо, ныне мертвы.',\n      'description_short' => 'Шпион - загадочнейшая персона Империи. У него сотни лиц, тысячи личностей и миллион идей, касающихся маскировки сооружений, оборонительных сетей и флота. Все, кто видел его настоящее лицо, ныне мертвы.',\n      'effect' => 'к уровню шпионажа за каждый уровень.',\n    ),\n\n    MRC_ACADEMIC => array(\n      'description' => 'Академики являются участниками Гильдии Технократов. Их склад ума и учёные степени позволяют им превосходить в своих деяниях даже Конструкторов. Они специализируются в области технологического прогресса.',\n      'description_short' => 'Академики являются участниками Гильдии Технократов. Их склад ума и учёные степени позволяют им превосходить в своих деяниях даже Конструкторов. Они специализируются в области технологического прогресса.',\n      'effect' => 'к скорости исследований за каждый уровень',\n    ),\n\n//    MRC_DESTRUCTOR => array(\n//      'description' => 'Разрушитель - беспощадный офицер. Он наводит порядок на планетах Империи зверскими методами. Так же Уничтожитель разработал технологию производств Звезд Смерти.',\n//      'effect' => 'Позволяет строить на верфи Звезду Смерти',\n//    ),\n\n    MRC_ADMIRAL => array(\n      'description' => 'Адмирал - это испытанный войной ветеран и гениальный стратег. Даже в самых горячих боях он не теряет обзора и поддерживает контакт с командирами флотов. Мудрый правитель может полностью положиться на него в бою и тем самым использовать для боя больше кораблей.',\n      'description_short' => 'Адмирал - это испытанный войной ветеран и гениальный стратег. Даже в самых горячих боях он не теряет обзора и поддерживает контакт с командирами флотов. Мудрый правитель может полностью положиться на него в бою и тем самым использовать для боя больше кораблей.',\n      'effect' => 'к броне, щитам и атаке кораблей за каждый уровень.',\n    ),\n\n    MRC_COORDINATOR => array(\n      'description' => 'Координатор является экспертом в управлении флотами. Его познания позволяют выжать максимум из системы управления флота.',\n      'description_short' => 'Координатор является экспертом в управлении флотами. Его познания позволяют выжать максимум из системы управления флота.',\n      'effect' => 'дополнительный флот за каждый уровень.',\n    ),\n\n    MRC_NAVIGATOR => array(\n      'description' => 'Навигатор - гений в расчете траекторий полета флотов. Его знания законов варп-пространства, устройства джамп-привода и технологий всех существующих типов двигателей позволяет увеличить скорость полета кораблей.',\n      'description_short' => 'Навигатор - гений в расчете траекторий полета флотов. Его знания законов варп-пространства, устройства джамп-привода и технологий всех существующих типов двигателей позволяет увеличить скорость полета кораблей.',\n      'effect' => 'к скорости кораблей за каждый уровень.',\n    ),\n\n    MRC_EMPEROR => array(\n      'description' => 'Император - Ваш личный помощник и заместитель. Точность его докладов и дотошная пунктуальность во всём - его лучшие качества, способные осуществлять тотальный контроль над Империей.',\n      'description_short' => 'Император - Ваш личный помощник и заместитель. Точность его докладов и дотошная пунктуальность во всём - его лучшие качества, способные осуществлять тотальный контроль над Империей.',\n      'effect' => 'Позволяет изменить характеристики Императора',\n    ),\n\n\n    ART_LHC => array(\n      'description' => 'БАК генерирует поток гравитонного излучения, направленный в место наибольшего скопления обломков на орбите, в результате чего обломки притягиваются друг к другу.<br /><span class=warning>ВНИМАНИЕ! Использование БАК не гарантирует появления луны!</span>',\n      'effect' => 'Позволяет повторить попытку создания луны<br />1% за каждый миллион обломков, но не более 30%',\n    ),\n\n    ART_HOOK_SMALL => array(\n      'description' => 'Принцип действия этого Артефакта до конца не изучен что, впрочем, не мешает его использовать. Малый Крюк телепортирует на устойчивую орбиту планеты небольшой астероид. Таким образом у планеты получается луна минимального диаметра',\n      'effect' => 'Создает у планеты Луну минимального размера',\n    ),\n\n    ART_HOOK_MEDIUM => array(\n      'description' => 'Принцип действия этого Артефакта до конца не изучен что, впрочем, не мешает его использовать. Средний Крюк телепортирует на устойчивую орбиту планеты астероид, создавая таким образом луну<br /><span class=warning>ВНИМАНИЕ! Размер луны СЛУЧАЕН!</span>',\n      'effect' => 'Создает у планеты Луну случайного размера',\n    ),\n\n    ART_HOOK_LARGE => array(\n      'description' => 'Принцип действия этого Артефакта до конца не изучен что, впрочем, не мешает его использовать. Большой Крюк телепортирует на устойчивую орбиту планеты огромный астероид. Таким образом у планеты получается луна максимального диаметра',\n      'effect' => 'Создает у планеты Луну максимального размера',\n    ),\n\n    ART_RCD_SMALL => array(\n      'description' => 'Малый Автономный Колонизационный Комплекc (сокращенно - АКК) является набором готовых конструкций и программ, позволяющий мгновенно развернуть на новооткрытой планете базовую колонию<br />Если на планете уже есть здания, входящие в АКК, они будут либо усовершенствованы, либо останутся нетронутыми. АКК может быть полностью развернут на планете даже при нехватке свободных секторов. АКК не может быть развернут на луне<br />Колония включает в себя Рудник, Синтезатор кристаллов и Синтезатор дейтерия 10го уровня, Солнечную электростанцию 14го уровня и Фабрику роботов 4го уровня',\n      'effect' => 'Позволяет мгновенно развернуть на планете базовую колонию',\n    ),\n\n    ART_RCD_MEDIUM => array(\n      'description' => 'Средний Автономный Колонизационный Комплекc (сокращенно - АКК) является набором готовых конструкций и программ, позволяющий мгновенно развернуть на новооткрытой планете колонию среднего уровня<br />Если на планете уже есть здания, входящие в АКК, они будут либо усовершенствованы, либо останутся нетронутыми. АКК может быть полностью развернут на планете даже при нехватке свободных секторов. АКК не может быть развернут на луне<br />Колония включает в себя Рудник, Синтезатор кристаллов и Синтезатор дейтерия 15го уровня, Солнечную электростанцию 20го уровня и Фабрику роботов 8го уровня',\n      'effect' => 'Позволяет мгновенно развернуть на планете колонию среднего уровня',\n    ),\n\n    ART_RCD_LARGE => array(\n      'description' => 'Большой Автономный Колонизационный Комплекc (сокращенно - АКК) является набором готовых конструкций и программ, позволяющий мгновенно развернуть на новооткрытой планете продвинутую колонию<br />Если на планете уже есть здания, входящие в АКК, они будут либо усовершенствованы, либо останутся нетронутыми. АКК может быть полностью развернут на планете даже при нехватке свободных секторов. АКК не может быть развернут на луне<br />Колония включает в себя Рудник, Синтезатор кристаллов и Синтезатор дейтерия 20го уровня, Солнечную электростанцию 25го уровня, Фабрику роботов 10го уровня и Нанофабрику 1го уровня',\n      'effect' => 'Позволяет мгновенно развернуть на планете продвинутую колонию',\n    ),\n\n    ART_HEURISTIC_CHIP => array(\n      'description' => 'Эвристический чип - уникальный преинсталлированный набор программ, записанных на кристаллический носитель. Подключаясь к исследовательской сети, алгоритмы чипа способны проанализировать текущее состояние исследования и выдать новые эффективные эвристики, таким образом значительно сокращая время исследования. Однажды активированный чип невозможно перенастроить на другое исследование. К сожалению, как и с любым другим кристаллическим чипом, декомпиляция &quot;зашитой&quot; программы принципиально невозможна, равно как и копирование сборщиками.',\n      'effect' => 'Уменьшает время текущего исследования в два раза (если до конца исследования осталось больше часа) или моментально заканчивает его (если до конца исследования осталось меньше 1 часа, но больше 1 минуты)', //. Если времени исследования осталось менее часа - остаток не переходит на следующий слот в очереди',\n    ),\n\n    ART_NANO_BUILDER => array(\n      'description' => 'Как известно, сборщики обычно не используются в строительстве крупных объектов типа зданий. Экономически целесообразней возводить строения методом традиционной &quot;блочной сборки&quot;, когда отдельные стандартизированные детали производятся на роботизированных фабриках. Однако специализированные наносборщики оказываются эффективнее традиционных методов. Эти крошечные роботы собраны в преконфигурированные пакеты, каждый из которых обладает своим собственным роевым суб-ИИ. Анализируя текущее состояние возводимого здания, наностроители безошибочно находят узкие места и вычисляют наиболее эффективные пути ускорения строительства. Пакет является одноразовым и после использования больше непригоден к работе. Вдобавок инициированный пакет уже невозможно перенастроить на интеграцию с другой стройкой. Хотя сборщики и способны воспроизвести отдельно взяты наностроитель, но без управляющего кристалла такая реплика является не более чем масштабной моделью...',\n      'effect' => 'Уменьшает время постройки/разрушения текущего здания на данной планете в два раза (если до конца процесса осталось больше часа) или моментально заканчивает его (если до конца процесса осталось меньше 1 часа, но больше 1 минуты)', // . Если времени строительства осталось менее часа - остаток не переходит на следующий слот в очереди',\n    ),\n\n    ART_DENSITY_CHANGER  => array(\n      'description' => '',\n      'effect' => '',\n    ),\n\n\n\n\n    UNIT_PLAN_STRUC_MINE_FUSION  => array(\n      'description' => 'Чертеж строения &quot;Термоядерная Электростанция&quot; ',\n      'effect' => 'Позволяет строить на планетах строение &quot;Термоядерная Электростанция&quot;',\n    ),\n\n    UNIT_PLAN_SHIP_CARGO_SUPER  => array(\n      'description' => 'Чертеж корабля &quot;Супертранспорт&quot; ',\n      'effect' => 'Позволяет строить на верфях корабль &quot;Супертранспорт&quot;',\n    ),\n\n    UNIT_PLAN_SHIP_CARGO_HYPER  => array(\n      'description' => 'Чертеж корабля &quot;Гипертранспорт&quot; ',\n      'effect' => 'Позволяет строить на верфях корабль &quot;Гипертранспорт&quot;',\n    ),\n\n    UNIT_PLAN_SHIP_DEATH_STAR  => array(\n      'description' => 'Чертеж корабля &quot;Звезда Смерти&quot; ',\n      'effect' => 'Позволяет строить на верфях корабль &quot;Звезда Смерти&quot;',\n    ),\n\n    UNIT_PLAN_SHIP_SUPERNOVA  => array(\n      'description' => 'Чертеж крейсера класса &quot;СуперНова&quot;',\n      'effect' => 'Позволяет строить на верфях крейсер класса &quot;СуперНова&quot;',\n    ),\n\n    UNIT_PLAN_DEF_SHIELD_PLANET  => array(\n      'description' => 'Чертеж защитного сооружения &quot;Планетарная защита&quot;',\n      'effect' => 'Позволяет строить на планете защитное сооружение &quot;Планетарная защита&quot;',\n    ),\n\n\n    RES_METAL => array(\n      'description' => 'Метаметаллический железо-нормированный энергонейтральный компаунд (или попросту &quot;металл&quot;) является базовым сырьем из которого наноботы производят все необходимые материалы и/или конструкции, используемые в строительстве зданий, кораблей, оборонных сооружений и так далее. Поставляется в виде слаборадиоактивных слитков объемом 127 литров и весом в 1 тонну, включая защитную оболочку. &quot;Железо-нормированный&quot; означает, что из одного слитка стандартный набор наноботов может произвести одну тонну железа. &quot;Энергонейтральный&quot; означает, что при этом будет использовано ровно столько энергии, сколько содержится в самом слитке. И, наконец, &quot;метаметаллический компаунд&quot; означает, что хотя основную часть слитка составляют различные металлы, в его состав могут входить и другие вещества - как сложные, так и простые. Состав компаунда может менятся от планеты к планете и даже от шахты к шахте, в пределе являясь чистым железом, однако размер, форма и вес слитков всегда остаются одинаковыми.',\n      'effect' => '',\n    ),\n\n    RES_CRYSTAL => array(\n      'description' => 'Кристалл - сложный термопластический полимер, демонстрирующий Эффект Сверхсветовой Проводимости. ЭСП - увеличение в теле кристалла скорости фотонов выше 300000 км/с. Все современные вычислительные приборы используют кристаллы в качестве базового строительного материала. Отходы производства (&quot;аномальные сборки&quot; - т.е. полимеры, эквивалентные по формуле кристаллам, но при этом не демонстрирующие ЭСП) используются в солнечных батареях, КПД которых приближается к 100%. Кроме того, специально отобранные кристаллы используются в джамп-приводах - устройствах, позволяющих совершать межпланетные и межзвездные путешествия со сверхсветовой скоростью',\n      'effect' => '',\n    ),\n\n    RES_DEUTERIUM => array(\n      'description' => 'Дейтерий, тяжёлый водород — стабильный изотоп водорода с атомной массой, равной 2. Ядро (дейтрон) состоит из одного протона и одного нейтрона. Дейтерий является топливом для термоядерных реакторов и всех типов двигателей. Он хранится в сжиженном виде в стандартных термоизолированных контейнерах, которые так же являются топливными блоками как для двигательных установок, так и для термоядерных реакторов. Поэтому трюмы, оснащенные автоматическим податчиком-укладчиком так же является и &quot;топливным баком&quot; любого космического корабля',\n      'effect' => '',\n    ),\n\n    RES_ENERGY => array(\n      'description' => 'Электроэнергия - единный универсальный вид энергии, которые используется повсеместно. На планетах она обычно вырабатывается солнечными электростанциями и солнечными спутниками. На особо далеких от солнца холодных планетах и космических кораблях электроэнергия вырабатывается дейтериевыми термоядерными реакторами',\n      'effect' => '',\n    ),\n\n    RES_DARK_MATTER => array(\n      'description' => '<span class=\"dark_matter\">Тёмная Материя</span> (сокращенно <span class=\"dark_matter\">ТМ</span>) - необнаружимая стандартными методами небарионная материя, на которую приходится 23% массы Вселенной. Из неё можно добывать невероятное количество энергии. Из-за этого, а так же из-за сложностей, связанных с её добычей, <span class=\"dark_matter\">Тёмная Материя</span> ценится очень высоко',\n      'effect' => '',\n    ),\n\n    RES_METAMATTER => array(\n      'description' => 'Метаматерия - это такая новая хня, к которой нужно написать описание',\n      'effect' => '',\n    ),\n\n    UNIT_PLANET_DENSITY => array(\n      'description' => 'Средняя планетарная плотность (далее - просто \"плотность\") характеризует химический состав геосферы планеты. В частности, она очень точно предсказывает соотношения добываемых полезных веществ.<br /><br />\n      В общем случае геосфера планеты делится на атмосферу (газовая оболочка), гидросферу (жидкая оболочка), литосферу (твердая оболочка из относительно легких веществ и элементов), мантию (промежуточная оболочка между литосферой и ядром) и ядро (самые тяжелые вещества и элементы, находящиеся в центре планеты под высоким давлением).<br /><br />\n      Основное влияние на плотность оказывает ядро. Именно оно содержит основную массу планеты, поскольку плотность химических соединений убывает по мере подъема от центра планеты к её поверхности.<br /><br />\n      Таким образом именно тип ядра полностью определяет виды и уровень добычи полезных ископаемых. Комбинации типов планетарных ядер с разными температурными режимами и размерами дают всё многообразие известных планет.<br /><br />\n      Для примера рассмотрим планету с ледяным ядром. Оно состоит из водяного льда с небольшой примесью других веществ, мантия - из твердого метана, а литосфера - из кристаллического водорода.<br />\n      В подавляющем большинстве такие планеты встречаются на периферии звездных систем. И выглядят они под стать своему названию: гигантские глыбы из смеси льда.<br />\n      Впрочем, иногда такая планета может оказаться и на низкой орбите вокруг звезды. Обычно это происходит после столкновения звездных систем. Так же звезда может захватить своим тяготением какую-нибудь блуждающую планету.<br />\n      Близость к звезде преображает планету. На ней образуется быстроулетучивающаяся атмосфера из легких газов (водород и гелий), а иногда даже и гидросфера из жидкого водорода и метана. При этом ядро планеты по-прежнему остается ледяным.<br />\n      Существование такой планеты чрезвычайно коротко во вселенских масштабах, но учитывая те же масштабы, обнаружить такую планету вполне возможно.\n      <br />\n      <br />\n\n      Тип ядра можно сменить за ТМ на странице <a href=\"overview.php?mode=manage\" class=\"link ok\">\"Управление планетой\"</a> (пункт меню \"Планета\", кнопка \"Управление\" на странице).\n      Стоимость смены типа ядра зависит от количества секторов самой планеты без учета секторов от Терраформеров, но учитывая сектора, купленные за ТМ. Стоимость нового типа ядра не зависит от текущего.\n      Добыча ресурсов в пределах одного класса приблизительно равны в пересчете на потраченную ТМ (как определяется добыча - см. ниже после таблицы).\n      Ядра более редкого класса стоят дороже, но и дают заметно больше ресурсов (см. выше).<br /><br />\n\n      Всего существует 10 типов ядер, рассортированных на четыре класса:\n      <table>\n        <tr class=\"c_c\">\n          <th rowspan=\"2\">Тип ядра</th>\n          <th colspan=\"2\">Плотность, кг/м&sup3;</th>\n          <th colspan=\"3\">Добыча</th>\n          <th rowspan=\"2\">Класс</th>\n        </tr>\n        <tr class=\"c_c\">\n          <th>Минимальная</th>\n          <th>Максимальная</th>\n          <th>Металл</th>\n          <th>Кристалл</th>\n          <th>Дейтерий</th>\n        </tr>\n\n        <tr class=\"c_c\">\n          <th>Водородный лёд</th>\n          <td>250</td>\n          <td>750</td>\n          <td class=\"error\">Очень плохая</td>\n          <td class=\"warning\">Плохая</td>\n          <td class=\"positive\">Отличная</td>\n          <td class=\"error\">Раритет</td>\n        </tr>\n\n        <tr class=\"c_c\">\n          <th>Метановый лёд</th>\n          <td>750</td>\n          <td>1250</td>\n          <td class=\"warning\">Плохая</td>\n          <td class=\"notice\">Неплохая</td>\n          <td class=\"positive\">Очень хорошая</td>\n          <td class=\"warning\">Редкий</td>\n        </tr>\n\n        <tr class=\"c_c\">\n          <th>Водный лёд</th>\n          <td>1250</td>\n          <td>2000</td>\n          <td class=\"notice\">Неплохая</td>\n          <td class=\"notice\">Неплохая</td>\n          <td class=\"positive\">Хорошая</td>\n          <td class=\"notice\">Продвинутый</td>\n        </tr>\n\n        <tr class=\"c_c\">\n          <th>Кристалл</th>\n          <td>2000</td>\n          <td>2500</td>\n          <td class=\"error\">Очень плохая</td>\n          <td class=\"positive\">Отличная</td>\n          <td class=\"warning\">Плохая</td>\n          <td class=\"error\">Раритет</td>\n        </tr>\n\n        <tr class=\"c_c\">\n          <th>Силикат</th>\n          <td>2500</td>\n          <td>3500</td>\n          <td class=\"warning\">Плохая</td>\n          <td class=\"positive\">Очень хорошая</td>\n          <td class=\"notice\">Неплохая</td>\n          <td class=\"warning\">Редкий</td>\n        </tr>\n\n        <tr class=\"c_c\">\n          <th>Камень</th>\n          <td>3500</td>\n          <td>4750</td>\n          <td class=\"notice\">Неплохая</td>\n          <td class=\"positive\">Хорошая</td>\n          <td class=\"notice\">Неплохая</td>\n          <td class=\"notice\">Продвинутый</td>\n        </tr>\n\n        <tr class=\"c_c\">\n          <th>Стандарт</th>\n          <td>4750</td>\n          <td>5750</td>\n          <td>Стандартная</td>\n          <td>Стандартная</td>\n          <td>Стандартная</td>\n          <td>Базовый</td>\n        </tr>\n\n        <tr class=\"c_c\">\n          <th>Руда</th>\n          <td>5750</td>\n          <td>7000</td>\n          <td class=\"positive\">Хорошая</td>\n          <td class=\"notice\">Неплохая</td>\n          <td class=\"notice\">Неплохая</td>\n          <td class=\"notice\">Продвинутый</td>\n        </tr>\n        <tr class=\"c_c\">\n          <th>Оливин</th>\n          <td>7000</td>\n          <td>8250</td>\n          <td class=\"positive\">Очень хорошая</td>\n          <td class=\"notice\">Неплохая</td>\n          <td class=\"warning\">Плохая</td>\n          <td class=\"warning\">Редкий</td>\n        </tr>\n        <tr class=\"c_c\">\n          <th>Металл</th>\n          <td>8250</td>\n          <td>9500</td>\n          <td class=\"positive\">Отличная</td>\n          <td class=\"error\">Очень плохая</td>\n          <td class=\"error\">Очень плохая</td>\n          <td class=\"error\">Раритет</td>\n        </tr>\n      </table><br />\n\n      Класс ядра, который можно найти в экспедиции, ограничен эффективным уровнем Астрокартографии (т.е. уровень с учётом всех бонусов: от родного мира, Премиум-аккаунта, внутриигровых акций итд).\n      Это сделано для облегчения игры для новичков и уравнивания стартовых условий\n      Например, если первая же найденная колония имеет тип ядра \"Водородный лёд\" - новый игрок получает сильное пенальти на развитие планеты (планеты \"Раритетного\" класса очень сложно застраивать сам-один и для нормальной застройки требуется транспорт других типов ресурсов с остальных планет).\n      <ul>\n        <li>Если эффективный уровень Астрокартографии меньше 6 - можно найти планеты с ядрами классов \"Стандарт\" и \"Продвинутый\";</li>\n        <li>6-10 - можно так же найти планеты с ядрами \"Редкого\" класса;</li>\n        <li>Более 11 - можно так же найти планеты с ядрами \"Раритетного\" класса.</li>\n      </ul>\n      <br />\n\n      Добыча считается в процентах от добычи указанного типа ресурсов на стандартном типе ядра в пересчете на металл приведенные к стоимости шахты:\n      <ul>\n        <li><span class=\"error\">Очень плохая</span> - менее 40%</li>\n        <li><span class=\"warning\">Плохая</span> - не меньше 40%, но менее 80% </li>\n        <li><span class=\"notice\">Неплохая</span> - не меньше 80%, меньше 100% </li>\n        <li>Неплохая - 100% базовой добычи</li>\n        <li><span class=\"ok\">Хорошая</span> - больше 100%, но меньше 300%</li>\n        <li><span class=\"ok\">Очень хорошая</span> - больше 300%, но меньше 400%</li>\n        <li><span class=\"ok\">Отличная</span> - больше 400%</li>\n      </ul><br/>\n      Класс типа ядра определяется в процентах от добычи всех ресурсов на стандартном типе ядра в пересчете на металл вычисленные для шахт эквивалентной стоимости предельно-достижимого уровня:\n      <ul>\n        <li>\"Базовый\" класс ядер - 100% от базовой добычи. Включает только ядра типа \"Стандарт\" - самый распространенный тип ядра планеты (примерно треть от всех планет)</li>\n        <li>\n          <span class=\"notice\">Продвинутый</span> - более 100%, но менее 150% от базовой добычи.\n            Включает в себя ядра типа \"Руда\", \"Камень\" и \"Водный лёд\".\n            Самый распространенный класс ядер - примерно половина планет содержат ядра этого класса;\n        </li>\n        <li><span class=\"warning\">Редкий</span> - не меньше 150%, но менее 250% от базовой добычи. Включает в себя ядра типа \"Оливин\", \"Силикат\" и \"Метановый лёд\". Как не сложно понять из названия класса - встречается редко;</li>\n        <li><span class=\"error\">Раритет</span> - не меньше 250% от базовой добычи. Включает в себя ядра типа \"Металл\", \"Кристалл\" и \"Водородный лёд\". Встречается ОЧЕНЬ редко - менее 5% планет содержат ядра этого класса.</li>\n      </ul><br />\n\n\n',\n/*\n      '<ul>\n        <li>\n          Базовый класс включает один тип ядра. Планеты с ядрами такого класса встречаются <span class=\"ok\">очень часто</span> - почти треть пригодных к колонизации планет обладают ядрами такого типа\n          <ul>\n            <li>\n              Плотность ядра типа \"Стандарт\" <span class=\"zero\">более 4750, но менее 5750</span> кг/м&sup3;\n            </li>\n            <li>\n              По химическому составу планеты с таким ядром сильно похожи на Землю\n            </li>\n            <li>\n              Своё название этот тип получил из-за того, что полезные ископаемые распределены на ней стандартно - нормальная добыча металлов, нормальная добыча кристаллов и нормальная добыча дейтерия.\n            </li>\n          </ul>\n          . Все стартовые планеты имеют стандартные ядра.\n          (плотность )\n          <br />\n          Из-за обилия водно-метанового льда и большого количества водорода в различных состояних, на ледяных планетах <span class=\"ok\">очень высокая добыча дейтерия</span>.\n          Есть и обратная сторона - из-за маленького количества более плотного вещества <span class=\"error\">очень низкая добыча кристаллов</span> и <span class=\"error\">очень низкая добыча металла</span>. Ледяные планеты встречаются <span class=\"error\">очень редко</span>.\n        </li>\n        <li>\n          <span class=\"ok\">Силикатные планеты</span> (плотность <span class=\"zero\">более 2000, но менее 3250 </span> кг/м&sup3;) встречаются <span class=\"warning\">редко</span>. На них\n          <span class=\"error\">очень низкая добыча металлов</span>, <span class=\"ok\">очень высокая добыча кристаллов</span> и чуть более низкая, чем обычно, но всё еще <span class=\"zero\">хорошая добыча дейтерия</span>.\n        </li>\n        <li>\n          <span class=\"ok\">Каменные планеты</span> (плотность <span class=\"zero\">более 3250, но менее 4500 </span> кг/м&sup3;) встречаются <span class=\"zero\">часто</span>. На них\n          слегка пониженная, но <span class=\"zero\">хорошая добыча металлов</span>, <span class=\"ok\">высокая добыча кристаллов</span> и <span class=\"warning\">пониженная добыча дейтерия</span>.\n        </li>\n        <li>\n          <span class=\"ok\">Стандарнтые планеты</span> (плотность <span class=\"zero\">более 4500, но менее 5750 </span> кг/м&sup3;) встречаются <span class=\"ok\">очень часто</span>.\n\n        </li>\n        <li>\n          <span class=\"ok\">Железнорудные планеты</span> (плотность <span class=\"zero\">более 5750, но менее 7000 </span> кг/м&sup3;) встречаются <span class=\"zero\">часто</span>.\n          На них <span class=\"ok\">очень хорошая добыча металлов</span>, <span class=\"warning\">пониженная добыча кристаллов</span> и <span class=\"zero\">пониженная добыча дейтерия</span>.\n        </li>\n        <li>\n          <span class=\"ok\">Металлические планеты</span> (плотность <span class=\"zero\">более 7000, но менее 8250 </span> кг/м&sup3;) встречаются <span class=\"warning\">редко</span>.\n          На них <span class=\"ok\">отличная добыча металлов</span>, <span class=\"warning\">низкая добыча кристаллов</span> и <span class=\"zero\">низкая добыча дейтерия</span>.\n        </li>\n        <li>\n          <span class=\"ok\">Тяжелометаллические планеты</span> (плотность <span class=\"zero\">более 8250 </span> кг/м&sup3;) встречаются <span class=\"error\">очень редко</span>.\n          На них <span class=\"ok\">великолепная добыча металлов</span>, <span class=\"error\">очень низкая добыча кристаллов</span> и <span class=\"error\">очень низкая добыча дейтерия</span>.\n        </li>\n      </ul>\n\n      <ul>\n        <li>\n          <span class=\"ok\">Ледяные планеты</span> (плотность <span class=\"zero\">менее 2000</span> кг/м&sup3;).\n          Из-за обилия водно-метанового льда и большого количества водорода в различных состояних, на ледяных планетах <span class=\"ok\">очень высокая добыча дейтерия</span>.\n          Есть и обратная сторона - из-за маленького количества более плотного вещества <span class=\"error\">очень низкая добыча кристаллов</span> и <span class=\"error\">очень низкая добыча металла</span>. Ледяные планеты встречаются <span class=\"error\">очень редко</span>.\n        </li>\n        <li>\n          <span class=\"ok\">Силикатные планеты</span> (плотность <span class=\"zero\">более 2000, но менее 3250 </span> кг/м&sup3;) встречаются <span class=\"warning\">редко</span>. На них\n          <span class=\"error\">очень низкая добыча металлов</span>, <span class=\"ok\">очень высокая добыча кристаллов</span> и чуть более низкая, чем обычно, но всё еще <span class=\"zero\">хорошая добыча дейтерия</span>.\n        </li>\n        <li>\n          <span class=\"ok\">Каменные планеты</span> (плотность <span class=\"zero\">более 3250, но менее 4500 </span> кг/м&sup3;) встречаются <span class=\"zero\">часто</span>. На них\n          слегка пониженная, но <span class=\"zero\">хорошая добыча металлов</span>, <span class=\"ok\">высокая добыча кристаллов</span> и <span class=\"warning\">пониженная добыча дейтерия</span>.\n        </li>\n        <li>\n          <span class=\"ok\">Стандарнтые планеты</span> (плотность <span class=\"zero\">более 4500, но менее 5750 </span> кг/м&sup3;) встречаются <span class=\"ok\">очень часто</span>. По химическому составу они сильно похожи на Землю.\n          Полезные ископаемые распределены на ней стандартно - <span class=\"zero\">хорошая добыча металлов</span>, <span class=\"zero\">хорошая добыча кристаллов</span> и <span class=\"zero\">хорошая добыча дейтерия</span>.\n        </li>\n        <li>\n          <span class=\"ok\">Железнорудные планеты</span> (плотность <span class=\"zero\">более 5750, но менее 7000 </span> кг/м&sup3;) встречаются <span class=\"zero\">часто</span>.\n          На них <span class=\"ok\">очень хорошая добыча металлов</span>, <span class=\"warning\">пониженная добыча кристаллов</span> и <span class=\"zero\">пониженная добыча дейтерия</span>.\n        </li>\n        <li>\n          <span class=\"ok\">Металлические планеты</span> (плотность <span class=\"zero\">более 7000, но менее 8250 </span> кг/м&sup3;) встречаются <span class=\"warning\">редко</span>.\n          На них <span class=\"ok\">отличная добыча металлов</span>, <span class=\"warning\">низкая добыча кристаллов</span> и <span class=\"zero\">низкая добыча дейтерия</span>.\n        </li>\n        <li>\n          <span class=\"ok\">Тяжелометаллические планеты</span> (плотность <span class=\"zero\">более 8250 </span> кг/м&sup3;) встречаются <span class=\"error\">очень редко</span>.\n          На них <span class=\"ok\">великолепная добыча металлов</span>, <span class=\"error\">очень низкая добыча кристаллов</span> и <span class=\"error\">очень низкая добыча дейтерия</span>.\n        </li>\n      </ul>',\n*/\n    ),\n\n    UNIT_CAN_NOT_BE_BUILD => array(\n      'description'       => 'Этот юнит не может быть построен игроком в настоящее время. Нет, его нельзя купить. Возможно, его можно было купить/построить раньше. И нет, не спрашивайте можно ли будет его построить когда-либо или получить как-нибудь. Сие неизвестно есть<br/>Хотя... может быть его можно получить другими способами? Спросите у других игроков... У АД-ии спрашивать бесполезно',\n      'description_short' => 'В настоящее время этот юнит невозможно построить или купить',\n      'effect'            => 'Этот юнит невозможно построить или купить',\n    ),\n  )\n);\n"
  },
  {
    "path": "language/ru/language.mo.php",
    "content": "<?php\n/*\n#############################################################################\n#  Filename: language.mo\n#  Create date: Sunday, March 30, 2008   19:56:23\n#  Project: SuperNova.WS\n#  Description: Massive Multiplayer Online Browser Space Strategy Game\n#\n#  Copyright © 2011-2018 Gorlum for Project \"SuperNova.WS\"\n#############################################################################\n*/\n\n/**\n*\n* system [Russian]\n*\n* @package language\n* @version $Id$\n*\n*/\n\n/**\n* DO NOT CHANGE\n*/\nif (!defined('INSIDE'))\n{\n  exit;\n}\n\n$lang_info = array(\n  'LANG_VERSION'      => 'V26e20',\n\n  'LANG_NAME_NATIVE'  => 'Русский',\n  'LANG_NAME_ENGLISH' => 'Russian',\n  'LANG_FLAG'         => 'ru.png',\n  'LANG_FLAG_MEDIUM'  => 'ru_medium.png',\n  'LANG_NAME_ISO2'    => 'ru',\n  'LANG_NAME_ISO3'    => 'rus',\n  'LANG_DIRECTION'    => 'ltr',\n\n  'LANG_MAINTAINER'   => 'Gorlum',\n  'LANG_HOMEPAGE'     => 'http://forum.supernova.ws/viewforum.php?f=73',\n\n  'LANG_COPYRIGHT'    => array(\n    'Copyright &copy; 2009 Gorlum for Project &quot;SuperNova.WS&quot;',\n    'Copyright &copy; 2009 MSW',\n  ),\n);\n"
  },
  {
    "path": "language/ru/login.mo.php",
    "content": "<?php\n\n/*\n#############################################################################\n#  Filename: login.mo\n#  Project: SuperNova.WS\n#  Website: http://www.supernova.ws\n#  Description: Massive Multiplayer Online Browser Space Strategy Game\n#\n#  Copyright © 2009-2018 Gorlum for Project \"SuperNova.WS\"\n#############################################################################\n*/\n\n/**\n*\n* @package language\n* @system [Russian]\n* @version 45d0\n*\n*/\n\n/**\n* DO NOT CHANGE\n*/\n\nif (!defined('INSIDE')) die();\n\nglobal $config;\n\n$a_lang_array = (array(\n  'Login' => 'Логин',\n  'User_name' => 'Имя:',\n  'Authorization' => 'Авторизация',\n  'Please_Login' => 'Пожалуйста <a href=\"login.php\" target=\"_main\">войдите...</a>',\n  'Please_Wait' => 'Подождите',\n  'Remember_me' => 'Запомнить меня',\n  'Register' => 'Информация о ошибке',\n  'Login_Error' => 'Ошибка',\n  'PleaseWait' => 'Подождите',\n  'PasswordLost' => 'Восстановить пароль',\n  'Login_Ok' => 'Успешное подключение, <a href=\"./\"><blink>перенаправление...</blink></a><br><center><img src=\"design/images/progressbar.gif\"></center>',\n  'Login_FailPassword' => 'Неверное имя и/или пароль<br /><a href=\"login.php\" target=\"_top\">Назад</a>',\n  'Login_FailUser' => 'Такого игрока не существует.<br><a href=login.php>Назад</a>',\n  'log_univ' => 'Добро пожаловать в нашу Вселенную!',\n  'log_reg' => 'Зарегистрироваться',\n  'log_reg_main' => 'Зарегистрироваться',\n  'log_menu' => 'Меню',\n  'log_stat_menu' => 'Статистика',\n  'log_enter' => 'Войти',\n  'log_news' => 'Новости сервера',\n  'log_cred' => 'О сервере',\n  'log_faq' => 'FAQ по игре',\n  'log_forums' => 'Форум',\n  'log_contacts' => 'Администрация',\n  'log_desc' => '<strong>СуперНова — это онлайновая мультиплеерная космическая браузерная стратегия.</strong> Тысячи игроков выступают одновременно против друг друга. Для игры Вам нужен лишь обычный браузер.',\n  'log_toreg' => 'Зарегистрируйся сейчас!',\n  'log_online' => 'Игроков Онлайн',\n  'log_lastreg' => 'Новичок',\n  'log_numbreg' => 'Всего аккаунтов',\n  'log_welcome' => 'Добро пожаловать в',\n  'vacation_mode' => 'Вы в отпуске<br> отключить режим отпуска можно через ',\n  'hours' => ' часов',\n  'vacations' => 'Режим отпуска',\n  'log_scr1' => 'Скриншот верфи, здесь строятся и заказываются корабли на текущей планете. Нажмите на изображение чтобы увеличить.',\n  'log_scr2' => 'Скриншот статистики, здесь здесь показывается ваш рейтинг среди других игроков по различным параметрам. Нажмите на изображение чтобы увеличить.',\n  'log_scr3' => 'Скриншот вселенной, здесь можно увидеть вашу планету во вселенной, а также найти планеты других игроков. Нажмите на изображение чтобы увеличить.',\n  'log_rules' => 'Правила игры',\n  'log_banned' => 'Список забаненных',\n  'log_see_you' => 'Надеемся вас снова увидеть на просторах нашей Вселенной. Удачи!<br><a href=\"login.php\">Перейти на страницу входа в игру</a>',\n  'log_session_closed' => 'Сессия закрыта.',\n  'registry' => 'Регистрация',\n  'form' => 'Форма регистрации',\n  'Undefined' => '- неопределённый -',\n  'Male' => 'Мужской',\n  'Female' => 'Женский',\n  'Multiverse' => 'XNova',\n  'E-Mail' => 'Адрес e-Mail',\n  'MainPlanet' => 'Имя главной планеты',\n  'GameName' => 'Имя',\n  'gender' => 'Пол',\n  'accept' => 'Я согласен с правилами',\n  'reg_i_agree' => 'Я ознакомился и согласен с',\n  'reg_with_rules' => 'правилами игры',\n  'signup' => 'Зарегистрироваться',\n  'Languese' => 'Язык',\n  'log_reg_text0' => 'Перед регистрацией ознакомьтесь с',\n  'log_reg_text1' => 'Регистрация означает, что вы полность прочли и согласились со всеми пунктами правил. Если вы не согласны хоть с каким-то пунктом правил - пожалуйста, не регестрируйтесь.',\n  'thanksforregistry' => 'Поздравляем вас с успешной регистрацией! Вы будете перенаправлены на главную страницу вашей планеты через 10 секунд, если этого не произошло нажмите на эту <a href=overview.php><u>ссылку!</u></a>',\n  'welcome_to_universe' => 'Добро пожаловать в OGame!!!',\n  'please_click_url' => 'Для того чтобы использовать аккаунт, вы должны активировать его нажав на эту ссылку',\n  'regards' => 'Удачи!',\n  'error_lang' => 'Этот язык не поддерживается!<br />',\n  'error_mail' => 'Неверный E-Mail !<br />',\n  'error_planet' => 'Другая планета уже имеет то же название !<br />',\n  'error_hplanetnum' => 'Название планеты должно быть написано ТОЛЬКО латинскими буквами !<br />',\n  'error_character' => 'Неверное имя !<br />',\n  'error_charalpha' => 'Вы можете использовать ТОЛЬКО латинские буквы !<br />',\n  'error_password' => 'Пароль должен состоять как минимум из 4 знаков !<br />',\n  'error_rgt' => 'Вы должны согласиться с правилами !<br />',\n  'error_userexist' => 'Такое имя уже используется !<br />',\n  'error_emailexist' => 'Такой e-mail уже используется !<br />',\n  'error_sex' => 'Ошибка в выборе пола !<br />',\n  'error_mailsend' => 'Ошибка в отправлении электронной почты, ваш пароль: ',\n  'reg_welldone' => 'Регистрация завершена! Ваш пароль отправлен на указанный при регистрации почтовый ящик. Вот он же еще раз на всякий случай:<br>',\n  'error_captcha' => 'Неверный графический код !<br/>',\n  'error_v' => 'Повторить еще раз !<br />',\n  'log_login_page' => 'Войти в игру',\n  'log_reg_already' => 'Уже есть регистрация? Воспользутесь ссылкой ',\n  'log_reg_already_lost' => 'Не помните пароль? Воспользутесь ссылкой ',\n\n  'log_lost_header' => 'Сброс пароля',\n  'log_lost_code' => 'Код подтверждения',\n  'log_lost_description2' => 'Если у Вас есть код подтверждения - введите его ниже и нажмите кнопку \"Сбросить пароль\". На Ваш e-mail будет отправлено письмо с новым паролем<br /><br />\n    Если вы уже запрашивали код подтвеждения, но не видите письмо в почтовом ящике - проверьте папку нежелательных писем (папка \"спама\"). Ваш почтовый сервер мог отметить наше письмо как \"нежелательное\".<br /><span style=\"color: red;\">ВНИМАНИЕ! mail.ru и его проекты банит письма от игры! Пишите на почту админу - см.ниже</span><br /><br />\n    Если и там нет письма - напишите письмо Администрации сервера на адрес <span class=\"ok\">' . $config->server_email . '</span>',\n  'log_lost_reset_pass' => 'Сбросить пароль',\n  'log_lost_send_mail' => 'Отправить код подтверждения',\n  'log_lost_sent_code' => 'На указанный емейл отправлено письмо с дальнейшими инструкциями по сбросу пароля',\n  'log_lost_sent_pass' => 'Так же на Ваш емейл отправлено письмо с новым паролем',\n\n  'log_lost_err_email' => 'Указанный емейл не зарегестрирован в базе данных. Это может означать одно из нижеперечисленного:<br>Вы ошиблись при вводе емейла. Вернитесь на предыдущую страницу и попробуйте еще раз<br>Ваш аккаунт был удален из-за неактивности. Зарегестрируйтесь заново<br>Вы пытаетесь зайти в неправильную игровую Вселенную. Внимательно проверьте название текущей Вселенной и в случае ошибки зайдите на правильную Вселенную',\n  'log_lost_err_sending' => 'Ошибка отправки сообщения по указанному емейлу. Сообщите об ошибке Администратору',\n  'log_lost_err_code' => 'Указанный код подтверждения не зарегестрирован в базе данных. Это может означать одно из нижеперечисленного:<br>Вы ошиблись при вводе кода подтверждения. Вернитесь на предыдущую страницу и внимательно введите код<br>Вы пытаетесь ввести код подтверждения не в той Вселенной, для которой он был сгенерирован. Внимательно проверьте название текущей Вселенной и в случае ошибки зайдите на правильную Вселенную<br>Ваш аккаунт был удален из-за неактивности. Зарегестрируйтесь заново<br>Истек срок действия кода подтверждения. Проверьте дату действия кода в письме. Если она прошла, запросите новый код подтверждения',\n  'log_lost_err_admin' => 'Члены Команды сервера (модераторы, операторы, администраторы итд) не могут использовать функцию сброса пароля. Обратитесь к Администратору сервера для смены пароля',\n  'log_lost_err_change' => 'Ошибка смены пароля в базе данных. Сообщите об ошибке Администратору',\n\n  'log_lost_description1' => 'Введите основной e-mail, на который зарегистрирован Ваш аккаунт. На него будет отправлено письмо с кодом подтверждения для сброса пароля',\n  'login_register_offer' => 'Нажмите здесь, что бы зарегестрироваться',\n  'login_password_restore_offer' => 'Нажмите здесь, что бы сбросить пароль',\n\n  'login_register_email_hint' => 'Указывайте работающий e-mail - владельцем аккаунта считается владелец указанного e-mail<br />\n    <span style=\"color: red;\">ВНИМАНИЕ! Не используйте почтовые ящики на \"@mail.ru\" и его проектов! Вы не будете получать письма от игры!</span>',\n\n  'login_account_name_or_email' => 'Е-мейл',\n\n));\n"
  },
  {
    "path": "language/ru/market.mo.php",
    "content": "<?php\n\n/*\n#############################################################################\n#  Filename: market.mo\n#  Project: SuperNova.WS\n#  Website: http://www.supernova.ws\n#  Description: Massive Multiplayer Online Browser Space Strategy Game\n#\n#  Copyright © 2009-2018 Gorlum for Project \"SuperNova.WS\"\n#############################################################################\n*/\n\n/**\n*\n* @package language\n* @system [Russian]\n* @version 43a16.13\n* @condition clear\n*\n*/\n\n/**\n* DO NOT CHANGE\n*/\n\nif (!defined('INSIDE')) die();\n\n$a_lang_array = (array(\n  'eco_mrk_title' => 'Чёрный Рынок',\n  'eco_mrk_description' => 'Странно, но в описании к интерфейсу управления Империей не было такого пункта... Интересно, откуда он взялся?',\n  'eco_mrk_service' => 'Услуга',\n  'eco_mrk_service_cost' => 'Стоимость услуги',\n\n  'eco_mrk_trader_do' => 'Обменять ресурсы',\n  'eco_mrk_trader' => 'Обмен ресурсов',\n  'eco_mrk_trader_cost' => 'Стоимость обмена ресурсов',\n  'eco_mrk_trader_exchange' => 'Количество обмениваемых ресурсов',\n  'eco_mrk_trader_to' => 'Обменять на',\n  'eco_mrk_trader_course' => 'Курс',\n  'eco_mrk_trader_left' => 'Итог операции',\n  'eco_mrk_trader_resources_all' => 'Все ресурсы',\n  'eco_mrk_trader_exchange_dm_confirm' => 'Вы точно хотите обменять {0} Тёмной Материи на ресурсы?',\n\n  'eco_mrk_scraper_do' => 'Продать корабли скупщику',\n  'eco_mrk_scraper' => 'Скупка кораблей',\n  'eco_mrk_scraper_price' => 'Выход лома',\n  'eco_mrk_scraper_perShip' => 'с корабля',\n  'eco_mrk_scraper_total' => 'Всего',\n  'eco_mrk_scraper_cost' => 'Продать корабли на лом стоит',\n  'eco_mrk_scraper_onOrbit' => 'На орбите',\n  'eco_mrk_scraper_to' => 'Пустить на слом',\n  'eco_mrk_scraper_res' => 'Получен следующий лом:',\n  'eco_mrk_scraper_ships' => 'Пущены на лом следующие корабли:',\n  'eco_mrk_scraper_noShip' => 'На орбите нет кораблей',\n\n  'eco_mrk_stockman_do' => 'Купить корабли у скупщика',\n  'eco_mrk_stockman' => 'Продавец б/у кораблей',\n  'eco_mrk_stockman_price' => 'Цена',\n  'eco_mrk_stockman_perShip' => 'корабля',\n  'eco_mrk_stockman_onStock' => 'У продавца',\n  'eco_mrk_stockman_buy' => 'Купить корабли',\n  'eco_mrk_stockman_res' => 'Стоимость купленных кораблей:',\n  'eco_mrk_stockman_ships' => 'Куплены следующие корабли:',\n  'eco_mrk_stockman_noShip' => 'У продавца сейчас нет кораблей для продажи',\n\n  'eco_mrk_exchange' => 'Биржа обмена ресурсов',\n  'eco_mrk_banker'   => 'Банкир',\n  'eco_mrk_pawnshop' => 'Ломбард',\n\n  'eco_mrk_info_do' => 'Купить информацию',\n  'eco_mrk_info' => 'Продавец информации',\n  'eco_mrk_info_description' => 'Во входящем почтовом ящике обнаружилось письмо со следующим содержанием:',\n  'eco_mrk_info_description_2' => 'У меня есть доступ ко множеству интересной информации. Я могу поделиться ею с вами... за скромное вознаграждение. За один запрос - всего',\n  'eco_mrk_info_buy' => 'Купить информацию',\n\n  'eco_mrk_info_player' => 'Сведения об игроке',\n  'eco_mrk_info_player_description' => 'Я могу узнать, какие наемники сейчас работают у игрока',\n  'eco_mrk_info_player_message' => 'По моим достоверным сведениям, список наемников у игрока ID %1$d [%2$s] выглядит следующим образом:',\n\n  'eco_mrk_info_not_hired' => 'не нанят',\n\n  'eco_mrk_info_ally' => 'Сведения об Альянсе',\n  'eco_mrk_info_online' => 'Текущая активность во Вселенной',\n\n  'eco_mrk_info_msg_from' => 'Неотслеживаемый источник',\n\n  'eco_mrk_error_title' => 'Чёрный Рынок - Ошибка',\n  'eco_mrk_errors' => array(\n    MARKET_RESOURCES => 'Операция прошла успешно',\n    MARKET_SCRAPPER => 'Обмен ресурсов произошел успешно',\n    MARKET_NOT_A_SHIP => 'Не надо пытаться продать что-нибудь, отличное от корабля!',\n    MARKET_STOCKMAN => 'Не хватает Тёмной Материи для завершения операции',\n    MARKET_NO_RESOURCES => 'Не хватает ресурсов для завершения операции',\n    MARKET_PAWNSHOP => 'Вы пытаетесь пустить на лом больше кораблей, чем есть на орбите',\n    MARKET_NO_STOCK => 'Вы пытаетесь купить больше кораблей, чем есть у продавца. Возможно, пока вы выбирали корабли, кто-то другой уже купил их',\n    MARKET_ZERO_DEAL => 'Не указано количество ресурсов для обмена',\n    MARKET_NOTHING => 'Нужно выбрать корабли для продажи',\n    MARKET_ZERO_RES_STOCK => 'Нужно выбрать корабли для покупки',\n    MARKET_NEGATIVE_SHIPS => 'Не надо пытаться продать отрицательное количество кораблей!',\n\n    MARKET_NO_DM => 'Не хватает Тёмной Материи для завершения операции',\n    MARKET_INFO_WRONG => 'Нет такой информации',\n    MARKET_INFO_PLAYER => 'Информация куплена успешно. Проверьте свой почтовый ящик',\n    MARKET_INFO_PLAYER_WRONG => 'Нужно указать ID или имя игрока',\n    MARKET_INFO_PLAYER_NOT_FOUND => 'Не могу идентифицировать игрока. Если имя игрока состоит из цифр или нечитаемо - попробуйте использовать его ID',\n    MARKET_INFO_PLAYER_SAME => 'Зачем узнавать информацию о самом себе?',\n  ),\n\n));\n"
  },
  {
    "path": "language/ru/menu.mo.php",
    "content": "<?php\n\n/*\n#############################################################################\n#  Filename: leftmenu.mo\n#  Project: SuperNova.WS\n#  Website: http://www.supernova.ws\n#  Description: Massive Multiplayer Online Browser Space Strategy Game\n#\n#  Copyright © 2009-2018 Gorlum for Project \"SuperNova.WS\"\n#############################################################################\n*/\n\n/**\n*\n* @package language\n* @system [Russian]\n* @version 43a16.13\n*\n*/\n\n/**\n* DO NOT CHANGE\n*/\n\nif (!defined('INSIDE')) die();\n\n$a_lang_array = (array(\n  'Multiverse' => '<b>Сервер</b> Uni',\n  'm_h_rules' => 'Правила',\n  'm_faq' => 'Как играть?',\n  'm_faq_hint' => 'Наиболее часто задаваемые вопросы пользователей и ответы на них',\n  'm_h_control' => 'Управление',\n  'm_forum' => 'Форум',\n  'm_others' => 'Прочее',\n  'm_simulator' => 'Симулятор боя',\n  'm_communication' => 'Коммуникации',\n  'm_exchange' => 'Биржа ресурсов',\n  'm_affilates' => 'ЗАРАБОТАЙ ТМ!',\n  'Overview' => 'Обзор планеты',\n  'Officiers' => 'Офицеры',\n//  'Buildings' => 'Постройки',\n  'Building' => 'Строительство',\n  'Research' => 'Исследования',\n  'Shipyard' => 'Верфь',\n  'Defense' => 'Оборона',\n  'Resources' => 'Ресурсы',\n  'Imperium' => 'Империя',\n  'Marchand' => 'Торговец',\n  'Annonces' => 'Объявления',\n  'Technology' => 'Технологии',\n  'Galaxy' => 'Галактика',\n  'lm_fleet_orbiting' => 'Флот на орбите',\n  'Alliance' => 'Ваш Альянс',\n  'Allianc' => 'Альянс',\n  'AllyChat' => 'Чат Альянса',\n  'Statistics' => 'Статистика',\n  'Search' => 'Поиск',\n  'Records' => 'Рекорды',\n  'Messages' => 'Сообщения',\n  'Notes' => 'Заметки',\n  'Buddylist' => 'Друзья',\n  'Chat' => 'Чат',\n  'Contact' => 'Администрация',\n  'Options' => 'Настройки',\n  'Bug' => 'Тех. помощь',\n  'Logout' => 'Выход',\n  'Rules' => 'Правила',\n  'devlp' => 'Развитие',\n  'navig' => 'Информация',\n  'Economy' => 'Экономика',\n  'trade' => 'Торговля',\n  'Society' => 'Общество',\n  'rinok' => 'Чёрный рынок',\n  'observ' => 'Обсерватория',\n  'commun' => 'Администрация',\n  'infog' => 'Информация',\n  'lm_combat_reports' => 'Боевые отчеты',\n  'adm_over' => 'Обзор',\n  'adm_conf' => 'Настройки',\n  'adm_reset' => 'Обнуление',\n  'adm_plrlst' => 'Список игроков',\n  'adm_panel' => 'Панель администратора',\n  'adm_plrsch' => 'Поиск игрока',\n  'adm_addres' => 'Добавить ресурсы',\n  'adm_pltlst' => 'Список планет',\n  'adm_actplt' => 'Активные планеты',\n  'adm_moonlst' => 'Список лун',\n  'adm_addmoon' => 'Добавить луну',\n  'adm_fleet' => 'Флоты в полёте',\n  'adm_ban' => 'Забанить',\n  'adm_unban' => 'Разбанить',\n  'adm_chat' => 'Редактор чата',\n  'adm_updpt' => 'Обновить стату',\n  'adm_msg' => 'Список сообщений',\n  'adm_md5' => 'Шифрование',\n  'adm_updrank' => 'Обнуление базы',\n  'adm_log_main' => 'Записи логов',\n  'adm_help' => 'Форум разработчиков',\n  'adm_back' => 'Вернуться',\n  'admin' => 'Администрирование',\n  'player' => 'Игроки',\n  'tool' => 'Утилиты',\n  'lm_ifo_serv' => 'Сырьё',\n  'lm_ifo_game' => 'Игра',\n  'lm_ifo_fleet' => 'Флот',\n  'lm_ifo_queue' => 'Очередь',\n  'lm_shortcuts' => 'Закладки',\n  'lm_banned' => 'Список банов',\n  'lm_announce_fresh' => 'СВЕЖИЕ',\n  'lm_server_info' => 'О сервере',\n\n  'menu_quest_list' => 'Список квестов',\n  'menu_universe_overview' => 'Обзор Вселенной',\n  'menu_stat_players' => 'Статистика',\n  'menu_stat_records' => 'Рекорды',\n  'menu_races' => 'Родные миры',\n  'menu_metamatter' => 'Метаматерия',\n\n  'menu_hide' => '<<',\n  'menu_show' => '>>',\n\n  'menu_pin' => 'Закрепить меню',\n  'menu_unpin' => 'Открепить меню',\n\n  'matter_analyze' => 'Баланс материи',\n));\n"
  },
  {
    "path": "language/ru/messages.mo.php",
    "content": "<?php\n\n/*\n#############################################################################\n#  Filename: messages.mo\n#  Project: SuperNova.WS\n#  Website: http://www.supernova.ws\n#  Description: Massive Multiplayer Online Browser Space Strategy Game\n#\n#  Copyright © 2009-2018 Gorlum for Project \"SuperNova.WS\"\n#############################################################################\n*/\n\n/**\n*\n* @package language\n* @system [Russian]\n* @version 45d0\n*\n*/\n\n/**\n* DO NOT CHANGE\n*/\n\nif (!defined('INSIDE')) die();\n\n$a_lang_array = [\n  'msg_page_header' => 'Личные сообщения',\n  'msg_head_type' => 'Категории',\n  'msg_head_count' => 'Непрочитанные',\n  'msg_head_total' => 'Всего',\n  'msg_mark_select' => '-- ВЫБЕРИТЕ ДИАПАЗОН --',\n  'msg_mark_checked' => 'Отмеченные сообщения',\n  'msg_mark_unchecked' => 'Неотмеченные сообщения',\n  'msg_mark_class' => 'Все сообщения категории',\n  'msg_mark_all' => 'ВСЕ личные сообщения',\n  'msg_select_all' => 'Выбрать все',\n  'msg_delete_checked' => 'Удалить отмеченные сообщения',\n  'msg_show_all' => 'Показать все',\n  'msg_date' => 'Дата',\n  'msg_from' => 'От',\n  'msg_recipient' => 'Кому',\n  'msg_subject' => 'Тема сообщения',\n  'msg_answer' => 'Ответить',\n  'msg_answer_prefix' => 'RE:',\n  'msg_compose' => 'Написать сообщение',\n  'msg_text' => 'Текст сообщения',\n  'msg_subject_default' => 'Новое сообщение',\n  'msg_not_message_sent' => 'Сообщение отправлено',\n  'msg_warn_no_messages' => 'Нет сообщений в данной категории',\n  'msg_err_player_not_found' => 'Игрок не найден',\n  'msg_err_no_text' => 'Нельзя отправить пустое сообщение',\n  'msg_err_self_send' => 'Нельзя отправить сообщение самому себе',\n  'msg_del_class' => 'Удалить все сообщения в этой категории',\n  'msg_page_hint_class' =>\n    '<ul>\n      <li>Категория \"Отправленные сообщения\" содержит список отправленных вами сообщений И еще не удаленных адресатом. Вы не можете удалять сообщения данной категории</li>\n      <li>Что бы удалить все сообщения определенной категории нажмите иконку удаления в соответствующей строке</li>\n      <li>Удаление сообщений из категории \"Все сообщения\" приведет к очистке почтового ящика</li>\n      <li>Медленное соединение и/или большое количество сообщений в одной из категорий может привести к невозможности просмотра сообщений. В таком случае вам необходимо очистить почтовый ящик целиком  или удалить все сообщения из категории, вызывающей проблемы</li>\n    </ul>',\n  'msg_header_dialog' => 'Диалог с',\n\n  'msg_ignore' => 'Игнорировать',\n  'msg_ignore_title' => \"Добавить игрока [PLAYER_NAME] в игнор-лист?\",\n  'msg_ignore_message' => \"Вы больше не увидите личных сообщений от игрока в игнор-листе.<br><br>Вы можете управлять своим игнор-листом на странице 'Настройки'.<br><br>Добавить игрока [PLAYER_NAME] в игнор-лист?\",\n  'msg_message' => 'Сообщение',\n  'msg_ignored_messages' => 'сообщений от пользователей в игнор-листе не показано',\n  'msg_ignore_control' => 'Вы можете управлять игнор-листом на странице \"Настройки\"',\n];\n"
  },
  {
    "path": "language/ru/mrc_mercenary.mo.php",
    "content": "<?php\n\n/*\n#############################################################################\n#  Filename: mercenary.mo\n#  Project: SuperNova.WS\n#  Website: http://www.supernova.ws\n#  Description: Massive Multiplayer Online Browser Space Strategy Game\n#\n#  Copyright © 2009-2018 Gorlum for Project \"SuperNova.WS\"\n#############################################################################\n*/\n\n/**\n*\n* @package language\n* @system [Russian]\n* @version 43a16.13\n*\n* @clean - all constants is used\n*\n*/\n\n/**\n* DO NOT CHANGE\n*/\n\nif (!defined('INSIDE'))\n{\n  exit;\n}\n\n$a_lang_array = (array(\n  'mrc_up_to' => 'до',\n  'mrc_hire' => 'Нанять',\n  'mrc_hire_for' => 'Нанять за',\n  'mrc_allowed' => 'Доступные',\n  'mrc_msg_error_wrong_mercenary' => 'Неправильный идентификатор Наёмника',\n  'mrc_msg_error_wrong_level' => 'Неправильный уровень Наёмника',\n  'mrc_msg_error_wrong_period' => 'Недопустимый срок найма',\n  'mrc_msg_error_already_hired' => 'Наёмник уже рекрутирован. Увольте его или дождитесь окончания срока найма',\n  'mrc_msg_error_no_resource' => 'Не хватает Тёмной Материи для найма',\n  'mrc_msg_error_requirements' => 'Не удовлетворены требования для найма',\n\n  'mrc_dismiss' => 'Уволить',\n  'mrc_dismiss_confirm' => 'При увольнении Наёмника теряется вся ТМ, раннее затраченная на его найм! Вы точно хотите уволить Наёмника?',\n  'mrc_dismiss_before_hire' => 'Что бы изменить уровень рекрутированного Наёмника нужно сначала уволить текущего - с потерей потраченных на найм ТМ',\n\n  'mrc_mercenary_hired_log' => 'Куплен Наёмник \"%1$s\" ID %2$d за %3$d ТМ на срок %4$d дней',\n  'mrc_mercenary_dismissed_log' => 'ПОТЕРЯНО %7$d дней найма и %8$d ТМ по текущим ценам! Уволен Наёмник \"%1$s\" ID %2$d, нанятый на срок %4$d дней (с %5$s по %6$s) и стоящий сейчас %3$d ТМ',\n  'mrc_plan_bought_log' => 'Куплен \"%1$s\" ID %2$d за %3$d ТМ',\n));\n"
  },
  {
    "path": "language/ru/notes.mo.php",
    "content": "<?php\n\n/*\n#############################################################################\n#  Filename: notes.mo\n#  Project: SuperNova.WS\n#  Website: http://www.supernova.ws\n#  Description: Massive Multiplayer Online Browser Space Strategy Game\n#\n#  Copyright © 2009-2018 Gorlum for Project \"SuperNova.WS\"\n#############################################################################\n*/\n\n/**\n*\n* @package language\n* @system [Russian]\n* @version 43a16.13\n*\n*/\n\n/**\n* DO NOT CHANGE\n*/\n\n!defined('INSIDE') && die();\n\n$a_lang_array = (array(\n  'note_page_header' => 'Заметки',\n  'note_date' => 'Дата',\n  'note_note' => 'Заметка',\n  'note_priority' => 'Важность',\n  'note_sticky' => 'Прилепленная',\n  'note_new_title' => 'Заголовок новой заметки',\n  'note_new_text' => 'Текст новой заметки',\n  'note_stick_it' => 'Прилепить заметку под навбар на каждой странице',\n\n  'note_err_none_added' => 'Заметка успешно добавлена',\n  'note_err_none_changed' => 'Заметка успешно изменена',\n  'note_err_note_not_found' => 'Заметка с таким ID не найдена. Возможно, её удалили в соседнем окне',\n  'note_err_owner_wrong' => 'Вы не являетесь владельцем этой заметки',\n  'note_err_note_empty' => 'Вы ничего не написали в заметке - она не будет добавлена',\n\n  'note_delete' => 'Удалить заметки',\n  'note_range_select' => '-- ВЫБЕРИТЕ ДИАПАЗОН --',\n  'note_range_marked' => 'Отмеченные заметки',\n  'note_range_marked_not' => 'Неотмеченые заметки',\n  'note_range_all' => 'Все заметки',\n\n  'note_warn_no_range' => 'Вы не выбрали диапазон. Нечего удалять',\n  'note_err_none_selected' => 'Не выбрано ни одной заметки - удаление не производится. Для удаления всех заметок выберите диапазон \"Все заметки\"',\n\n));\n"
  },
  {
    "path": "language/ru/options.mo.php",
    "content": "<?php\n/** @noinspection HtmlUnknownTarget */\n\n/*\n#############################################################################\n#  Filename: options.mo\n#  Project: SuperNova.WS\n#  Website: http://www.supernova.ws\n#  Description: Massive Multiplayer Online Browser Space Strategy Game\n#\n#  Copyright © 2009-2018 Gorlum for Project \"SuperNova.WS\"\n#############################################################################\n*/\n\n/**\n*\n* @package language\n* @system [Russian]\n* @version 45d0\n*\n*/\n\n/**\n* DO NOT CHANGE\n*/\n\nif (!defined('INSIDE')) die();\n\n$a_lang_array = [\n  'opt_account' => 'Профиль',\n  'opt_int_options' => 'Интерфейс',\n  'opt_settings_statistics' => 'Статистика игрока',\n  'opt_settings_info' => 'Информация об игроке',\n  'opt_alerts' => 'Уведомления',\n  'opt_common' => 'Общие',\n  'opt_tutorial' => 'Обучение',\n\n  'opt_birthday' => 'День рождения',\n\n  'opt_header' => 'Настройки пользователя',\n  'opt_messages' => 'Автоматические уведомления',\n  'opt_msg_saved' => 'Настройки успешно изменены',\n  'opt_msg_name_changed' => 'Имя пользователя успешно изменено',\n  'opt_msg_name_change_err_used_name' => 'Это имя принадлежит другому пользователю',\n  'opt_msg_name_change_err_no_dm' => 'Не хватает ТМ для смены имени',\n\n  'username_old' => 'Текущее имя',\n  'username_new' => 'Новое имя',\n  'username_change_confirm' => 'Сменить имя',\n  'username_change_confirm_payed' => 'за',\n\n  'opt_msg_pass_changed' => 'Пароль успешно изменен',\n  'opt_err_pass_wrong' => 'Неправильный текущий пароль. Пароль не был изменен',\n  'opt_err_pass_unmatched' => 'Введенный пароль не совпадает с подтвержденим пароля. Пароль не был изменен',\n  'changue_pass' => 'Сменить пароль',\n  'Download' => 'Загрузка',\n  'userdata' => 'Информация',\n  'username' => 'Имя',\n  'lastpassword' => 'Старый пароль',\n  'newpassword' => 'Новый пароль<br>(мин. 8 символов)',\n  'newpasswordagain' => 'Повторите новый пароль',\n  'emaildir' => 'Адрес e-mail',\n  'emaildir_tip' => 'Этот адрес может быть изменён в любое время. Адрес станет основным, если он не изменялся в течении 7 дней.',\n  'permanentemaildir' => 'Основной адрес e-mail',\n  'opt_planet_sort_title' => 'Сортировать планеты по',\n  'opt_planet_sort_options' => [\n    SORT_ID       => 'Времени колонизации',\n    SORT_LOCATION => 'Координатам',\n    SORT_NAME     => 'Алфавиту',\n    SORT_SIZE     => 'Количеству полей',\n  ],\n  'opt_planet_sort_ascending' => [\n    SORT_ASCENDING  => 'Возрастанию',\n    SORT_DESCENDING => 'Убыванию',\n  ],\n\n  'opt_navbar_title' => 'Панель навигации',\n  'opt_navbar_description' => 'Панель навигации (или попросту \"навбар\") располагается в самом верху экрана. Этот раздел позволяет настроить вид навбара',\n  'opt_navbar_resourcebar_description' => 'Ресурсбар - панель ресурсов',\n  'opt_navbar_buttons_title' => 'Настройка кнопок навбара',\n  'opt_player_options' => [\n    PLAYER_OPTION_NAVBAR_PLANET_VERTICAL        => 'Вертикальный ресурсбар',\n    PLAYER_OPTION_NAVBAR_PLANET_DISABLE_STORAGE => 'Отключить показ ёмкость складов в ресурсбаре',\n    PLAYER_OPTION_NAVBAR_PLANET_OLD             => 'Использовать старое отображение ресурсов в виде таблицы',\n\n    PLAYER_OPTION_NAVBAR_RESEARCH_WIDE          => 'Широкая кнопка исследований (старый вид)',\n    PLAYER_OPTION_NAVBAR_DISABLE_RESEARCH       => 'Отключить кнопку исследований',\n    PLAYER_OPTION_NAVBAR_DISABLE_PLANET         => 'Отключить кнопку планеты',\n    PLAYER_OPTION_NAVBAR_DISABLE_HANGAR         => 'Отключить кнопку верфи',\n    PLAYER_OPTION_NAVBAR_DISABLE_DEFENSE        => 'Отключить кнопку обороны',\n    PLAYER_OPTION_NAVBAR_DISABLE_EXPEDITIONS    => 'Отключить кнопку экспедиций',\n    PLAYER_OPTION_NAVBAR_DISABLE_FLYING_FLEETS  => 'Отключить кнопку летящих флотов',\n    PLAYER_OPTION_NAVBAR_DISABLE_QUESTS         => 'Отключить кнопку квестов',\n    PLAYER_OPTION_NAVBAR_DISABLE_META_MATTER    => 'Отключить кнопку МетаМатерии',\n\n    PLAYER_OPTION_UNIVERSE_OLD                  => 'Использовать старый вид \"Обзора Вселенной\"',\n    PLAYER_OPTION_UNIVERSE_DISABLE_COLONIZE     => 'Отключить кнопку колонизации',\n    PLAYER_OPTION_DESIGN_DISABLE_BORDERS        => 'Отключить рамки-картинки у таблиц',\n    PLAYER_OPTION_TECH_TREE_TABLE               => 'Страница Технологий в виде таблицы (старый вид)',\n    PLAYER_OPTION_FLEET_SHIP_SELECT_OLD         => 'Количество кораблей в отдельной колонке (старый вид)',\n    PLAYER_OPTION_FLEET_SHIP_HIDE_SPEED         => 'Не показывать скорость корабля',\n    PLAYER_OPTION_FLEET_SHIP_HIDE_CAPACITY      => 'Не показывать ёмкость трюмов корабля',\n    PLAYER_OPTION_FLEET_SHIP_HIDE_CONSUMPTION   => 'Не показывать потребление топлива корабля',\n    PLAYER_OPTION_TUTORIAL_DISABLED             => 'Полностью отключить обучение',\n    PLAYER_OPTION_TUTORIAL_WINDOWED             => 'Показывать обучающий текст во всплывающем окне (popup)',\n    PLAYER_OPTION_TUTORIAL_CURRENT              => 'Сбросить обучение - обучение начнётся заново',\n\n    PLAYER_OPTION_PLANET_SORT_INVERSE           => 'В обратном порядке',\n    PLAYER_OPTION_BUILD_AUTOCONVERT_HIDE        => 'Скрыть кнопку автоконвертации',\n\n    PLAYER_OPTION_SOUND_ENABLED                 => 'Включить звуки в игре',\n    PLAYER_OPTION_ANIMATION_DISABLED            => 'Отключить эффекты анимации',\n    PLAYER_OPTION_PROGRESS_BARS_DISABLED        => 'Отключить прогресс-бары',\n  ],\n\n  'opt_chk_skin' => 'Использовать оформление',\n  'opt_adm_title' => 'Опции администрирования',\n  'opt_adm_planet_prot' => 'Защита планет',\n  'thanksforregistry' => 'Спасибо за регистрацию.<br />Через несколько минут вы получите ваше сообщение с паролем.',\n  'general_settings' => 'Общие настройки',\n  'skins_example' => 'Оформление',\n\n\n  'opt_avatar' => 'Аватар',\n  'opt_avatar_search' => 'Искать в Google',\n  'opt_avatar_remove' => 'Удалить аватар',\n  'opt_upload' => 'Загрузить',\n\n  'opt_msg_avatar_removed' => 'Аватар удален',\n  'opt_msg_avatar_uploaded' => 'Аватар изменен успешно',\n  'opt_msg_avatar_error_delete' => 'Ошибка удаления файла аватара. Обратитесь к Администрации сервера',\n  'opt_msg_avatar_error_writing' => 'Ошибка сохранения файла аватара. Обратитесь к Администрации сервера',\n  'opt_msg_avatar_error_upload' => 'Ошибка загрузки изображения %1. Обратитесь к Администрации сервера',\n  'opt_msg_avatar_error_unsupported' => 'Формат загруженного изображения не поддерживается. Поддерживаются только файлы JPG, GIF, PNG размером до 200КБ',\n\n  'untoggleip' => 'Выключить функцию проверки по IP',\n  'untoggleip_tip' => 'Проверка IP означает то, что вы не сможете войти под своим именем с двух разных IP. Проверка даёт вам преимущество в безопасности!',\n  'galaxyvision_options' => 'Вселенная',\n  'spy_cant' => 'Количество зондов',\n  'spy_cant_tip' => 'Количество зондов, которое будет отправляться, когда вы будете за кем-то следить.',\n  'tooltip_time' => 'Задержка перед показом подсказки',\n  'mess_ammount_max' => 'Количество максимальных сообщений флота',\n  'seconds' => 'Секунд(а/ы)',\n  'shortcut' => 'Быстрый доступ',\n  'show' => 'Показывать',\n  'write_a_messege' => 'Написать сообщение',\n  'spy' => 'Шпионаж',\n  'add_to_buddylist' => 'Добавить в друзья',\n  'attack_with_missile' => 'Ракетная атака',\n  'show_report' => 'Просмотреть отчёт',\n  'delete_vacations' => 'Управление профилем',\n  'mode_vacations' => 'Включить режим отпуска',\n  'vacations_tip' => 'Режим отпуска нужен для защиты планет во время вашего отсутствия.',\n  'deleteaccount' => 'Отключить профиль',\n  'deleteaccount_tip' => 'Профиль будет удалён через 45 дней неактивности.',\n  'deleteaccount_on' => 'При неактивности аккаунта его удаление произойдет',\n  'save_settings' => 'Сохранить изменения',\n  'exit_vacations' => 'Выйти из режима отпуска',\n  'Vaccation_mode' => 'Режим отпуска включён. Он продлится до: ',\n  'You_cant_exit_vmode' => 'Вы не можете выйти из режима отпуска, пока не истечёт минимальное время',\n  'Error' => 'Ошибка',\n  'cans_resource' => 'Прекратите добычу ресурсов на планетах',\n  'cans_reseach' => 'Остановите иследования на планетах',\n  'cans_build' => 'Остановите строительство на планетах',\n  'cans_fleet_build' => 'Остановите постройку флота и обороны',\n  'cans_fly_fleet2' => 'Чужой флот приближается... вы не можите уйти в отпуск',\n  'vacations_exit' => 'Режим отпуска отключен... Перезайдите',\n  'select_skin_path' => 'ВЫБРАТЬ',\n  'opt_language' => 'Язык интерфейса',\n  'opt_compatibility' => 'Совместимость - старые интерфейсов',\n  'opt_compat_structures' => 'Старый интерфейс строительства зданий',\n  'opt_vacation_err_your_fleet' => 'Нельзя уйти в отпуск пока в полете находится хотя бы один ваш флот',\n  'opt_vacation_err_building' => 'Вы что-то строите или исследуете на %s и поэтому вы не можете уйти в отпуск',\n  'opt_vacation_err_research' => 'Ваши ученные исследует технологию и поэтому вы не можете уйти в отпуск',\n  'opt_vacation_err_que' => 'У вас либо исследуются технология, либо что-то строиться на одной из планет и поэтому вы не можете уйти в отпуск. Используйте ссылку \"Империя\", что бы просмотреть очереди построек на планетах',\n  'opt_vacation_err_timeout' => 'Вы еще не наработали на отпуск - таймаут ухода в отпуск не исчерпан',\n  'opt_vacation_next' => 'Пойти в отпуск можно будет после',\n  'opt_vacation_min' => 'минимум до',\n  'succeful_changepass' => 'Пароль успешно изменён.<br /><a href=\"login.php\" target=\"_top\">Назад</a>',\n\n  'opt_time_diff_clear' => 'Замерить разницу между временем у игрока и временем на сервере',\n  'opt_time_diff_manual' => 'Задать вручную разницу во времени',\n  'opt_time_diff_explain' => 'При правильно выставленной разнице во времени, часы \"Время у игрока\" в навбаре должны идти секунда в секунду с часами на устройстве игрока<br />\n  Обычно игра сама автоматически устанавливает правильную разницу во времени. Однако при неправильной установке часового пояса на устройстве игрока, при игре с нескольких устройств, а так же\n  при очень медленном интернете иногда нужно установить разницу во времени вручную',\n\n  'opt_custom' => [\n    'opt_uni_avatar_user' => 'Показывать аватар пользователя',\n    'opt_uni_avatar_ally' => 'Показывать логотип Альянса',\n    'opt_int_struc_vertical' => 'Вертикальная очередь построек',\n    'opt_int_navbar_resource_force' => 'Всегда показывать ресурсбар',\n    'opt_int_overview_planet_columns' => 'Количество колонок в списке планет',\n    'opt_int_overview_planet_columns_hint' => '0 - рассчитать по максимальному количество рядов',\n    'opt_int_overview_planet_rows' => 'Максимальное количество рядов в списке планет',\n    'opt_int_overview_planet_rows_hint' => 'Игнорируется, если указано количество колонок',\n  ],\n\n  'opt_mail_optional_description' => 'На этот почтовый адрес отправляются личные сообщения от других игроков и уведомления о внутриигровых событиях (например, отчеты об экспедициях и отчеты шпионажа)',\n  'opt_mail_permanent_description' => 'К этому почтовому адресу привязывается игровой аккаунт. Ввести его можно только один раз. Все системные уведомления (например, о смене пароля) отправляются именно на этот адрес',\n\n  'opt_account_name' => 'Ваш логин<br />Это имя надо вводить при входе в игру',\n  'opt_game_user_name' => 'Имя в игре (ник)<br />По этим именем вы будете видны другим игрокам сервера',\n\n  'opt_universe_title' => 'Вселенная',\n\n  'option_fleets' => 'Флоты',\n  'option_fleet_send' => 'Отправка флота',\n\n  'option_change_nick_disabled' => 'Смена ника запрещена настройками сервера',\n\n  'opt_ignores' => 'Игнор-лист',\n  'opt_unignore_do' => 'Удалить из игнор-листа',\n  'opt_ignore_list_empty' => 'Ваш игнор-лист пуст',\n\n];\n"
  },
  {
    "path": "language/ru/overview.mo.php",
    "content": "<?php\n\n/*\n#############################################################################\n#  Filename: overview.mo\n#  Project: SuperNova.WS\n#  Website: http://www.supernova.ws\n#  Description: Massive Multiplayer Online Browser Space Strategy Game\n#\n#  Copyright © 2009-2018 Gorlum for Project \"SuperNova.WS\"\n#############################################################################\n*/\n\n/**\n*\n* @package language\n* @system [Russian]\n* @version 43a16.13\n*\n*/\n\n/**\n* DO NOT CHANGE\n*/\n\nif (!defined('INSIDE')) die();\n\n\n$a_lang_array = (array(\n  'ov_hack_alert' => 'Попытка взлома БД!!!',\n  'ov_you_have' => 'У вас',\n  'ov_new_message' => 'одно новое сообщение',\n  'ov_new_messages' => 'новых сообщений',\n  'cancel' => 'Отменить',\n  'Planet_menu' => 'Строительство на планете',\n  'Planet' => 'Планета',\n  'Moon' => 'Луна',\n  'Have_new_level_mineur' => 'За достижения в экономике Вы выиграли очко развития офицеров!',\n  'Have_new_level_raid' => 'За успешные атаки Вы выиграли очко развития офицеров!',\n  'Server_time' => 'Время',\n  'Left_time' => 'Оставшееся время',\n  'Now_build' => 'Сейчас строится',\n  'Events' => 'События',\n  'Free' => 'нет заданий',\n  'Diameter' => 'Диаметр',\n  'fields' => 'Сектора',\n  'Developed_fields' => 'Занято секторов',\n  'max_eveloped_fields' => 'Максимальное количество секторов',\n  'Temperature' => 'Температура',\n  'min_avg_max' => 'мин/ср/макс',\n  'approx' => 'приблизительно',\n  'to' => 'to',\n  'Centigrade' => 'C',\n  'Position' => 'Координаты',\n//  'Buildings' => 'Строения',\n  'Fleet' => 'Флот',\n  'Research' => 'Исследования',\n  'Total' => 'Всего',\n  'Rank' => 'Место',\n  'of' => 'из',\n  'Miner' => 'Геолог',\n  'Raider' => 'Рейдер',\n  'Debris_Field' => 'Debris Field',\n  'rename_and_abandon_planet' => 'Управление планетой',\n  'functions' => 'Функции',\n  'coords' => 'Координаты',\n  'your_planet' => 'Ваша планета',\n  'colony_abandon' => 'Покинуть колонию',\n  'deleteplanet' => 'Удалить планету!',\n  'security_query' => 'Система безопасности',\n  'name' => 'Имя',\n  'namer' => 'Сменить название',\n//  'password' => 'Пароль',\n  'confirm_planet_delete' => 'Подтвердите удаление планеты',\n  'confirmed_with_password' => 'Подтвердите с паролем',\n  'ov_delete_ok' => 'Колония успешно удалена',\n  'ov_delete_wrong_planet' => 'Планета не может быть оставлена! Вы пытаетесь покинуть вашу основную планету или вы сменили текущую планету в другом окне браузера.',\n  'ov_delete_wrong_pass' => 'Неверный пароль!',\n  'MembersOnline' => 'Игроков',\n  'ov_fleet_list' => 'График движения флотов',\n  'ov_fleet' => 'Флот',\n  'ov_destination' => 'Куда',\n  'ov_source' => 'Откуда',\n  'event_time' => 'Время',\n  'ov_mission' => 'Задание',\n  'ov_event' => 'Событие',\n  'ov_flying_fleets' => 'Флоты, летящие на',\n  'ov_other_planets' => 'другие планеты',\n  'ov_fleet_arrive' => 'Прибытие',\n  'ov_fleet_return' => 'Возвращение',\n  'ov_fleet_hold' => 'конец задания',\n  'ov_fleet_rocket' => 'Ракетный удар',\n  'ov_fleet_exploration' => 'Исследование',\n  'ov_fleet_colonization' => 'Колонизация планеты',\n  'ov_fleet_no_flying' => 'Нет флотов в полете',\n  'ov_vennant' => ' отправленный ',\n  'ov_planet_to' => 'с планеты ',\n  'ov_moon_to' => 'с луны ',\n  'ov_atteint' => ' отправлен на ',\n  'ov_planet_to_target' => 'планету ',\n  'ov_moon_to_target' => 'луну ',\n  'ov_debris_to_target' => 'поле обломков ',\n  'ov_explo_to_target' => 'позиции ',\n  'ov_explo_stay' => ' на исследование ',\n  'ov_explo_mission' => '. Задание : ',\n  'ov_rentrant' => ' возвращается ',\n  'ov_planet_from' => 'с планеты ',\n  'ov_moon_from' => 'с луны ',\n  'ov_debris_from' => 'с поля обломков ',\n  'ov_explo_from' => 'с позиции ',\n  'ov_back_planet' => ' на планету ',\n  'ov_back_moon' => ' на луну ',\n  'ov_hostile' => ' игрока ',\n  'ov_message' => 'Отправить сообщение',\n\n  'imp_user_points_struc' => 'За постройки',\n  'imp_user_points_tech' => 'За исследования',\n  'imp_user_points_fleet' => 'За флот',\n  'imp_user_points_def' => 'За оборону',\n  'imp_user_points_res' => 'За ресурсы',\n  'imp_user_points_all' => 'Всего',\n  'NumberOfRaids' => 'Проведено',\n  'RaidsWin' => 'Выиграно',\n  'RaidsLoose' => 'Проиграно',\n  'Economica' => 'Постройки',\n  'Teching' => 'Иследования',\n  'imp_statistics' => 'Статистика',\n\n  'Points_1' => 'сектора',\n  'km' => 'км',\n  'orb' => 'Обломки на орбите',\n  'buildings_on_planet' => 'Застройка',\n  'ov_planet_details' => 'Подробно о планете',\n  'ov_building' => 'Здания',\n  'ov_hangar' => 'Верфи',\n  'ov_rank' => 'Ранг',\n  'ov_rpg_new_level_miner' => 'За достижения в экономике Вы получаете Тёмную Материю.',\n  'ov_rpg_new_level_raid' => 'За успешные атаки Вы получаете Тёмную Материю.',\n  'ov_points' => 'Очки',\n  'ov_raids' => 'Рейды',\n  'ov_experience' => 'Опыт',\n  'ov_player_rpg' => 'Статистика игрока',\n  'ov_banner' => 'Баннер',\n  'ov_userbar' => 'Юзербар',\n  'ov_banner_empty_id' => 'SuperNova - Join The Game!',\n  'ov_new' => 'НОВАЯ',\n  'ov_overview' => 'Обзор',\n  'ov_manage' => 'Управление',\n  'ov_return' => 'Вернуться к обзору',\n  'ov_rename' => 'Переименовать',\n  'ov_planet_rename_dialog_title' => 'Переименование планеты/луны',\n  'ov_new_name' => 'Новое название',\n  'cur_governor' => 'Текущий губернатор',\n  'ov_mrc_confirm_1' => 'Вы точно хотите заменить губернатора',\n  'ov_mrc_confirm_2' => 'уровня',\n  'ov_mrc_confirm_3' => 'на губернатора',\n  'ov_mrc_confirm_4' => 'первого уровня? Вся Тёмная Материя, вложенная в текущего губернатора БУДЕТ ПОТЕРЯНА!',\n  'ov_manage_page_hint' => '<li class=\"warning\">ВНИМАНИЕ! При найме губернатора, отличного от текущего, предыдущий губернатор увольняется БЕЗ КОМПЕНСАЦИИ ТЁМНОЙ МАТЕРИИ, а уровень нового губернатора будет равен единице!  Поэтому планируйте развитие своей Империи, заранее подбирая губернатора для каждой конкретной планеты в зависимости от её планируемой роли!</li>\n  <li>Губернаторы - это наемники, управлюящие отдельной планетой и дающие определенные бонусы для данной планеты</li>\n  <li>Кликните на изображении губренатора в списке, чтобы увидеть его описание и предоставляемые им бонусы</li>\n  <li>Чтобы нанять конкретного губернатора кликните на надпись \"Нанять\"</li>\n  <li>Повторный найм текущего губрентатора увеличивает его уровень и, соответственно, планетарный эффект</li>\n  <li>Некоторые губернаторы имеют ограничения по уровню. Некоторые - нет</li>\n  <li>Телепортация перемещает в новые координаты планету вместе с флотами, находящимися на орбите планеты</li>\n  <li>Если у планеты есть луна - она также перемещается в новые координаты вместе с флотами</li>\n  <li>Телепортация невозможна, если в окрестностях планеты есть какая-то активность флотов (т.е. есть флоты, имеющие в качестве точки отправления или назначения саму планету, луну или поле обломков)</li>\n  <li>После телепортации необходимо выждать некоторое время перед следующей телепортацией - нарушенная метрика пространства вокруг планеты должна нормализироваться</li>',\n\n  'ov_gate_time_left' => 'Время до следующего прыжка',\n\n  'ov_teleport' => 'Телепортировать',\n  'ov_teleport_new_coordinates' => 'Новые координаты',\n  'ov_teleport_err_none' => 'Планета успешно телепортирована',\n  'ov_teleport_err_wrong_coordinates' => 'Неправильные координаты',\n  'ov_teleport_err_fleet' => 'В окрестностях планеты наблюдается активность флотов',\n  'ov_teleport_err_destination_busy' => 'Место назначения занято',\n  'ov_teleport_err_cooldown' => 'Невозможно телепортироваться два раза подряд. Подождите пока нормализуется метрика пространства',\n  'ov_teleport_err_no_dark_matter' => 'Не хватает Тёмной Материи для телепортации',\n  'ov_teleport_log_record' => 'Планета {%2$d} %1$s телепортирована с координат %3$s по координатам %4$s',\n\n  'ov_capital' => 'Сделать столицей Империи',\n  'ov_capital_err_none' => 'Планета теперь является столицей Империи',\n  'ov_capital_err_capital_already' => 'Эта планета уже является столицей Империи',\n  'ov_capital_err_no_dark_matter' => 'Не хватает Тёмной Материи для переноса столицы Империи',\n  'ov_capital_err_not_a_planet' => 'Только планету можно сделать столицей Империи',\n\n  'read_all_news' => 'Отметить все как прочитанное',\n\n  'imp_TBA' => 'Что-то будет...',\n  'imp_experience_current' => 'Текущий',\n  'imp_experience_to_level' => 'Левелап',\n\n  'ov_manage_special' => 'Особые функции',\n  'ov_password' => 'Ваш пароль на вход в игру',\n\n  'ov_governor_purchase' => 'Игрок купил Губернатора %1$s ID %2$d уровня %3$d на планету %4$s',\n\n\n));\n"
  },
  {
    "path": "language/ru/payment.mo.php",
    "content": "<?php\n\n/*\n#############################################################################\n#  Filename: payment.mo.php\n#  Project: SuperNova.WS\n#  Website: http://www.supernova.ws\n#  Description: Massive Multiplayer Online Browser Space Strategy Game\n#\n#  Copyright © 2009-2018 Gorlum for Project \"SuperNova.WS\"\n#############################################################################\n*/\n\n/**\n*\n* @package language\n* @system [Russian]\n* @version 45d0\n*\n*/\n\n/**\n* DO NOT CHANGE\n*/\n\nif (!defined('INSIDE')) die();\n\n$a_lang_array = (array(\n  // Metamatter\n  'sys_metamatter_what_header' => 'Что такое <span class=\"metamatter\">Метаматерия</span>',\n  'sys_metamatter_what_description' => '<span class=\"metamatter\">Метаматерия</span> (сокращенно <span class=\"metamatter\">ММ</span>) - это весьма условное название для особого состояния Вселенной. Фактически - это даже не материя, а факторизируемая вероятность.<br /><br />\n  У <span class=\"metamatter\">Метаматерии</span> нет состояния - и в то же время она находится во всех состояних. <span class=\"metamatter\">Метаматерия</span> нигде не находится - и в то же время находится везде. Потенциально метаматерия может стать чем угодно и где угодно - если правильно актуализировать вероятность.',\n  'sys_metamatter_what_purchase' => '<span class=\"metamatter\">Метаматерию</span> можно рассматривать как \"Покупную <span class=\"dark_matter\">Тёмную Материю</span>\" - при нехватке <span class=\"metamatter\">ТМ</span> для приобретения чего-либо <span class=\"metamatter\">ММ</span> будет <span class=\"ok\">автоматически сконвертирована</span> в недостающее количество <span class=\"dark_matter\">ТМ</span> по курсу <span class=\"dark_matter\">1 ТМ</span> = <span class=\"metamatter\">1 ММ</span>',\n\n  'pay_mm_convert_header' => 'Конвертация Метаматерии в Тёмную Материю',\n  'pay_mm_convert_text' => 'Метаматерия конвертируется в Тёмную Материю по курсу 1 к 1',\n  'pay_mm_convert_no_mm' => 'Нет Метаматерии - купите её сначала',\n  'pay_mm_convert_prefix' => 'Единицы Метаматерии',\n  'pay_mm_convert_suffix' => '',\n  'pay_mm_convert_do' => 'Сконвертировать в ТМ',\n\n  'pay_msg_mm_convert_wrong_amount' => 'Неправильное количество Метаматерии',\n  'pay_msg_mm_convert_not_enough' => 'Не хватает Метаматерии для конвертации в Тёмную Материю',\n  'pay_msg_mm_convert_mm_error' => 'Ошибка изменения количеста Метаматерии',\n  'pay_msg_mm_convert_dm_error' => 'Ошибка изменения количеста Тёмной Материи',\n\n  'pay_mm_buy' => 'Приобрести <span class=\"metamatter\">Метаматерию</span>',\n  'pay_mm_buy_text_cost' => 'Стоимость',\n  'pay_mm_buy_text_unit' => 'составляет',\n  'pay_mm_buy_url_description' => '<span class=\"metamatter\">ММ</span> можно приобрести только за реальные деньги',\n  'pay_mm_buy_url_get'  => 'Откройте эту ссылку, что бы узнать подробности',\n  'pay_mm_buy_url_none' => 'Свяжитесь с Администрацией сервера по вопросам получения <span class=\"metamatter\">Метаматерии</span>',\n\n  'pay_mm_bonus_header' => 'Стоимость <span class=\"metamatter\">Метаматерии</span> и бонусы за оптовую покупку',\n  'pay_mm_bonus' => 'При оптовой покупке <span class=\"metamatter\">ММ</span> предоставляются бонусы',\n  'pay_mm_bonus_each' => 'от %s <span class=\"metamatter\">ММ</span> - бонус %d%% к количеству <span class=\"metamatter\">ММ</span>',\n  'pay_mm_bonus_text' => 'Бонус',\n\n  'pay_mm_buy_step1_text' => 'Выберите количество <span class=\"metamatter\">ММ</span>, способ оплаты и подтвердите свой выбор',\n  'pay_mm_buy_metamatter_amount' => 'Выберите количество <span class=\"metamatter\">Метаматерии</span> из списка',\n  'pay_mm_buy_metamatter_amount_enter' => '...или введите другое количество <span class=\"metamatter\">Метаматерии</span>',\n  'pay_mm_buy_price_for' => 'Цена за',\n  'pay_mm_buy_unit' => '<span class=\"metamatter\">Метаматерии</span>',\n  'pay_mm_buy_select' => 'Выберите платёжную систему',\n  'pay_mm_buy_method_detail' => 'Некоторые способы оплаты предлагают выбор разных платёжных систем. Если платёж не проходит через одну платёжную систему - попробуйте использовать тот же способ оплаты с другой платёжной системой',\n  'pay_mm_buy_confirm' => 'Подтвердить выбор',\n  'pay_mm_buy_payment_selected' => 'Оплата будет произведена с использованием платёжной системы',\n  'pay_mm_buy_purchase' => 'Покупка',\n\n  'pay_mm_buy_payment_method_more' => 'Нажмите здесь, что бы увидеть больше способов оплаты',\n\n  'pay_mm_buy_payment_method_select' => 'Выберите способ оплаты',\n  'pay_mm_buy_payment_method_selected' => 'Вы выбрали способ оплаты',\n\n  'pay_mm_buy_step2_text' => 'Рассчётная стоимость не включает дополнительные комиссии, которые могут взимать платёжные системы и/или разнообразные посредники. Проверьте выбранное количество <span class=\"metamatter\">Метаматерии</span> и способ оплаты. Если все правильно - нажмите кнопку \"Оплатить <span class=\"metamatter\">Метаматерию</span>\". Если вы ошиблись - нажмите кнопку \"Начать заново\"',\n  'pay_mm_buy_pay' => 'Оплатить <span class=\"metamatter\">Метаматерию</span>',\n  'pay_mm_buy_reset' => 'Начать заново',\n  'pay_mm_buy_in_progress' => 'Происходит оплата...',\n  'pay_mm_buy_conversion_cost' => 'Рассчётная стоимость %1$s единиц <span class=\"metamatter\">Метаматерии</span> в валюте платежной системы составит <span class=\"%4$s\">%2$s</span> %3$s',\n  'pay_mm_buy_cost_base' => 'Стоимость составит',\n  'pay_mm_buy_real_income' => 'Бонус за оптовую покупку составит %s%% и на ваш игровой счёт будет зачислено %s <span class=\"metamatter\">ММ</span>',\n  'pay_mm_buy_approximate_cost' => 'Приблизительная стоимость ММ на платёжной системе составляет <span class=\"notice\">%1$s %2$s</span> (стоимость дана ПРИБЛИЗИТЕЛЬНО. Итоговая сумма на платёжной системе может отличаться)',\n\n  'pay_currency_name' => 'Валюта',\n  'pay_currency_symbol' => 'Символ',\n  'pay_currency_choose' => 'Выберите валюту платежа',\n  'pay_currency_list' => array(\n    'RUB' => 'Российский рубль',\n    'USD' => 'Доллар США',\n    'EUR' => 'Евро',\n    'UAH' => 'Украинская гривна',\n//    'WMR' => 'WebMoney рубль',\n//    'WMZ' => 'WebMoney доллар',\n//    'WME' => 'WebMoney евро',\n//    'WMU' => 'WebMoney гривна',\n//    'WMB' => 'WebMoney белорусский рубль',\n  ),\n\n  'pay_methods' => array(\n    PAYMENT_METHOD_EMONEY => 'Электронный кошелёк',\n    PAYMENT_METHOD_EMONEY_YANDEX => 'Яндекс.Деньги',\n    PAYMENT_METHOD_EMONEY_WEBMONEY_WMR => 'WebMoney WMR',\n    PAYMENT_METHOD_EMONEY_WEBMONEY_WMZ => 'WebMoney WMZ',\n    PAYMENT_METHOD_EMONEY_WEBMONEY_WMU => 'WebMoney WMU',\n    PAYMENT_METHOD_EMONEY_WEBMONEY_WME => 'WebMoney WME',\n    PAYMENT_METHOD_EMONEY_WEBMONEY_WMB => 'WebMoney WMB',\n    PAYMENT_METHOD_EMONEY_QIWI => 'QIWI Кошелек',\n    PAYMENT_METHOD_EMONEY_ELECSNET => 'Кошелек Элекснет',\n    PAYMENT_METHOD_EMONEY_MAILRU => 'Деньги@Mail.Ru',\n    PAYMENT_METHOD_EMONEY_EASYPAY => 'EasyPay',\n    PAYMENT_METHOD_EMONEY_RUR_W1R => 'RUR Единый Кошелек',\n    PAYMENT_METHOD_EMONEY_TELEMONEY => 'TeleMoney',\n\n    PAYMENT_METHOD_BANK_CARD => 'Платежная карта (VISA, MasterCard итд)',\n    PAYMENT_METHOD_BANK_CARD_STANDARD => 'Банковская карта',\n    PAYMENT_METHOD_BANK_CARD_LIQPAY => 'LiqPay',\n    PAYMENT_METHOD_BANK_CARD_EASYPAY => 'EasyPay',\n    PAYMENT_METHOD_BANK_CARD_AMERICAN_EXPRESS => 'American Express',\n    PAYMENT_METHOD_BANK_CARD_JCB => 'JCB',\n    PAYMENT_METHOD_BANK_CARD_UNIONPAY => 'UnionPay',\n\n    PAYMENT_METHOD_BANK_INTERNET => 'Через интернет-банк',\n    PAYMENT_METHOD_BANK_INTERNET_ALFA_BANK => 'Альфа-Клик',\n    PAYMENT_METHOD_BANK_INTERNET_RUSSKIY_STANDART => 'Банк Русский Стандарт',\n    PAYMENT_METHOD_BANK_INTERNET_PROSMVYAZBANK => 'Промсвязьбанк',\n    PAYMENT_METHOD_BANK_INTERNET_VTB24 => 'ВТБ24',\n    PAYMENT_METHOD_BANK_INTERNET_OCEAN_BANK => 'Океан Банк',\n    PAYMENT_METHOD_BANK_INTERNET_HANDY_BANK => 'HandyBank',\n    PAYMENT_METHOD_BANK_INTERNET_007 => 'Банк Богородский',\n    PAYMENT_METHOD_BANK_INTERNET_008 => 'Банк Образование',\n    PAYMENT_METHOD_BANK_INTERNET_009 => 'ФлексБанк',\n    PAYMENT_METHOD_BANK_INTERNET_010 => 'ФьючерБанк',\n    PAYMENT_METHOD_BANK_INTERNET_011 => 'КранБанк',\n    PAYMENT_METHOD_BANK_INTERNET_012 => 'Костромаселькомбанк',\n    PAYMENT_METHOD_BANK_INTERNET_013 => 'Липецкий областной банк',\n    PAYMENT_METHOD_BANK_INTERNET_014 => 'Независимый строительный банк',\n    PAYMENT_METHOD_BANK_INTERNET_015 => 'Русский Трастовый Банк',\n    PAYMENT_METHOD_BANK_INTERNET_016 => 'ВестИнтерБанк',\n    PAYMENT_METHOD_BANK_INTERNET_017 => 'Межтопэнергобанк',\n    PAYMENT_METHOD_BANK_INTERNET_018 => 'Московский Индустриальный Банк',\n    PAYMENT_METHOD_BANK_INTERNET_019 => 'Банк Интеза',\n    PAYMENT_METHOD_BANK_INTERNET_020 => 'Банк Город',\n    PAYMENT_METHOD_BANK_INTERNET_021 => 'Банк АВБ',\n    PAYMENT_METHOD_BANK_INTERNET_BANK24 => 'Банк24 Национальный кредит',\n    PAYMENT_METHOD_BANK_INTERNET_PRIVAT24 => \"Приват24\",\n    PAYMENT_METHOD_BANK_INTERNET_SBERBANK => \"Сбербанк Онлайн\",\n\n    PAYMENT_METHOD_BANK_TRANSFER => 'Банковский перевод',\n\n    PAYMENT_METHOD_MOBILE => 'С мобильного телефона',\n    PAYMENT_METHOD_MOBILE_SMS => 'SMS',\n//    PAYMENT_METHOD_MOBILE_XSOLLA => 'Со счёта мобильного',\n    PAYMENT_METHOD_MOBILE_PAYPAL_ZONG => 'Со счёта или SMS',\n    PAYMENT_METHOD_MOBILE_MEGAPHONE => 'Мегафон',\n    PAYMENT_METHOD_MOBILE_MTS => 'МТС',\n    PAYMENT_METHOD_MOBILE_KYIVSTAR => 'Киевстар',\n    PAYMENT_METHOD_MOBILE_BEELINE => 'Билайн',\n\n    PAYMENT_METHOD_TERMINAL => 'Терминал оплаты',\n    PAYMENT_METHOD_TERMINAL_QIWI => 'QIWI Кошелек',\n    PAYMENT_METHOD_TERMINAL_ELECSNET => 'Элекснет',\n    PAYMENT_METHOD_TERMINAL_ELEMENT => 'Мобил Элемент',\n    PAYMENT_METHOD_TERMINAL_KASSIRANET => 'Кассира.нет',\n    PAYMENT_METHOD_TERMINAL_IBOX => 'Ibox',\n    PAYMENT_METHOD_TERMINAL_UKRAINE => 'Терминалы Украины',\n    PAYMENT_METHOD_TERMINAL_RUSSIA => 'Терминалы России',\n    PAYMENT_METHOD_TERMINAL_EASYPAY => 'EasyPay',\n\n    PAYMENT_METHOD_OTHER => 'Другие способы',\n    PAYMENT_METHOD_OTHER_EVROSET => 'Евросеть',\n    PAYMENT_METHOD_OTHER_SVYAZNOY => 'Связной',\n    PAYMENT_METHOD_OTHER_ROBOKASSA_MOBILE => 'Мобильная ROBOKASSA',\n\n    PAYMENT_METHOD_GENERIC => 'Выше перечислены далеко не все возможнные способы оплаты. Если вы не нашли подходящего для себя способа - воспользуйтесь услугами агрегаторов',\n//    PAYMENT_METHOD_GENERIC_XSOLLA => 'xSolla',\n//    PAYMENT_METHOD_GENERIC_ROBOKASSA => 'RoboKassa',\n  ),\n\n  'pay_currency_exchange_title' => 'Внутренние курсы валют',\n  'pay_currency_exchange_rate' => 'Курс',\n  'pay_currency_exchange_direct' => 'Прямой',\n  'pay_currency_exchange_reverse' => 'Обратный',\n  'pay_currency_exchange_mm' => '<span class=\"metamatter\">ММ</span> за 1 у.е.',\n  'pay_currency_exchange_note' => 'Внутренний курс используется для пересчета из основной валюты сервера в валюту платёжной системы. Курс не включает комиссию посредников и/или платёжных систем',\n\n  'pay_msg_mm_purchase_complete'   => 'Вы успешно заплатили за %d единиц Метаматерии через сервис %s. Вам начислено %s единиц <span class=\"metamatter\">Метаматерии</span>',\n  'pay_msg_mm_purchase_incomplete' => 'Ваш платёж за %d единиц <span class=\"metamatter\">Метаматерии</span> через сервис %s не завершен. Если вы считаете, что произошла ошибка - свяжитесь с Администрацией сервера',\n  'pay_msg_mm_purchase_test'       => 'На самом деле - шутка. Платеж был тестовый, поэтому ты ничего не получил ха-ха-ха! Если считаешь, что это ошибка - обратись к Администрации сервера',\n\n  'pay_msg_request_user_found' => 'Пользователь найден',\n  'pay_msg_request_payment_complete' => 'Платёж завершен',\n  'pay_msg_request_payment_cancel_complete' => 'Платёж успешно отменён',\n\n  'pay_msg_request_unsupported' => 'Данный тип запроса не поддерживается',\n  'pay_msg_request_signature_invalid' => 'Неправильная подпись запроса',\n  'pay_msg_request_user_invalid' => 'Неправильный идентификатор пользователя',\n  'pay_msg_request_server_wrong' => 'Неправильный сервер',\n  'pay_msg_request_payment_amount_invalid' => 'Неправильная сумма платежа',\n  'pay_msg_request_payment_id_invalid' => 'Неправильный идентификатор платежа',\n  'pay_msg_request_payment_date_invalid' => 'Неправильная дата платежа',\n  'pay_msg_request_internal_error' => 'Внутренняя ошибка сервера. Попробуйте повторить платёж позже',\n  'pay_msg_request_paylink_unsupported' => 'Данный тип платёжной ссылке не поддерживается. Возможно используется устаревшая версия СН, не совместимая с данным платёжным модулем',\n  'pay_msg_request_payment_write_error' => 'Ошибка записи платежа',\n  'pay_msg_request_payment_cancelled_already' => 'Платёж уже отменен',\n  'pay_msg_request_payment_cancel_not_complete' => 'Платёж еще не завершен и не может быть отменен',\n  'pay_msg_request_payment_cancelled' => '!!! Платёж отозван платёжной системой!!!',\n  'pay_msg_request_payment_not_found' => 'Платёж не найден',\n\n  'pay_msg_module_disabled' => 'Платёжный модуль отключен',\n\n  'pay_msg_mm_request_money_and_mm_mismatched' => 'Не совпадает сумма оплаты и количество покупаемой ММ',\n\n  'pay_msg_mm_request_amount_invalid' => 'Неправильное количество <span class=\"metamatter\">Метаматерии</span>',\n  'pay_msg_mm_request_config_invalid' => 'Ошибка в конфигурации модуля платежа. Свяжитесь с Администрацией сервера',\n  'pay_msg_mm_request_mm_adjust_error' => 'Ошибка начисления <span class=\"metamatter\">Метаматерии</span>',\n\n  'pay_msg_request_error_db_payment_create' => 'Ошибка создания платежа в БД',\n  'pay_msg_request_error_test_payment' => 'Статус платежа в БД не совпадает с информацией в запросе',\n  'pay_error_internal_no_external_currency_set' => 'ВНУТРЕННЯЯ ОШИБКА или ОШИБКА КОНФИГУРАЦИИ ПЛАТЁЖНОГО МОДУЛЯ! Не установлена валюта платёжной системы! Пожалуйста, сообщите Администрации сервера!',\n\n));\n"
  },
  {
    "path": "language/ru/quest.mo.php",
    "content": "<?php\n\n/*\n#############################################################################\n#  Filename: quest.mo\n#  Project: SuperNova.WS\n#  Website: http://www.supernova.ws\n#  Description: Massive Multiplayer Online Browser Space Strategy Game\n#\n#  Copyright © 2009-2018 Gorlum for Project \"SuperNova.WS\"\n#############################################################################\n*/\n\n/**\n*\n* @package language\n* @system [Russian]\n* @version 43a16.13\n*\n*/\n\n/**\n* DO NOT CHANGE\n*/\n\nif (!defined('INSIDE')) die();\n\n\n$a_lang_array = (array(\n  'qst_quest' => 'Квест',\n  'qst_quest_of' => 'квеста',\n  'qst_name' => 'Название',\n  'qst_description' => 'Описание',\n  'qst_adm_conditions' => 'Требования',\n  'qst_conditions' => 'Нужно построить/исследовать',\n  'qst_rewards' => 'Награда за выполнение квеста',\n  'qst_total' => 'Квестов',\n  'qst_status' => 'Статус',\n  'qst_status_list' => array(\n    QUEST_STATUS_ALL => '-- Все квесты --',\n    QUEST_STATUS_NOT_STARTED => 'Не&nbsp;начат',\n    QUEST_STATUS_STARTED => 'Начат',\n    QUEST_STATUS_EXCEPT_COMPLETE => 'Все,&nbsp;кроме&nbsp;выполненных',\n    QUEST_STATUS_COMPLETE => 'Выполнен',\n  ),\n\n  'qst_filter_by_status' => 'Показывать квесты со статусом',\n\n  'qst_add' => 'Добавление квеста',\n  'qst_edit' => 'Редактирование квеста',\n  'qst_copy' => 'Копирование квеста',\n  'qst_mode_add' => 'Добавление',\n  'qst_mode_edit' => 'Редактирование',\n  'qst_mode_copy' => 'Копирование',\n  'qst_adm_err_unit_id' => 'Неправильный юнит',\n  'qst_adm_err_unit_amount' => 'Неправильное количество юнитов',\n  'qst_adm_err_reward_amount' => 'Неправильный размер награды',\n  'qst_adm_err_reward_type' => 'Неправильный тип награды',\n  'qst_adm_err_reward_empty' => 'Пустая награда квеста',\n));\n"
  },
  {
    "path": "language/ru/search.mo.php",
    "content": "<?php\n\n/*\n#############################################################################\n#  Filename: search.mo\n#  Project: SuperNova.WS\n#  Website: http://www.supernova.ws\n#  Description: Massive Multiplayer Online Browser Space Strategy Game\n#\n#  Copyright © 2009-2018 Gorlum for Project \"SuperNova.WS\"\n#############################################################################\n*/\n\n/**\n*\n* @package language\n* @system [Russian]\n* @version #43a16.13#\n*\n*/\n\n/**\n* DO NOT CHANGE\n*/\n\nif (!defined('INSIDE')) die();\n\n$a_lang_array = (array(\n  'srch_title' => 'Поиск по серверу',\n  'srch_search_do' => 'Искать',\n  'srch_result_none' => 'По данному запросу ничего не найдено',\n  'srch_player_name' => 'Имя игрока',\n  'srch_ally_name' => 'Название Альянса',\n  'srch_ally_members' => 'Участников',\n  'srch_planet_name' => 'Имя планеты',\n  'srch_rank' => 'Место',\n  'srch_action_pm' => 'Написать сообщение',\n  'srch_action_buddy' => 'Добавить в друзья',\n\n  'srch_aka' => 'AKA',\n\n  'srch_page_hint' => '<li>Показывается только первые 30 записей</li>\n  <li>Если игрок менял своё имя и одно из его старых имен подходит под критерии поиска, то оно будет так же показано отдельной строкой.\n  В этом случае в качестве имени будет выступать текущее имя игрока, а в скобках <span class=\"warning\">цветом</span> будет указано старое имя</li>',\n));\n"
  },
  {
    "path": "language/ru/stat.mo.php",
    "content": "<?php\n\n/*\n#############################################################################\n#  Filename: stat.mo\n#  Project: SuperNova.WS\n#  Website: http://www.supernova.ws\n#  Description: Massive Multiplayer Online Browser Space Strategy Game\n#\n#  Copyright © 2009-2018 Gorlum for Project \"SuperNova.WS\"\n#############################################################################\n*/\n\n/**\n*\n* @package language\n* @system [Russian]\n* @version 43a16.13\n*\n*/\n\n/**\n* DO NOT CHANGE\n*/\n\nif (!defined('INSIDE')) die();\n\n$a_lang_array = (array(\n  'stat_header' => 'Статистика',\n  'stat_refresh_last' => 'Предыдущее обновление:',\n  'stat_refresh_next' => 'Следующее обновление:',\n\n  'stat_rank' => 'Место',\n  'stat_rank_diff' => 'Изм.',\n  'stat_points' => 'Очки',\n  'stat_per_member' => 'На участника',\n  'stat_members' => 'Участников',\n  'stat_message_write' => 'Написать сообщение',\n\n  'stat_show' => 'Показать статистику',\n  'stat_type' => array(\n    STAT_TOTAL => 'общую',\n    STAT_FLEET => 'флотов',\n    STAT_TECH => 'исследований',\n    STAT_BUILDING => 'построек',\n    STAT_DEFENSE => 'обороны',\n    STAT_RESOURCE => 'ресурсов',\n    STAT_RAID_TOTAL => 'проведенных боев',\n    STAT_RAID_WON => 'выигранных боев',\n    STAT_RAID_LOST => 'проигранных боев',\n    STAT_LVL_BUILDING => 'уровней постройки',\n    STAT_LVL_TECH => 'уровней исследований',\n    STAT_LVL_RAID => 'уровней рейдерства',\n  ),\n\n  'stat_by' => 'для',\n  'stat_player' => 'Игроки',\n  'stat_allys' => 'Альянсы',\n  'stat_range' => 'места',\n\n  'stat_details' => 'Информация об игроке',\n));\n"
  },
  {
    "path": "language/ru/system.mo.php",
    "content": "<?php\n\n/*\n#############################################################################\n#  Filename: system.mo\n#  Project: SuperNova.WS\n#  Website: http://www.supernova.ws\n#  Description: Massive Multiplayer Online Browser Space Strategy Game\n#\n#  Copyright © 2009 Gorlum for Project \"SuperNova.WS\"\n#  Copyright © 2009 MSW\n#############################################################################\n*/\n\n/**\n*\n* @package language\n* @system [Russian]\n* @version #46d0#\n*\n*/\n\n/**\n* DO NOT CHANGE\n*/\n\nuse Fleet\\Constants;\n\nif (!defined('INSIDE'))\n{\n  exit;\n}\n\n// System-wide localization\n\nglobal $config;\n\n$a_lang_array = [\n  'sys_birthday' => 'День рождения',\n  'sys_birthday_message' => '%1$s! Администрация СуперНовы сердечно поздравляет тебя с твоим Днем Рождения, который пришелся на %2$s и преподносит тебе в качестве подарка %3$d %4$s! От всей души желаем тебе успехов в игре и высоких рангов в статистике! Может это поздравление и запоздало, но лучше раньше, чем позже.',\n\n  'adm_err_denied' => 'Доступ запрещен. У вас не хватает прав, что бы пользоваться этой страницей интерфейса управления сервером',\n\n  'sys_empire'          => 'Империя',\n  'VacationMode'\t\t\t=> \"Ваше производство закрыто, так как вы в Отпуске\",\n  'sys_moon_destruction_report' => \"Рапорт разрушения луны\",\n  'sys_moon_destroyed' => \"Ваши Звёзды Смерти произвели мощную гравитационную волну, которая разрушила луну! \",\n  'sys_rips_destroyed' => \"Ваши Звёзды Смерти произвели мощную гравитационную волну, но её мощности оказалось не достаточно для уничтожения луны такого размера. Но гравитационная волна отразилась от лунной поверхности и разрушила ваш флот.\",\n  'sys_rips_come_back' => \"Ваши Звёзды Смерти не имеют достаточно энергии, чтоб нанести ущерб этой луне. Ваш флот возвращается не уничтожив луну.\",\n  'sys_chance_moon_destroy' => \"Изменение лунного уничтожения: \",\n  'sys_chance_rips_destroy' => \"Изменение разрывного уничтожения: \",\n\n  'sys_impersonate' => 'Воплотиться',\n  'sys_impersonate_done' => 'Развоплотиться',\n  'sys_impersonated_as' => 'ВНИМАНИЕ! Вы сейчас Воплотились в игрока %1$s. Не забывайте, что на самом деле - вы %2$s! Развоплотиться можно выбрав соответствующий пункт меню.',\n\n  'menu_admin_mining'          => 'Добыча игроков',\n  'menu_admin_units'          => 'Юниты',\n  'menu_admin_ube_balance'          => 'Баланс UBE',\n\n  'sys_day' => \"дней\",\n  'sys_hrs' => \"часов\",\n  'sys_min' => \"минут\",\n  'sys_sec' => \"секунд\",\n  'sys_day_short' => \"д\",\n  'sys_hrs_short' => \"ч\",\n  'sys_min_short' => \"м\",\n  'sys_sec_short' => \"с\",\n\n  'sys_ask_admin' => 'Вопросы и предложения направлять по адресу',\n\n  'sys_wait'      => 'Запрос выполняется. Пожалуйста, подождите.',\n\n  'sys_fleets'       => 'Флоты',\n  'sys_expeditions'  => 'Экспедиции',\n  'sys_fleet'        => 'Флот',\n  'sys_expedition'   => 'Экспедиция',\n  'sys_event_next'   => 'Следующее событие:',\n  'sys_event_arrive' => 'прибудет',\n  'sys_event_stay'   => 'закончит задание',\n  'sys_event_return' => 'вернется',\n\n  'sys_total'           => \"ИТОГО\",\n  'sys_need'\t\t\t\t=> 'Нужно',\n  'sys_register_date'   => 'Дата регистрации',\n\n  'sys_attacker' \t\t=> \"Атакующий\",\n  'sys_defender' \t\t=> \"Обороняющийся\",\n\n  'COE_combatSimulator' => \"Симулятор боя\",\n  'COE_simulate'        => \"Запуск симулятора\",\n  'COE_fleet'           => \"Флот\",\n  'COE_defense'         => \"Оборона\",\n  'sys_coe_combat_start'=> \"Флоты соперников встретились\",\n  'sys_coe_combat_end'  => \"Результаты боя\",\n  'sys_coe_round'       => \"Раунд\",\n\n  'sys_coe_attacker_turn'=> 'Атакующий делает выстрелы общей мощностью %1$s. Щиты обороняющегося поглощают %2$s выстрелов<br />',\n  'sys_coe_defender_turn'=> 'Обороняющийся делает выстрелы общей мощностью %1$s. Щиты атакующего поглощают %2$s выстрелов<br /><br /><br />',\n  'sys_coe_outcome_win'  => 'Обороняющийся выиграл битву!<br />',\n  'sys_coe_outcome_loss' => 'Атакующий выиграл битву!<br />',\n  'sys_coe_outcome_loot' => 'Он получает %1$s металла, %2$s кристаллов, %3$s дейтерия<br />',\n  'sys_coe_outcome_draw' => 'Бой закончился ничьёй.<br />',\n  'sys_coe_attacker_lost'=> 'Атакующий потерял %1$s единиц.<br />',\n  'sys_coe_defender_lost'=> 'Обороняющийся потерял %1$s единиц.<br />',\n  'sys_coe_debris_left'  => 'Теперь на этих пространственных координатах находятся %1$s металла и %2$s кристаллов.<br /><br />',\n  'sys_coe_moon_chance'  => 'Шанс появления луны составляет %1$s%%<br />',\n  'sys_coe_rw_time'      => 'Время генерации страницы %1$s секунд<br />',\n\n  'sys_resources'       => \"Ресурсы\",\n  'sys_ships'           => \"Корабли\",\n  'sys_metal'          => \"Металл\",\n  'sys_metal_sh'       => \"М\",\n  'sys_crystal'        => \"Кристалл\",\n  'sys_crystal_sh'     => \"К\",\n  'sys_deuterium'      => \"Дейтерий\",\n  'sys_deuterium_sh'   => \"Д\",\n  'sys_energy'         => \"Энергия\",\n  'sys_energy_sh'      => \"Э\",\n  'sys_dark_matter'    => \"Тёмная Материя\",\n  'sys_dark_matter_sh' => \"ТМ\",\n  'sys_metamatter'     => \"Метаматерия\",\n  'sys_metamatter_sh'  => \"ММ\",\n\n  'sys_reset'           => \"Сбросить\",\n  'sys_send'            => \"Отправить\",\n  'sys_characters'      => \"символов\",\n  'sys_back'            => \"Назад\",\n  'sys_return'          => \"Вернуться\",\n  'sys_delete'          => \"Удалить\",\n  'sys_writeMessage'    => \"Написать сообщение\",\n  'sys_hint'            => \"Подсказка\",\n\n  'sys_alliance'        => \"Альянс\",\n  'sys_player'          => \"Игрок\",\n  'sys_coordinates'     => \"Координаты\",\n\n  'sys_online'          => \"Онлайн\",\n  'sys_offline'         => \"Оффлайн\",\n  'sys_status'          => \"Статус\",\n\n  'sys_universe'        => \"Вселенная\",\n  'sys_goto'            => \"Перейти\",\n\n  'sys_time'            => \"Время\",\n  'sys_temperature'\t\t=> 'Температура',\n\n  'sys_no_task'         => \"нет задания\",\n\n  'sys_affilates'       => \"Приглашенные игроки\",\n\n  'sys_fleet_arrived'   => \"Флот прибыл\",\n\n  'sys_planet_type' => [\n    PT_PLANET => 'Планета',\n    PT_DEBRIS => 'Поле обломков',\n    PT_MOON   => 'Луна',\n  ],\n\n  'sys_planet_type_sh' => [\n    PT_PLANET => '(П)',\n    PT_DEBRIS => '(О)',\n    PT_MOON   => '(Л)',\n  ],\n\n  'sys_planet_expedition' => 'неисследованное пространство',\n\n  'sys_capacity' \t\t\t=> 'Грузоподъёмность',\n  'sys_cargo_bays' \t\t=> 'Трюмы',\n\n  'sys_supernova' \t\t=> 'СуперНова',\n  'sys_server' \t\t\t=> 'Сервер',\n\n  'sys_unbanned'\t\t\t=> 'Разблокирован',\n\n  'sys_date_time'\t\t\t=> 'Дата и время',\n  'sys_from_person'\t   => 'От кого',\n  'sys_from_speed'\t   => 'от',\n\n  'sys_from'\t\t  => 'с',\n  'tp_on'            => 'на',\n\n// Resource page\n  'res_planet_production' => 'Производство ресурсов на планете',\n  'res_basic_starting_resources' => 'Стартовые ресурсы на планете',\n  'res_basic_income' => 'Естественное производство',\n  'res_basic_storage_size' => 'Размер складов',\n  'res_total' => 'ВСЕГО',\n  'res_calculate' => 'Рассчитать',\n  'res_hourly' => 'В час',\n  'res_daily' => 'За день',\n  'res_weekly' => 'За неделю',\n  'res_monthly' => 'За месяц',\n  'res_storage_fill' => 'Заполненность складов',\n  'res_hint' => '<ul><li>Производство ресурсов <100% означает нехватку энергии. Постройте дополнительные электростанции или уменьшите производство ресурсов<li>Если ваше производство равно 0% скорее всего вы вышли из отпуска и вам нужно включить все заводы<li>Что бы выставить добычу для всех заводов сразу используйте дроп-даун в загловке таблицы. Особенно удобно использовать его после выхода из отпуска</ul>',\n\n// Build page\n  'bld_destroy' => 'Уничтожить',\n  'bld_create'  => 'Построить',\n  'bld_research' => 'Исследовать',\n  'bld_hire' => 'Нанять',\n\n// Imperium page\n  'imp_imperator' => \"Император\",\n  'imp_overview' => \"Обзор Империи\",\n  'imp_fleets' => \"Флоты в полете\",\n  'imp_production' => \"Производство\",\n  'imp_name' => \"Название\",\n  'imp_research' => \"Исследования\",\n  'imp_exploration' => \"Экспедиции\",\n  'imp_imperator_none' => \"Нет такого Императора во Вселенной!\",\n  'sys_fields' => \"Сектора\",\n\n// Cookies\n  'err_cookie' => \"Ошибка! Невозможно авторизировать пользователя по информации в cookie.<br />Очистите куки браузера, затем еще раз попытайтесь <a href='login\" . DOT_PHP_EX . \"'>войти</a> в игру или <a href='reg\" . DOT_PHP_EX . \"'>зарегестрироваться</a>.\",\n\n// Supported languages\n  'ru'              \t  => 'Русский',\n  'en'              \t  => 'Английский',\n\n  'sys_vacation'        => 'Вы же в отпуске до',\n  'sys_vacation_leave'  => 'Я уже отдохнул - выйти из отпуска!',\n  'sys_vacation_in'     => 'В отпуске',\n  'sys_level'           => 'Уровень',\n  'sys_level_short'     => 'Ур',\n  'sys_level_max'       => 'Максимальный уровень',\n\n  'sys_yes'             => 'Да',\n  'sys_no'              => 'Нет',\n\n  'sys_on'              => 'Включен',\n  'sys_off'             => 'Отключен',\n\n  'sys_confirm'         => 'Подтвердить',\n  'sys_save'            => 'Сохранить',\n  'sys_create'          => 'Создать',\n  'sys_write_message'   => 'Написать сообщение',\n\n// top bar\n  'top_of_year' => 'г.',\n  'top_online'\t\t\t=> 'Игроки',\n\n  'sys_first_round_crash_1'\t=> 'Контакт с атакованным флотом потерян.',\n  'sys_first_round_crash_2'\t=> 'Это означает что он был уничтожен в первом раунде боя.',\n\n  'sys_ques' => [\n    QUE_STRUCTURES => 'Здания',\n    QUE_HANGAR     => 'Верфь',\n    SUBQUE_DEFENSE => 'Оборона',\n    QUE_RESEARCH   => 'Исследования',\n  ],\n\n  'navbar_button_expeditions_short' => 'Экспа',\n  'navbar_button_fleets' => 'Флоты',\n  'navbar_button_quests' => 'Квесты',\n  'navbar_font' => 'Шрифт',\n  'navbar_font_normal' => 'Норма',\n  'sys_que_structures' => 'Здания',\n  'sys_que_hangar' => 'Верфь',\n  'sys_que_defense' => 'Оборона',\n  'sys_que_research' => 'Исследования',\n  'sys_que_research_short' => 'Наука',\n\n  'eco_que' => 'Очередь',\n  'eco_que_empty' => 'Очередь пуста',\n  'eco_que_clear' => 'Очистить очередь',\n  'eco_que_trim'  => 'Отменить последнее',\n  'eco_que_artifact'  => 'Использовать Артефакт',\n\n  'sys_cancel' => 'Отменить',\n\n  'sys_overview'\t\t\t=> 'Обзор',\n  'mod_marchand'\t\t\t=> 'Торговец',\n  'sys_galaxy'\t\t\t=> 'Галактика',\n  'sys_system'\t\t\t=> 'Система',\n  'sys_planet'\t\t\t=> 'Планета',\n  'sys_planet_title'\t\t\t=> 'Тип планеты',\n  'sys_planet_title_short'\t\t\t=> 'Тип',\n  'sys_moon'\t\t\t=> 'Луна',\n  'sys_error'\t\t\t=> 'Ошибка',\n  'sys_done'\t\t\t\t=> 'Готово',\n  'sys_no_vars'\t\t\t=> 'Ошибка инициализации переменных, обратитесь к администрации!',\n  'sys_attacker_lostunits'\t\t=> 'Атакующий потерял %s единиц.',\n  'sys_defender_lostunits'\t\t=> 'Обороняющийся потерял %s единиц.',\n  'sys_gcdrunits' \t\t\t=> 'Теперь на этих пространственных координатах находятся %s %s и %s %s.',\n  'sys_moonproba' \t\t\t=> 'Шанс появления луны составляет: %d %% ',\n  'sys_moonbuilt' \t\t\t=> 'Благодаря огромной энергии огромные куски металла и кристалла соединяются и образуется новая луна %s %s!',\n  'sys_attack_title'    \t\t=> '%s. Произошёл бой между следующими флотами::',\n  'sys_attack_attacker_pos'      \t=> 'Атакующий %s [%s:%s:%s]',\n  'sys_attack_techologies' \t=> 'Вооружение: %d %% Щиты: %d %% Броня: %d %% ',\n  'sys_attack_defender_pos' \t=> 'Обороняющийся %s [%s:%s:%s]',\n  'sys_ship_type' \t\t\t=> 'Тип',\n  'sys_ship_count' \t\t=> 'Кол-во',\n  'sys_ship_weapon' \t\t=> 'Вооружение',\n  'sys_ship_shield' \t\t=> 'Щиты',\n  'sys_ship_armour' \t\t=> 'Броня',\n  'sys_ship_speed' \t\t=> 'Скорость',\n  'sys_ship_consumption' \t\t=> 'Потребление',\n  'sys_ship_capacity' \t\t=> 'Трюм/Бак',\n  'sys_destroyed' \t\t\t=> 'уничтожен',\n  'sys_attack_attack_wave' \t=> 'Атакующий делает выстрелы общей мощностью %s по обороняющемуся. Щиты обороняющегося поглощают %s выстрелов.',\n  'sys_attack_defend_wave'\t\t=> 'Обороняющийся делает выстрелы общей мощностью %s по атакующему. Щиты атакующего поглащают %s выстрелов.',\n  'sys_attacker_won' \t\t=> 'Атакующий выиграл битву!',\n  'sys_defender_won' \t\t=> 'Обороняющийся выиграл битву!',\n  'sys_both_won' \t\t\t=> 'Бой закончился ничьёй!',\n  'sys_stealed_ressources' \t=> 'Он получает %s металла %s %s кристалла %s и %s дейтерия.',\n  'sys_rapport_build_time' \t=> 'Время генерации страницы %s секунд',\n  'sys_mess_tower' \t\t=> 'Транспорт',\n  'sys_coe_lost_contact' \t\t=> 'Связь с вашим флотом потеряна',\n  'sys_spy_activity' => 'Наблюдается шпионская активность возле ваших планет',\n  'sys_spy_materials' \t\t=> 'Сырьё на',\n  'sys_spy_fleet' \t\t\t=> 'Флот',\n  'sys_spy_defenses' \t\t=> 'Оборона',\n  'sys_mess_qg' \t\t\t=> 'Командование флотом',\n  'sys_mess_spy_report' \t\t=> 'Шпионский доклад',\n  'sys_mess_spy_lostproba' \t=> 'Погрешность информации, полученной спутником %d %% ',\n  'sys_mess_spy_detect_chance' \t=> 'Шанс обнаружения вашего разведывательного флота %d%%',\n  'sys_mess_spy_detect_chance_no_percent' \t=> 'Шанс обнаружения вашего разведывательного флота',\n  'sys_mess_spy_control' \t\t=> 'Контрразведка',\n  'sys_mess_spy_activity' \t\t=> 'Шпионская активность',\n  'sys_mess_spy_enemy_fleet' \t=> 'Чужой флот с планеты',\n  'sys_mess_spy_seen_at'\t\t=> 'был обнаружен возле планеты',\n  'sys_mess_spy_destroyed'\t\t=> 'Разведывательный флот был уничтожен',\n  'sys_mess_spy_destroyed_enemy'\t\t=> 'Вражеский шпионский флот уничтожен',\n  'sys_object_arrival'\t\t=> 'Прибыл на планету',\n  'sys_stay_mess_stay' => 'Передислокация флота',\n  'sys_stay_mess_start' \t\t=> 'Ваш флот прибыл на планету',\n  'sys_stay_mess_back'\t\t=> 'Ваш флот вернулся ',\n  'sys_stay_mess_end'\t\t=> ' и доставил:',\n  'sys_stay_mess_bend'\t\t=> ' и доставил следующие ресурсы:',\n  'sys_address_planet' \t\t=> '[%s:%s:%s]',\n  'sys_stay_mess_goods' \t\t=> '%s : %s, %s : %s, %s : %s',\n  'sys_colo_mess_from' \t\t=> 'Колонизация',\n  'sys_colo_mess_report' \t\t=> 'Отчёт о колонизации',\n  'sys_colo_default_name' \t\t=> 'Колония',\n  'sys_colo_arrival' \t\t=> 'Флот достигает координат ',\n  'sys_colo_max_colo' \t\t=> ', но колонизировать планету нельзя, достигнуто максимальное число колоний для вашего уровня колонизации',\n  'sys_colo_all_is_ok' \t\t=> ', и колонисты начинают осваивать новую планету.',\n  'sys_colo_bad_pos'  \t\t\t=> ', и колонисты нашли среду мало выгодной для Вашей империи. Миссия колонизации возвращается обратно на планету отправки.',\n  'sys_colo_not_free' \t\t\t=> ', и колонисты не нашли планету в этих координатах. Они вынуждены проложить дорогу обратно абсолютно обескураженными.',\n  'sys_colo_no_colonizer'     => 'Во флоте нет колонизатора',\n  'sys_colo_planet'  \t\t=> ' Планета колонизирована!',\n  'sys_expe_report' \t\t=> 'Отчёт экспедиции',\n  'sys_recycler_report' \t\t=> 'Системная информация',\n  'sys_expe_blackholl_1' \t\t=> 'Ваш флот попал в чёрную дыру и частично потерян!',\n  'sys_expe_blackholl_2' \t\t=> 'Ваш флот попал в чёрную дыру и полностью потерян!',\n  'sys_expe_nothing_1' \t\t=> 'Ваш исследователи стали свидетелями СуперНовы! И ваши накопители успели принять часть высвободившейся энергии.',\n  'sys_expe_nothing_2' \t\t=> 'Ваш исследователи ничего не обнаружили!',\n  'sys_expe_found_goods' \t\t=> 'Ваш исследователи нашли планету, богатую сырьём!<br>Вы получили %s %s, %s %s и %s %s',\n  'sys_expe_found_ships' \t\t=> 'Ваш исследователи нашли безупречно новый флот!<br>Вы получили: ',\n  'sys_expe_back_home' \t\t=> 'Ваш флот возвращается обратно.',\n  'sys_mess_transport' \t\t=> 'Транспорт',\n//  'sys_tran_mess_owner' \t\t=> 'Один из ваших флотов достигает планеты %s %s и доставляет %s %s, %s  %s и %s %s.',\n  'sys_tran_mess_user'  \t\t=> 'Флот с планеты %s %s прибыл на %s %s и доставил %s %s, %s %s и %s %s.',\n  'sys_relocate_mess_user'  \t\t=> 'Так же на планету передислоцированы следующие боевые единицы:<br />',\n  'sys_mess_fleetback' \t\t=> 'Возвращение',\n  'sys_tran_mess_back' \t\t=> 'Один из ваших флотов возвращается на планету %s %s.',\n  'sys_recycler_gotten' \t\t=> 'Один из Ваших флотов добыл %s %s и %s %s Возвращается на планету.',\n  'sys_notenough_money' \t\t=> 'Вам не хватает ресурсов, чтобы построить: %s. У Вас сейчас: %s %s , %s %s и %s %s. Для строительства необходимо: %s %s , %s %s и %s %s.',\n  'sys_nomore_level'\t\t=> 'Вы больше не можете совершенствовать это. Оно достигло макс. уровня ( %s ).',\n  'sys_buildlist' \t\t\t=> 'Список построек',\n  'sys_buildlist_fail' \t\t=> 'нет построек',\n  'sys_gain' \t\t\t=> 'Добыча: ',\n  'sys_debris' \t\t\t=> 'Обломки: ',\n  'sys_noaccess' \t\t\t=> 'В доступе отказано',\n  'sys_noalloaw' \t\t\t=> 'Вам закрыт доступ в эту зону!',\n  'sys_governor'        => 'Губернатор',\n\n  'flt_error_duration_wrong' => 'Невозможно отправить флот - нет доступных интервалов для задержки. Изучите еще уровни Астрокартографии',\n  'flt_stay_duration' => 'Время',\n\n  'flt_mission_expedition' => [\n    'msg_sender' => 'Отчет экспедиции',\n    'msg_title' => 'Отчет экспедиции',\n\n    'found_dark_matter' => 'Получено %1$d единиц ТМ',\n    'found_resources' => \"Найдены ресурсы:\\r\\n\",\n    'found_fleet' => \"Найдены корабли:\\r\\n\",\n    'lost_fleet' => \"Потеряны следующие корабли:\\r\\n\",\n\n    'outcomes' => [\n      Constants::OUTCOME_NONE => [\n        'messages' => [\n          'Ваши исследователи ничего не обнаружили',\n        ],\n      ],\n\n      Constants::EXPEDITION_OUTCOME_LOST_FLEET => [\n        'messages' => [\n          'Флот попал в черную дыру и частично утерян',\n        ],\n      ],\n\n      Constants::EXPEDITION_OUTCOME_LOST_FLEET_ALL => [\n        'messages' => [\n          'Если бы вы только это видели! Оно такое красивое... Оно зовёт к себе... (связь с флотом утеряна)',\n          // 'Отчёт флота %1$s. Мы завершили исследование сектора. Команда недовольна Эй, ты что делаешь на мостике?! (связь с флотом утеряна)',\n          'Отчёт флота %1$s. Всё спокойно (помехи) (связь с флотом утеряна)',\n          'АААААА! ЧТО ЭТО?! ОТКУДА ОНО ВЗЯ (связь с флотом утеряна)',\n          'Обнаружен неизвестный объект. Он не отвечает на запросы стандартных протоколов. Высылаем зонд для проведения исследований (связь с флотом утеряна)',\n        ],\n      ],\n\n      Constants::EXPEDITION_OUTCOME_FOUND_FLEET => [\n        'no_result' => 'К сожалению, совокупной мощности всех компьютеров флота не хватило даже на контроль самого мелкого корабля. Попробуйте отправлять больше кораблей и/или более крупные корабли',\n        'messages' => [\n          0 => [\n            'Вы нашли абсолютно новый флот',\n          ],\n          1 => [\n            'Вы нашли флот',\n          ],\n          2 => [\n            'Вы нашли б/у флот',\n          ],\n        ],\n      ],\n\n      Constants::EXPEDITION_OUTCOME_FOUND_RESOURCES => [\n        'no_result' => 'Трюмы вашего флота оказались неспособны вместить хоть один контейнер с ресурсами. Попробуйте отправлять флот с большим количеством транспортников',\n        'messages' => [\n          0 => [\n            'Вы нашли пиратский клад с ресурсами. Сколько же кораблей было уничтожено, что бы собрать столько добра?',\n          ],\n          1 => [\n            'Вы нашли заброшенную астероидную базу. Интересно, куда делись её обитатели? Исследовав руины, вы нашли несколько уцелевших складов с ресурсами',\n          ],\n          2 => [\n            'Вы наткнулись на уничтоженный транспортный конвой. Обыскав трюмы разбитых кораблей, вы обнаружили немного ресурсов',\n          ],\n        ],\n      ],\n\n      Constants::EXPEDITION_OUTCOME_FOUND_DM => [\n        'no_result' => 'К сожалению, всех накопителей флота не хватило что бы собрать одну-единственую ТМ. Попробуйте отправлять флот побольше',\n        'messages' => 'Ваш флот стал свидетелем рождения СуперНовы',\n        // 'messages' => array(\n        //   'Ваш флот стал свидетелем рождения СуперНовы 1',\n        //   'Ваш флот стал свидетелем рождения СуперНовы 2',\n        //   'Ваш флот стал свидетелем рождения СуперНовы 3',\n        // ),\n      ],\n\n    ],\n  ],\n\n  // News page & a bit of imperator page\n  'news_fresh'      => 'Свежие новости',\n  'news_all'        => 'Все новости',\n  'news_title'      => 'Новости',\n  'news_none'       => 'Нет новостей',\n  'news_new'        => 'СВЕЖАЯ',\n  'news_future'     => 'АНОНС',\n  'news_more'       => 'Подробнее...',\n  'news_hint'       => 'Что бы убрать список последних новостей - прочтите их все, кликнув на заголовке \"[ Новости ]\"',\n\n  'news_date'       => 'Дата',\n  'news_announce'   => 'Содержание',\n  'news_detail_url' => 'Ссылка на подробности',\n  'news_mass_mail'  => 'Разослать новость всем игрокам',\n\n  'news_total'      => 'Всего новостей: ',\n\n  'news_add'        => 'Добавить новость',\n  'news_edit'       => 'Редактировать новость',\n  'news_copy'       => 'Скопировать новость',\n  'news_mode_new'   => 'Новая',\n  'news_mode_edit'  => 'Редактирование',\n  'news_mode_copy'  => 'Копия',\n\n  'sys_administration' => 'Администрация сервера',\n\n  'note_add'        => 'Добавить заметку',\n  'note_del'        => 'Удалить заметку',\n  'note_edit'        => 'Изменить заметку',\n\n  // Shortcuts\n  'shortcut_title'     => 'Закладки',\n  'shortcut_none'      => 'Нет закладок',\n  'shortcut_new'       => 'НОВАЯ',\n  'shortcut_text'      => 'Текст',\n\n  'shortcut_add'       => 'Добавить закладку',\n  'shortcut_edit'      => 'Редактировать закладку',\n  'shortcut_copy'      => 'Скопировать закладку',\n  'shortcut_mode_new'  => 'Новая',\n  'shortcut_mode_edit' => 'Редактирование',\n  'shortcut_mode_copy' => 'Копия',\n\n  // Missile-related\n  'mip_h_launched'\t\t\t=> 'Запуск межпланетных ракет',\n  'mip_launched'\t\t\t\t=> 'Запущено межпланетных ракет: <b>%s</b>!',\n\n  'mip_no_silo'\t\t\t\t=> 'Недостаточен уровень ракетных шахт на планете <b>%s</b>.',\n  'mip_no_impulse'\t\t\t=> 'Необходимо исследовать импульсный двигатель.',\n  'mip_too_far'\t\t\t\t=> 'Ракета не может лететь так далеко.',\n  'mip_planet_error'\t\t\t=> 'Ошибка - больше одной планеты по одной координате',\n  'mip_no_rocket'\t\t\t\t=> 'Недостаточно ракет в шахте для проведения атаки.',\n  'mip_hack_attempt'\t\t\t=> ' Ты чо хакер? Еще один такой прикол и будешь забанен. ip адрес и логин я записал.',\n\n  'mip_all_destroyed' \t\t=> 'Все межпланетные ракеты были уничтожены ракетами-перехватчиками<br>',\n  'mip_destroyed'\t\t\t\t=> '%s межпланетых ракет были уничтожены ракетами-перехватчиками.<br>',\n  'mip_defense_destroyed'\t=> 'Уничтожены следующие оборонительные сооружения:<br />',\n  'mip_recycled'\t\t\t\t=> 'Переработано из обломков защитных сооружений: ',\n  'mip_no_defense'\t\t\t=> 'На атакуемой планете не было защиты!',\n\n  'mip_sender_amd'\t\t\t=> 'Ракетно-космические войска',\n  'mip_subject_amd'\t\t\t=> 'Ракетная атака',\n  'mip_body_attack'\t\t\t=> 'Атака межпланетными ракетами (%1$s шт.) с планеты %2$s <a href=\"galaxy.php?mode=3&galaxy=%3$d&system=%4$d&planet=%5$d\">[%3$d:%4$d:%5$d]</a> на планету %6$s <a href=\"galaxy.php?mode=3&galaxy=%7$d&system=%8$d&planet=%9$d\">[%7$d:%8$d:%9$d]</a><br><br>',\n\n  // Misc\n  'sys_game_rules' => 'Правила игры',\n  'sys_game_documentation' => 'Описание игры',\n  'sys_banned_msg' => 'Вы забанены. Для получения информации зайдите <a href=\"banned.php\">сюда</a>. Срок окончания блокировки аккаунта: ',\n  'sys_total_time' => 'Общее время',\n  'sys_total_time_short' => 'Очередь',\n  'eco_que_finish' => 'Завершение',\n\n  // Universe\n  'uni_moon_of_planet' => 'планеты',\n\n  // Combat reports\n  'cr_view_title'  => \"Просмотр боевых отчетов\",\n  'cr_view_button' => \"Просмотреть отчет\",\n  'cr_view_prompt' => \"Введите код\",\n  'cr_view_my'     => \"Мои боевые отчеты\",\n  'cr_view_hint'   => '<ul><li>Свои боевые отчеты можно посмотреть, кликнув по ссылке \"Мои боевые отчеты\" в заголовке</li><li>Код боевого отчета указывается в его последней строке и является последовательностью 32 цифр и символов латинского алфавита</li></ul>',\n\n  // Fleet\n  'flt_gather_all'    => 'Свезти ресурсы',\n\n  // Ban system\n  'ban_title'      => 'Чёрный список',\n  'ban_name'       => 'Имя',\n  'ban_reason'     => 'Причина блокировки',\n  'ban_from'       => 'Дата блокировки',\n  'ban_to'         => 'Срок блокировки',\n  'ban_by'         => 'Выдал',\n  'ban_no'         => 'Нет заблокированных игроков',\n  'ban_thereare'   => 'Всего',\n  'ban_players'    => 'заблокировано',\n  'ban_banned'     => 'Игроков заблокировано: ',\n\n  // Contacts\n  'ctc_title' => 'Администрация',\n  'ctc_intro' => 'Здесь вы найдёте адреса всех администраторов и операторов игры для обратной связи',\n  'ctc_name'  => 'Имя',\n  'ctc_rank'  => 'Звание',\n  'ctc_mail'  => 'eMail',\n\n  // Records page\n  'rec_title'  => 'Рекорды Вселенной',\n  'rec_build'  => 'Постройки',\n  'rec_specb'  => 'Специальные постройки',\n  'rec_playe'  => 'Игрок',\n  'rec_defes'  => 'Оборона',\n  'rec_fleet'  => 'Флот',\n  'rec_techn'  => 'Технологии',\n  'rec_level'  => 'Уровень',\n  'rec_nbre'   => 'Количество',\n  'rec_rien'   => '-',\n\n  // Credits page\n  'cred_link'    => 'Интернет',\n  'cred_site'    => 'Сайт',\n  'cred_forum'   => 'Форум',\n  'cred_credit'  => 'Авторы',\n  'cred_creat'   => 'Директор',\n  'cred_prog'    => 'Программист',\n  'cred_master'  => 'Ведущий',\n  'cred_design'  => 'Дизайнер',\n  'cred_web'     => 'Вебмастер',\n  'cred_thx'     => 'Благодарности',\n  'cred_based'   => 'Основа для создания XNova',\n  'cred_start'   => 'Место дебюта XNova',\n\n  // Built-in chat\n  'chat_common'   => 'Общий чат',\n  'chat_ally'     => 'Чат Альянса',\n  'chat_history'  => 'История чата',\n  'chat_message'  => 'Сообщение',\n  'chat_send'     => 'Отправить',\n  'chat_page'     => 'Страница',\n  'chat_timeout'  => 'Чат отключен из-за вашей неактивности. Обновите страницу.',\n\n  // ----------------------------------------------------------------------------------------------------------\n  // Interface of Jump Gate\n  'gate_start_moon' => 'Начальная Луна',\n  'gate_dest_moon'  => 'Конечная Луна',\n  'gate_use_gate'   => 'Использовать врата',\n  'gate_ship_sel'   => 'Выделить корабли',\n  'gate_ship_dispo' => 'доступно',\n  'gate_jump_btn'   => 'Выполнить прыжок!!',\n  'gate_jump_done'  => 'врата находятся в стадии перезарядки!<br>Врата будут готовы к использованию через: ',\n  'gate_wait_dest'  => 'Точка назначения врат в стадии подготовки! Врата будут готовы к использованию через: ',\n  'gate_no_dest_g'  => 'На конечной точке назначения не обнаруженно врат для перемещения флота',\n  'gate_no_src_ga'  => 'Нет врат для перемещения флота',\n  'gate_wait_star'  => 'врата находятся в стадии перезарядки!<br>ворота будут готовы к использованию через: ',\n  'gate_wait_data'  => 'Ошибка, нет данных для прыжка!',\n  'gate_vacation'   => 'Ошибка, Вы не можете совершить прыжок т.к. находитесь в Режиме Отпуска !',\n  'gate_ready'      => 'Врата готовы к прыжку',\n\n  // quests\n  'qst_quests'               => 'Квесты',\n  'qst_msg_complete_subject' => 'Квест закончен',\n  'qst_msg_complete_body'    => 'Вы выполнили квест \"%s\".',\n  'qst_msg_your_reward'      => 'Ваша награда:',\n\n  // Messages\n  'msg_from_admin' => 'Администрация Вселенной',\n  'msg_class' => [\n    MSG_TYPE_OUTBOX => 'Отправленные сообщения',\n    MSG_TYPE_SPY => 'Шпионские отчёты',\n    MSG_TYPE_PLAYER => 'Сообщения от игроков',\n    MSG_TYPE_ALLIANCE => 'Сообщения Альянса',\n    MSG_TYPE_COMBAT => 'Военные отчёты',\n    MSG_TYPE_RECYCLE => 'Отчеты переработки',\n    MSG_TYPE_TRANSPORT => 'Прибытие флота',\n    MSG_TYPE_ADMIN => 'Сообщения Администрации',\n    MSG_TYPE_EXPLORE => 'Отчёты экспедиций',\n    MSG_TYPE_QUE => 'Сообщения очереди построек',\n    MSG_TYPE_NEW => 'Все сообщения',\n  ],\n\n  'msg_que_research_from'    => 'Научно-исследовательский институт',\n  'msg_que_research_subject' => 'Новая технология',\n  'msg_que_research_message' => 'Исследована новая технология \\'%s\\'. Новый уровень - %d',\n\n  'msg_que_planet_from'    => 'Губернатор',\n\n  'msg_que_hangar_subject' => 'Работа на верфи завершена',\n  'msg_que_hangar_message' => \"Верфь на %s завершила работу\",\n\n  'msg_que_built_subject'   => 'Планетарные работы завершены',\n  'msg_que_built_message'   => \"Завершено строительство здания '%2\\$s' на %1\\$s. Построено уровней: %3\\$d\",\n  'msg_que_destroy_message' => \"Завершено разрушение здания '%2\\$s' на %1\\$s. Разрушено уровней: %3\\$d\",\n\n  'msg_personal_messages' => 'Личные сообщения',\n\n  'sys_opt_bash_info'    => 'Настройки системы антибашинга',\n  'sys_opt_bash_attacks' => 'Количество атак в одной волне',\n  'sys_opt_bash_interval' => 'Интервал между волнами',\n  'sys_opt_bash_scope' => 'Период расчета башинга',\n  'sys_opt_bash_war_delay' => 'Мораторий после объявления войны',\n  'sys_opt_bash_waves' => 'Количество волн за один период',\n  'sys_opt_bash_disabled'    => 'Система антибашинга отключена',\n\n  'sys_id' => 'ИД',\n  'sys_identifier' => 'Идентификатор',\n\n  'sys_email'   => 'Е-Мейл',\n  'sys_ip' => 'IP',\n\n  'sys_max' => 'Макс',\n  'sys_maximum' => 'Максимум',\n  'sys_maximum_level' => 'Максимальный уровень',\n\n  'sys_user_name' => 'Имя пользователя',\n  'sys_player_name' => 'Имя игрока',\n  'sys_user_name_short' => 'Имя',\n\n  'sys_planets' => 'Планеты',\n  'sys_moons' => 'Луны',\n\n  'sys_quantity' => 'Количество',\n  'sys_quantity_maximum' => 'Максимальное количество',\n  'sys_qty' => 'К-во',\n  'sys_quantity_total' => 'Общее количество',\n\n  'sys_buy_for' => 'Купить за',\n  'sys_buy' => 'Купить',\n  'sys_for' => 'за',\n\n  'sys_eco_lack_dark_matter' => 'Не хватает Тёмной Материи',\n\n  'time_local' => 'Время у игрока',\n  'time_server' => 'Время на сервере',\n\n  'sys_result' => [\n    'error_dark_matter_not_enough' => 'Не хватает Тёмной Материи для завершения операции',\n    'error_dark_matter_change' => 'Ошибка изменения количества Тёмной Материи! Повторите операцию еще раз. Если ошибка повторится - сообщите Администрации сервера',\n  ],\n\n  // Arrays\n  'sys_build_result' => [\n    BUILD_ALLOWED => 'Можно построить',\n    BUILD_REQUIRE_NOT_MEET => 'Требования не удовлетворены',\n    BUILD_AMOUNT_WRONG => 'Слишком много',\n    BUILD_QUE_WRONG => 'Несуществующая очередь',\n    BUILD_QUE_UNIT_WRONG => 'Неправильная очередь',\n    BUILD_INDESTRUCTABLE => 'Нельзя уничтожить',\n    BUILD_NO_RESOURCES => 'Не хватает ресурсов',\n    BUILD_NO_UNITS => 'Нет юнитов',\n    BUILD_UNIT_BUSY => [\n      0 => 'Строение занято',\n      STRUC_LABORATORY => 'Идет исследование',\n      STRUC_LABORATORY_NANO => 'Идет исследование',\n    ],\n    BUILD_QUE_FULL => 'Очередь полна',\n    BUILD_SILO_FULL => 'Ракетная шахта заполнена',\n    BUILD_MAX_REACHED => 'Вы уже построили и/или поставили в очередь максимальное количество юнитов данного типа',\n    BUILD_SECTORS_NONE => 'Нет свободных секторов',\n    BUILD_AUTOCONVERT_AVAILABLE => 'Доступна автоконвертация',\n    BUILD_HIGHSPOT_NOT_ACTIVE => 'Ивент не активен',\n  ],\n\n  'sys_game_mode' => [\n    GAME_SUPERNOVA => 'СуперНова',\n    GAME_OGAME     => 'оГейм',\n    GAME_BLITZ     => 'Блиц-сервер',\n  ],\n\n  'months' => [\n     1 =>'января',\n     2 =>'февраля',\n     3 =>'марта',\n     4 =>'апреля',\n     5 =>'мая',\n     6 =>'июня',\n     7 =>'июля',\n     8 =>'августа',\n     9 =>'сентября',\n    10 =>'октября',\n    11 =>'ноября',\n    12 =>'декабря'\n  ],\n\n  'weekdays' => [\n    0 => 'Воскресенье',\n    1 => 'Понедельник',\n    2 => 'Вторник',\n    3 => 'Среда',\n    4 => 'Четверг',\n    5 => 'Пятница',\n    6 => 'Суббота'\n  ],\n\n  'user_level' => [\n    0 => 'Игрок',\n    1 => 'Модератор',\n    2 => 'Оператор',\n    3 => 'Администратор',\n    4 => 'Разработчик',\n  ],\n\n  'user_level_shortcut' => [\n    0 => 'И',\n    1 => 'М',\n    2 => 'О',\n    3 => 'А',\n    4 => 'Р',\n  ],\n\n  'sys_lessThen15min'   => '&lt; 15 мин',\n\n  'sys_no_points'         => 'У вас недостаточно <span class=\"dark_matter\">Тёмной Материи</span>!',\n  'sys_dark_matter_obtain_header' => 'Как получить <span class=\"dark_matter\">Тёмную Материю</span>',\n  'sys_dark_matter_desc' => 'Тёмная материя - необнаружимая стандартными методами небарионная материя, на которую приходится 23% массы Вселенной. Из неё можно добывать невероятное количество энергии. Из-за этого, а так же из-за сложностей, связанных с её добычей, Тёмная Материя ценится очень высоко.',\n  'sys_dark_matter_hint' => 'При помощи этой субстанции можно нанять офицеров и командиров.',\n\n  'sys_dark_matter_what_why_how' => 'Что такое <span class=\"dark_matter\">Тёмная Материя</span> и <span class=\"metamatter\">Метаматерия</span>',\n  'sys_dark_matter_what_header' => 'Что такое <span class=\"dark_matter\">Тёмная Материя</span>',\n  'sys_dark_matter_description_header' => 'Зачем нужна <span class=\"dark_matter\">Тёмная Материя</span>',\n  'sys_dark_matter_description_text' => '<span class=\"dark_matter\">Тёмная Материя</span> - это внутриигровой ресурс, за счет которой в игре вы можете совершать различные операции:\n    <ul>\n      <li>Покупать <a href=\"index.php?page=premium\"><span class=\"link\">Премиум-аккаунт</span></a></li>\n      <li>Рекрутировать <a href=\"officer.php?mode=600\"><span class=\"link\">Наемников</span></a> в Империю </li>\n      <li>Нанимать Губернаторов и покупать дополнительные сектора <a href=\"overview.php?mode=manage\"><span class=\"link\">на планеты</span></a></li>\n      <li>Покупать <a href=\"officer.php?mode=1100\"><span class=\"link\">Чертежи</span></a></li>\n      <li>Покупать <a href=\"artifacts.php\"><span class=\"link\">Артефакты</span></a></li>\n      <li>Использовать <a href=\"market.php\"><span class=\"link\">Чёрный Рынок</span></a>: Обменивать один вид ресурсов на другой; продавать корабли; покупать Б/У корабли итд</li>\n      <li>...и многое, многое другое</li>\n    </ul>',\n  'sys_dark_matter_obtain_text' => 'Вы получаете <span class=\"metamatter\">Тёмную Материю</span> в процессе игры: набирая опыт за успешные рейды на чужие планеты, исследование новых технологий, а так же за постройку и разрушение зданий.\n    Так же иногда исследовательские экспедиции могут принести <span class=\"metamatter\">ТМ</span>.',\n\n  // 'sys_dark_matter_obtain_text_convert' => '<br /><br />Кроме того, вы можете сконвертировать Метаматерию в Тёмную Материю. <a href=\"metamatter.php\" class=\"link\">Узнать подробнее про Метаматерию</a>',\n  'sys_dark_matter_obtain_text_convert' => '<br />Если вам не хватает <span class=\"dark_matter\">Тёмной Материи</span> - приобретите <span class=\"metamatter\">Метаматерию</span>. В случае недостатка <span class=\"dark_matter\">ТМ</span> нужное количество <span class=\"metamatter\">Метаматерии</span> будет использована вместо <span class=\"dark_matter\">ТМ</span>',\n\n  'sys_msg_err_update_dm' => 'Ошибка обновления количества ТМ!',\n\n  'sys_na' => 'Не доступен',\n  'sys_na_short' => 'Н/Д',\n\n  'sys_ali_res_title' => 'Ресурсы Альянса',\n\n  'sys_bonus' => 'Бонус',\n\n  'sys_of_ally' => 'Альянса',\n\n  'sys_hint_player_name' => 'Поиск игрока может производиться по идентификатору или имени. Если имя игрока состоит из нечитаемых символов или только из цифр - для поиска нужно использовать идентификатор',\n  'sys_hint_ally_name' => 'Поиск Альянса может производиться по идентификатору, тэгу или имени. Если тэг или название Альянса состоят из нечитаемых символов или только из цифр - для поиска нужно использовать идентификатор',\n\n  'sys_fleet_and' => '+ флоты',\n\n  'sys_on_planet' => 'На планете',\n  'fl_on_stores' => 'На складе',\n\n  'sys_ali_bonus_members' => 'Минимальное размер Альянса для получения бонуса',\n\n  'sys_premium' => 'Премиум',\n\n  'mrc_period_list' => [\n    PERIOD_MINUTE    => '1 минута',\n    PERIOD_MINUTE_3  => '3 минуты',\n    PERIOD_MINUTE_5  => '5 минут',\n    PERIOD_MINUTE_10 => '10 минут',\n    PERIOD_DAY       => '1 день',\n    PERIOD_DAY_3     => '3 дня',\n    PERIOD_WEEK      => '1 неделя',\n    PERIOD_WEEK_2    => '2 недели',\n    PERIOD_MONTH     => '30 дней',\n    PERIOD_MONTH_2   => '60 дней',\n    PERIOD_MONTH_3   => '90 дней',\n  ],\n\n  'sys_sector_buy' => 'Купить 1 сектор',\n\n  'sys_select_confirm' => 'Подвердить выбор',\n\n  'sys_capital' => 'Столица',\n\n  'sys_result_operation' => 'Сообщения',\n\n  'sys_password' => 'Пароль',\n  'sys_password_length' => 'Длина пароля',\n  'sys_password_seed' => 'Используемые символы',\n\n  'sys_msg_ube_report_err_not_found' => 'Боевой отчет не найден. Проверьте правильность ключа. Так же есть вероятность, что отчет удален как устаревший',\n\n  'sys_mess_attack_report' \t=> 'Боевой отчет',\n  'sys_perte_attaquant' \t\t=> 'Атакующий потерял',\n  'sys_perte_defenseur' \t\t=> 'Обороняющийся потерял',\n\n\n\n  'ube_report_info_page_header' => 'Боевой отчёт',\n  'ube_report_info_page_header_cypher' => 'Код допуска',\n  'ube_report_info_main' => 'Основная информация о бое',\n  'ube_report_info_date' => 'Дата и время',\n  'ube_report_info_location' => 'Место',\n  'ube_report_info_rounds_number' => 'Количество раундов',\n  'ube_report_info_outcome' => 'Результат боя',\n  'ube_report_info_outcome_win' => 'Атакующий выиграл бой',\n  'ube_report_info_outcome_loss' => 'Атакующий проиграл бой',\n  'ube_report_info_outcome_draw' => 'Бой закончился ничьей',\n  'ube_report_info_link' => 'Ссылка на боевой отчет',\n  'ube_report_info_bbcode' => 'BBCode для вставки в чат',\n  'ube_report_info_sfr' => 'Бой закончился за один раунд проигрышем атакующего<br />Вероятна РМФ',\n  'ube_report_info_debris' => 'Обломки на орбите',\n  'ube_report_info_debris_simulator' => '(не считая создания Луны)',\n  'ube_report_info_loot' => 'Добыча',\n  'ube_report_info_loss' => 'Боевые потери',\n  'ube_report_info_generate' => 'Время генерации страницы',\n\n  'ube_report_moon_was' => 'У этой планеты уже была луна',\n  'ube_report_moon_chance' => 'Шанс образования луны',\n  'ube_report_moon_created' => 'На орбите планеты образовалась луна диаметром',\n\n  'ube_report_moon_reapers_none' => 'Все корабли с гравитационными двигателями были уничтожены в процессе боя',\n  'ube_report_moon_reapers_wave' => 'Корабли атакующего создали сфокусированную гравитационную волну',\n  'ube_report_moon_reapers_chance' => 'Шанс уничтожения луны',\n  'ube_report_moon_reapers_success' => 'Луна уничтожена',\n  'ube_report_moon_reapers_failure' => 'Мощности волны не хватило для уничтожения луны',\n\n  'ube_report_moon_reapers_outcome' => 'Шанс взрыва двигателей',\n  'ube_report_moon_reapers_survive' => 'Точная компенсация гравитационных полей системы позволила погасить отдачу от разрушения луны',\n  'ube_report_moon_reapers_died' => 'Не сумев компенсировать добавочные гравитационные поля системы, флот был уничтожен',\n\n  'ube_report_side_attacker' => 'Атакующий',\n  'ube_report_side_defender' => 'Защитник',\n\n  'ube_report_round' => 'Раунд',\n  'ube_report_unit' => 'Боевая единица',\n  'ube_report_attack' => 'Атака',\n  'ube_report_shields' => 'Щиты',\n  'ube_report_shields_passed' => 'Пробой',\n  'ube_report_armor' => 'Броня',\n  'ube_report_damage' => 'Урон',\n  'ube_report_loss' => 'Потери',\n\n\n  'ube_report_info_restored' => 'Восстановленно оборонительных сооружений',\n  'ube_report_info_loss_final' => 'Итоговые потери боевых единиц',\n  'ube_report_info_loss_resources' => 'Потери в пересчете на ресурсы',\n  'ube_report_info_loss_dropped' => 'Потери ресурсов из-за уменьшения трюмов',\n  'ube_report_info_loot_lost' => 'Увезено ресурсов со складов планеты',\n  'ube_report_info_loss_gained' => 'Потери из-за вывоза ресурсов с планеты',\n  'ube_report_info_loss_in_metal' => 'Общие потери в пересчете на металл',\n\n\n  'ube_report_msg_body_common' => 'Бой состоялся %s на орбите %s [%d:%d:%d] %s<br />%s<br /><br />',\n  'ube_report_msg_body_debris' => 'В результат боя на орбите планеты образовались обломки:<br />',\n  'ube_report_msg_body_sfr' => 'Связь с флотом утеряна',\n\n  'ube_report_capture' => 'Захват планеты',\n  'ube_report_capture_result' => [\n    UBE_CAPTURE_DISABLED => 'Захват планет отключён',\n    UBE_CAPTURE_NON_PLANET => 'Захватывать можно только планеты',\n    UBE_CAPTURE_NOT_A_WIN_IN_1_ROUND => 'Для захвата планеты бой должен закончиться победой в первом раунде',\n    UBE_CAPTURE_TOO_MUCH_FLEETS => 'При захвате планеты в бою должен участвовать только флот-захватчик и планетарный флот',\n    UBE_CAPTURE_NO_ATTACKER_USER_ID => 'ВНУТРЕННЯЯ ОШИБКА - Нет ИД атакующего! Сообщите разработчику!',\n    UBE_CAPTURE_NO_DEFENDER_USER_ID => 'ВНУТРЕННЯЯ ОШИБКА - Нет ИД защитника! Сообщите разработчику!',\n    UBE_CAPTURE_CAPITAL => 'Нельзя захватывать столицу',\n    UBE_CAPTURE_TOO_LOW_POINTS => 'Можно захватывать планеты только у игроков, чье общее количество очков не менее чем в 2 раза больше количество очков у атакующего',\n    UBE_CAPTURE_NOT_ENOUGH_SLOTS => 'Больше нет слотов захвата планеты',\n    UBE_CAPTURE_SUCCESSFUL => 'Планета захвачена атакующим игроком',\n  ],\n\n  'sys_kilometers_short' => 'км',\n\n  'ube_simulation' => 'Симуляция',\n\n  'sys_hire_do' => 'Нанять',\n\n  'sys_captains' => 'Капитаны',\n\n  'sys_fleet_composition' => 'Состав флота',\n\n  'sys_continue' => 'Продолжить',\n\n  'uni_planet_density_types' => [\n    PLANET_DENSITY_NONE => 'Не бывает',\n    PLANET_DENSITY_ICE_HYDROGEN => 'Водородный лёд',\n    PLANET_DENSITY_ICE_METHANE => 'Метановый лёд',\n    PLANET_DENSITY_ICE_WATER => 'Водный лёд',\n    PLANET_DENSITY_CRYSTAL_RAW => 'Кристалл',\n    PLANET_DENSITY_CRYSTAL_SILICATE => 'Силикат',\n    PLANET_DENSITY_CRYSTAL_STONE => 'Камень',\n    PLANET_DENSITY_STANDARD => 'Стандарт',\n    PLANET_DENSITY_METAL_ORE => 'Руда',\n    PLANET_DENSITY_METAL_PERIDOT => 'Оливин',\n    PLANET_DENSITY_METAL_RAW => 'Металл',\n  ],\n\n  'sys_planet_density' => 'Плотность',\n  'sys_planet_density_units' => 'кг/м&sup3;',\n  'sys_planet_density_core' => 'Тип ядра',\n\n  'sys_change' => 'Изменить',\n  'sys_show' => 'Показать',\n  'sys_hide' => 'Скрыть',\n  'sys_close' => 'Закрыть',\n  'sys_unlimited' => 'Без ограничений',\n\n  'ov_core_type_current' => 'Текущий тип ядра',\n  'ov_core_change_to' => 'Изменить на',\n  'ov_core_err_none' => 'Тип ядра планеты успешно изменен с \"%s\" на \"%s\".<br />Новая плотность планеты %d кг/м3',\n  'ov_core_err_not_a_planet' => 'Только на планете можно менять плотность ядра',\n  'ov_core_err_denisty_type_wrong' => 'Неправильный тип ядра',\n  'ov_core_err_same_density' => 'Новый тип ядра не отличается от текущего - нечего менять',\n  'ov_core_err_no_dark_matter' => 'Не хватает Тёмной Материи для смены типа ядра',\n\n  'sys_color'    => \"Цвет\",\n\n  'topnav_imp_attack' => 'Ваша Империя атакована!',\n  'topnav_user_rank' => 'Ваше текущее место в рейтинговой статистике',\n  'topnav_users' => 'Всего зарегистрированных игроков',\n  'topnav_users_online' => 'Текущее количество игроков онлайн',\n\n  'topnav_refresh_page' => 'Перегрузить страницу',\n\n  'sys_colonies' => 'Колонии',\n  'sys_radio' => 'Радио \"Космос\"',\n\n  'sys_auth_provider_list' => [\n    ACCOUNT_PROVIDER_NONE => 'Таблица USERS',\n    ACCOUNT_PROVIDER_LOCAL => 'Таблица ACCOUNT',\n    ACCOUNT_PROVIDER_CENTRAL => 'Центральная таблица ACCOUNT',\n  ],\n\n  'sys_login_messages' => [\n    LOGIN_UNDEFINED => 'Процесс логина не начался',\n    LOGIN_SUCCESS => 'Вход успешен',\n    LOGIN_ERROR_USERNAME_EMPTY => 'Имя игрока не может быть пустым',\n    LOGIN_ERROR_USERNAME_RESTRICTED_CHARACTERS => 'В имени игрока и логине не допускаются символы ',\n    LOGIN_ERROR_USERNAME => 'Игрок с таким именем не найден',\n    LOGIN_ERROR_USERNAME_ALLY_OR_BOT => 'Это имя принадлежит Альянсу или боту. Под ним нельзя логиниться... по крайней мере пока',\n    LOGIN_ERROR_PASSWORD_EMPTY => 'Пароль не может быть пустым',\n    LOGIN_ERROR_PASSWORD_TRIMMED => 'Пароль не может начинаться или заканчиваться пробелом, табуляцией или символом перевода строки',\n    LOGIN_ERROR_PASSWORD => 'Неправильный пароль',\n  //    LOGIN_ERROR_COOKIE => '',\n\n    REGISTER_SUCCESS => 'Регистрация успешно завершена',\n    REGISTER_ERROR_BLITZ_MODE => 'Регистрация новых игроков в режиме Блиц-сервера отключена',\n    REGISTER_ERROR_USERNAME_WRONG => 'Некорректное имя игрока',\n    REGISTER_ERROR_ACCOUNT_NAME_EXISTS => 'Имя учётной записи уже занято. Попробуйте войти с этим именем и вашим паролем или воспользоваться сбросом пароля',\n    REGISTER_ERROR_PASSWORD_INSECURE => 'Неправильный пароль. Пароль должен состоять минимум из ' . PASSWORD_LENGTH_MIN . ' символов',\n    REGISTER_ERROR_USERNAME_SHORT => 'Слишком короткое имя. Имя должно состоять минимум из ' . LOGIN_LENGTH_MIN. ' символов',\n    REGISTER_ERROR_PASSWORD_DIFFERENT => 'Пароль и проверочный пароль не совпадают. Проверьте правильность ввода',\n    REGISTER_ERROR_EMAIL_EMPTY => 'Е-Мейл не может быть пустым',\n    REGISTER_ERROR_EMAIL_WRONG => 'Введенный Е-Мейл не является допустимым адресом электронной почты. Проверьте написание Е-Мейла или используйте другой адрес почтового ящика',\n    REGISTER_ERROR_EMAIL_EXISTS => 'Этот электронный адрес уже зарегестрирован. Если вы уже регистрировались в игре - попробуйте воспользоваться сбросом пароля. Иначе - используйте другой почтовый ящик',\n\n    PASSWORD_RESTORE_ERROR_EMAIL_NOT_EXISTS => 'Нет игрока с таким основным емейлом',\n    PASSWORD_RESTORE_ERROR_TOO_OFTEN => 'Запросить код восстановления можно только 1 раз в 10 минут. Если вы не получили письмо - проверьте папку СПАМа или напишите письмо Администрации сервера на email <span class=\"ok\">' . $config->server_email . '</span> с адреса, который вы использовали при регистрации',\n    PASSWORD_RESTORE_ERROR_SENDING => 'Ошибка отправки письма. Напишите письмо Администрации сервера на email <span class=\"ok\">' . $config->server_email . '</span>',\n    PASSWORD_RESTORE_SUCCESS_CODE_SENT => 'Письмо с кодом восстановления успешно отправлено',\n\n    PASSWORD_RESTORE_ERROR_CODE_EMPTY => 'Код восстановления не может быть пустым',\n    PASSWORD_RESTORE_ERROR_CODE_WRONG => 'Неправильный код восстановления',\n    PASSWORD_RESTORE_ERROR_CODE_TOO_OLD => 'Код восстановления устарел. Получите новый',\n    PASSWORD_RESTORE_ERROR_CODE_OK_BUT_NO_ACCOUNT_FOR_EMAIL => 'Код восстановления указан верно, однако не найдено ни одного аккаунта с таким емейлом. Возможно, он был удалён или произошла внутренняя ошибка. Обратитесь к Администрации сервера',\n    PASSWORD_RESTORE_SUCCESS_PASSWORD_SENT => 'Пароль успешно сброшен. Вам отправлено письмо с новым паролем',\n    PASSWORD_RESTORE_SUCCESS_PASSWORD_SEND_ERROR => 'Ошибка отправки письма с новым паролем. Получите новый код восстановления и повторите попытку',\n\n    REGISTER_ERROR_PLAYER_NAME_TRIMMED => 'Имя игрока не может начинаться или заканчиваться пробельными символами (символы \"Пробел\", \"Табуляция\", \"Перенос строки\" итд)',\n    REGISTER_ERROR_PLAYER_NAME_EMPTY => 'Имя игрока не может быть пустым',\n    REGISTER_ERROR_PLAYER_NAME_RESTRICTED_CHARACTERS => 'Имя игрока содержит запрещенные символы',\n    REGISTER_ERROR_PLAYER_NAME_SHORT => 'Имя игрока не может быть короче ' . LOGIN_LENGTH_MIN . ' символов',\n    REGISTER_ERROR_PLAYER_NAME_EXISTS => 'Это имя игрока уже занято. Пожалуйста, выберите другое',\n\n    // Внутренние ошибки\n    AUTH_ERROR_INTERNAL_PASSWORD_CHANGE_ON_RESTORE => 'ВНУТРЕННЯЯ ОШИБКА! СООБЩИТЕ АДМИНИСТРАЦИИ! Ошибка смены пароля. Пожалуйста, сообщите об этой ошибке Администрации Вселенной!',\n    PASSWORD_RESTORE_ERROR_ADMIN_ACCOUNT => 'Запрещено восстановление пароля для Команды сервера. Обратитесь к Администратору',\n    REGISTER_ERROR_ACCOUNT_CREATE => 'Ошибка создания учётной записи! Пожалуйста, сообщите об этом Администрации!',\n    LOGIN_ERROR_SYSTEM_ACCOUNT_TRANSLATION => 'СИСТЕМНАЯ ОШИБКА - СБОЙ В ТАБЛИЦЕ ТРАНСЛЯЦИИ ПРОВАЙДЕРОВ! Сообщите администрации сервера!',\n    PASSWORD_RESTORE_ERROR_ACCOUNT_NOT_EXISTS => 'Внутренняя ошибка - при смене пароля аккаунт не найден! Сообщите об этой ошибке Администрации!',\n    AUTH_PASSWORD_RESET_INSIDE_ERROR_NO_ACCOUNT_FOR_CONFIRMATION => 'ВНУТРЕННЯЯ ОШИБКА! Нет аккаунтов для сброса пароля при корректном коде подтверждения! Пожалуйста, сообщите об этой ошибке Администрации Вселенной!',\n    LOGIN_ERROR_NO_ACCOUNT_FOR_COOKIE_SET => 'ВНУТРЕННЯЯ ОШИБКА! СООБЩИТЕ АДМИНИСТРАЦИИ! Не установлен аккаунт при cookie_set()! Пожалуйста, сообщите об этой ошибке Администрации Вселенной!',\n  ],\n\n  'log_reg_email_title' => \"Ваша регистрация на сервере %1\\$s игры СуперНова\",\n  'log_reg_email_text' => \"Подтверждение регистрации для %3\\$s\\r\\n\\r\\n\n  Это письмо содержит Ваши регистрационные данные на сервере %1\\$s игры СуперНова\\r\\n\n  Сохраните эти данные в безопасном месте\\r\\n\\r\\n\n  Адрес сервера: %2\\$s\\r\\n\n  Ваш логин: %3\\$s\\r\\n\n  Ваш пароль: %4\\$s\\r\\n\\r\\n\n  Спасибо за регистрацию на нашем сервере! Желаем Вам удачи в игре!\\r\\n\n  Администрация сервера %1\\$s %2\\$s\\r\\n\\r\\n\n  Сервер работает на свободном движке 'Project SuperNova.WS'. Зажги свою СуперНову http://supernova.ws/\",\n\n  'log_lost_email_title' => 'СуперНова, Вселенная %s: Сброс пароля',\n  'log_lost_email_code' => \"Кто-то (может быть Вы) запросил сброс пароля во Вселенной %1\\$4 игры СуперНова. Если Вы не запрашивали сброс пароля - просто проигнорируйте это письмо.\\r\\n\\r\\nДля сброса пароля перейдите по адресу \\r\\n%1\\$s?password_reset_confirm=1&password_reset_code=%2\\$s#tab_password_reset\\r\\n или введите код подтверждения \\\"%2\\$s\\\" (БЕЗ ДВОЙНЫХ КАВЫЧЕК!) на странице %1\\$s#tab_password_reset\\r\\n\\r\\nЭтот код будет действителен до %3\\$s. После указанного срока для сброса пароля Вам нужно будет запросить новый код подтверждения\",\n  'log_lost_email_pass' => \"Вы сбросили пароль на сервере %1\\$s игры 'СуперНова'.\\r\\n\\r\\nВаше имя для входа в игру:\\r\\n%2\\$s\\r\\n\\r\\nВаш новый пароль для входа в игру:\\r\\n%3\\$s\\r\\n\\r\\nЗапомните его!\\r\\n\\r\\nВы можете войти в игру по ссылке \" . SN_ROOT_VIRTUAL . \"login.php используя имя и пароль, указанные выше\",\n\n  'login_player_register_player_name' => 'Имя игрока',\n  'login_player_register_description' => 'Остался всего один шаг! Выберите имя игрока - имя, которое будет показываться другим другим игрокам в этой Вселенной',\n  'login_player_register_do' => 'Выбрать имя',\n  'login_player_register_logout' => 'Зайти под другим аккаунтом',\n  'login_player_register_logout_description' => 'Если вы хотите зайти под другим аккаунтом - нажмите кнопку',\n\n  'sys_password_reset_message_body' => \"Вы сбросили пароль для доступа в игру в этой Вселенной.\\r\\n\\r\\nВаш новый пароль для входа в игру:\\r\\n\\r\\n%1\\$s\\r\\n\\r\\nЗапомните его!\\r\\n\\r\\nВы можете в любой момент сменить пароль на более удобный для вас в пункте меню 'Настройки'.\",\n\n  'sys_login_password_show' => 'Показать пароль',\n  'sys_login_password_hide' => 'Скрыть пароль',\n  'sys_password_repeat' => 'Повторите пароль',\n\n  'sys_game_disable_reason' => [\n    GAME_DISABLE_NONE => 'Игра включена',\n    GAME_DISABLE_REASON => 'Игра отключена. Игроки увидят сообщение',\n    GAME_DISABLE_UPDATE => 'Игра обновляется',\n    GAME_DISABLE_STAT => 'Происходит пересчет статистики',\n    GAME_DISABLE_INSTALL => 'Игра еще не сконфигурирована',\n    GAME_DISABLE_MAINTENANCE => 'Техобслуживание базы данных сервера',\n    GAME_DISABLE_EVENT_BLACK_MOON => 'Чёрная Луна!',\n    GAME_DISABLE_EVENT_OIS => 'Объекты в космосе',\n  ],\n\n  'sys_sector_purchase_log' => 'Пользователь {%2$d} {%1$s} купил 1 сектор на планете {%5$d} {%3$s} тип \"%4$s\" за %6$d ТМ',\n\n  'sys_notes' => 'Заметки',\n  'sys_notes_priorities' => [\n    0 => 'Совсем не важная',\n    1 => 'Не важная',\n    2 => 'Обычная',\n    3 => 'Важная',\n    4 => 'Очень важная',\n  ],\n\n  'sys_milliseconds' => 'миллисекунд',\n\n  'sys_gender' => 'Пол',\n  'sys_gender_list' => [\n    GENDER_UNKNOWN => 'Вырастет - само решит',\n    GENDER_MALE => 'Мужской',\n    GENDER_FEMALE => 'Женский',\n  ],\n\n  'imp_stat_header' => 'График изменений данных статистики',\n  'imp_stat_types' => [\n    'TOTAL_RANK' => 'Место в общей статистике',\n    'TOTAL_POINTS' => 'Общее количество очков',\n    // 'TOTAL_COUNT' => 'Общее количество ресурсов',\n    'TECH_RANK' => 'Место в статистике по Исследованиям',\n    'TECH_POINTS' => 'Количество очков за Исследования',\n    // 'TECH_COUNT' => 'Количество уровней',\n    'BUILD_RANK' => 'Место в статистике по Постройкам',\n    'BUILD_POINTS' => 'Количество очков за Постройки',\n    // 'BUILD_COUNT' => '',\n    'DEFS_RANK' => 'Место в статистике по Обороне',\n    'DEFS_POINTS' => 'Количество очков за Оборону',\n    //'DEFS_COUNT' => '',\n    'FLEET_RANK' => 'Место в статистике по Кораблям',\n    'FLEET_POINTS' => 'Количество очков за Корабли',\n    //'FLEET_COUNT' => '',\n    'RES_RANK' => 'Место в статистике по свободным ресурсам',\n    'RES_POINTS' => 'Количество очков за свободные ресурсы',\n    //'RES_COUNT' => '',\n  ],\n\n  'sys_date' => 'Дата',\n\n  'sys_blitz_global_button' => 'Блиц-сервер',\n  'sys_blitz_page_disabled' => 'В режиме Блиц-сервера эта страница недоступна',\n  'sys_blitz_registration_disabled' => 'Регистрация на игру в Блиц-сервер отключена',\n  'sys_blitz_registration_no_users' => 'Нет зарегестрированных игроков',\n  'sys_blitz_registration_player_register' => 'Зарегестрироваться для игры',\n  'sys_blitz_registration_player_register_un' => 'Отозвать регистрацию',\n  'sys_blitz_registration_closed' => 'Регистрация пока закрыта. Попробуйте зайти позже',\n  'sys_blitz_registration_player_generate' => 'Сгенерировать логины и пароли',\n  'sys_blitz_registration_player_import_generated' => 'Импортировать сгенерированную строку',\n  'sys_blitz_registration_player_name' => 'Ваш логин для Блиц-сервера:',\n  'sys_blitz_registration_player_password' => 'Ваш пароль для Блиц-сервера:',\n  'sys_blitz_registration_server_link' => 'Ссылка на Блиц-сервер',\n  'sys_blitz_registration_player_blitz_name' => 'Имя на Блиц-сервере',\n  'sys_blitz_registration_price' => 'Стоимость подачи заявки',\n  'sys_blitz_registration_mode_list' => [\n    BLITZ_REGISTER_DISABLED => 'Регистрация отключена',\n    BLITZ_REGISTER_OPEN => 'Регистрация открыта',\n    BLITZ_REGISTER_CLOSED => 'Регистрация закрыта',\n    BLITZ_REGISTER_SHOW_LOGIN => 'Открыты логины и пароли',\n    BLITZ_REGISTER_DISCLOSURE_NAMES => 'Подведение итогов',\n  ],\n\n  'survey' => 'Опрос',\n  'survey_questions' => 'Варианты для выбора',\n  'survey_questions_hint' => '1 вариант на строку',\n  'survey_questions_hint_edit' => 'Редактированние опроса обнулит его результаты',\n  'survey_until' => 'Длительность опроса (1 сутки по умолчанию)',\n\n  'survey_votes_total_none' => 'Еще никто не проголосовал... Проголосуй первым!',\n  'survey_votes_total_voted' => 'Уже проголосовало:',\n  'survey_votes_total_voted_join' => 'Голосуй - или проиграешь!',\n  'survey_votes_total_voted_has_answer' => 'Вы уже проголосовали. Вместе с вами проголосовавших',\n\n  'survey_lasts_until' => 'Опрос продлится до',\n\n  'survey_select_one' => 'Выберите один вариант ответа и нажмите',\n  'survey_confirm' => 'Проголосовать!',\n  'survey_result_sent' => 'Ваш голос учтен. Обновите страницу или воспользуйтесь ссылкой <a class=\"link\" href=\"announce.php\">Новости</a> что бы увидеть текущие результаты опроса',\n  'survey_complete' => 'Опрос завершен',\n\n  'player_option_fleet_ship_sort' => [\n    PLAYER_OPTION_SORT_DEFAULT => 'Стандартная',\n    PLAYER_OPTION_SORT_NAME => 'По названию',\n    PLAYER_OPTION_SORT_ID => 'По ID',\n    PLAYER_OPTION_SORT_SPEED => 'По скорости',\n    PLAYER_OPTION_SORT_COUNT => 'По количеству',\n  ],\n\n  'player_option_building_sort' => [\n    PLAYER_OPTION_SORT_DEFAULT => 'Стандартная',\n    PLAYER_OPTION_SORT_NAME => 'По названию',\n    PLAYER_OPTION_SORT_ID => 'По ID',\n    PLAYER_OPTION_SORT_CREATE_TIME_LENGTH => 'По времени постройки',\n  ],\n\n  'sys_sort' => 'Сортировка',\n  'sys_sort_inverse' => 'В обратном порядке',\n\n  'sys_blitz_reward_log_message' => 'Блиц-сервер %1$d призовое место блиц-имя \"%2$s\"',\n  'sys_blitz_registration_view_stat' => 'Посмотреть статистику Блиц-сервера',\n\n  'sys_login_register_message_title' => \"Ваше имя и пароль для входа в игру\",\n  'sys_login_register_message_body' => \"Ваше имя для входа в игру (логин)\\r\\n%1\\$s\\r\\n\\r\\nВаш пароль для входа в игру\\r\\n%2\\$s\\r\\n\\r\\nЗапишите или запомните эти данные!\",\n\n  'auth_provider_list' => [\n    ACCOUNT_PROVIDER_NONE => 'Таблица users',\n    ACCOUNT_PROVIDER_LOCAL => 'Таблица account',\n    ACCOUNT_PROVIDER_CENTRAL => 'Центральное хранилище',\n  ],\n\n  'bld_autoconvert' => 'Автоматическая конвертация при создании на планете %3$s юнита {%1$d} \"%4$s\" в количесте %2$d ценой \"%5$s\". Debug: $resource_got = \"%6$s\", $exchange = %7$s\"\"',\n\n  'news_show_rest' => 'Показать текст новости',\n\n  'wiki_requrements' => 'Требуется',\n  'wiki_grants' => 'Предоставляет',\n\n  'que_slot_length' => 'Слоты',\n  'que_slot_length_long' => 'Слоты очереди',\n\n  'sys_buy_doing' => 'Вы покупаете',\n  'sys_planet_sector' => 'сектор',\n  'sys_planet_on' => 'на',\n\n  'sys_purchase_confirm' => 'Подвердите покупку',\n\n  'sys_confirm_action_title' => 'Подтвердите ваше действие',\n  'sys_confirm_action' => 'Вы действительно хотите сделать это?',\n\n  'sys_system_speed_original' => 'Оригинальная скорость',\n  'sys_system_speed_for_action' => 'В рамках акции',\n\n  'menu_info_best_battles' => 'Лучшие бои',\n\n  'sys_cost' => 'Стоимость',\n  'sys_price' => 'Цена',\n\n  'sys_governor_none' => 'Губернатор не нанят',\n  'sys_governor_hire' => 'Нанять Губернатора',\n  'sys_governor_upgrade_or_change' => 'Улучшить или сменить Губернатора',\n\n  'tutorial_prev' => '<< Предыдущий',\n  'tutorial_next' => 'Следующий >>',\n  'tutorial_finish' => 'Завершить',\n  'tutorial_window' => 'Открыть в окне',\n  'tutorial_window_off' => 'Вернуть на страницу',\n\n  'tutorial_error_load' => \"Ошибка загрузки туториала - попробуйте еще раз! В случае повторной ошибки - сообщите Администрации игры\",\n  'tutorial_error_next' => \"Ошибка: Не существует следующей страницы туториала - сообщите Администрации игры\",\n  'tutorial_error_prev' => \"Ошибка: Не существует предыдущей страницы туториала - сообщите Администрации игры\",\n\n  'sys_click_here_to_continue' => 'Кликните здесь, что бы продолжить',\n\n  'sys_module_error_not_found' => 'Модуль наград \"%1$s\" не найден или отключён!',\n\n  'rank_page_title' => 'Воинские звания',\n  'rank' => 'Звание',\n  'ranks' => [\n    0  => 'Курсант',\n    1  => 'Рекрут',\n    2  => 'Рядовой',\n    3  => 'Ефрейтор',\n    4  => 'Капрал',\n    5  => 'Сержант',\n    6  => 'Старшина',\n    7  => 'Гардемарин',\n    8  => 'Мичман',\n    9  => 'Энсин',\n    10 => 'Лейтенант',\n    11 => 'Капитан',\n    12 => 'Майор',\n    13 => 'Подполковник',\n    14 => 'Полковник',\n    15 => 'Контр-Адмирал',\n    16 => 'Вице-Адмирал',\n    17 => 'Адмирал',\n    18 => 'Адмирал флота',\n    19 => 'Маршал',\n    20 => 'Генералиссимус',\n  ],\n\n];\n"
  },
  {
    "path": "language/ru/tech.mo.php",
    "content": "<?php\n\n/*\n#############################################################################\n#  Filename: tech.mo\n#  Project: SuperNova.WS\n#  Website: http://www.supernova.ws\n#  Description: Massive Multiplayer Online Browser Space Strategy Game\n#\n#  Copyright © 2009-2018 Gorlum for Project \"SuperNova.WS\"\n#############################################################################\n*/\n\n/**\n*\n* @package language\n* @system [Russian]\n* @version 46d0\n*\n*/\n\n/**\n* DO NOT CHANGE\n*/\n\nif (!defined('INSIDE')) die();\n\n\n$a_lang_array = (array(\n  'tech_storage_max' => 'Размер склада',\n  'tech_storage' => 'На складе',\n  'tech_storage_energy' => 'Потребление',\n  'tech_storage_energy_max' => 'Производство',\n  'tech_storage_energy_fullness' => 'Загрузка',\n  'Tech' => 'Технология',\n  'Requirements' => 'Требуется',\n  'No_requirements' => 'Нет требований',\n  'Metal' => 'Металл',\n  'Crystal' => 'Кристалл',\n  'Deuterium' => 'Дейтерий',\n  'Energy' => 'Энергия',\n  'dark_matter' => 'Тёмная Материя',\n  'ds' => 'Сообщения',\n  'Message' => 'Сообщения',\n  'level' => 'Уровень',\n  'treeinfo' => '[i]',\n  'comingsoon' => 'Скоро',\n  'te_dt_tx_pre' => 'Слабая добыча',\n  'tech_fullness' => 'Заполнение',\n\n  'type_mission' => array(\n    MT_NONE => '-- неизвестно --',\n    MT_ATTACK => 'Атака',\n    MT_AKS => 'Совместная атака',\n    MT_TRANSPORT => 'Транспорт',\n    MT_RELOCATE => 'Передислокация',\n    MT_HOLD => 'Удержание',\n    MT_SPY => 'Шпионаж',\n    MT_COLONIZE => 'Колонизация',\n    MT_RECYCLE => 'Переработать',\n    MT_DESTROY => 'Уничтожение',\n    MT_MISSILE => 'Ракетная атака',\n    MT_EXPLORE => 'Экспедиция',\n  ),\n  'fleet_events' => array(\n    EVENT_FLEET_NONE   => 'Нет события',\n    EVENT_FLEET_ARRIVE => 'Прибытие',\n    EVENT_FLEET_STAY   => 'Удержание/Экспедиция',\n    EVENT_FLEET_RETURN => 'Возвращение',\n  ),\n\n  'tech' => array(\n    UNIT_STRUCTURES => 'Постройки',\n    STRUC_MINE_METAL => 'Рудник',\n    STRUC_MINE_CRYSTAL => 'Синтезатор кристаллов',\n    STRUC_MINE_DEUTERIUM => 'Синтезатор дейтерия',\n    STRUC_MINE_SOLAR => 'Солнечная электростанция',\n    STRUC_MINE_FUSION => 'Термоядерная электростанция',\n    STRUC_FACTORY_ROBOT => 'Фабрика роботов',\n    STRUC_FACTORY_NANO => 'Нанофабрика',\n    STRUC_FACTORY_HANGAR => 'Верфь',\n    STRUC_STORE_METAL => 'Склад металла',\n    STRUC_STORE_CRYSTAL => 'Склад кристаллов',\n    STRUC_STORE_DEUTERIUM => 'Емкость для дейтерия',\n    STRUC_LABORATORY => 'Лаборатория',\n    STRUC_TERRAFORMER => 'Терраформер',\n    STRUC_ALLY_DEPOSIT => 'Склад альянса',\n    STRUC_LABORATORY_NANO => 'Нанолаборатория',\n\n    UNIT_STRUCTURES_SPECIAL => 'Лунные постройки',\n    STRUC_MOON_STATION => 'Лунная база',\n    STRUC_MOON_PHALANX => 'Сенсорная фаланга',\n    STRUC_MOON_GATE => 'Межгалактические врата',\n    STRUC_SILO => 'Ракетная шахта',\n\n    UNIT_TECHNOLOGIES => 'Технологии',\n    TECH_ENERGY => 'Энергетическая технология',\n    TECH_COMPUTER => 'Компьютерная технология',\n    TECH_ARMOR => 'Броня космических кораблей',\n    TECH_WEAPON => 'Оружейная технология',\n    TECH_SHIELD => 'Щитовая технология',\n    TECH_ENGINE_CHEMICAL => 'Химический двигатель',\n    TECH_ENGINE_ION => 'Ионный двигатель',\n    TECH_ENGINE_HYPER => 'Гипердвигатель',\n    TECH_LASER => 'Лазерная технология',\n    TECH_ION => 'Ионная технология',\n    TECH_PLASMA => 'Плазменная технология',\n    TECH_HYPERSPACE => 'Гипертехнология',\n    TECH_SPY => 'Технология шпионажа',\n    TECH_EXPEDITION => 'Экспедиционная технология',\n    TECH_COLONIZATION => 'Колонизационная технология',\n    TECH_ASTROTECH => 'Астрокартография',\n    TECH_GRAVITON => 'Гравитационная технология',\n    TECH_RESEARCH => 'Исследовательская сеть',\n\n    UNIT_SHIPS               => 'Флот',\n    SHIP_SATTELITE_SOLAR     => 'Солнечный спутник',\n    SHIP_SPY                 => 'Шпионский зонд',\n    SHIP_CARGO_SMALL         => 'Малый транспорт',\n    SHIP_CARGO_BIG           => 'Большой транспорт',\n    SHIP_CARGO_SUPER         => 'Супертранспорт',\n    SHIP_CARGO_HYPER         => 'Гипертранспорт',\n    SHIP_RECYCLER            => 'Переработчик',\n    SHIP_COLONIZER           => 'Колонизатор',\n    SHIP_SMALL_FIGHTER_LIGHT => 'Лёгкий истребитель',\n    SHIP_SMALL_FIGHTER_HEAVY => 'Тяжёлый истребитель',\n    SHIP_MEDIUM_DESTROYER    => 'Эсминец',\n    SHIP_LARGE_CRUISER       => 'Крейсер',\n    SHIP_LARGE_BOMBER        => 'Бомбардировщик',\n    SHIP_LARGE_BATTLESHIP    => 'Линейный крейсер',\n    SHIP_LARGE_DESTRUCTOR    => 'Уничтожитель',\n    SHIP_HUGE_DEATH_STAR     => 'Звезда смерти',\n    SHIP_HUGE_SUPERNOVA      => 'Крейсер класса &quot;СуперНова&quot;',\n\n    UNIT_DEFENCE => 'Оборона',\n    UNIT_DEF_TURRET_MISSILE => 'Ракетная установка',\n    UNIT_DEF_TURRET_LASER_SMALL => 'Легкий лазер',\n    UNIT_DEF_TURRET_LASER_BIG => 'Тяжёлый лазер',\n    UNIT_DEF_TURRET_GAUSS => 'Пушка Гаусса',\n    UNIT_DEF_TURRET_ION => 'Ионное орудие',\n    UNIT_DEF_TURRET_PLASMA => 'Плазменное орудие',\n    UNIT_DEF_SHIELD_SMALL => 'Малый щитовой купол',\n    UNIT_DEF_SHIELD_BIG => 'Большой щитовой купол',\n    UNIT_DEF_SHIELD_PLANET => 'Планетарная защита',\n    UNIT_DEF_MISSILE_INTERCEPTOR => 'Ракета-перехватчик',\n    UNIT_DEF_MISSILE_INTERPLANET => 'Межпланетная ракета',\n\n    UNIT_MERCENARIES => 'Наемники',\n    MRC_STOCKMAN => 'Карго-мастер',\n    MRC_SPY => 'Шпион',\n    MRC_ACADEMIC => 'Академик',\n//    MRC_DESTRUCTOR => 'Разрушитель',\n    MRC_ADMIRAL => 'Адмирал',\n    MRC_COORDINATOR => 'Координатор',\n    MRC_NAVIGATOR => 'Навигатор',\n//    MRC_ASSASIN => 'Ассасин',\n\n    UNIT_GOVERNORS => 'Губернаторы',\n    MRC_TECHNOLOGIST => 'Технолог',\n    MRC_ENGINEER => 'Инженер',\n    MRC_FORTIFIER => 'Фортификатор',\n\n    UNIT_RESOURCES => 'Ресурсы',\n    RES_METAL => 'Металл',\n    RES_CRYSTAL => 'Кристалл',\n    RES_DEUTERIUM => 'Дейтерий',\n    RES_ENERGY => 'Энергия',\n    RES_DARK_MATTER => 'Тёмная Материя',\n    RES_METAMATTER => 'Метаматерия',\n\n    UNIT_ARTIFACTS     => 'Артефакты',\n    ART_LHC            => 'Большой Адронный Коллайдер',\n    ART_HOOK_SMALL     => 'Малый Крюк',\n    ART_HOOK_MEDIUM    => 'Средний Крюк',\n    ART_HOOK_LARGE     => 'Большой Крюк',\n    ART_RCD_SMALL      => 'Малый АКК',\n    ART_RCD_MEDIUM     => 'Средний АКК',\n    ART_RCD_LARGE      => 'Большой АКК',\n    ART_HEURISTIC_CHIP => 'Эвристический чип',\n    ART_NANO_BUILDER   => 'Наностроитель',\n    ART_DENSITY_CHANGER => 'Матрица трансмутации',\n\n    UNIT_PLANS => 'Чертежи',\n    UNIT_PLAN_STRUC_MINE_FUSION => 'Чертеж &quot;Термоядерная электростанция&quot;',\n    UNIT_PLAN_SHIP_CARGO_SUPER => 'Чертеж &quot;Супертранспорт&quot;',\n    UNIT_PLAN_SHIP_CARGO_HYPER => 'Чертеж &quot;Гипертранспорт&quot;',\n    UNIT_PLAN_SHIP_DEATH_STAR => 'Чертеж &quot;Звезда Смерти&quot;',\n    UNIT_PLAN_SHIP_SUPERNOVA => 'Чертеж крейсер &quot;СуперНова&quot;',\n    UNIT_PLAN_DEF_SHIELD_PLANET => 'Чертеж &quot;Планетарная защита&quot;',\n\n    UNIT_PREMIUM => 'Премиум',\n    UNIT_CAPTAIN => 'Капитан',\n\n    UNIT_PLANET_DENSITY => 'Плотность',\n\n    UNIT_CAN_NOT_BE_BUILD => 'Юнит не может быть построен игроком',\n  ),\n\n  'tech_short' => array(\n    UNIT_STRUCTURES          => 'Постройки',\n    STRUC_MINE_METAL         => 'Рудник',\n    STRUC_MINE_CRYSTAL       => 'СинтКрис',\n    STRUC_MINE_DEUTERIUM     => 'СинтДейт',\n    STRUC_MINE_SOLAR         => 'СолЭС',\n    STRUC_MINE_FUSION        => 'ТермЭС',\n    STRUC_FACTORY_ROBOT      => 'Фабрика',\n    STRUC_FACTORY_NANO       => 'НаноФаб',\n    STRUC_FACTORY_HANGAR     => 'Верфь',\n    STRUC_STORE_METAL        => 'СклМет',\n    STRUC_STORE_CRYSTAL      => 'СклКрис',\n    STRUC_STORE_DEUTERIUM    => 'СклДейт',\n    STRUC_LABORATORY         => 'Лаба',\n    STRUC_TERRAFORMER        => 'ТФ',\n    STRUC_ALLY_DEPOSIT       => 'СклАлл',\n    STRUC_LABORATORY_NANO    => 'НаноЛаба',\n    UNIT_STRUCTURES_SPECIAL  => 'Особые постройки',\n    STRUC_MOON_STATION       => 'ЛунБаза',\n    STRUC_MOON_PHALANX       => 'Фаланга',\n    STRUC_MOON_GATE          => 'Врата',\n    STRUC_SILO               => 'РакШахта',\n    UNIT_TECHNOLOGIES        => 'Технологии',\n    TECH_ENERGY              => 'ЭнергТех',\n    TECH_COMPUTER            => 'КомпТеха',\n    TECH_ARMOR               => 'Броня',\n    TECH_WEAPON              => 'Оружие',\n    TECH_SHIELD              => 'Щиты',\n    TECH_ENGINE_CHEMICAL     => 'ХимДвиг',\n    TECH_ENGINE_ION          => 'ИонДвиг',\n    TECH_ENGINE_HYPER        => 'ГиперДвиг',\n    TECH_LASER               => 'ЛазТех',\n    TECH_ION                 => 'ИонТех',\n    TECH_PLASMA              => 'ПлазТех',\n    TECH_HYPERSPACE          => 'ГиперТех',\n    TECH_SPY                 => 'Шпионаж',\n//    TECH_EXPEDITION          => 'Экспедиционная технология',\n//    TECH_COLONIZATION        => 'Колонизационная технология',\n    TECH_ASTROTECH           => 'АстроТех',\n    TECH_GRAVITON            => 'ГравиТех',\n    TECH_RESEARCH            => 'МИС',\n\n    UNIT_SHIPS               => 'Флот',\n    SHIP_SATTELITE_SOLAR     => 'СолCп',\n    SHIP_SPY                 => 'Шпай',\n    SHIP_CARGO_SMALL         => 'МалТр',\n    SHIP_CARGO_BIG           => 'БолТр',\n    SHIP_CARGO_SUPER         => 'СуперТр',\n    SHIP_CARGO_HYPER         => 'ГиперТр',\n    SHIP_RECYCLER            => 'Перераб',\n    SHIP_COLONIZER           => 'Колон',\n    SHIP_SMALL_FIGHTER_LIGHT => 'ЛтИс',\n    SHIP_SMALL_FIGHTER_HEAVY => 'ТяИс',\n    SHIP_MEDIUM_DESTROYER    => 'Эсминец',\n    SHIP_LARGE_CRUISER       => 'Крейсер',\n    SHIP_LARGE_BOMBER        => 'Бомбер',\n    SHIP_LARGE_BATTLESHIP    => 'Линк',\n    SHIP_LARGE_DESTRUCTOR    => 'Уник',\n    SHIP_HUGE_DEATH_STAR     => 'ЗС',\n    SHIP_HUGE_SUPERNOVA      => 'СН',\n\n    UNIT_DEFENCE                 => 'Оборона',\n    UNIT_DEF_TURRET_MISSILE      => 'Ракеты',\n    UNIT_DEF_TURRET_LASER_SMALL  => 'ЛеЛаз',\n    UNIT_DEF_TURRET_LASER_BIG    => 'ТяЛаз',\n    UNIT_DEF_TURRET_GAUSS        => 'Гаусс',\n    UNIT_DEF_TURRET_ION          => 'Ионка',\n    UNIT_DEF_TURRET_PLASMA       => 'Плазма',\n    UNIT_DEF_SHIELD_SMALL        => 'МЩК',\n    UNIT_DEF_SHIELD_BIG          => 'БЩК',\n    UNIT_DEF_SHIELD_PLANET       => 'ПланЗащ',\n    UNIT_DEF_MISSILE_INTERCEPTOR => 'Перехв',\n    UNIT_DEF_MISSILE_INTERPLANET => 'МПР',\n\n//    UNIT_MERCENARIES => 'Наемники',\n//    MRC_STOCKMAN => 'Карго-мастер',\n//    MRC_SPY => 'Шпион',\n//    MRC_ACADEMIC => 'Академик',\n////    MRC_DESTRUCTOR => 'Разрушитель',\n//    MRC_ADMIRAL => 'Адмирал',\n//    MRC_COORDINATOR => 'Координатор',\n//    MRC_NAVIGATOR => 'Навигатор',\n////    MRC_ASSASIN => 'Ассасин',\n//\n//    UNIT_GOVERNORS => 'Губернаторы',\n//    MRC_TECHNOLOGIST => 'Технолог',\n//    MRC_ENGINEER => 'Инженер',\n//    MRC_FORTIFIER => 'Фортификатор',\n//\n    UNIT_RESOURCES => 'Ресы',\n    RES_METAL => 'Мет',\n    RES_CRYSTAL => 'Крис',\n    RES_DEUTERIUM => 'Дейт',\n    RES_ENERGY => 'Энер',\n    RES_DARK_MATTER => 'ТМ',\n    RES_METAMATTER => 'ММ',\n//\n//    UNIT_ARTIFACTS     => 'Артефакты',\n//    ART_LHC            => 'Большой Адронный Коллайдер',\n//    ART_HOOK_SMALL     => 'Малый Крюк',\n//    ART_HOOK_MEDIUM    => 'Средний Крюк',\n//    ART_HOOK_LARGE     => 'Большой Крюк',\n//    ART_RCD_SMALL      => 'Малый АКК',\n//    ART_RCD_MEDIUM     => 'Средний АКК',\n//    ART_RCD_LARGE      => 'Большой АКК',\n//    ART_HEURISTIC_CHIP => 'Эвристический чип',\n//    ART_NANO_BUILDER   => 'Наностроитель',\n//    ART_DENSITY_CHANGER => 'Матрица трансмутации',\n//\n//    UNIT_PLANS => 'Чертежи',\n//    UNIT_PLAN_STRUC_MINE_FUSION => 'Чертеж &quot;Термоядерная электростанция&quot;',\n//    UNIT_PLAN_SHIP_CARGO_SUPER => 'Чертеж &quot;Супертранспорт&quot;',\n//    UNIT_PLAN_SHIP_CARGO_HYPER => 'Чертеж &quot;Гипертранспорт&quot;',\n//    UNIT_PLAN_SHIP_DEATH_STAR => 'Чертеж &quot;Звезда Смерти&quot;',\n//    UNIT_PLAN_SHIP_SUPERNOVA => 'Чертеж крейсер &quot;СуперНова&quot;',\n//    UNIT_PLAN_DEF_SHIELD_PLANET => 'Чертеж &quot;Планетарная защита&quot;',\n//\n//    UNIT_PREMIUM => 'Премиум',\n//    UNIT_CAPTAIN => 'Капитан',\n//\n//    UNIT_PLANET_DENSITY => 'Плотность',\n\n    UNIT_CAN_NOT_BE_BUILD => 'Нельзя строить',\n  ),\n\n));\n"
  },
  {
    "path": "language/ru/universe.mo.php",
    "content": "<?php\n\n/*\n#############################################################################\n#  Filename: universe.mo\n#  Project: SuperNova.WS\n#  Website: http://www.supernova.ws\n#  Description: Massive Multiplayer Online Browser Space Strategy Game\n#\n#  Copyright © 2009-2018 Gorlum for Project \"SuperNova.WS\"\n#############################################################################\n*/\n\n/**\n*\n* @package language\n* @system [Russian]\n* @version 43a16.13\n*\n*/\n\n/**\n* DO NOT CHANGE\n*/\n\nif (!defined('INSIDE')) die();\n\n\n$a_lang_array = (array(\n  'Galaxy' => 'Галактика',\n  'Solar_system' => 'Солнечная система',\n  'Show' => 'Показать',\n  'vacation_shortcut' => 'U',\n  'banned_shortcut' => 'G',\n  'active_shortcut' => '*',\n  'inactif_7_shortcut' => 'i',\n  'inactif_28_shortcut' => 'I',\n  'strong_player_shortcut' => 'S',\n  'weak_player_shortcut' => 'N',\n  'uni_protected_player_shortcut' => 'n',\n  'Legend' => 'Условные обозначения',\n  'Strong_player' => 'Сильный игрок',\n  'Weak_player' => 'Слабый игрок',\n  'Way_vacation' => 'Режим отпуска',\n  'Pendent_user' => 'Заблокирован',\n  'Active' => 'Активный игрок',\n  'Inactive_7_days' => 'Неактивен более 7 дней',\n  'Inactive_28_days' => 'Неактивен более 28 дней',\n  'uni_protected_player' => 'Начинающий игрок',\n  'uni_legend_myplanet' => 'Мои планеты',\n  'uni_legend_allyplanet' => 'Планеты соальянсовца',\n  'Solar_system_at' => 'Солнечная система %g:%s',\n  'Activity' => 'Активность',\n  'Planet_info' => 'Информация о планете',\n  'Moon_info' => 'Информация о луне',\n  'Available' => 'Доступно',\n  'type1' => 'планета',\n  'type3' => 'луна',\n  'Pos' => 'Поз',\n  'Planet' => 'Вид',\n  'Name' => 'Название',\n  'Moon' => 'Луна',\n  'Debris' => 'Поле обломков',\n  'caracters' => 'Характеристика',\n  'diameter' => 'Диаметр',\n  'temperature' => 'Температура',\n  'Place' => 'Место',\n  'unPlace' => '(вне статистики)',\n  'State' => 'Статус',\n  'Alliance' => 'Альянс',\n  'Actions' => 'Действия',\n  'aava' => 'Аватар',\n  'Player' => 'Игрок (статус)',\n  'AllyInfoText' => 'Альянс %n на месте %r содержит %m членов',\n  'Sending_fleet' => 'Отправление флота...',\n  'Sent_fleet' => 'Отправить флот...',\n  'Obtaining_data' => 'Проверка',\n  'Planet_info_tip' => 'Для получения информации о планете, а также для выполнения различных действий наведите курсор мыши на интересующую вас планету',\n  'an_error_has_happened_while_it_was_sent' => 'во время отправки произошла ошибка',\n  'error_there_is_no_moon' => 'ошибка, это не луна',\n  'error_the_player_is_under_the_protection_of_beginners' => 'ошибка, игрок под защитой для новичков',\n  'error_the_player_is_too_strong' => 'ошибка, игрок слишком сильный',\n  'error_the_player_is_in_way_vacation' => 'ошибка, игрок в режиме отпуска',\n  'error_only_x_available_probes_sending' => 'ошибка, доступно только \"+retVals[1]+\" зондов для отправки',\n  'error_there_are_no_available_probes_of_spying' => 'ошибка, нет доступных зондов для шпионажа',\n  'error_you_cannot_send_any_more_fleets' => 'ошибка, вы не можете отправлять больше флотов',\n  'error_you_do_not_have_sufficient_deuterium' => 'ошибка, недостаточно дейтерия',\n  'There_is_not_planet' => 'Это не планета',\n  'error_there_is_no_sufficient_fuel' => 'ошибка, недостаточно топлива',\n  'multialarm' => 'мультитревога',\n  'gm_all' => 'Всё',\n  'gm_launch' => 'Начать ракетную атаку на',\n  'gm_send' => 'Отправить',\n  'gm_target' => 'Цель:',\n  'gal_mis_toLaunch' => 'Количество ракет ',\n  'gal_mis_rest' => 'Ракет: ',\n  'gal_mis_launch' => 'ПУСК',\n  'gl_espionner' => 'Шпионаж',\n  'gl_mipattack' => 'Межпланетная атака',\n  'gl_shortcut' => 'Добавить в закладки',\n  'gl_with' => 'с',\n  'gl_membre' => 'членами',\n  'gl_ally_internal' => 'Инфо&nbsp;альянса',\n  'gl_ally_web' => 'Сайт альянса',\n  'gl_sendmess' => 'Послать сообщение',\n  'gl_buddy' => 'Список друзей',\n  'gl_buddyreq' => 'Добавить в друзья',\n  'gl_stats' => 'Статистика',\n  'gl_planet' => 'Планета',\n  'gl_destroyedplanet' => 'Планета уничтожена',\n  'gl_phalanx' => 'Фаланга',\n  'gl_ressource' => 'Ресурсы',\n  'gl_action' => 'Задание',\n  'gs_c00' => 'Ошибка, миссия недоступна из интерфейса Вселенной',\n  'gs_c01' => 'Ошибка, планета не найдена.',\n  'gs_c02' => 'Ошибка, неправильные координаты во Вселенной',\n  'gs_c03' => 'Ошибка, игрок слишком слабый.',\n  'gs_c04' => 'Ошибка, игрок слишком сильный.',\n  'gs_c04k' => ' У вас нет шпионских зондов!',\n  'gs_c04r' => ' У вас нет переработчиков!',\n  'gs_c05' => 'Ошибка, игрок в режиме отпуска',\n  'gs_c610' => 'Ошибка, недостаточно шпионских дронов',\n  'gs_c610a' => 'Ошибка, with ',\n  'gs_c610b' => 'Ошибка, недостаточно шпионских зондов.',\n  'gs_c611' => 'Ошибка, недостаточно кораблей.',\n  'gs_c612' => 'Ошибка: не хватает мощности компьютеров.',\n  'gs_c613' => 'Ошибка: недостаточно дейтерия',\n  'gs_c616' => 'Ошибка, Тревога!',\n  'gs_c618' => 'Ошибка, вы пытаетесь атаковать самого себя!',\n  'gs_c619' => 'Ошибка',\n  'gs_c620' => 'Ошибка, вы в отпуске!',\n  'gs_sending' => 'отправлен',\n  'gs_to' => 'по координатам',\n  'Sending' => 'Отправить',\n  'gal_planets' => 'Планет заселено: ',\n  'gal_planetNone' => 'Нет заселенных планет',\n  'gf_cntmone' => 'заселённые планеты',\n  'gf_cntmnone' => 'Нет заселённых планет.',\n  'gf_cntmsome' => 'заселённые планеты.',\n  'gf_mi_title' => 'межпланетных ракет',\n  'gf_fleetslt' => 'Флотов',\n  'gf_rc_title' => 'переработчиков',\n  'gf_sp_title' => 'шпионских зондов',\n  'gf_unknowsp' => 'Неисследованное пространство - отправить Экспедицию',\n  'phalanx_header' => 'Флоты в полёте',\n  'phalanx_noflotes' => 'Нет флотов в полете',\n  'phalanx_nodeuterium' => 'Нехватает Дейтерия для работы Сенсорной фаланги.',\n  'phalanx_onlyformoons' => 'Эта постройка доступна только для Луны.',\n  'phalanx_nosensoravailable' => 'У Вас нет Сенсорной фаланги.',\n  'phalanx_rangeerror' => 'Не хватает уровня Сенсорной фаланги!',\n  'phalanx_planet_destroyed' => 'Нельзя сканировать уничтоженную планету!',\n  'phalanx_planet_not_exists' => 'Этой планеты не существует!',\n  'gal_sys_members' => 'Участников:&nbsp;',\n  'gal_sys_hint' => '<ul><li>Подведите крусор мышки к надписи \"Условные обозначения\" в правом верхнем углу таблицы системы что бы получить всплывающее окно с рашифровкой цветов надписей, символов и иконок в таблице системы</li><li>Подведите курсор мышки к картинке планеты, луны, обломков или к имени пользователя, названию альянса, что бы получить всплывающее меню с дополнительными опциями</li></ul>',\n  'uni_debris' => 'Обломки',\n  'uni_need' => 'Нужно',\n  'uni_flying' => 'Летит',\n  'uni_incoming_fleets' => 'Состав прибывающих флотов',\n  'Planets_count' => '( %n планет(а, ы) заселено )',\n\n  'uni_rename' => 'Переименовать',\n  'uni_name' => 'Название',\n  'uni_to_name' => 'Назвать',\n  'uni_naming' => 'Именование',\n  'uni_for' => 'за',\n  'uni_msg_error_wrong_galaxy' => 'Недопустимый номер галактики',\n  'uni_msg_error_wrong_system' => 'Недопустимый номер системы',\n  'uni_msg_error_low_price' => 'Слишком низкая цена именования',\n  'uni_msg_error_no_dm' => 'Не хватает ТМ для именования',\n  'uni_name_page_hint' => \"\n    <li>Вы можете назвать галактику или систему выбранным вами именем</li>\n    <li>Выбранное имя будет видно всем игрокам на странице \\\"Вселенная\\\"</li>\n    <li>Именование неназванной галактики или системы имеет свою базовую цену, которую можно узнать на странице \\\"Мировых констант\\\"</li>\n    <li>При именовании галактики или системы вы можете выбрать цену именования. Цена именования не может быть меньше предложенной</li>\n    <li>Зачем повышать цену именования галактики или системы? Следующий игрок, желающий переименовать галактику или систему после вас, должен будет заплатить вашу цену именования плюс базовую цену. Таким образом покупка имени галактики или вселенной за более высокую цену, чем предложено, позволяет в известной степени защитить объект от переименования</li>\n  \",\n\n  'uni_galaxy_of' => 'галактике',\n  'uni_system_of' => 'системе',\n  'uni_msg_admin_rename' => 'Игрок ID %d [%s] за %d ТМ дал %s [%s%s] новое название: %s',\n\n  'uni_debris_recyclable' => 'К&nbsp;переработке',\n  'uni_debris_incoming_recyclers' => 'В&nbsp;полете',\n  'uni_debris_on_planet' => 'На&nbsp;орбите',\n  'uni_recyclers_send' => 'Отправить переработчики',\n\n  'uni_colonize' => 'Послать колонизатор для основания колонии на позиции номер',\n\n  'uni_scan_start' => 'Включить режим сканирования',\n  'uni_scan_stop' => 'Выйти из режима сканирования',\n\n));\n"
  },
  {
    "path": "login.php",
    "content": "<?php\n\n/**\n * login.php\n *\n * @version 2.0 Security checks & tests by Gorlum for http://supernova.ws\n * @version 1.1 Security checks & tests by Gorlum for http://supernova.ws\n * @version 1.0\n * @copyright 2008 by ?????? for XNova\n */\n\ndefine('LOGIN_LOGOUT', true);\n\n$allow_anonymous = true;\n\ninclude('includes/init.' . substr(strrchr(__FILE__, '.'), 1));\n// die();\nif($template_result[F_USER_IS_AUTHORIZED]) {\n  sys_redirect('index' . DOT_PHP_EX);\n}\nlng_include('login');\nlng_include('admin');\n\n$username_unsafe = sys_get_param_str_unsafe('username');\n$password_raw = trim(sys_get_param('password'));\n$password_repeat_raw = trim(sys_get_param('password_repeat'));\n$email = sys_get_param_str('email');\n\n\n$template = SnTemplate::gettemplate('login_body', true);\n$template->assign_vars(array(\n  'last_user'    => db_user_last_registered_username(),\n  'online_users' => db_user_count(true),\n  'id_ref' => sys_get_param_int('id_ref'),\n  'F_LOGIN_MESSAGE' => $template_result[F_LOGIN_MESSAGE],\n  'F_LOGIN_STATUS' => $template_result[F_LOGIN_STATUS],\n  'LOGIN_ERROR_USERNAME' => LOGIN_ERROR_USERNAME,\n  'LOGIN_ERROR_PASSWORD' => LOGIN_ERROR_PASSWORD,\n  'REGISTER_ERROR_EMAIL_EXISTS' => REGISTER_ERROR_EMAIL_EXISTS,\n  'PASSWORD_RESTORE_ERROR_WRONG_EMAIL' => PASSWORD_RESTORE_ERROR_EMAIL_NOT_EXISTS,\n  'USERNAME'     => htmlentities($username_unsafe, ENT_QUOTES, 'UTF-8'),\n  'EMAIL'     => htmlentities($email, ENT_QUOTES, 'UTF-8'),\n  'PASSWORD'     => htmlentities($password_raw, ENT_QUOTES, 'UTF-8'),\n  'PASSWORD_REPEAT' => htmlentities($password_repeat_raw, ENT_QUOTES, 'UTF-8'),\n  'URL_RULES'    => SN::$config->url_rules,\n  'URL_FORUM'    => SN::$config->url_forum,\n  'URL_FAQ'      => SN::$config->url_faq,\n  'GAME_BLITZ'   => SN::$config->game_mode == GAME_BLITZ,\n));\n\nSnTemplate::tpl_login_lang($template);\n\nSnTemplate::display($template, $lang['Login']);\n"
  },
  {
    "path": "logout.php",
    "content": "<?php\n\n/**\n * logout.php\n *\n * @version 2.0\n */\n\ndefine('LOGIN_LOGOUT', true);\n\ninclude('common.' . substr(strrchr(__FILE__, '.'), 1));\n\n// sn_sys_logout(true);\n// core_auth::logout(true);\nSN::$auth->logout(true);\n"
  },
  {
    "path": "lostpassword.php",
    "content": "<?php\n\n$id_ref = $_GET['id_ref'] ? intval($_GET['id_ref']) : 0;\n$id_ref = $id_ref ? '?id_ref=' . $id_ref : '';\n\nheader('HTTP/1.1 301 Moved Permanently');\nheader(\"Location: login.php{$id_ref}#tab_password_reset\");\n"
  },
  {
    "path": "market.php",
    "content": "<?php\n\n/**\n * market.php\n *\n * Black market\n *\n * 1.0 - copyright (c) 2010 by Gorlum for http://supernova.ws\n *\n */\n\nrequire_once('common.' . substr(strrchr(__FILE__, '.'), 1));\n\ndefine('SN_IN_MARKET', true);\n\nlng_include('market');\nlng_include('fleet');\n\n$mode = sys_get_param_int('mode');\n$action = sys_get_param_int('action');\n$shipList = $_POST['ships'];\n\n$page_title = \"{$lang['eco_mrk_title']}\";\n\n$stock = sys_unit_str2arr(SN::$config->eco_stockman_fleet);\n$newstock = $stock;\n$intError = MARKET_DEAL;\n\nswitch($mode)\n{\n  case MARKET_RESOURCES: // Resource trader\n    require('includes/includes/market_trader.inc');\n    $template = eco_mrk_trader($user, $planetrow);\n  break;\n\n  case MARKET_SCRAPPER: // Fleet scraper\n    $rpg_cost = SN::$config->rpg_cost_scraper;\n    $submode = 'scraper';\n    $error_no_stock = MARKET_NO_SHIPS;\n    $error_zero_res = MARKET_ZERO_RES;\n\n    $config_rpg_scrape_metal     = SN::$config->rpg_scrape_metal;\n    $config_rpg_scrape_crystal   = SN::$config->rpg_scrape_crystal;\n    $config_rpg_scrape_deuterium = SN::$config->rpg_scrape_deuterium;\n\n    $array = sn_get_groups('fleet');\n\n    require('includes/includes/market_fleeter.inc');\n  break;\n\n  case MARKET_STOCKMAN: // S/H ship seller\n    $rpg_cost = SN::$config->rpg_cost_stockman;\n    $submode = 'stockman';\n    $error_no_stock = MARKET_NO_STOCK;\n    $error_zero_res = MARKET_ZERO_RES_STOCK;\n\n    $config_rpg_scrape_metal     = 1 / SN::$config->rpg_scrape_metal;\n    $config_rpg_scrape_crystal   = 1 / SN::$config->rpg_scrape_crystal;\n    $config_rpg_scrape_deuterium = 1 / SN::$config->rpg_scrape_deuterium;\n\n    $array = &$stock;\n\n    require('includes/includes/market_fleeter.inc');\n  break;\n\n  case MARKET_INFO: // Infotrader\n    require('includes/includes/market_info.inc');\n  break;\n\n  case MARKET_EXCHANGE: // Cross-player resource exchange\n  break;\n\n  case MARKET_BANKER: // Banker\n  break;\n\n  case MARKET_PAWNSHOP: // Pawnshop\n  break;\n\n  default:\n    $template = SnTemplate::gettemplate('market', true);\n  break;\n}\n\n$message_id = sys_get_param_int('message');\nif($message_id != MARKET_NOTHING)\n{\n  $template->assign_block_vars('result', array('MESSAGE' => $lang['eco_mrk_errors'][$message_id]));\n}\n\nif($message)\n{\n  $template->assign_block_vars('result', array('MESSAGE' => $message));\n}\n\n$template->assign_vars(array(\n  'rpg_cost_trader'   => SN::$config->rpg_cost_trader,\n  'rpg_cost_scraper'  => SN::$config->rpg_cost_scraper,\n  'rpg_cost_stockman' => SN::$config->rpg_cost_stockman,\n  'rpg_cost_info'     => SN::$config->rpg_cost_info,\n\n  'rpg_cost_banker'   => SN::$config->rpg_cost_banker,\n  'rpg_cost_exchange' => SN::$config->rpg_cost_exchange,\n  'rpg_cost_pawnshop' => SN::$config->rpg_cost_pawnshop,\n\n//  'message' => $message,\n  'MODE' => $mode\n));\n\nSnTemplate::display($template, $page_title);\n"
  },
  {
    "path": "messages.php",
    "content": "<?php\n\n/**\n * messages.php\n * Handles internal message system\n *\n * @package messages\n * @version 3.0\n *\n * Revision History\n * ================\n *\n * 3.0 - copyright (c) 2010-2011 by Gorlum for http://supernova.ws\n *   [!] Full rewrite\n *\n * 2.0 - copyright (c) 2010 by Gorlum for http://supernova.ws\n *   [!] Fully rewrote MessPageMode = 'show' part\n *   [~] All HTML code from 'show' part moved to messages.tpl\n *   [~] Tweaks and optimizations\n *\n * 1.5 - copyright (c) 2010 by Gorlum for http://supernova.ws\n *   [~] Replaced table 'galaxy' with table 'planets'\n *\n * 1.4 - copyright (c) 2010 by Gorlum for http://supernova.ws\n *   [~] Security checked & verified for SQL-injection by Gorlum for http://supernova.ws\n *\n * 1.3 - copyright (c) 2010 by Gorlum for http://supernova.ws\n *   [+] \"Outbox\" added\n *\n * 1.2 - copyright 2008 by Chlorel for XNova\n *   [+] Regroupage des 2 fichiers vers 1 seul plus simple a mettre en oeuvre et a gerer !\n *\n * 1.1 - Mise a plat, linearisation, suppression des doublons / triplons / 'n'gnions dans le code (Chlorel)\n *\n * 1.0 - Version originelle (Tom1991)\n *\n */\n\ninclude('common.' . substr(strrchr(__FILE__, '.'), 1));\n\nglobal $sn_message_class_list, $user, $lang;\n\nlng_include('messages');\n\n$messagePage = new \\Pages\\Deprecated\\PageMessage();\n$messagePage->route();\n"
  },
  {
    "path": "metamatter.php",
    "content": "<?php\n\n// Придумать какой статус должен быть у глобальных ответов, что бы не перекрывать статусы платежных систем\n// Может добавить спецстатус \"Ответ системы платежа\" и парсить дальше getMessage\n// см constants.php\n\nuse Payment\\PaymentMethods;\n\nglobal $debug;\nglobal $template_result;\nglobal $config;\n\n/** @noinspection PhpIncludeInspection */\nrequire_once('common.' . substr(strrchr(__FILE__, '.'), 1));\n\nif (!SN::$gc->modules->countModulesInGroup('payment')) {\n  sys_redirect('dark_matter.php');\n  die();\n}\n\nlng_include('payment');\nlng_include('infos');\n\n$template = SnTemplate::gettemplate('metamatter', true);\n\n// $player_currency_default = player_load_option($user, PLAYER_OPTION_CURRENCY_DEFAULT);\n$player_currency_default = SN::$user_options[PLAYER_OPTION_CURRENCY_DEFAULT];\n$player_currency         = sys_get_param_str('player_currency', $player_currency_default);\nempty(SN::$lang['pay_currency_list'][$player_currency]) ? ($player_currency = $player_currency_default ? $player_currency_default : SN::$config->payment_currency_default) : false;\n// $player_currency_default != $player_currency ? player_save_option($user, PLAYER_OPTION_CURRENCY_DEFAULT, $player_currency) : false;\n$player_currency_default != $player_currency ? SN::$user_options[PLAYER_OPTION_CURRENCY_DEFAULT] = $player_currency : false;\n\n// Таблица скидок\n$prev_discount = 0;\nif (isset(sn_module_payment::$bonus_table) && is_array(sn_module_payment::$bonus_table)) {\n  foreach (sn_module_payment::$bonus_table as $sum => $discount) {\n    if ($discount && $discount != $prev_discount) {\n      $template->assign_block_vars('discount', array(\n        'SUM'          => $sum,\n        'DISCOUNT'     => $discount * 100,\n        'DISCOUNT_ONE' => 1 + $discount,\n        'TEXT'         => sprintf(SN::$lang['pay_mm_bonus_each'], HelperString::numberFloorAndFormat($sum), round($discount * 100)),\n      ));\n      $prev_discount = $discount;\n    }\n  }\n}\n\n// Результат платежа\nif (\n  ($payment_id = sys_get_param_id('payment_id'))\n  ||\n  ($payment_id = sys_get_param_id('ik_pm_no'))\n) {\n  /** @noinspection PhpDeprecationInspection */\n  $payment = doquery(\"SELECT * FROM {{payment}} WHERE `payment_id` = {$payment_id} LIMIT 1;\", true);\n  if ($payment && $payment['payment_user_id'] == $user['id']) {\n    if ($payment['payment_status'] == PAYMENT_STATUS_COMPLETE) {\n      $template->assign_block_vars('result', array('MESSAGE' => sprintf(SN::$lang['pay_msg_mm_purchase_complete'], $payment['payment_dark_matter_paid'], $payment['payment_module_name'], $payment['payment_dark_matter_gained'])));\n    }\n    if ($payment['payment_status'] == PAYMENT_STATUS_NONE) {\n      $template->assign_block_vars('result', array(\n        'MESSAGE' => sprintf(SN::$lang['pay_msg_mm_purchase_incomplete'], $payment['payment_dark_matter_paid'], $payment['payment_module_name']),\n        'STATUS'  => 1,\n      ));\n    }\n    if ($payment['payment_test']) {\n      $template->assign_block_vars('result', array(\n        'MESSAGE' => sprintf(SN::$lang['pay_msg_mm_purchase_test']),\n        'STATUS'  => -1,\n      ));\n    }\n  }\n}\n\n$unit_available_amount_list = &sn_module_payment::$bonus_table;\n\n$request = array(\n  'metamatter' => sys_get_param_float('metamatter'),\n);\n\nif (!$request['metamatter']) {\n  unset($_POST);\n}\n\n$payment_module_request  = sys_get_param_str('payment_module');\n//$payment_type_selected   = sys_get_param_int('payment_type');\n$payment_method_selected = sys_get_param_int('payment_method');\n\n//pdump($payment_module_request, '$payment_module_request');\n//pdump($payment_type_selected, '$payment_type_selected');\n//pdump($payment_method_selected, '$payment_method_selected');\n\nlist($payment_module_request, $payment_method_selected) = PaymentMethods::getActiveMethods()->processInputParams($payment_module_request, $payment_method_selected);\n\n//pdump($payment_module_request, '$payment_module_request');\n//pdump($payment_type_selected, '$payment_type_selected');\n//pdump($payment_method_selected, '$payment_method_selected');\n\n//die();\n\nif (!$payment_module_request && $payment_method_selected) {\n  $template_result['.']['payment_module'] = PaymentMethods::renderModulesForMethod($payment_method_selected, $player_currency, $request);\n} elseif (!$payment_module_request || !$payment_method_selected) {\n  $template_result['.']['payment'] = PaymentMethods::renderPaymentMethodList();\n}\n\nforeach (SN::$lang['pay_currency_list'] as $key => $value) {\n  $course = get_exchange_rate($key);\n  if (!$course) {\n    continue;\n  }\n  $template->assign_block_vars('exchange', array(\n    'SYMBOL'          => $key,\n    'TEXT'            => $value,\n    'COURSE_DIRECT'   => HelperString::numberFormat($course, 4),\n    'COURSE_REVERSE'  => HelperString::numberFormat(1 / $course, 4),\n    'MM_PER_CURRENCY' => HelperString::numberFormat(sn_module_payment::currency_convert(1, $key, 'MM_'), 2),\n    'LOT_PRICE'       => sn_module_payment::currency_convert(get_mm_cost(), 'MM_', $key),\n    'DEFAULT'         => $key == SN::$config->payment_currency_default,\n  ));\n}\n\nif ($request['metamatter'] && $payment_module_request && $payment_method_selected) {\n  try {\n    $paymentModuleReal = SN::$gc->modules->getModule($payment_module_request);\n    if (!is_object($paymentModuleReal)) {\n      throw new Exception('{ Менеджер модулей вернул null вместо платёжного модуля для }' . $payment_module_request, ERR_ERROR);\n    }\n\n    /**\n     * @var sn_module_payment $paymentModuleReal\n     */\n    // Any possible errors about generating paylink should be raised in module!\n    $pay_link = $paymentModuleReal->compile_request($request, $payment_method_selected);\n\n    // Поддержка дополнительной информации\n    if (is_array($pay_link['RENDER'])) {\n      foreach ($pay_link['RENDER'] as $html_data) {\n        $template->assign_block_vars('render', $html_data);\n        if (isset($html_data['VALUE']) && is_array($html_data['VALUE'])) {\n          foreach ($html_data['VALUE'] as $value_id => $value_value) {\n            $template->assign_block_vars('render.value', array(\n              'FIELD' => $value_id,\n              'VALUE' => $value_value,\n            ));\n          }\n        }\n      }\n    }\n\n    // Поддержка передачи данных для многошаговых платежных систем\n    if (is_array($pay_link['DATA'])) {\n      foreach ($pay_link['DATA'] as $key => $value) {\n        $template->assign_block_vars('pay_link_data', array(\n          'FIELD' => $key,\n          'VALUE' => $value,\n        ));\n      }\n    }\n\n    if (is_array($pay_link) && in_array($pay_link['PAY_LINK_METHOD'], array('POST', 'GET', 'LINK', 'STEP', 'REDIRECT'))) {\n      if($pay_link['PAY_LINK_METHOD'] == 'REDIRECT') {\n        sys_redirect($pay_link['PAY_LINK_URL']);\n      }\n\n      // TODO Переделать это под assign_vars_recursive и возвращать пустые строки если нет платежного метода - для унификации формы в темплейте\n      $template->assign_vars(array(\n        'PAY_LINK_METHOD' => $pay_link['PAY_LINK_METHOD'],\n        'PAY_LINK_URL'    => $pay_link['PAY_LINK_URL'],\n      ));\n    } else {\n      throw new exception(SN::$lang['pay_msg_request_paylink_unsupported'], ERR_ERROR);\n    }\n  } catch (exception $e) {\n    $template->assign_block_vars('result', $response = array(\n      'STATUS'  => $e->getCode(),\n      'MESSAGE' => $e->getMessage(),\n    ));\n    $debug->warning('Результат операции: код ' . $e->getCode() . ' сообщение \"' . $e->getMessage() . '\"', 'Ошибка платежа', LOG_INFO_PAYMENT);\n  }\n}\n\n// Прегенерированные пакеты\nforeach ($unit_available_amount_list as $unit_amount => $discount) {\n  $temp = sn_module_payment::currency_convert($unit_amount, 'MM_', $player_currency);\n  $template->assign_block_vars('mm_amount', array(\n    'VALUE'            => $unit_amount,\n    // 'PRICE' => $temp,\n    'PRICE_TEXT'       => HelperString::numberFormat($temp, 2),\n    'CURRENCY'         => $player_currency,\n    'DISCOUNT'         => $discount,\n    'DISCOUNT_PERCENT' => $discount * 100,\n    'DISCOUNTED'       => $unit_amount * (1 + $discount),\n    'TEXT'             => HelperString::numberFloorAndFormat($unit_amount),\n    'TEXT_DISCOUNTED'  => HelperString::numberFloorAndFormat($unit_amount * (1 + $discount)),\n  ));\n}\n\n$currency               = PaymentMethods::getCurrencyFromMethod($payment_module_request, $payment_method_selected);\n$bonus_percent          = round(sn_module_payment::bonus_calculate($request['metamatter'], true, true) * 100);\n$income_metamatter_text = prettyNumberStyledDefault(sn_module_payment::bonus_calculate($request['metamatter']));\n\n$approxCost = '';\nif (!empty($payment_module_request) && !empty($payment_method_selected)) {\n  $mod = SN::$gc->modules->getModule($payment_module_request);\n\n  /**\n   * @var sn_module_payment $mod\n   */\n  $tPrice = $mod->getPrice($payment_method_selected, $player_currency, $request['metamatter']);\n  if (!empty($tPrice) && is_array($tPrice)) {\n    $approxCost = sprintf(\n      SN::$lang['pay_mm_buy_approximate_cost'],\n      HelperString::numberFormat($tPrice[$mod::FIELD_SUM], 2),\n      $tPrice[$mod::FIELD_CURRENCY]\n    );\n  }\n}\n\n\n$template->assign_vars([\n  'PAGE_HEADER' => SN::$lang['sys_metamatter'],\n\n  'URL_PURCHASE' => SN::$config->url_purchase_metamatter,\n\n  'PAYMENT_METHOD'      => $payment_method_selected,\n  'PAYMENT_METHOD_NAME' => SN::$lang['pay_methods'][$payment_method_selected],\n\n  'PAYMENT_MODULE'             => $payment_module_request,\n  'PAYMENT_MODULE_NAME'        => SN::$lang[\"module_{$payment_module_request}_name\"],\n  'PAYMENT_MODULE_DESCRIPTION' => SN::$lang[\"module_{$payment_module_request}_description\"],\n\n  'PLAYER_CURRENCY'              => $player_currency,\n  'PLAYER_CURRENCY_PRICE_PER_MM' => sn_module_payment::currency_convert(1, $player_currency, 'MM_', 10),\n\n  'UNIT_AMOUNT'                 => (float)$request['metamatter'],\n  'UNIT_AMOUNT_TEXT'            => HelperString::numberFloorAndFormat($request['metamatter']),\n  'UNIT_AMOUNT_BONUS_PERCENT'   => $bonus_percent,\n  'UNIT_AMOUNT_TEXT_DISCOUNTED' => $income_metamatter_text,\n  'UNIT_AMOUNT_TEXT_COST_BASE'  => HelperString::numberFormat(sn_module_payment::currency_convert($request['metamatter'], 'MM_', $player_currency), 2),\n\n  'PAYMENT_CURRENCY_EXCHANGE_DEFAULT' => prettyNumberStyledDefault(get_mm_cost()),\n  'PAYMENT_CURRENCY_DEFAULT_TEXT'     => SN::$lang['pay_currency_list'][SN::$config->payment_currency_default],\n\n  'METAMATTER' => mrc_get_level($user, '', RES_METAMATTER),\n\n  'METAMATTER_COST_TEXT'       => sprintf(SN::$lang['pay_mm_buy_conversion_cost'],\n    prettyNumberStyledDefault($request['metamatter']),\n    number_format($mmWish = sn_module_payment::currency_convert($request['metamatter'], 'MM_', $currency), 2, ',', '.'),\n    $currency,\n    prettyNumberGetClass($mmWish, true)),\n  'METAMATTER_COST_BONUS_TEXT' => $bonus_percent\n    ? sprintf(SN::$lang['pay_mm_buy_real_income'], prettyNumberStyledDefault($bonus_percent), $income_metamatter_text)\n    : '',\n\n  'METAMATTER_COST_ON_PAYMENT' => $approxCost,\n\n  'DARK_MATTER_DESCRIPTION' => SN::$lang['info'][RES_DARK_MATTER]['description'],\n\n  'PAYMENT_AVAILABLE' => SN::$gc->modules->countModulesInGroup('payment') && !SN_GOOGLE,\n\n]);\n\n$template->assign_recursive($template_result);\n\nSnTemplate::display($template, SN::$lang['sys_metamatter']);\n"
  },
  {
    "path": "notes.php",
    "content": "<?php\n\n/**\n * notes.php\n *\n * Changelog:\n *   2.0 copyright © 2009-2012 Gorlum for http://supernova.ws\n *     [!] Wrote from scratch\n */\n\nuse DBAL\\db_mysql;\n\ninclude('common.' . substr(strrchr(__FILE__, '.'), 1));\n\nlng_include('notes');\n\n$template = SnTemplate::gettemplate('notes', true);\n\n$result = array();\nif(($result_message = sys_get_param_str('MESSAGE')) && isset($lang[$result_message])) {\n  $result[] = array('STATUS' => sys_get_param_int('STATUS'), 'MESSAGE' => $lang[$result_message]);\n}\n\n$note_id_edit = sys_get_param_id('note_id_edit');\nif(sys_get_param('note_delete')) {\n  try {\n    $not = '';\n    $query_where = '';\n    switch(sys_get_param_str('note_delete_range')) {\n      case 'all':\n      break;\n\n      case 'marked_not':\n        $not = 'NOT';\n      case 'marked':\n        if(!is_array($notes_marked = sys_get_param('note'))) {\n          throw new exception('note_err_none_selected', ERR_WARNING);\n        }\n\n        $notes_marked_filtered = array();\n        foreach($notes_marked as $note_id => $note_select) {\n          if($note_select == 'on' && $note_id = idval($note_id)) {\n            $notes_marked_filtered[] = $note_id;\n          }\n        }\n\n        if(empty($notes_marked_filtered)) {\n          throw new exception('note_err_none_selected', ERR_WARNING);\n        }\n\n        $notes_marked_filtered = implode(',', $notes_marked_filtered);\n        $query_where = \"AND `id` {$not} IN ({$notes_marked_filtered})\";\n      break;\n\n      default:\n        throw new exception('note_warn_no_range', ERR_WARNING);\n      break;\n    }\n\n    db_mysql::db_transaction_start();\n    doquery(\"DELETE FROM {{notes}} WHERE `owner` = {$user['id']} {$query_where};\");\n    db_mysql::db_transaction_commit();\n    throw new exception($note_id_edit ? 'note_err_none_changed' : 'note_err_none_added', ERR_NONE);\n  } catch(exception $e) {\n    $note_id_edit = 0;\n    db_mysql::db_transaction_rollback();\n    $result[] = array(\n      'STATUS'  => in_array($e->getCode(), array(ERR_NONE, ERR_WARNING, ERR_ERROR)) ? $e->getCode() : ERR_ERROR,\n      'MESSAGE' => $lang[$e->getMessage()],\n    );\n  }\n} elseif(($note_title = sys_get_param_str('note_title')) || ($note_text = sys_get_param_str('note_text'))) {\n  $note_title == SN::$db->db_escape($lang['note_new_title']) ? $note_title = '' : false;\n  ($note_text = sys_get_param_str('note_text')) == SN::$db->db_escape($lang['note_new_text']) ? $note_text = '' : false;\n\n  try {\n    $note_galaxy = max(0, min(sys_get_param_id('note_galaxy'), SN::$config->game_maxGalaxy));\n    $note_system = max(0, min(sys_get_param_id('note_system'), SN::$config->game_maxSystem));\n    $note_planet = max(0, min(sys_get_param_id('note_planet'), SN::$config->game_maxPlanet + 1));\n\n    if(!$note_text && !$note_title && !$note_galaxy && !$note_system && !$note_planet) {\n      throw new exception('note_err_note_empty', ERR_WARNING);\n    }\n\n    $note_priority = min(sys_get_param_id('note_priority', 2), count($note_priority_classes) - 1);\n    $note_planet_type = max(1, min(sys_get_param_id('note_planet_type', 1), count($lang['sys_planet_type'])));\n    $note_sticky = intval(sys_get_param_id('note_sticky')) ? 1 : 0;\n\n    db_mysql::db_transaction_start();\n    if($note_id_edit) {\n      $check_note_id = doquery(\"SELECT `id`, `owner` FROM {{notes}} WHERE `id` = {$note_id_edit} LIMIT 1 FOR UPDATE\", true);\n      if(!$check_note_id) {\n        throw new exception('note_err_note_not_found', ERR_ERROR);\n      }\n    }\n\n    if($note_id_edit) {\n      if($check_note_id['owner'] != $user['id']) {\n        throw new exception('note_err_owner_wrong', ERR_ERROR);\n      }\n\n      doquery(\"UPDATE {{notes}} SET `time` = \" . SN_TIME_NOW . \", `priority` = {$note_priority}, `title` = '{$note_title}', `text` = '{$note_text}',\n        `galaxy` = {$note_galaxy}, `system` = {$note_system}, `planet` = {$note_planet}, `planet_type` = {$note_planet_type}, `sticky` = {$note_sticky}\n        WHERE `id` = {$note_id_edit} LIMIT 1;\");\n    } else {\n      doquery(\"INSERT INTO {{notes}} SET `owner` = {$user['id']}, `time` = \" . SN_TIME_NOW . \", `priority` = {$note_priority}, `title` = '{$note_title}', `text` = '{$note_text}',\n        `galaxy` = {$note_galaxy}, `system` = {$note_system}, `planet` = {$note_planet}, `planet_type` = {$note_planet_type}, `sticky` = {$note_sticky};\");\n    }\n\n    db_mysql::db_transaction_commit();\n    sys_redirect('notes.php?STATUS=' . ERR_NONE . '&MESSAGE=' . ($note_id_edit ? 'note_err_none_changed' : 'note_err_none_added'));\n//    throw new exception($note_id_edit ? 'note_err_none_changed' : 'note_err_none_added', ERR_NONE);\n  } catch(exception $e) {\n    $note_id_edit = 0;\n    db_mysql::db_transaction_rollback();\n    $result[] = array(\n      'STATUS'  => in_array($e->getCode(), array(ERR_NONE, ERR_WARNING, ERR_ERROR)) ? $e->getCode() : ERR_ERROR,\n      'MESSAGE' => $lang[$e->getMessage()],\n    );\n  }\n}\n\nif(!$note_id_edit) {\n  \\Note\\Note::note_assign($template, array(\n    'id' => 0,\n    'time' => SN_TIME_NOW,\n    'priority' => 2,\n    'planet_type' => PT_PLANET,\n    'title' => $lang['note_new_title'],\n    'text' => $lang['note_new_text'],\n  ));\n}\n\n$note_exist = false;\n$notes_query = doquery(\"SELECT * FROM {{notes}} WHERE owner={$user['id']} ORDER BY priority DESC, galaxy ASC, system ASC, planet ASC, planet_type ASC, `time` DESC\");\nwhile($note_row = db_fetch($notes_query)) {\n  \\Note\\Note::note_assign($template, $note_row);\n  $note_exist = $note_exist || $note_row['id'] == $note_id_edit;\n}\n$note_id_edit = $note_exist ? $note_id_edit : 0;\n\nforeach($note_priority_classes as $note_priority_id => $note_priority_class) {\n  $template->assign_block_vars('note_priority', array(\n    'ID' => $note_priority_id,\n    'CLASS' => $note_priority_classes[$note_priority_id],\n    'TEXT' => $lang['sys_notes_priorities'][$note_priority_id],\n  ));\n}\n\nforeach($lang['sys_planet_type'] as $planet_type_id => $planet_type_string) {\n  $template->assign_block_vars('planet_type', array(\n    'ID' => $planet_type_id,\n    'TEXT' => $planet_type_string,\n  ));\n}\n\nforeach($result as $result_data) {\n  $template->assign_block_vars('result', $result_data);\n}\n\n$template->assign_vars(array(\n  'PAGE_HEADER' => $lang['note_page_header'],\n  'NOTE_ID_EDIT' => $note_id_edit,\n  'NOTE_FULL_RENDER' => true,\n));\n\nSnTemplate::display($template);\n"
  },
  {
    "path": "officer.php",
    "content": "<?php\n\n/**\n * officer.php\n * Handles officer hire\n *\n * @package roleplay\n * @version 2.0\n *\n * copyright (c) 2009-2012 by Gorlum for http://supernova.ws\n *\n */\n\ninclude('common.' . substr(strrchr(__FILE__, '.'), 1));\n\nglobal $user;\n$thePage = new \\Pages\\Deprecated\\PageMercenary();\n$thePage->mrc_mercenary_render($user);\n"
  },
  {
    "path": "overview.php",
    "content": "<?php\n/**\n * index.php - overview.php\n *\n * 2.4 - copyright (c) 2010 by Gorlum for http://supernova.ws\n *     [-] Removed News frame\n *     [-] Time & Usersonline moved to Top-Frame\n * 2.3 - copyright (c) 2010 by Gorlum for http://supernova.ws\n *     [*] Complying with PCG\n * 2.2 - copyright (c) 2010 by Gorlum for http://supernova.ws\n *     [+] Redo flying fleet list\n * 2.1 - copyright (c) 2010 by Gorlum for http://supernova.ws\n *     [+] Planets on planet list now have indication of planet fill\n *     [+] Planets on planet list now have indication when there is enemy fleet flying to planet\n * 2.0 - copyright (c) 2010 by Gorlum for http://supernova.ws\n *     [+] Now there is full planet list on right side of screen a-la oGame\n *     [+] Planet list now include icons for buildings/tech/fleet on progress\n * 1.5 - copyright (c) 2010 by Gorlum for http://supernova.ws\n *     [*] Subplanet timers now use sn_timer.js library\n * 1.4 - copyright (c) 2010 by Gorlum for http://supernova.ws\n *     [*] All mainplanet timers now use new sn_timer.js library\n * 1.3 - copyright (c) 2010 by Gorlum for http://supernova.ws\n *     [*] Adjusted layouts of player infos\n * 1.2 - copyright (c) 2010 by Gorlum for http://supernova.ws\n *     [*] Adjusted layouts of planet infos\n * 1.1 - Security checks by Gorlum for http://supernova.ws\n * @version 1\n * @copyright 2008 By Chlorel for XNova\n */\n\n//define('SN_RENDER_NAVBAR_PLANET', false);\n\nuse Pages\\Deprecated\\PageOverview;\n\ninclude('common.' . substr(strrchr(__FILE__, '.'), 1));\n\n$pageOverview = new PageOverview();\n$pageOverview->route();\n"
  },
  {
    "path": "phalanx.php",
    "content": "<?php\n\n/**\n * phalanx.php\n *\n * 2.0 copyright (c) 2009-2011 by Gorlum for http://supernova.ws\n     [!] Full rewrote using SN functions\n * 1.2 - Security checks & tests by Gorlum for http://supernova.ws\n * @version 1.1\n * @original made by ????\n * @copyright 2008 by Pada for XNova.project.es\n */\n\nuse Fleet\\DbFleetStatic;\nuse Planet\\DBStaticPlanet;\n\ninclude('common.' . substr(strrchr(__FILE__, '.'), 1));\n\nlng_include('overview');\nlng_include('universe');\n\n$sensorLevel = mrc_get_level($user, $planetrow, STRUC_MOON_PHALANX);\nif (!intval($sensorLevel)) {\n  SnTemplate::messageBox($lang['phalanx_nosensoravailable'], $lang['tech'][STRUC_MOON_PHALANX], '', 3);\n}\n\nif ($planetrow['planet_type'] != PT_MOON) {\n  SnTemplate::messageBox($lang['phalanx_onlyformoons'], $lang['tech'][STRUC_MOON_PHALANX], '', 3);\n}\n\n$scan_galaxy  = sys_get_param_int('galaxy');\n$scan_system  = sys_get_param_int('system');\n$scan_planet  = sys_get_param_int('planet');\n$scan_planet_type  = 1; // sys_get_param_int('planettype');\n$id = sys_get_param_id('id');\n\n$source_galaxy = $planetrow['galaxy'];\n$source_system = $planetrow['system'];\n$source_planet = $planetrow['planet'];\n\n$sensorRange = GetPhalanxRange($sensorLevel);\n\n$system_distance = abs($source_system - $scan_system);\nif($system_distance > $sensorRange || $scan_galaxy != $source_galaxy)\n{\n  SnTemplate::messageBox($lang['phalanx_rangeerror'], $lang['tech'][STRUC_MOON_PHALANX], '', 3);\n}\n\n$cost = $sensorLevel * 1000;\n\nif ($planetrow['deuterium'] < $cost)\n{\n  SnTemplate::messageBox($lang['phalanx_nodeuterium'], \"phalanx\", '', 3);\n}\n\n$planet_scanned = DBStaticPlanet::db_planet_by_gspt($scan_galaxy, $scan_system, $scan_planet, $scan_planet_type);\nif(!$planet_scanned['id'])\n{\n  SnTemplate::messageBox($lang['phalanx_planet_not_exists'], $lang['tech'][STRUC_MOON_PHALANX], '', 3);\n}\n\nif($planet_scanned['destruyed'])\n{\n  SnTemplate::messageBox($lang['phalanx_planet_destroyed'], $lang['tech'][STRUC_MOON_PHALANX], '', 3);\n}\n\nDBStaticPlanet::db_planet_set_by_id($user['current_planet'], \"deuterium = deuterium - {$cost}\");\n\n$template = SnTemplate::gettemplate('planet_fleet_list', true);\n\n$fleet_list = DbFleetStatic::fleet_and_missiles_list_by_coordinates($planet_scanned, true);\n$fleets = flt_parse_fleets_to_events($fleet_list, $planet_scanned);\ntpl_assign_fleet($template, $fleets);\n\n$template->assign_vars(array(\n  'MENU' => false,\n  'NAVBAR' => false,\n));\n\nSnTemplate::display($template, $lang['tech'][STRUC_MOON_PHALANX]);\n"
  },
  {
    "path": "quest.php",
    "content": "<?php\n\n/**\n * quest.php\n *\n * @v1 (c) copyright 2011 by Gorlum for http://supernova.ws\n *\n */\n\ndefine('INSIDE'  , true);\ndefine('INSTALL' , false);\n\nrequire('common.' . substr(strrchr(__FILE__, '.'), 1));\n\nroughQuestRenderWrapper();\n"
  },
  {
    "path": "ranks.php",
    "content": "<?php\n/**\n * Created by Gorlum 18.02.2018 16:42\n */\n\ninclude_once('common.' . substr(strrchr(__FILE__, '.'), 1));\n\n$template = SnTemplate::gettemplate('rank_list', true);\n\nfor ($i = 0; $i <= 20; $i++) {\n  $template->assign_block_vars('player_rank', [\n    'ID' => $i,\n    'NAME' => $lang['ranks'][$i],\n    'SELECTED' => $i == SN::$gc->playerLevelHelper->getPointLevel($user['total_points'], $user['authlevel']),\n  ]);\n}\n\n$template->assign_vars([\n  'PAGE_HEADER' => $lang['rank_page_title'],\n]);\n\nSnTemplate::display($template, $lang['rank_page_title']);\n"
  },
  {
    "path": "records.php",
    "content": "<?php\n\n/**\n * records.php\n *\n * 2.0 - Full rewrite by Gorlum for http://supernova.ws\n * 1.4st - Security checks & tests by Gorlum for http://supernova.ws\n * @version 1.4\n * @copyright 2008 by Chlorel for XNova\n */\n\ninclude('common.' . substr(strrchr(__FILE__, '.'), 1));\n\nif(HIDE_BUILDING_RECORDS)\n{\n  return;\n}\n\n$template = SnTemplate::gettemplate('records', true);\n\n$user_skip_list_data = sys_stat_get_user_skip_list();\n$user_skip_list = empty($user_skip_list_data) ? '' : (' AND p.id_owner NOT IN (' . implode(',', $user_skip_list_data) . ')');\n$user_skip_list_un = empty($user_skip_list_data) ? '' : (' AND un.unit_player_id NOT IN (' . implode(',', $user_skip_list_data) . ')');\n\n$user_skip_list_unit = empty($user_skip_list_data) ? '' : (' AND unit_player_id NOT IN (' . implode(',', $user_skip_list_data) . ')');\n\n$show_groups = array(\n  UNIT_TECHNOLOGIES => 'tech',\n  UNIT_STRUCTURES => 'structures',\n  UNIT_STRUCTURES_SPECIAL => 'structures',\n  UNIT_SHIPS => 'fleet',\n  UNIT_DEFENCE => 'defense',\n);\n\n$user_name_cache = array();\n\nforeach($show_groups as $unit_group_id => $mode)\n{\n  $template->assign_block_vars('records', array(\n    'UNIT' => $lang['tech'][$unit_group_id],\n    'COUNT' => in_array($unit_group_id, array(UNIT_STRUCTURES, UNIT_STRUCTURES_SPECIAL, UNIT_TECHNOLOGIES)) ? $lang['sys_level_max'] : $lang['sys_quantity_total'],\n    'HEADER' => true,\n  ));\n  $unit_group = get_unit_param('techtree', $unit_group_id); // TODO - REWRITE!!!!\n\n  foreach($unit_group as $unit_id)\n  {\n    $unit_name = &$lang['tech'][$unit_id];\n    if($unit_name)\n    {\n      // TODO - ISUNITSTACKABLE!\n      $data_row = $unit_group_id == UNIT_SHIPS || $unit_group_id == UNIT_DEFENCE ? db_unit_records_sum($unit_id, $user_skip_list_unit) : db_unit_records_plain($unit_id, $user_skip_list_unit);\n\n      if($data_row)\n      {\n        $template->assign_block_vars('records', array(\n          'UNIT' => $unit_name,\n          'USER' => $data_row['username'] ? js_safe_string($data_row['username']) : $lang['rec_rien'],\n          'COUNT' => $data_row['unit_level'] ? HelperString::numberFloorAndFormat($data_row['unit_level']) : $lang['rec_rien'],\n        ));\n      }\n    }\n  }\n}\n\nSnTemplate::display($template, $lang['rec_title']);\n"
  },
  {
    "path": "reg.php",
    "content": "<?php\n\n$id_ref = isset($_GET['id_ref']) ? intval($_GET['id_ref']) : 0;\n$id_ref = $id_ref ? '?id_ref=' . $id_ref : '';\n\nheader('HTTP/1.1 301 Moved Permanently');\nheader(\"Location: login.php{$id_ref}#tab_register\");\n"
  },
  {
    "path": "resources.php",
    "content": "<?php\n\n/**\n * resources.php\n *\n * Planet resource interface page\n *\n * 2.2 - copyright (ñ) 2010 by Gorlum for http://supernova.ws\n *   [~] - more optimization to utilize PTE\n *   [~] - code formatting according to PCG\n *   [~] - content of BuildRessourcePage.php moved to resource.php\n * 2.1 - copyright 2010 by Gorlum for http://supernova.ws\n *   [~] - Security checked for SQL-injection\n * 2.0 - copyright 2010 by Gorlum for http://supernova.ws\n *   [+] - almost fully rewrote and optimized\n * 1.0 [BuildRessourcePage.php] copyright 2008 by ShadoV for XNova\n *   [+] - Mise en module initiale (creation)\n * 1.1 - copyright 2010 by Gorlum for http://supernova.ws\n *   [%] - Security checks & tests by Gorlum for http://supernova.ws\n * 1.0 copyright (ñ) 2008 by Chlorel for XNova\n *   [!] - Passage en fonction pour utilisation XNova\n *\n**/\n\ninclude('common.' . substr(strrchr(__FILE__, '.'), 1));\n\nuse \\Meta\\Economic\\ResourceCalculations;\nuse Planet\\DBStaticPlanet;\nuse Planet\\Planet;\n\n/**\n * @param $resource_id\n * @param ResourceCalculations $capsObj\n */\nfunction int_calc_storage_bar($resource_id, $capsObj)\n{\n  global $lang, $template, $planetrow, $user;\n\n  $totalProduction      = $capsObj->getProduction($resource_id);\n  $storage_fill         = $capsObj->getStorage($resource_id) ? floor(mrc_get_level($user, $planetrow, $resource_id) / $capsObj->getStorage($resource_id) * 100) : 0;\n\n  $template->assign_block_vars('resources', [\n    'NAME'        => $lang[\"sys_\" . pname_resource_name($resource_id)],\n\n    'HOURLY'      => $totalProduction,\n    'DAILY'       => $totalProduction * 24,\n    'WEEKLY'      => $totalProduction * 24 * 7,\n    'MONTHLY'     => $totalProduction * 24 * 30,\n\n    'STORAGE'     => intval($storage_fill),\n    'BAR'         => min($storage_fill, 100),\n  ]);\n};\n\n$ValidList['percent'] = array (  0,  10,  20,  30,  40,  50,  60,  70,  80,  90, 100 );\n$template = SnTemplate::gettemplate('resources', true);\n\n/** @noinspection PhpUnhandledExceptionInspection */\n$planet = SN::$gc->repoV2->getPlanet($planetrow['id']);\nif(!empty($transmutation_result = $planet->sn_sys_planet_core_transmute($user))) {\n  $template->assign_block_vars('result', $transmutation_result);\n  $planet->dbLoadRecord($planetrow['id']);\n}\n\n$sn_group_factories = sn_get_groups('factories');\n/**\n * @param debug  $debug\n * @param array  $sn_group_factories\n * @param array  $planetrow\n * @param Planet $planet\n *\n * @return mixed\n */\nfunction updateProductionSpeeds($debug, $sn_group_factories, $planetrow, $planet) {\n  $production = $_POST['production'];\n  if (!is_array($production)) {\n    return $planetrow;\n  }\n\n  $SubQry = [];\n  foreach ($production as $prod_id => $percent) {\n    if ($percent > 100 || $percent < 0) {\n      $debug->warning('Supplying wrong production percent (less then 0 or greater then 100)', 'Hack attempt', 302, array('base_dump' => true));\n      die();\n    }\n\n    $prod_id = intval($prod_id);\n    if (in_array($prod_id, $sn_group_factories) && get_unit_param($prod_id, P_MINING_IS_MANAGED)) {\n      $field_name = pname_factory_production_field_name($prod_id);\n      $percent = floor($percent / 10);\n      $planetrow[$field_name] = $percent;\n      //$SubQry                 .= \"`{$field_name}` = '{$percent}',\";\n      $SubQry[] = \"`{$field_name}` = '{$percent}'\";\n    } else {\n      $debug->warning('Supplying wrong ID in production array - attempt to change some field - ID' . $prod_id, 'Resource Page', 301);\n      continue;\n    }\n  }\n\n  !empty($SubQry) ? DBStaticPlanet::db_planet_set_by_id($planetrow['id'], implode(',', $SubQry)) : false;\n  if (!empty($SubQry)) {\n    $planet->dbLoadRecord($planetrow['id']);\n  }\n\n  return $planetrow;\n}\n\n$planetrow = updateProductionSpeeds($debug, $sn_group_factories, $planetrow, $planet);\n\n// -------------------------------------------------------------------------------------------------------\n// $BuildTemp                   = $planetrow[ 'temp_max' ];\n// $BuildEnergyTech             = $user['energy_tech'];\nfor ($Option = 10; $Option >= 0; $Option--)\n{\n $template->assign_block_vars('option', array(\n   'VALUE' => $Option * 10,\n ));\n}\n\n$capsObj = new ResourceCalculations();\n$capsObj->eco_get_planet_caps($user, $planetrow, 3600);\n\n$template->assign_block_vars('production', array(\n  'TYPE'           => $lang['res_basic_income'],\n\n  'METAL_TYPE'     => $capsObj->productionCurrentMatrix[RES_METAL][0],\n  'CRYSTAL_TYPE'   => $capsObj->productionCurrentMatrix[RES_CRYSTAL][0],\n  'DEUTERIUM_TYPE' => $capsObj->productionCurrentMatrix[RES_DEUTERIUM][0],\n  'ENERGY_TYPE'    => $capsObj->productionCurrentMatrix[RES_ENERGY][0],\n));\n\nforeach($sn_group_factories as $unit_id)\n{\n  if(mrc_get_level($user, $planetrow, $unit_id) > 0 && get_unit_param($unit_id))\n  {\n    $level_plain = mrc_get_level($user, $planetrow, $unit_id, false, true);\n    $template->assign_block_vars('production', array(\n      'ID'             => $unit_id,\n      'PERCENT'        => $planetrow[pname_factory_production_field_name($unit_id)] * 10,\n      'TYPE'           => $lang['tech'][$unit_id],\n      'LEVEL'          => $level_plain,\n      'LEVEL_BONUS'    => mrc_get_level($user, $planetrow, $unit_id) - $level_plain,\n      'LEVEL_TYPE'     => ($unit_id > 200) ? $lang['quantity'] : $lang['level'],\n\n      'METAL_TYPE'     => $capsObj->productionCurrentMatrix[RES_METAL][$unit_id],\n      'CRYSTAL_TYPE'   => $capsObj->productionCurrentMatrix[RES_CRYSTAL][$unit_id],\n      'DEUTERIUM_TYPE' => $capsObj->productionCurrentMatrix[RES_DEUTERIUM][$unit_id],\n      'ENERGY_TYPE'    => $capsObj->productionCurrentMatrix[RES_ENERGY][$unit_id],\n\n      'METAL_FULL'     => $capsObj->productionFullMatrix[RES_METAL][$unit_id],\n      'CRYSTAL_FULL'   => $capsObj->productionFullMatrix[RES_CRYSTAL][$unit_id],\n      'DEUTERIUM_FULL' => $capsObj->productionFullMatrix[RES_DEUTERIUM][$unit_id],\n      'ENERGY_FULL'    => $capsObj->productionFullMatrix[RES_ENERGY][$unit_id],\n\n      'P_MINING_IS_MANAGED' => get_unit_param($unit_id, P_MINING_IS_MANAGED),\n    ));\n  }\n}\n\n\n$user_dark_matter = mrc_get_level($user, false, RES_DARK_MATTER);\n$template->assign_recursive($planet->tpl_planet_density_info($user_dark_matter));\n\n$template->assign_block_vars('production', array(\n  'TYPE'           => $lang['res_total'],\n\n  'METAL_TYPE'     => $capsObj->getProduction(RES_METAL),\n  'CRYSTAL_TYPE'   => $capsObj->getProduction(RES_CRYSTAL),\n  'DEUTERIUM_TYPE' => $capsObj->getProduction(RES_DEUTERIUM),\n  'ENERGY_TYPE'    => $capsObj->getProduction(RES_ENERGY),\n\n  'METAL_FULL'     => $capsObj->getProductionFull(RES_METAL),\n  'CRYSTAL_FULL'   => $capsObj->getProductionFull(RES_CRYSTAL),\n  'DEUTERIUM_FULL' => $capsObj->getProductionFull(RES_DEUTERIUM),\n  'ENERGY_FULL'    => $capsObj->getProductionFull(RES_ENERGY),\n));\n\nint_calc_storage_bar(RES_METAL, $capsObj);\nint_calc_storage_bar(RES_CRYSTAL, $capsObj);\nint_calc_storage_bar(RES_DEUTERIUM, $capsObj);\n\n$template->assign_vars(array(\n 'PLANET_NAME'          => $planetrow['name'],\n 'PLANET_TYPE'          => $planetrow['planet_type'],\n\n 'PRODUCTION_LEVEL'     => floor($capsObj->efficiency * 100),\n\n 'PAGE_HINT'            => $lang['res_hint'],\n));\n\nSnTemplate::display($template, $lang['res_planet_production']);\n"
  },
  {
    "path": "robots.txt",
    "content": "User-agent: *\nAllow: /reg.php\nDisallow: /\n\nUser-agent: Yandex\nAllow: /reg.php\nDisallow: /"
  },
  {
    "path": "scheduler.php",
    "content": "<?php\n\n/**\n * scheduler.php\n * Built-in autorun scheduler\n *\n * @package statistics\n * @version 2\n *\n * Revision History\n * ================\n *    2 - copyright (c) 2009-2010 by Gorlum for http://supernova.ws\n *      [+] Added locking mechanic made impossible to run several updates at once\n *      [~] Complies to PCG1\n *\n *    1 - copyright (c) 2009-2010 by Gorlum for http://supernova.ws\n *      [!] Initial revision wrote from scratch\n *\n */\n\nrequire_once('includes/init.php');\n\ndefine('IN_AJAX', true);\n\nif(($result = StatUpdateLauncher::scheduler_process()) && !defined('IN_ADMIN')) {\n  $result = htmlspecialchars($result, ENT_QUOTES, 'UTF-8');\n  print(json_encode($result));\n}\n\nif(!defined('IN_ADMIN')) {\n  die();\n}\n"
  },
  {
    "path": "search.php",
    "content": "<?php\n/**\n * search.php\n *\n * 1.3 copyright (c) 2009-2010 by Gorlum for http://supernova.ws\n *   [%] Fixed search of players without alliance\n * 1.2 - Security checks & tests by Gorlum for http://supernova.ws\n * @version 1.1\n * @copyright 2009 by angelus_ira for Project. XNova\n * @copyright 2008 by ??????? for XNova\n */\n\ninclude('common.' . substr(strrchr(__FILE__, '.'), 1));\n\nif(SN::$config->game_mode == GAME_BLITZ) {\n  SnTemplate::messageBox($lang['sys_blitz_page_disabled'], $lang['sys_error'], 'overview.php', 10);\n  die();\n}\n\nlng_include('search');\n\n$searchtext = sys_get_param_str('searchtext');\n$type = sys_get_param_str('type');\n\n\n$template = SnTemplate::gettemplate('search', true);\n\nif($searchtext && $type)\n{\n  switch($type)\n  {\n    case \"planetname\":\n      // $search = db_planet_list_search($searchtext);\n    break;\n\n    case \"ally\":\n      $search = doquery(\"SELECT ally_name, ally_tag, total_rank, ally_members FROM {{alliance}} WHERE ally_tag LIKE '%{$searchtext}%' OR ally_name LIKE '%{$searchtext}%' LIMIT 30\");\n    break;\n\n    case \"playername\":\n    default:\n      $search = db_user_list_search($searchtext);\n    break;\n  }\n\n  while($row = db_fetch($search))\n  {\n    if($type=='playername' || $type=='planetname')\n    {\n      $template->assign_block_vars('search_result', array(\n        'PLAYER_ID' => $row['uid'],\n        'PLAYER_NAME' => htmlentities($row['username'], ENT_COMPAT, 'UTF-8'),\n        'PLAYER_NAME_OLD' => htmlentities($row['player_name'], ENT_COMPAT, 'UTF-8'),\n        'PLAYER_RANK' => HelperString::numberFloorAndFormat($row['total_rank']),\n        'PLAYER_RANK_RAW' => floatval($row['total_rank']),\n        'PLANET_NAME' => htmlentities($row['planet_name'], ENT_COMPAT, 'UTF-8'),\n        'PLANET_GALAXY' => $row['galaxy'],\n        'PLANET_SYSTEM' => $row['system'],\n        'PLANET_PLANET' => $row['planet'],\n        'PLANET_TYPE' => $lang['sys_planet_type_sh'][$row['planet_type']],\n        'ALLY_NAME' => htmlentities($row['ally_name'], ENT_COMPAT, 'UTF-8'),\n        'ALLY_TAG' => htmlentities($row['ally_tag'], ENT_COMPAT, 'UTF-8'),\n      ));\n    }\n    elseif($type=='ally')\n    {\n      $template->assign_block_vars('search_result', array(\n        'ALLY_NAME' => htmlentities($row['ally_name'], ENT_COMPAT, 'UTF-8'),\n        'ALLY_TAG' => htmlentities($row['ally_tag'], ENT_COMPAT, 'UTF-8'),\n        'ALLY_RANK' => HelperString::numberFloorAndFormat($row['total_rank']),\n        'ALLY_RANK_RAW' => floatval($row['total_rank']),\n        'ALLY_MEMBERS' => HelperString::numberFloorAndFormat($row['ally_members']),\n      ));\n    }\n  }\n}\n\n$search_type = array(\n  'playername' => 'srch_player_name',\n//  'planetname' => 'srch_planet_name',\n  'ally' => 'sys_alliance',\n);\n\nforeach($search_type as $type_id => $type_lang)\n{\n  $template->assign_block_vars('type', array(\n    'ID' => $type_id,\n    'TEXT' => $lang[$type_lang],\n    'SELECTED' => $type_id == $type,\n  ));\n}\n\n$template->assign_vars(array(\n  'PAGE_HEADER' => $lang['Search'],\n  'PAGE_HINT' => $lang['srch_page_hint'],\n  'TEXT' => $searchtext,\n  'IS_ALLY' => $type == 'ally',\n  'STATS_HIDE_PM_LINK' => SN::$config->stats_hide_pm_link,\n));\n\nSnTemplate::display($template);\n"
  },
  {
    "path": "server_info.php",
    "content": "<?php\n\n$allow_anonymous = true;\ninclude('common.' . substr(strrchr(__FILE__, '.'), 1));\n\nlng_include('admin');\n\n$template = SnTemplate::gettemplate('server_info', true);\n\n$template->assign_vars(array(\n  'game_build_and_research' => SN::$config->BuildLabWhileRun,\n  'USER_VACATION_DISABLE' => SN::$config->user_vacation_disable,\n  'ALLOW_BUFFING' => SN::$config->allow_buffing,\n  'ALLY_HELP_WEAK' => SN::$config->ally_help_weak,\n  'FLEET_BASHING_ATTACKS' => SN::$config->fleet_bashing_attacks,\n  'fleet_bashing_interval' => sys_time_human(SN::$config->fleet_bashing_interval),\n  'fleet_bashing_scope' => sys_time_human(SN::$config->fleet_bashing_scope),\n  'fleet_bashing_war_delay' => sys_time_human(SN::$config->fleet_bashing_war_delay),\n  'EMPIRE_MERCENARY_TEMPORARY' => SN::$config->empire_mercenary_temporary,\n  'ALI_BONUS_MEMBERS' => !empty(SN::$gc->modules->getModule('ali_ally_player')) ? SN::$config->ali_bonus_members : 0,\n\n  'PLAYER_MAX_COLONIES' => SN::$config->player_max_colonies,\n\n  'GAME_MULTIACCOUNT_ENABLED' => SN::$config->game_multiaccount_enabled,\n\n  'GAME_SPEED' => get_game_speed(),\n  'GAME_SPEED_PLAIN' => get_game_speed(true),\n  'FLEET_SPEED' => Universe::flt_server_flight_speed_multiplier(),\n  'FLEET_SPEED_PLAIN' => Universe::flt_server_flight_speed_multiplier(true),\n  'RESOURCE_MULTIPLIER' => game_resource_multiplier(),\n  'RESOURCE_MULTIPLIER_PLAIN' => game_resource_multiplier(true),\n\n  'DB_PATCH_VERSION' => dbPatchGetCurrent(),\n));\n\nSnTemplate::display($template);\n"
  },
  {
    "path": "simulator.php",
    "content": "<?php\n\ninclude('common.' . substr(strrchr(__FILE__, '.'), 1));\n\nif (sys_get_param_int('BE_DEBUG') && !defined('BE_DEBUG')) {\n  define('BE_DEBUG', true);\n}\n\nrequire_once(\"includes/includes/ube_attack_calculate.php\");\nrequire_once('includes/includes/coe_simulator_helpers.php');\n\n$replay = $_GET['replay'] ? $_GET['replay'] : $_POST['replay'];\n$execute = intval($_GET['execute']);\n$sym_defender = $_POST['defender'] ? $_POST['defender'] : array();\n$sym_attacker = $_POST['attacker'] ? $_POST['attacker'] : array();\n\nif ($replay) {\n  $unpacked = sn_ube_simulator_decode_replay($replay);\n\n  $sym_defender = $unpacked['D'];\n  $sym_attacker = $unpacked['A'];\n} else {\n  $sym_defender = array(0 => $sym_defender);\n  $sym_attacker = array(1 => $sym_attacker);\n}\n\nif ($_POST['submit'] || $execute) {\n  $replay = sn_ube_simulator_encode_replay($sym_defender, 'D');\n  $replay .= sn_ube_simulator_encode_replay($sym_attacker, 'A');\n\n  $ubePrepare = new \\Ube\\Ube4_1\\Ube4_1Prepare();\n  $combat_data = $ubePrepare->sn_ube_simulator_fleet_converter($sym_attacker, $sym_defender);\n\n  $combat_data[UBE_OPTIONS][UBE_METHOD] = SN::$config->game_ube_method ? SN::$config->game_ube_method : 0;\n  $ubeCalc = new \\Ube\\Ube4_1\\Ube4_1Calc();\n  $ubeCalc->sn_ube_combat($combat_data);\n  // Это используется для тестов - отключено в стандартном режиме\n//  if(!sys_get_param_int('simulator') || sys_get_param_str('reload')) {\n//    sn_ube_report_save($combat_data);\n//  }\n\n  if (sys_get_param_str('reload')) {\n    $combat_data = sn_ube_report_load($combat_data[UBE_REPORT_CYPHER]);\n  }\n\n//debug($combat_data);\n  // Рендерим их в темплейт\n  sn_ube_report_generate($combat_data, $template_result);\n\n  $template_result['MICROTIME'] = $combat_data[UBE_TIME_SPENT];\n\n  $template = SnTemplate::gettemplate('ube_combat_report', true);\n  $template->assign_recursive($template_result);\n\n  $template->assign_vars(array(\n    'MENU'   => false,\n    'NAVBAR' => false,\n  ));\n\n  SnTemplate::display($template);\n} else {\n  $template = SnTemplate::gettemplate('simulator', true);\n  $techs_and_officers = array(TECH_WEAPON, TECH_SHIELD, TECH_ARMOR, MRC_ADMIRAL);\n\n  foreach ($techs_and_officers as $tech_id) {\n    if (!$sym_attacker[1][$tech_id]) {\n      $sym_attacker[1][$tech_id] = mrc_get_level($user, false, $tech_id);\n    }\n  }\n\n  $show_groups = array(\n    UNIT_TECHNOLOGIES => array(TECH_WEAPON, TECH_SHIELD, TECH_ARMOR),\n    UNIT_MERCENARIES  => array(MRC_ADMIRAL),\n    UNIT_SHIPS        => sn_get_groups('fleet'),\n    UNIT_RESOURCES    => sn_get_groups('resources_loot'),\n    UNIT_GOVERNORS    => array(MRC_FORTIFIER),\n    UNIT_DEFENCE      => sn_get_groups('defense_active'),\n  );\n  foreach ($show_groups as $unit_group_id => $unit_group) {\n    $template->assign_block_vars('simulator', array(\n      'GROUP' => $unit_group_id,\n      'NAME'  => $lang['tech'][$unit_group_id],\n    ));\n\n    foreach ($unit_group as $unit_id) {\n      $tab++;\n\n      $value = mrc_get_level($user, $planetrow, $unit_id);\n\n      $template->assign_block_vars('simulator', array(\n        'NUM'      => $tab < 9 ? \"0{$tab}\" : $tab,\n        'ID'       => $unit_id,\n        'GROUP'    => $unit_group_id,\n        'NAME'     => $lang['tech'][$unit_id],\n        'ATTACKER' => intval($sym_attacker[1][$unit_id]),\n        'DEFENDER' => intval($sym_defender[0][$unit_id]),\n        'VALUE'    => $value,\n      ));\n    }\n  }\n\n  $template->assign_vars(array(\n    'BE_DEBUG'       => BE_DEBUG,\n    'UNIT_DEFENCE'   => UNIT_DEFENCE,\n    'UNIT_GOVERNORS' => UNIT_GOVERNORS,\n  ));\n\n  $template->assign_vars(array(\n    'NAVBAR' => false,\n  ));\n  SnTemplate::display($template, $lang['coe_combatSimulator']);\n}\n"
  },
  {
    "path": "skins/EpicBlue/skin.css",
    "content": "/*\n                                 █████████████████████████████████████████████████████████\n                                 ███▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒███▒\n                                 ███▒                                                  ███▒\n                                 ███▒                SuperNova: Epic Blue              ███▒\n                                 ███▒                                                  ███▒\n                                 ███▒                      V 2.0                       ███▒\n                                 ███▒                                                  ███▒\n                                 ███▒    Skin for \"SuperNova.ws\" http://supernova.ws   ███▒\n                                 ███▒                                                  ███▒\n                                 ███▒                                                  ███▒\n                                 ███▒     (c) 2009-2015 Gorlum smeagorl@gmail.com      ███▒\n                                 ███▒                                                  ███▒\n                                 █████████████████████████████████████████████████████████▒\n                                  ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒\n\nColor reference\n~~~~~~~~~~~~~~~\n\nJQ means \"jQuery Theme Roller\" http://jqueryui.com/themeroller/\nYou can look at actual JQ link for theme in /design/css/jquery-ui.css\n\n#13233E\n  TD background\n  JQ Header-Toolbar text & icon\n#222D42\n  Button default color\n  JQ Clickable: Default State\n#233452\n  Between TH and TD when you need mid-header in TD. Example - News header background and Survey header background\n#344566\n  TH background\n  JQ Content background\n#415680\n  Highlighted background\n  Active background\n  JQ Hover state background\n  JQ Active State background\n  JQ Highlight Background\n#dfeafd\n  JQ Header/Toolbar background\n#E6EBFB\n  Input element color\n\n*/\n\n.bg_title {\n  background-color: #222D42;\n}\n\n.bg_header, tr.bg_header > th, tr.bg_header > td, .header_bg {\n  background-color: #13233E;\n}\n\n.bg_subheader, tr > th.bg_subheader, tr > td.bg_subheader {\n  background-color: #233452;\n}\n.bg_cell {\n  background-color: #344566;\n}\n\nbody {\n  color: #E6EBFB;\n  background-color: #132544;\n  scrollbar-arrow-color: #E6EBFB;\n  scrollbar-base-color: #344566;\n  scrollbar-track-color: #344566;\n  scrollbar-face-color: #344566;\n  scrollbar-highlight-color: #344566;\n  scrollbar-3dlight-color: #465673;\n  scrollbar-darkshadow-color: #344566;\n  scrollbar-shadow-color: #465673;\n}\n\ninput, select, textarea, div.z {\n color: #E6EBFB;\n}\n\nth, td, input, select, textarea {\n  border-color: #415680;\n  background-color: #344566;\n}\n\ntd.c, th.c_l, th.c_r, th.c_c, th.c_j, tr.c_l > th, tr.c_r > th, tr.c_c > th, tr.c_j > th,\ntable.c_l th, table.c_c th, table.c_j th, table.c_r th\n{\n  background-image: none;\n  background-color: #13233E;\n}\n\ntd.l {\n  background-image: url(\"/skins/EpicBlue/img/bg2.gif\");\n}\n\na:not(.nohover):hover {\n  background-color: #415680;\n  text-decoration: none;\n}\n\n.style div a {\n/*  color                : #f2f2f2;*/\n}\n\n.style div a:link {\n/*  color                : #f2f2f2;*/\n}\n\n.style div a:visited {\n/*  color                : #f2f2f2;*/\n}\n\n.style div a:hover {\n/*  background-color     : #415680;\n  text-decoration      : none;*/\n}\n\n#left_menu th {\n  background-color: #212c42;\n}\n\n#menu_planet_overview, #menu_empire_overview, #menu_empire_mercenaries, #menu_info_research, #menu_empire_techtree {\n  color: #999933;\n}\n\n#menu_admin, #menu_news {\n  color: lime;\n}\n\n#menu_premium {\n  color: gold;\n}\n\n#menu_affiliates, #menu_planet_fleets, #menu_empire_fleets {\n  color: #cc7567;\n}\n\n#menu_rules {\n  color: orange;\n}\n\n#menu_faq, #menu_batch_operations {\n  color: yellow;\n}\n\n#menu_planet_structures {\n  color: #669933;\n}\n\n#menu_planet_shipyard, #menu_planet_defense {\n  color: #79c9ca;\n}\n#menu_empire_emperor, #menu_dark_matter {\n  color: #ca3aca;\n}\n\n#menu_empire_universe\n{\n  color: #999966;\n}\n\n#menu_ally_chat, #menu_comm_chat\n{\n  color: #996699;\n}\n\n#menu_comm_forum {\n  color: #0066CC;\n}\n\n#menu_utils_simulator {\n  color: #FF9966;\n}\n\n#menu_utils_search {\n  color: #887766;\n}\n\n#menu_options {\n  color: #CCCC66;\n}\n\n#menu_logout /* .lm_logout */\n{\n  color: #993333;\n}\n\n.link:hover {\n  background-color     : #415680;\n}\n\n/* Setting popup table background color */\ndiv .ui-dialog-content table {\n  background: #344055;\n}\n\n/* Icon file */\n.icons {\n  background-image: url(\"/skins/EpicBlue/images/icons.png\") !important;\n}\n\ndiv.ui-slider-range {\n  background: #f6a828 !important;\n}\n\n/* Unit preview box on build pages */\n.unit_preview {\n  border-color: #344566;\n}\n\n.unit_border_selected {\n  border-color: #6B90D3;  /* Reserved for unit_preview border color */\n}\n\ndiv#tab_container > div {\n  background-color: #222D42;\n}\n\ndiv.market_services {\n  background-color: transparent;\n  padding: 0;\n}\n\n.market_services div:first-child {\n  background-color: #344566; /* #344566 */\n  padding: 0.5em;\n  margin-bottom: 0;\n}\n\n.ui-widget-header {\n  background-color: #222D42; /* #344566 */\n  border: none;\n  background-image: none;\n}\n\n/*.ui-tabs-active.ui-state-active {*/\n/*  background-color: #222D42; !* #344566 *!*/\n/*}*/\n\n.ui-tabs .ui-tabs-panel {\n  background-color: #222D42;\n}\n\n/*\nBreak points:\n< 480 - slow mobile devices\n  No background at all\n>= 480 - still slow mobile devices or portrait orientation\n  Depends on size. 40kb - OK, 100kb - no background\n>= 760 - Desktops and laptops medium-res\n  800px background\n>= 950 - Desktops and laptops medium-res, includes 960, 1024 width\n  1024 background\n>= 1224 - Desktops and laptops normal res\n  1280 background\n>= 1550 - Desktops and laptops above normal res. I.e. 1600x\n  1600 background\n>= 1824 - Desktops and laptops high res\n  1920 background\n*/\n\n@media only screen and (min-width: 480px) {\n  body {\n    background-image: url(\"/skins/EpicBlue/img/background_0800.jpg\");\n  }\n}\n\n/* Desktops and laptops medium-res */\n/*@media only screen and (min-width: 760px) {*/\n/*body {*/\n/*background-image: url(img/background_0800.jpg);*/\n/*}*/\n/*}*/\n\n/* Desktops and laptops ----------- */\n@media only screen and (min-width: 950px) {\n  body {\n    background-image: url(\"/skins/EpicBlue/img/background_1024.jpg\");\n  }\n}\n\n@media only screen and (min-width: 1224px) {\n  body {\n    background-image: url(\"/skins/EpicBlue/img/background_1280.jpg\");\n  }\n}\n\n/* Large screens ----------- */\n@media only screen and (min-width: 1550px) {\n  body {\n    background-image: url(\"/skins/EpicBlue/img/background_1600.jpg\");\n  }\n}\n\n@media only screen and (min-width: 1824px) {\n  body {\n    background-image: url(\"/skins/EpicBlue/img/background_1920.jpg\");\n  }\n}\n\n/* Navbar button images ***********************************************************************************************/\n.sprite_navbar_buttons{background-image: url('/skins/EpicBlue/images/sprite_navbar_buttons.png');display: inline-block;}\n.navbar_research_button_wide{background-position: -64px -192px;width: 128px;height: 64px;zoom:calc(77.438/64);}\n.navbar_research_button{background-position: -0px -192px;width: 64px;height: 64px;zoom:calc(77.438/64);}\n.navbar_hangar_button{background-position: -128px -0px;width: 128px;height: 128px;zoom:calc(77.438/128);}\n.navbar_expedition_button{background-position: -320px -0px;width: 64px;height: 64px;zoom:calc(77.438/64);}\n.navbar_fleet_own_button{background-position: -0px -128px;width: 64px;height: 64px;zoom:calc(77.438/64);}\n.navbar_quest_button{background-position: -192px -128px;width: 64px;height: 64px;zoom:calc(77.438/64);}\n.navbar_mail_button{background-position: -64px -128px;width: 64px;height: 64px;zoom:calc(77.438/64);}\n.navbar_dark_matter_button{background-position: -256px -0px;width: 64px;height: 64px;zoom:calc(77.438/64);}\n.navbar_metamatter_button{background-position: -128px -128px;width: 64px;height: 64px;zoom:calc(77.438/64);}\n\n.navbar_defense_button{background-position: -0px -0px;width: 128px;height: 128px;zoom:calc(77.438/128);}\n"
  },
  {
    "path": "skins/EpicBlue/skin.ini",
    "content": "; Test Comment\n\n; Skin name to inherit pictures from\n; If there is no required image - image from parent skin would be used\n; _inherit = \"none\"\n\n; abort = \"pic/abort.gif\"\n; img/galaxie.gif = \"pic/abort.gif\"\n; array_test[index_test] = \"pic/test\"\n\n;<image name> = \"<path>\"\n;<image name> can be:\n;    - plain string with underscores, i.e.\n;        1\n;        navbar_research\n;    - array ????????\n\n;<path> can be:\n;    - relative, inside the skin:\n;        - \"gebaeude/1.gif\" will become \"skins/EpicBlue/gebaeude/1.gif\"\n;    - relative, outside the skin:\n;        - \"../../design/images/navbar_hangar.png\" will become \"skins/EpicBlue/../../design/images/navbar_hangar.png\" and points to image on site root image folder because this file is currently 2 levels deep\n;    - absolute to HTTP-root - starting with '/':\n;        - '/design/images/navbar_hangar.png' will become 'http://localhost/supernova/design/images/navbar_hangar.png' if current SN HTTP root is located in http://localhost/supernova/\n\n;In template images addresses with {I_<image name address>}\n;<image name address> can be:\n;    - <image name> from above:\n;        - {I_navbar_hangar} will resolve to path to image with name \"navbar_hangar\"\n;    - PTL template variable:\n;        - {I_[TOPNAV_CURRENT_PLANET_IMAGE]} will resolve to path with image name from \"TOPNAV_CURRENT_PLANET_IMAGE\" PTL variable\n;        - ??? PTL variable \"$xxx\" support ?\n;        - ??? PTL block name \"xxx.yyy\" support ?\n\n; Plug for no image - to override default system plug\n;_no_image = \"/design/images/_no_image.png\"\n\n; \"_webp\" suffix can be used as alternative image for web browsers with appropriate support\n\n; Some black magic to support JS-templating\n;UNIT_ID = \"gebaeude/[UNIT_ID].gif\"\nPLANET_IMAGE_SMALL = \"planeten/small/s_[PLANET_IMAGE].jpg\"\n\n; NavBar\nnavbar_research = \"/design/images/navbar_research_64x64.png\"\nnavbar_research_wide = \"/design/images/navbar_research_standard.png\"\nnavbar_hangar = \"/design/images/navbar_hangar.png\"\nnavbar_structures = \"/design/images/navbar_structures.png\"\nnavbar_defense = \"/design/images/navbar_defense.png\"\nnavbar_expedition = \"/design/images/navbar_expedition_standard.png\"\nnavbar_fleet_own = \"/design/images/navbar_fleet_own_standard.png\"\nnavbar_quest = \"/design/images/navbar_quest_standard.png\"\nnavbar_mail = \"/design/images/navbar_mail_standard.png\"\nnavbar_dark_matter = \"/design/images/navbar_dark_matter_standard.png\"\nnavbar_metamatter = \"/design/images/navbar_metamatter_standard.png\"\n\n; Icons\nr1 = \"/design/images/r1.png\"\nicon_help_16 = \"/design/images/icon_help_16x16.png\"\nicon_help_32 = \"/design/images/icon_help_32x32.png\"\nicon_help_48 = \"/design/images/icon_help_48x48.png\"\nicon_help_64 = \"/design/images/icon_help_64x64.png\"\nicon_ship_capacity_16 = \"/design/images/icon_ship_capacity_16x16.png\"\nicon_ship_speed_16 = \"/design/images/icon_ship_speed_16x16.png\"\nicon_ship_consumption_16 = \"/design/images/icon_ship_consumption_16x16.png\"\nicon_pin_16 = \"/design/images/icon_pin_16x16.png\"\nicon_pin_32 = \"/design/images/icon_pin_32x32.png\"\nicon_pin_64 = \"/design/images/icon_pin_64x64.png\"\nicon_note_pinned_16 = \"/design/images/icon_note_pinned_16x16.png\"\nicon_note_pinned_32 = \"/design/images/icon_note_pinned_32x32.png\"\nicon_note_pinned_64 = \"/design/images/icon_note_pinned_64x64.png\"\n\nicon_vacation = \"/design/images/icon_vacation.png\"\nicon_birthday = \"/design/images/birthday.png\"\nicon_buddy = \"img/b.gif\"\n\n; Units\n1 = \"gebaeude/1.gif\"\n2 = \"gebaeude/2.gif\"\n3 = \"gebaeude/3.gif\"\n4 = \"gebaeude/4.gif\"\n12 = \"gebaeude/12.gif\"\n14 = \"gebaeude/14.gif\"\n15 = \"gebaeude/15.gif\"\n21 = \"gebaeude/21.gif\"\n22 = \"gebaeude/22.gif\"\n23 = \"gebaeude/23.gif\"\n24 = \"gebaeude/24.gif\"\n31 = \"gebaeude/31.gif\"\n33 = \"gebaeude/33.gif\"\n34 = \"gebaeude/34.gif\"\n35 = \"gebaeude/35.gif\"\n41 = \"gebaeude/41.gif\"\n42 = \"gebaeude/42.gif\"\n43 = \"gebaeude/43.gif\"\n44 = \"gebaeude/44.gif\"\n\n106 = \"gebaeude/106.gif\"\n108 = \"gebaeude/108.gif\"\n109 = \"gebaeude/109.gif\"\n110 = \"gebaeude/110.gif\"\n111 = \"gebaeude/111.gif\"\n113 = \"gebaeude/113.gif\"\n114 = \"gebaeude/114.gif\"\n115 = \"gebaeude/115.gif\"\n117 = \"gebaeude/117.gif\"\n118 = \"gebaeude/118.gif\"\n119 = \"gebaeude/119.jpg\"\n120 = \"gebaeude/120.gif\"\n121 = \"gebaeude/121.gif\"\n122 = \"gebaeude/122.gif\"\n123 = \"gebaeude/123.gif\"\n125 = \"gebaeude/125.jpg\"\n150 = \"gebaeude/150.gif\"\n151 = \"gebaeude/151.gif\"\n199 = \"gebaeude/199.gif\"\n\n201 = \"gebaeude/201.gif\"\n202 = \"gebaeude/202.gif\"\n203 = \"gebaeude/203.gif\"\n204 = \"gebaeude/204.gif\"\n205 = \"gebaeude/205.gif\"\n206 = \"gebaeude/206.gif\"\n207 = \"gebaeude/207.gif\"\n208 = \"gebaeude/208.gif\"\n209 = \"gebaeude/209.gif\"\n210 = \"gebaeude/210.gif\"\n211 = \"gebaeude/211.gif\"\n212 = \"gebaeude/212.gif\"\n213 = \"gebaeude/213.gif\"\n214 = \"gebaeude/214.gif\"\n215 = \"gebaeude/215.gif\"\n216 = \"gebaeude/216.gif\"\n218 = \"gebaeude/218.gif\"\n219 = \"gebaeude/219.gif\"\n220 = \"gebaeude/220.gif\"\n221 = \"gebaeude/221.gif\"\n222 = \"gebaeude/222.gif\"\n223 = \"gebaeude/223.gif\"\n224 = \"gebaeude/224.gif\"\n225 = \"gebaeude/225.gif\"\n\n228 = \"gebaeude/228.jpg\"\n229 = \"gebaeude/229.jpg\"\n230 = \"gebaeude/230.jpg\"\n231 = \"gebaeude/231.jpg\"\n232 = \"gebaeude/232.jpg\"\n\n401 = \"gebaeude/401.gif\"\n402 = \"gebaeude/402.gif\"\n403 = \"gebaeude/403.gif\"\n404 = \"gebaeude/404.gif\"\n405 = \"gebaeude/405.gif\"\n406 = \"gebaeude/406.gif\"\n407 = \"gebaeude/407.gif\"\n408 = \"gebaeude/408.gif\"\n409 = \"gebaeude/409.gif\"\n\n501 = \"gebaeude/501.gif\"\n502 = \"gebaeude/502.gif\"\n503 = \"gebaeude/503.gif\"\n\n601 = \"gebaeude/601.jpg\"\n601_large = \"gebaeude/601_large.jpg\"\n602 = \"gebaeude/602.jpg\"\n602_large = \"gebaeude/602_large.jpg\"\n603 = \"gebaeude/603.jpg\"\n604 = \"gebaeude/604.jpg\"\n605 = \"gebaeude/605.jpg\"\n605_large = \"gebaeude/605_large.jpg\"\n606 = \"gebaeude/606.jpg\"\n606_large = \"gebaeude/606_large.jpg\"\n607 = \"gebaeude/607.jpg\"\n607_large = \"gebaeude/607_large.jpg\"\n608 = \"gebaeude/608.jpg\"\n608_large = \"gebaeude/608_large.jpg\"\n609 = \"gebaeude/609.jpg\"\n610 = \"gebaeude/610.jpg\"\n610_large = \"gebaeude/610_large.jpg\"\n611 = \"gebaeude/611.jpg\"\n611_large = \"gebaeude/611_large.jpg\"\n612 = \"gebaeude/612.jpg\"\n613 = \"gebaeude/613.jpg\"\n613_large = \"gebaeude/613_large.jpg\"\n614 = \"gebaeude/614.jpg\"\n615 = \"gebaeude/615.jpg\"\n\n901 = \"gebaeude/901.jpg\"\n902 = \"gebaeude/902.jpg\"\n903 = \"gebaeude/903.jpg\"\n904 = \"gebaeude/904.jpg\"\n905 = \"gebaeude/905.jpg\"\n950 = \"gebaeude/950.png\"\n\n1001 = \"gebaeude/1001.jpg\"\n1002 = \"gebaeude/1002.jpg\"\n1003 = \"gebaeude/1003.jpg\"\n1004 = \"gebaeude/1004.jpg\"\n1005 = \"gebaeude/1005.jpg\"\n1006 = \"gebaeude/1006.png\"\n1008 = \"gebaeude/1008.jpg\"\n1009 = \"gebaeude/1008.jpg\"\n1010 = \"gebaeude/1008.jpg\"\n1101 = \"gebaeude/1101.jpg\"\n1102 = \"gebaeude/1102.jpg\"\n1103 = \"gebaeude/1103.jpg\"\n1104 = \"gebaeude/1104.jpg\"\n1105 = \"gebaeude/1105.jpg\"\n1106 = \"gebaeude/1106.jpg\"\n1107 = \"gebaeude/1107.png\"\n\n1601 = \"gebaeude/1601.png\"\n\n790001 = \"gebaeude/790001.jpg\"\n\n; Menu\nmenu_affiliates = \"icons/menu_affiliates.png\"\nmenu_ally = \"icons/menu_ally.png\"\nmenu_ally_chat = \"icons/menu_ally_chat.png\"\nmenu_ally_overview = \"icons/menu_ally_overview.png\"\nmenu_captain = \"icons/menu_captain.png\"\nmenu_comm_chat = \"icons/menu_comm_chat.png\"\nmenu_comm_forum = \"icons/menu_comm_forum.png\"\nmenu_comm_messages = \"icons/menu_comm_messages.png\"\nmenu_documentation = \"icons/menu_documentation.png\"\nmenu_empire_artifacts = \"icons/menu_empire_artifacts.png\"\nmenu_empire_emperor = \"icons/menu_empire_emperor.png\"\nmenu_empire_fleets = \"icons/menu_empire_fleets.png\"\nmenu_empire_market = \"icons/menu_empire_market.png\"\nmenu_empire_mercenaries = \"icons/menu_empire_mercenaries.png\"\nmenu_empire_overview = \"icons/menu_empire_overview.png\"\nmenu_empire_quests = \"icons/menu_empire_quests.png\"\nmenu_empire_schematics = \"icons/menu_empire_schematics.png\"\nmenu_empire_techtree = \"icons/menu_empire_techtree.png\"\nmenu_empire_universe = \"icons/menu_empire_universe.png\"\nmenu_faq = \"icons/menu_faq.png\"\nmenu_info_admins = \"icons/menu_info_admins.png\"\nmenu_info_ban = \"icons/menu_info_ban.png\"\nmenu_info_records = \"icons/menu_info_records.png\"\nmenu_info_research = \"icons/menu_info_research.png\"\nmenu_info_server = \"icons/menu_info_server.png\"\nmenu_info_stats = \"icons/menu_info_stats.png\"\nmenu_news = \"icons/menu_news.png\"\nmenu_planet_defense = \"icons/menu_planet_defense.png\"\nmenu_planet_fleets = \"icons/menu_planet_fleets.png\"\nmenu_planet_overview = \"icons/menu_planet_overview.png\"\nmenu_planet_resources = \"icons/menu_planet_resources.png\"\nmenu_planet_shipyard = \"icons/menu_planet_shipyard.png\"\nmenu_planet_structures = \"icons/menu_planet_structures.png\"\nmenu_premium = \"icons/menu_premium.png\"\nmenu_races = \"icons/menu_races.png\"\nmenu_radio = \"icons/menu_radio.png\"\nmenu_rules = \"icons/menu_rules.png\"\nmenu_shop = \"icons/menu_shop.png\"\nmenu_utils_buddies = \"icons/menu_utils_buddies.png\"\nmenu_utils_notes = \"icons/menu_utils_notes.png\"\nmenu_utils_reports = \"icons/menu_utils_reports.png\"\nmenu_utils_search = \"icons/menu_utils_search.png\"\nmenu_utils_shortcuts = \"icons/menu_utils_shortcuts.png\"\nmenu_utils_simulator = \"icons/menu_utils_simulator.png\"\nborder = \"images/border.png\"\nborder_small = \"images/border_small.png\"\ndeuterium = \"images/deuterium.gif\"\ndm_klein_2 = \"images/dm_klein_2.jpg\"\nenergie = \"images/energie.gif\"\ngender_female = \"images/gender_female.png\"\ngender_male = \"images/gender_male.png\"\ngender_unknown = \"images/gender_unknown.png\"\nicons = \"images/icons.png\"\nkristall = \"images/kristall.gif\"\nmessage = \"images/message.gif\"\nmetall = \"images/metall.gif\"\ntable_th_background = \"images/table_th_background.png\"\nbg_flat_0_aaaaaa_40x100 = \"images/ui-bg_flat_0_aaaaaa_40x100.png\"\nbg_flat_55_13233f_40x100 = \"images/ui-bg_flat_55_13233f_40x100.png\"\nbg_flat_75_132544_40x100 = \"images/ui-bg_flat_75_132544_40x100.png\"\nhard_65_344566_1x100 = \"images/ui-bg_highlight-hard_65_344566_1x100.png\"\nhard_75_344566_1x100 = \"images/ui-bg_highlight-hard_75_344566_1x100.png\"\nsoft_75_344566_1x100 = \"images/ui-bg_highlight-soft_75_344566_1x100.png\"\nsoft_95_13233e_1x100 = \"images/ui-bg_inset-soft_95_13233e_1x100.png\"\nicons_2e83ff_256x240 = \"images/ui-icons_2e83ff_256x240.png\"\nicons_cd0a0a_256x240 = \"images/ui-icons_cd0a0a_256x240.png\"\nicons_e6ebfb_256x240 = \"images/ui-icons_e6ebfb_256x240.png\"\nb = \"img/b.gif\"\nbackground_0800 = \"img/background_0800.jpg\"\nbackground_1024 = \"img/background_1024.jpg\"\nbackground_1280 = \"img/background_1280.jpg\"\nbackground_1600 = \"img/background_1600.jpg\"\nbackground_1920 = \"img/background_1920.jpg\"\nbackground_medium = \"img/background_medium.jpg\"\nbackground_original = \"img/background_original.jpg\"\nbg1 = \"img/bg1.gif\"\nbg2 = \"img/bg2.gif\"\nblank = \"img/blank.gif\"\nblitz_background = \"img/blitz_background.jpg\"\nblitz_background_medium = \"img/blitz_background_medium.jpg\"\n;e = \"img/e.jpg\"\n;galaxie = \"img/galaxie.gif\"\n;p = \"img/p.png\"\n;p1 = \"img/p1.jpg\"\n;p1z1 = \"img/p1z1.png\"\n;p2 = \"img/p2.jpg\"\n;p2z2 = \"img/p2z2.png\"\n;p3 = \"img/p3.jpg\"\n;p3z2 = \"img/p3z2.png\"\n;p4 = \"img/p4.jpg\"\n;p5 = \"img/p5.jpg\"\n;p6 = \"img/p6.jpg\"\n;p7 = \"img/p7.jpg\"\n;p8 = \"img/p8.jpg\"\n;p9 = \"img/p9.jpg\"\ns = \"img/s.gif\"\n;saturn2 = \"img/saturn2.jpg\"\n;srgs = \"img/srgs.jpg\"\n;starz2 = \"img/starz2.png\"\nabort = \"images/abort.gif\"\nblack_moon = \"planeten/black_moon.jpg\"\nblack_planet = \"planeten/black_planet.jpg\"\ndebris = \"planeten/debris.gif\"\ndschjungelplanet01 = \"planeten/dschjungelplanet01.jpg\"\ndschjungelplanet02 = \"planeten/dschjungelplanet02.jpg\"\ndschjungelplanet03 = \"planeten/dschjungelplanet03.jpg\"\ndschjungelplanet04 = \"planeten/dschjungelplanet04.jpg\"\ndschjungelplanet05 = \"planeten/dschjungelplanet05.jpg\"\ndschjungelplanet06 = \"planeten/dschjungelplanet06.jpg\"\ndschjungelplanet07 = \"planeten/dschjungelplanet07.jpg\"\ndschjungelplanet08 = \"planeten/dschjungelplanet08.jpg\"\ndschjungelplanet09 = \"planeten/dschjungelplanet09.jpg\"\ndschjungelplanet10 = \"planeten/dschjungelplanet10.jpg\"\neisplanet01 = \"planeten/eisplanet01.jpg\"\neisplanet02 = \"planeten/eisplanet02.jpg\"\neisplanet03 = \"planeten/eisplanet03.jpg\"\neisplanet04 = \"planeten/eisplanet04.jpg\"\neisplanet05 = \"planeten/eisplanet05.jpg\"\neisplanet06 = \"planeten/eisplanet06.jpg\"\neisplanet07 = \"planeten/eisplanet07.jpg\"\neisplanet08 = \"planeten/eisplanet08.jpg\"\neisplanet09 = \"planeten/eisplanet09.jpg\"\neisplanet10 = \"planeten/eisplanet10.jpg\"\ngasplanet01 = \"planeten/gasplanet01.jpg\"\ngasplanet02 = \"planeten/gasplanet02.jpg\"\ngasplanet03 = \"planeten/gasplanet03.jpg\"\ngasplanet04 = \"planeten/gasplanet04.jpg\"\ngasplanet05 = \"planeten/gasplanet05.jpg\"\ngasplanet06 = \"planeten/gasplanet06.jpg\"\ngasplanet07 = \"planeten/gasplanet07.jpg\"\ngasplanet08 = \"planeten/gasplanet08.jpg\"\nmond = \"planeten/mond.jpg\"\nnormaltempplanet01 = \"planeten/normaltempplanet01.jpg\"\nnormaltempplanet02 = \"planeten/normaltempplanet02.jpg\"\nnormaltempplanet03 = \"planeten/normaltempplanet03.jpg\"\nnormaltempplanet04 = \"planeten/normaltempplanet04.jpg\"\nnormaltempplanet05 = \"planeten/normaltempplanet05.jpg\"\nnormaltempplanet06 = \"planeten/normaltempplanet06.jpg\"\nnormaltempplanet07 = \"planeten/normaltempplanet07.jpg\"\ntrockenplanet01 = \"planeten/trockenplanet01.jpg\"\ntrockenplanet02 = \"planeten/trockenplanet02.jpg\"\ntrockenplanet03 = \"planeten/trockenplanet03.jpg\"\ntrockenplanet04 = \"planeten/trockenplanet04.jpg\"\ntrockenplanet05 = \"planeten/trockenplanet05.jpg\"\ntrockenplanet06 = \"planeten/trockenplanet06.jpg\"\ntrockenplanet07 = \"planeten/trockenplanet07.jpg\"\ntrockenplanet08 = \"planeten/trockenplanet08.jpg\"\ntrockenplanet09 = \"planeten/trockenplanet09.jpg\"\ntrockenplanet10 = \"planeten/trockenplanet10.jpg\"\nwasserplanet01 = \"planeten/wasserplanet01.jpg\"\nwasserplanet02 = \"planeten/wasserplanet02.jpg\"\nwasserplanet03 = \"planeten/wasserplanet03.jpg\"\nwasserplanet04 = \"planeten/wasserplanet04.jpg\"\nwasserplanet05 = \"planeten/wasserplanet05.jpg\"\nwasserplanet06 = \"planeten/wasserplanet06.jpg\"\nwasserplanet07 = \"planeten/wasserplanet07.jpg\"\nwasserplanet08 = \"planeten/wasserplanet08.jpg\"\nwasserplanet09 = \"planeten/wasserplanet09.jpg\"\nwuestenplanet01 = \"planeten/wuestenplanet01.jpg\"\nwuestenplanet02 = \"planeten/wuestenplanet02.jpg\"\nwuestenplanet03 = \"planeten/wuestenplanet03.jpg\"\nwuestenplanet04 = \"planeten/wuestenplanet04.jpg\"\ns_black_moon = \"planeten/small/s_black_moon.jpg\"\ns_black_planet = \"planeten/small/s_black_planet.jpg\"\ns_debris = \"planeten/small/s_debris.jpg\"\ns_dschjungelplanet01 = \"planeten/small/s_dschjungelplanet01.jpg\"\ns_dschjungelplanet02 = \"planeten/small/s_dschjungelplanet02.jpg\"\ns_dschjungelplanet03 = \"planeten/small/s_dschjungelplanet03.jpg\"\ns_dschjungelplanet04 = \"planeten/small/s_dschjungelplanet04.jpg\"\ns_dschjungelplanet05 = \"planeten/small/s_dschjungelplanet05.jpg\"\ns_dschjungelplanet06 = \"planeten/small/s_dschjungelplanet06.jpg\"\ns_dschjungelplanet07 = \"planeten/small/s_dschjungelplanet07.jpg\"\ns_dschjungelplanet08 = \"planeten/small/s_dschjungelplanet08.jpg\"\ns_dschjungelplanet09 = \"planeten/small/s_dschjungelplanet09.jpg\"\ns_dschjungelplanet10 = \"planeten/small/s_dschjungelplanet10.jpg\"\ns_eisplanet01 = \"planeten/small/s_eisplanet01.jpg\"\ns_eisplanet02 = \"planeten/small/s_eisplanet02.jpg\"\ns_eisplanet03 = \"planeten/small/s_eisplanet03.jpg\"\ns_eisplanet04 = \"planeten/small/s_eisplanet04.jpg\"\ns_eisplanet05 = \"planeten/small/s_eisplanet05.jpg\"\ns_eisplanet06 = \"planeten/small/s_eisplanet06.jpg\"\ns_eisplanet07 = \"planeten/small/s_eisplanet07.jpg\"\ns_eisplanet08 = \"planeten/small/s_eisplanet08.jpg\"\ns_eisplanet09 = \"planeten/small/s_eisplanet09.jpg\"\ns_eisplanet10 = \"planeten/small/s_eisplanet10.jpg\"\ns_gasplanet01 = \"planeten/small/s_gasplanet01.jpg\"\ns_gasplanet02 = \"planeten/small/s_gasplanet02.jpg\"\ns_gasplanet03 = \"planeten/small/s_gasplanet03.jpg\"\ns_gasplanet04 = \"planeten/small/s_gasplanet04.jpg\"\ns_gasplanet05 = \"planeten/small/s_gasplanet05.jpg\"\ns_gasplanet06 = \"planeten/small/s_gasplanet06.jpg\"\ns_gasplanet07 = \"planeten/small/s_gasplanet07.jpg\"\ns_gasplanet08 = \"planeten/small/s_gasplanet08.jpg\"\ns_mond = \"planeten/small/s_mond.jpg\"\ns_normaltempplanet01 = \"planeten/small/s_normaltempplanet01.jpg\"\ns_normaltempplanet02 = \"planeten/small/s_normaltempplanet02.jpg\"\ns_normaltempplanet03 = \"planeten/small/s_normaltempplanet03.jpg\"\ns_normaltempplanet04 = \"planeten/small/s_normaltempplanet04.jpg\"\ns_normaltempplanet05 = \"planeten/small/s_normaltempplanet05.jpg\"\ns_normaltempplanet06 = \"planeten/small/s_normaltempplanet06.jpg\"\ns_normaltempplanet07 = \"planeten/small/s_normaltempplanet07.jpg\"\ns_trockenplanet01 = \"planeten/small/s_trockenplanet01.jpg\"\ns_trockenplanet02 = \"planeten/small/s_trockenplanet02.jpg\"\ns_trockenplanet03 = \"planeten/small/s_trockenplanet03.jpg\"\ns_trockenplanet04 = \"planeten/small/s_trockenplanet04.jpg\"\ns_trockenplanet05 = \"planeten/small/s_trockenplanet05.jpg\"\ns_trockenplanet06 = \"planeten/small/s_trockenplanet06.jpg\"\ns_trockenplanet07 = \"planeten/small/s_trockenplanet07.jpg\"\ns_trockenplanet08 = \"planeten/small/s_trockenplanet08.jpg\"\ns_trockenplanet09 = \"planeten/small/s_trockenplanet09.jpg\"\ns_trockenplanet10 = \"planeten/small/s_trockenplanet10.jpg\"\ns_wasserplanet01 = \"planeten/small/s_wasserplanet01.jpg\"\ns_wasserplanet02 = \"planeten/small/s_wasserplanet02.jpg\"\ns_wasserplanet03 = \"planeten/small/s_wasserplanet03.jpg\"\ns_wasserplanet04 = \"planeten/small/s_wasserplanet04.jpg\"\ns_wasserplanet05 = \"planeten/small/s_wasserplanet05.jpg\"\ns_wasserplanet06 = \"planeten/small/s_wasserplanet06.jpg\"\ns_wasserplanet07 = \"planeten/small/s_wasserplanet07.jpg\"\ns_wasserplanet08 = \"planeten/small/s_wasserplanet08.jpg\"\ns_wasserplanet09 = \"planeten/small/s_wasserplanet09.jpg\"\n; s_[PLANET_IMAGE] = \"planeten/small/s_[PLANET_IMAGE].jpg\"\nl_wasserplanet01 = \"planeten/large/wasserplanet01.jpg\"\nl_wasserplanet02 = \"planeten/large/wasserplanet02.jpg\"\nl_wasserplanet03 = \"planeten/large/wasserplanet03.jpg\"\nl_wasserplanet04 = \"planeten/large/wasserplanet04.jpg\"\nl_wasserplanet05 = \"planeten/large/wasserplanet05.jpg\"\nl_wasserplanet06 = \"planeten/large/wasserplanet06.jpg\"\nl_wasserplanet07 = \"planeten/large/wasserplanet07.jpg\"\nl_wasserplanet08 = \"planeten/large/wasserplanet08.jpg\"\nl_wasserplanet09 = \"planeten/large/wasserplanet09.jpg\"\n\nobject_in_space = \"planeten/object_in_space.jpg\"\ns_object_in_space = \"planeten/small/s_object_in_space.jpg\"\nl_object_in_space = \"planeten/large/object_in_space.png\"\n\nmenu_info_best_battles = \"icons/menu_info_best_battles.png\"\n"
  },
  {
    "path": "skins/EpicBlue/tmpl.ini",
    "content": "OpenGame"
  },
  {
    "path": "skins/index.html",
    "content": ""
  },
  {
    "path": "skins/supernova-ivash/skin.css",
    "content": "/*\n                                 █████████████████████████████████████████████████████████\n                                 ███▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒███▒\n                                 ███▒                                                  ███▒\n                                 ███▒                SuperNova: SuperNova              ███▒\n                                 ███▒                                                  ███▒\n                                 ███▒                      V 2.0                       ███▒\n                                 ███▒                                                  ███▒\n                                 ███▒    Skin for \"SuperNova.ws\" ogame.supernova.ws    ███▒\n                                 ███▒                                                  ███▒\n                                 ███▒                                                  ███▒\n                                 ███▒     The Emperor with 08.08.2009 22:44:47 Ivash   ███▒\n                                 ███▒            Alliance: NewSTAR                     ███▒\n                                 ███▒                                                  ███▒\n                                 █████████████████████████████████████████████████████████▒\n                                  ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒\n\nColor reference\n~~~~~~~~~~~~~~~\n\nJQ means \"jQuery Theme Roller\" http://jqueryui.com/themeroller/\nYou can look at actual JQ link for theme in /design/css/jquery-ui.css\n\n#201410 - #13233E\n  TH background\n  JQ Header-Toolbar text & icon\n#221C1A - #233452\n  Between TH and TD when you need mid-header in TD. Example - News header background and Survey header background\n#242424 - #344566\n  TD background\n  JQ Content background\n#705955 - #415680\n  Highlighted background\n  Active background\n  JQ Hover state background\n  JQ Active State background\n  JQ Highlight Background\n- #222D42\n  Button default color\n  JQ Clickable: Default State\n#786560 - #dfeafd\n  JQ Header/Toolbar background\n#D58000 - #E6EBFB\n  Input element color\n\n*/\n\n.ui-widget-content, .ui-widget-content a,\n.ui-state-hover, .ui-widget-content .ui-state-hover, .ui-widget-header .ui-state-hover, .ui-state-focus, .ui-widget-content .ui-state-focus, .ui-widget-header .ui-state-focus,\n.ui-state-active, .ui-widget-content .ui-state-active, .ui-widget-header .ui-state-active,\n.ui-state-default, .ui-widget-content .ui-state-default, .ui-widget-header .ui-state-default,\n.ui-state-default a, .ui-state-default a:link, .ui-state-default a:visited,\n.ui-state-hover a, .ui-state-hover a:hover, .ui-state-hover a:link, .ui-state-hover a:visited, .ui-state-focus a, .ui-state-focus a:hover, .ui-state-focus a:link, .ui-state-focus a:visited,\n.ui-state-active a, .ui-state-active a:link, .ui-state-active a:visited\n{\n  color: #e6ebfb;\n}\n\n\n.ui-widget-header, .ui-widget-header a\n{\n  color: #201410; /* #13233E;*/\n}\n\n.ui-state-highlight, .ui-widget-content .ui-state-highlight, .ui-widget-header .ui-state-highlight,\n.ui-state-highlight a, .ui-widget-content .ui-state-highlight a, .ui-widget-header .ui-state-highlight a\n{\n  color: #ecc504;\n}\n\n.ui-state-error, .ui-widget-content .ui-state-error, .ui-widget-header .ui-state-error,\n.ui-state-error a, .ui-widget-content .ui-state-error a, .ui-widget-header .ui-state-error a,\n.ui-state-error-text, .ui-widget-content .ui-state-error-text, .ui-widget-header .ui-state-error-text\n{\n  color: #ff0000;\n}\n\n\n\n\n\n.ui-state-default, .ui-widget-content .ui-state-default, .ui-widget-header .ui-state-default\n{\n  /*background-color: #222D42;*/\n  /*background-color: #363636;*/\n  background-image: none;\n  background-color: #483634;\n}\n\n.ui-state-hover, .ui-widget-content .ui-state-hover, .ui-widget-header .ui-state-hover,\n.ui-state-highlight, .ui-widget-content .ui-state-highlight, .ui-widget-header .ui-state-highlight,\n.ui-state-error, .ui-widget-content .ui-state-error, .ui-widget-header .ui-state-error\n{\n  /*background-color: #705955; /!* #415680; *!/*/\n  background-image: none;\n  background-color: #705955; /* #415680; */\n}\n\n.ui-state-focus, .ui-widget-content .ui-state-focus, .ui-widget-header .ui-state-focus,\n.ui-state-active, .ui-widget-content .ui-state-active, .ui-widget-header .ui-state-active\n{\n  /*background-color: #705955; /!* #415680; *!/*/\n  background-image: none;\n  background-color: #705955; /* #415680; */\n}\n\n.ui-widget-content {\n  background-image: none;\n  background-color: #242424; /* #344566; */\n}\n\n.ui-widget-header {\n  /*background-color: #ecc504; /!* #dfeafd;*!/*/\n  background-image: none;\n  background-color: #786560;\n}\n\n\n\n\n.ui-button.ui-state-active, /* Hack */\n.ui-state-hover, .ui-widget-content .ui-state-hover, .ui-widget-header .ui-state-hover, .ui-state-focus, .ui-widget-content .ui-state-focus, .ui-widget-header .ui-state-focus,\n.ui-state-active, .ui-widget-content .ui-state-active, .ui-widget-header .ui-state-active,\n.ui-state-highlight, .ui-widget-content .ui-state-highlight, .ui-widget-header .ui-state-highlight\n{\n  border-color: #483634; /* #415680; */\n}\n\n.ui-state-error, .ui-widget-content .ui-state-error, .ui-widget-header .ui-state-error\n{\n  border-color: #ff0000;\n}\n\n\n.ui-widget-content,\n.ui-widget-header,\n.ui-state-default, .ui-widget-content .ui-state-default, .ui-widget-header .ui-state-default\n{\n  border-color: #483634; /* #415680; */\n}\n\n.ui-button.ui-input-text {\n  background-image: none;\n  background-color: #786560;\n}\n\nbody {\n  background-color     : #80605D;\n  scrollbar-arrow-color: #80605D;\n  scrollbar-base-color: #363636;\n  scrollbar-track-color: #363636;\n  scrollbar-face-color: #363636;\n  scrollbar-highlight-color: #344566;\n  scrollbar-3dlight-color: #80605D;\n  scrollbar-darkshadow-color: #344566;\n  scrollbar-shadow-color: #80605D;\n}\n\ninput, select, textarea, div.z {\n color                : #D58000;\n}\n\n.cell, .th, .td, th, td, input, select, textarea {\n  border-color         : #483634;\n  background-color     : #242424;\n}\n\n.header, .th, .td, td.c, th.c_l, th.c_r, th.c_c, th.c_j, tr.c_l > th, tr.c_r > th, tr.c_c > th, tr.c_j > th,\ntable.c_l th, table.c_c th, table.c_j th, table.c_r th\n{\n  background-image     : url(\"/skins/supernova-ivash/img/bg1.gif\");\n}\n\ntd.l {\n  background-image     : url(\"/skins/supernova-ivash/img/bg2.gif\");\n}\n\na:hover {\n  color                : #80605D;\n  text-decoration      : underline;\n}\n\n.style div a {\n  color                : #00AA00;\n}\n\n.style div a:link {\n  color                : #f2f2f2;\n}\n\n.style div a:visited {\n  color                : #f2f2f2;\n}\n\n.style div a:hover {\n  /*background-color     : #415680;*/\n  background-color     : #705955;\n  text-decoration      : none;\n}\n\n#left_menu th {\n  background-image: url(\"/skins/supernova-ivash/img/bg1.gif\");\n}\n\n#left_menu th a {\n  color: inherit;\n}\n\n#left_menu td a {\n  color: #E6EBFB;\n}\n\n.link:hover {\n  background-color     : #483634;\n}\n\n/* Setting popup table background color */\ndiv .ui-dialog-content table {\n  background: #242424;\n}\n\n/* Icon file */\n.icons {\n  background-image: url(\"/skins/supernova-ivash/images/icons.png\") !important;\n}\n\ndiv.ui-slider-range {\n  background: #f6a828; !important;\n}\n\n/* Unit preview box on build pages */\n.unit_preview {\n  border-color: #483634;\n}\n\n.unit_border_selected {\n  border-color: #F9C749;  /* Reserved for unit_preview border color */\n}\n\n/* Flying fleet & message color coding */\n*.owncolony {\n  color: #C76838;\n}\n\n.mnl_buildlist, .mnl_buildlist * {\n  color: #00B7F3;\n}\n\n.button_pseudo {\n  border-color: #483634;\n}\n\n\n.button_pseudo:not(img):hover, .button_pseudo_pressed:not(img) {\n  background-color: #705955; /* rgba(65,86,128,1) */\n}\n\n#left_menu a:not(.lm_with_image) {\n  background-color: rgba(34, 28, 26, 0.5); /* #221C1A - #233452; */\n  border: 1px solid #483634;\n}\n#left_menu.MENU_NARROW_CELLS a:not(.lm_with_image) {\n  background-color: #221C1A; /* - #233452;*/\n}\n\n#left_menu td a:hover, #left_menu th a:hover, #left_menu.MENU_NARROW_CELLS td a:hover, #left_menu.MENU_NARROW_CELLS th a:hover {\n  background-color: #705955;\n}\n\n.news_header, .survey_confirm {\n  background-color: #221C1A; /*#233452;*/\n}\n.survey_header, .survey_voted {\n  background-color: #221C1A; /*#233452;*/\n}\n\n.survey_block {\n  border-color: #705955 /*#415680*/;\n}\n\nbody:not(.no_border_image) table:not(.markup):not(.noborder):not(.no_border_image):not(.border_image_small),\nbody:not(.no_border_image) div#tab_container,\nbody:not(.no_border_image) div.market_services,\nbody:not(.no_border_image) .border_image_large\n{\n  /*border: solid transparent;*/\n  /*border-radius: 0.5em;*/\n\n  /*border-width: 2.181818181818em 1em 2.272727272727em 2.272727272727em;*/\n\n  -moz-border-image:    url(\"/skins/supernova-ivash/images/border.png\") 24 11 25 25 round round;\n  -o-border-image:      url(\"/skins/supernova-ivash/images/border.png\") 24 11 25 25 round round;\n  -webkit-border-image: url(\"/skins/supernova-ivash/images/border.png\") 24 11 25 25 round round;\n  border-image:         url(\"/skins/supernova-ivash/images/border.png\") 24 11 25 25 round round;\n}\n\nbody:not(.no_border_image) .border_image_small, body:not(.no_border_image) .ui-dialog\n{\n  /*border: solid transparent;*/\n  /*border-radius: 0.5em;*/\n\n  /*border-width: 1em 1em 1em 1em;*/\n\n  -moz-border-image:    url(\"/skins/supernova-ivash/images/border_small.png\") 11 11 11 11 round round;\n  -o-border-image:      url(\"/skins/supernova-ivash/images/border_small.png\") 11 11 11 11 round round;\n  -webkit-border-image: url(\"/skins/supernova-ivash/images/border_small.png\") 11 11 11 11 round round;\n  border-image:         url(\"/skins/supernova-ivash/images/border_small.png\") 11 11 11 11 round round;\n}\n\ndiv#tab_container > div {\n  background-color: #242424;\n}\n\ndiv.market_services {\n  background-color: transparent;\n  padding: 0;\n}\n\n.market_services div:first-child {\n  background-color: #242424; /* #344566 */\n  padding: 0.5em;\n  margin-bottom: 0;\n}\n\n.ui-widget-header {\n  background-color: #242424; /* #344566 */\n  border: none;\n  background-image: none;\n}\n\n.ui-tabs-active.ui-state-active {\n  background-color: #242424; /* #344566 */\n}\n\n.ui-tabs .ui-tabs-panel {\n  background-color: #242424;\n}\n\n#tab_tech_tree .tech_container {\n  border-color: #483634;\n}\n\n.planet-manage-governor-hire-container {\n  border-color: #483634;\n}\n\n.button_pseudo:not(img) {\n  background-color: #483634;\n}\ninput[type=checkbox] {\n  background-image: none;\n  background-color: #483634;\n  color: #483634;\n}\ninput[type=checkbox]:hover:not(:checked) {\n  background-image: none;\n  background-color: #786560;\n  color: #786560;\n}\n.subheader, tr.subheader > td, tr.subheader > th {\n  background-color: #221C1A;\n}\n.planet_list_fields {\n  background-color: #242424;\n}\n.ov_struc_timer {\n  background-color: #242424;\n}\n\n/* Setting dialog content background - which is 'transparent' in jQuery-UI */\n.ui-dialog .ui-dialog-content {\n  background-image: none;\n  background-color: #705955;\n}\n\n\n/*\nBreak points:\n< 480 - slow mobile devices\n  No background at all\n>= 480 - still slow mobile devices or portrait orientation\n  Depends on size. 40kb - OK, 100kb - no background\n>= 760 - Desktops and laptops medium-res\n  800px background\n>= 950 - Desktops and laptops medium-res, includes 960, 1024 width\n  1024 background\n>= 1224 - Desktops and laptops normal res\n  1280 background\n>= 1550 - Desktops and laptops above normal res. I.e. 1600x\n  1600 background\n>= 1824 - Desktops and laptops high res\n  1920 background\n*/\n\n/*@media only screen and (min-width: 480px) {*/\n/*body {*/\n/*background-image: url(img/background_0800.jpg);*/\n/*}*/\n/*}*/\n\n/* Desktops and laptops medium-res */\n@media only screen and (min-width: 760px) {\n  body {\n    background-image: url(\"/skins/supernova-ivash/img/background_0800.jpg\");\n  }\n}\n\n/* Desktops and laptops ----------- */\n@media only screen and (min-width: 950px) {\n  body {\n    background-image: url(\"/skins/supernova-ivash/img/background_1024.jpg\");\n  }\n}\n\n@media only screen and (min-width: 1224px) {\n  body {\n    background-image: url(\"/skins/supernova-ivash/img/background_1280.jpg\");\n  }\n}\n\n/* Large screens ----------- */\n@media only screen and (min-width: 1550px) {\n  body {\n    background-image: url(\"/skins/supernova-ivash/img/background_1600.jpg\");\n  }\n}\n\n@media only screen and (min-width: 1824px) {\n  body {\n    background-image: url(\"/skins/supernova-ivash/img/background_1920.jpg\");\n  }\n}\n\n/* Navbar button images ***********************************************************************************************/\n.sprite_navbar_buttons{background-image: url('/skins/supernova-ivash/images/sprite_navbar_buttons_ivash.png');display: inline-block;}\n.navbar_hangar_button{background-position: -192px -192px;width: 64px;height: 64px;zoom:calc(77.438/64);}\n\n.navbar_research_button_wide{background-position: -64px -192px;width: 128px;height: 64px;zoom:calc(77.438/64);}\n.navbar_research_button{background-position: -0px -192px;width: 64px;height: 64px;zoom:calc(77.438/64);}\n.navbar_hangar_button{background-position: -128px -0px;width: 128px;height: 128px;zoom:calc(77.438/128);}\n.navbar_expedition_button{background-position: -320px -0px;width: 64px;height: 64px;zoom:calc(77.438/64);}\n.navbar_fleet_own_button{background-position: -0px -128px;width: 64px;height: 64px;zoom:calc(77.438/64);}\n.navbar_quest_button{background-position: -192px -128px;width: 64px;height: 64px;zoom:calc(77.438/64);}\n.navbar_mail_button{background-position: -64px -128px;width: 64px;height: 64px;zoom:calc(77.438/64);}\n.navbar_dark_matter_button{background-position: -256px -0px;width: 64px;height: 64px;zoom:calc(77.438/64);}\n.navbar_metamatter_button{background-position: -128px -128px;width: 64px;height: 64px;zoom:calc(77.438/64);}\n\n.navbar_defense_button{background-position: -0px -0px;width: 128px;height: 128px;zoom:calc(77.438/128);}\n"
  },
  {
    "path": "skins/supernova-ivash/skin.ini",
    "content": "; For details about configuration - see /skins/EpicBlue/skin.ini\n\n_inherit = \"EpicBlue\"\n\n; Plug for no image - to override inherited system plug\n;_no_image = \"/design/images/_no_image.png\"\n\n; Some black magic to support JS-templating\n; UNIT_ID = \"gebaeude/[UNIT_ID].gif\"\n;PLANET_IMAGE_SMALL = \"planeten/small/s_[PLANET_IMAGE].png\"\n\n; NavBar\nnavbar_research = \"navbar/navbar_research_64x64.png\"\nnavbar_research_wide = \"navbar/navbar_research_standard.png\"\nnavbar_hangar = \"navbar/navbar_hangar.png\"\nnavbar_expedition = \"navbar/navbar_expedition_standard.png\"\nnavbar_fleet_own = \"navbar/navbar_fleet_own_standard.png\"\nnavbar_quest = \"navbar/navbar_quest_standard.png\"\nnavbar_mail = \"navbar/navbar_mail_standard.png\"\nnavbar_dark_matter = \"navbar/navbar_dark_matter_standard.png\"\nnavbar_metamatter = \"navbar/navbar_metamatter_standard.png\"\n\n; Units\n1 = \"gebaeude/1.gif\"\n2 = \"gebaeude/2.gif\"\n3 = \"gebaeude/3.gif\"\n4 = \"gebaeude/4.gif\"\n12 = \"gebaeude/12.gif\"\n14 = \"gebaeude/14.jpg\"\n15 = \"gebaeude/15.gif\"\n21 = \"gebaeude/21.jpg\"\n22 = \"gebaeude/22.gif\"\n23 = \"gebaeude/23.gif\"\n24 = \"gebaeude/24.gif\"\n31 = \"gebaeude/31.gif\"\n33 = \"gebaeude/33.gif\"\n34 = \"gebaeude/34.gif\"\n35 = \"gebaeude/35.gif\"\n41 = \"gebaeude/41.gif\"\n42 = \"gebaeude/42.gif\"\n43 = \"gebaeude/43.gif\"\n44 = \"gebaeude/44.gif\"\n\n106 = \"gebaeude/106.gif\"\n108 = \"gebaeude/108.gif\"\n109 = \"gebaeude/109.gif\"\n110 = \"gebaeude/110.gif\"\n111 = \"gebaeude/111.gif\"\n113 = \"gebaeude/113.gif\"\n114 = \"gebaeude/114.gif\"\n115 = \"gebaeude/115.gif\"\n117 = \"gebaeude/117.gif\"\n118 = \"gebaeude/118.gif\"\n119 = \"gebaeude/119.jpg\"\n120 = \"gebaeude/120.gif\"\n121 = \"gebaeude/121.gif\"\n122 = \"gebaeude/122.gif\"\n123 = \"gebaeude/123.gif\"\n125 = \"gebaeude/125.jpg\"\n150 = \"gebaeude/150.gif\"\n151 = \"gebaeude/151.gif\"\n199 = \"gebaeude/199.gif\"\n\n201 = \"gebaeude/201.gif\"\n202 = \"gebaeude/202.gif\"\n203 = \"gebaeude/203.gif\"\n204 = \"gebaeude/204.gif\"\n205 = \"gebaeude/205.gif\"\n206 = \"gebaeude/206.gif\"\n207 = \"gebaeude/207.gif\"\n208 = \"gebaeude/208.gif\"\n209 = \"gebaeude/209.gif\"\n210 = \"gebaeude/210.gif\"\n211 = \"gebaeude/211.gif\"\n212 = \"gebaeude/212.gif\"\n213 = \"gebaeude/213.gif\"\n214 = \"gebaeude/214.gif\"\n215 = \"gebaeude/215.gif\"\n216 = \"gebaeude/216.gif\"\n218 = \"gebaeude/218.gif\"\n219 = \"gebaeude/219.gif\"\n220 = \"gebaeude/220.gif\"\n221 = \"gebaeude/221.gif\"\n222 = \"gebaeude/222.gif\"\n223 = \"gebaeude/223.gif\"\n224 = \"gebaeude/224.gif\"\n225 = \"gebaeude/225.gif\"\n\n228 = \"gebaeude/228.jpg\"\n229 = \"gebaeude/229.jpg\"\n230 = \"gebaeude/230.jpg\"\n231 = \"gebaeude/231.jpg\"\n232 = \"gebaeude/232.jpg\"\n\n401 = \"gebaeude/401.gif\"\n402 = \"gebaeude/402.gif\"\n403 = \"gebaeude/403.gif\"\n404 = \"gebaeude/404.gif\"\n405 = \"gebaeude/405.gif\"\n406 = \"gebaeude/406.gif\"\n407 = \"gebaeude/407.gif\"\n408 = \"gebaeude/408.gif\"\n409 = \"gebaeude/409.gif\"\n\n501 = \"gebaeude/501.gif\"\n502 = \"gebaeude/502.jpg\"\n503 = \"gebaeude/503.gif\"\n\n;601 = \"gebaeude/601.jpg\"\n;602 = \"gebaeude/602.jpg\"\n603 = \"gebaeude/603.jpg\"\n604 = \"gebaeude/604.jpg\"\n;605 = \"gebaeude/605.jpg\"\n;606 = \"gebaeude/606.jpg\"\n;607 = \"gebaeude/607.jpg\"\n;608 = \"gebaeude/608.jpg\"\n609 = \"gebaeude/609.jpg\"\n;610 = \"gebaeude/610.jpg\"\n;611 = \"gebaeude/611.jpg\"\n612 = \"gebaeude/612.jpg\"\n;613 = \"gebaeude/613.jpg\"\n614 = \"gebaeude/614.jpg\"\n615 = \"gebaeude/615.jpg\"\n\n901 = \"gebaeude/901.jpg\"\n902 = \"gebaeude/902.jpg\"\n903 = \"gebaeude/903.jpg\"\n904 = \"gebaeude/904.jpg\"\n905 = \"gebaeude/905.jpg\"\n950 = \"gebaeude/950.png\"\n\n1001 = \"gebaeude/1001.jpg\"\n1101 = \"gebaeude/1101.jpg\"\n1102 = \"gebaeude/1102.jpg\"\n1103 = \"gebaeude/1103.jpg\"\n1104 = \"gebaeude/1104.jpg\"\n1105 = \"gebaeude/1105.jpg\"\n1106 = \"gebaeude/1106.jpg\"\n\n790001 = \"gebaeude/790001.jpg\"\n\nhelp = \"gfx/info-help.jpg\"\nproduktion = \"gfx/ogame-produktion.jpg\"\nmenu = \"gfx/user-menu.jpg\"\nmenu_affiliates = \"icons/menu_affiliates.png\"\nmenu_ally = \"icons/menu_ally.png\"\nmenu_ally_chat = \"icons/menu_ally_chat.png\"\nmenu_ally_overview = \"icons/menu_ally_overview.png\"\nmenu_captain = \"icons/menu_captain.png\"\nmenu_comm_chat = \"icons/menu_comm_chat.png\"\nmenu_comm_forum = \"icons/menu_comm_forum.png\"\nmenu_comm_messages = \"icons/menu_comm_messages.png\"\nmenu_documentation = \"icons/menu_documentation.png\"\nmenu_empire_artifacts = \"icons/menu_empire_artifacts.png\"\nmenu_empire_emperor = \"icons/menu_empire_emperor.png\"\nmenu_empire_fleets = \"icons/menu_empire_fleets.png\"\nmenu_empire_market = \"icons/menu_empire_market.png\"\nmenu_empire_mercenaries = \"icons/menu_empire_mercenaries.png\"\nmenu_empire_overview = \"icons/menu_empire_overview.png\"\nmenu_empire_quests = \"icons/menu_empire_quests.png\"\nmenu_empire_schematics = \"icons/menu_empire_schematics.png\"\nmenu_empire_techtree = \"icons/menu_empire_techtree.png\"\nmenu_empire_universe = \"icons/menu_empire_universe.png\"\nmenu_faq = \"icons/menu_faq.png\"\nmenu_info_admins = \"icons/menu_info_admins.png\"\nmenu_info_ban = \"icons/menu_info_ban.png\"\nmenu_info_records = \"icons/menu_info_records.png\"\nmenu_info_research = \"icons/menu_info_research.png\"\nmenu_info_server = \"icons/menu_info_server.png\"\nmenu_info_stats = \"icons/menu_info_stats.png\"\nmenu_news = \"icons/menu_news.png\"\nmenu_planet_defense = \"icons/menu_planet_defense.png\"\nmenu_planet_fleets = \"icons/menu_planet_fleets.png\"\nmenu_planet_overview = \"icons/menu_planet_overview.png\"\nmenu_planet_resources = \"icons/menu_planet_resources.png\"\nmenu_planet_shipyard = \"icons/menu_planet_shipyard.png\"\nmenu_planet_structures = \"icons/menu_planet_structures.png\"\nmenu_premium = \"icons/menu_premium.png\"\nmenu_races = \"icons/menu_races.png\"\nmenu_radio = \"icons/menu_radio.png\"\nmenu_rules = \"icons/menu_rules.png\"\nmenu_shop = \"icons/menu_shop.png\"\nmenu_utils_buddies = \"icons/menu_utils_buddies.png\"\nmenu_utils_notes = \"icons/menu_utils_notes.png\"\nmenu_utils_reports = \"icons/menu_utils_reports.png\"\nmenu_utils_search = \"icons/menu_utils_search.png\"\nmenu_utils_shortcuts = \"icons/menu_utils_shortcuts.png\"\nmenu_utils_simulator = \"icons/menu_utils_simulator.png\"\nborder = \"images/border.png\"\nborder_small = \"images/border_small.png\"\ndeuterium = \"images/deuterium.gif\"\ndm_klein_2 = \"images/dm_klein_2.jpg\"\nenergie = \"images/energie.gif\"\ngender_female = \"images/gender_female.png\"\ngender_male = \"images/gender_male.png\"\ngender_unknown = \"images/gender_unknown.png\"\nicons = \"images/icons.png\"\nkristall = \"images/kristall.gif\"\nmessage = \"images/message.gif\"\nmetall = \"images/metall.gif\"\ntable_th_background = \"images/table_th_background.png\"\nbg_flat_0_aaaaaa_40x100 = \"images/ui-bg_flat_0_aaaaaa_40x100.png\"\nbg_flat_55_13233f_40x100 = \"images/ui-bg_flat_55_13233f_40x100.png\"\nbg_flat_75_132544_40x100 = \"images/ui-bg_flat_75_132544_40x100.png\"\nhard_65_344566_1x100 = \"images/ui-bg_highlight-hard_65_344566_1x100.png\"\nhard_75_344566_1x100 = \"images/ui-bg_highlight-hard_75_344566_1x100.png\"\nsoft_75_344566_1x100 = \"images/ui-bg_highlight-soft_75_344566_1x100.png\"\nsoft_95_13233e_1x100 = \"images/ui-bg_inset-soft_95_13233e_1x100.png\"\nicons_2e83ff_256x240 = \"images/ui-icons_2e83ff_256x240.png\"\nicons_cd0a0a_256x240 = \"images/ui-icons_cd0a0a_256x240.png\"\nicons_e6ebfb_256x240 = \"images/ui-icons_e6ebfb_256x240.png\"\nb = \"img/b.gif\"\nbackground_0800 = \"img/background_0800.jpg\"\nbackground_1024 = \"img/background_1024.jpg\"\nbackground_1280 = \"img/background_1280.jpg\"\nbackground_1600 = \"img/background_1600.jpg\"\nbackground_1920 = \"img/background_1920.jpg\"\nbackground_medium = \"img/background_medium.jpg\"\nbackground_original = \"img/background_original.jpg\"\nbg1 = \"img/bg1.gif\"\nbg2 = \"img/bg2.gif\"\nblank = \"img/blank.gif\"\nblitz_background = \"img/blitz_background.jpg\"\nblitz_background_medium = \"img/blitz_background_medium.jpg\"\ne = \"img/e.jpg\"\ngalaxie = \"img/galaxie.gif\"\np = \"img/p.png\"\np1 = \"img/p1.jpg\"\np1z1 = \"img/p1z1.png\"\np2 = \"img/p2.jpg\"\np2z2 = \"img/p2z2.png\"\np3 = \"img/p3.jpg\"\np3z2 = \"img/p3z2.png\"\np4 = \"img/p4.jpg\"\np5 = \"img/p5.jpg\"\np6 = \"img/p6.jpg\"\np7 = \"img/p7.jpg\"\np8 = \"img/p8.jpg\"\np9 = \"img/p9.jpg\"\ns = \"img/s.gif\"\n;saturn2 = \"img/saturn2.jpg\"\n;srgs = \"img/srgs.jpg\"\n;starz2 = \"img/starz2.png\"\nabort = \"images/abort.gif\"\n;black_moon = \"planeten/black_moon.jpg\"\n;black_planet = \"planeten/black_planet.jpg\"\ndebris = \"planeten/debris.gif\"\ndschjungelplanet01 = \"planeten/dschjungelplanet01.jpg\"\ndschjungelplanet02 = \"planeten/dschjungelplanet02.jpg\"\ndschjungelplanet03 = \"planeten/dschjungelplanet03.jpg\"\ndschjungelplanet04 = \"planeten/dschjungelplanet04.jpg\"\ndschjungelplanet05 = \"planeten/dschjungelplanet05.jpg\"\ndschjungelplanet06 = \"planeten/dschjungelplanet06.jpg\"\ndschjungelplanet07 = \"planeten/dschjungelplanet07.jpg\"\ndschjungelplanet08 = \"planeten/dschjungelplanet08.jpg\"\ndschjungelplanet09 = \"planeten/dschjungelplanet09.jpg\"\ndschjungelplanet10 = \"planeten/dschjungelplanet10.jpg\"\neisplanet01 = \"planeten/eisplanet01.jpg\"\neisplanet02 = \"planeten/eisplanet02.jpg\"\neisplanet03 = \"planeten/eisplanet03.jpg\"\neisplanet04 = \"planeten/eisplanet04.jpg\"\neisplanet05 = \"planeten/eisplanet05.jpg\"\neisplanet06 = \"planeten/eisplanet06.jpg\"\neisplanet07 = \"planeten/eisplanet07.jpg\"\neisplanet08 = \"planeten/eisplanet08.jpg\"\neisplanet09 = \"planeten/eisplanet09.jpg\"\neisplanet10 = \"planeten/eisplanet10.jpg\"\ngasplanet01 = \"planeten/gasplanet01.jpg\"\ngasplanet02 = \"planeten/gasplanet02.jpg\"\ngasplanet03 = \"planeten/gasplanet03.jpg\"\ngasplanet04 = \"planeten/gasplanet04.jpg\"\ngasplanet05 = \"planeten/gasplanet05.jpg\"\ngasplanet06 = \"planeten/gasplanet06.jpg\"\ngasplanet07 = \"planeten/gasplanet07.jpg\"\ngasplanet08 = \"planeten/gasplanet08.jpg\"\nmond = \"planeten/mond.jpg\"\nnormaltempplanet01 = \"planeten/normaltempplanet01.jpg\"\nnormaltempplanet02 = \"planeten/normaltempplanet02.jpg\"\nnormaltempplanet03 = \"planeten/normaltempplanet03.jpg\"\nnormaltempplanet04 = \"planeten/normaltempplanet04.jpg\"\nnormaltempplanet05 = \"planeten/normaltempplanet05.jpg\"\nnormaltempplanet06 = \"planeten/normaltempplanet06.jpg\"\nnormaltempplanet07 = \"planeten/normaltempplanet07.jpg\"\ntrockenplanet01 = \"planeten/trockenplanet01.jpg\"\ntrockenplanet02 = \"planeten/trockenplanet02.jpg\"\ntrockenplanet03 = \"planeten/trockenplanet03.jpg\"\ntrockenplanet04 = \"planeten/trockenplanet04.jpg\"\ntrockenplanet05 = \"planeten/trockenplanet05.jpg\"\ntrockenplanet06 = \"planeten/trockenplanet06.jpg\"\ntrockenplanet07 = \"planeten/trockenplanet07.jpg\"\ntrockenplanet08 = \"planeten/trockenplanet08.jpg\"\ntrockenplanet09 = \"planeten/trockenplanet09.jpg\"\ntrockenplanet10 = \"planeten/trockenplanet10.jpg\"\nwasserplanet01 = \"planeten/wasserplanet01.jpg\"\nwasserplanet02 = \"planeten/wasserplanet02.jpg\"\nwasserplanet03 = \"planeten/wasserplanet03.jpg\"\nwasserplanet04 = \"planeten/wasserplanet04.jpg\"\nwasserplanet05 = \"planeten/wasserplanet05.jpg\"\nwasserplanet06 = \"planeten/wasserplanet06.jpg\"\nwasserplanet07 = \"planeten/wasserplanet07.jpg\"\nwasserplanet08 = \"planeten/wasserplanet08.jpg\"\nwasserplanet09 = \"planeten/wasserplanet09.jpg\"\nwuestenplanet01 = \"planeten/wuestenplanet01.jpg\"\nwuestenplanet02 = \"planeten/wuestenplanet02.jpg\"\nwuestenplanet03 = \"planeten/wuestenplanet03.jpg\"\nwuestenplanet04 = \"planeten/wuestenplanet04.jpg\"\n;s_black_moon = \"planeten/small/s_black_moon.jpg\"\n;s_black_planet = \"planeten/small/s_black_planet.jpg\"\ns_debris = \"planeten/small/s_debris.jpg\"\ns_dschjungelplanet01 = \"planeten/small/s_dschjungelplanet01.jpg\"\ns_dschjungelplanet02 = \"planeten/small/s_dschjungelplanet02.jpg\"\ns_dschjungelplanet03 = \"planeten/small/s_dschjungelplanet03.jpg\"\ns_dschjungelplanet04 = \"planeten/small/s_dschjungelplanet04.jpg\"\ns_dschjungelplanet05 = \"planeten/small/s_dschjungelplanet05.jpg\"\ns_dschjungelplanet06 = \"planeten/small/s_dschjungelplanet06.jpg\"\ns_dschjungelplanet07 = \"planeten/small/s_dschjungelplanet07.jpg\"\ns_dschjungelplanet08 = \"planeten/small/s_dschjungelplanet08.jpg\"\ns_dschjungelplanet09 = \"planeten/small/s_dschjungelplanet09.jpg\"\ns_dschjungelplanet10 = \"planeten/small/s_dschjungelplanet10.jpg\"\ns_eisplanet01 = \"planeten/small/s_eisplanet01.jpg\"\ns_eisplanet02 = \"planeten/small/s_eisplanet02.jpg\"\ns_eisplanet03 = \"planeten/small/s_eisplanet03.jpg\"\ns_eisplanet04 = \"planeten/small/s_eisplanet04.jpg\"\ns_eisplanet05 = \"planeten/small/s_eisplanet05.jpg\"\ns_eisplanet06 = \"planeten/small/s_eisplanet06.jpg\"\ns_eisplanet07 = \"planeten/small/s_eisplanet07.jpg\"\ns_eisplanet08 = \"planeten/small/s_eisplanet08.jpg\"\ns_eisplanet09 = \"planeten/small/s_eisplanet09.jpg\"\ns_eisplanet10 = \"planeten/small/s_eisplanet10.jpg\"\ns_gasplanet01 = \"planeten/small/s_gasplanet01.jpg\"\ns_gasplanet02 = \"planeten/small/s_gasplanet02.jpg\"\ns_gasplanet03 = \"planeten/small/s_gasplanet03.jpg\"\ns_gasplanet04 = \"planeten/small/s_gasplanet04.jpg\"\ns_gasplanet05 = \"planeten/small/s_gasplanet05.jpg\"\ns_gasplanet06 = \"planeten/small/s_gasplanet06.jpg\"\ns_gasplanet07 = \"planeten/small/s_gasplanet07.jpg\"\ns_gasplanet08 = \"planeten/small/s_gasplanet08.jpg\"\ns_mond = \"planeten/small/s_mond.jpg\"\ns_normaltempplanet01 = \"planeten/small/s_normaltempplanet01.jpg\"\ns_normaltempplanet02 = \"planeten/small/s_normaltempplanet02.jpg\"\ns_normaltempplanet03 = \"planeten/small/s_normaltempplanet03.jpg\"\ns_normaltempplanet04 = \"planeten/small/s_normaltempplanet04.jpg\"\ns_normaltempplanet05 = \"planeten/small/s_normaltempplanet05.jpg\"\ns_normaltempplanet06 = \"planeten/small/s_normaltempplanet06.jpg\"\ns_normaltempplanet07 = \"planeten/small/s_normaltempplanet07.jpg\"\ns_trockenplanet01 = \"planeten/small/s_trockenplanet01.jpg\"\ns_trockenplanet02 = \"planeten/small/s_trockenplanet02.jpg\"\ns_trockenplanet03 = \"planeten/small/s_trockenplanet03.jpg\"\ns_trockenplanet04 = \"planeten/small/s_trockenplanet04.jpg\"\ns_trockenplanet05 = \"planeten/small/s_trockenplanet05.jpg\"\ns_trockenplanet06 = \"planeten/small/s_trockenplanet06.jpg\"\ns_trockenplanet07 = \"planeten/small/s_trockenplanet07.jpg\"\ns_trockenplanet08 = \"planeten/small/s_trockenplanet08.jpg\"\ns_trockenplanet09 = \"planeten/small/s_trockenplanet09.jpg\"\ns_trockenplanet10 = \"planeten/small/s_trockenplanet10.jpg\"\ns_wasserplanet01 = \"planeten/small/s_wasserplanet01.jpg\"\ns_wasserplanet02 = \"planeten/small/s_wasserplanet02.jpg\"\ns_wasserplanet03 = \"planeten/small/s_wasserplanet03.jpg\"\ns_wasserplanet04 = \"planeten/small/s_wasserplanet04.jpg\"\ns_wasserplanet05 = \"planeten/small/s_wasserplanet05.jpg\"\ns_wasserplanet06 = \"planeten/small/s_wasserplanet06.jpg\"\ns_wasserplanet07 = \"planeten/small/s_wasserplanet07.jpg\"\ns_wasserplanet08 = \"planeten/small/s_wasserplanet08.jpg\"\ns_wasserplanet09 = \"planeten/small/s_wasserplanet09.jpg\"\n;wasserplanet01 = \"planeten/small/wasserplanet01.jpg\"\n;wasserplanet02 = \"planeten/small/wasserplanet02.jpg\"\n;wasserplanet03 = \"planeten/small/wasserplanet03.jpg\"\n;wasserplanet04 = \"planeten/small/wasserplanet04.jpg\"\n;wasserplanet05 = \"planeten/small/wasserplanet05.jpg\"\n;wasserplanet06 = \"planeten/small/wasserplanet06.jpg\"\n;wasserplanet07 = \"planeten/small/wasserplanet07.jpg\"\n;wasserplanet08 = \"planeten/small/wasserplanet08.jpg\"\n;wasserplanet09 = \"planeten/small/wasserplanet09.jpg\"\n\nobject_in_space = \"planeten/object_in_space.jpg\"\ns_object_in_space = \"planeten/small/s_object_in_space.jpg\"\nl_object_in_space = \"planeten/large/object_in_space.png\"\n"
  },
  {
    "path": "skins/supernova-ivash/tmpl.ini",
    "content": "OpenGame"
  },
  {
    "path": "stat.php",
    "content": "<?php\n\nuse Alliance\\DBStaticAlly;\n\nconst WHO_IS_USER = 1;\n\n/**\n * stat.php\n *\n * 2.0 copyright (c) 2010-2012 by Gorlum for http://supernova.ws\n *   [!] Full rewrote\n*/\n\nfunction stat_tpl_assign(&$template, $selected, $array_name, $array, $sn_group_stat_common) {\n  global $who, $lang;\n\n  // $sn_group_stat_common = sn_get_groups('STAT_COMMON');\n  foreach($array as $key => $value) {\n    if($array_name == 'type' && $who == 2 && !in_array($key, $sn_group_stat_common)) {\n      continue;\n    }\n\n    $header = isset($value['header']) ? $value['header'] : $lang['stat_type'][$key];\n\n    $template->assign_block_vars($array_name, array(\n      'ID'       => $key,\n      'HEADER'   => $header,\n      'SELECTED' => $key == $selected,\n    ));\n  }\n}\n\n$allow_anonymous = true;\n\ninclude('common.' . substr(strrchr(__FILE__, '.'), 1));\n\nlng_include('stat');\n\n$sn_group_stat_common = sn_get_groups('STAT_COMMON');\n$who = sys_get_param_int('who', WHO_IS_USER);\n$type = sys_get_param_int('type');\n$type = $who != WHO_IS_USER && !in_array($type, $sn_group_stat_common) ? 1 : $type;\n$range = sys_get_param_int('range', 1);\n$source = sys_get_param_str('source');\n\n$template = SnTemplate::gettemplate('stat_statistics', true);\n\n$subject_list = array(\n  1 => array('header' => $lang['stat_player']),\n);\nif(!$source) {\n  $subject_list[2] = array('header' => $lang['stat_allys']);\n}\nstat_tpl_assign($template, $who, 'subject', $subject_list, $sn_group_stat_common);\n\n$stat_types = array(\n   STAT_TOTAL => array(\n     'type' => 'total',\n   ),\n\n   STAT_FLEET => array(\n     'type' => 'fleet',\n   ),\n\n   STAT_TECH => array(\n     'type' => 'tech',\n   ),\n\n   STAT_BUILDING => array(\n     'type' => 'build',\n   ),\n\n   STAT_DEFENSE => array(\n     'type' => 'defs',\n   ),\n\n   STAT_RESOURCE => array(\n     'type' => 'res',\n   ),\n\n   STAT_RAID_TOTAL => array(\n     'type' => 'raids',\n   ),\n\n   STAT_RAID_WON => array(\n     'type' => 'raidswin',\n   ),\n\n   STAT_RAID_LOST => array(\n     'type' => 'raidsloose',\n   ),\n\n  STAT_LVL_BUILDING => array(\n     'type' => 'lvl_minier',\n  ),\n\n  STAT_LVL_TECH => array(\n     'type' => 'player_rpg_tech_level',\n  ),\n\n  STAT_LVL_RAID => array(\n     'type' => 'lvl_raid',\n  ),\n);\nstat_tpl_assign($template, $type, 'type', $stat_types, $sn_group_stat_common);\n\n$Rank = $stat_types[$type]['type'];\n\n$is_common_stat = in_array($type, $sn_group_stat_common);\n$start = floor($range / 100 % 100) * 100;\n$query = db_stat_list_statistic($who, $is_common_stat, $Rank, $start, $source);\n\n// TODO - Не работает, если игроков на Блице > 100\n$record_count = $source ? SN::$db->db_num_rows($query) : ($who == WHO_IS_USER ? db_user_count() : DBStaticAlly::db_ally_count());\n\n$page_count = floor($record_count / 100);\n$pages = array();\nfor($i = 0; $i <= $page_count; $i++) {\n  $first_element = $i * 100 + 1;\n  $last_element = $first_element + 99;\n  $pages[$first_element] = array(\n    'header' => \"{$first_element}-{$last_element}\",\n  );\n}\n\n$range = $range > $record_count ? $record_count : $range;\nstat_tpl_assign($template, $range, 'range', $pages, $sn_group_stat_common);\n\nwhile ($row = db_fetch($query)) {\n  $row_stat = array(\n    'ID' => $row['id'],\n    'RANK'        => $row['rank'],\n    'RANK_CHANGE' => $row['rank_old'] ? $row['rank_old'] - $row['rank'] : 0,\n    'POINTS' => HelperString::numberFloorAndFormat($row['points']),\n  );\n\n  if($who == WHO_IS_USER) {\n    $row_stat['ALLY_NAME'] = $row['ally_name'];\n    $row_stat['ALLY_ID'] = $row['ally_id'];\n    empty($row['username']) ? $row['username'] = $row['name'] : false;\n    $row_stat['NAME'] = player_nick_render_to_html($row, [\n      'icons' => empty($source),\n      'color' => empty($source),\n    ]);\n\n\n//      // TODO - Добавлять реальное имя игрока на Блице для закрытого раунда\n  } else {\n    $row_stat['MEMBERS'] = $row['ally_members'];\n    $row_stat['POINTS_PER_MEMBER'] = HelperString::numberFloorAndFormat(floor($row['points'] / $row['ally_members']));\n    $row_stat['NAME'] = $row['name'];\n  }\n\n  $template->assign_block_vars('stat', $row_stat);\n}\n\n$next_run = sys_schedule_get_prev_run(SN::$config->stats_schedule, SN::$config->var_stat_update, true);\n$template->assign_vars(array(\n  'REFRESH_DATE' => SN::$config->var_stat_update ? date(FMT_DATE_TIME, strtotime(SN::$config->var_stat_update) + SN_CLIENT_TIME_DIFF) : '',\n  'NEXT_DATE' => $next_run ? date(FMT_DATE_TIME, $next_run + SN_CLIENT_TIME_DIFF) : '',\n  'RANGE' => $range,\n  'SUBJECT' => $who,\n  'TYPE' => $type,\n  'USER_ALLY' => $user['ally_id'],\n  // TODO - Для блица - вытаскивать blitz_player_id и подсвечивать пользователя на блице\n  'USER_ID' => $source ? 0 : $user['id'],\n  'SOURCE' => $source,\n  'STATS_HIDE_PM_LINK' => SN::$config->stats_hide_pm_link || $source,\n));\n\nSnTemplate::display($template, $lang['stat_header']);\n"
  },
  {
    "path": "tests/Bonus/BonusAtomAbilityTest.php",
    "content": "<?php\n/**\n * Created by Gorlum 03.12.2017 22:55\n */\n\nnamespace Bonus;\n\nuse Core\\GlobalContainer;\nuse Fixtures\\UnitInfo;\n\n/**\n * Class BonusAtomAbilityTest\n * @package Bonus\n * @coversDefaultClass \\Bonus\\BonusAtomAbility\n */\nclass BonusAtomAbilityTest extends \\PHPUnit_Framework_TestCase {\n\n  /**\n   * @var string $objectClass\n   */\n  protected $objectClass = BonusAtomAbility::class;\n\n  /**\n   * @var BonusAtomAbility $object\n   */\n  protected $object;\n\n  protected function setUp() {\n    parent::setUp();\n\n    UnitInfo::build(UNIT_TEST_ID_STRING_1)->bonus(BONUS_ABILITY, TEST_VALUE_INT_7)->install();\n    $this->object = new $this->objectClass(UNIT_TEST_ID_STRING_1, BonusAtom::RETURN_IF_BASE_NOT_ZERO);\n  }\n\n  public function dataAdjustValue() {\n    return [\n      [BonusAtom::RETURN_ALWAYS, 0, 0, 0, 0],\n      [BonusAtom::RETURN_ALWAYS, 0, 2.5, 1, 1],\n      [BonusAtom::RETURN_ALWAYS, 7, 0, 0, 1],\n      [BonusAtom::RETURN_ALWAYS, 7, 2.5, 1, 1],\n\n      [BonusAtom::RETURN_IF_BASE_NOT_ZERO, 0, 0, 0, 0],\n      [BonusAtom::RETURN_IF_BASE_NOT_ZERO, 0, 2.5, 0, 0],\n      [BonusAtom::RETURN_IF_BASE_NOT_ZERO, 7, 0, 0, 1],\n      [BonusAtom::RETURN_IF_BASE_NOT_ZERO, 7, 2.5, 1, 1],\n    ];\n  }\n\n  /**\n   * @covers ::adjustValue\n   * @covers ::calcAdjustment\n   * @dataProvider dataAdjustValue\n   */\n  public function testAdjustValue($isZeroReturned, $baseValue, $bonusAmount, $expectedResult, $expectedValue) {\n    /**\n     * @var ValueBonused $valueBonused\n     */\n    \\SN::$gc = new GlobalContainer();\n    $valueBonused = $this->getMockBuilder(ValueBonused::class)\n      ->setConstructorArgs([UNIT_TEST_ID_STRING_0, $baseValue])\n      ->getMock();\n\n    $this->object->ifBaseNonZero = $isZeroReturned;\n\n    $this->assertEquals($expectedResult, $this->object->adjustValue($valueBonused->value, $bonusAmount, $valueBonused->base));\n    $this->assertEquals($expectedValue, $valueBonused->value);\n  }\n\n}\n"
  },
  {
    "path": "tests/Bonus/BonusAtomAddTest.php",
    "content": "<?php\n/**\n * Created by Gorlum 03.12.2017 23:54\n */\n\nnamespace Bonus;\n\nuse Fixtures\\UnitInfo;\n\n/**\n * Class BonusAtomAddTest\n * @package Bonus\n * @coversDefaultClass \\Bonus\\BonusAtomAdd\n */\nclass BonusAtomAddTest extends BonusAtomAbilityTest {\n\n  /**\n   * @var string $objectClass\n   */\n  protected $objectClass = BonusAtomAdd::class;\n\n  /**\n   * @var BonusAtomAdd $object\n   */\n  protected $object;\n\n  protected function setUp() {\n    parent::setUp();\n\n    UnitInfo::build(UNIT_TEST_ID_STRING_1)->bonus(BONUS_ADD, TEST_VALUE_INT_7)->install();\n  }\n\n  public function dataAdjustValue() {\n    return [\n      [BonusAtom::RETURN_ALWAYS, 0, 0, 0, 0],\n      [BonusAtom::RETURN_ALWAYS, 0, 2.5, 17.5, 17.5], // Unit power is 7, so 7 * 2.5 = 17.5\n      [BonusAtom::RETURN_ALWAYS, 7, 0, 0, 7],\n      [BonusAtom::RETURN_ALWAYS, 7, 2.5, 17.5, 24.5],\n\n      [BonusAtom::RETURN_IF_BASE_NOT_ZERO, 0, 0, 0, 0],\n      [BonusAtom::RETURN_IF_BASE_NOT_ZERO, 0, 2.5, 0, 0],\n      [BonusAtom::RETURN_IF_BASE_NOT_ZERO, 7, 0, 0, 7],\n      [BonusAtom::RETURN_IF_BASE_NOT_ZERO, 7, 2.5, 17.5, 24.5],\n    ];\n  }\n\n}\n"
  },
  {
    "path": "tests/Bonus/BonusAtomMultiplyTest.php",
    "content": "<?php\n/**\n * Created by Gorlum 04.12.2017 0:08\n */\n\nnamespace Bonus;\n\nuse Fixtures\\UnitInfo;\n\n/**\n * Class BonusAtomMultiplyTest\n * @package Bonus\n * @coversDefaultClass \\Bonus\\BonusAtomMultiply\n */\nclass BonusAtomMultiplyTest extends BonusAtomAbilityTest {\n\n  /**\n   * @var string $objectClass\n   */\n  protected $objectClass = BonusAtomMultiply::class;\n\n  /**\n   * @var BonusAtomMultiply $object\n   */\n  protected $object;\n\n  protected function setUp() {\n    parent::setUp();\n\n    UnitInfo::build(UNIT_TEST_ID_STRING_1)->bonus(BONUS_MULTIPLY, TEST_VALUE_INT_7)->install();\n  }\n\n  public function dataAdjustValue() {\n    return [\n      [BonusAtom::RETURN_ALWAYS, 0, 0, 0, 0],\n      [BonusAtom::RETURN_ALWAYS, 0, 2.5, 0, 0],\n      [BonusAtom::RETURN_ALWAYS, 7, 0, -7, 0], // Multiply is 0 so final result is 0 too. Also we should substract 7 to make base value equal 0\n      [BonusAtom::RETURN_ALWAYS, 7, 2.5, 115.5, 122.5], // Unit power is 7, so 7 * 2.5 = 17.5 - multiplier. 17.5 * 7 = 122.5. (final)122.5 - (current)7 = 115.5 (returned)\n\n\n      [BonusAtom::RETURN_ALWAYS, 2, 0.2, 0.8, 2.8], // 7 * 0.2 = 1.4;  1.4 * 2 = 2.8 (final); 2.8 - 2 = 0.8 (returned)\n      [BonusAtom::RETURN_ALWAYS, 2, 0.1, -0.6, 1.4], // 7 * 0.1 = 0.7;  0.7 * 2 = 1.4 (final); 1.4 - 2 = -0.6 (returned)\n      [BonusAtom::RETURN_ALWAYS, 5, 1, 30, 35], // Amount = 1\n\n      [BonusAtom::RETURN_IF_BASE_NOT_ZERO, 0, 0, 0, 0],\n      [BonusAtom::RETURN_IF_BASE_NOT_ZERO, 0, 2.5, 0, 0],\n      [BonusAtom::RETURN_IF_BASE_NOT_ZERO, 7, 0, -7, 0],\n      [BonusAtom::RETURN_IF_BASE_NOT_ZERO, 7, 2.5, 115.5, 122.5],\n    ];\n  }\n\n}\n"
  },
  {
    "path": "tests/Bonus/BonusAtomPercentTest.php",
    "content": "<?php\n/**\n * Created by Gorlum 04.12.2017 0:40\n */\n\nnamespace Bonus;\n\nuse Fixtures\\UnitInfo;\n\n/**\n * Class BonusAtomPercentTest\n * @package Bonus\n * @coversDefaultClass \\Bonus\\BonusAtomPercent\n */\nclass BonusAtomPercentTest extends BonusAtomAbilityTest {\n\n  /**\n   * @var string $objectClass\n   */\n  protected $objectClass = BonusAtomPercent::class;\n\n  /**\n   * @var BonusAtomPercent $object\n   */\n  protected $object;\n\n  protected function setUp() {\n    parent::setUp();\n\n    UnitInfo::build(UNIT_TEST_ID_STRING_1)->bonus(BONUS_PERCENT, 25)->install();\n  }\n\n  public function dataAdjustValue() {\n    return [\n      [BonusAtom::RETURN_ALWAYS, 0, 0, 0, 0, 25],\n      [BonusAtom::RETURN_ALWAYS, 0, 2.5, 0, 0, 25],\n      [BonusAtom::RETURN_ALWAYS, 7, 0, 0, 7, 25],\n      [BonusAtom::RETURN_ALWAYS, 6, 2.5, 3.75, 9.75, 25], // 25%(6) = 1.5; 1.5 * 2.5 = 3.75\n\n      [BonusAtom::RETURN_ALWAYS, 6, 2.5, 15, 21, 100], // 100%(6) = 6; 6 * 2.5 = 15\n      [BonusAtom::RETURN_ALWAYS, 6, 2.5, 0, 6, 0], // 0%(6) = 0; 0 * 2.5 = 0\n      [BonusAtom::RETURN_ALWAYS, 6, 2.5, -3.75, 2.25, -25], // -25%(6) = -15; -1.5 * 2.5 = -3.75\n\n      [BonusAtom::RETURN_IF_BASE_NOT_ZERO, 0, 0, 0, 0, 25],\n      [BonusAtom::RETURN_IF_BASE_NOT_ZERO, 0, 2.5, 0, 0, 25],\n      [BonusAtom::RETURN_IF_BASE_NOT_ZERO, 7, 0, 0, 7, 25],\n      [BonusAtom::RETURN_IF_BASE_NOT_ZERO, 6, 2.5, 3.75, 9.75, 25],\n    ];\n  }\n\n  /**\n   * @covers ::adjustValue\n   * @covers ::calcAdjustment\n   * @dataProvider dataAdjustValue\n   */\n  public function testAdjustValue($isZeroReturned, $baseValue, $bonusAmount, $expectedResult, $expectedValue, $percent = 0) {\n    UnitInfo::build(UNIT_TEST_ID_STRING_1)->bonus(BONUS_PERCENT, $percent)->install();\n\n    // Recalculating object because of new UnitInfo\n    $this->object = new $this->objectClass(UNIT_TEST_ID_STRING_1, BonusAtom::RETURN_IF_BASE_NOT_ZERO);\n\n    parent::testAdjustValue($isZeroReturned, $baseValue, $bonusAmount, $expectedResult, $expectedValue);\n  }\n\n}\n"
  },
  {
    "path": "tests/Bonus/BonusAtomTest.php",
    "content": "<?php\n/**\n * Created by Gorlum 03.12.2017 14:49\n */\n\nnamespace Bonus;\n\nuse Fixtures\\UnitInfo;\n\n/**\n * Class BonusAtomTest\n * @package Bonus\n * @coversDefaultClass \\Bonus\\BonusAtom\n */\nclass BonusAtomTest extends BonusAtomAbilityTest {\n\n  /**\n   * @var string $objectClass\n   */\n  protected $objectClass = BonusAtom::class;\n\n  /**\n   * @var BonusAtom $object\n   */\n  protected $object;\n\n//  protected function setUp() {\n//    parent::setUp();\n//\n//    UnitInfo::build(UNIT_TEST_ID_STRING_1)->bonus(BONUS_MULTIPLY, TEST_VALUE_INT_7)->install();\n//    $this->object = new $this->objectClass(UNIT_TEST_ID_STRING_1, BonusCatalog::VALUE_NON_ZERO);\n//  }\n\n  /**\n   * @covers ::calcBonusType\n   */\n  public function testCalcBonusType() {\n    $this->assertEquals(BONUS_ADD, BonusAtom::calcBonusType(UnitInfo::build()->asArray()));\n    $this->assertEquals(BONUS_MULTIPLY, BonusAtom::calcBonusType(UnitInfo::build()->bonus(BONUS_MULTIPLY)->asArray()));\n  }\n\n  /**\n   * @covers ::calcBonusPower\n   */\n  public function testCalcBonusPower() {\n    $this->assertEquals(TEST_VALUE_INT_7, BonusAtom::calcBonusPower(UnitInfo::build()->bonus(BONUS_MULTIPLY, TEST_VALUE_INT_7)->asArray()));\n\n    $this->assertEquals(TEST_VALUE_INT_1, BonusAtom::calcBonusPower(UnitInfo::build()->bonus(BONUS_ABILITY)->asArray()));\n    $this->assertEquals(TEST_VALUE_INT_0, BonusAtom::calcBonusPower(UnitInfo::build()->bonus(BONUS_MULTIPLY)->asArray()));\n  }\n\n  /**\n   * @covers ::__construct\n   * @backupGlobals enabled\n   */\n  public function test__construct() {\n\n    $this->assertAttributeEquals(UNIT_TEST_ID_STRING_1, 'snId', $this->object);\n    $this->assertAttributeEquals(true, 'ifBaseNonZero', $this->object);\n    $this->assertAttributeEquals(TEST_VALUE_INT_7, 'power', $this->object);\n  }\n\n  public function dataIsReturnNothing() {\n    return [\n      [BonusAtom::RETURN_ALWAYS, 0, false],\n      [BonusAtom::RETURN_ALWAYS, 1, false],\n\n      [BonusAtom::RETURN_IF_BASE_NOT_ZERO, 0, true],\n      [BonusAtom::RETURN_IF_BASE_NOT_ZERO, 1, false],\n    ];\n  }\n\n\n  /**\n   * @covers ::isReturnNothing\n   * @dataProvider dataIsReturnNothing\n   */\n  public function testIsReturnNothing($isZeroReturned, $baseValue, $expected) {\n    /**\n     * @var ValueBonused $valueBonused\n     */\n    $valueBonused = $this->createMock(ValueBonused::class);\n    $valueBonused->base = $baseValue;\n\n    $this->object = new BonusAtom(UNIT_TEST_ID_STRING_1, $isZeroReturned);\n    $this->assertEquals($expected, invokeMethod($this->object, 'isReturnNothing', [$valueBonused->base]));\n  }\n\n  public function dataAdjustValue() {\n    return [\n      [BonusAtom::RETURN_IF_BASE_NOT_ZERO, 0, 0, 0, 0],\n\n      [BonusAtom::RETURN_IF_BASE_NOT_ZERO, 2, 3, 0, 2],\n    ];\n  }\n\n}\n"
  },
  {
    "path": "tests/Bonus/BonusCatalogTest.php",
    "content": "<?php\n/**\n * Created by Gorlum 03.12.2017 14:37\n */\n\nnamespace Bonus;\n\nuse SN;\nuse Core\\GlobalContainer;\nuse \\classConfig;\n\n/**\n * Class BonusCatalogTest\n * @package Bonus\n * @coversDefaultClass \\Bonus\\BonusCatalog\n */\nclass BonusCatalogTest extends \\PHPUnit_Framework_TestCase {\n  /**\n   * @var BonusCatalog $object\n   */\n  protected $object;\n\n  /**\n   * @var GlobalContainer $gc\n   */\n  protected $gc;\n\n\n  protected function setUp() {\n    parent::setUp();\n\n    $this->gc = $this->createMock(GlobalContainer::class);\n\n    $this->object = new BonusCatalog($this->gc);\n  }\n\n  /**\n   * @covers ::__construct\n   * @covers ::loadDefaults\n   */\n  public function test__construct() {\n    $this->assertAttributeEquals($this->gc, 'gc', $this->object);\n  }\n\n  /**\n   * @covers ::registerBonus\n   * @covers ::getBonusListAtom\n   */\n  public function testGetBonusDescriptions() {\n    $this->assertNull($this->object->getBonusListAtom(UNIT_TEST_ID_STRING_1));\n    $this->object->registerBonus(UNIT_TEST_ID_STRING_1, UNIT_TEST_ID_STRING_1);\n    $this->object->registerBonus(UNIT_TEST_ID_STRING_1, UNIT_TEST_ID_STRING_2);\n    $this->assertCount(2, $this->object->getBonusListAtom(UNIT_TEST_ID_STRING_1));\n  }\n\n  protected function tearDown() {\n    parent::tearDown();\n\n    unset($this->object);\n  }\n\n}\n"
  },
  {
    "path": "tests/Bonus/BonusFactoryTest.php",
    "content": "<?php\n/**\n * Created by Gorlum 04.12.2017 3:18\n */\n\nnamespace Bonus;\n\nuse Fixtures\\UnitInfo;\n\n/**\n * Class BonusFactoryTest\n * @package Bonus\n * @coversDefaultClass \\Bonus\\BonusFactory\n */\nclass BonusFactoryTest extends \\PHPUnit_Framework_TestCase {\n\n  public function dataBuild() {\n    return [\n      [BONUS_NONE, 2, BonusAtom::class],\n      [BONUS_PERCENT, 2, BonusAtomPercent::class],\n      [BONUS_ADD, 2, BonusAtomAdd::class],\n      [BONUS_ABILITY, 2, BonusAtomAbility::class],\n      [BONUS_MULTIPLY, 2, BonusAtomMultiply::class],\n    ];\n  }\n\n  /**\n   * @covers ::build\n   * @dataProvider dataBuild\n   */\n  public function testBuild($type, $power, $className) {\n    UnitInfo::build(UNIT_TEST_ID_STRING_1)->bonus($type, $power)->install();\n\n    $bonus = BonusFactory::build(UNIT_TEST_ID_STRING_1, BonusAtom::RETURN_ALWAYS);\n\n    // Checking that appropriate type returned\n    $this->assertTrue($bonus instanceof $className);\n    $this->assertEquals($power, $bonus->power);\n  }\n\n}\n"
  },
  {
    "path": "tests/Bonus/BonusListAtomTest.php",
    "content": "<?php\n/**\n * Created by Gorlum 06.12.2017 16:27\n */\n\nnamespace Bonus;\n\nuse Fixtures\\UnitInfo;\n\n/**\n * Class BonusListAtomTest\n * @package Bonus\n * @coversDefaultClass \\Bonus\\BonusListAtom\n */\nclass BonusListAtomTest extends \\PHPUnit_Framework_TestCase {\n\n  protected $object;\n\n  /**\n   * @covers ::__construct\n   * @covers ::addUnit\n   * @covers ::bonusSort\n   * @covers ::count\n   * @covers ::getBonusAtoms\n   * @backupGlobals enable\n   */\n  public function test__construct() {\n    // Main bonus\n    UnitInfo::build(UNIT_TEST_ID_STRING_0)->bonus(BONUS_MULTIPLY, TEST_VALUE_INT_7)->install();\n    // Installing bonuses\n    UnitInfo::build(UNIT_TEST_ID_STRING_5)->bonus(BONUS_MULTIPLY, TEST_VALUE_INT_7)->install();\n    UnitInfo::build(UNIT_TEST_ID_STRING_4)->bonus(BONUS_PERCENT, TEST_VALUE_INT_7)->install();\n    UnitInfo::build(UNIT_TEST_ID_STRING_3)->bonus(BONUS_ADD, TEST_VALUE_INT_7)->install();\n    UnitInfo::build(UNIT_TEST_ID_STRING_2)->bonus(BONUS_ABILITY, TEST_VALUE_INT_7)->install();\n    UnitInfo::build(UNIT_TEST_ID_STRING_1)->bonus(BONUS_NONE, TEST_VALUE_INT_7)->install();\n\n    $this->object = new BonusListAtom(UNIT_TEST_ID_STRING_0);\n    $this->assertAttributeEquals(UNIT_TEST_ID_STRING_0, 'bonusId', $this->object);\n\n    $this->object->addUnit(UNIT_TEST_ID_STRING_1);\n    $this->object->addUnit(UNIT_TEST_ID_STRING_5);\n    $this->assertEquals(2, $this->object->count());\n\n    $this->object->addUnit(UNIT_TEST_ID_STRING_2);\n    $this->object->addUnit(UNIT_TEST_ID_STRING_3);\n    $this->object->addUnit(UNIT_TEST_ID_STRING_4);\n\n    $this->assertEquals(5, $this->object->count());\n\n    $atomList = $this->object->getBonusAtoms();\n    $i = 0;\n    $checkOrder = [\n      UNIT_TEST_ID_STRING_1,\n      UNIT_TEST_ID_STRING_2,\n      UNIT_TEST_ID_STRING_3,\n      UNIT_TEST_ID_STRING_4,\n      UNIT_TEST_ID_STRING_5,\n    ];\n    foreach ($atomList as $bonusUnitId => $bonusAtom) {\n      $this->assertEquals($checkOrder[$i++], $bonusAtom->snId);\n    }\n  }\n\n}\n"
  },
  {
    "path": "tests/DBAL/Tests/ActiveRecordAbstractTest.php",
    "content": "<?php\n/**\n * Created by Gorlum 12.07.2017 12:28\n */\n\nnamespace DBAL\\Tests;\n\nuse Core\\GlobalContainer;\nuse DBAL\\db_mysql;\nuse DBAL\\ActiveRecordAbstract;\nuse DBAL\\Tests\\Fixtures\\ActiveAbstractObjectDump;\nuse DBAL\\Tests\\Fixtures\\RecordActiveAbstractObject;\n\n/**\n * Class AccessLoggedTest\n * @coversDefaultClass \\DBAL\\ActiveRecordAbstract\n * @package classes\n */\nclass ActiveRecordAbstractTest extends \\PHPUnit_Framework_TestCase {\n\n  /**\n   * Checking values setting\n   *\n   * @covers ::db\n   * @covers ::setDb\n   * @covers ::tableName\n   * @covers ::calcTableName\n   * @covers ::dbPrepareQuery\n   */\n  public function testDbData() {\n    $this->assertEquals(\\SN::services()->db, RecordActiveAbstractObject::db());\n\n    $db = new db_mysql(new GlobalContainer());\n    RecordActiveAbstractObject::setDb($db);\n    $this->assertAttributeEquals($db, 'db', 'DBAL\\Tests\\Fixtures\\RecordActiveAbstractObject');\n    $this->assertEquals($db, RecordActiveAbstractObject::db());\n\n    $this->assertEquals('active_abstract_object', RecordActiveAbstractObject::tableName());\n    $this->assertEquals('active_abstract_object_dump', ActiveAbstractObjectDump::tableName());\n\n    $dbq = invokeMethod(RecordActiveAbstractObject::class, 'dbPrepareQuery');\n    $this->assertEquals(\"DBAL\\\\DbQuery\", get_class($dbq));\n    $this->assertAttributeEquals('active_abstract_object', 'table', $dbq);\n  }\n\n  /**\n   * @covers ::haveTranslationToProperty\n   */\n  public function testHaveTranslationToProperty() {\n    // Direct access field have NO translation\n    $this->assertFalse(invokeMethod(RecordActiveAbstractObject::class, 'haveTranslationToProperty', ['varchar']));\n    // Translated field DO HAVE translation\n    $this->assertTrue(invokeMethod(RecordActiveAbstractObject::class, 'haveTranslationToProperty', ['timestamp_current']));\n    // Property name have NO translation\n    $this->assertFalse(invokeMethod(RecordActiveAbstractObject::class, 'haveTranslationToProperty', ['timestampCurrent']));\n    // Not exists field have NO translation\n    $this->assertFalse(invokeMethod(RecordActiveAbstractObject::class, 'haveTranslationToProperty', ['notAField']));\n  }\n\n  /**\n   * @covers ::haveField\n   */\n  public function testHaveField() {\n    // Checking for fields and properties\n    $this->assertTrue(invokeMethod(RecordActiveAbstractObject::class, 'haveField', ['varchar']));\n    $this->assertTrue(invokeMethod(RecordActiveAbstractObject::class, 'haveField', ['timestamp_current']));\n    $this->assertFalse(invokeMethod(RecordActiveAbstractObject::class, 'haveField', ['timestampCurrent']));\n    $this->assertFalse(invokeMethod(RecordActiveAbstractObject::class, 'haveField', ['notAField']));\n  }\n\n  /**\n   * @covers ::haveProperty\n   */\n  public function testHaveProperty() {\n    $this->assertTrue(invokeMethod(RecordActiveAbstractObject::class, 'haveProperty', ['varchar']));\n    $this->assertTrue(invokeMethod(RecordActiveAbstractObject::class, 'haveProperty', ['timestampCurrent']));\n    $this->assertFalse(invokeMethod(RecordActiveAbstractObject::class, 'haveProperty', ['timestamp_current']));\n    $this->assertFalse(invokeMethod(RecordActiveAbstractObject::class, 'haveProperty', ['notAField']));\n  }\n\n\n  public function dataGetPropertyName() {\n    return\n      [\n        ['varchar', 'varchar'],\n        ['timestamp_current', 'timestampCurrent'],\n        ['timestampCurrent', ''],\n        ['notAField', ''],\n      ];\n  }\n\n  /**\n   * @covers ::getPropertyName\n   * @dataProvider dataGetPropertyName\n   */\n  public function testGetPropertyName($param, $expected) {\n    $this->assertEquals($expected, invokeMethod(RecordActiveAbstractObject::class, 'getPropertyName', [$param]));\n  }\n\n\n  /**\n   * @return array\n   */\n  public function dataGetFieldName() {\n    return\n      [\n        ['varchar', 'varchar'],\n        ['timestampCurrent', 'timestamp_current'],\n        ['timestamp_current', ''],\n        ['notAField', ''],\n      ];\n  }\n\n  /**\n   * @covers ::getFieldName\n   * @dataProvider dataGetFieldName\n   */\n  public function testGetFieldName($param, $expected) {\n    $this->assertEquals($expected, invokeMethod(RecordActiveAbstractObject::class, 'getFieldName', [$param]));\n  }\n\n  /**\n   * @covers ::translateNames\n   */\n  public function testTranslation() {\n    // Fields to Properties\n    $this->assertEquals(\n      [\n        'timestampCurrent' => TEST_VALUE_SQL_DATE,\n        'varchar'          => 'varvalue',\n        // TODO - add incorrect field\n      ],\n      invokeMethod(RecordActiveAbstractObject::class, 'translateNames', [\n        [\n          'timestamp_current' => TEST_VALUE_SQL_DATE,\n          'varchar'           => 'varvalue',\n          'notAField'         => 'test',\n        ],\n        ActiveRecordAbstract::FIELDS_TO_PROPERTIES\n      ]));\n\n    // Properties to Fields\n    $this->assertEquals(\n      [\n        'timestamp_current' => TEST_VALUE_SQL_DATE,\n        'varchar'           => 'varvalue',\n      ],\n      invokeMethod(RecordActiveAbstractObject::class, 'translateNames', [\n        [\n          'timestampCurrent' => TEST_VALUE_SQL_DATE,\n          'varchar'          => 'varvalue',\n          'notAProperty'     => 'test',\n        ],\n        ActiveRecordAbstract::PROPERTIES_TO_FIELDS\n      ]));\n  }\n\n  /**\n   * @covers ::__construct\n   * @covers ::accept\n   */\n  public function testConstructor() {\n    $object = new RecordActiveAbstractObject();\n//    $this->assertAttributeEquals(\\SN::$gc, 'services', $object);\n    $this->assertAttributeEquals(true, '_isNew', $object);\n//    $this->assertEquals(0, $object->id);\n//\n//    $object->accept();\n//    $this->assertAttributeEquals(true, '_isNew', $object);\n//    $this->assertEquals(0, $object->id);\n//\n//    $object->id = 5;\n//    $object->accept();\n//    $this->assertAttributeEquals(false, '_isNew', $object);\n//    $this->assertEquals(5, $object->id);\n\n  }\n\n  /**\n   * @covers ::defaultValues\n   * @covers ::fromProperties\n   * @covers ::buildEvenEmpty\n   * @covers ::build\n   */\n  public function testBuild() {\n    // Testing empty buildEvenEmpty()\n    $this->assertEquals(\n      RecordActiveAbstractObject::class,\n      get_class(invokeMethod(RecordActiveAbstractObject::class, 'buildEvenEmpty', [[]]))\n    );\n\n    // Testing empty build\n    $this->assertFalse(RecordActiveAbstractObject::build([]));\n\n    // Testing default values and overriding CURRENT_TIMESTAMP\n    $object = RecordActiveAbstractObject::build([\n      'timestampCurrent' => TEST_VALUE_SQL_DATE,\n      'varchar'          => 'varvalue',\n    ]);\n    $this->assertEquals(TEST_VALUE_SQL_DATE, $object->timestampCurrent);\n    $this->assertEquals('varvalue', $object->varchar);\n    $this->assertNull($object->null);\n    $this->assertNull($object->notAField);\n    $this->assertEquals(\n      [\n        'timestampCurrent' => TEST_VALUE_SQL_DATE,\n        'varchar'          => \"varvalue\",\n        'null'             => null,\n      ],\n      $object->asArray()\n    );\n\n\n    // Testing CURRENT_TIMESTAMP and setting values to NULL-defaulted values\n    $object = RecordActiveAbstractObject::build([\n      'null' => 'nullvalue',\n    ]);\n\n    $this->assertEquals(date(FMT_DATE_TIME_SQL, SN_TIME_NOW), $object->timestampCurrent);\n    $this->assertEquals('', $object->varchar);\n    $this->assertEquals('nullvalue', $object->null);\n  }\n\n  /**\n   * @covers ::fromFields\n   */\n  public function testFromFields() {\n    $object = new RecordActiveAbstractObject();\n    invokeMethod($object, 'fromFields', [[\n      'timestamp_current' => TEST_VALUE_SQL_DATE,\n      'notAField'         => 'test',\n    ]]);\n\n    $this->assertEquals(TEST_VALUE_SQL_DATE, $object->timestampCurrent);\n    $this->assertEquals('', $object->varchar);\n    $this->assertNull($object->notAField);\n  }\n\n\n  /**\n   * @covers ::getDefault\n   */\n  public function testGetDefault() {\n    $object = RecordActiveAbstractObject::buildEvenEmpty([]);\n\n    $this->assertNull(invokeMethod($object, 'getDefault', ['timestamp_current']));\n    $this->assertNull(invokeMethod($object, 'getDefault', ['NonExistingProperty']));\n  }\n\n  /**\n   * @covers ::shieldName\n   * @covers ::__set\n   * expectedExceptionMessageRegExp /{{{ Свойство \\s+ не существует в ActiveRecord \\s+ }}}/\n   * expectedExceptionMessage {{{ Свойство \\s+ не существует в ActiveRecord \\s+ }}}\n   */\n  public function test__set() {\n    $object = RecordActiveAbstractObject::buildEvenEmpty([]);\n    $this->assertEquals(SN_TIME_SQL, $object->timestampCurrent);\n    $object->timestampCurrent = TEST_VALUE_SQL_DATE;\n    $this->assertEquals(TEST_VALUE_SQL_DATE, $object->timestampCurrent);\n    $this->expectExceptionMessage('{{{ Свойство \\'q\\' не существует в ActiveRecord \\'' . RecordActiveAbstractObject::class . '\\' }}}');\n    $object->q = 5;\n  }\n\n  /**\n   * @covers ::shieldName\n   * @covers ::getDefault\n   * @covers ::__get\n   */\n  public function test__get() {\n    // Testing directly set values\n    $object = RecordActiveAbstractObject::build([\n      'timestampCurrent' => TEST_VALUE_SQL_DATE,\n      'varchar'          => 'varvalue',\n      'null'             => 'qwe',\n    ]);\n    $this->assertEquals(TEST_VALUE_SQL_DATE, $object->__get('timestampCurrent'));\n    $this->assertEquals(TEST_VALUE_SQL_DATE, $object->timestampCurrent);\n\n    // Checking false-positive for field translated to property\n    $this->assertNull($object->__get('timestamp_current'));\n    $this->assertNull($object->timestamp_current);\n\n    $this->assertEquals('varvalue', $object->varchar);\n    $this->assertEquals('qwe', $object->null);\n    $this->assertNull($object->notAProperty);\n\n    // Testing CURRENT_TIMESTAMP and setting values to NULL-defaulted values\n    $object = RecordActiveAbstractObject::buildEvenEmpty([]);\n\n    $this->assertEquals(date(FMT_DATE_TIME_SQL, SN_TIME_NOW), $object->timestampCurrent);\n    $this->assertEquals('', $object->varchar);\n    $this->assertNull($object->null);\n  }\n\n  /**\n   * @covers ::fromRecordList\n   */\n  public function testFromRecordList() {\n    $this->assertEquals([], invokeMethod(RecordActiveAbstractObject::class, 'fromRecordList', ['']));\n    $this->assertEquals([], invokeMethod(RecordActiveAbstractObject::class, 'fromRecordList', [[]]));\n\n    $testable = invokeMethod(RecordActiveAbstractObject::class, 'fromRecordList', [[\n      ['null' => null,],\n      [],\n      ['timestamp_current' => TEST_VALUE_SQL_DATE, 'null' => 'test',],\n    ]]);\n    $this->assertCount(2, $testable);\n    $this->assertEquals(RecordActiveAbstractObject::class, get_class($testable[0]));\n    // Checking that indexes are maintained\n    $this->assertEquals(RecordActiveAbstractObject::class, get_class($testable[2]));\n    $this->assertEquals(TEST_VALUE_SQL_DATE, $testable[2]->timestampCurrent);\n    $this->assertEquals('test', $testable[2]->null);\n\n    $testable = invokeMethod(RecordActiveAbstractObject::class, 'fromRecordList', [\n      [\n        ['null' => null,],\n        [],\n        ['timestampCurrent' => TEST_VALUE_SQL_DATE, 'null' => 'test',],\n      ],\n      RecordActiveAbstractObject::PROPERTIES_TO_FIELDS\n    ]);\n    $this->assertCount(2, $testable);\n    $this->assertEquals(RecordActiveAbstractObject::class, get_class($testable[0]));\n    $this->assertEquals(RecordActiveAbstractObject::class, get_class($testable[2]));\n    $this->assertEquals(TEST_VALUE_SQL_DATE, $testable[2]->timestampCurrent);\n    $this->assertEquals('test', $testable[2]->null);\n  }\n\n}\n"
  },
  {
    "path": "tests/DBAL/Tests/Fixtures/ActiveAbstractObjectDump.php",
    "content": "<?php\n/**\n * Created by Gorlum 13.07.2017 15:33\n */\n\nnamespace DBAL\\Tests\\Fixtures;\n\n\nuse DBAL\\ActiveRecordAbstract;\n\nclass ActiveAbstractObjectDump extends ActiveRecordAbstract {\n  protected static $_tableName = '';\n\n  /**\n   * @return bool\n   */\n  protected function dbInsert() {\n    // TODO: Implement dbInsert() method.\n  }\n\n  /**\n   * Asks DB for last insert ID\n   *\n   * @return int|string\n   */\n  protected function dbLastInsertId() {\n    // TODO: Implement dbLastInsertId() method.\n  }\n\n  /**\n   * @return bool\n   */\n  protected function dbUpdate() {\n    // TODO: Implement dbUpdate() method.\n  }\n\n}\n"
  },
  {
    "path": "tests/DBAL/Tests/Fixtures/RecordActiveAbstractObject.php",
    "content": "<?php\n/**\n * Created by Gorlum 12.07.2017 12:34\n */\n\nnamespace DBAL\\Tests\\Fixtures;\n\n\nuse DBAL\\ActiveRecordAbstract;\nuse DBAL\\DbFieldDescription;\n\nclass RecordActiveAbstractObject extends ActiveRecordAbstract {\n  protected static $_tableName = '';\n  protected static $_fieldsToProperties = [\n    'timestamp_current' => 'timestampCurrent',\n  ];\n\n  protected static function dbGetFieldsDescription() {\n    $result = [];\n    foreach([\n      'id'                =>\n        [\n          'Field'      => 'id',\n          'Type'       => 'bigint(20) unsigned',\n          'Collation'  => null,\n          'Null'       => 'NO',\n          'Key'        => 'PRI',\n          'Default'    => null,\n          'Extra'      => 'auto_increment',\n          'Privileges' => 'select,insert,update,references',\n          'Comment'    => '',\n        ],\n      'timestamp_current' =>\n        [\n          'Field'      => 'timestamp_current',\n          'Type'       => 'timestamp',\n          'Collation'  => null,\n          'Null'       => 'NO',\n          'Key'        => '',\n          'Default'    => 'CURRENT_TIMESTAMP',\n          'Extra'      => 'on update CURRENT_TIMESTAMP',\n          'Privileges' => 'select,insert,update,references',\n          'Comment'    => '',\n        ],\n      'varchar'           =>\n        [\n          'Field'      => 'varchar',\n          'Type'       => 'varchar(32)',\n          'Collation'  => 'utf8_general_ci',\n          'Null'       => 'YES',\n          'Key'        => 'UNI',\n          'Default'    => '',\n          'Extra'      => '',\n          'Privileges' => 'select,insert,update,references',\n          'Comment'    => '',\n        ],\n      'null'              =>\n        [\n          'Field'      => 'null',\n          'Type'       => 'varchar(32)',\n          'Collation'  => 'utf8_general_ci',\n          'Null'       => 'YES',\n          'Key'        => 'UNI',\n          'Default'    => null,\n          'Extra'      => '',\n          'Privileges' => 'select,insert,update,references',\n          'Comment'    => '',\n        ],\n//        'owner'             =>\n//          [\n//            'Field'      => 'ally_owner',\n//            'Type'       => 'bigint(20) unsigned',\n//            'Collation'  => null,\n//            'Null'       => 'YES',\n//            'Key'        => 'MUL',\n//            'Default'    => null,\n//            'Extra'      => '',\n//            'Privileges' => 'select,insert,update,references',\n//            'Comment'    => '',\n//          ],\n//        'unix_time'         =>\n//          [\n//            'Field'      => 'ally_register_time',\n//            'Type'       => 'int(11)',\n//            'Collation'  => null,\n//            'Null'       => 'NO',\n//            'Key'        => '',\n//            'Default'    => '0',\n//            'Extra'      => '',\n//            'Privileges' => 'select,insert,update,references',\n//            'Comment'    => '',\n//          ],\n//        'medium_text'       =>\n//          [\n//            'Field'      => 'ally_description',\n//            'Type'       => 'mediumtext',\n//            'Collation'  => 'utf8_general_ci',\n//            'Null'       => 'YES',\n//            'Key'        => '',\n//            'Default'    => null,\n//            'Extra'      => '',\n//            'Privileges' => 'select,insert,update,references',\n//            'Comment'    => '',\n//          ],\n    ] as $fieldData) {\n      $dbf = new DbFieldDescription();\n      $result[$fieldData['Field']] = $dbf->fromMySqlDescription($fieldData);\n    }\n\n    return $result;\n  }\n\n  /**\n   * @return bool\n   */\n  protected function dbInsert() {\n    // TODO: Implement dbInsert() method.\n  }\n\n  /**\n   * Asks DB for last insert ID\n   *\n   * @return int|string\n   */\n  protected function dbLastInsertId() {\n    // TODO: Implement dbLastInsertId() method.\n  }\n\n  /**\n   * @return bool\n   */\n  protected function dbUpdate() {\n    // TODO: Implement dbUpdate() method.\n  }\n\n}\n"
  },
  {
    "path": "tests/Fixtures/UnitInfo.php",
    "content": "<?php\n/**\n * Created by Gorlum 03.12.2017 14:59\n */\n\nnamespace Fixtures;\n\nclass UnitInfo {\n\n  protected $unitId;\n\n  protected $unitInfo = [];\n\n  public static function build($unitId = 0) {\n    return new static($unitId);\n  }\n\n  public function __construct($unitId) {\n    $this->unitId = $unitId;\n\n    return $this;\n  }\n\n  public function type($unitType) {\n    $this->unitInfo[P_UNIT_TYPE] = $unitType;\n\n    return $this;\n  }\n\n  public function bonus($bonusType, $bonusPower = null) {\n    $this->unitInfo[P_BONUS_TYPE] = $bonusType;\n    if ($bonusPower !== null) {\n      $this->unitInfo[P_BONUS_VALUE] = $bonusPower;\n    }\n\n    return $this;\n  }\n\n  public function asArray() {\n    return $this->unitInfo;\n  }\n\n  /**\n   * Installs unit into current unit lists\n   */\n  public function install() {\n    global $sn_data;\n\n    $sn_data[$this->unitId] = $this->asArray();\n\n    return $this;\n  }\n\n}\n"
  },
  {
    "path": "tests/Meta/Economic/EcoHelperTest.php",
    "content": "<?php\n/**\n * Created by Gorlum 01.10.2017 12:56\n */\n\nnamespace Meta\\Economic;\n\nuse Core\\GlobalContainer;\nuse \\classConfig;\n\n/**\n * Class EcoHelperTest\n * @coversDefaultClass \\Meta\\Economic\\EconomicHelper\n *\n * @package Meta\\Economic\n *\n *\n * Testing cost calculations\n */\nclass EcoHelperTest extends \\PHPUnit_Framework_TestCase {\n\n  /**\n   * @var EconomicHelper $object\n   */\n  protected $object;\n\n  /**\n   * @var classConfig $config\n   */\n  protected $config;\n\n  /**\n   * @var GlobalContainer $gc\n   */\n  protected $gc;\n\n  public function setUp() {\n    parent::setUp();\n\n    $this->config = $this->createMock(classConfig::class);\n    $this->config->method('__get')\n      ->will($this->returnValueMap(\n        [\n          ['rpg_exchange_metal', 1],\n          ['rpg_exchange_crystal', 2],\n          ['rpg_exchange_deuterium', 4],\n          ['rpg_exchange_darkMatter', 4000],\n        ]\n      ));\n\n    $this->gc = $this->createMock(GlobalContainer::class);\n    $this->gc->method('__get')\n      ->willReturn($this->config);\n\n    $this->object = new EconomicHelper($this->gc);\n  }\n\n  public function tearDown() {\n    parent::tearDown();\n\n    unset($this->object);\n    unset($this->config);\n    unset($this->gc);\n  }\n\n  /**\n   * @covers ::__construct\n   */\n  public function test___construct() {\n    $this->object = new EconomicHelper($this->gc);\n  }\n\n  /**\n   * @covers ::getResourcesExchange\n   * @covers ::resetResourcesExchange\n   */\n  public function test_getResourcesExchange() {\n    // First call\n    $this->assertEquals([\n      RES_METAL => 1,\n      RES_CRYSTAL => 2,\n      RES_DEUTERIUM => 4,\n      RES_DARK_MATTER => 4000,\n    ], $this->object->getResourcesExchange());\n\n    // Replacing internal $config property with new mock\n    $this->config = $this->createMock(classConfig::class);\n    $this->config->method('__get')\n      ->will($this->returnValue(0\n      ));\n    setProtectedProperty($this->object, 'config', $this->config);\n\n    // Without reset 2nd should return cached values\n    $this->assertEquals([\n      RES_METAL => 1,\n      RES_CRYSTAL => 2,\n      RES_DEUTERIUM => 4,\n      RES_DARK_MATTER => 4000,\n    ], $this->object->getResourcesExchange());\n\n    // Resetting internal cache\n    $this->object->resetResourcesExchange();\n\n    // Now $config should return 0 for all rates and Helper would replace them with 1-s\n    $this->assertEquals([\n      RES_METAL => 1,\n      RES_CRYSTAL => 1,\n      RES_DEUTERIUM => 1,\n      RES_DARK_MATTER => 1,\n    ], $this->object->getResourcesExchange());\n  }\n\n  /**\n   * @covers ::getResourceExchangeIn\n   */\n  public function test_getResourceExchangeIn() {\n    $this->assertEquals([\n      RES_METAL => 0.25,\n      RES_CRYSTAL => 0.5,\n      RES_DEUTERIUM => 1,\n      RES_DARK_MATTER => 1000,\n    ], $this->object->getResourceExchangeIn(RES_DEUTERIUM));\n\n    $this->assertEquals([\n      RES_METAL => 1,\n      RES_CRYSTAL => 2,\n      RES_DEUTERIUM => 4,\n      RES_DARK_MATTER => 4000,\n    ], $this->object->getResourceExchangeIn(RES_METAL));\n  }\n\n}\n"
  },
  {
    "path": "tests/Tests/Common/Hooker/HookerTest.php",
    "content": "<?php\n/**\n * Created by Gorlum 18.03.2018 16:57\n */\n\nnamespace Tests\\Common\\Hooker;\n\nuse Core\\GlobalContainer;\nuse Common\\Hooker\\Pimp;\nuse Common\\Hooker\\Hooker;\n\n/**\n * Class HookerTest\n * @package Tests\n * @coversDefaultClass \\Common\\Hooker\\Hooker\n */\nclass HookerTest extends \\PHPUnit_Framework_TestCase {\n  /**\n   * @var GlobalContainer $gc\n   */\n  protected $gc;\n\n  /**\n   * @var Pimp $pimp\n   */\n  protected $pimp;\n\n  /**\n   * @var Hooker $object\n   */\n  protected $object;\n\n  public function setUp() {\n    parent::setUp();\n\n    $this->gc = new GlobalContainer();\n    $this->pimp = new Pimp($this->gc);\n\n    $this->object = new Hooker($this->pimp);\n  }\n\n  public function tearDown() {\n    unset($this->object);\n    unset($this->pimp);\n    parent::tearDown();\n  }\n\n\n  /**\n   * @covers ::__construct\n   */\n  public function test__construct() {\n    $this->assertEquals($this->pimp, getPrivatePropertyValue($this->object, 'pimp'));\n  }\n\n  /**\n   * @covers ::addClient\n   */\n  public function testAddClient() {\n    $func1 = function ($prevResult, $arg) {return $arg + 1;};\n    $func2 = function ($prevResult, $arg) {return $prevResult + 2;};\n    $func3 = function ($prevResult, $arg) {return $prevResult + 3;};\n\n    $this->object->addClient('functionNotExists');\n    $this->object->addClient($func1);\n    $this->object->addClient($func2);\n    $this->object->addClient($func3, -1);\n\n    $hookerClients = getPrivatePropertyValue($this->object, 'clients');\n    $this->assertEquals([0 => ['functionNotExists', $func1, $func2], -1 => [$func3]], $hookerClients);\n\n    // Checking that there was sorting applied\n    reset($hookerClients);\n    $this->assertEquals(-1, key($hookerClients));\n  }\n\n  /**\n   * @covers ::serve\n   * @covers ::__invoke\n   */\n  public function testServe() {\n    $this->assertNull($this->object->serve(5));\n    $this->assertNull($this->object->__invoke(5));\n\n    $func1 = function ($prevResult, $arg) {return $arg + 1;};\n    $func2 = function ($prevResult, $arg) {return $prevResult + 2;};\n\n    $this->object->addClient($func1);\n    $this->object->addClient($func2);\n\n    $this->assertEquals(8, $this->object->serve([5]));\n    $this->assertEquals(8, $this->object->__invoke([5]));\n\n    // Testing not an array as argument\n    $this->assertEquals(8, $this->object->serve(5));\n    $this->assertEquals(8, $this->object->__invoke(5));\n  }\n\n}\n"
  },
  {
    "path": "tests/Tests/Common/Hooker/PimpTest.php",
    "content": "<?php\n/**\n * Created by Gorlum 18.03.2018 17:41\n */\n\nnamespace Tests\\Common\\Hooker;\n\nuse Core\\GlobalContainer;\nuse Common\\Hooker\\Hooker;\nuse Common\\Hooker\\Pimp;\n\n/**\n * Class HookerTest\n * @package Tests\n * @coversDefaultClass \\Common\\Hooker\\Pimp\n */\nclass PimpTest extends \\PHPUnit_Framework_TestCase {\n  /**\n   * @var GlobalContainer $gc\n   */\n  protected $gc;\n\n  /**\n   * @var Pimp $pimp\n   */\n  protected $object;\n\n  public function setUp() {\n    parent::setUp();\n\n    $this->gc = new GlobalContainer();\n    $this->object = new Pimp($this->gc);\n  }\n\n  public function tearDown() {\n    unset($this->object);\n    unset($this->gc);\n\n    parent::tearDown();\n  }\n\n  /**\n   * @covers ::__construct\n   */\n  public function test__construct() {\n    $this->assertEquals($this->gc, getPrivatePropertyValue($this->object, 'gc'));\n  }\n\n  /**\n   * @covers ::register\n   */\n  public function testRegister() {\n    $this->object->register(UNIT_TEST_STRING, function () {});\n\n    $hookers = getPrivatePropertyValue($this->object, 'hookers');\n\n    $this->assertTrue(is_array($hookers));\n    $this->assertTrue($hookers[UNIT_TEST_STRING] instanceof Hooker);\n  }\n\n  /**\n   * @covers ::__call\n   */\n  public function test__call() {\n    $this->object->register(UNIT_TEST_STRING, function ($prevResult, $arg) {return $arg + 2;});\n    $this->object->register(UNIT_TEST_STRING, function ($prevResult, $arg) {return $prevResult + 4;});\n\n    $this->assertEquals(7, $this->object->test(1));\n\n    $this->assertNull($this->object->testNotExists(1));\n  }\n\n}\n"
  },
  {
    "path": "tests/Tests/Common/Pimple/Fixtures/Invokable.php",
    "content": "<?php\n\n/*\n * This file is part of Pimple.\n *\n * Copyright (c) 2009 Fabien Potencier\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is furnished\n * to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all\n * copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n\nnamespace Tests\\Common\\Pimple\\Fixtures;\n\nclass Invokable\n{\n    public function __invoke($value = null)\n    {\n        $service = new Service();\n        $service->value = $value;\n\n        return $service;\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Common/Pimple/Fixtures/NonInvokable.php",
    "content": "<?php\n\n/*\n * This file is part of Pimple.\n *\n * Copyright (c) 2009 Fabien Potencier\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is furnished\n * to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all\n * copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n\nnamespace Tests\\Common\\Pimple\\Fixtures;\n\nclass NonInvokable\n{\n    public function __call($a, $b)\n    {\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Common/Pimple/Fixtures/PimpleServiceProvider.php",
    "content": "<?php\n\n/*\n * This file is part of Pimple.\n *\n * Copyright (c) 2009 Fabien Potencier\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is furnished\n * to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all\n * copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n\nnamespace Tests\\Common\\Pimple\\Fixtures;\n\nuse Common\\Pimple\\Container;\nuse Common\\Pimple\\ServiceProviderInterface;\n\nclass PimpleServiceProvider implements ServiceProviderInterface\n{\n    /**\n     * Registers services on the given container.\n     *\n     * This method should only be used to configure services and parameters.\n     * It should not get services.\n     *\n     * @param Container $pimple An Container instance\n     */\n    public function register(Container $pimple)\n    {\n        $pimple['param'] = 'value';\n\n        $pimple['service'] = function () {\n            return new Service();\n        };\n\n        $pimple['factory'] = $pimple->factory(function () {\n            return new Service();\n        });\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Common/Pimple/Fixtures/Service.php",
    "content": "<?php\n\n/*\n * This file is part of Pimple.\n *\n * Copyright (c) 2009 Fabien Potencier\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is furnished\n * to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all\n * copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n\nnamespace Tests\\Common\\Pimple\\Fixtures;\n\n/**\n * @author  Igor Wiedler <igor@wiedler.ch>\n */\nclass Service\n{\n    public $value;\n}\n"
  },
  {
    "path": "tests/Tests/Common/Pimple/PimpleServiceProviderInterfaceTest.php",
    "content": "<?php\n\n/*\n * This file is part of Pimple.\n *\n * Copyright (c) 2009 Fabien Potencier\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is furnished\n * to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all\n * copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n\nnamespace Tests\\Common\\Pimple;\n\nuse Common\\Pimple\\Container;\n\n/**\n * @author  Dominik Zogg <dominik.zogg@gmail.com>\n */\nclass PimpleServiceProviderInterfaceTest extends \\PHPUnit_Framework_TestCase\n{\n    public function testProvider()\n    {\n        $pimple = new Container();\n\n        $pimpleServiceProvider = new Fixtures\\PimpleServiceProvider();\n        $pimpleServiceProvider->register($pimple);\n\n        $this->assertEquals('value', $pimple['param']);\n        $this->assertInstanceOf(\\Tests\\Common\\Pimple\\Fixtures\\Service::class, $pimple['service']);\n\n        $serviceOne = $pimple['factory'];\n        $this->assertInstanceOf(\\Tests\\Common\\Pimple\\Fixtures\\Service::class, $serviceOne);\n\n        $serviceTwo = $pimple['factory'];\n        $this->assertInstanceOf(\\Tests\\Common\\Pimple\\Fixtures\\Service::class, $serviceTwo);\n\n        $this->assertNotSame($serviceOne, $serviceTwo);\n    }\n\n    public function testProviderWithRegisterMethod()\n    {\n        $pimple = new Container();\n\n        $pimple->register(new Fixtures\\PimpleServiceProvider(), array(\n            'anotherParameter' => 'anotherValue',\n        ));\n\n        $this->assertEquals('value', $pimple['param']);\n        $this->assertEquals('anotherValue', $pimple['anotherParameter']);\n\n        $this->assertInstanceOf(\\Tests\\Common\\Pimple\\Fixtures\\Service::class, $pimple['service']);\n\n        $serviceOne = $pimple['factory'];\n        $this->assertInstanceOf(\\Tests\\Common\\Pimple\\Fixtures\\Service::class, $serviceOne);\n\n        $serviceTwo = $pimple['factory'];\n        $this->assertInstanceOf(\\Tests\\Common\\Pimple\\Fixtures\\Service::class, $serviceTwo);\n\n        $this->assertNotSame($serviceOne, $serviceTwo);\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Common/Pimple/PimpleTest.php",
    "content": "<?php\n\n/*\n * This file is part of Pimple.\n *\n * Copyright (c) 2009 Fabien Potencier\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is furnished\n * to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all\n * copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n\nnamespace Tests\\Common\\Pimple;\n\nuse Common\\Pimple\\Container;\nuse Common\\Pimple\\ServiceProviderInterface;\n\n/**\n * @author  Igor Wiedler <igor@wiedler.ch>\n */\nclass PimpleTest extends \\PHPUnit_Framework_TestCase\n{\n    public function testWithString()\n    {\n        $pimple = new Container();\n        $pimple['param'] = 'value';\n\n        $this->assertEquals('value', $pimple['param']);\n    }\n\n    public function testWithClosure()\n    {\n        $pimple = new Container();\n        $pimple['service'] = function () {\n            return new Fixtures\\Service();\n        };\n\n        $this->assertInstanceOf(\\Tests\\Common\\Pimple\\Fixtures\\Service::class, $pimple['service']);\n    }\n\n    public function testServicesShouldBeDifferent()\n    {\n        $pimple = new Container();\n        $pimple['service'] = $pimple->factory(function () {\n            return new Fixtures\\Service();\n        });\n\n        $serviceOne = $pimple['service'];\n        $this->assertInstanceOf(\\Tests\\Common\\Pimple\\Fixtures\\Service::class, $serviceOne);\n\n        $serviceTwo = $pimple['service'];\n        $this->assertInstanceOf(\\Tests\\Common\\Pimple\\Fixtures\\Service::class, $serviceTwo);\n\n        $this->assertNotSame($serviceOne, $serviceTwo);\n    }\n\n    public function testShouldPassContainerAsParameter()\n    {\n        $pimple = new Container();\n        $pimple['service'] = function () {\n            return new Fixtures\\Service();\n        };\n        $pimple['container'] = function ($container) {\n            return $container;\n        };\n\n        $this->assertNotSame($pimple, $pimple['service']);\n        $this->assertSame($pimple, $pimple['container']);\n    }\n\n    public function testIsset()\n    {\n        $pimple = new Container();\n        $pimple['param'] = 'value';\n        $pimple['service'] = function () {\n            return new Fixtures\\Service();\n        };\n\n        $pimple['null'] = null;\n\n        $this->assertTrue(isset($pimple['param']));\n        $this->assertTrue(isset($pimple['service']));\n        $this->assertTrue(isset($pimple['null']));\n        $this->assertFalse(isset($pimple['non_existent']));\n    }\n\n    public function testConstructorInjection()\n    {\n        $params = array('param' => 'value');\n        $pimple = new Container($params);\n\n        $this->assertSame($params['param'], $pimple['param']);\n    }\n\n    /**\n     * @expectedException \\InvalidArgumentException\n     * @expectedExceptionMessage Identifier \"foo\" is not defined.\n     */\n    public function testOffsetGetValidatesKeyIsPresent()\n    {\n        $pimple = new Container();\n        echo $pimple['foo'];\n    }\n\n    public function testOffsetGetHonorsNullValues()\n    {\n        $pimple = new Container();\n        $pimple['foo'] = null;\n        $this->assertNull($pimple['foo']);\n    }\n\n    public function testUnset()\n    {\n        $pimple = new Container();\n        $pimple['param'] = 'value';\n        $pimple['service'] = function () {\n            return new Fixtures\\Service();\n        };\n\n        unset($pimple['param'], $pimple['service']);\n        $this->assertFalse(isset($pimple['param']));\n        $this->assertFalse(isset($pimple['service']));\n    }\n\n    /**\n     * @dataProvider serviceDefinitionProvider\n     */\n    public function testShare($service)\n    {\n        $pimple = new Container();\n        $pimple['shared_service'] = $service;\n\n        $serviceOne = $pimple['shared_service'];\n        $this->assertInstanceOf(\\Tests\\Common\\Pimple\\Fixtures\\Service::class, $serviceOne);\n\n        $serviceTwo = $pimple['shared_service'];\n        $this->assertInstanceOf(\\Tests\\Common\\Pimple\\Fixtures\\Service::class, $serviceTwo);\n\n        $this->assertSame($serviceOne, $serviceTwo);\n    }\n\n    /**\n     * @dataProvider serviceDefinitionProvider\n     */\n    public function testProtect($service)\n    {\n        $pimple = new Container();\n        $pimple['protected'] = $pimple->protect($service);\n\n        $this->assertSame($service, $pimple['protected']);\n    }\n\n    public function testGlobalFunctionNameAsParameterValue()\n    {\n        $pimple = new Container();\n        $pimple['global_function'] = 'strlen';\n        $this->assertSame('strlen', $pimple['global_function']);\n    }\n\n    public function testRaw()\n    {\n        $pimple = new Container();\n        $pimple['service'] = $definition = $pimple->factory(function () { return 'foo'; });\n        $this->assertSame($definition, $pimple->raw('service'));\n    }\n\n    public function testRawHonorsNullValues()\n    {\n        $pimple = new Container();\n        $pimple['foo'] = null;\n        $this->assertNull($pimple->raw('foo'));\n    }\n\n    public function testFluentRegister()\n    {\n        $pimple = new Container();\n        $this->assertSame($pimple, $pimple->register($this->createMock(ServiceProviderInterface::class)));\n    }\n\n    /**\n     * @expectedException \\InvalidArgumentException\n     * @expectedExceptionMessage Identifier \"foo\" is not defined.\n     */\n    public function testRawValidatesKeyIsPresent()\n    {\n        $pimple = new Container();\n        $pimple->raw('foo');\n    }\n\n    /**\n     * @dataProvider serviceDefinitionProvider\n     */\n    public function testExtend($service)\n    {\n        $pimple = new Container();\n        $pimple['shared_service'] = function () {\n            return new Fixtures\\Service();\n        };\n        $pimple['factory_service'] = $pimple->factory(function () {\n            return new Fixtures\\Service();\n        });\n\n        $pimple->extend('shared_service', $service);\n        $serviceOne = $pimple['shared_service'];\n        $this->assertInstanceOf(\\Tests\\Common\\Pimple\\Fixtures\\Service::class, $serviceOne);\n        $serviceTwo = $pimple['shared_service'];\n        $this->assertInstanceOf(\\Tests\\Common\\Pimple\\Fixtures\\Service::class, $serviceTwo);\n        $this->assertSame($serviceOne, $serviceTwo);\n        $this->assertSame($serviceOne->value, $serviceTwo->value);\n\n        $pimple->extend('factory_service', $service);\n        $serviceOne = $pimple['factory_service'];\n        $this->assertInstanceOf(\\Tests\\Common\\Pimple\\Fixtures\\Service::class, $serviceOne);\n        $serviceTwo = $pimple['factory_service'];\n        $this->assertInstanceOf(\\Tests\\Common\\Pimple\\Fixtures\\Service::class, $serviceTwo);\n        $this->assertNotSame($serviceOne, $serviceTwo);\n        $this->assertNotSame($serviceOne->value, $serviceTwo->value);\n    }\n\n    public function testExtendDoesNotLeakWithFactories()\n    {\n        if (extension_loaded('pimple')) {\n            $this->markTestSkipped('Pimple extension does not support this test');\n        }\n        $pimple = new Container();\n\n        $pimple['foo'] = $pimple->factory(function () { return; });\n        $pimple['foo'] = $pimple->extend('foo', function ($foo, $pimple) { return; });\n        unset($pimple['foo']);\n\n        $p = new \\ReflectionProperty($pimple, 'values');\n        $p->setAccessible(true);\n        $this->assertEmpty($p->getValue($pimple));\n\n        $p = new \\ReflectionProperty($pimple, 'factories');\n        $p->setAccessible(true);\n        $this->assertCount(0, $p->getValue($pimple));\n    }\n\n    /**\n     * @expectedException \\InvalidArgumentException\n     * @expectedExceptionMessage Identifier \"foo\" is not defined.\n     */\n    public function testExtendValidatesKeyIsPresent()\n    {\n        $pimple = new Container();\n        $pimple->extend('foo', function () {});\n    }\n\n    public function testKeys()\n    {\n        $pimple = new Container();\n        $pimple['foo'] = 123;\n        $pimple['bar'] = 123;\n\n        $this->assertEquals(array('foo', 'bar'), $pimple->keys());\n    }\n\n    /** @test */\n    public function settingAnInvokableObjectShouldTreatItAsFactory()\n    {\n        $pimple = new Container();\n        $pimple['invokable'] = new Fixtures\\Invokable();\n\n        $this->assertInstanceOf(\\Tests\\Common\\Pimple\\Fixtures\\Service::class, $pimple['invokable']);\n    }\n\n    /** @test */\n    public function settingNonInvokableObjectShouldTreatItAsParameter()\n    {\n        $pimple = new Container();\n        $pimple['non_invokable'] = new Fixtures\\NonInvokable();\n\n        $this->assertInstanceOf(\\Tests\\Common\\Pimple\\Fixtures\\NonInvokable::class, $pimple['non_invokable']);\n    }\n\n    /**\n     * @dataProvider badServiceDefinitionProvider\n     * @expectedException \\InvalidArgumentException\n     * @expectedExceptionMessage Service definition is not a Closure or invokable object.\n     */\n    public function testFactoryFailsForInvalidServiceDefinitions($service)\n    {\n        $pimple = new Container();\n        $pimple->factory($service);\n    }\n\n    /**\n     * @dataProvider badServiceDefinitionProvider\n     * @expectedException \\InvalidArgumentException\n     * @expectedExceptionMessage Callable is not a Closure or invokable object.\n     */\n    public function testProtectFailsForInvalidServiceDefinitions($service)\n    {\n        $pimple = new Container();\n        $pimple->protect($service);\n    }\n\n    /**\n     * @dataProvider badServiceDefinitionProvider\n     * @expectedException \\InvalidArgumentException\n     * @expectedExceptionMessage Identifier \"foo\" does not contain an object definition.\n     */\n    public function testExtendFailsForKeysNotContainingServiceDefinitions($service)\n    {\n        $pimple = new Container();\n        $pimple['foo'] = $service;\n        $pimple->extend('foo', function () {});\n    }\n\n    /**\n     * @dataProvider badServiceDefinitionProvider\n     * @expectedException \\InvalidArgumentException\n     * @expectedExceptionMessage Extension service definition is not a Closure or invokable object.\n     */\n    public function testExtendFailsForInvalidServiceDefinitions($service)\n    {\n        $pimple = new Container();\n        $pimple['foo'] = function () {};\n        $pimple->extend('foo', $service);\n    }\n\n    /**\n     * Provider for invalid service definitions.\n     */\n    public function badServiceDefinitionProvider()\n    {\n        return array(\n          array(123),\n          array(new Fixtures\\NonInvokable()),\n        );\n    }\n\n    /**\n     * Provider for service definitions.\n     */\n    public function serviceDefinitionProvider()\n    {\n        return array(\n            array(function ($value) {\n                $service = new Fixtures\\Service();\n                $service->value = $value;\n\n                return $service;\n            }),\n            array(new Fixtures\\Invokable()),\n        );\n    }\n\n    public function testDefiningNewServiceAfterFreeze()\n    {\n        $pimple = new Container();\n        $pimple['foo'] = function () {\n            return 'foo';\n        };\n        $foo = $pimple['foo'];\n\n        $pimple['bar'] = function () {\n            return 'bar';\n        };\n        $this->assertSame('bar', $pimple['bar']);\n    }\n\n    /**\n     * @expectedException \\RuntimeException\n     * @expectedExceptionMessage Cannot override frozen service \"foo\".\n     */\n    public function testOverridingServiceAfterFreeze()\n    {\n        $pimple = new Container();\n        $pimple['foo'] = function () {\n            return 'foo';\n        };\n        $foo = $pimple['foo'];\n\n        $pimple['foo'] = function () {\n            return 'bar';\n        };\n    }\n\n    public function testRemovingServiceAfterFreeze()\n    {\n        $pimple = new Container();\n        $pimple['foo'] = function () {\n            return 'foo';\n        };\n        $foo = $pimple['foo'];\n\n        unset($pimple['foo']);\n        $pimple['foo'] = function () {\n            return 'bar';\n        };\n        $this->assertSame('bar', $pimple['foo']);\n    }\n\n    public function testExtendingService()\n    {\n        $pimple = new Container();\n        $pimple['foo'] = function () {\n            return 'foo';\n        };\n        $pimple['foo'] = $pimple->extend('foo', function ($foo, $app) {\n            return \"$foo.bar\";\n        });\n        $pimple['foo'] = $pimple->extend('foo', function ($foo, $app) {\n            return \"$foo.baz\";\n        });\n        $this->assertSame('foo.bar.baz', $pimple['foo']);\n    }\n\n    public function testExtendingServiceAfterOtherServiceFreeze()\n    {\n        $pimple = new Container();\n        $pimple['foo'] = function () {\n            return 'foo';\n        };\n        $pimple['bar'] = function () {\n            return 'bar';\n        };\n        $foo = $pimple['foo'];\n\n        $pimple['bar'] = $pimple->extend('bar', function ($bar, $app) {\n            return \"$bar.baz\";\n        });\n        $this->assertSame('bar.baz', $pimple['bar']);\n    }\n}\n"
  },
  {
    "path": "tests/bootstrap.php",
    "content": "<?php\n\nuse \\Core\\Autoloader;\n\ndefine('INSIDE', true);\n\ndefine('SN_TIME_MICRO', microtime(true));\ndefine('SN_MEM_START', memory_get_usage());\n\ndefine('SN_ROOT_PHYSICAL', str_replace(array('\\\\', '//'), '/', dirname(__DIR__) . '/'));\ndefine('SN_ROOT_PHYSICAL_STR_LEN', strlen(SN_ROOT_PHYSICAL)); // mb_strlen ???\n\nrequire_once __DIR__ . '/includes/test_constants.php';\nrequire_once __DIR__ . '/includes/test_functions.php';\n\nrequire_once SN_ROOT_PHYSICAL . 'includes/constants/constants.php';\nrequire_once SN_ROOT_PHYSICAL . 'includes/general/general.php';\n\nrequire_once SN_ROOT_PHYSICAL . 'classes/Core/Autoloader.php';\n\nAutoloader::register('classes/');\nAutoloader::register('classes/UBE/');\n\nAutoloader::register('tests/');\nAutoloader::register('tests/Tests');\nAutoloader::register('tests/Fixtures');\n"
  },
  {
    "path": "tests/classes/AccessAccessorsTest.php",
    "content": "<?php\n\n/**\n * Created by Gorlum 10.02.2017 01:31\n */\n\nuse Common\\AccessAccessors;\nuse Common\\AccessorsV2;\n\n/**\n * Class AccessAccessorsTest\n * @coversDefaultClass \\Common\\AccessAccessors\n */\nclass AccessAccessorsTest extends PHPUnit_Framework_TestCase {\n\n  /**\n   * @var AccessAccessors $object\n   */\n  protected $object;\n\n  /**\n   * @var AccessorsV2 $accessors\n   */\n  protected $accessors;\n\n  public function setUp() {\n    parent::setUp();\n\n    $this->object = new AccessAccessors();\n    $this->accessors = new AccessorsV2();\n\n    $this->accessors->__setConstant = function ($that, $varName) {\n      $that[$varName] = '__setConstant';\n    };\n\n    $this->accessors->__setVar = function ($that, $varName, $value) {\n      $that[$varName] = $value;\n    };\n\n    $this->accessors->__getVar1 = function ($that, $varName) {\n      return $that[$varName];\n    };\n\n    $this->accessors->__setVar2 = function ($that, $varName, $value) {\n      $that[$varName] = $value;\n    };\n    $this->accessors->__getVar2 = function ($that, $varName) {\n      return $that[$varName];\n    };\n\n    $this->accessors->__setVarModifySet = function ($that, $varName, $value) {\n      $that[$varName] = $value . 'Set';\n    };\n\n    $this->accessors->__getVarModifyGet = function ($that, $varName) {\n      return $that[$varName] . 'Get';\n    };\n\n    $this->accessors->__setVarModifySetGet = function ($that, $varName, $value) {\n      $that[$varName] = $value . 'Set';\n    };\n    $this->accessors->__getVarModifySetGet = function ($that, $varName) {\n      return $that[$varName] . 'Get';\n    };\n\n    $this->accessors->call = function ($that) {\n      return 'Called' . $that->VarModifySet;\n    };\n\n    $this->accessors->share('__setShareSet', function ($that, $varName, $value) {\n      $that[$varName] = $value . 'ShareSet';\n    });\n    $this->accessors->share('__getShareGet', function ($that, $varName) {\n      return $that[$varName] . 'ShareGet';\n    });\n    $this->accessors->share('__setShareSetGet', function ($that, $varName, $value) {\n      $that[$varName] = $value . 'ShareSet';\n    });\n    $this->accessors->share('__getShareSetGet', function ($that, $varName) {\n      return $that[$varName] . 'ShareGet';\n    });\n  }\n\n  public function tearDown() {\n    unset($this->object);\n    parent::tearDown();\n  }\n\n  /**\n   * @covers ::__construct\n   * @covers ::isEmpty\n   * @covers ::clear\n   */\n  public function test__get() {\n    $object = new AccessAccessors();\n\n    $this->assertTrue($object->isEmpty());\n    $this->assertFalse(isset($object->test));\n    $this->assertNull($object->test);\n  }\n\n  public function dataSetterGetter() {\n    return array(\n      // Testing constant setter\n      array('Constant', 'test', '__setConstant'),\n      // Testing simple setter w/o getter\n      array('Var', ' ', ' '),\n      // Testing simple getter w/o setter\n      array('Var1', 1, 1),\n      // Testing simple setter and simple getter\n      array('Var2', 2, 2),\n      // Testing setting/getting w/o accessors\n      array('Var3', 3, 3),\n      // Value modification on set\n      array('VarModifySet', 4, '4Set'),\n      // Value modification on get\n      array('VarModifyGet', 5, '5Get'),\n      // Value modification on set and get\n      array('VarModifySetGet', 6, '6SetGet'),\n    );\n  }\n\n  /**\n   * @covers ::setAccessors\n   * @covers ::__get\n   * @covers ::__isset\n   * @covers ::__set\n   * @covers ::__unset\n   * @covers ::__call\n   * @covers ::isEmpty\n   *\n   * @dataProvider dataSetterGetter\n   */\n  public function testSetterGetter($varName, $setValue, $expectedValue) {\n    $this->object->setAccessors($this->accessors);\n\n    $this->assertTrue($this->object->isEmpty());\n\n    $this->assertFalse(isset($this->object->$varName));\n    $this->object->$varName = $setValue;\n    $this->assertEquals($expectedValue, $this->object->$varName);\n    $this->assertTrue(isset($this->object->$varName));\n    unset($this->object->$varName);\n    $this->assertFalse(isset($this->object->$varName));\n\n    $this->assertTrue($this->object->isEmpty());\n  }\n\n\n  public function dataShared() {\n    return array(\n      array('ShareSet', '1ShareSet'),\n      array('ShareGet', '1ShareGet'),\n      array('ShareSetGet', '1ShareSetShareGet'),\n    );\n  }\n\n  /**\n   * Testing shared accessors\n   *\n   * @covers ::setAccessors\n   * @covers ::__get\n   * @covers ::__set\n   * @covers ::__call\n   *\n   * @dataProvider dataShared\n   */\n  public function testShared($varName, $expected) {\n    $this->object->setAccessors($this->accessors);\n\n    $this->object->$varName = 1;\n    $this->assertEquals($expected, $this->object->$varName);\n    $this->object->$varName = 2;\n    $this->assertEquals($expected, $this->object->$varName);\n  }\n\n\n  /**\n   * @covers ::isEmpty\n   * @covers ::clear\n   * @covers ::__get\n   * @covers ::__set\n   * @covers ::__call\n   */\n  public function testCall() {\n    $this->object->setAccessors($this->accessors);\n\n    $this->assertTrue($this->object->isEmpty());\n\n    $this->object->VarModifySet = 1;\n    $this->assertEquals('Called1Set', $this->object->call());\n\n    $this->object->clear();\n    $this->assertTrue($this->object->isEmpty());\n  }\n\n  /**\n   * @covers ::isEmpty\n   * @covers ::clear\n   * @covers ::__get\n   * @covers ::__set\n   * @covers ::__call\n   * @covers ::offsetGet\n   * @covers ::offsetSet\n   * @covers ::offsetExists\n   * @covers ::offsetUnset\n   */\n  public function testArrayAccess() {\n    $this->object->setAccessors($this->accessors);\n\n    $this->assertTrue($this->object->isEmpty());\n\n    // Bypassing getter\n    $this->object->VarModifyGet = 1;\n    $this->assertEquals('1Get', $this->object->VarModifyGet);\n    $this->assertEquals(1, $this->object['VarModifyGet']);\n\n    // Bypassing setter\n    $this->object['VarModifySet'] = 2;\n    $this->assertEquals(2, $this->object->VarModifySet);\n    $this->assertEquals(2, $this->object['VarModifySet']);\n\n    // Checking unset/isset\n    $this->assertTrue(isset($this->object['VarModifySet']));\n    unset($this->object['VarModifySet']);\n    $this->assertFalse(isset($this->object['VarModifySet']));\n  }\n\n}\n"
  },
  {
    "path": "tests/classes/AccessLoggedTest.php",
    "content": "<?php\n/**\n * Created by Gorlum 25.06.2017 13:07\n */\n\nnamespace classes;\n\nuse Common\\AccessLogged;\n\n/**\n * Class AccessLoggedTest\n * @coversDefaultClass \\Common\\AccessLogged\n * @package classes\n */\nclass AccessLoggedTest extends \\PHPUnit_Framework_TestCase {\n\n  /**\n   * @var AccessLogged $object\n   */\n  protected $object;\n\n  public function setUp() {\n    parent::setUp();\n\n    $this->object = new AccessLogged();\n  }\n\n  public function tearDown() {\n    unset($this->object);\n    parent::tearDown();\n  }\n\n  public function test__construct() {\n    // Checking initial state\n    $this->assertTrue($this->object->isEmpty());\n    $this->assertFalse(isset($this->object->test));\n    $this->assertNull($this->object->test);\n    $this->assertAttributeEquals([], 'values', $this->object);\n    $this->assertAttributeEquals([], '_startValues', $this->object);\n    $this->assertAttributeEquals([], '_changes', $this->object);\n    $this->assertAttributeEquals([], '_deltas', $this->object);\n  }\n\n  /**\n   * Checking values setting\n   *\n   * @covers ::__set\n   * @covers ::valueSet\n   * @covers ::blockChange\n   * @covers ::getChanges\n   */\n  public function testSet() {\n    // Checking base __set()\n    $this->object->test = 5;\n    $this->assertFalse($this->object->isEmpty());\n    $this->assertTrue(isset($this->object->test));\n    $this->assertTrue($this->object->__isset('test'));\n    $this->assertEquals(5, $this->object->test);\n    $this->assertAttributeEquals(['test' => 5], 'values', $this->object);\n    $this->assertAttributeEquals(['test' => 5], '_startValues', $this->object);\n    $this->assertAttributeEquals([], '_changes', $this->object);\n    $this->assertAttributeEquals([], '_deltas', $this->object);\n\n    // Checking first value change\n    $this->object->test = 6;\n    $this->assertEquals(6, $this->object->test);\n    $this->assertAttributeEquals(['test' => 6], 'values', $this->object);\n    $this->assertAttributeEquals(['test' => 5], '_startValues', $this->object);\n    $this->assertAttributeEquals(['test' => 6], '_changes', $this->object);\n    $this->assertAttributeEquals([], '_deltas', $this->object);\n\n    // Checking second value change\n    $this->object->test = 7;\n    $this->assertEquals(7, $this->object->test);\n    $this->assertAttributeEquals(['test' => 7], 'values', $this->object);\n    $this->assertAttributeEquals(['test' => 5], '_startValues', $this->object);\n    $this->assertAttributeEquals(['test' => 7], '_changes', $this->object);\n    $this->assertAttributeEquals([], '_deltas', $this->object);\n\n    // Checking changes extraction\n    $this->assertAttributeEquals($this->object->getChanges(), '_changes', $this->object);\n\n    // Checking change block after increment\n    $this->expectExceptionMessage('Common\\AccessLogged::test2 already INCREMENTED/DECREMENTED - can not CHANGE');\n    $this->object->inc()->test2 = 7;\n    $this->object->test2 = 10;\n  }\n\n  /**\n   * Checking values changing\n   *\n   * @covers ::inc\n   * @covers ::dec\n   * @covers ::__set\n   * @covers ::valueSet\n   * @covers ::valueDelta\n   * @covers ::getDeltas\n   * @covers ::blockDelta\n   * @covers ::clear\n   * @covers ::accept\n   * @covers ::reject\n   * @covers ::isChanged\n   */\n  public function testDelta() {\n    // Checking base inc()\n    $this->assertFalse($this->object->isChanged());\n    $this->object->inc()->fromZero = 5;\n    $this->assertTrue($this->object->isChanged());\n    $this->assertFalse($this->object->isEmpty());\n    $this->assertTrue(isset($this->object->fromZero));\n    $this->assertTrue($this->object->__isset('fromZero'));\n    $this->assertEquals(5, $this->object->fromZero);\n    $this->assertAttributeEquals(['fromZero' => 5], 'values', $this->object);\n    $this->assertAttributeEquals(['fromZero' => 0], '_startValues', $this->object);\n    $this->assertAttributeEquals(['fromZero' => 5], '_deltas', $this->object);\n    $this->assertAttributeEquals([], '_changes', $this->object);\n\n    // Checking base dec()\n    $this->object->dec()->fromZeroDec = 5;\n    $this->assertFalse($this->object->isEmpty());\n    $this->assertTrue(isset($this->object->fromZeroDec));\n    $this->assertTrue($this->object->__isset('fromZeroDec'));\n    $this->assertEquals(-5, $this->object->fromZeroDec);\n    $this->assertAttributeEquals(['fromZero' => 5, 'fromZeroDec' => -5], 'values', $this->object);\n    $this->assertAttributeEquals(['fromZero' => 0, 'fromZeroDec' => 0], '_startValues', $this->object);\n    $this->assertAttributeEquals(['fromZero' => 5, 'fromZeroDec' => -5], '_deltas', $this->object);\n    $this->assertAttributeEquals([], '_changes', $this->object);\n\n    // Checking deltas extraction\n    $this->assertAttributeEquals($this->object->getDeltas(), '_deltas', $this->object);\n\n    // Checking accept()\n    $this->object->changed = 7;\n    $this->object->changed = 8;\n    $this->assertAttributeEquals(['changed' => 8], '_changes', $this->object);\n    $this->object->accept();\n    $this->assertAttributeEquals(['fromZero' => 5, 'fromZeroDec' => -5, 'changed' => 8], 'values', $this->object);\n    $this->assertAttributeEquals(['fromZero' => 5, 'fromZeroDec' => -5, 'changed' => 8], '_startValues', $this->object);\n    $this->assertAttributeEquals([], '_deltas', $this->object);\n    $this->assertAttributeEquals([], '_changes', $this->object);\n\n    // Checking clear()\n    $this->object->changed = 9;\n    $this->object->inc()->fromZero = 5;\n    $this->object->dec()->fromZeroDec = 5;\n    $this->assertAttributeEquals(['fromZero' => 10, 'fromZeroDec' => -10, 'changed' => 9], 'values', $this->object);\n    $this->assertAttributeEquals(['fromZero' => 5, 'fromZeroDec' => -5, 'changed' => 8], '_startValues', $this->object);\n    $this->assertAttributeEquals(['fromZero' => 5, 'fromZeroDec' => -5], '_deltas', $this->object);\n    $this->assertAttributeEquals(['changed' => 9], '_changes', $this->object);\n    $this->object->clear();\n\n    $this->assertFalse($this->object->isChanged());\n    $this->object->changed = 3;\n    // TODO - Это тоже должно работать!\n//    $this->assertTrue($this->object->isChanged());\n    $this->assertFalse($this->object->isChanged());\n    $this->object->changed = 5;\n    $this->assertTrue($this->object->isChanged());\n    $this->object->clear();\n\n    $this->test__construct();\n\n    $this->object->changed = 3;\n    $this->object->inc()->integer = 4;\n    $this->assertTrue($this->object->isChanged());\n    $this->assertAttributeEquals(['changed' => 3, 'integer' => 0], '_startValues', $this->object);\n    $this->assertAttributeEquals(['integer' => 4], '_deltas', $this->object);\n    $this->object->accept();\n    $this->assertAttributeEquals(['changed' => 3, 'integer' => 4], '_startValues', $this->object);\n    $this->object->changed = 7;\n    $this->object->inc()->integer = 2;\n    $this->assertEquals(7, $this->object->changed);\n    $this->assertEquals(6, $this->object->integer);\n    $this->object->reject();\n    $this->assertEquals(3, $this->object->changed);\n    $this->assertEquals(4, $this->object->integer);\n    $this->object->clear();\n\n    // Checking delta block after direct change\n    $this->expectExceptionMessage('Common\\AccessLogged::changed already changed - can not use DELTA');\n    // First change is legit - filling startValue\n    $this->object->changed = 7;\n    // Second change makes value unchangeable by delta\n    $this->object->changed = 8;\n    // Raising exception\n    $this->object->inc()->changed = 10;\n  }\n\n}\n"
  },
  {
    "path": "tests/classes/AccessMagicTest.php",
    "content": "<?php\n\n/**\n * Created by Gorlum 10.08.2016 21:29\n */\n\nuse Common\\AccessMagic;\n\n/**\n * Class AccessMagicTest\n * @coversDefaultClass \\Common\\AccessMagic\n */\nclass AccessMagicTest extends PHPUnit_Framework_TestCase {\n\n  /**\n   * @var AccessMagic $object\n   */\n  protected $object;\n\n  public function setUp() {\n    parent::setUp();\n\n    $this->object = new AccessMagic();\n  }\n\n  public function tearDown() {\n    unset($this->object);\n    parent::tearDown();\n  }\n\n  /**\n   * @covers ::__get\n   * @covers ::__isset\n   * @covers ::__set\n   * @covers ::__unset\n   * @covers ::isEmpty\n   * @covers ::asArray\n   * @covers ::clear\n   */\n  public function test__get() {\n    $this->assertTrue($this->object->isEmpty());\n    $this->assertFalse(isset($this->object->test));\n    $this->assertNull($this->object->test);\n\n    $this->object->test = 'value';\n    $this->assertFalse($this->object->isEmpty());\n    $this->assertAttributeEquals(array('test' => 'value'), 'values', $this->object);\n    $this->assertTrue(isset($this->object->test));\n    $this->assertEquals('value', $this->object->test);\n\n    $this->assertAttributeEquals($this->object->asArray(), 'values', $this->object);\n\n    unset($this->object->test);\n    $this->assertTrue($this->object->isEmpty());\n    $this->assertFalse(isset($this->object->test));\n    $this->assertNull($this->object->test);\n    $this->assertAttributeEquals(array(), 'values', $this->object);\n\n    $this->object->test = 'value';\n    $this->object->clear();\n    $this->assertTrue($this->object->isEmpty());\n  }\n\n}\n"
  },
  {
    "path": "tests/classes/AccessorsV2Test.php",
    "content": "<?php\n\n/**\n * Created by Gorlum 10.08.2016 21:29\n */\n\nuse Common\\AccessorsV2;\n\n/**\n * Class AccessorsV2Test\n * @coversDefaultClass \\Common\\AccessorsV2\n */\nclass AccessorsV2Test extends PHPUnit_Framework_TestCase {\n\n  /**\n   * @var AccessorsV2 $object\n   */\n  protected $object;\n\n  public function setUp() {\n    parent::setUp();\n\n    $this->object = new AccessorsV2();\n  }\n\n  public function tearDown() {\n    unset($this->object);\n    parent::tearDown();\n  }\n\n  /**\n   * @covers ::__get\n   * @covers ::__isset\n   * @covers ::__set\n   * @covers ::__unset\n   * @covers ::__call\n   * @covers ::isEmpty\n   */\n  public function test__get() {\n    $this->assertTrue($this->object->isEmpty());\n    $this->assertFalse(isset($this->object->test));\n    $this->assertNull($this->object->test);\n\n    $callable = function () {\n      static $q = '';\n\n      return 'value' . $q++;\n    };\n    $this->object->test = $callable;\n    $this->assertFalse($this->object->isEmpty());\n    $this->assertTrue(isset($this->object->test));\n    $this->assertAttributeEquals(array('test' => $callable), 'accessors', $this->object);\n    $this->assertAttributeEquals(array(), 'shared', $this->object);\n    $this->assertAttributeEquals(array(), 'executed', $this->object);\n\n    $this->assertEquals('value', $this->object->test());\n    $this->assertAttributeEquals(array(), 'shared', $this->object);\n    $this->assertAttributeEquals(array('test' => 'value'), 'executed', $this->object);\n\n    // Checking that executed result is NOT shared - i.e. accessor function called every time\n    $this->assertEquals('value1', $this->object->test());\n    $this->assertAttributeEquals(array(), 'shared', $this->object);\n    $this->assertAttributeEquals(array('test' => 'value1'), 'executed', $this->object);\n\n    unset($this->object->test);\n    $this->assertTrue($this->object->isEmpty());\n    $this->assertFalse(isset($this->object->test));\n    $this->assertNull($this->object->test);\n    $this->assertAttributeEquals(array(), 'accessors', $this->object);\n    $this->assertAttributeEquals(array(), 'shared', $this->object);\n    $this->assertAttributeEquals(array(), 'executed', $this->object);\n\n  }\n\n  /**\n   * Testing shared functions behavior and clear() function\n   *\n   * @covers ::__call\n   * @covers ::share\n   * @covers ::clear\n   */\n  public function testShare() {\n    $callable = function () {\n      static $q = '';\n\n      return 'value' . $q++;\n    };\n\n    $this->object->share('test', $callable);\n    $this->assertFalse($this->object->isEmpty());\n    $this->assertTrue(isset($this->object->test));\n    $this->assertAttributeEquals(array('test' => $callable), 'accessors', $this->object);\n    $this->assertAttributeEquals(array('test' => true), 'shared', $this->object);\n    $this->assertAttributeEquals(array(), 'executed', $this->object);\n\n    $this->assertEquals('value', $this->object->test());\n    $this->assertAttributeEquals(array('test' => true), 'shared', $this->object);\n    $this->assertAttributeEquals(array('test' => 'value'), 'executed', $this->object);\n\n    // Sequental call should NOT change stored value of shared function\n    $this->assertEquals('value', $this->object->test());\n    $this->assertAttributeEquals(array('test' => true), 'shared', $this->object);\n    $this->assertAttributeEquals(array('test' => 'value'), 'executed', $this->object);\n\n    $this->object->clear();\n    $this->assertAttributeEquals(array(), 'accessors', $this->object);\n    $this->assertAttributeEquals(array(), 'shared', $this->object);\n    $this->assertAttributeEquals(array(), 'executed', $this->object);\n  }\n\n  // TODO - method test\n  // TODO - Invoker test\n\n  /**\n   * @covers ::__set\n   */\n  public function testUseFunctionName() {\n    $this->object->test = 'mt_rand';\n    $this->assertFalse($this->object->isEmpty());\n    $this->assertTrue($this->object->test instanceof \\Common\\Invoker);\n    $this->assertAttributeNotEmpty('accessors', $this->object);\n    $this->assertAttributeEquals(array(), 'shared', $this->object);\n    $this->assertAttributeEquals(array(), 'executed', $this->object);\n\n    $this->assertTrue(is_integer($stored = $this->object->test()));\n    $this->assertAttributeEquals(array(), 'shared', $this->object);\n    $this->assertAttributeEquals(array('test' => $stored), 'executed', $this->object);\n  }\n\n\n  public function helperMethod() {\n    return 'value';\n  }\n\n  /**\n   * How setting hook on class method performs\n   *\n   * @covers ::__set\n   */\n  public function testUseClass() {\n    $this->object->test = array($this, 'helperMethod');\n    $this->assertFalse($this->object->isEmpty());\n    $this->assertTrue($this->object->test instanceof \\Common\\Invoker);\n    $this->assertAttributeNotEmpty('accessors', $this->object);\n    $this->assertAttributeEquals(array(), 'shared', $this->object);\n    $this->assertAttributeEquals(array(), 'executed', $this->object);\n\n    $this->assertEquals('value', $this->object->test());\n    $this->assertAttributeEquals(array(), 'shared', $this->object);\n    $this->assertAttributeEquals(array('test' => 'value'), 'executed', $this->object);\n  }\n\n  /**\n   * @covers ::__set\n   */\n  public function testExceptionSettingNotExistingCallable() {\n    $this->setExpectedException(\n      '\\Exception',\n      'Error assigning callable in Common\\AccessorsV2::set()! Callable labeled [test] is not a callable or not accessible in this scope'\n    );\n    $this->object->test = 12345;\n  }\n\n  protected function hiddenMethod() {\n    return 'protected';\n  }\n\n  /**\n   * @covers ::__set\n   */\n  public function testExceptionSettingInaccessibleMethod() {\n    $this->setExpectedException(\n      '\\Exception',\n      'Error assigning callable in Common\\AccessorsV2::set()! Callable labeled [test] is not a callable or not accessible in this scope'\n    );\n    $this->object->test = array($this, 'hiddenMethod');\n  }\n\n  /**\n   * @covers ::__call\n   */\n  public function testExceptionCallNotExistingFunction() {\n    $this->setExpectedException('\\Exception', 'No [test] accessor found on Common\\AccessorsV2::Common\\AccessorsV2::__call');\n    $this->object->test();\n  }\n\n}\n"
  },
  {
    "path": "tests/classes/DbQueryTest.php",
    "content": "<?php\n/**\n * Created by Gorlum 07.08.2016 18:45\n */\n\nuse \\DBAL\\DbQuery;\n\nclass DbEscape {\n  public function db_escape($value) {\n    // Characters encoded are NUL (ASCII 0), \\n, \\r, \\, ', \", and Control-Z.\n    return str_replace(\n      array(\"\\\\\", \"\\0\", \"\\n\", \"\\r\", \"'\", \"\\\"\", \"\\z\",),\n      array('\\\\\\\\', '\\0', '\\n', '\\r', '\\\\\\'', '\\\"', '\\z',),\n      $value\n    );\n  }\n}\n\n/**\n * Class DbQueryTest\n * @coversDefaultClass \\DBAL\\DbQuery\n */\nclass DbQueryTest extends PHPUnit_Framework_TestCase {\n\n  /**\n   * @var DbQuery $object\n   */\n  protected $object;\n\n  protected $db;\n\n  public function setUp() {\n    parent::setUp();\n\n    $this->db = new DbEscape();\n    $this->object = new DbQuery($this->db);\n  }\n\n  public function tearDown() {\n    unset($this->object);\n    parent::tearDown();\n  }\n\n  /**\n   * @covers ::__construct\n   */\n  public function test__construct() {\n    $this->assertAttributeEquals($this->db, 'db', $this->object);\n  }\n\n  /**\n   * @covers ::build\n   */\n  public function testBuild() {\n    $this->assertEquals('DBAL\\DbQuery', get_class($query = DbQuery::build($this->db)));\n    $this->assertAttributeEquals($this->db, 'db', $query);\n  }\n\n  /**\n   * @covers ::escape\n   */\n  public function testEscape() {\n    // TODO - create real DB connection here\n    $this->assertEquals('\\0\\n\\r \\\\\\\\z \\\\\\\\ \\\\\\' \\\"', $result = invokeMethod($this->object, 'escape', array(\"\\0\\n\\r \\z \\\\ ' \\\"\")));\n    $this->assertEquals(19, strlen($result));\n  }\n\n  /**\n   * @covers ::escapeEmulator\n   */\n  public function testEscapeEmulator() {\n    $this->assertEquals('\\0\\n\\r \\\\\\\\z \\\\\\\\ \\\\\\' \\\"', $result = invokeMethod($this->object, 'escapeEmulator', array(\"\\0\\n\\r \\z \\\\ ' \\\"\")));\n    $this->assertEquals(19, strlen($result));\n  }\n\n  /**\n   * @covers ::stringValue\n   */\n  public function testStringValue() {\n    $this->assertEquals('\\'The\\\"test\\\\\\\\of\\\\\\'escape\\'', $result = invokeMethod($this->object, 'stringValue', array('The\"test\\of\\'escape')));\n  }\n\n  /**\n   * @covers ::quote\n   */\n  public function testQuote() {\n    $this->assertEquals('`The\\\"test\\\\\\\\of\\\\\\'escape`', $result = invokeMethod($this->object, 'quote', array('The\"test\\of\\'escape')));\n  }\n\n  /**\n   * @covers ::makeAdjustString\n   */\n  public function testMakeAdjustString() {\n    $this->assertEquals(\"'test'\", $result = invokeMethod($this->object, 'makeAdjustString', array('test', 1)));\n    $this->assertEquals(\"`f1` = `f1` + ('v1')\", $result = invokeMethod($this->object, 'makeAdjustString', array('v1', 'f1')));\n  }\n\n  /**\n   * @covers ::makeFieldEqualValue\n   */\n  public function testMakeFieldEqualValue() {\n    $this->assertEquals(\"'test2'\", $result = invokeMethod($this->object, 'makeFieldEqualValue', array('test2', 1)));\n    $this->assertEquals(\"`f2` = 'v2'\", $result = invokeMethod($this->object, 'makeFieldEqualValue', array('v2', 'f2')));\n  }\n\n  /**\n   * @covers ::quoteTable\n   */\n  public function testQuoteTable() {\n    $this->assertEquals('`{{The\\\"test\\\\\\\\of\\\\\\'escape}}`', $result = invokeMethod($this->object, 'quoteTable', array('The\"test\\of\\'escape')));\n  }\n\n  public function dataCastAsDbValue() {\n    return array(\n      array(10, TYPE_INTEGER, 10, TYPE_INTEGER),\n      array(PHP_INT_MAX, TYPE_INTEGER, PHP_INT_MAX, TYPE_INTEGER),\n\n      array(11.0, TYPE_DOUBLE, 11.0, TYPE_DOUBLE),\n      array(PHP_INT_MAX + 1, TYPE_DOUBLE, PHP_INT_MAX + 1, TYPE_DOUBLE),\n\n      array(0, TYPE_INTEGER, false, TYPE_BOOLEAN),\n      array(1, TYPE_INTEGER, true, TYPE_BOOLEAN),\n\n      array('NULL', TYPE_STRING, null, TYPE_NULL),\n\n      array('\\'\\'', TYPE_STRING, '', TYPE_EMPTY),\n\n      array('\\'\\'', TYPE_STRING, '', TYPE_STRING),\n      array('\\'\\0\\n\\r \\\\\\\\z \\\\\\\\ \\\\\\' \\\"\\'', TYPE_STRING, \"\\0\\n\\r \\z \\\\ ' \\\"\", TYPE_STRING),\n      array('\\'The\\\"test\\\\\\\\of\\\\\\'escape\\'', TYPE_STRING, 'The\"test\\of\\'escape', TYPE_STRING),\n\n      array('\\'a:0:{}\\'', TYPE_STRING, array(), TYPE_ARRAY),\n      array('\\'a:1:{s:4:\\\"k\\\\\\'ey\\\";s:9:\\\"st\\\\\\'ri\\\"n\\\\\\\\g\\\";}\\'', TYPE_STRING, array('k\\'ey' => 'st\\'ri\"n\\\\g'), TYPE_ARRAY),\n    );\n  }\n\n  /**\n   * @covers ::makeValueSafe\n   * @dataProvider dataCastAsDbValue\n   */\n  public function testCastAsDbValue($expected, $type, $value, $originalType) {\n    if ($originalType != TYPE_NULL && $originalType != TYPE_EMPTY) {\n      $this->assertInternalType($originalType, $value);\n    }\n    $this->assertEquals($expected, invokeMethod($this->object, 'makeValueSafe', array($value)));\n    $this->assertInternalType($type, invokeMethod($this->object, 'makeValueSafe', array($value)));\n  }\n\n  /**\n   * @covers ::setTable\n   * @covers ::setOneRow\n   * @covers ::setValues\n   * @covers ::setValuesDanger\n   * @covers ::setAdjust\n   * @covers ::setAdjustDanger\n   * @covers ::setFields\n   * @covers ::setWhereArray\n   * @covers ::setWhereArrayDanger\n   */\n  public function testSetters() {\n    $result = $this->object\n      ->setTable('table')\n      ->setOneRow(DbQuery::DB_RECORD_ONE)\n      ->setValues(array('f1' => 'v1'))\n      ->setValuesDanger(array('f2 <= v1'))\n      ->setAdjust(array('f7' => '7'))\n      ->setAdjustDanger(array('f3' => '3'))\n      ->setFields(array('f4' => 'v4'))\n      ->setWhereArray(array('f5' => '5'))\n      ->setWhereArrayDanger(array('f6' => '6'));\n\n    // Fluid interface is working\n    $this->assertEquals($this->object, $result);\n\n    $this->assertAttributeEquals('table', 'table', $this->object);\n    $this->assertAttributeEquals(DbQuery::DB_RECORD_ONE, 'isOneRow', $this->object);\n    $this->assertAttributeEquals(array('f1' => 'v1'), 'values', $this->object);\n    $this->assertAttributeEquals(array('f2 <= v1'), 'valuesDanger', $this->object);\n    $this->assertAttributeEquals(array('f7' => '7'), 'adjust', $this->object);\n    $this->assertAttributeEquals(array('f3' => '3'), 'adjustDanger', $this->object);\n    $this->assertAttributeEquals(array('f4' => 'v4'), 'fields', $this->object);\n    $this->assertAttributeEquals(array('f5' => '5'), 'where', $this->object);\n    $this->assertAttributeEquals(array('f6' => '6'), 'whereDanger', $this->object);\n  }\n\n  public function dataBuildCommand() {\n    return array(\n      array('', ''),\n      array('SELECT', DbQuery::SELECT),\n      array('INSERT INTO `{{theTable}}`', DbQuery::INSERT),\n      array('INSERT IGNORE INTO `{{theTable}}`', DbQuery::INSERT_IGNORE),\n      array('REPLACE INTO `{{theTable}}`', DbQuery::REPLACE),\n      array('UPDATE `{{theTable}}`', DbQuery::UPDATE),\n      array('DELETE FROM `{{theTable}}`', DbQuery::DELETE),\n    );\n  }\n\n  /**\n   * @covers ::buildCommand\n   * @covers ::__toString\n   * @dataProvider dataBuildCommand\n   */\n  public function testBuildCommand($expected, $command) {\n    $this->object->setTable('theTable');\n//    $property = getPrivateProperty($this->object, 'command');\n//    $property->setValue($this->object, $command);\n    invokeMethod($this->object, 'buildCommand', array($command));\n    $this->assertEquals($expected, $this->object->__toString());\n  }\n\n\n  /**\n   * @covers ::buildSetFields\n   */\n  public function testBuildSetFields() {\n    $this->object\n      ->setValues(array('f1' => 's1', 'f2' => 1))\n      ->setValuesDanger(array('f3 = 5, f4 = \"str4\"  '))\n      ->setAdjust(array('f5' => 1, 'f6' => 'str6', 0 => 'quotedString'))\n      ->setAdjustDanger(array('f7 = f7 + 2  '));\n\n    invokeMethod($this->object, 'buildSetFields', array());\n    $this->assertEquals(\" SET f3 = 5, f4 = \\\"str4\\\"  ,`f1` = 's1',`f2` = 1,f7 = f7 + 2  ,`f5` = `f5` + (1),`f6` = `f6` + ('str6'),'quotedString'\", $this->object->__toString());\n  }\n\n\n  /**\n   * @covers ::buildFieldNames\n   */\n  public function testBuildFieldNames() {\n    $this->object->setFields(array('v1', 'v2'));\n    invokeMethod($this->object, 'buildFieldNames', array());\n    $this->assertEquals('`v1`,`v2`', $this->object->__toString());\n  }\n\n  /**\n   * @covers ::buildValuesVector\n   */\n  public function testBuildValuesVector() {\n    $this->object->setValues(array(\n      array('f1' => 'string', 'f2' => 1, 'f3' => null),\n      array('f1' => 'v1', 'f2' => 2, 'f3' => null),\n    ));\n    $this->object->setValuesDanger('(DANGER!)');\n    invokeMethod($this->object, 'buildValuesVector', array());\n    $this->assertEquals(\"(DANGER!),('string',1,NULL),('v1',2,NULL)\", $this->object->__toString());\n  }\n\n\n  /**\n   * @covers ::buildWhere\n   */\n  public function testBuildWhere() {\n    invokeMethod($this->object, 'buildWhere', array());\n    $this->assertEquals('', $this->object->__toString());\n\n    $this->object\n      ->setWhereArray(array('f1' => 's1', 0 => 'quotedString'))\n      ->setWhereArrayDanger(array('f2' => 's2'));\n    invokeMethod($this->object, 'buildWhere', array());\n    $this->assertEquals(\" WHERE s2 AND `f1` = 's1' AND 'quotedString'\", $this->object->__toString());\n  }\n\n\n  /**\n   * @covers ::buildLimit\n   */\n  public function testBuildLimit() {\n    $this->object->setOneRow(DbQuery::DB_RECORD_ONE);\n    invokeMethod($this->object, 'buildLimit', array());\n    $this->assertEquals(\" LIMIT 1\", $this->object->__toString());\n  }\n\n\n  /**\n   * @covers ::delete\n   */\n  public function testDelete() {\n    $this->object\n      ->setTable('aT')\n      ->setWhereArray(array('f1' => 's1', 0 => 'ANDinQuote'))\n      ->setWhereArrayDanger(array('f2' => 'f3 = 1'))\n      ->setOneRow(DbQuery::DB_RECORD_ONE);\n    $this->assertEquals(\"DELETE FROM `{{aT}}` WHERE f3 = 1 AND `f1` = 's1' AND 'ANDinQuote' LIMIT 1\", $this->object->delete());\n  }\n\n\n  /**\n   * @covers ::update\n   */\n  public function testUpdate() {\n    $this->object\n      ->setTable('aT')\n      ->setValues(array('f1' => 's1'))\n      ->setValuesDanger(array(' f3 = 5 '))\n      ->setAdjust(array('f5' => 1, 'f6' => -1))\n      ->setAdjustDanger(array(' f7 = f7 + 2 '))\n      ->setWhereArray(array('f1' => 's1'))\n      ->setWhereArrayDanger(array('f2' => 'f3 = 1'))\n      ->setOneRow(DbQuery::DB_RECORD_ONE);\n    $this->assertEquals(\n      \"UPDATE `{{aT}}`\" .\n      \" SET  f3 = 5 ,`f1` = 's1',\" .\n      \" f7 = f7 + 2 ,`f5` = `f5` + (1),`f6` = `f6` + (-1)\" .\n      \" WHERE f3 = 1 AND `f1` = 's1' LIMIT 1\",\n      $this->object->update()\n    );\n  }\n\n\n\n  /**\n   * Insert SET test\n   *\n   * @covers ::insert\n   * @covers ::setInsertCommand\n   */\n  public function testInsertSet() {\n    $this->object\n      ->setTable('aT')\n      ->setValues(array('f1' => 's1'))\n      ->setValuesDanger(array(' f3 = 5 '))\n      ->setAdjust(array('f5' => 1, 'f6' => -1))\n      ->setAdjustDanger(array(' f7 = f7 + 2 '));\n    $this->assertEquals(\n      \"INSERT INTO `{{aT}}`\" .\n      \" SET  f3 = 5 ,`f1` = 's1',\" .\n      \" f7 = f7 + 2 ,`f5` = `f5` + (1),`f6` = `f6` + (-1)\",\n      invokeMethod($this->object, 'insert', array(DbQuery::DB_INSERT_PLAIN))\n    );\n\n    $this->object->setTable('aT2')->setAdjust(array('f25' => 21, 'f26' => -21));\n    $this->assertEquals(\n      \"INSERT IGNORE INTO `{{aT2}}`\" .\n      \" SET  f3 = 5 ,`f1` = 's1',\" .\n      \" f7 = f7 + 2 ,`f5` = `f5` + (1),`f6` = `f6` + (-1),\" .\n      \"`f25` = `f25` + (21),`f26` = `f26` + (-21)\",\n      invokeMethod($this->object, 'insert', array(DbQuery::DB_INSERT_IGNORE))\n    );\n\n    $this->object->setTable('aT2')->setAdjust(array('f36' => -31));\n    $this->assertEquals(\n      \"REPLACE INTO `{{aT2}}`\" .\n      \" SET  f3 = 5 ,`f1` = 's1',\" .\n      \" f7 = f7 + 2 ,`f5` = `f5` + (1),`f6` = `f6` + (-1),\" .\n      \"`f25` = `f25` + (21),`f26` = `f26` + (-21),`f36` = `f36` + (-31)\",\n      invokeMethod($this->object, 'insert', array(DbQuery::DB_INSERT_REPLACE))\n    );\n  }\n\n\n  /**\n   * Insert BATCH test\n   *\n   * @covers ::insert\n   * @covers ::setInsertCommand\n   */\n  public function testInsertBatch() {\n    $this->object\n      ->setTable('aT')\n      ->setFields(array('f1', 'f2'))\n      ->setValues(array(array('v1', 1)))\n    ;\n    $this->assertEquals(\n      \"INSERT INTO `{{aT}}` (`f1`,`f2`) VALUES \" .\n      \"('v1',1)\"\n      ,\n      invokeMethod($this->object, 'insert', array(DbQuery::DB_INSERT_PLAIN))\n    );\n\n    $this->object\n      ->setTable('aT2')\n      ->setValues(array(array('v2', 2)))\n    ;\n    $this->assertEquals(\n      \"INSERT IGNORE INTO `{{aT2}}` (`f1`,`f2`) VALUES \" .\n      \"('v1',1),('v2',2)\"\n      ,\n      invokeMethod($this->object, 'insert', array(DbQuery::DB_INSERT_IGNORE))\n    );\n\n    $this->object\n      ->setTable('aT2')\n      ->setValues(array(array('v3', 3),array('v4', 4)))\n      ->setValuesDanger(array('this IS danger!'))\n    ;\n    $this->assertEquals(\n      \"REPLACE INTO `{{aT2}}` (`f1`,`f2`) VALUES \" .\n      \"this IS danger!,('v1',1),('v2',2),('v3',3),('v4',4)\"\n      ,\n      invokeMethod($this->object, 'insert', array(DbQuery::DB_INSERT_REPLACE))\n    );\n  }\n\n  /**\n   * @covers ::select\n   */\n  public function testSelect() {\n    $this->object\n      ->setTable('aT')\n      // ->setFields(array('f1', 'f2')) // TODO - unused\n      ->setWhereArray(array('f1' => 's1'))\n      ->setWhereArrayDanger(array('f2' => '(f3 = 1 OR f3 = 2)'))\n      ->setOneRow(DbQuery::DB_RECORD_ONE)\n    ;\n    $this->assertEquals(\n      \"SELECT * FROM `{{aT}}` WHERE (f3 = 1 OR f3 = 2) AND `f1` = 's1' LIMIT 1\",\n      $this->object->select()\n    );\n  }\n\n}\n"
  },
  {
    "path": "tests/classes/HelperArrayTest.php",
    "content": "<?php\n\n/**\n * Class HelperArrayTest\n *\n * @coversDefaultClass HelperArray\n */\nclass HelperArrayTest extends PHPUnit_Framework_TestCase {\n\n  public function setUp() {\n    parent::setUp(); // TODO: Change the autogenerated stub\n  }\n\n  public function tearDown() {\n    parent::tearDown(); // TODO: Change the autogenerated stub\n  }\n\n  public function dataStringToArray() {\n    return array(\n      array(123, ',', array()), // Only string would be converted to array\n      array('', ',', array()), // Empty data would be converted to empty array()\n      array('123', ',', array('123')), // One element string\n      array('123,234', ',', array('123', '234')), // Two element string\n      array('123;234', ';', array('123', '234')), // Specified delimiter\n    );\n  }\n\n  /**\n   * @param string $value\n   * @param string $delimiter\n   * @param array  $expected\n   *\n   * @dataProvider dataStringToArray\n   *\n   * @covers ::stringToArray\n   */\n  public function testStringToArray($value, $delimiter, $expected) {\n    $this->assertEquals($expected, HelperArray::stringToArray($value, $delimiter));\n  }\n\n\n  public function dataMakeArray() {\n    return array(\n      array(array('test'), 0, array('test')), // Straightforward: just array\n      array('test', 0, array(0 => 'test')), // Value with default index\n      array('test', 1, array(1 => 'test')), // Value with non-default index\n    );\n  }\n\n  /**\n   * @dataProvider dataMakeArray\n   *\n   * @covers ::makeArrayRef\n   */\n  public function testMakeArrayRef($value, $index, $expected) {\n    HelperArray::makeArrayRef($value, $index);\n    $this->assertEquals($expected, $value);\n  }\n\n  /**\n   * @dataProvider dataMakeArray\n   *\n   * @covers ::makeArray\n   */\n  public function testMakeArray($value, $index, $expected) {\n    $this->assertEquals($expected, HelperArray::makeArray($value, $index));\n  }\n\n  public function dataFilter() {\n    $callback = function ($value) {\n      return !empty($value);\n    };\n\n    return array(\n      array(1, $callback, array()), // Not array\n      array(array(), $callback, array()), // Empty array\n      array(array(''), $callback, array()), // Not empty array with one empty element\n      array(array('0', ''), $callback, array()), // Not empty array with both filterable elements\n      array(array('test', ''), $callback, array(0 => 'test')), // Not empty array with one filterable element\n      array(array('test1', '', 'test2'), $callback, array(0 => 'test1', 1 => 'test2')),\n      array(array('test1', 'test', 'test2'), $callback, array(0 => 'test1', 1 => 'test', 2 => 'test2')),\n    );\n  }\n\n  /**\n   * @param mixed    $value\n   * @param callable $callback\n   * @param array    $expected\n   *\n   * @dataProvider dataFilter\n   *\n   * @covers ::filter\n   */\n  public function testFilter($value, $callback, $expected) {\n    $this->assertEquals($expected, HelperArray::filter($value, $callback));\n  }\n\n  /**\n   * @param mixed    $value\n   * @param callable $callback\n   * @param array    $expected\n   *\n   * @dataProvider dataFilter\n   *\n   * @covers ::filterEmpty\n   * @covers       Validators::isNotEmpty\n   * @covers       Validators::isNotEmptyByRef\n   * @covers ::filter\n   */\n  public function testFilterEmpty($value, $callback, $expected) {\n    $this->assertEquals($expected, HelperArray::filterEmpty($value));\n  }\n\n\n  /**\n   * @covers ::stringToArrayFilterEmpty\n   * @covers ::stringToArray\n   * @covers ::filterEmpty\n   * @covers Validators::isNotEmpty\n   * @covers ::filter\n   */\n  public function testStringToArrayFilterEmpty() {\n    // Not string\n    $this->assertEquals(array(), HelperArray::stringToArrayFilterEmpty(1));\n\n    // Empty string\n    $this->assertEquals(array(), HelperArray::stringToArrayFilterEmpty(''));\n  }\n\n  /**\n   * @covers ::merge\n   */\n  public function testMerge() {\n    // Testing ARRAY_REPLACE mode\n    $array1 = array('a' => 'b');\n    $array2 = array('c' => 'd', 0 => 10);\n    HelperArray::merge($array1, $array2);\n    $this->assertEquals($array2, $array1);\n\n    // Testing ARRAY_MERGE mode\n    // String keyed value should be replaced\n    // Integer keyed value should be added and integer keys should be recalculated\n    $array2 = array('c' => 'e', 0 => 20);\n    HelperArray::merge($array1, $array2, HelperArray::MERGE_PHP);\n    $this->assertEquals(array('c' => 'e', 0 => 10, 1 => 20), $array1);\n\n    // First array is not an array\n    $array1 = 1;\n    // Element int(20) should have key recalculated and become key(2)\n    $array2 = array('c' => 'd', 1 => 10, 5 => 20);\n    HelperArray::merge($array1, $array2, HelperArray::MERGE_PHP);\n    $this->assertEquals(array(0 => 1, 'c' => 'd', 1 => 10, 2 => 20), $array1);\n\n  }\n\n  /**\n   * @covers ::keyExistsOr\n   */\n  public function testKeyExistsOr() {\n    $array = array('a' => 'b');\n    $this->assertEquals('b', HelperArray::keyExistsOr($array, 'a', 'q'));\n    $this->assertEquals('q', HelperArray::keyExistsOr($array, 'c', 'q'));\n  }\n\n  public function dataCloneDeep() {\n    return array(\n      array(HelperArray::CLONE_ARRAY_NONE, true, 2, true, 6),\n      array(HelperArray::CLONE_ARRAY_SHALLOW, false, 1, true, 6),\n      array(HelperArray::CLONE_ARRAY_RECURSIVE, false, 1, false, 5),\n    );\n  }\n\n  protected function helpCloneDeep($source, $destination, $isSameObject, $value, $valueAfterChange) {\n    // Checking 0-level object\n    $this->assertEquals($isSameObject, $source === $destination);\n\n    // Checking property 'test' of 0-level object\n    $this->assertEquals($source->test, $destination->test);\n    $this->assertEquals($value, $destination->test);\n    $this->assertEquals($value, $source->test);\n\n    // Changing property of 0-level and checking result\n    $destination->test = $value + 1;\n    $this->assertEquals($value + 1, $destination->test);\n    $this->assertEquals($valueAfterChange, $source->test);\n  }\n\n\n  /**\n   * @dataProvider dataCloneDeep\n   *\n   * @covers ::cloneDeep\n   */\n  public function testCloneDeep($deep, $l0Eq, $l0Prop, $l1Eq, $l1Prop) {\n    // Init values\n    $source = array(new StdClass(), array(new StdClass()));\n    $source[0]->test = 1;\n    $source[1][0]->test = 5;\n    $destination = $source;\n\n    // Making clone\n    HelperArray::cloneDeep($destination, $deep);\n    $this->helpCloneDeep($source[0], $destination[0], $l0Eq, 1, $l0Prop);\n    $this->helpCloneDeep($source[1][0], $destination[1][0], $l1Eq, 5, $l1Prop);\n  }\n\n}\n"
  },
  {
    "path": "tests/classes/InvokerTest.php",
    "content": "<?php\n\n/**\n * Created by Gorlum 10.08.2016 21:29\n */\n\nuse Common\\Invoker;\n\n/**\n * Class InvokerTest\n * @coversDefaultClass \\Common\\Invoker\n */\nclass InvokerTest extends PHPUnit_Framework_TestCase {\n\n  public function publicFunction() {\n    return 'public';\n  }\n\n  protected function protectedFunction() {\n    return 'protected';\n  }\n\n  /**\n   * @covers ::build\n   * @covers ::__construct\n   * @covers ::__invoke\n   */\n  public function testBuild() {\n    $t = Invoker::build(12345);\n    $this->assertNull($t());\n\n    $t = Invoker::build(array($this, 'protectedFunction'));\n    $this->assertNull($t());\n\n\n    $t = Invoker::build(function () { return 'lambda';});\n    $this->assertEquals('lambda', $t());\n\n    $t = Invoker::build(array($this, 'publicFunction'));\n    $this->assertEquals('public', $t());\n\n    $t = Invoker::build('mt_rand');\n    $this->assertTrue(is_integer($t()));\n\n//    $t = Invoker::build('array_unshift');\n//    $array = array('q');\n//    $this->assertEquals(2, $t($array, 'w'));\n//    // TODO - uncomment for PHP > 5.3\n//    $this->assertEquals(array('w', 'q'), $array);\n  }\n\n}\n"
  },
  {
    "path": "tests/classes/ToolsTest.php",
    "content": "<?php\n\n/**\n * Created by Gorlum 01.03.2017 14:03\n */\n\n/**\n * Class ToolsTest\n *\n * @coversDefaultClass Tools\n */\nclass ToolsTest extends PHPUnit_Framework_TestCase {\n  public function setUp() {\n    parent::setUp();\n  }\n\n  public function tearDown() {\n    parent::tearDown();\n  }\n\n  public function dataFillPercentStyle() {\n    return array(\n      array(100, 101, 'error'),\n      array(100, 100, 'warning'),\n      array(100, 99, 'warning'),\n      array(100, 91, 'warning'),\n      array(100, 90, 'notice'),\n      array(100, 89, 'notice'),\n      array(100, 76, 'notice'),\n      array(100, 75, 'info'),\n      array(100, 74, 'info'),\n      array(100, 51, 'info'),\n      array(100, 50, 'ok'),\n      array(100, 49, 'ok'),\n      array(100, 0, 'ok'),\n\n      array(0, 1, 'error'),\n      array(0, 0, 'zero_number'),\n      array(0, -1, 'zero_number'),\n    );\n  }\n\n  /**\n   * Test for style select in fillPercentStyle\n   *\n   * @covers ::fillPercentStyle\n   * @dataProvider dataFillPercentStyle\n   */\n  public function testFillPercentStyle($number, $sample, $expected) {\n    $this->assertEquals($expected, Tools::fillPercentStyle($number, $sample));\n  }\n\n  /**\n   * Test for span-wrapping of fillPercentStyle\n   *\n   * @covers ::numberPercentSpan\n   */\n  public function testNumberStyleSpan() {\n    $this->assertEquals(\"<span class=\\\"ok\\\">100</span>\", Tools::numberPercentSpan(100, 10));\n  }\n\n}\n"
  },
  {
    "path": "tests/classes/ValidatorsTest.php",
    "content": "<?php\n\n/**\n * Class ValidatorsTest\n *\n * @coversDefaultClass Validators\n */\nclass ValidatorsTest extends PHPUnit_Framework_TestCase {\n\n  public function setUp() {\n    parent::setUp();\n  }\n\n  public function tearDown() {\n    parent::tearDown();\n  }\n\n  /**\n   * @covers ::isNotEmpty\n   * @covers ::isNotEmptyByRef\n   */\n  public function testIsNotEmpty() {\n    $tested = new Validators();\n\n    // Not empty\n    $this->assertTrue(invokeMethod($tested, 'isNotEmpty', array('1')));\n\n    // Empty\n    $this->assertFalse(invokeMethod($tested, 'isNotEmpty', array('0')));\n  }\n\n}\n"
  },
  {
    "path": "tests/classes/VectorTest.php",
    "content": "<?php\n\nuse Common\\Vector;\n\n/**\n * Class VectorTest\n *\n * @coversDefaultClass Common\\Vector\n */\nclass VectorTest extends PHPUnit_Framework_TestCase {\n\n  /**\n   * @var Vector $object\n   */\n  protected $object;\n  /**\n   * @var stdClass $config\n   */\n  protected $config;\n\n  public function setUp() {\n    parent::setUp();\n    $this->object = new Vector();\n\n    $this->object->type = PT_PLANET;\n\n    $this->config = new stdClass();\n    $this->config->game_maxGalaxy = 2;\n    $this->config->game_maxSystem = 4;\n    $this->config->game_maxPlanet = 6;\n    $this->config->uni_galaxy_distance = 20000;\n\n    Vector::_staticInit($this->config);\n  }\n\n  public function tearDown() {\n    unset($this->object);\n    parent::tearDown();\n  }\n\n  /**\n   * @covers ::_staticInit\n   */\n  public function test_staticInit() {\n    $className = get_class($this->object);\n    $this->assertAttributeEquals(true, '_isStaticInit', $className);\n    $this->assertAttributeEquals(2, 'knownGalaxies', $className);\n    $this->assertAttributeEquals(4, 'knownSystems', $className);\n    $this->assertAttributeEquals(6, 'knownPlanets', $className);\n    $this->assertAttributeEquals(20000, 'galaxyDistance', $className);\n\n    $this->assertAttributeEquals(PT_PLANET, 'type', $this->object);\n  }\n\n  /**\n   * @covers ::readFromVector\n   */\n  public function testReadFromVector() {\n    $vector = new Vector(1, 3, 5, PT_MOON);\n    $this->object->readFromVector($vector);\n\n    $this->assertAttributeEquals(1, 'galaxy', $this->object);\n    $this->assertAttributeEquals(3, 'system', $this->object);\n    $this->assertAttributeEquals(5, 'planet', $this->object);\n    $this->assertAttributeEquals(PT_MOON, 'type', $this->object);\n  }\n\n  /**\n   * @covers ::getParamInt\n   */\n  // TODO\n  public function testGetParamInt() {\n  }\n\n  /**\n   * @covers ::readFromParamFleets\n   */\n  // TODO\n  public function testReadFromParamFleets() {\n  }\n\n  public function data__construct() {\n    $vector = new Vector(4, 5, 6, PT_PLANET);\n\n    return array(\n      array(1, 2, 3, PT_MOON, 1, 2, 3, PT_MOON),\n      array(Vector::READ_VECTOR, $vector, 3, PT_MOON, 4, 5, 6, PT_PLANET),\n      // TODO\n//      array(Vector::READ_PARAMS_FLEET, array('galaxy' => 90, 'system' => 91, 'planet' => 92, 'planet_type' => 93,), 99, PT_MOON, 4, 5, 6, PT_PLANET),\n    );\n  }\n\n  /**\n   * @dataProvider data__construct\n   *\n   * @covers ::__construct\n   */\n  public function test__construct($galaxy, $system, $planet, $type, $g, $s, $p, $t) {\n    $this->object = new Vector($galaxy, $system, $planet, $type);\n\n    $this->assertAttributeEquals($g, 'galaxy', $this->object);\n    $this->assertAttributeEquals($s, 'system', $this->object);\n    $this->assertAttributeEquals($p, 'planet', $this->object);\n    $this->assertAttributeEquals($t, 'type', $this->object);\n  }\n\n  public function dataDistance() {\n    return array(\n      // Checking that $returnZero works on same planet\n      array(new Vector(2, 4, 6, PT_PLANET), new Vector(2, 4, 6, PT_PLANET), true, 0),\n      array(new Vector(2, 4, 6, PT_PLANET), new Vector(2, 4, 6, PT_PLANET), false, 5),\n      array(new Vector(2, 4, 6, PT_PLANET), new Vector(2, 4, 6, PT_MOON), true, 5),\n      array(new Vector(2, 4, 6, PT_PLANET), new Vector(2, 4, 6, PT_MOON), false, 5),\n      array(new Vector(2, 4, 6, PT_PLANET), new Vector(2, 4, 6, PT_DEBRIS), true, 5),\n      array(new Vector(2, 4, 6, PT_PLANET), new Vector(2, 4, 6, PT_DEBRIS), false, 5),\n\n      array(new Vector(2, 4, 6, PT_PLANET), new Vector(2, 4, 7, PT_PLANET), true, 1005),\n      array(new Vector(2, 4, 6, PT_PLANET), new Vector(2, 4, 7, PT_PLANET), false, 1005),\n      array(new Vector(2, 4, 6, PT_PLANET), new Vector(2, 4, 7, PT_MOON), true, 1005),\n      array(new Vector(2, 4, 6, PT_PLANET), new Vector(2, 4, 7, PT_MOON), false, 1005),\n      array(new Vector(2, 4, 6, PT_PLANET), new Vector(2, 4, 7, PT_DEBRIS), true, 1005),\n      array(new Vector(2, 4, 6, PT_PLANET), new Vector(2, 4, 7, PT_DEBRIS), false, 1005),\n\n      array(new Vector(2, 4, 6, PT_PLANET), new Vector(2, 4, 5, PT_PLANET), true, 1005),\n      array(new Vector(2, 4, 6, PT_PLANET), new Vector(2, 4, 5, PT_PLANET), false, 1005),\n      array(new Vector(2, 4, 6, PT_PLANET), new Vector(2, 4, 5, PT_MOON), true, 1005),\n      array(new Vector(2, 4, 6, PT_PLANET), new Vector(2, 4, 5, PT_MOON), false, 1005),\n      array(new Vector(2, 4, 6, PT_PLANET), new Vector(2, 4, 5, PT_DEBRIS), true, 1005),\n      array(new Vector(2, 4, 6, PT_PLANET), new Vector(2, 4, 5, PT_DEBRIS), false, 1005),\n\n      array(new Vector(2, 4, 6, PT_PLANET), new Vector(2, 4, 8, PT_PLANET), true, 1010),\n      array(new Vector(2, 4, 6, PT_PLANET), new Vector(2, 4, 8, PT_PLANET), false, 1010),\n      array(new Vector(2, 4, 6, PT_PLANET), new Vector(2, 4, 8, PT_MOON), true, 1010),\n      array(new Vector(2, 4, 6, PT_PLANET), new Vector(2, 4, 8, PT_MOON), false, 1010),\n      array(new Vector(2, 4, 6, PT_PLANET), new Vector(2, 4, 8, PT_DEBRIS), true, 1010),\n      array(new Vector(2, 4, 6, PT_PLANET), new Vector(2, 4, 8, PT_DEBRIS), false, 1010),\n\n      array(new Vector(2, 4, 6, PT_PLANET), new Vector(2, 5, 6, PT_PLANET), true, 2795),\n      array(new Vector(2, 4, 6, PT_PLANET), new Vector(2, 5, 6, PT_PLANET), false, 2795),\n      array(new Vector(2, 4, 6, PT_PLANET), new Vector(2, 5, 6, PT_MOON), true, 2795),\n      array(new Vector(2, 4, 6, PT_PLANET), new Vector(2, 5, 6, PT_MOON), false, 2795),\n      array(new Vector(2, 4, 6, PT_PLANET), new Vector(2, 5, 6, PT_DEBRIS), true, 2795),\n      array(new Vector(2, 4, 6, PT_PLANET), new Vector(2, 5, 6, PT_DEBRIS), false, 2795),\n\n      array(new Vector(2, 4, 6, PT_PLANET), new Vector(2, 3, 6, PT_PLANET), true, 2795),\n      array(new Vector(2, 4, 6, PT_PLANET), new Vector(2, 3, 6, PT_PLANET), false, 2795),\n      array(new Vector(2, 4, 6, PT_PLANET), new Vector(2, 3, 6, PT_MOON), true, 2795),\n      array(new Vector(2, 4, 6, PT_PLANET), new Vector(2, 3, 6, PT_MOON), false, 2795),\n      array(new Vector(2, 4, 6, PT_PLANET), new Vector(2, 3, 6, PT_DEBRIS), true, 2795),\n      array(new Vector(2, 4, 6, PT_PLANET), new Vector(2, 3, 6, PT_DEBRIS), false, 2795),\n\n      array(new Vector(2, 4, 6, PT_PLANET), new Vector(2, 2, 6, PT_PLANET), true, 2890),\n      array(new Vector(2, 4, 6, PT_PLANET), new Vector(2, 2, 6, PT_PLANET), false, 2890),\n      array(new Vector(2, 4, 6, PT_PLANET), new Vector(2, 2, 6, PT_MOON), true, 2890),\n      array(new Vector(2, 4, 6, PT_PLANET), new Vector(2, 2, 6, PT_MOON), false, 2890),\n      array(new Vector(2, 4, 6, PT_PLANET), new Vector(2, 2, 6, PT_DEBRIS), true, 2890),\n      array(new Vector(2, 4, 6, PT_PLANET), new Vector(2, 2, 6, PT_DEBRIS), false, 2890),\n\n      array(new Vector(2, 4, 6, PT_PLANET), new Vector(1, 4, 6, PT_PLANET), true, 20000),\n      array(new Vector(2, 4, 6, PT_PLANET), new Vector(1, 4, 6, PT_PLANET), false, 20000),\n      array(new Vector(2, 4, 6, PT_PLANET), new Vector(1, 4, 6, PT_MOON), true, 20000),\n      array(new Vector(2, 4, 6, PT_PLANET), new Vector(1, 4, 6, PT_MOON), false, 20000),\n      array(new Vector(2, 4, 6, PT_PLANET), new Vector(1, 4, 6, PT_DEBRIS), true, 20000),\n      array(new Vector(2, 4, 6, PT_PLANET), new Vector(1, 4, 6, PT_DEBRIS), false, 20000),\n\n      array(new Vector(2, 4, 6, PT_PLANET), new Vector(3, 4, 6, PT_PLANET), true, 20000),\n      array(new Vector(2, 4, 6, PT_PLANET), new Vector(3, 4, 6, PT_PLANET), false, 20000),\n      array(new Vector(2, 4, 6, PT_PLANET), new Vector(3, 4, 6, PT_MOON), true, 20000),\n      array(new Vector(2, 4, 6, PT_PLANET), new Vector(3, 4, 6, PT_MOON), false, 20000),\n      array(new Vector(2, 4, 6, PT_PLANET), new Vector(3, 4, 6, PT_DEBRIS), true, 20000),\n      array(new Vector(2, 4, 6, PT_PLANET), new Vector(3, 4, 6, PT_DEBRIS), false, 20000),\n\n      array(new Vector(2, 4, 6, PT_PLANET), new Vector(4, 4, 6, PT_PLANET), true, 40000),\n      array(new Vector(2, 4, 6, PT_PLANET), new Vector(4, 4, 6, PT_PLANET), false, 40000),\n      array(new Vector(2, 4, 6, PT_PLANET), new Vector(4, 4, 6, PT_MOON), true, 40000),\n      array(new Vector(2, 4, 6, PT_PLANET), new Vector(4, 4, 6, PT_MOON), false, 40000),\n      array(new Vector(2, 4, 6, PT_PLANET), new Vector(4, 4, 6, PT_DEBRIS), true, 40000),\n      array(new Vector(2, 4, 6, PT_PLANET), new Vector(4, 4, 6, PT_DEBRIS), false, 40000),\n    );\n  }\n\n  /**\n   * @dataProvider dataDistance\n   *\n   * @covers ::distance\n   *\n   * @param Vector $vector1\n   * @param Vector $vector2\n   * @param bool   $returnZero\n   * @param int    $distance\n   */\n  public function testDistance($vector1, $vector2, $returnZero, $distance) {\n    $this->object->readFromVector($vector1);\n    $this->assertEquals($distance, $this->object->distance($vector2, $returnZero));\n  }\n\n  public function dataConvertToVector() {\n    return array(\n      array(array('galaxy' => 1, 'system' => 3, 'planet' => 5, 'planet_type' => PT_MOON), '', 1, 3, 5, PT_MOON, 20000),\n      array(array('galaxy' => 1, 'system' => 3, 'planet' => 5, 'type' => PT_MOON), '', 1, 3, 5, PT_MOON, 20000),\n      array(array('pr_galaxy' => 1, 'pr_system' => 3, 'pr_planet' => 5, 'pr_planet_type' => PT_MOON), 'pr_', 1, 3, 5, PT_MOON, 40000),\n      array(array('pr_galaxy' => 1, 'pr_system' => 3, 'pr_planet' => 5, 'pr_type' => PT_MOON), 'pr_', 1, 3, 5, PT_MOON, 40000),\n\n      array(array('galaxy' => 2, 'system' => 3, 'planet' => 5, 'type' => PT_MOON), '', 2, 3, 5, PT_MOON, 2795),\n      array(array('galaxy' => 2, 'system' => 4, 'planet' => 5, 'type' => PT_MOON), '', 2, 4, 5, PT_MOON, 1005),\n      array(array('galaxy' => 2, 'system' => 4, 'planet' => 6, 'type' => PT_MOON), '', 2, 4, 6, PT_MOON, 5),\n      array(array('galaxy' => 2, 'system' => 4, 'planet' => 6, 'type' => PT_PLANET), '', 2, 4, 6, PT_PLANET, 5),\n    );\n  }\n\n  /**\n   * @dataProvider dataConvertToVector\n   *\n   * @covers ::convertToVector\n   *\n   * @param array  $coordinates\n   * @param string $prefix\n   * @param        $g\n   * @param        $s\n   * @param        $p\n   * @param        $t\n   * @param        $d\n   */\n  public function testConvertToVector($coordinates, $prefix, $g, $s, $p, $t, $d) {\n    $this->object = Vector::convertToVector($coordinates, $prefix);\n\n    $this->assertAttributeEquals($g, 'galaxy', $this->object);\n    $this->assertAttributeEquals($s, 'system', $this->object);\n    $this->assertAttributeEquals($p, 'planet', $this->object);\n    $this->assertAttributeEquals($t, 'type', $this->object);\n  }\n\n  /**\n   * @dataProvider dataConvertToVector\n   *\n   * @covers ::distanceFromCoordinates\n   *\n   * @param Vector $vector\n   * @param int    $distance\n   */\n  public function testDistanceFromCoordinates($coordinates, $prefix, $g, $s, $p, $t, $d) {\n    $this->object->galaxy = 2;\n    $this->object->system = 4;\n    $this->object->planet = 6;\n    $this->assertEquals($d, $this->object->distanceFromCoordinates($coordinates));\n  }\n\n}\n"
  },
  {
    "path": "tests/includes/test_constants.php",
    "content": "<?php\n/**\n * Created by Gorlum 16.07.2017 11:43\n */\n\ndefine('TEST_VALUE_SQL_DATE', '2001-01-01');\ndefine('TEST_VALUE_INT_0', 0);\ndefine('TEST_VALUE_INT_1', 1);\ndefine('TEST_VALUE_INT_7', 7);\n\nconst UNIT_TEST_ID_STRING_0 = 'UNIT_TEST_ID_STRING_0';\nconst UNIT_TEST_ID_STRING_1 = 'UNIT_TEST_ID_STRING_1';\nconst UNIT_TEST_ID_STRING_2 = 'UNIT_TEST_ID_STRING_2';\nconst UNIT_TEST_ID_STRING_3 = 'UNIT_TEST_ID_STRING_3';\nconst UNIT_TEST_ID_STRING_4 = 'UNIT_TEST_ID_STRING_4';\nconst UNIT_TEST_ID_STRING_5 = 'UNIT_TEST_ID_STRING_5';\n\nconst UNIT_TEST_STRING = 'test';"
  },
  {
    "path": "tests/includes/test_functions.php",
    "content": "<?php\n/**\n * Created by Gorlum 16.07.2017 11:46\n */\n\n/**\n * Call protected/private method of a class.\n *\n * @param object|string $object Instantiated object that we will run method on.\n * @param string        $methodName Method name to call\n * @param array         $parameters Array of parameters to pass into method.\n *\n * @return mixed Method return.\n */\nfunction invokeMethod($object, $methodName, array $parameters = array()) {\n  //is_object($object) ? $object = get_class($object) : false;\n\n  $reflection = new ReflectionClass($object);\n  $method = $reflection->getMethod($methodName);\n  $method->setAccessible(true);\n\n  return $method->invokeArgs(is_object($object) ? $object : null, $parameters);\n}\n\n/**\n * getPrivateProperty\n *\n * @param string $className\n * @param string $propertyName\n *\n * @return ReflectionProperty\n */\nfunction getPrivateProperty($className, $propertyName) {\n  $reflector = new ReflectionClass($className);\n  $property = $reflector->getProperty($propertyName);\n  $property->setAccessible(true);\n\n  return $property;\n}\n\nfunction getPrivatePropertyValue($object, $propertyName) {\n  return getPrivateProperty(get_class($object), $propertyName)->getValue($object);\n}\n\n/**\n * Sets a protected property on a given object via reflection\n *\n * @param object $object - instance in which protected value is being modified\n * @param string $property - property on instance being modified\n * @param mixed  $value - new value of the property being modified\n *\n * @return void\n */\nfunction setProtectedProperty($object, $property, $value) {\n  $reflection = new ReflectionClass($object);\n  $reflection_property = $reflection->getProperty($property);\n  $reflection_property->setAccessible(true);\n  $reflection_property->setValue($object, $value);\n}\n"
  },
  {
    "path": "tests/phpunit.xml",
    "content": "<phpunit\n        bootstrap=\"bootstrap.php\"\n>\n    <filter>\n        <whitelist processUncoveredFilesFromWhitelist=\"false\">\n            <!--<file>../classes/HelperArray.php</file>-->\n            <directory suffix=\".php\">../classes/</directory>\n\n            <!--<directory suffix=\".php\">../</directory>-->\n            <exclude>\n                <directory>../.git/</directory>\n                <directory>../.local/</directory>\n                <directory>../__local/</directory>\n                <directory>../tests.int/</directory>\n                <directory>../admin/sxd/</directory>\n            </exclude>\n        </whitelist>\n    </filter>\n</phpunit>"
  },
  {
    "path": "time_probe.php",
    "content": "<?php\n\nuse Player\\playerTimeDiff;\n\ndefine('IN_AJAX', true);\n\nrequire_once('common.' . substr(strrchr(__FILE__, '.'), 1));\n\n/*\n$time_local  = $time_server + $time_diff\n$time_diff   = $time_local  - $time_server\n$time_server = $time_local  - $time_diff\n*/\n\nif ($font_size = sys_get_param_str('font_size')) {\n  if (strpos($font_size, '%') !== false) {\n    // Размер шрифта в процентах\n    $font_size = min(max(floatval($font_size), FONT_SIZE_PERCENT_MIN), FONT_SIZE_PERCENT_MAX) . '%';\n  } elseif (strpos($font_size, 'px') !== false) {\n    // Размер шрифта в пикселях\n    $font_size = min(max(floatval($font_size), FONT_SIZE_PIXELS_MIN), FONT_SIZE_PIXELS_MAX) . 'px';\n  } else {\n    // Не мышонка, не лягушка...\n    $font_size = FONT_SIZE_PERCENT_DEFAULT_STRING;\n  }\n\n  sn_setcookie(SN_COOKIE_F, $font_size, SN_TIME_NOW + PERIOD_YEAR);\n  SN::$user_options[PLAYER_OPTION_BASE_FONT_SIZE] = $font_size;\n} elseif (isParamExists('webpSupport')) {\n  sn_setcookie(SN_COOKIE_WEBP, sys_get_param_int('webpSupport'), SN_TIME_NOW + PERIOD_HOUR);\n} else {\n  echo playerTimeDiff::timeProbeAjax();\n}\n"
  },
  {
    "path": "tools/classes/ImageContainer.php",
    "content": "<?php\n/** Created by Gorlum 09.01.2024 15:59 */\n\nnamespace Tools;\n\n/**\n * @property int $height\n * @property int $width\n */\nclass ImageContainer {\n  private $height = -1;\n  private $width = -1;\n\n  /** @var resource|null $image */\n  public $image = null;\n\n  /**\n   * @param string $file\n   *\n   * @return static|null\n   */\n  public static function load($file) {\n    $image = @imagecreatefromstring(file_get_contents($file));\n    if (!$image) {\n      return null;\n    }\n\n    $that = new static();\n\n    $that->image = $image;\n    imagesavealpha($that->image, true);\n\n    $that->width  = imagesx($that->image);\n    $that->height = imagesy($that->image);\n\n    return $that;\n  }\n\n  /**\n   * @param string $file\n   *\n   * @return static|null\n   */\n  public static function copyGdImage($gdImage) {\n    $image = @imagecreatefromstring(file_get_contents($file));\n    if (!$image) {\n      return null;\n    }\n\n    $that = new static();\n\n    $that->image = $image;\n    imagesavealpha($that->image, true);\n\n    $that->width  = imagesx($that->image);\n    $that->height = imagesy($that->image);\n\n    return $that;\n  }\n\n  /**\n   * @param int $width\n   * @param int $height\n   *\n   * @return static\n   */\n  public static function create($width, $height) {\n    $that = new static();\n\n    $that->width  = $width;\n    $that->height = $height;\n\n    $that->imageReset();\n\n    return $that;\n  }\n\n  public function __get($property) {\n    if (in_array($property, ['height', 'width',]) && ($this->$property === -1)) {\n      if (isset($this->image)) {\n        $this->width  = imagesx($this->image);\n        $this->height = imagesy($this->image);\n      } else {\n        $this->width = $this->height = 0;\n      }\n    }\n\n    return property_exists($this, $property) ? $this->$property : null;\n  }\n\n  public function __destruct() {\n    if (!empty($this->image)) {\n      imagedestroy($this->image);\n    }\n  }\n\n  /**\n   * @param ImageContainer $anImage\n   * @param int            $positionX\n   * @param int            $positionY\n   * @param int            $sourceX\n   * @param int            $sourceY\n   *\n   * @return bool\n   */\n  public function copyFrom(ImageContainer $anImage, $positionX, $positionY, $sourceX = 0, $sourceY = 0) {\n    return imagecopy($this->image, $anImage->image, $positionX, $positionY, $sourceX, $sourceY, $anImage->width, $anImage->height);\n  }\n\n  /**\n   * @param \\GdImage|resource $anImage\n   * @param int               $positionX\n   * @param int               $positionY\n   * @param int               $sourceX\n   * @param int               $sourceY\n   *\n   * @return bool\n   */\n  public function copyFromGd($anImage, $positionX, $positionY, $sourceX = 0, $sourceY = 0) {\n    return imagecopy($this->image, $anImage, $positionX, $positionY, $sourceX, $sourceY, imagesx($anImage), imagesy($anImage));\n  }\n\n  /**\n   * @param string $string\n   *\n   * @return bool\n   */\n  public function savePng($string) {\n    return imagepng($this->image, $string, 9);\n  }\n\n  /**\n   * @return void\n   */\n  protected function imageReset() {\n    if (!empty($this->image)) {\n      imagedestroy($this->image);\n    }\n\n    $this->image = imagecreatetruecolor($this->width, $this->height);\n    imagealphablending($this->image, true);\n    imagesavealpha($this->image, true);\n    $color = imagecolorallocatealpha($this->image, 0, 0, 0, 127);\n    imagefill($this->image, 0, 0, $color);\n  }\n\n}\n"
  },
  {
    "path": "tools/classes/ImageFile.php",
    "content": "<?php\n\n/** Created by Gorlum 08.01.2024 19:14 */\n\nuse GIFEndec\\Decoder;\nuse GIFEndec\\Events\\FrameDecodedEvent;\nuse GIFEndec\\IO\\FileStream;\nuse Tools\\ImageContainer;\n\n/**\n * @property int $height\n * @property int $width\n */\nclass ImageFile {\n  public $dir = '';\n  public $fileName = '';\n  public $fullPath = '';\n\n  private $image = null;\n  /** @var string|false $content Image file content */\n  protected $content;\n\n  /**\n   * @param $fileName\n   * @param $dir\n   *\n   * @return static|null\n   */\n  public static function read($fileName, $dir = '') {\n    $that = new static($fileName, $dir);\n\n    if ($that->getImageContainer() === null) {\n      unset($that);\n      $that = null;\n    }\n\n    return $that;\n  }\n\n  /**\n   * @param string $fileName Name of image file. Can be full path to file or just filename. In latter case $dir will be used\n   * @param string $dir      If present and filename is just name of file will we added to make full path. If empty - tools root folder will be assumed\n   */\n  public function __construct($fileName, $dir = '') {\n    if (dirname($fileName) !== '.') {\n      $this->dir      = realpath(dirname($fileName));\n      $this->fileName = basename($fileName);\n    } else {\n      $this->dir      = realpath($dir ?: __DIR__ . '/../');\n      $this->fileName = $fileName;\n    }\n    $this->dir = str_replace('\\\\', '/', $this->dir) . '/';\n\n    $this->fullPath = $this->dir . $this->fileName;\n  }\n\n  public function __get($property) {\n    if (in_array($property, ['height', 'width',])) {\n      return $this->getImageContainer() ? $this->getImageContainer()->$property : 0;\n    }\n\n    return property_exists($this, $property) ? $this->$property : null;\n  }\n\n//  public function __set($property, $value) {\n//    if (property_exists($this, $property)) {\n//      $this->$property = $value;\n//    }\n//\n//    return $this;\n//  }\n\n  /**\n   * @return ImageContainer\n   */\n  public function getImageContainer() {\n    if (empty($this->image)) {\n      $this->image = ImageContainer::load($this->fullPath);\n    }\n\n    return $this->image;\n  }\n\n  public function isAnimatedGif() {\n    // Checking file extension\n    if (!strtolower(pathinfo($this->fullPath, PATHINFO_EXTENSION)) === 'gif') {\n      return false;\n    }\n\n    // Counting frame(s)\n    if (substr($content = $this->loadContent(), 0, 4) !== 'GIF8') {\n      return false;\n    }\n    //an animated gif contains multiple \"frames\", with each frame having a\n    //header made up of:\n    // * a static 4-byte sequence (\\x00\\x21\\xF9\\x04)\n    // * 4 variable bytes\n    // * a static 2-byte sequence (\\x00\\x2C)\n    if (preg_match_all('#\\x00\\x21\\xF9\\x04.{4}\\x00[\\x2C\\x21]#s', $content) <= 1) {\n      return false;\n    }\n\n    return true;\n  }\n\n  /**\n   * @return false|string\n   */\n  protected function loadContent() {\n    if (empty($this->content)) {\n      $this->content = file_get_contents($this->fullPath);\n    }\n\n    return $this->content;\n  }\n\n\n}\n"
  },
  {
    "path": "tools/classes/Sprite.php",
    "content": "<?php\n/** Created by Gorlum 08.01.2024 19:57 */\n\nnamespace Tools;\n\nrequire_once __DIR__ . '/SpriteLine.php';\nrequire_once __DIR__ . '/SpriteLineGif.php';\nrequire_once __DIR__ . '/ImageFile.php';\nrequire_once __DIR__ . '/ImageContainer.php';\n\nuse Exception;\nuse ImageFile;\n\nclass Sprite {\n  const LAYOUT_LINE = 0;\n  const LAYOUT_COLUMN = 1;\n  const LAYOUT_SQUARE = 'square';\n  // const LAYOUT_MINIMAL = 'minimal'; // Minimize sum of linear dimensions\n  // const LAYOUT_EQUAL_HEIGHT = 'equal_height'; // Put images with equal height in one line\n  const LAYOUT_BTREE = 'btree';\n\n  /** @var ImageFile[] $imageList */\n  public $imageList = [];\n  /** @var SpriteLine[] $lines */\n  public $lines = [];\n  /** @var ImageContainer|null $image */\n  public $image = null;\n\n  /** @var int $gridSize Sprite grid size in images */\n  protected $gridSize = 0;\n  /** @var int $scaleToPx Scale input images to specified PX. Default: 0 - no scaling */\n  protected $scaleToPx = 0;\n\n  protected $lineIndex = 0;\n  protected $columnIndex = 0;\n\n  protected $height = 0;\n  protected $width = 0;\n\n  /**\n   * @param ImageFile[] $images\n   * @param             $layout\n   */\n  public function __construct($images, $layout, $scaleToPx) {\n    $this->scaleToPx = $scaleToPx;\n\n    if ($layout === self::LAYOUT_SQUARE) {\n      $gridSize = ceil(sqrt(count($images)));\n    } elseif ($layout === self::LAYOUT_COLUMN) {\n      $gridSize = 1;\n    } elseif ($layout === self::LAYOUT_LINE) {\n      $gridSize = 0;\n    } else {\n      $gridSize = 1;\n    }\n\n    $this->gridSize = $gridSize;\n\n    $this->imageList = $images;\n  }\n\n  /**\n   * @return void\n   */\n  protected function imageReset() {\n    if (!empty($this->image)) {\n      unset($this->image);\n    }\n\n    $this->image = ImageContainer::create($this->width, $this->height);\n  }\n\n  /**\n   * @param $scaleToPx\n   */\n  protected function renderLines($scaleToPx) {\n    $this->width = $this->height = 0;\n    // Generating lines and calculating line sizes\n    foreach ($this->lines as $line) {\n      $line->generate($this->height, $scaleToPx);\n\n      $this->height += $line->height;\n      $this->width  = max($this->width, $line->width);\n\n//      $line->image->savePng(__DIR__ . '/../gif_line_' . $this->height . '.png'); // TODO remove debug\n    }\n  }\n\n  /**\n   *\n   * @return ImageContainer|null\n   */\n  public function generate() {\n    $this->createGrid();\n\n    $this->renderLines($this->scaleToPx);\n\n    // Recreating main sprite image with new width and height\n    $this->imageReset();\n\n    // Generating final sprite\n    $position = 0;\n    foreach ($this->lines as $line) {\n      $this->image->copyFrom($line->image, 0, $position);\n\n      $position += $line->height;\n    }\n\n    return $this->image;\n  }\n\n  /**\n   *\n   * @return string %3$s - $outName, %4$s - $relativeUrl\n   */\n  protected function generateCss() {\n    $css = '';\n    foreach ($this->lines as $line) {\n      $css .= $line->css;\n    }\n\n    return \".%3\\$s{background-image: url('%4\\$s%3\\$s.png');display: inline-block;\" .\n      ($this->scaleToPx > 0 ? \"transform-origin: top left;\" : \"\") .\n      \"}\\n\" . $css;\n  }\n\n  public function savePng($fileName) {\n    // Saving PNG\n    $this->image->savePng($fileName);\n  }\n\n  /**\n   * @param          $fileName\n   * @param string[] $vsprintf [$cssPrefix, $cssSuffix, $outName, $relativeUrl,]\n   *\n   * @return false|int\n   */\n  public function saveCss($fileName, $vsprintf = []) {\n    // Saving CSS\n    return file_put_contents($fileName, vsprintf($this->generateCss(), $vsprintf));\n  }\n\n  /**\n   * @param string   $dirOut\n   * @param string   $outName\n   * @param string[] $vsprintf [$cssPrefix, $cssSuffix, $outName, $relativeUrl,]\n   *\n   * @return void\n   * @throws Exception\n   */\n  public function saveOutput($dirOut, $outName, $vsprintf = []) {\n    // Checking if output directory exists and creating one - if not\n    if (!is_dir($dirOut) && !mkdir($dirOut, 0777, true)) {\n      throw new Exception(\"Can't create output directory {$dirOut}\\n\");\n    }\n    // Saving PNG\n    $this->savePng($dirOut . $outName . '.png');\n    $this->saveCss($dirOut . $outName . '.css', $vsprintf);\n  }\n\n  /**\n   * @return void\n   */\n  protected function createGrid() {\n    $this->lineIndex = 0;\n\n    usort($this->imageList, function (ImageFile $a, ImageFile $b) { return $b->height - $a->height; });\n\n    $prevLine = new SpriteLine();\n    foreach ($this->imageList as $image) {\n      $line = $prevLine->fillLine($image, $this->gridSize);\n      if ($line != $prevLine) {\n        if ($prevLine && $prevLine->width > 0) {\n          $this->lines[$this->lineIndex] = $prevLine;\n          $this->lineIndex++;\n        }\n        $prevLine = $line;\n      }\n    }\n\n    if ($prevLine) {\n      // There is something in line to keep\n      $this->lines[$this->lineIndex] = $prevLine;\n    }\n  }\n\n}\n"
  },
  {
    "path": "tools/classes/SpriteLine.php",
    "content": "<?php\n\n/** Created by Gorlum 08.01.2024 20:04 */\n\n/** @noinspection PhpUnnecessaryCurlyVarSyntaxInspection */\n\nnamespace Tools;\n\nuse ImageFile;\n\nclass SpriteLine {\n  /** @var ImageFile[] $files */\n  public $files = [];\n\n  public $height = 0;\n  public $width = 0;\n\n  /** @var ImageContainer|null $image */\n  public $image = null;\n  /** @var string */\n  public $css = '';\n\n  /**\n   * @param ImageFile $image\n   * @param int       $gridSize\n   *\n   * @return static|null\n   */\n  public function fillLine($image, $gridSize) {\n    if($image->isAnimatedGif()) {\n      $line = new SpriteLineGif();\n    } else {\n      $line = $this->isFull($gridSize) ? new static() : $this;\n    }\n\n    $line->addImage($image);\n\n    return $line;\n  }\n\n  /**\n   * @param ImageFile $imageFile\n   *\n   * @return void\n   */\n  protected function addImage($imageFile) {\n    $this->files[] = $imageFile;\n\n    $this->height = max($this->height, $imageFile->height);\n    $this->width  += $imageFile->width;\n  }\n\n  public function getImageCount() {\n    return count($this->files);\n  }\n\n  public function generate($posY, $scaleToPx) {\n    unset($this->image);\n\n    $this->image = ImageContainer::create($this->width, $this->height);\n\n    $position = 0;\n    foreach ($this->files as $file) {\n      $this->image->copyFrom($file->getImageContainer(), $position, 0);\n\n      $onlyName = explode('.', $file->fileName);\n      if (count($onlyName) > 1) {\n        array_pop($onlyName);\n      }\n      $onlyName = implode('.', $onlyName);\n\n      $css = \"%1\\$s{$onlyName}%2\\$s{background-position: -{$position}px -{$posY}px;\";\n      if ($scaleToPx > 0) {\n        $maxSize = max($file->width, $file->height);\n        if ($maxSize != $scaleToPx) {\n          $css .= \"zoom: calc({$scaleToPx}/{$maxSize});\";\n        }\n      }\n      $css .= \"width: {$file->width}px;height: {$file->height}px;}\\n\";\n\n      $this->css .= $css;\n\n      $position += $file->width;\n    }\n  }\n\n  protected function isFull($gridSize) {\n    return $this->getImageCount() >= $gridSize;\n  }\n\n}\n"
  },
  {
    "path": "tools/classes/SpriteLineGif.php",
    "content": "<?php\n/** Created by Gorlum 21.01.2024 01:07 */\n\nnamespace Tools;\n\nuse GIFEndec\\Decoder;\nuse GIFEndec\\Events\\FrameDecodedEvent;\nuse GIFEndec\\Frame;\nuse GIFEndec\\IO\\FileStream;\n\nclass SpriteLineGif extends SpriteLine {\n  /** @var bool $expandFrame Should frame be expanded for CSS animation? */\n  protected $expandFrame = true;\n\n  /** @var Frame[] $frames */\n  protected $frames = [];\n  /**\n   * @var int\n   */\n  protected $maxWidth = 0;\n\n  protected function addImage($imageFile) {\n    $this->files[] = $imageFile;\n\n    $this->frames = [];\n\n    $this->height = $this->width = $this->maxWidth = 0;\n\n    /** Open GIF as FileStream */\n    // TODO - own class from loaded\n    $gifStream = new FileStream($imageFile->fullPath);\n    /** Create Decoder instance from MemoryStream */\n    $gifDecoder = new Decoder($gifStream);\n\n    /** Run decoder. Pass callback function to process decoded Frames when they're ready. */\n    $gifDecoder->decode(function (FrameDecodedEvent $event) {\n      $this->frames[] = $event->decodedFrame;\n\n      $this->width    += $event->decodedFrame->getSize()->getWidth();\n      $this->maxWidth = max($this->maxWidth, $event->decodedFrame->getSize()->getWidth());\n\n      $this->height = max($this->height, $event->decodedFrame->getSize()->getHeight());\n    });\n    // For EXPAND_FRAME delta width would be equal size of the largest frame\n//    $this->width = count($this->frames) * reset($this->frames)->getSize()->getWidth();\n    if ($this->expandFrame) {\n      $this->width = count($this->frames) * $this->maxWidth;\n    }\n  }\n\n  /**\n   * GIF image line considered always full\n   *\n   * @param $gridSize\n   *\n   * @return bool\n   */\n  protected function isFull($gridSize) {\n    return true;\n  }\n\n  public function generate($posY, $scaleToPx) {\n    // Extracting file name from full path\n    $file     = reset($this->files);\n    $onlyName = explode('.', $file->fileName);\n    if (count($onlyName) > 1) {\n      array_pop($onlyName);\n    }\n    $onlyName = implode('.', $onlyName);\n    // You can't have this chars in CSS qualifier\n    $onlyName = str_replace(['.', '#'], '_', $onlyName);\n\n    // Expanding frames. Their sizes can change due to offset\n    foreach ($this->frames as $i => $frame) {\n      $this->expandFrame($i);\n    }\n\n//    $firstFrame = reset($this->frames);\n//    $this->width  = imagesx($firstFrame->gdImage) * count($this->frames);\n//    $this->height = imagesy($firstFrame->gdImage);\n//    $maxDimension = max(imagesx($firstFrame->gdImage), imagesy($firstFrame->gdImage));\n    $maxDimension = max($this->maxWidth, $this->height);\n\n    // Recreating image - if any\n    unset($this->image);\n    $this->image = ImageContainer::create($this->width, $this->height);\n\n    $durations = [];\n    $position  = 0;\n    foreach ($this->frames as $i => $frame) {\n//      $frameGdImage = $this->expandFrame($i);\n      $frameGdImage = $frame->gdImage;\n\n      $width  = imagesx($frameGdImage);\n      $height = imagesy($frameGdImage);\n\n      $this->image->copyFromGd($frameGdImage, $position, 0);\n\n//      $frame = $this->frames[$i];\n      // Fixing duration 0 to 10\n      $durations[$i] = ($duration = $frame->getDuration()) ? $duration : 10;\n\n      $css = \"%1\\$s{$onlyName}_{$i}%2\\$s{background-position: -{$position}px -{$posY}px;\";\n\n      // Extra info about frame\n      $size   = $frame->getSize();\n      $offset = $frame->getOffset();\n      $css    = \"/* Frame {$size->getWidth()}x{$size->getHeight()} @ ({$offset->getX()},{$offset->getY()}) duration {$frame->getDuration()} disposition {$frame->getDisposalMethod()} */\" . $css;\n\n      if ($scaleToPx > 0) {\n        if ($maxDimension != $scaleToPx) {\n          $css .= \"zoom: calc({$scaleToPx}/{$maxDimension});\";\n        }\n      }\n      $css .= \"width: {$width}px;height: {$height}px;}\\n\";\n\n      if ($i === 0) {\n        // If it's first frame - generating CSS for static image\n        $css = \"%1\\$s{$onlyName}%2\\$s,\\n\" . $css;\n      }\n\n      $this->css .= $css;\n\n      $position += $width;\n    }\n\n    $totalDuration = array_sum($durations);\n    $durInSec      = round($totalDuration / 100, 4);\n\n    $animation  = '';\n    $cumulative = 0;\n    $position   = 0;\n    foreach ($durations as $i => $duration) {\n      $animation .= $cumulative . \"%% {background-position-x: {$position}px;}\\n\";\n\n      $cumulative += round($duration / $totalDuration * 100, 3);\n      $position   -= imagesx($this->frames[$i]->gdImage);\n    }\n    $animation = \"%1\\$s{$onlyName}%2\\$s {animation: {$onlyName}_animation%2\\$s {$durInSec}s step-end infinite;}\\n\" .\n      \"@keyframes {$onlyName}_animation%2\\$s {\\n\" .\n      $animation .\n      \"}\";\n\n    $this->css .= $animation;\n  }\n\n  /**\n   * @param int $i\n   *\n   * @return resource|\\GdImage\n   */\n  protected function expandFrame($i) {\n    /**\n     * Disposal method\n     * Values :\n     *   0 - No disposal specified. The decoder is not required to take any action.\n     *   1 - Do not dispose. The graphic is to be left in place.\n     *   2 - Restore to background color. The area used by the graphic must be restored to the background color.\n     *   3 - Restore to previous. The decoder is required to restore the area overwritten by the graphic with\n     *       what was there prior to rendering the graphic.\n     */\n    $thisFrame = $this->frames[$i];\n    if (!$this->expandFrame) {\n      return $thisFrame->gdImage = $thisFrame->createGDImage();\n    }\n\n    if ($i === 0) {\n      // This is first frame\n      $sizeX = $this->maxWidth;\n      $sizeY = $this->height;\n//      $sizeX = $thisFrame->getSize()->getWidth() + $thisFrame->getOffset()->getX();\n//      $sizeY = $thisFrame->getSize()->getHeight() + $thisFrame->getOffset()->getY();\n    } else {\n      $prevFrame = $this->frames[$i - 1];\n      if (!in_array($prevFrame->getDisposalMethod(), [0, 1, 2])) {\n        die(\"Disposal method {$prevFrame->getDisposalMethod()} does not supported yet\");\n      }\n\n      // Creating detached copy of previous frame image\n      $sizeX = imagesx($prevFrame->gdImage);\n      $sizeY = imagesy($prevFrame->gdImage);\n    }\n    $newGdImage = imagecreatetruecolor($sizeX, $sizeY);\n    imagealphablending($newGdImage, false);\n    imagesavealpha($newGdImage, true);\n    $color = imagecolorallocatealpha($newGdImage, 0, 0, 0, 127);\n    imagefill($newGdImage, 0, 0, $color);\n\n    if ($i !== 0) {\n      imagecopy($newGdImage, $prevFrame->gdImage,\n        0, 0,\n        0, 0, imagesx($prevFrame->gdImage), imagesy($prevFrame->gdImage)\n      );\n\n      if ($prevFrame->getDisposalMethod() === 2) {\n        imagefilledrectangle($newGdImage,\n          $prevFrame->getOffset()->getX(), $prevFrame->getOffset()->getY(),\n          $prevFrame->getOffset()->getX() + ($prevFrame->getSize()->getWidth() - 1),\n          $prevFrame->getOffset()->getY() + ($prevFrame->getSize()->getHeight() - 1),\n          $color\n        );\n      }\n    }\n\n    $anImage = $thisFrame->createGDImage();\n    imagecopy($newGdImage, $anImage,\n      $thisFrame->getOffset()->getX(), $thisFrame->getOffset()->getY(),\n      0, 0, imagesx($anImage), imagesy($anImage)\n    );\n\n    return $thisFrame->gdImage = $newGdImage;\n  }\n\n}\n"
  },
  {
    "path": "tools/classes/Spritify.php",
    "content": "<?php\n\n/** Created by Gorlum 08.01.2024 19:21 */\n\n/** @noinspection PhpUnnecessaryCurlyVarSyntaxInspection */\n\nnamespace Tools;\n\nuse Exception;\nuse ImageFile;\n\ndefine('INSIDE', true);\n\nrequire_once __DIR__ . '/../../classes/debug.php';\nrequire_once __DIR__ . '/Sprite.php';\n\nclass Spritify {\n  /**\n   * @param string|string[] $dirInArray  Input directory\n   * @param string          $dirOut      Output directory\n   * @param string          $outName     Name to use CSS/PNG file names and global CSS class\n   * @param string          $cssPrefix   Prefix to CSS qualifier. Default '#'\n   * @param string          $cssSuffix   Suffix to CSS qualifier. Default ''\n   * @param int             $scaleToPx   Pixel size to scale largest side of sprite to for scale(). Default 0 - no scaling\n   * @param string          $relativeUrl Url relative to root where PNG sprite will reside. Default: '/design/images/'\n   * @param string          $layout      How images should be arranged in sprite. Default: Sprite::LAYOUT_SQUARE\n   *\n   * @return void\n   *\n   * @throws Exception\n   * @see Sprite::LAYOUT_SQUARE and others Sprite::LAYOUT_XXX constants\n   *\n   */\n  public static function go($dirInArray, $dirOut, $outName, $cssPrefix = '#', $cssSuffix = '', $scaleToPx = 0, $relativeUrl = '/design/images/', $layout = Sprite::LAYOUT_SQUARE) {\n    if (!is_array($dirInArray)) {\n      $dirInArray = [$dirInArray];\n    }\n\n    $images = [];\n    foreach ($dirInArray as $dirIn) {\n      print \"Loading files from folder/filter `$dirIn` \\n\";\n\n//    $images = self::propagateImagesScanDir($dirIn);\n      $images = array_merge($images, self::propagateImagesGlob($dirIn));\n    }\n\n    print \"Processing images to `$outName`\\n\";\n    if (empty($images)) {\n      print \"No images found to process\\n\\n\";\n\n      return;\n    }\n\n    $sprite = new Sprite($images, $layout, $scaleToPx);\n    $sprite->generate();\n    $sprite->saveOutput($dirOut, $outName, [$cssPrefix, $cssSuffix, $outName, $relativeUrl,]);\n\n    print \"Folder/filter processed\\n\\n\";\n  }\n\n  /**\n   * Propagates array with image files from glob pattern (as in OS - with * and ?)\n   *\n   * @param string $dirIn\n   *\n   * @return ImageFile[]|false\n   */\n  protected static function propagateImagesGlob($dirIn) {\n    if (($fullPathList = glob($dirIn, GLOB_BRACE)) === false) {\n      print \"Can't find directory `$dirIn`\\n\";\n\n      return false;\n    }\n\n    /** @var ImageFile[] $images */\n    $images = [];\n    foreach ($fullPathList as $fullPath) {\n      $fullPath = str_replace('\\\\', '/', realpath($fullPath));\n      $images   = self::tryReadFile($fullPath, $images);\n    }\n\n    return $images;\n  }\n\n  /**\n   * Propagates array with image files from folder\n   *\n   * @param string $dirIn\n   *\n   * @return ImageFile[]|false\n   */\n  protected static function propagateImagesScanDir($dirIn) {\n    $dirRealPath = str_replace('\\\\', '/', realpath($dirIn ?: './'));\n    if (!file_exists($dirRealPath) || !is_dir($dirRealPath)) {\n      print \"Can't find directory `$dirIn`\\n\";\n\n      return false;\n    }\n\n    /** @var ImageFile[] $images */\n    $images = [];\n\n    foreach (scandir($dirRealPath) as $fileNameOnly) {\n      if (in_array($fileNameOnly, ['.', '..',]) || is_dir($fullPath = \"$dirRealPath/$fileNameOnly\")) {\n        continue;\n      }\n      $images = self::tryReadFile($fullPath, $images);\n    }\n\n    return $images;\n  }\n\n  /**\n   * @param             $fullPath\n   * @param ImageFile[] $images\n   *\n   * @return ImageFile[]\n   */\n  protected static function tryReadFile($fullPath, $images) {\n    if ($image = ImageFile::read($fullPath)) {\n      $images[] = $image;\n      print \"`$fullPath` read OK\\n\";\n    } else {\n      print \"ERROR: Can't read file `$fullPath`\\n\";\n    }\n\n    return $images;\n  }\n\n}\n"
  },
  {
    "path": "tools/generate.php",
    "content": "﻿<?php\n\nrequire_once __DIR__ .'/../includes/debug.tools.php';\n\n$override = file_get_contents(getcwd() . '/global_override.css');\n\n$files = glob(getcwd() . '/skins/EpicBlueSnowy/gebaeude/*.png');\n\n\n///* Unit image in list on Build page - single small image */\n//div.unit_preview[id=\"unit1\"][unit_id=\"1\"] > img.unit_preview_image,\n///* Selected unit on build page - single small image*/\n//#unit_info #unit_info_image_wrapper > img#unit_info_image[unit_id=\"1\"],\n///* Novapedia Image - small or big */\n//table.novapedia img[unit_id=\"1\"]\n//  /*table.novapedia .unit_image_large[unit_id=\"1\"] {*/\n//  /*table.novapedia .unit_image_small[unit_id=\"1\"],*/\n//{\n//content: url(\"/skins/EpicBlueSnowy/gebaeude/0001.png\");\n//}\n\n$template = <<<TEMPLATE\n\n/* Unit image in list on Build page - single small image */\ndiv.unit_preview[unit_id=\"%1\\$d\"] > img.unit_preview_image,\n/* Selected unit on build page - single small image*/\n#unit_info #unit_info_image_wrapper > img#unit_info_image[unit_id=\"%1\\$d\"],\n/* Novapedia Image - small  */\ntable.novapedia img.unit_image_small[unit_id=\"%1\\$d\"]\n{\ncontent: url(\"/skins/EpicBlueSnowy/gebaeude/%1\\$s.png\");\n}\n/* Novapedia Image - large image */\ntable.novapedia img.unit_image_large[unit_id=\"%1\\$d\"] {\ncontent: url(\"/skins/EpicBlueSnowy/gebaeude/%1\\$s_large.png\");\n}\n/* Tech page - small image */\ndiv.tech_image[unit_id=\"%1\\$d\"] > img {\ncontent: url(\"/skins/EpicBlueSnowy/gebaeude/%1\\$s.png\");\n}\nTEMPLATE;\n\n$governors = <<<TEMPLATE\n\n/* Unit image in list on Build page - single small image */\ndiv.unit_preview[unit_id=\"%1\\$d\"] img\n{\ncontent: url(\"/skins/EpicBlueSnowy/gebaeude/%1\\$s.png\");\n}\nTEMPLATE;\n\n$ships = <<<TEMPLATE\n\n/* Unit image on fleet on orbit - small image */\ndiv.ship_miniature_container[unit_id=\"%1\\$d\"]\n{\nbackground-image: url(\"/skins/EpicBlueSnowy/gebaeude/%1\\$s.png\") !important;\nheight: 120px !important;\nwidth: 120px !important;\n}\n/* Ship image when sending fleet */\ndiv.unit_miniatures_wrapper[unit_id=\"%1\\$d\"] img { content: url(\"/skins/EpicBlueSnowy/gebaeude/%1\\$s.png\"); }\nTEMPLATE;\n\n$css = [];\nforeach ($files as $filepath) {\n  $filepath = realpath($filepath);\n\n  $filename = basename($filepath);\n\n  $strUnitId = explode('.', $filename)[0];\n\n  if(!is_numeric($strUnitId)) {continue;}\n\n  $css[] = sprintf($template, $strUnitId);\n  // Governors\n  if($strUnitId >= 600 && $strUnitId < 700) {\n    $css[] = sprintf($governors, $strUnitId);\n  }\n  // ships\n  if($strUnitId >= 200 && $strUnitId < 400) {\n    $css[] = sprintf($ships, $strUnitId);\n  }\n}\n\nfile_put_contents(getcwd() . '/design/css/global_override.css', $override . implode(\"\\n\", $css));"
  },
  {
    "path": "tools/spritify.md",
    "content": "# Spritify #46d0#\n\nTool that make sprites from set of images - PNG sprite file along with CSS to use\n\nWill arrange all images into predefined layout pattern.\n\n* Patterns supported:\n    * Square\n    * Line\n    * Column\n* Layout pattern ignored for animated GIFs - each GIF decompresses in it's own line of images\n\nSupports scaling (via `zoom` CSS property) to predefined box (square only)\n\nBasic support for animated GIFs:\n\n* Extract all frames in one \"line\" (layout pattern ignored for animated GIFs)\n* Expand each sprite to fully-qualified image (SpriteLineGif::$expandFrame === true)\n    * Only `DO_NOT_DISPOSE`, `UNSPECIFIED` and `RESTORE_TO_BACKGROUND_COLOR` disposal methods supported for now\n* Generates CSS per frame with extra info: frame position and size along with disposition method\n* Generates pure CSS animations via `@keyframes` and `animation` property\n    * Spritify honors A-GIF frame delays\n    * Spritify honors A-GIF frame offsets\n\n# Thanks\n\n* https://github.com/stil/gif-endec - Animated GIF encoding/decoding lib\n* Animated GIF detection - https://itecnote.com/tecnote/php-detect-animated-gifs-using-php-and-gd/\n* CSS sprite animation - https://jsfiddle.net/simurai/CGmCe/\n\n# Todo\n\n* Layouts: btree, same_height, same_width, pixel-size\n* Сортировать изображения еще и по полному пути/имени изображения/префиксу имени?\n    * menu_item -> menu_item_hidden -> menu_item_highlighted\n* Code\n    * Отдельный класс/метод построения спрайта\n    * Картинки должен хранить спрайт\n        * Позиции в спрайте хранятся в image\n    * Не линии - а имеджсет, который может быть и линией и... не-линией?\n* CSS animations from animated gif\n    * JS animated sprites https://jsfiddle.net/Camilo/n3xnn/\n    * CSS sprite animation https://jsfiddle.net/simurai/CGmCe/\n    * Animated GIF PHP encoder/decoder https://github.com/stil/gif-endec\n        * https://itecnote.com/tecnote/php-detect-animated-gifs-using-php-and-gd/\n    * file:///X:/Documents/Projects/supernova/supernova_trunk/tools/_test/border.html\n    * https://www.youtube.com/watch?v=jLbz6LRblV0\n        * https://matthewrayfield.com/articles/encoding-animated-gifs-into-pure-css/\n        * https://matthewrayfield.com/projects/gif2css/\n        * https://github.com/MatthewRayfield/gif2css\n    * https://www.w3.org/Graphics/GIF/spec-gif89a.txt\n\n# WiP\n\n* GIF decoding\n    * ? Background color on disposition `RESTORE_TO_BACKGROUND_COLOR`\n    * ? Is it possible then size + offset be larger then largest frame? Read spec or find GIF\n    * Streamline code for files to use GdImageWrapper as base image class and convert frames to it\n\n* Layout: Fit to width\n* Image: now it fit (zoom) to largest side\n  * todo: fit (zoom) to smallest, to width, to height\n* INI file\n  * Per-image file settings\n\n* Write `zoom: calc()` separately? It will help to fine-tune actual sizes later\n\n# Changelog\n\n2024-01-19 16:04:51 is Fri Jan 19 18:04:55 2024 +0200\n\n* #ctv\n\n\n* 2024-01-21 07:04:38 46a104\n    * Adjusted behavior in case if first frame in A-GIF smaller then largest one\n\n\n* 2024-01-21 06:17:01 46a103\n    * Now Spritify honors offset in first frame of A-GIF\n\n\n* 2024-01-21 04:44:55 46a102\n    * Spritify supports `RESTORE_TO_BACKGROUND_COLOR` disposition method\n\n\n* 2024-01-21 03:55:30 46a101\n    * Now Spritify generates pure CSS animations for extracted A-GIF frames, honoring delay between frames\n\n\n* 2024-01-21 02:45:25 46a100\n    * Now Spritify expand each extracted A-GIF partial frame to fully rendered one\n    * Only disposal methods `DO_NOT_DISPOSE` and `UNSPECIFIED` supported\n\n\n* 2024-01-21 01:27:46 46a99\n    * Now Spritify can decompress animated GIF to set of frames\n\n\n* 2024-01-19 17:51:35 46a98\n    * Now `SpriteLine` aware of line limitations and also can control it's filling\n\n\n* 2024-01-19 16:04:51 46a97\n    * Now supports several arrange patterns: Square, Line, Column\n    * File mask now can be array of glob masks\n    * Code refactoring\n\n\n* 2024-01-14 09:53:29 46a89\n    * Error handling\n    * Streamlining sources\n\n\n* 2024-01-14 06:52:51 46a88\n    * Now support glob masks (like in OS - with `*` and `?`) with `GLOB_BRACE` extension\n"
  },
  {
    "path": "tools/spritify.php",
    "content": "<?php\n\n/** Created by Gorlum 08.01.2024 18:59 */\n\n/**\n * Generate one PNG sprite file from several small files\n *\n * This CLI tool will generate one sprite PNG file from input (all files in specified directory) along with CSS file\n * Filenames would be used as a key for CSS qualifier\n * Prefix and suffix can be used to produce different types of CSS qualifiers: IDs, classes, nested qualifiers etc\n * Have support to scale all sprites to single-sized square\n *\n * @version 46d0\n */\n\n/** @noinspection PhpRedundantOptionalArgumentInspection */\n\nrequire_once __DIR__ . '/classes/Spritify.php';\nrequire_once __DIR__ . '/../vendor/autoload.php';\n\nuse Tools\\Spritify;\n\n// Tests\n//Spritify::go('', __DIR__ . '/.output/', 'DELETE', '#DELETE', '', 0, '/design/images/');\n//Spritify::go(__DIR__ . '/../skins/EpicBlue/icons/', __DIR__ . '/.output/', 'menu_icons_full', '#icon_full_', '', 0, '/design/images/');\n//Spritify::go(__DIR__ . '/../includes/', __DIR__ . '/.output/', 'NO_FILES', '#NO_FILES_', '', 0, '/design/images/');\n//Spritify::go(__DIR__ . '/../includes/zzzzz/', __DIR__ . '/.output/', 'NOT_EXISTS', '#NOT_EXISTS', '', 0, '/design/images/');\n//Spritify::go(__DIR__ . '/../skins/EpicBlue/icn/*', __DIR__ . '/.output/', 'menu_icons_full', '#icon_full_', '', 0, '/design/images/');\n\n//Spritify::go(__DIR__ . '/../skins/EpicBlue/icons/*', __DIR__ . '/.output/', 'menu_icons_full', '#icon_full_', '', 0, '/design/images/');\n\n// WiP\nSpritify::go(\n  [\n    __DIR__ . '/../design/images/smileys/blink.gif',\n    __DIR__ . '/../design/images/smileys/cool.gif',\n    __DIR__ . '/../design/images/smileys/bomb.gif',\n    __DIR__ . '/../design/images/smileys/blush.gif',\n    __DIR__ . '/../design/images/smileys/maniac.gif',\n\n//    __DIR__ . '/../design/images/smileys/accordion.gif',\n//    __DIR__ . '/../design/images/smileys/aggressive.gif',\n//    __DIR__ . '/../design/images/smileys/angel.gif',\n//    __DIR__ . '/../design/images/smileys/bad.gif',\n//    __DIR__ . '/../design/images/smileys/ban.gif',\n//    __DIR__ . '/../design/images/smileys/bayan.gif',\n//    __DIR__ . '/../design/images/smileys/blackeye.gif',\n//    __DIR__ . '/../design/images/smileys/blink.gif',\n//    __DIR__ . '/../design/images/smileys/blush.gif',\n//    __DIR__ . '/../design/images/smileys/bomb.gif',\n//    __DIR__ . '/../design/images/smileys/censored.gif',\n//    __DIR__ . '/../design/images/smileys/clapping.gif',\n//    __DIR__ . '/../design/images/smileys/coctail.gif',\n//    __DIR__ . '/../design/images/smileys/coffee.gif',\n//    __DIR__ . '/../design/images/smileys/contract.gif',\n//    __DIR__ . '/../design/images/smileys/cool.gif',\n//    __DIR__ . '/../design/images/smileys/cray.gif',\n//    __DIR__ . '/../design/images/smileys/crazy.gif',\n//    __DIR__ . '/../design/images/smileys/diablo.gif',\n//    __DIR__ . '/../design/images/smileys/dirol.gif',\n//    __DIR__ . '/../design/images/smileys/drinks.gif',\n//    __DIR__ . '/../design/images/smileys/facepalm.gif',\n//    __DIR__ . '/../design/images/smileys/fool.gif',\n//    __DIR__ . '/../design/images/smileys/friends.gif',\n//    __DIR__ . '/../design/images/smileys/give_rose.gif',\n//    __DIR__ . '/../design/images/smileys/good.gif',\n//    __DIR__ . '/../design/images/smileys/help.gif',\n//    __DIR__ . '/../design/images/smileys/index.html',\n//    __DIR__ . '/../design/images/smileys/lol.gif',\n//    __DIR__ . '/../design/images/smileys/maniac.gif',\n//    __DIR__ . '/../design/images/smileys/mellow.gif',\n//    __DIR__ . '/../design/images/smileys/mill.gif',\n//    __DIR__ . '/../design/images/smileys/nea.gif',\n//    __DIR__ . '/../design/images/smileys/new_year',\n//    __DIR__ . '/../design/images/smileys/panic.gif',\n//    __DIR__ . '/../design/images/smileys/pardon.gif',\n//    __DIR__ . '/../design/images/smileys/pleasantry.gif',\n//    __DIR__ . '/../design/images/smileys/plushit.gif',\n//    __DIR__ . '/../design/images/smileys/poke.gif',\n//    __DIR__ . '/../design/images/smileys/popcorn.gif',\n//    __DIR__ . '/../design/images/smileys/pray.gif',\n//    __DIR__ . '/../design/images/smileys/rofl.gif',\n//    __DIR__ . '/../design/images/smileys/sad.gif',\n//    __DIR__ . '/../design/images/smileys/sarcasm.gif',\n//    __DIR__ . '/../design/images/smileys/shok.gif',\n//    __DIR__ . '/../design/images/smileys/shout.gif',\n//    __DIR__ . '/../design/images/smileys/smile.gif',\n//    __DIR__ . '/../design/images/smileys/sorry.gif',\n//    __DIR__ . '/../design/images/smileys/spiteful.gif',\n//    __DIR__ . '/../design/images/smileys/suicide.gif',\n//    __DIR__ . '/../design/images/smileys/tease.gif',\n//    __DIR__ . '/../design/images/smileys/tongue.gif',\n//    __DIR__ . '/../design/images/smileys/unknw.gif',\n//    __DIR__ . '/../design/images/smileys/wall.gif',\n//    __DIR__ . '/../design/images/smileys/whistle.gif',\n//    __DIR__ . '/../design/images/smileys/wink.gif',\n//    __DIR__ . '/../design/images/smileys/yahoo.gif',\n//    __DIR__ . '/../design/images/smileys/yu.gif',\n//    __DIR__ . '/../design/images/smileys/hmm.gif',\n//\n////    Gives problem if with other files\n////    __DIR__ . '/../design/images/smileys/huh.gif',\n  ],\n  __DIR__ . '/.output/', 'smile_icons', '#icon_smile_', '', 64, '/design/images/'\n);\n\n// Actual converts\nSpritify::go(__DIR__ . '/../skins/EpicBlue/icons/menu*', __DIR__ . '/.output/', 'menu_icons', '#icon_', '', 14, '/design/images/');\nSpritify::go(__DIR__ . '/../design/images/navbar*', __DIR__ . '/.output/', 'sprite_navbar_buttons', '.', '_button', 0, '/skins/EpicBlue/images/');\nSpritify::go(\n  [\n    // By file to move small hangar at the end to maintain pixel compatibility with EpicBlue navbar\n    __DIR__ . '/../skins/supernova-ivash/navbar/navbar_dark_matter_standard.png',\n    __DIR__ . '/../skins/supernova-ivash/navbar/navbar_defense.png',\n    __DIR__ . '/../skins/supernova-ivash/navbar/navbar_expedition_standard.png',\n    __DIR__ . '/../skins/supernova-ivash/navbar/navbar_fleet_own_standard.png',\n    __DIR__ . '/../skins/supernova-ivash/navbar/navbar_hangar_scaled.png',\n    __DIR__ . '/../skins/supernova-ivash/navbar/navbar_mail_standard.png',\n    __DIR__ . '/../skins/supernova-ivash/navbar/navbar_metamatter_standard.png',\n    __DIR__ . '/../skins/supernova-ivash/navbar/navbar_quest_standard.png',\n    __DIR__ . '/../skins/supernova-ivash/navbar/navbar_research_64x64.png',\n    __DIR__ . '/../skins/supernova-ivash/navbar/navbar_research_standard.png',\n    __DIR__ . '/../skins/supernova-ivash/navbar/navbar_hangar.png',\n  ], __DIR__ . '/.output/', 'sprite_navbar_buttons_ivash', '.', '_button_ivash', 0, '/skins/supernova-ivash/images/');\n"
  },
  {
    "path": "viewreport.php",
    "content": "<?php\n\ninclude('common.' . substr(strrchr(__FILE__, '.'), 1));\n\n$template = SnTemplate::gettemplate('viewreport', true);\n$template->assign_var('PAGE_HINT', $lang['cr_view_hint']);\n\nSnTemplate::display($template, $lang['cr_view_title']);\n"
  }
]